gtk/gdk/gdkmemorytexture.c
Matthias Clasen d718fda057 memory texture: Fix a memory leak
The callers assumed that gdk_memory_sanitize is transfer full in
both directions, but it wasn't. Make it so.
2024-08-10 22:24:29 -04:00

285 lines
9.1 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright © 2018 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.1 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/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gdkmemorytextureprivate.h"
#include "gdkcolorstateprivate.h"
#include "gdkmemoryformatprivate.h"
/**
* GdkMemoryTexture:
*
* A `GdkTexture` representing image data in memory.
*/
struct _GdkMemoryTexture
{
GdkTexture parent_instance;
GBytes *bytes;
gsize stride;
};
struct _GdkMemoryTextureClass
{
GdkTextureClass parent_class;
};
G_DEFINE_TYPE (GdkMemoryTexture, gdk_memory_texture, GDK_TYPE_TEXTURE)
static void
gdk_memory_texture_dispose (GObject *object)
{
GdkMemoryTexture *self = GDK_MEMORY_TEXTURE (object);
g_clear_pointer (&self->bytes, g_bytes_unref);
G_OBJECT_CLASS (gdk_memory_texture_parent_class)->dispose (object);
}
static void
gdk_memory_texture_download (GdkTexture *texture,
GdkMemoryFormat format,
GdkColorState *color_state,
guchar *data,
gsize stride)
{
GdkMemoryTexture *self = GDK_MEMORY_TEXTURE (texture);
gdk_memory_convert (data, stride,
format,
color_state,
(guchar *) g_bytes_get_data (self->bytes, NULL),
self->stride,
texture->format,
texture->color_state,
gdk_texture_get_width (texture),
gdk_texture_get_height (texture));
}
static void
gdk_memory_texture_class_init (GdkMemoryTextureClass *klass)
{
GdkTextureClass *texture_class = GDK_TEXTURE_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
texture_class->download = gdk_memory_texture_download;
gobject_class->dispose = gdk_memory_texture_dispose;
}
static void
gdk_memory_texture_init (GdkMemoryTexture *self)
{
}
static GBytes *
gdk_memory_sanitize (GBytes *bytes,
int width,
int height,
GdkMemoryFormat format,
gsize stride,
gsize *out_stride)
{
gsize align, size, copy_stride, bpp;
const guchar *data;
guchar *copy;
int y;
data = g_bytes_get_data (bytes, &size);
align = gdk_memory_format_alignment (format);
if (GPOINTER_TO_SIZE (data) % align == 0 &&
stride % align == 0)
{
*out_stride = stride;
return bytes;
}
bpp = gdk_memory_format_bytes_per_pixel (format);
copy_stride = bpp * width;
/* align to multiples of 4, just to be sure */
copy_stride = (copy_stride + 3) & ~3;
copy = g_malloc (copy_stride * height);
for (y = 0; y < height; y++)
memcpy (copy + y * copy_stride, data + y * stride, bpp * width);
g_bytes_unref (bytes);
*out_stride = copy_stride;
return g_bytes_new_take (copy, copy_stride * height);
}
GdkTexture *
gdk_memory_texture_new_from_builder (GdkMemoryTextureBuilder *builder)
{
GdkMemoryTexture *self;
GdkTexture *texture, *update_texture;
self = g_object_new (GDK_TYPE_MEMORY_TEXTURE,
"width", gdk_memory_texture_builder_get_width (builder),
"height", gdk_memory_texture_builder_get_height (builder),
"color-state", gdk_memory_texture_builder_get_color_state (builder),
NULL);
texture = GDK_TEXTURE (self);
texture->format = gdk_memory_texture_builder_get_format (builder);
self->bytes = gdk_memory_sanitize (g_bytes_ref (gdk_memory_texture_builder_get_bytes (builder)),
texture->width,
texture->height,
texture->format,
gdk_memory_texture_builder_get_stride (builder),
&self->stride);
update_texture = gdk_memory_texture_builder_get_update_texture (builder);
if (update_texture)
{
cairo_region_t *update_region = gdk_memory_texture_builder_get_update_region (builder);
if (update_region)
{
update_region = cairo_region_copy (update_region);
cairo_region_intersect_rectangle (update_region,
&(cairo_rectangle_int_t) {
0, 0,
update_texture->width, update_texture->height
});
gdk_texture_set_diff (GDK_TEXTURE (self), update_texture, update_region);
}
}
return GDK_TEXTURE (self);
}
/**
* gdk_memory_texture_new:
* @width: the width of the texture
* @height: the height of the texture
* @format: the format of the data
* @bytes: the `GBytes` containing the pixel data
* @stride: rowstride for the data
*
* Creates a new texture for a blob of image data.
*
* The `GBytes` must contain @stride × @height pixels
* in the given format.
*
* Returns: (type GdkMemoryTexture): A newly-created `GdkTexture`
*/
GdkTexture *
gdk_memory_texture_new (int width,
int height,
GdkMemoryFormat format,
GBytes *bytes,
gsize stride)
{
GdkMemoryTexture *self;
g_return_val_if_fail (width > 0, NULL);
g_return_val_if_fail (height > 0, NULL);
g_return_val_if_fail (bytes != NULL, NULL);
g_return_val_if_fail (stride >= width * gdk_memory_format_bytes_per_pixel (format), NULL);
/* needs to be this complex to support subtexture of the bottom right part */
g_return_val_if_fail (g_bytes_get_size (bytes) >= gdk_memory_format_min_buffer_size (format, stride, width, height), NULL);
bytes = gdk_memory_sanitize (g_bytes_ref (bytes), width, height, format, stride, &stride);
self = g_object_new (GDK_TYPE_MEMORY_TEXTURE,
"width", width,
"height", height,
"color-state", GDK_COLOR_STATE_SRGB,
NULL);
GDK_TEXTURE (self)->format = format;
self->bytes = bytes;
self->stride = stride;
return GDK_TEXTURE (self);
}
GdkTexture *
gdk_memory_texture_new_subtexture (GdkMemoryTexture *source,
int x,
int y,
int width,
int height)
{
GdkTexture *texture, *result;
gsize offset, size;
GBytes *bytes;
g_return_val_if_fail (GDK_IS_MEMORY_TEXTURE (source), NULL);
g_return_val_if_fail (x >= 0 && x < GDK_TEXTURE (source)->width, NULL);
g_return_val_if_fail (y >= 0 && y < GDK_TEXTURE (source)->height, NULL);
g_return_val_if_fail (width > 0 && x + width <= GDK_TEXTURE (source)->width, NULL);
g_return_val_if_fail (height > 0 && y + height <= GDK_TEXTURE (source)->height, NULL);
texture = GDK_TEXTURE (source);
offset = y * source->stride + x * gdk_memory_format_bytes_per_pixel (texture->format);
size = gdk_memory_format_min_buffer_size (texture->format, source->stride, width, height);
bytes = g_bytes_new_from_bytes (source->bytes, offset, size);
result = gdk_memory_texture_new (width,
height,
texture->format,
bytes,
source->stride);
g_bytes_unref (bytes);
return result;
}
GdkMemoryTexture *
gdk_memory_texture_from_texture (GdkTexture *texture)
{
GdkTexture *result;
GBytes *bytes;
guchar *data;
gsize stride;
g_return_val_if_fail (GDK_IS_TEXTURE (texture), NULL);
if (GDK_IS_MEMORY_TEXTURE (texture))
return g_object_ref (GDK_MEMORY_TEXTURE (texture));
stride = texture->width * gdk_memory_format_bytes_per_pixel (texture->format);
data = g_malloc_n (stride, texture->height);
gdk_texture_do_download (texture, texture->format, texture->color_state, data, stride);
bytes = g_bytes_new_take (data, stride * texture->height);
result = gdk_memory_texture_new (texture->width,
texture->height,
texture->format,
bytes,
stride);
g_bytes_unref (bytes);
return GDK_MEMORY_TEXTURE (result);
}
GBytes *
gdk_memory_texture_get_bytes (GdkMemoryTexture *self,
gsize *out_stride)
{
*out_stride = self->stride;
return self->bytes;
}