forked from AuroraMiddleware/gtk
13d943f763
A problem with textures is that they can become too big for GPU memory, which would require tiling. But for tiling we only need to download the pixels needed by the tile. Similarly, there might be interest to not upload full textures if a renderer knows it only needs a small part. Both of these methods require the ability to specify an area of the texture to be downloaded. So change the download vfunc to include this parameter now before we add even more textures later. A private gdk_texture_download_area() function has also been added, but nobody is using it yet.
528 lines
15 KiB
C
528 lines
15 KiB
C
/* gdktexture.c
|
|
*
|
|
* Copyright 2016 Benjamin Otte
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/**
|
|
* SECTION:textures
|
|
* @Title: GdkTexture
|
|
* @Short_description: Pixel data
|
|
*
|
|
* #GdkTexture is the basic element used to refer to pixel data.
|
|
* It is primarily mean for pixel data that will not change over
|
|
* multiple frames, and will be used for a long time.
|
|
*
|
|
* You cannot get your pixel data back once you've uploaded it.
|
|
*
|
|
* #GdkTexture is an immutable object: That means you cannot change
|
|
* anything about it other than increasing the reference count via
|
|
* g_object_ref().
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "gdktextureprivate.h"
|
|
|
|
#include "gdkinternals.h"
|
|
#include "gdkmemorytextureprivate.h"
|
|
|
|
/**
|
|
* SECTION:gdktexture
|
|
* @Short_description: Image data for display
|
|
* @Title: GdkTexture
|
|
*
|
|
* A GdkTexture represents image data that can be displayed on screen.
|
|
*
|
|
* There are various ways to create GdkTexture objects from a #GdkPixbuf
|
|
* or a cairo surface, or other pixel data.
|
|
*
|
|
* An important aspect of GdkTextures is that they are immutable - once
|
|
* the image data has been wrapped in a GdkTexture, it may be uploaded
|
|
* to the GPU or used in other ways that make it impractical to allow
|
|
* modification.
|
|
*/
|
|
|
|
/**
|
|
* GdkTexture:
|
|
*
|
|
* The `GdkTexture` structure contains only private data.
|
|
*/
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_WIDTH,
|
|
PROP_HEIGHT,
|
|
|
|
N_PROPS
|
|
};
|
|
|
|
static GParamSpec *properties[N_PROPS];
|
|
|
|
G_DEFINE_ABSTRACT_TYPE (GdkTexture, gdk_texture, G_TYPE_OBJECT)
|
|
|
|
#define GDK_TEXTURE_WARN_NOT_IMPLEMENTED_METHOD(obj,method) \
|
|
g_critical ("Texture of type '%s' does not implement GdkTexture::" # method, G_OBJECT_TYPE_NAME (obj))
|
|
|
|
static void
|
|
gdk_texture_real_download (GdkTexture *self,
|
|
const GdkRectangle *area,
|
|
guchar *data,
|
|
gsize stride)
|
|
{
|
|
GDK_TEXTURE_WARN_NOT_IMPLEMENTED_METHOD (self, download);
|
|
}
|
|
|
|
static cairo_surface_t *
|
|
gdk_texture_real_download_surface (GdkTexture *texture)
|
|
{
|
|
cairo_surface_t *surface;
|
|
|
|
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
|
|
texture->width, texture->height);
|
|
gdk_texture_download (texture,
|
|
cairo_image_surface_get_data (surface),
|
|
cairo_image_surface_get_stride (surface));
|
|
cairo_surface_mark_dirty (surface);
|
|
|
|
return surface;
|
|
}
|
|
|
|
static void
|
|
gdk_texture_set_property (GObject *gobject,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GdkTexture *self = GDK_TEXTURE (gobject);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_WIDTH:
|
|
self->width = g_value_get_int (value);
|
|
break;
|
|
|
|
case PROP_HEIGHT:
|
|
self->height = g_value_get_int (value);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gdk_texture_get_property (GObject *gobject,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GdkTexture *self = GDK_TEXTURE (gobject);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_WIDTH:
|
|
g_value_set_int (value, self->width);
|
|
break;
|
|
|
|
case PROP_HEIGHT:
|
|
g_value_set_int (value, self->height);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gdk_texture_dispose (GObject *object)
|
|
{
|
|
GdkTexture *self = GDK_TEXTURE (object);
|
|
|
|
gdk_texture_clear_render_data (self);
|
|
|
|
G_OBJECT_CLASS (gdk_texture_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
gdk_texture_class_init (GdkTextureClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
klass->download = gdk_texture_real_download;
|
|
klass->download_surface = gdk_texture_real_download_surface;
|
|
|
|
gobject_class->set_property = gdk_texture_set_property;
|
|
gobject_class->get_property = gdk_texture_get_property;
|
|
gobject_class->dispose = gdk_texture_dispose;
|
|
|
|
/**
|
|
* GdkTexture:width:
|
|
*
|
|
* The width of the texture.
|
|
*/
|
|
properties[PROP_WIDTH] =
|
|
g_param_spec_int ("width",
|
|
"Width",
|
|
"The width of the texture",
|
|
1,
|
|
G_MAXINT,
|
|
1,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT_ONLY |
|
|
G_PARAM_STATIC_STRINGS |
|
|
G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
/**
|
|
* GdkTexture:height:
|
|
*
|
|
* The height of the texture.
|
|
*/
|
|
properties[PROP_HEIGHT] =
|
|
g_param_spec_int ("height",
|
|
"Height",
|
|
"The height of the texture",
|
|
1,
|
|
G_MAXINT,
|
|
1,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT_ONLY |
|
|
G_PARAM_STATIC_STRINGS |
|
|
G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
|
}
|
|
|
|
static void
|
|
gdk_texture_init (GdkTexture *self)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* gdk_texture_new_for_data:
|
|
* @data: (array): the pixel data
|
|
* @width: the number of pixels in each row
|
|
* @height: the number of rows
|
|
* @stride: the distance from the beginning of one row to the next, in bytes
|
|
*
|
|
* Creates a new texture object holding the given data.
|
|
* The data is assumed to be in CAIRO_FORMAT_ARGB32 format.
|
|
*
|
|
* Returns: a new #GdkTexture
|
|
*/
|
|
GdkTexture *
|
|
gdk_texture_new_for_data (const guchar *data,
|
|
int width,
|
|
int height,
|
|
int stride)
|
|
{
|
|
GdkTexture *texture;
|
|
cairo_surface_t *original, *copy;
|
|
cairo_t *cr;
|
|
|
|
g_return_val_if_fail (width > 0, NULL);
|
|
g_return_val_if_fail (height > 0, NULL);
|
|
|
|
original = cairo_image_surface_create_for_data ((guchar *) data, CAIRO_FORMAT_ARGB32, width, height, stride);
|
|
copy = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
|
|
|
|
cr = cairo_create (copy);
|
|
cairo_set_source_surface (cr, original, 0, 0);
|
|
cairo_paint (cr);
|
|
cairo_destroy (cr);
|
|
|
|
texture = gdk_texture_new_for_surface (copy);
|
|
|
|
cairo_surface_destroy (copy);
|
|
cairo_surface_finish (original);
|
|
cairo_surface_destroy (original);
|
|
|
|
return texture;
|
|
}
|
|
|
|
/**
|
|
* gdk_texture_new_for_surface:
|
|
* @surface: a cairo image surface
|
|
*
|
|
* Creates a new texture object representing the surface.
|
|
* @surface must be an image surface with format CAIRO_FORMAT_ARGB32.
|
|
*
|
|
* Returns: a new #GdkTexture
|
|
*/
|
|
GdkTexture *
|
|
gdk_texture_new_for_surface (cairo_surface_t *surface)
|
|
{
|
|
GdkTexture *texture;
|
|
GBytes *bytes;
|
|
|
|
g_return_val_if_fail (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE, NULL);
|
|
g_return_val_if_fail (cairo_image_surface_get_width (surface) > 0, NULL);
|
|
g_return_val_if_fail (cairo_image_surface_get_height (surface) > 0, NULL);
|
|
|
|
bytes = g_bytes_new_with_free_func (cairo_image_surface_get_data (surface),
|
|
cairo_image_surface_get_height (surface)
|
|
* cairo_image_surface_get_stride (surface),
|
|
(GDestroyNotify) cairo_surface_destroy,
|
|
cairo_surface_reference (surface));
|
|
|
|
texture = gdk_memory_texture_new (cairo_image_surface_get_width (surface),
|
|
cairo_image_surface_get_height (surface),
|
|
GDK_MEMORY_CAIRO_FORMAT_ARGB32,
|
|
bytes,
|
|
cairo_image_surface_get_stride (surface));
|
|
|
|
g_bytes_unref (bytes);
|
|
|
|
return texture;
|
|
}
|
|
|
|
/**
|
|
* gdk_texture_new_for_pixbuf:
|
|
* @pixbuf: a #GdkPixbuf
|
|
*
|
|
* Creates a new texture object representing the GdkPixbuf.
|
|
*
|
|
* Returns: a new #GdkTexture
|
|
*/
|
|
GdkTexture *
|
|
gdk_texture_new_for_pixbuf (GdkPixbuf *pixbuf)
|
|
{
|
|
GdkTexture *texture;
|
|
GBytes *bytes;
|
|
|
|
g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
|
|
|
|
bytes = g_bytes_new_with_free_func (gdk_pixbuf_get_pixels (pixbuf),
|
|
gdk_pixbuf_get_height (pixbuf)
|
|
* gdk_pixbuf_get_rowstride (pixbuf),
|
|
g_object_unref,
|
|
g_object_ref (pixbuf));
|
|
|
|
texture = gdk_memory_texture_new (gdk_pixbuf_get_width (pixbuf),
|
|
gdk_pixbuf_get_height (pixbuf),
|
|
gdk_pixbuf_get_has_alpha (pixbuf)
|
|
? GDK_MEMORY_GDK_PIXBUF_ALPHA
|
|
: GDK_MEMORY_GDK_PIXBUF_OPAQUE,
|
|
bytes,
|
|
gdk_pixbuf_get_rowstride (pixbuf));
|
|
|
|
g_bytes_unref (bytes);
|
|
|
|
return texture;
|
|
}
|
|
|
|
/**
|
|
* gdk_texture_new_from_resource:
|
|
* @resource_path: the path of the resource file
|
|
*
|
|
* Creates a new texture by loading an image from a resource.
|
|
* The file format is detected automatically.
|
|
*
|
|
* It is a fatal error if @resource_path does not specify a valid
|
|
* image resource and the program will abort if that happens.
|
|
* If you are unsure about the validity of a resource, use
|
|
* gdk_texture_new_from_file() to load it.
|
|
*
|
|
* Return value: A newly-created texture
|
|
*/
|
|
GdkTexture *
|
|
gdk_texture_new_from_resource (const char *resource_path)
|
|
{
|
|
GError *error = NULL;
|
|
GdkTexture *texture;
|
|
GdkPixbuf *pixbuf;
|
|
|
|
g_return_val_if_fail (resource_path != NULL, NULL);
|
|
|
|
pixbuf = gdk_pixbuf_new_from_resource (resource_path, &error);
|
|
if (pixbuf == NULL)
|
|
g_error ("Resource path %s is not a valid image: %s", resource_path, error->message);
|
|
|
|
texture = gdk_texture_new_for_pixbuf (pixbuf);
|
|
g_object_unref (pixbuf);
|
|
|
|
return texture;
|
|
}
|
|
|
|
/**
|
|
* gdk_texture_new_from_file:
|
|
* @file: #GFile to load
|
|
* @error: Return location for an error
|
|
*
|
|
* Creates a new texture by loading an image from a file. The file format is
|
|
* detected automatically. If %NULL is returned, then @error will be set.
|
|
*
|
|
* Return value: A newly-created #GdkTexture or %NULL if an error occured.
|
|
**/
|
|
GdkTexture *
|
|
gdk_texture_new_from_file (GFile *file,
|
|
GError **error)
|
|
{
|
|
GdkTexture *texture;
|
|
GdkPixbuf *pixbuf;
|
|
GInputStream *stream;
|
|
|
|
g_return_val_if_fail (G_IS_FILE (file), NULL);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
|
|
|
stream = G_INPUT_STREAM (g_file_read (file, NULL, error));
|
|
if (stream == NULL)
|
|
return NULL;
|
|
|
|
pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error);
|
|
g_object_unref (stream);
|
|
if (pixbuf == NULL)
|
|
return NULL;
|
|
|
|
texture = gdk_texture_new_for_pixbuf (pixbuf);
|
|
g_object_unref (pixbuf);
|
|
|
|
return texture;
|
|
}
|
|
|
|
/**
|
|
* gdk_texture_get_width:
|
|
* @texture: a #GdkTexture
|
|
*
|
|
* Returns the width of @texture.
|
|
*
|
|
* Returns: the width of the #GdkTexture
|
|
*/
|
|
int
|
|
gdk_texture_get_width (GdkTexture *texture)
|
|
{
|
|
g_return_val_if_fail (GDK_IS_TEXTURE (texture), 0);
|
|
|
|
return texture->width;
|
|
}
|
|
|
|
/**
|
|
* gdk_texture_get_height:
|
|
* @texture: a #GdkTexture
|
|
*
|
|
* Returns the height of the @texture.
|
|
*
|
|
* Returns: the height of the #GdkTexture
|
|
*/
|
|
int
|
|
gdk_texture_get_height (GdkTexture *texture)
|
|
{
|
|
g_return_val_if_fail (GDK_IS_TEXTURE (texture), 0);
|
|
|
|
return texture->height;
|
|
}
|
|
|
|
cairo_surface_t *
|
|
gdk_texture_download_surface (GdkTexture *texture)
|
|
{
|
|
return GDK_TEXTURE_GET_CLASS (texture)->download_surface (texture);
|
|
}
|
|
|
|
void
|
|
gdk_texture_download_area (GdkTexture *texture,
|
|
const GdkRectangle *area,
|
|
guchar *data,
|
|
gsize stride)
|
|
{
|
|
g_assert (area->x >= 0);
|
|
g_assert (area->y >= 0);
|
|
g_assert (area->x + area->width <= texture->width);
|
|
g_assert (area->y + area->height <= texture->height);
|
|
|
|
return GDK_TEXTURE_GET_CLASS (texture)->download (texture, area, data, stride);
|
|
}
|
|
|
|
/*
|
|
* gdk_texture_download:
|
|
* @texture: a #GdkTexture
|
|
* @data: (array): pointer to enough memory to be filled with the
|
|
* downloaded data of @texture
|
|
* @stride: rowstride in bytes
|
|
*
|
|
* Downloads the @texture into local memory. This may be
|
|
* an expensive operation, as the actual texture data may
|
|
* reside on a GPU or on a remote display server.
|
|
*
|
|
* The data format of the downloaded data is equivalent to
|
|
* %CAIRO_FORMAT_ARGB32, so every downloaded pixel requires
|
|
* 4 bytes of memory.
|
|
*
|
|
* Downloading a texture into a Cairo image surface:
|
|
* |[<!-- language="C" -->
|
|
* surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
|
|
* gdk_texture_get_width (texture),
|
|
* gdk_texture_get_height (texture));
|
|
* gdk_texture_download (texture,
|
|
* cairo_image_surface_get_data (surface),
|
|
* cairo_image_surface_get_stride (surface));
|
|
* cairo_surface_mark_dirty (surface);
|
|
* ]|
|
|
**/
|
|
void
|
|
gdk_texture_download (GdkTexture *texture,
|
|
guchar *data,
|
|
gsize stride)
|
|
{
|
|
g_return_if_fail (GDK_IS_TEXTURE (texture));
|
|
g_return_if_fail (data != NULL);
|
|
g_return_if_fail (stride >= gdk_texture_get_width (texture) * 4);
|
|
|
|
gdk_texture_download_area (texture,
|
|
&(GdkRectangle) { 0, 0, texture->width, texture->height },
|
|
data,
|
|
stride);
|
|
}
|
|
|
|
gboolean
|
|
gdk_texture_set_render_data (GdkTexture *self,
|
|
gpointer key,
|
|
gpointer data,
|
|
GDestroyNotify notify)
|
|
{
|
|
g_return_val_if_fail (data != NULL, FALSE);
|
|
|
|
if (self->render_key != NULL)
|
|
return FALSE;
|
|
|
|
self->render_key = key;
|
|
self->render_data = data;
|
|
self->render_notify = notify;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
gdk_texture_clear_render_data (GdkTexture *self)
|
|
{
|
|
if (self->render_notify)
|
|
self->render_notify (self->render_data);
|
|
|
|
self->render_key = NULL;
|
|
self->render_data = NULL;
|
|
self->render_notify = NULL;
|
|
}
|
|
|
|
gpointer
|
|
gdk_texture_get_render_data (GdkTexture *self,
|
|
gpointer key)
|
|
{
|
|
if (self->render_key != key)
|
|
return NULL;
|
|
|
|
return self->render_data;
|
|
}
|