forked from AuroraMiddleware/gtk
8920639a2b
GdkMemoryTexture is a texture implementation for holding data in memory (read: GBytes). You specify the GdkMemoryFormat that data is in and off you go. Renderers can use this to add uploads in various different formats and don't need to fallback to GDK doing the conersion on the CPU. Supported formats can be extended if we need new ones, for now I just added the relevant ones for Cairo and GdkPixbuf. The constructor is also private still, because I'm not sure we want to export GdkMemoryFormat. Wrappers that do from_cairo_surface() and for_pixbuf() do exist though.
510 lines
14 KiB
C
510 lines
14 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,
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
|
|
return GDK_TEXTURE_GET_CLASS (texture)->download (texture, 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;
|
|
}
|