1/* gdktexture.c
2 *
3 * Copyright 2016 Benjamin Otte
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19/**
20 * GdkTexture:
21 *
22 * `GdkTexture` is the basic element used to refer to pixel data.
23 *
24 * It is primarily meant for pixel data that will not change over
25 * multiple frames, and will be used for a long time.
26 *
27 * There are various ways to create `GdkTexture` objects from a
28 * [class@GdkPixbuf.Pixbuf], or a Cairo surface, or other pixel data.
29 *
30 * The ownership of the pixel data is transferred to the `GdkTexture`
31 * instance; you can only make a copy of it, via [method@Gdk.Texture.download].
32 *
33 * `GdkTexture` is an immutable object: That means you cannot change
34 * anything about it other than increasing the reference count via
35 * [method@GObject.Object.ref], and consequently, it is a thread-safe object.
36 */
37
38#include "config.h"
39
40#include "gdktextureprivate.h"
41
42#include "gdkintl.h"
43#include "gdkmemorytextureprivate.h"
44#include "gdkpaintable.h"
45#include "gdksnapshot.h"
46
47#include <graphene.h>
48#include "loaders/gdkpngprivate.h"
49#include "loaders/gdktiffprivate.h"
50#include "loaders/gdkjpegprivate.h"
51
52G_DEFINE_QUARK (gdk-texture-error-quark, gdk_texture_error)
53
54/* HACK: So we don't need to include any (not-yet-created) GSK or GTK headers */
55void
56gtk_snapshot_append_texture (GdkSnapshot *snapshot,
57 GdkTexture *texture,
58 const graphene_rect_t *bounds);
59
60enum {
61 PROP_0,
62 PROP_WIDTH,
63 PROP_HEIGHT,
64
65 N_PROPS
66};
67
68static GParamSpec *properties[N_PROPS];
69
70static void
71gdk_texture_paintable_snapshot (GdkPaintable *paintable,
72 GdkSnapshot *snapshot,
73 double width,
74 double height)
75{
76 GdkTexture *self = GDK_TEXTURE (paintable);
77
78 gtk_snapshot_append_texture (snapshot,
79 texture: self,
80 bounds: &GRAPHENE_RECT_INIT (0, 0, width, height));
81}
82
83static GdkPaintableFlags
84gdk_texture_paintable_get_flags (GdkPaintable *paintable)
85{
86 return GDK_PAINTABLE_STATIC_SIZE
87 | GDK_PAINTABLE_STATIC_CONTENTS;
88}
89
90static int
91gdk_texture_paintable_get_intrinsic_width (GdkPaintable *paintable)
92{
93 GdkTexture *self = GDK_TEXTURE (paintable);
94
95 return self->width;
96}
97
98static int
99gdk_texture_paintable_get_intrinsic_height (GdkPaintable *paintable)
100{
101 GdkTexture *self = GDK_TEXTURE (paintable);
102
103 return self->height;
104}
105
106static void
107gdk_texture_paintable_init (GdkPaintableInterface *iface)
108{
109 iface->snapshot = gdk_texture_paintable_snapshot;
110 iface->get_flags = gdk_texture_paintable_get_flags;
111 iface->get_intrinsic_width = gdk_texture_paintable_get_intrinsic_width;
112 iface->get_intrinsic_height = gdk_texture_paintable_get_intrinsic_height;
113}
114
115static GVariant *
116gdk_texture_icon_serialize (GIcon *icon)
117{
118 GVariant *result;
119 GBytes *bytes;
120
121 bytes = gdk_texture_save_to_png_bytes (GDK_TEXTURE (icon));
122 result = g_variant_new_from_bytes (G_VARIANT_TYPE_BYTESTRING, bytes, TRUE);
123 g_bytes_unref (bytes);
124
125 return g_variant_new (format_string: "(sv)", "bytes", result);
126}
127
128static void
129gdk_texture_icon_init (GIconIface *iface)
130{
131 iface->hash = (guint (*) (GIcon *)) g_direct_hash;
132 iface->equal = (gboolean (*) (GIcon *, GIcon *)) g_direct_equal;
133 iface->serialize = gdk_texture_icon_serialize;
134}
135
136static GInputStream *
137gdk_texture_loadable_icon_load (GLoadableIcon *icon,
138 int size,
139 char **type,
140 GCancellable *cancellable,
141 GError **error)
142{
143 GInputStream *stream;
144 GBytes *bytes;
145
146 bytes = gdk_texture_save_to_png_bytes (GDK_TEXTURE (icon));
147 stream = g_memory_input_stream_new_from_bytes (bytes);
148 g_bytes_unref (bytes);
149
150 if (type)
151 *type = NULL;
152
153 return stream;
154}
155
156static void
157gdk_texture_loadable_icon_load_in_thread (GTask *task,
158 gpointer source_object,
159 gpointer task_data,
160 GCancellable *cancellable)
161{
162 GInputStream *stream;
163 GBytes *bytes;
164
165 bytes = gdk_texture_save_to_png_bytes (texture: source_object);
166 stream = g_memory_input_stream_new_from_bytes (bytes);
167 g_bytes_unref (bytes);
168 g_task_return_pointer (task, result: stream, result_destroy: g_object_unref);
169}
170
171static void
172gdk_texture_loadable_icon_load_async (GLoadableIcon *icon,
173 int size,
174 GCancellable *cancellable,
175 GAsyncReadyCallback callback,
176 gpointer user_data)
177{
178 GTask *task;
179
180 task = g_task_new (source_object: icon, cancellable, callback, callback_data: user_data);
181 g_task_run_in_thread (task, task_func: gdk_texture_loadable_icon_load_in_thread);
182 g_object_unref (object: task);
183}
184
185static GInputStream *
186gdk_texture_loadable_icon_load_finish (GLoadableIcon *icon,
187 GAsyncResult *res,
188 char **type,
189 GError **error)
190{
191 GInputStream *result;
192
193 g_return_val_if_fail (g_task_is_valid (res, icon), NULL);
194
195 result = g_task_propagate_pointer (G_TASK (res), error);
196 if (result == NULL)
197 return NULL;
198
199 if (type)
200 *type = NULL;
201
202 return result;
203}
204
205static void
206gdk_texture_loadable_icon_init (GLoadableIconIface *iface)
207{
208 iface->load = gdk_texture_loadable_icon_load;
209 iface->load_async = gdk_texture_loadable_icon_load_async;
210 iface->load_finish = gdk_texture_loadable_icon_load_finish;
211}
212
213G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GdkTexture, gdk_texture, G_TYPE_OBJECT,
214 G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
215 gdk_texture_paintable_init)
216 G_IMPLEMENT_INTERFACE (G_TYPE_ICON,
217 gdk_texture_icon_init)
218 G_IMPLEMENT_INTERFACE (G_TYPE_LOADABLE_ICON, gdk_texture_loadable_icon_init))
219
220#define GDK_TEXTURE_WARN_NOT_IMPLEMENTED_METHOD(obj,method) \
221 g_critical ("Texture of type '%s' does not implement GdkTexture::" # method, G_OBJECT_TYPE_NAME (obj))
222
223static void
224gdk_texture_default_download (GdkTexture *texture,
225 GdkMemoryFormat format,
226 guchar *data,
227 gsize stride)
228{
229 GDK_TEXTURE_WARN_NOT_IMPLEMENTED_METHOD (texture, download);
230}
231
232static void
233gdk_texture_set_property (GObject *gobject,
234 guint prop_id,
235 const GValue *value,
236 GParamSpec *pspec)
237{
238 GdkTexture *self = GDK_TEXTURE (gobject);
239
240 switch (prop_id)
241 {
242 case PROP_WIDTH:
243 self->width = g_value_get_int (value);
244 break;
245
246 case PROP_HEIGHT:
247 self->height = g_value_get_int (value);
248 break;
249
250 default:
251 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
252 break;
253 }
254}
255
256static void
257gdk_texture_get_property (GObject *gobject,
258 guint prop_id,
259 GValue *value,
260 GParamSpec *pspec)
261{
262 GdkTexture *self = GDK_TEXTURE (gobject);
263
264 switch (prop_id)
265 {
266 case PROP_WIDTH:
267 g_value_set_int (value, v_int: self->width);
268 break;
269
270 case PROP_HEIGHT:
271 g_value_set_int (value, v_int: self->height);
272 break;
273
274 default:
275 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
276 break;
277 }
278}
279
280static void
281gdk_texture_dispose (GObject *object)
282{
283 GdkTexture *self = GDK_TEXTURE (object);
284
285 gdk_texture_clear_render_data (self);
286
287 G_OBJECT_CLASS (gdk_texture_parent_class)->dispose (object);
288}
289
290static void
291gdk_texture_class_init (GdkTextureClass *klass)
292{
293 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
294
295 klass->download = gdk_texture_default_download;
296
297 gobject_class->set_property = gdk_texture_set_property;
298 gobject_class->get_property = gdk_texture_get_property;
299 gobject_class->dispose = gdk_texture_dispose;
300
301 /**
302 * GdkTexture:width: (attributes org.gtk.Property.get=gdk_texture_get_width)
303 *
304 * The width of the texture, in pixels.
305 */
306 properties[PROP_WIDTH] =
307 g_param_spec_int (name: "width",
308 nick: "Width",
309 blurb: "The width of the texture",
310 minimum: 1,
311 G_MAXINT,
312 default_value: 1,
313 flags: G_PARAM_READWRITE |
314 G_PARAM_CONSTRUCT_ONLY |
315 G_PARAM_STATIC_STRINGS |
316 G_PARAM_EXPLICIT_NOTIFY);
317
318 /**
319 * GdkTexture:height: (attributes org.gtk.Property.get=gdk_texture_get_height)
320 *
321 * The height of the texture, in pixels.
322 */
323 properties[PROP_HEIGHT] =
324 g_param_spec_int (name: "height",
325 nick: "Height",
326 blurb: "The height of the texture",
327 minimum: 1,
328 G_MAXINT,
329 default_value: 1,
330 flags: G_PARAM_READWRITE |
331 G_PARAM_CONSTRUCT_ONLY |
332 G_PARAM_STATIC_STRINGS |
333 G_PARAM_EXPLICIT_NOTIFY);
334
335 g_object_class_install_properties (oclass: gobject_class, n_pspecs: N_PROPS, pspecs: properties);
336}
337
338static void
339gdk_texture_init (GdkTexture *self)
340{
341}
342
343/**
344 * gdk_texture_new_for_surface:
345 * @surface: a cairo image surface
346 *
347 * Creates a new texture object representing the surface.
348 *
349 * @surface must be an image surface with format `CAIRO_FORMAT_ARGB32`.
350 *
351 * Returns: a new `GdkTexture`
352 */
353GdkTexture *
354gdk_texture_new_for_surface (cairo_surface_t *surface)
355{
356 GdkTexture *texture;
357 GBytes *bytes;
358
359 g_return_val_if_fail (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE, NULL);
360 g_return_val_if_fail (cairo_image_surface_get_width (surface) > 0, NULL);
361 g_return_val_if_fail (cairo_image_surface_get_height (surface) > 0, NULL);
362
363 bytes = g_bytes_new_with_free_func (data: cairo_image_surface_get_data (surface),
364 size: cairo_image_surface_get_height (surface)
365 * cairo_image_surface_get_stride (surface),
366 free_func: (GDestroyNotify) cairo_surface_destroy,
367 user_data: cairo_surface_reference (surface));
368
369 texture = gdk_memory_texture_new (width: cairo_image_surface_get_width (surface),
370 height: cairo_image_surface_get_height (surface),
371 GDK_MEMORY_DEFAULT,
372 bytes,
373 stride: cairo_image_surface_get_stride (surface));
374
375 g_bytes_unref (bytes);
376
377 return texture;
378}
379
380/**
381 * gdk_texture_new_for_pixbuf:
382 * @pixbuf: a `GdkPixbuf`
383 *
384 * Creates a new texture object representing the `GdkPixbuf`.
385 *
386 * This function is threadsafe, so that you can e.g. use GTask
387 * and [method@Gio.Task.run_in_thread] to avoid blocking the main thread
388 * while loading a big image.
389 *
390 * Returns: a new `GdkTexture`
391 */
392GdkTexture *
393gdk_texture_new_for_pixbuf (GdkPixbuf *pixbuf)
394{
395 GdkTexture *texture;
396 GBytes *bytes;
397
398 g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
399
400 bytes = g_bytes_new_with_free_func (data: gdk_pixbuf_get_pixels (pixbuf),
401 size: gdk_pixbuf_get_height (pixbuf)
402 * gdk_pixbuf_get_rowstride (pixbuf),
403 free_func: g_object_unref,
404 g_object_ref (pixbuf));
405 texture = gdk_memory_texture_new (width: gdk_pixbuf_get_width (pixbuf),
406 height: gdk_pixbuf_get_height (pixbuf),
407 format: gdk_pixbuf_get_has_alpha (pixbuf)
408 ? GDK_MEMORY_GDK_PIXBUF_ALPHA
409 : GDK_MEMORY_GDK_PIXBUF_OPAQUE,
410 bytes,
411 stride: gdk_pixbuf_get_rowstride (pixbuf));
412
413 g_bytes_unref (bytes);
414
415 return texture;
416}
417
418/**
419 * gdk_texture_new_from_resource:
420 * @resource_path: the path of the resource file
421 *
422 * Creates a new texture by loading an image from a resource.
423 *
424 * The file format is detected automatically. The supported formats
425 * are PNG and JPEG, though more formats might be available.
426 *
427 * It is a fatal error if @resource_path does not specify a valid
428 * image resource and the program will abort if that happens.
429 * If you are unsure about the validity of a resource, use
430 * [ctor@Gdk.Texture.new_from_file] to load it.
431 *
432 * This function is threadsafe, so that you can e.g. use GTask
433 * and [method@Gio.Task.run_in_thread] to avoid blocking the main thread
434 * while loading a big image.
435 *
436 * Return value: A newly-created `GdkTexture`
437 */
438GdkTexture *
439gdk_texture_new_from_resource (const char *resource_path)
440{
441 GBytes *bytes;
442 GdkTexture *texture;
443 GError *error = NULL;
444
445 g_return_val_if_fail (resource_path != NULL, NULL);
446
447 bytes = g_resources_lookup_data (path: resource_path, lookup_flags: 0, error: &error);
448 if (bytes != NULL)
449 {
450 texture = gdk_texture_new_from_bytes (bytes, error: &error);
451 g_bytes_unref (bytes);
452 }
453 else
454 texture = NULL;
455
456 if (texture == NULL)
457 g_error ("Resource path %s is not a valid image: %s", resource_path, error->message);
458
459 return texture;
460}
461
462/**
463 * gdk_texture_new_from_file:
464 * @file: `GFile` to load
465 * @error: Return location for an error
466 *
467 * Creates a new texture by loading an image from a file.
468 *
469 * The file format is detected automatically. The supported formats
470 * are PNG and JPEG, though more formats might be available.
471 *
472 * If %NULL is returned, then @error will be set.
473 *
474 * This function is threadsafe, so that you can e.g. use GTask
475 * and [method@Gio.Task.run_in_thread] to avoid blocking the main thread
476 * while loading a big image.
477 *
478 * Return value: A newly-created `GdkTexture`
479 */
480GdkTexture *
481gdk_texture_new_from_file (GFile *file,
482 GError **error)
483{
484 GBytes *bytes;
485 GdkTexture *texture;
486
487 g_return_val_if_fail (G_IS_FILE (file), NULL);
488 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
489
490 bytes = g_file_load_bytes (file, NULL, NULL, error);
491 if (bytes == NULL)
492 return NULL;
493
494 texture = gdk_texture_new_from_bytes (bytes, error);
495
496 g_bytes_unref (bytes);
497
498 return texture;
499}
500
501gboolean
502gdk_texture_can_load (GBytes *bytes)
503{
504 return gdk_is_png (bytes) ||
505 gdk_is_jpeg (bytes) ||
506 gdk_is_tiff (bytes);
507}
508
509static GdkTexture *
510gdk_texture_new_from_bytes_internal (GBytes *bytes,
511 GError **error)
512{
513 if (gdk_is_png (bytes))
514 {
515 return gdk_load_png (bytes, error);
516 }
517 else if (gdk_is_jpeg (bytes))
518 {
519 return gdk_load_jpeg (bytes, error);
520 }
521 else if (gdk_is_tiff (bytes))
522 {
523 return gdk_load_tiff (bytes, error);
524 }
525 else
526 {
527 g_set_error_literal (err: error,
528 GDK_TEXTURE_ERROR, code: GDK_TEXTURE_ERROR_UNSUPPORTED_FORMAT,
529 _("Unknown image format."));
530 return NULL;
531 }
532}
533
534static GdkTexture *
535gdk_texture_new_from_bytes_pixbuf (GBytes *bytes,
536 GError **error)
537{
538 GInputStream *stream;
539 GdkPixbuf *pixbuf;
540 GdkTexture *texture;
541
542 stream = g_memory_input_stream_new_from_bytes (bytes);
543 pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error);
544 g_object_unref (object: stream);
545 if (pixbuf == NULL)
546 return NULL;
547
548 texture = gdk_texture_new_for_pixbuf (pixbuf);
549 g_object_unref (object: pixbuf);
550
551 return texture;
552}
553
554
555/**
556 * gdk_texture_new_from_bytes:
557 * @bytes: a `GBytes` containing the data to load
558 * @error: Return location for an error
559 *
560 * Creates a new texture by loading an image from memory,
561 *
562 * The file format is detected automatically. The supported formats
563 * are PNG and JPEG, though more formats might be available.
564 *
565 * If %NULL is returned, then @error will be set.
566 *
567 * This function is threadsafe, so that you can e.g. use GTask
568 * and [method@Gio.Task.run_in_thread] to avoid blocking the main thread
569 * while loading a big image.
570 *
571 * Return value: A newly-created `GdkTexture`
572 *
573 * Since: 4.6
574 */
575GdkTexture *
576gdk_texture_new_from_bytes (GBytes *bytes,
577 GError **error)
578{
579 GdkTexture *texture;
580 GError *internal_error = NULL;
581
582 g_return_val_if_fail (bytes != NULL, NULL);
583 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
584
585 texture = gdk_texture_new_from_bytes_internal (bytes, error: &internal_error);
586 if (texture)
587 return texture;
588
589 if (!g_error_matches (error: internal_error, GDK_TEXTURE_ERROR, code: GDK_TEXTURE_ERROR_UNSUPPORTED_CONTENT) &&
590 !g_error_matches (error: internal_error, GDK_TEXTURE_ERROR, code: GDK_TEXTURE_ERROR_UNSUPPORTED_FORMAT))
591 {
592 g_propagate_error (dest: error, src: internal_error);
593 return NULL;
594 }
595
596 g_clear_error (err: &internal_error);
597
598 return gdk_texture_new_from_bytes_pixbuf (bytes, error);
599}
600
601/**
602 * gdk_texture_new_from_filename:
603 * @path: (type filename): the filename to load
604 * @error: Return location for an error
605 *
606 * Creates a new texture by loading an image from a file.
607 *
608 * The file format is detected automatically. The supported formats
609 * are PNG and JPEG, though more formats might be available.
610 *
611 * If %NULL is returned, then @error will be set.
612 *
613 * This function is threadsafe, so that you can e.g. use GTask
614 * and [method@Gio.Task.run_in_thread] to avoid blocking the main thread
615 * while loading a big image.
616 *
617 * Return value: A newly-created `GdkTexture`
618 *
619 * Since: 4.6
620 */
621GdkTexture *
622gdk_texture_new_from_filename (const char *path,
623 GError **error)
624{
625 GdkTexture *texture;
626 GFile *file;
627
628 g_return_val_if_fail (path, NULL);
629 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
630
631 file = g_file_new_for_path (path);
632
633 texture = gdk_texture_new_from_file (file, error);
634
635 g_object_unref (object: file);
636
637 return texture;
638}
639
640/**
641 * gdk_texture_get_width: (attributes org.gtk.Method.get_property=width)
642 * @texture: a `GdkTexture`
643 *
644 * Returns the width of @texture, in pixels.
645 *
646 * Returns: the width of the `GdkTexture`
647 */
648int
649gdk_texture_get_width (GdkTexture *texture)
650{
651 g_return_val_if_fail (GDK_IS_TEXTURE (texture), 0);
652
653 return texture->width;
654}
655
656/**
657 * gdk_texture_get_height: (attributes org.gtk.Method.get_property=height)
658 * @texture: a `GdkTexture`
659 *
660 * Returns the height of the @texture, in pixels.
661 *
662 * Returns: the height of the `GdkTexture`
663 */
664int
665gdk_texture_get_height (GdkTexture *texture)
666{
667 g_return_val_if_fail (GDK_IS_TEXTURE (texture), 0);
668
669 return texture->height;
670}
671
672void
673gdk_texture_do_download (GdkTexture *texture,
674 GdkMemoryFormat format,
675 guchar *data,
676 gsize stride)
677{
678 GDK_TEXTURE_GET_CLASS (texture)->download (texture, format, data,stride);
679}
680
681cairo_surface_t *
682gdk_texture_download_surface (GdkTexture *texture)
683{
684 cairo_surface_t *surface;
685 cairo_status_t surface_status;
686
687 surface = cairo_image_surface_create (format: CAIRO_FORMAT_ARGB32,
688 width: texture->width, height: texture->height);
689
690 surface_status = cairo_surface_status (surface);
691 if (surface_status != CAIRO_STATUS_SUCCESS)
692 g_warning ("%s: surface error: %s", __FUNCTION__,
693 cairo_status_to_string (surface_status));
694
695 gdk_texture_download (texture,
696 data: cairo_image_surface_get_data (surface),
697 stride: cairo_image_surface_get_stride (surface));
698 cairo_surface_mark_dirty (surface);
699
700 return surface;
701}
702
703/**
704 * gdk_texture_download:
705 * @texture: a `GdkTexture`
706 * @data: (array): pointer to enough memory to be filled with the
707 * downloaded data of @texture
708 * @stride: rowstride in bytes
709 *
710 * Downloads the @texture into local memory.
711 *
712 * This may be an expensive operation, as the actual texture data
713 * may reside on a GPU or on a remote display server.
714 *
715 * The data format of the downloaded data is equivalent to
716 * %CAIRO_FORMAT_ARGB32, so every downloaded pixel requires
717 * 4 bytes of memory.
718 *
719 * Downloading a texture into a Cairo image surface:
720 * ```c
721 * surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
722 * gdk_texture_get_width (texture),
723 * gdk_texture_get_height (texture));
724 * gdk_texture_download (texture,
725 * cairo_image_surface_get_data (surface),
726 * cairo_image_surface_get_stride (surface));
727 * cairo_surface_mark_dirty (surface);
728 * ```
729 */
730void
731gdk_texture_download (GdkTexture *texture,
732 guchar *data,
733 gsize stride)
734{
735 g_return_if_fail (GDK_IS_TEXTURE (texture));
736 g_return_if_fail (data != NULL);
737 g_return_if_fail (stride >= gdk_texture_get_width (texture) * 4);
738
739 gdk_texture_do_download (texture,
740 GDK_MEMORY_DEFAULT,
741 data,
742 stride);
743}
744
745GdkMemoryFormat
746gdk_texture_get_format (GdkTexture *self)
747{
748 return self->format;
749}
750
751gboolean
752gdk_texture_set_render_data (GdkTexture *self,
753 gpointer key,
754 gpointer data,
755 GDestroyNotify notify)
756{
757 g_return_val_if_fail (data != NULL, FALSE);
758
759 if (self->render_key != NULL)
760 return FALSE;
761
762 self->render_key = key;
763 self->render_data = data;
764 self->render_notify = notify;
765
766 return TRUE;
767}
768
769void
770gdk_texture_clear_render_data (GdkTexture *self)
771{
772 if (self->render_notify)
773 self->render_notify (self->render_data);
774
775 self->render_key = NULL;
776 self->render_data = NULL;
777 self->render_notify = NULL;
778}
779
780gpointer
781gdk_texture_get_render_data (GdkTexture *self,
782 gpointer key)
783{
784 if (self->render_key != key)
785 return NULL;
786
787 return self->render_data;
788}
789
790/**
791 * gdk_texture_save_to_png:
792 * @texture: a `GdkTexture`
793 * @filename: (type filename): the filename to store to
794 *
795 * Store the given @texture to the @filename as a PNG file.
796 *
797 * This is a utility function intended for debugging and testing.
798 * If you want more control over formats, proper error handling or
799 * want to store to a [iface@Gio.File] or other location, you might want to
800 * use [method@Gdk.Texture.save_to_png_bytes] or look into the
801 * gdk-pixbuf library.
802 *
803 * Returns: %TRUE if saving succeeded, %FALSE on failure.
804 */
805gboolean
806gdk_texture_save_to_png (GdkTexture *texture,
807 const char *filename)
808{
809 GBytes *bytes;
810 gboolean result;
811
812 g_return_val_if_fail (GDK_IS_TEXTURE (texture), FALSE);
813 g_return_val_if_fail (filename != NULL, FALSE);
814
815 bytes = gdk_save_png (texture);
816 result = g_file_set_contents (filename,
817 contents: g_bytes_get_data (bytes, NULL),
818 length: g_bytes_get_size (bytes),
819 NULL);
820 g_bytes_unref (bytes);
821
822 return result;
823}
824
825/**
826 * gdk_texture_save_to_png_bytes:
827 * @texture: a `GdkTexture`
828 *
829 * Store the given @texture in memory as a PNG file.
830 *
831 * Use [ctor@Gdk.Texture.new_from_bytes] to read it back.
832 *
833 * If you want to serialize a texture, this is a convenient and
834 * portable way to do that.
835 *
836 * If you need more control over the generated image, such as
837 * attaching metadata, you should look into an image handling
838 * library such as the gdk-pixbuf library.
839 *
840 * If you are dealing with high dynamic range float data, you
841 * might also want to consider [method@Gdk.Texture.save_to_tiff_bytes]
842 * instead.
843 *
844 * Returns: a newly allocated `GBytes` containing PNG data
845 *
846 * Since: 4.6
847 */
848GBytes *
849gdk_texture_save_to_png_bytes (GdkTexture *texture)
850{
851 g_return_val_if_fail (GDK_IS_TEXTURE (texture), NULL);
852
853 return gdk_save_png (texture);
854}
855
856/**
857 * gdk_texture_save_to_tiff:
858 * @texture: a `GdkTexture`
859 * @filename: (type filename): the filename to store to
860 *
861 * Store the given @texture to the @filename as a TIFF file.
862 *
863 * GTK will attempt to store data without loss.
864 * Returns: %TRUE if saving succeeded, %FALSE on failure.
865 *
866 * Since: 4.6
867 */
868gboolean
869gdk_texture_save_to_tiff (GdkTexture *texture,
870 const char *filename)
871{
872 GBytes *bytes;
873 gboolean result;
874
875 g_return_val_if_fail (GDK_IS_TEXTURE (texture), FALSE);
876 g_return_val_if_fail (filename != NULL, FALSE);
877
878 bytes = gdk_save_tiff (texture);
879 result = g_file_set_contents (filename,
880 contents: g_bytes_get_data (bytes, NULL),
881 length: g_bytes_get_size (bytes),
882 NULL);
883 g_bytes_unref (bytes);
884
885 return result;
886}
887
888/**
889 * gdk_texture_save_to_tiff_bytes:
890 * @texture: a `GdkTexture`
891 *
892 * Store the given @texture in memory as a TIFF file.
893 *
894 * Use [ctor@Gdk.Texture.new_from_bytes] to read it back.
895 *
896 * This function is intended to store a representation of the
897 * texture's data that is as accurate as possible. This is
898 * particularly relevant when working with high dynamic range
899 * images and floating-point texture data.
900 *
901 * If that is not your concern and you are interested in a
902 * smaller size and a more portable format, you might want to
903 * use [method@Gdk.Texture.save_to_png_bytes].
904 *
905 * Returns: a newly allocated `GBytes` containing TIFF data
906 *
907 * Since: 4.6
908 */
909GBytes *
910gdk_texture_save_to_tiff_bytes (GdkTexture *texture)
911{
912 g_return_val_if_fail (GDK_IS_TEXTURE (texture), NULL);
913
914 return gdk_save_tiff (texture);
915}
916

source code of gtk/gdk/gdktexture.c