1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ |
2 | /* GdkPixbuf library - Basic memory management |
3 | * |
4 | * Copyright (C) 1999 The Free Software Foundation |
5 | * |
6 | * Authors: Mark Crichton <crichton@gimp.org> |
7 | * Miguel de Icaza <miguel@gnu.org> |
8 | * Federico Mena-Quintero <federico@gimp.org> |
9 | * |
10 | * This library is free software; you can redistribute it and/or |
11 | * modify it under the terms of the GNU Lesser General Public |
12 | * License as published by the Free Software Foundation; either |
13 | * version 2 of the License, or (at your option) any later version. |
14 | * |
15 | * This library is distributed in the hope that it will be useful, |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
18 | * Lesser General Public License for more details. |
19 | * |
20 | * You should have received a copy of the GNU Lesser General Public |
21 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
22 | */ |
23 | |
24 | /** |
25 | * GdkPixbuf: |
26 | * |
27 | * A pixel buffer. |
28 | * |
29 | * `GdkPixbuf` contains information about an image's pixel data, |
30 | * its color space, bits per sample, width and height, and the |
31 | * rowstride (the number of bytes between the start of one row |
32 | * and the start of the next). |
33 | * |
34 | * ## Creating new `GdkPixbuf` |
35 | * |
36 | * The most basic way to create a pixbuf is to wrap an existing pixel |
37 | * buffer with a [class@GdkPixbuf.Pixbuf] instance. You can use the |
38 | * [`ctor@GdkPixbuf.Pixbuf.new_from_data`] function to do this. |
39 | * |
40 | * Every time you create a new `GdkPixbuf` instance for some data, you |
41 | * will need to specify the destroy notification function that will be |
42 | * called when the data buffer needs to be freed; this will happen when |
43 | * a `GdkPixbuf` is finalized by the reference counting functions. If |
44 | * you have a chunk of static data compiled into your application, you |
45 | * can pass in `NULL` as the destroy notification function so that the |
46 | * data will not be freed. |
47 | * |
48 | * The [`ctor@GdkPixbuf.Pixbuf.new`] constructor function can be used |
49 | * as a convenience to create a pixbuf with an empty buffer; this is |
50 | * equivalent to allocating a data buffer using `malloc()` and then |
51 | * wrapping it with `gdk_pixbuf_new_from_data()`. The `gdk_pixbuf_new()` |
52 | * function will compute an optimal rowstride so that rendering can be |
53 | * performed with an efficient algorithm. |
54 | * |
55 | * As a special case, you can use the [`ctor@GdkPixbuf.Pixbuf.new_from_xpm_data`] |
56 | * function to create a pixbuf from inline XPM image data. |
57 | * |
58 | * You can also copy an existing pixbuf with the [method@Pixbuf.copy] |
59 | * function. This is not the same as just acquiring a reference to |
60 | * the old pixbuf instance: the copy function will actually duplicate |
61 | * the pixel data in memory and create a new [class@Pixbuf] instance |
62 | * for it. |
63 | * |
64 | * ## Reference counting |
65 | * |
66 | * `GdkPixbuf` structures are reference counted. This means that an |
67 | * application can share a single pixbuf among many parts of the |
68 | * code. When a piece of the program needs to use a pixbuf, it should |
69 | * acquire a reference to it by calling `g_object_ref()`; when it no |
70 | * longer needs the pixbuf, it should release the reference it acquired |
71 | * by calling `g_object_unref()`. The resources associated with a |
72 | * `GdkPixbuf` will be freed when its reference count drops to zero. |
73 | * Newly-created `GdkPixbuf` instances start with a reference count |
74 | * of one. |
75 | * |
76 | * ## Image Data |
77 | * |
78 | * Image data in a pixbuf is stored in memory in an uncompressed, |
79 | * packed format. Rows in the image are stored top to bottom, and |
80 | * in each row pixels are stored from left to right. |
81 | * |
82 | * There may be padding at the end of a row. |
83 | * |
84 | * The "rowstride" value of a pixbuf, as returned by [`method@GdkPixbuf.Pixbuf.get_rowstride`], |
85 | * indicates the number of bytes between rows. |
86 | * |
87 | * **NOTE**: If you are copying raw pixbuf data with `memcpy()` note that the |
88 | * last row in the pixbuf may not be as wide as the full rowstride, but rather |
89 | * just as wide as the pixel data needs to be; that is: it is unsafe to do |
90 | * `memcpy (dest, pixels, rowstride * height)` to copy a whole pixbuf. Use |
91 | * [method@GdkPixbuf.Pixbuf.copy] instead, or compute the width in bytes of the |
92 | * last row as: |
93 | * |
94 | * ```c |
95 | * last_row = width * ((n_channels * bits_per_sample + 7) / 8); |
96 | * ``` |
97 | * |
98 | * The same rule applies when iterating over each row of a `GdkPixbuf` pixels |
99 | * array. |
100 | * |
101 | * The following code illustrates a simple `put_pixel()` |
102 | * function for RGB pixbufs with 8 bits per channel with an alpha |
103 | * channel. |
104 | * |
105 | * ```c |
106 | * static void |
107 | * put_pixel (GdkPixbuf *pixbuf, |
108 | * int x, |
109 | * int y, |
110 | * guchar red, |
111 | * guchar green, |
112 | * guchar blue, |
113 | * guchar alpha) |
114 | * { |
115 | * int n_channels = gdk_pixbuf_get_n_channels (pixbuf); |
116 | * |
117 | * // Ensure that the pixbuf is valid |
118 | * g_assert (gdk_pixbuf_get_colorspace (pixbuf) == GDK_COLORSPACE_RGB); |
119 | * g_assert (gdk_pixbuf_get_bits_per_sample (pixbuf) == 8); |
120 | * g_assert (gdk_pixbuf_get_has_alpha (pixbuf)); |
121 | * g_assert (n_channels == 4); |
122 | * |
123 | * int width = gdk_pixbuf_get_width (pixbuf); |
124 | * int height = gdk_pixbuf_get_height (pixbuf); |
125 | * |
126 | * // Ensure that the coordinates are in a valid range |
127 | * g_assert (x >= 0 && x < width); |
128 | * g_assert (y >= 0 && y < height); |
129 | * |
130 | * int rowstride = gdk_pixbuf_get_rowstride (pixbuf); |
131 | * |
132 | * // The pixel buffer in the GdkPixbuf instance |
133 | * guchar *pixels = gdk_pixbuf_get_pixels (pixbuf); |
134 | * |
135 | * // The pixel we wish to modify |
136 | * guchar *p = pixels + y * rowstride + x * n_channels; |
137 | * p[0] = red; |
138 | * p[1] = green; |
139 | * p[2] = blue; |
140 | * p[3] = alpha; |
141 | * } |
142 | * ``` |
143 | * |
144 | * ## Loading images |
145 | * |
146 | * The `GdkPixBuf` class provides a simple mechanism for loading |
147 | * an image from a file in synchronous and asynchronous fashion. |
148 | * |
149 | * For GUI applications, it is recommended to use the asynchronous |
150 | * stream API to avoid blocking the control flow of the application. |
151 | * |
152 | * Additionally, `GdkPixbuf` provides the [class@GdkPixbuf.PixbufLoader`] |
153 | * API for progressive image loading. |
154 | * |
155 | * ## Saving images |
156 | * |
157 | * The `GdkPixbuf` class provides methods for saving image data in |
158 | * a number of file formats. The formatted data can be written to a |
159 | * file or to a memory buffer. `GdkPixbuf` can also call a user-defined |
160 | * callback on the data, which allows to e.g. write the image |
161 | * to a socket or store it in a database. |
162 | */ |
163 | |
164 | #include "config.h" |
165 | |
166 | #include <math.h> |
167 | #include <stdlib.h> |
168 | #include <string.h> |
169 | |
170 | #define GDK_PIXBUF_C_COMPILATION |
171 | #include "gdk-pixbuf-private.h" |
172 | #include "gdk-pixbuf-features.h" |
173 | #include "gdk-pixbuf-enum-types.h" |
174 | |
175 | /* Include the marshallers */ |
176 | #include <glib-object.h> |
177 | #include <gio/gio.h> |
178 | #include "gdk-pixbuf-marshal.h" |
179 | |
180 | static void gdk_pixbuf_finalize (GObject *object); |
181 | static void gdk_pixbuf_set_property (GObject *object, |
182 | guint prop_id, |
183 | const GValue *value, |
184 | GParamSpec *pspec); |
185 | static void gdk_pixbuf_get_property (GObject *object, |
186 | guint prop_id, |
187 | GValue *value, |
188 | GParamSpec *pspec); |
189 | static void gdk_pixbuf_constructed (GObject *object); |
190 | |
191 | |
192 | enum |
193 | { |
194 | PROP_0, |
195 | PROP_COLORSPACE, |
196 | PROP_N_CHANNELS, |
197 | PROP_HAS_ALPHA, |
198 | PROP_BITS_PER_SAMPLE, |
199 | PROP_WIDTH, |
200 | PROP_HEIGHT, |
201 | PROP_ROWSTRIDE, |
202 | PROP_PIXELS, |
203 | PROP_PIXEL_BYTES |
204 | }; |
205 | |
206 | static void gdk_pixbuf_icon_iface_init (GIconIface *iface); |
207 | static void gdk_pixbuf_loadable_icon_iface_init (GLoadableIconIface *iface); |
208 | |
209 | G_DEFINE_TYPE_WITH_CODE (GdkPixbuf, gdk_pixbuf, G_TYPE_OBJECT, |
210 | G_IMPLEMENT_INTERFACE (G_TYPE_ICON, gdk_pixbuf_icon_iface_init) |
211 | G_IMPLEMENT_INTERFACE (G_TYPE_LOADABLE_ICON, gdk_pixbuf_loadable_icon_iface_init)) |
212 | |
213 | static void |
214 | gdk_pixbuf_init (GdkPixbuf *pixbuf) |
215 | { |
216 | pixbuf->colorspace = GDK_COLORSPACE_RGB; |
217 | pixbuf->n_channels = 3; |
218 | pixbuf->bits_per_sample = 8; |
219 | pixbuf->has_alpha = FALSE; |
220 | pixbuf->storage = STORAGE_UNINITIALIZED; |
221 | } |
222 | |
223 | static void |
224 | gdk_pixbuf_class_init (GdkPixbufClass *klass) |
225 | { |
226 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
227 | |
228 | _gdk_pixbuf_init_gettext (); |
229 | |
230 | object_class->finalize = gdk_pixbuf_finalize; |
231 | object_class->set_property = gdk_pixbuf_set_property; |
232 | object_class->get_property = gdk_pixbuf_get_property; |
233 | object_class->constructed = gdk_pixbuf_constructed; |
234 | |
235 | #define PIXBUF_PARAM_FLAGS G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|\ |
236 | G_PARAM_EXPLICIT_NOTIFY|\ |
237 | G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB |
238 | /** |
239 | * GdkPixbuf:n-channels: |
240 | * |
241 | * The number of samples per pixel. |
242 | * |
243 | * Currently, only 3 or 4 samples per pixel are supported. |
244 | */ |
245 | g_object_class_install_property (oclass: object_class, |
246 | property_id: PROP_N_CHANNELS, |
247 | pspec: g_param_spec_int (name: "n-channels" , |
248 | _("Number of Channels" ), |
249 | _("The number of samples per pixel" ), |
250 | minimum: 0, |
251 | G_MAXINT, |
252 | default_value: 3, |
253 | PIXBUF_PARAM_FLAGS)); |
254 | /** |
255 | * GdkPixbuf:colorspace: |
256 | * |
257 | * The color space of the pixbuf. |
258 | * |
259 | * Currently, only `GDK_COLORSPACE_RGB` is supported. |
260 | */ |
261 | g_object_class_install_property (oclass: object_class, |
262 | property_id: PROP_COLORSPACE, |
263 | pspec: g_param_spec_enum (name: "colorspace" , |
264 | _("Colorspace" ), |
265 | _("The colorspace in which the samples are interpreted" ), |
266 | enum_type: GDK_TYPE_COLORSPACE, |
267 | default_value: GDK_COLORSPACE_RGB, |
268 | PIXBUF_PARAM_FLAGS)); |
269 | /** |
270 | * GdkPixbuf:has-alpha: |
271 | * |
272 | * Whether the pixbuf has an alpha channel. |
273 | */ |
274 | g_object_class_install_property (oclass: object_class, |
275 | property_id: PROP_HAS_ALPHA, |
276 | pspec: g_param_spec_boolean (name: "has-alpha" , |
277 | _("Has Alpha" ), |
278 | _("Whether the pixbuf has an alpha channel" ), |
279 | FALSE, |
280 | PIXBUF_PARAM_FLAGS)); |
281 | /** |
282 | * GdkPixbuf:bits-per-sample: |
283 | * |
284 | * The number of bits per sample. |
285 | * |
286 | * Currently only 8 bit per sample are supported. |
287 | */ |
288 | g_object_class_install_property (oclass: object_class, |
289 | property_id: PROP_BITS_PER_SAMPLE, |
290 | pspec: g_param_spec_int (name: "bits-per-sample" , |
291 | _("Bits per Sample" ), |
292 | _("The number of bits per sample" ), |
293 | minimum: 1, |
294 | maximum: 16, |
295 | default_value: 8, |
296 | PIXBUF_PARAM_FLAGS)); |
297 | /** |
298 | * GdkPixbuf:width: |
299 | * |
300 | * The number of columns of the pixbuf. |
301 | */ |
302 | g_object_class_install_property (oclass: object_class, |
303 | property_id: PROP_WIDTH, |
304 | pspec: g_param_spec_int (name: "width" , |
305 | _("Width" ), |
306 | _("The number of columns of the pixbuf" ), |
307 | minimum: 1, |
308 | G_MAXINT, |
309 | default_value: 1, |
310 | PIXBUF_PARAM_FLAGS)); |
311 | /** |
312 | * GdkPixbuf:height: |
313 | * |
314 | * The number of rows of the pixbuf. |
315 | */ |
316 | g_object_class_install_property (oclass: object_class, |
317 | property_id: PROP_HEIGHT, |
318 | pspec: g_param_spec_int (name: "height" , |
319 | _("Height" ), |
320 | _("The number of rows of the pixbuf" ), |
321 | minimum: 1, |
322 | G_MAXINT, |
323 | default_value: 1, |
324 | PIXBUF_PARAM_FLAGS)); |
325 | /** |
326 | * GdkPixbuf:rowstride: |
327 | * |
328 | * The number of bytes between the start of a row and |
329 | * the start of the next row. |
330 | * |
331 | * This number must (obviously) be at least as large as the |
332 | * width of the pixbuf. |
333 | */ |
334 | g_object_class_install_property (oclass: object_class, |
335 | property_id: PROP_ROWSTRIDE, |
336 | pspec: g_param_spec_int (name: "rowstride" , |
337 | _("Rowstride" ), |
338 | _("The number of bytes between the start of a row and the start of the next row" ), |
339 | minimum: 1, |
340 | G_MAXINT, |
341 | default_value: 1, |
342 | PIXBUF_PARAM_FLAGS)); |
343 | /** |
344 | * GdkPixbuf:pixels: |
345 | * |
346 | * A pointer to the pixel data of the pixbuf. |
347 | */ |
348 | g_object_class_install_property (oclass: object_class, |
349 | property_id: PROP_PIXELS, |
350 | pspec: g_param_spec_pointer (name: "pixels" , |
351 | _("Pixels" ), |
352 | _("A pointer to the pixel data of the pixbuf" ), |
353 | PIXBUF_PARAM_FLAGS)); |
354 | /** |
355 | * GdkPixbuf::pixel-bytes: |
356 | * |
357 | * If set, this pixbuf was created from read-only #GBytes. |
358 | * |
359 | * Replaces GdkPixbuf::pixels. |
360 | * |
361 | * Since: 2.32 |
362 | */ |
363 | g_object_class_install_property (oclass: object_class, |
364 | property_id: PROP_PIXEL_BYTES, |
365 | pspec: g_param_spec_boxed (name: "pixel-bytes" , |
366 | _("Pixel Bytes" ), |
367 | _("Readonly pixel data" ), |
368 | G_TYPE_BYTES, |
369 | PIXBUF_PARAM_FLAGS)); |
370 | } |
371 | |
372 | static void |
373 | free_pixels (GdkPixbuf *pixbuf) |
374 | { |
375 | g_assert (pixbuf->storage == STORAGE_PIXELS); |
376 | |
377 | if (pixbuf->s.pixels.pixels && pixbuf->s.pixels.destroy_fn) { |
378 | (* pixbuf->s.pixels.destroy_fn) (pixbuf->s.pixels.pixels, pixbuf->s.pixels.destroy_fn_data); |
379 | } |
380 | |
381 | pixbuf->s.pixels.pixels = NULL; |
382 | } |
383 | |
384 | static void |
385 | free_bytes (GdkPixbuf *pixbuf) |
386 | { |
387 | g_assert (pixbuf->storage == STORAGE_BYTES); |
388 | |
389 | g_clear_pointer (&pixbuf->s.bytes.bytes, g_bytes_unref); |
390 | } |
391 | |
392 | static void |
393 | gdk_pixbuf_finalize (GObject *object) |
394 | { |
395 | GdkPixbuf *pixbuf = GDK_PIXBUF (object); |
396 | |
397 | switch (pixbuf->storage) { |
398 | case STORAGE_PIXELS: |
399 | free_pixels (pixbuf); |
400 | break; |
401 | |
402 | case STORAGE_BYTES: |
403 | free_bytes (pixbuf); |
404 | break; |
405 | |
406 | default: |
407 | g_assert_not_reached (); |
408 | } |
409 | |
410 | G_OBJECT_CLASS (gdk_pixbuf_parent_class)->finalize (object); |
411 | } |
412 | |
413 | |
414 | /** |
415 | * gdk_pixbuf_ref: (skip) |
416 | * @pixbuf: A pixbuf. |
417 | * |
418 | * Adds a reference to a pixbuf. |
419 | * |
420 | * Return value: The same as the @pixbuf argument. |
421 | * |
422 | * Deprecated: 2.0: Use g_object_ref(). |
423 | **/ |
424 | GdkPixbuf * |
425 | gdk_pixbuf_ref (GdkPixbuf *pixbuf) |
426 | { |
427 | return (GdkPixbuf *) g_object_ref (pixbuf); |
428 | } |
429 | |
430 | /** |
431 | * gdk_pixbuf_unref: (skip) |
432 | * @pixbuf: A pixbuf. |
433 | * |
434 | * Removes a reference from a pixbuf. |
435 | * |
436 | * Deprecated: 2.0: Use g_object_unref(). |
437 | **/ |
438 | void |
439 | gdk_pixbuf_unref (GdkPixbuf *pixbuf) |
440 | { |
441 | g_object_unref (object: pixbuf); |
442 | } |
443 | |
444 | static GBytes * |
445 | gdk_pixbuf_make_bytes (GdkPixbuf *pixbuf, |
446 | GError **error) |
447 | { |
448 | gchar *buffer; |
449 | gsize size; |
450 | |
451 | if (!gdk_pixbuf_save_to_buffer (pixbuf, buffer: &buffer, buffer_size: &size, type: "png" , error, NULL)) |
452 | return NULL; |
453 | |
454 | return g_bytes_new_take (data: buffer, size); |
455 | } |
456 | |
457 | static GVariant * |
458 | gdk_pixbuf_serialize (GIcon *icon) |
459 | { |
460 | GError *error = NULL; |
461 | GVariant *result; |
462 | GBytes *bytes; |
463 | |
464 | bytes = gdk_pixbuf_make_bytes (GDK_PIXBUF (icon), error: &error); |
465 | if (!bytes) |
466 | { |
467 | g_critical ("Unable to serialise GdkPixbuf to png (via g_icon_serialize()): %s" , error->message); |
468 | g_error_free (error); |
469 | return NULL; |
470 | } |
471 | result = g_variant_new_from_bytes (G_VARIANT_TYPE_BYTESTRING, bytes, TRUE); |
472 | g_bytes_unref (bytes); |
473 | |
474 | return g_variant_new (format_string: "(sv)" , "bytes" , result); |
475 | } |
476 | |
477 | static GInputStream * |
478 | gdk_pixbuf_load (GLoadableIcon *icon, |
479 | int size, |
480 | char **type, |
481 | GCancellable *cancellable, |
482 | GError **error) |
483 | { |
484 | GInputStream *stream; |
485 | GBytes *bytes; |
486 | |
487 | bytes = gdk_pixbuf_make_bytes (GDK_PIXBUF (icon), error); |
488 | if (!bytes) |
489 | return NULL; |
490 | |
491 | stream = g_memory_input_stream_new_from_bytes (bytes); |
492 | g_bytes_unref (bytes); |
493 | |
494 | if (type) |
495 | *type = g_strdup (str: "image/png" ); |
496 | |
497 | return stream; |
498 | } |
499 | |
500 | static void |
501 | gdk_pixbuf_load_async (GLoadableIcon *icon, |
502 | int size, |
503 | GCancellable *cancellable, |
504 | GAsyncReadyCallback callback, |
505 | gpointer user_data) |
506 | { |
507 | GTask *task; |
508 | |
509 | task = g_task_new (source_object: icon, cancellable, callback, callback_data: user_data); |
510 | g_task_return_pointer (task, result: icon, NULL); |
511 | g_object_unref (object: task); |
512 | } |
513 | |
514 | static GInputStream * |
515 | gdk_pixbuf_load_finish (GLoadableIcon *icon, |
516 | GAsyncResult *res, |
517 | char **type, |
518 | GError **error) |
519 | { |
520 | g_return_val_if_fail (g_task_is_valid (res, icon), NULL); |
521 | |
522 | if (!g_task_propagate_pointer (G_TASK (res), error)) |
523 | return NULL; |
524 | |
525 | return gdk_pixbuf_load (icon, size: 0, type, NULL, error); |
526 | } |
527 | |
528 | static void |
529 | gdk_pixbuf_loadable_icon_iface_init (GLoadableIconIface *iface) |
530 | { |
531 | iface->load = gdk_pixbuf_load; |
532 | |
533 | /* In theory encoding a png could be time-consuming but we're talking |
534 | * about icons here, so assume it's probably going to be OK and handle |
535 | * the async variant of the call in-thread instead of having the |
536 | * default implementation dispatch it to a worker. |
537 | */ |
538 | iface->load_async = gdk_pixbuf_load_async; |
539 | iface->load_finish = gdk_pixbuf_load_finish; |
540 | } |
541 | |
542 | static void |
543 | gdk_pixbuf_icon_iface_init (GIconIface *iface) |
544 | { |
545 | iface->hash = (guint (*) (GIcon *)) g_direct_hash; |
546 | iface->equal = (gboolean (*) (GIcon *, GIcon *)) g_direct_equal; |
547 | iface->serialize = gdk_pixbuf_serialize; |
548 | } |
549 | |
550 | /* Used as the destroy notification function for gdk_pixbuf_new() */ |
551 | static void |
552 | free_buffer (guchar *pixels, gpointer data) |
553 | { |
554 | g_free (mem: pixels); |
555 | } |
556 | |
557 | /** |
558 | * gdk_pixbuf_calculate_rowstride: |
559 | * @colorspace: Color space for image |
560 | * @has_alpha: Whether the image should have transparency information |
561 | * @bits_per_sample: Number of bits per color sample |
562 | * @width: Width of image in pixels, must be > 0 |
563 | * @height: Height of image in pixels, must be > 0 |
564 | * |
565 | * Calculates the rowstride that an image created with those values would |
566 | * have. |
567 | * |
568 | * This function is useful for front-ends and backends that want to check |
569 | * image values without needing to create a `GdkPixbuf`. |
570 | * |
571 | * Return value: the rowstride for the given values, or -1 in case of error. |
572 | * |
573 | * Since: 2.36.8 |
574 | */ |
575 | gint |
576 | gdk_pixbuf_calculate_rowstride (GdkColorspace colorspace, |
577 | gboolean has_alpha, |
578 | int bits_per_sample, |
579 | int width, |
580 | int height) |
581 | { |
582 | unsigned int channels; |
583 | |
584 | g_return_val_if_fail (colorspace == GDK_COLORSPACE_RGB, -1); |
585 | g_return_val_if_fail (bits_per_sample == 8, -1); |
586 | g_return_val_if_fail (width > 0, -1); |
587 | g_return_val_if_fail (height > 0, -1); |
588 | |
589 | channels = has_alpha ? 4 : 3; |
590 | |
591 | /* Overflow? */ |
592 | if (width > (G_MAXINT - 3) / channels) |
593 | return -1; |
594 | |
595 | /* Always align rows to 32-bit boundaries */ |
596 | return (width * channels + 3) & ~3; |
597 | } |
598 | |
599 | /** |
600 | * gdk_pixbuf_new: |
601 | * @colorspace: Color space for image |
602 | * @has_alpha: Whether the image should have transparency information |
603 | * @bits_per_sample: Number of bits per color sample |
604 | * @width: Width of image in pixels, must be > 0 |
605 | * @height: Height of image in pixels, must be > 0 |
606 | * |
607 | * Creates a new `GdkPixbuf` structure and allocates a buffer for it. |
608 | * |
609 | * If the allocation of the buffer failed, this function will return `NULL`. |
610 | * |
611 | * The buffer has an optimal rowstride. Note that the buffer is not cleared; |
612 | * you will have to fill it completely yourself. |
613 | * |
614 | * Return value: (transfer full) (nullable): A newly-created pixel buffer |
615 | **/ |
616 | GdkPixbuf * |
617 | gdk_pixbuf_new (GdkColorspace colorspace, |
618 | gboolean has_alpha, |
619 | int bits_per_sample, |
620 | int width, |
621 | int height) |
622 | { |
623 | guchar *buf; |
624 | int rowstride; |
625 | |
626 | rowstride = gdk_pixbuf_calculate_rowstride (colorspace, |
627 | has_alpha, |
628 | bits_per_sample, |
629 | width, |
630 | height); |
631 | if (rowstride <= 0) |
632 | return NULL; |
633 | |
634 | buf = g_try_malloc0_n (n_blocks: height, n_block_bytes: rowstride); |
635 | if (!buf) |
636 | return NULL; |
637 | |
638 | return gdk_pixbuf_new_from_data (data: buf, colorspace, has_alpha, bits_per_sample, |
639 | width, height, rowstride, |
640 | destroy_fn: free_buffer, NULL); |
641 | } |
642 | |
643 | /** |
644 | * gdk_pixbuf_copy: |
645 | * @pixbuf: A pixbuf. |
646 | * |
647 | * Creates a new `GdkPixbuf` with a copy of the information in the specified |
648 | * `pixbuf`. |
649 | * |
650 | * Note that this does not copy the options set on the original `GdkPixbuf`, |
651 | * use gdk_pixbuf_copy_options() for this. |
652 | * |
653 | * Return value: (nullable) (transfer full): A newly-created pixbuf |
654 | **/ |
655 | GdkPixbuf * |
656 | gdk_pixbuf_copy (const GdkPixbuf *pixbuf) |
657 | { |
658 | guchar *buf; |
659 | int size; |
660 | |
661 | g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL); |
662 | |
663 | /* Calculate a semi-exact size. Here we copy with full rowstrides; |
664 | * maybe we should copy each row individually with the minimum |
665 | * rowstride? |
666 | */ |
667 | |
668 | size = gdk_pixbuf_get_byte_length (pixbuf); |
669 | |
670 | buf = g_try_malloc (n_bytes: size); |
671 | if (!buf) |
672 | return NULL; |
673 | |
674 | memcpy (dest: buf, src: gdk_pixbuf_read_pixels (pixbuf), n: size); |
675 | |
676 | return gdk_pixbuf_new_from_data (data: buf, |
677 | colorspace: pixbuf->colorspace, has_alpha: pixbuf->has_alpha, |
678 | bits_per_sample: pixbuf->bits_per_sample, |
679 | width: pixbuf->width, height: pixbuf->height, |
680 | rowstride: pixbuf->rowstride, |
681 | destroy_fn: free_buffer, |
682 | NULL); |
683 | } |
684 | |
685 | /** |
686 | * gdk_pixbuf_new_subpixbuf: |
687 | * @src_pixbuf: a `GdkPixbuf` |
688 | * @src_x: X coord in @src_pixbuf |
689 | * @src_y: Y coord in @src_pixbuf |
690 | * @width: width of region in @src_pixbuf |
691 | * @height: height of region in @src_pixbuf |
692 | * |
693 | * Creates a new pixbuf which represents a sub-region of `src_pixbuf`. |
694 | * |
695 | * The new pixbuf shares its pixels with the original pixbuf, so |
696 | * writing to one affects both. The new pixbuf holds a reference to |
697 | * `src_pixbuf`, so `src_pixbuf` will not be finalized until the new |
698 | * pixbuf is finalized. |
699 | * |
700 | * Note that if `src_pixbuf` is read-only, this function will force it |
701 | * to be mutable. |
702 | * |
703 | * Return value: (transfer full): a new pixbuf |
704 | **/ |
705 | GdkPixbuf* |
706 | gdk_pixbuf_new_subpixbuf (GdkPixbuf *src_pixbuf, |
707 | int src_x, |
708 | int src_y, |
709 | int width, |
710 | int height) |
711 | { |
712 | guchar *pixels; |
713 | GdkPixbuf *sub; |
714 | |
715 | g_return_val_if_fail (GDK_IS_PIXBUF (src_pixbuf), NULL); |
716 | g_return_val_if_fail (src_x >= 0 && src_x + width <= src_pixbuf->width, NULL); |
717 | g_return_val_if_fail (src_y >= 0 && src_y + height <= src_pixbuf->height, NULL); |
718 | |
719 | /* Note causes an implicit copy where src_pixbuf owns the data */ |
720 | pixels = (gdk_pixbuf_get_pixels (pixbuf: src_pixbuf) |
721 | + src_y * src_pixbuf->rowstride |
722 | + src_x * src_pixbuf->n_channels); |
723 | |
724 | sub = gdk_pixbuf_new_from_data (data: pixels, |
725 | colorspace: src_pixbuf->colorspace, |
726 | has_alpha: src_pixbuf->has_alpha, |
727 | bits_per_sample: src_pixbuf->bits_per_sample, |
728 | width, height, |
729 | rowstride: src_pixbuf->rowstride, |
730 | NULL, NULL); |
731 | |
732 | /* Keep a reference to src_pixbuf */ |
733 | g_object_ref (src_pixbuf); |
734 | |
735 | g_object_set_qdata_full (G_OBJECT (sub), |
736 | quark: g_quark_from_static_string (string: "gdk-pixbuf-subpixbuf-src" ), |
737 | data: src_pixbuf, |
738 | destroy: (GDestroyNotify) g_object_unref); |
739 | |
740 | return sub; |
741 | } |
742 | |
743 | |
744 | |
745 | /* Accessors */ |
746 | |
747 | /** |
748 | * gdk_pixbuf_get_colorspace: |
749 | * @pixbuf: A pixbuf. |
750 | * |
751 | * Queries the color space of a pixbuf. |
752 | * |
753 | * Return value: Color space. |
754 | **/ |
755 | GdkColorspace |
756 | gdk_pixbuf_get_colorspace (const GdkPixbuf *pixbuf) |
757 | { |
758 | g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), GDK_COLORSPACE_RGB); |
759 | |
760 | return pixbuf->colorspace; |
761 | } |
762 | |
763 | /** |
764 | * gdk_pixbuf_get_n_channels: |
765 | * @pixbuf: A pixbuf. |
766 | * |
767 | * Queries the number of channels of a pixbuf. |
768 | * |
769 | * Return value: Number of channels. |
770 | **/ |
771 | int |
772 | gdk_pixbuf_get_n_channels (const GdkPixbuf *pixbuf) |
773 | { |
774 | g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), -1); |
775 | |
776 | return pixbuf->n_channels; |
777 | } |
778 | |
779 | /** |
780 | * gdk_pixbuf_get_has_alpha: |
781 | * @pixbuf: A pixbuf. |
782 | * |
783 | * Queries whether a pixbuf has an alpha channel (opacity information). |
784 | * |
785 | * Return value: `TRUE` if it has an alpha channel, `FALSE` otherwise. |
786 | **/ |
787 | gboolean |
788 | gdk_pixbuf_get_has_alpha (const GdkPixbuf *pixbuf) |
789 | { |
790 | g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), FALSE); |
791 | |
792 | return pixbuf->has_alpha ? TRUE : FALSE; |
793 | } |
794 | |
795 | /** |
796 | * gdk_pixbuf_get_bits_per_sample: |
797 | * @pixbuf: A pixbuf. |
798 | * |
799 | * Queries the number of bits per color sample in a pixbuf. |
800 | * |
801 | * Return value: Number of bits per color sample. |
802 | **/ |
803 | int |
804 | gdk_pixbuf_get_bits_per_sample (const GdkPixbuf *pixbuf) |
805 | { |
806 | g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), -1); |
807 | |
808 | return pixbuf->bits_per_sample; |
809 | } |
810 | |
811 | /** |
812 | * gdk_pixbuf_get_pixels: |
813 | * @pixbuf: A pixbuf. |
814 | * |
815 | * Queries a pointer to the pixel data of a pixbuf. |
816 | * |
817 | * This function will cause an implicit copy of the pixbuf data if the |
818 | * pixbuf was created from read-only data. |
819 | * |
820 | * Please see the section on [image data](class.Pixbuf.html#image-data) for information |
821 | * about how the pixel data is stored in memory. |
822 | * |
823 | * Return value: (array): A pointer to the pixbuf's pixel data. |
824 | **/ |
825 | guchar * |
826 | gdk_pixbuf_get_pixels (const GdkPixbuf *pixbuf) |
827 | { |
828 | return gdk_pixbuf_get_pixels_with_length (pixbuf, NULL); |
829 | } |
830 | |
831 | static void |
832 | downgrade_to_pixels (const GdkPixbuf *pixbuf) |
833 | { |
834 | switch (pixbuf->storage) { |
835 | case STORAGE_PIXELS: |
836 | return; |
837 | |
838 | case STORAGE_BYTES: { |
839 | GdkPixbuf *mut_pixbuf = (GdkPixbuf *) pixbuf; |
840 | gsize len; |
841 | Pixels pixels; |
842 | |
843 | pixels.pixels = g_bytes_unref_to_data (bytes: pixbuf->s.bytes.bytes, size: &len); |
844 | pixels.destroy_fn = free_buffer; |
845 | pixels.destroy_fn_data = NULL; |
846 | |
847 | mut_pixbuf->storage = STORAGE_PIXELS; |
848 | mut_pixbuf->s.pixels = pixels; |
849 | break; |
850 | } |
851 | |
852 | default: |
853 | g_assert_not_reached (); |
854 | } |
855 | } |
856 | |
857 | /** |
858 | * gdk_pixbuf_get_pixels_with_length: (rename-to gdk_pixbuf_get_pixels) |
859 | * @pixbuf: A pixbuf. |
860 | * @length: (out): The length of the binary data. |
861 | * |
862 | * Queries a pointer to the pixel data of a pixbuf. |
863 | * |
864 | * This function will cause an implicit copy of the pixbuf data if the |
865 | * pixbuf was created from read-only data. |
866 | * |
867 | * Please see the section on [image data](class.Pixbuf.html#image-data) for information |
868 | * about how the pixel data is stored in memory. |
869 | * |
870 | * Return value: (array length=length): A pointer to the pixbuf's |
871 | * pixel data. |
872 | * |
873 | * Since: 2.26 |
874 | */ |
875 | guchar * |
876 | gdk_pixbuf_get_pixels_with_length (const GdkPixbuf *pixbuf, |
877 | guint *length) |
878 | { |
879 | g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL); |
880 | |
881 | downgrade_to_pixels (pixbuf); |
882 | g_assert (pixbuf->storage == STORAGE_PIXELS); |
883 | |
884 | if (length) |
885 | *length = gdk_pixbuf_get_byte_length (pixbuf); |
886 | |
887 | return pixbuf->s.pixels.pixels; |
888 | } |
889 | |
890 | /** |
891 | * gdk_pixbuf_read_pixels: |
892 | * @pixbuf: A pixbuf |
893 | * |
894 | * Provides a read-only pointer to the raw pixel data. |
895 | * |
896 | * This function allows skipping the implicit copy that must be made |
897 | * if gdk_pixbuf_get_pixels() is called on a read-only pixbuf. |
898 | * |
899 | * Returns: a read-only pointer to the raw pixel data |
900 | * |
901 | * Since: 2.32 |
902 | */ |
903 | const guint8* |
904 | gdk_pixbuf_read_pixels (const GdkPixbuf *pixbuf) |
905 | { |
906 | g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL); |
907 | |
908 | switch (pixbuf->storage) { |
909 | case STORAGE_PIXELS: |
910 | return pixbuf->s.pixels.pixels; |
911 | |
912 | case STORAGE_BYTES: { |
913 | gsize len; |
914 | /* Ignore len; callers know the size via other variables */ |
915 | return g_bytes_get_data (bytes: pixbuf->s.bytes.bytes, size: &len); |
916 | } |
917 | |
918 | default: |
919 | g_assert_not_reached (); |
920 | return NULL; |
921 | } |
922 | } |
923 | |
924 | /** |
925 | * gdk_pixbuf_read_pixel_bytes: |
926 | * @pixbuf: A pixbuf |
927 | * |
928 | * Provides a #GBytes buffer containing the raw pixel data; the data |
929 | * must not be modified. |
930 | * |
931 | * This function allows skipping the implicit copy that must be made |
932 | * if gdk_pixbuf_get_pixels() is called on a read-only pixbuf. |
933 | * |
934 | * Returns: (transfer full): A new reference to a read-only copy of |
935 | * the pixel data. Note that for mutable pixbufs, this function will |
936 | * incur a one-time copy of the pixel data for conversion into the |
937 | * returned #GBytes. |
938 | * |
939 | * Since: 2.32 |
940 | */ |
941 | GBytes * |
942 | gdk_pixbuf_read_pixel_bytes (const GdkPixbuf *pixbuf) |
943 | { |
944 | g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL); |
945 | |
946 | switch (pixbuf->storage) { |
947 | case STORAGE_PIXELS: |
948 | return g_bytes_new (data: pixbuf->s.pixels.pixels, |
949 | size: gdk_pixbuf_get_byte_length (pixbuf)); |
950 | |
951 | case STORAGE_BYTES: |
952 | return g_bytes_ref (bytes: pixbuf->s.bytes.bytes); |
953 | |
954 | default: |
955 | g_assert_not_reached (); |
956 | } |
957 | } |
958 | |
959 | /** |
960 | * gdk_pixbuf_get_width: |
961 | * @pixbuf: A pixbuf. |
962 | * |
963 | * Queries the width of a pixbuf. |
964 | * |
965 | * Return value: Width in pixels. |
966 | **/ |
967 | int |
968 | gdk_pixbuf_get_width (const GdkPixbuf *pixbuf) |
969 | { |
970 | g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), -1); |
971 | |
972 | return pixbuf->width; |
973 | } |
974 | |
975 | /** |
976 | * gdk_pixbuf_get_height: |
977 | * @pixbuf: A pixbuf. |
978 | * |
979 | * Queries the height of a pixbuf. |
980 | * |
981 | * Return value: Height in pixels. |
982 | **/ |
983 | int |
984 | gdk_pixbuf_get_height (const GdkPixbuf *pixbuf) |
985 | { |
986 | g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), -1); |
987 | |
988 | return pixbuf->height; |
989 | } |
990 | |
991 | /** |
992 | * gdk_pixbuf_get_rowstride: |
993 | * @pixbuf: A pixbuf. |
994 | * |
995 | * Queries the rowstride of a pixbuf, which is the number of bytes between |
996 | * the start of a row and the start of the next row. |
997 | * |
998 | * Return value: Distance between row starts. |
999 | **/ |
1000 | int |
1001 | gdk_pixbuf_get_rowstride (const GdkPixbuf *pixbuf) |
1002 | { |
1003 | g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), -1); |
1004 | |
1005 | return pixbuf->rowstride; |
1006 | } |
1007 | |
1008 | /** |
1009 | * gdk_pixbuf_get_byte_length: |
1010 | * @pixbuf: A pixbuf |
1011 | * |
1012 | * Returns the length of the pixel data, in bytes. |
1013 | * |
1014 | * Return value: The length of the pixel data. |
1015 | * |
1016 | * Since: 2.26 |
1017 | */ |
1018 | gsize |
1019 | gdk_pixbuf_get_byte_length (const GdkPixbuf *pixbuf) |
1020 | { |
1021 | g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), -1); |
1022 | |
1023 | return ((pixbuf->height - 1) * pixbuf->rowstride + |
1024 | pixbuf->width * ((pixbuf->n_channels * pixbuf->bits_per_sample + 7) / 8)); |
1025 | } |
1026 | |
1027 | |
1028 | |
1029 | /* General initialization hooks */ |
1030 | const guint gdk_pixbuf_major_version = GDK_PIXBUF_MAJOR; |
1031 | const guint gdk_pixbuf_minor_version = GDK_PIXBUF_MINOR; |
1032 | const guint gdk_pixbuf_micro_version = GDK_PIXBUF_MICRO; |
1033 | |
1034 | const char *gdk_pixbuf_version = GDK_PIXBUF_VERSION; |
1035 | |
1036 | /* Error quark */ |
1037 | GQuark |
1038 | gdk_pixbuf_error_quark (void) |
1039 | { |
1040 | return g_quark_from_static_string (string: "gdk-pixbuf-error-quark" ); |
1041 | } |
1042 | |
1043 | /** |
1044 | * gdk_pixbuf_fill: |
1045 | * @pixbuf: a `GdkPixbuf` |
1046 | * @pixel: RGBA pixel to used to clear (`0xffffffff` is opaque white, |
1047 | * `0x00000000` transparent black) |
1048 | * |
1049 | * Clears a pixbuf to the given RGBA value, converting the RGBA value into |
1050 | * the pixbuf's pixel format. |
1051 | * |
1052 | * The alpha component will be ignored if the pixbuf doesn't have an alpha |
1053 | * channel. |
1054 | */ |
1055 | void |
1056 | gdk_pixbuf_fill (GdkPixbuf *pixbuf, |
1057 | guint32 pixel) |
1058 | { |
1059 | guchar *pixels; |
1060 | guint r, g, b, a; |
1061 | guchar *p; |
1062 | guint w, h; |
1063 | |
1064 | g_return_if_fail (GDK_IS_PIXBUF (pixbuf)); |
1065 | |
1066 | if (pixbuf->width == 0 || pixbuf->height == 0) |
1067 | return; |
1068 | |
1069 | /* Force an implicit copy */ |
1070 | pixels = gdk_pixbuf_get_pixels (pixbuf); |
1071 | |
1072 | r = (pixel & 0xff000000) >> 24; |
1073 | g = (pixel & 0x00ff0000) >> 16; |
1074 | b = (pixel & 0x0000ff00) >> 8; |
1075 | a = (pixel & 0x000000ff); |
1076 | |
1077 | h = pixbuf->height; |
1078 | |
1079 | while (h--) { |
1080 | w = pixbuf->width; |
1081 | p = pixels; |
1082 | |
1083 | switch (pixbuf->n_channels) { |
1084 | case 3: |
1085 | while (w--) { |
1086 | p[0] = r; |
1087 | p[1] = g; |
1088 | p[2] = b; |
1089 | p += 3; |
1090 | } |
1091 | break; |
1092 | case 4: |
1093 | while (w--) { |
1094 | p[0] = r; |
1095 | p[1] = g; |
1096 | p[2] = b; |
1097 | p[3] = a; |
1098 | p += 4; |
1099 | } |
1100 | break; |
1101 | default: |
1102 | break; |
1103 | } |
1104 | |
1105 | pixels += pixbuf->rowstride; |
1106 | } |
1107 | } |
1108 | |
1109 | |
1110 | |
1111 | /** |
1112 | * gdk_pixbuf_get_option: |
1113 | * @pixbuf: a `GdkPixbuf` |
1114 | * @key: a nul-terminated string. |
1115 | * |
1116 | * Looks up @key in the list of options that may have been attached to the |
1117 | * @pixbuf when it was loaded, or that may have been attached by another |
1118 | * function using gdk_pixbuf_set_option(). |
1119 | * |
1120 | * For instance, the ANI loader provides "Title" and "Artist" options. |
1121 | * The ICO, XBM, and XPM loaders provide "x_hot" and "y_hot" hot-spot |
1122 | * options for cursor definitions. The PNG loader provides the tEXt ancillary |
1123 | * chunk key/value pairs as options. Since 2.12, the TIFF and JPEG loaders |
1124 | * return an "orientation" option string that corresponds to the embedded |
1125 | * TIFF/Exif orientation tag (if present). Since 2.32, the TIFF loader sets |
1126 | * the "multipage" option string to "yes" when a multi-page TIFF is loaded. |
1127 | * Since 2.32 the JPEG and PNG loaders set "x-dpi" and "y-dpi" if the file |
1128 | * contains image density information in dots per inch. |
1129 | * Since 2.36.6, the JPEG loader sets the "comment" option with the comment |
1130 | * EXIF tag. |
1131 | * |
1132 | * Return value: (transfer none) (nullable): the value associated with `key` |
1133 | **/ |
1134 | const gchar * |
1135 | gdk_pixbuf_get_option (GdkPixbuf *pixbuf, |
1136 | const gchar *key) |
1137 | { |
1138 | gchar **options; |
1139 | gint i; |
1140 | |
1141 | g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL); |
1142 | g_return_val_if_fail (key != NULL, NULL); |
1143 | |
1144 | options = g_object_get_qdata (G_OBJECT (pixbuf), |
1145 | quark: g_quark_from_static_string (string: "gdk_pixbuf_options" )); |
1146 | if (options) { |
1147 | for (i = 0; options[2*i]; i++) { |
1148 | if (strcmp (s1: options[2*i], s2: key) == 0) |
1149 | return options[2*i+1]; |
1150 | } |
1151 | } |
1152 | |
1153 | return NULL; |
1154 | } |
1155 | |
1156 | /** |
1157 | * gdk_pixbuf_get_options: |
1158 | * @pixbuf: a `GdkPixbuf` |
1159 | * |
1160 | * Returns a `GHashTable` with a list of all the options that may have been |
1161 | * attached to the `pixbuf` when it was loaded, or that may have been |
1162 | * attached by another function using [method@GdkPixbuf.Pixbuf.set_option]. |
1163 | * |
1164 | * Return value: (transfer container) (element-type utf8 utf8): a #GHashTable |
1165 | * of key/values pairs |
1166 | * |
1167 | * Since: 2.32 |
1168 | **/ |
1169 | GHashTable * |
1170 | gdk_pixbuf_get_options (GdkPixbuf *pixbuf) |
1171 | { |
1172 | GHashTable *ht; |
1173 | gchar **options; |
1174 | |
1175 | g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL); |
1176 | |
1177 | ht = g_hash_table_new (hash_func: g_str_hash, key_equal_func: g_str_equal); |
1178 | |
1179 | options = g_object_get_qdata (G_OBJECT (pixbuf), |
1180 | quark: g_quark_from_static_string (string: "gdk_pixbuf_options" )); |
1181 | if (options) { |
1182 | gint i; |
1183 | |
1184 | for (i = 0; options[2*i]; i++) |
1185 | g_hash_table_insert (hash_table: ht, key: options[2*i], value: options[2*i+1]); |
1186 | } |
1187 | |
1188 | return ht; |
1189 | } |
1190 | |
1191 | /** |
1192 | * gdk_pixbuf_remove_option: |
1193 | * @pixbuf: a `GdkPixbuf` |
1194 | * @key: a nul-terminated string representing the key to remove. |
1195 | * |
1196 | * Removes the key/value pair option attached to a `GdkPixbuf`. |
1197 | * |
1198 | * Return value: `TRUE` if an option was removed, `FALSE` if not. |
1199 | * |
1200 | * Since: 2.36 |
1201 | **/ |
1202 | gboolean |
1203 | gdk_pixbuf_remove_option (GdkPixbuf *pixbuf, |
1204 | const gchar *key) |
1205 | { |
1206 | GQuark quark; |
1207 | gchar **options; |
1208 | guint n; |
1209 | GPtrArray *array; |
1210 | gboolean found; |
1211 | |
1212 | g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), FALSE); |
1213 | g_return_val_if_fail (key != NULL, FALSE); |
1214 | |
1215 | quark = g_quark_from_static_string (string: "gdk_pixbuf_options" ); |
1216 | |
1217 | options = g_object_get_qdata (G_OBJECT (pixbuf), quark); |
1218 | if (!options) |
1219 | return FALSE; |
1220 | |
1221 | g_object_steal_qdata (G_OBJECT (pixbuf), quark); |
1222 | |
1223 | /* There's at least a nul-terminator */ |
1224 | array = g_ptr_array_new_full (reserved_size: 1, element_free_func: g_free); |
1225 | |
1226 | found = FALSE; |
1227 | for (n = 0; options[2*n]; n++) { |
1228 | if (strcmp (s1: options[2*n], s2: key) != 0) { |
1229 | g_ptr_array_add (array, data: g_strdup (str: options[2*n])); /* key */ |
1230 | g_ptr_array_add (array, data: g_strdup (str: options[2*n+1])); /* value */ |
1231 | } else { |
1232 | found = TRUE; |
1233 | } |
1234 | } |
1235 | |
1236 | if (array->len == 0) { |
1237 | g_ptr_array_unref (array); |
1238 | g_strfreev (str_array: options); |
1239 | return found; |
1240 | } |
1241 | |
1242 | if (!found) { |
1243 | g_ptr_array_free (array, TRUE); |
1244 | g_object_set_qdata_full (G_OBJECT (pixbuf), quark, |
1245 | data: options, destroy: (GDestroyNotify) g_strfreev); |
1246 | return FALSE; |
1247 | } |
1248 | |
1249 | g_ptr_array_add (array, NULL); |
1250 | g_object_set_qdata_full (G_OBJECT (pixbuf), quark, |
1251 | data: g_ptr_array_free (array, FALSE), destroy: (GDestroyNotify) g_strfreev); |
1252 | g_strfreev (str_array: options); |
1253 | |
1254 | return TRUE; |
1255 | } |
1256 | |
1257 | /** |
1258 | * gdk_pixbuf_set_option: |
1259 | * @pixbuf: a `GdkPixbuf` |
1260 | * @key: a nul-terminated string. |
1261 | * @value: a nul-terminated string. |
1262 | * |
1263 | * Attaches a key/value pair as an option to a `GdkPixbuf`. |
1264 | * |
1265 | * If `key` already exists in the list of options attached to the `pixbuf`, |
1266 | * the new value is ignored and `FALSE` is returned. |
1267 | * |
1268 | * Return value: `TRUE` on success |
1269 | * |
1270 | * Since: 2.2 |
1271 | **/ |
1272 | gboolean |
1273 | gdk_pixbuf_set_option (GdkPixbuf *pixbuf, |
1274 | const gchar *key, |
1275 | const gchar *value) |
1276 | { |
1277 | GQuark quark; |
1278 | gchar **options; |
1279 | gint n = 0; |
1280 | |
1281 | g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), FALSE); |
1282 | g_return_val_if_fail (key != NULL, FALSE); |
1283 | g_return_val_if_fail (value != NULL, FALSE); |
1284 | |
1285 | quark = g_quark_from_static_string (string: "gdk_pixbuf_options" ); |
1286 | |
1287 | options = g_object_get_qdata (G_OBJECT (pixbuf), quark); |
1288 | |
1289 | if (options) { |
1290 | for (n = 0; options[2*n]; n++) { |
1291 | if (strcmp (s1: options[2*n], s2: key) == 0) |
1292 | return FALSE; |
1293 | } |
1294 | |
1295 | g_object_steal_qdata (G_OBJECT (pixbuf), quark); |
1296 | options = g_renew (gchar *, options, 2*(n+1) + 1); |
1297 | } else { |
1298 | options = g_new (gchar *, 3); |
1299 | } |
1300 | |
1301 | options[2*n] = g_strdup (str: key); |
1302 | options[2*n+1] = g_strdup (str: value); |
1303 | options[2*n+2] = NULL; |
1304 | |
1305 | g_object_set_qdata_full (G_OBJECT (pixbuf), quark, |
1306 | data: options, destroy: (GDestroyNotify) g_strfreev); |
1307 | |
1308 | return TRUE; |
1309 | } |
1310 | |
1311 | /** |
1312 | * gdk_pixbuf_copy_options: |
1313 | * @src_pixbuf: the source pixbuf |
1314 | * @dest_pixbuf: the destination pixbuf |
1315 | * |
1316 | * Copies the key/value pair options attached to a `GdkPixbuf` to another |
1317 | * `GdkPixbuf`. |
1318 | * |
1319 | * This is useful to keep original metadata after having manipulated |
1320 | * a file. However be careful to remove metadata which you've already |
1321 | * applied, such as the "orientation" option after rotating the image. |
1322 | * |
1323 | * Return value: `TRUE` on success. |
1324 | * |
1325 | * Since: 2.36 |
1326 | **/ |
1327 | gboolean |
1328 | gdk_pixbuf_copy_options (GdkPixbuf *src_pixbuf, |
1329 | GdkPixbuf *dest_pixbuf) |
1330 | { |
1331 | GQuark quark; |
1332 | gchar **options; |
1333 | |
1334 | g_return_val_if_fail (GDK_IS_PIXBUF (src_pixbuf), FALSE); |
1335 | g_return_val_if_fail (GDK_IS_PIXBUF (dest_pixbuf), FALSE); |
1336 | |
1337 | quark = g_quark_from_static_string (string: "gdk_pixbuf_options" ); |
1338 | |
1339 | options = g_object_dup_qdata (G_OBJECT (src_pixbuf), |
1340 | quark, |
1341 | dup_func: (GDuplicateFunc) g_strdupv, |
1342 | NULL); |
1343 | |
1344 | if (options == NULL) |
1345 | return TRUE; |
1346 | |
1347 | g_object_set_qdata_full (G_OBJECT (dest_pixbuf), quark, |
1348 | data: options, destroy: (GDestroyNotify) g_strfreev); |
1349 | |
1350 | return TRUE; |
1351 | } |
1352 | |
1353 | static void |
1354 | gdk_pixbuf_set_property (GObject *object, |
1355 | guint prop_id, |
1356 | const GValue *value, |
1357 | GParamSpec *pspec) |
1358 | { |
1359 | GdkPixbuf *pixbuf = GDK_PIXBUF (object); |
1360 | gboolean notify = TRUE; |
1361 | |
1362 | switch (prop_id) { |
1363 | case PROP_COLORSPACE: |
1364 | notify = pixbuf->colorspace != g_value_get_enum (value); |
1365 | pixbuf->colorspace = g_value_get_enum (value); |
1366 | break; |
1367 | case PROP_N_CHANNELS: |
1368 | notify = pixbuf->n_channels != g_value_get_int (value); |
1369 | pixbuf->n_channels = g_value_get_int (value); |
1370 | break; |
1371 | case PROP_HAS_ALPHA: |
1372 | notify = pixbuf->has_alpha != g_value_get_boolean (value); |
1373 | pixbuf->has_alpha = g_value_get_boolean (value); |
1374 | break; |
1375 | case PROP_BITS_PER_SAMPLE: |
1376 | notify = pixbuf->bits_per_sample != g_value_get_int (value); |
1377 | pixbuf->bits_per_sample = g_value_get_int (value); |
1378 | break; |
1379 | case PROP_WIDTH: |
1380 | notify = pixbuf->width != g_value_get_int (value); |
1381 | pixbuf->width = g_value_get_int (value); |
1382 | break; |
1383 | case PROP_HEIGHT: |
1384 | notify = pixbuf->height != g_value_get_int (value); |
1385 | pixbuf->height = g_value_get_int (value); |
1386 | break; |
1387 | case PROP_ROWSTRIDE: |
1388 | notify = pixbuf->rowstride != g_value_get_int (value); |
1389 | pixbuf->rowstride = g_value_get_int (value); |
1390 | break; |
1391 | |
1392 | /* The following two are a bit strange. Both PROP_PIXELS and |
1393 | * PROP_PIXEL_BYTES are G_PARAM_CONSTRUCT_ONLY properties, which means |
1394 | * that GObject will generate default values for any missing one and |
1395 | * call us for *both*. So, we need to check whether the passed value is |
1396 | * not NULL before actually setting pixbuf->storage. |
1397 | */ |
1398 | case PROP_PIXELS: { |
1399 | guchar *pixels = g_value_get_pointer (value); |
1400 | |
1401 | if (pixels) { |
1402 | g_assert (pixbuf->storage == STORAGE_UNINITIALIZED); |
1403 | |
1404 | pixbuf->storage = STORAGE_PIXELS; |
1405 | pixbuf->s.pixels.pixels = pixels; |
1406 | pixbuf->s.pixels.destroy_fn = NULL; |
1407 | pixbuf->s.pixels.destroy_fn_data = NULL; |
1408 | } else { |
1409 | notify = FALSE; |
1410 | } |
1411 | |
1412 | break; |
1413 | } |
1414 | |
1415 | case PROP_PIXEL_BYTES: { |
1416 | GBytes *bytes = g_value_get_boxed (value); |
1417 | |
1418 | if (bytes) { |
1419 | g_assert (pixbuf->storage == STORAGE_UNINITIALIZED); |
1420 | |
1421 | pixbuf->storage = STORAGE_BYTES; |
1422 | pixbuf->s.bytes.bytes = g_value_dup_boxed (value); |
1423 | } else { |
1424 | notify = FALSE; |
1425 | } |
1426 | |
1427 | break; |
1428 | } |
1429 | |
1430 | default: |
1431 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
1432 | break; |
1433 | } |
1434 | |
1435 | if (notify) |
1436 | g_object_notify_by_pspec (G_OBJECT (object), pspec); |
1437 | } |
1438 | |
1439 | static void |
1440 | gdk_pixbuf_get_property (GObject *object, |
1441 | guint prop_id, |
1442 | GValue *value, |
1443 | GParamSpec *pspec) |
1444 | { |
1445 | GdkPixbuf *pixbuf = GDK_PIXBUF (object); |
1446 | |
1447 | switch (prop_id) { |
1448 | case PROP_COLORSPACE: |
1449 | g_value_set_enum (value, v_enum: gdk_pixbuf_get_colorspace (pixbuf)); |
1450 | break; |
1451 | case PROP_N_CHANNELS: |
1452 | g_value_set_int (value, v_int: gdk_pixbuf_get_n_channels (pixbuf)); |
1453 | break; |
1454 | case PROP_HAS_ALPHA: |
1455 | g_value_set_boolean (value, v_boolean: gdk_pixbuf_get_has_alpha (pixbuf)); |
1456 | break; |
1457 | case PROP_BITS_PER_SAMPLE: |
1458 | g_value_set_int (value, v_int: gdk_pixbuf_get_bits_per_sample (pixbuf)); |
1459 | break; |
1460 | case PROP_WIDTH: |
1461 | g_value_set_int (value, v_int: gdk_pixbuf_get_width (pixbuf)); |
1462 | break; |
1463 | case PROP_HEIGHT: |
1464 | g_value_set_int (value, v_int: gdk_pixbuf_get_height (pixbuf)); |
1465 | break; |
1466 | case PROP_ROWSTRIDE: |
1467 | g_value_set_int (value, v_int: gdk_pixbuf_get_rowstride (pixbuf)); |
1468 | break; |
1469 | case PROP_PIXELS: |
1470 | g_value_set_pointer (value, v_pointer: gdk_pixbuf_get_pixels (pixbuf)); |
1471 | break; |
1472 | case PROP_PIXEL_BYTES: |
1473 | g_value_set_boxed (value, v_boxed: gdk_pixbuf_read_pixel_bytes (pixbuf)); |
1474 | break; |
1475 | default: |
1476 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
1477 | break; |
1478 | } |
1479 | } |
1480 | |
1481 | static void |
1482 | make_storage_invalid (GdkPixbuf *pixbuf) |
1483 | { |
1484 | char *buf; |
1485 | gsize bufsize = 3; |
1486 | |
1487 | buf = g_new0(char, bufsize); |
1488 | |
1489 | pixbuf->storage = STORAGE_BYTES; |
1490 | pixbuf->s.bytes.bytes = g_bytes_new_with_free_func (data: buf, size: bufsize, free_func: g_free, NULL); |
1491 | |
1492 | pixbuf->colorspace = GDK_COLORSPACE_RGB; |
1493 | pixbuf->n_channels = 3; |
1494 | pixbuf->bits_per_sample = 8; |
1495 | pixbuf->width = 1; |
1496 | pixbuf->height = 1; |
1497 | pixbuf->rowstride = 3; |
1498 | pixbuf->has_alpha = FALSE; |
1499 | } |
1500 | |
1501 | static void |
1502 | gdk_pixbuf_constructed (GObject *object) |
1503 | { |
1504 | GdkPixbuf *pixbuf = GDK_PIXBUF (object); |
1505 | |
1506 | G_OBJECT_CLASS (gdk_pixbuf_parent_class)->constructed (object); |
1507 | |
1508 | switch (pixbuf->storage) { |
1509 | case STORAGE_UNINITIALIZED: |
1510 | /* This means that neither of the construct properties "pixels" nor "pixel-bytes" |
1511 | * was specified during a call to g_object_new(). |
1512 | * |
1513 | * To avoid breaking ABI, we don't emit this warning. We'll want |
1514 | * to emit it once we can have fallible construction. |
1515 | * |
1516 | * g_warning ("pixbuf needs to be constructed with the 'pixels' or 'pixel-bytes' properties"); |
1517 | */ |
1518 | |
1519 | make_storage_invalid (pixbuf); |
1520 | break; |
1521 | |
1522 | case STORAGE_PIXELS: |
1523 | g_assert (pixbuf->s.pixels.pixels != NULL); |
1524 | break; |
1525 | |
1526 | case STORAGE_BYTES: { |
1527 | gsize bytes_size; |
1528 | gint width, height; |
1529 | gboolean has_alpha; |
1530 | |
1531 | g_assert (pixbuf->s.bytes.bytes != NULL); |
1532 | |
1533 | bytes_size = g_bytes_get_size (bytes: pixbuf->s.bytes.bytes); |
1534 | width = pixbuf->width; |
1535 | height = pixbuf->height; |
1536 | has_alpha = pixbuf->has_alpha; |
1537 | |
1538 | /* This is the same check as in gdk_pixbuf_new_from_bytes() */ |
1539 | if (!(bytes_size >= width * height * (has_alpha ? 4 : 3))) { |
1540 | g_error ("GBytes is too small to fit the pixbuf's declared width and height" ); |
1541 | } |
1542 | break; |
1543 | } |
1544 | |
1545 | default: |
1546 | g_assert_not_reached (); |
1547 | } |
1548 | |
1549 | g_assert (pixbuf->storage != STORAGE_UNINITIALIZED); |
1550 | } |
1551 | |