1 | /* GdkPixbuf library - Main loading interface. |
2 | * |
3 | * Copyright (C) 1999 The Free Software Foundation |
4 | * |
5 | * Authors: Miguel de Icaza <miguel@gnu.org> |
6 | * Federico Mena-Quintero <federico@gimp.org> |
7 | * |
8 | * This library is free software; you can redistribute it and/or |
9 | * modify it under the terms of the GNU Lesser General Public |
10 | * License as published by the Free Software Foundation; either |
11 | * version 2 of the License, or (at your option) any later version. |
12 | * |
13 | * This library is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | * Lesser General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU Lesser General Public |
19 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
20 | */ |
21 | |
22 | #include "config.h" |
23 | |
24 | #include <stdlib.h> |
25 | #include <stdio.h> |
26 | #include <string.h> |
27 | #include <errno.h> |
28 | #ifdef HAVE_UNISTD_H |
29 | #include <unistd.h> |
30 | #endif |
31 | |
32 | #include <glib.h> |
33 | #include <gio/gio.h> |
34 | |
35 | #include "gdk-pixbuf-private.h" |
36 | #include "gdk-pixbuf-loader.h" |
37 | #include "gdk-pixdata.h" |
38 | |
39 | #include <glib/gstdio.h> |
40 | |
41 | #ifdef G_OS_WIN32 |
42 | #define STRICT |
43 | #include <windows.h> |
44 | #undef STRICT |
45 | #endif |
46 | #ifdef OS_DARWIN |
47 | #include <mach-o/dyld.h> |
48 | #endif |
49 | |
50 | /** |
51 | * GdkPixbufModule: |
52 | * @module_name: the name of the module, usually the same as the |
53 | * usual file extension for images of this type, eg. "xpm", "jpeg" or "png". |
54 | * @module_path: the path from which the module is loaded. |
55 | * @module: the loaded `GModule`. |
56 | * @info: a `GdkPixbufFormat` holding information about the module. |
57 | * @load: loads an image from a file. |
58 | * @load_xpm_data: loads an image from data in memory. |
59 | * @begin_load: begins an incremental load. |
60 | * @stop_load: stops an incremental load. |
61 | * @load_increment: continues an incremental load. |
62 | * @load_animation: loads an animation from a file. |
63 | * @save: saves a `GdkPixbuf` to a file. |
64 | * @save_to_callback: saves a `GdkPixbuf` by calling the given `GdkPixbufSaveFunc`. |
65 | * @is_save_option_supported: returns whether a save option key is supported by the module |
66 | * |
67 | * A `GdkPixbufModule` contains the necessary functions to load and save |
68 | * images in a certain file format. |
69 | * |
70 | * If `GdkPixbuf` has been compiled with `GModule` support, it can be extended |
71 | * by modules which can load (and perhaps also save) new image and animation |
72 | * formats. |
73 | * |
74 | * ## Implementing modules |
75 | * |
76 | * The `GdkPixbuf` interfaces needed for implementing modules are contained in |
77 | * `gdk-pixbuf-io.h` (and `gdk-pixbuf-animation.h` if the module supports |
78 | * animations). They are not covered by the same stability guarantees as the |
79 | * regular GdkPixbuf API. To underline this fact, they are protected by the |
80 | * `GDK_PIXBUF_ENABLE_BACKEND` pre-processor symbol. |
81 | * |
82 | * Each loadable module must contain a `GdkPixbufModuleFillVtableFunc` function |
83 | * named `fill_vtable`, which will get called when the module |
84 | * is loaded and must set the function pointers of the `GdkPixbufModule`. |
85 | * |
86 | * In order to make format-checking work before actually loading the modules |
87 | * (which may require calling `dlopen` to load image libraries), modules export |
88 | * their signatures (and other information) via the `fill_info` function. An |
89 | * external utility, `gdk-pixbuf-query-loaders`, uses this to create a text |
90 | * file containing a list of all available loaders and their signatures. |
91 | * This file is then read at runtime by `GdkPixbuf` to obtain the list of |
92 | * available loaders and their signatures. |
93 | * |
94 | * Modules may only implement a subset of the functionality available via |
95 | * `GdkPixbufModule`. If a particular functionality is not implemented, the |
96 | * `fill_vtable` function will simply not set the corresponding |
97 | * function pointers of the `GdkPixbufModule` structure. If a module supports |
98 | * incremental loading (i.e. provides `begin_load`, `stop_load` and |
99 | * `load_increment`), it doesn't have to implement `load`, since `GdkPixbuf` |
100 | * can supply a generic `load` implementation wrapping the incremental loading. |
101 | * |
102 | * ## Installing modules |
103 | * |
104 | * Installing a module is a two-step process: |
105 | * |
106 | * - copy the module file(s) to the loader directory (normally |
107 | * `$libdir/gdk-pixbuf-2.0/$version/loaders`, unless overridden by the |
108 | * environment variable `GDK_PIXBUF_MODULEDIR`) |
109 | * - call `gdk-pixbuf-query-loaders` to update the module file (normally |
110 | * `$libdir/gdk-pixbuf-2.0/$version/loaders.cache`, unless overridden |
111 | * by the environment variable `GDK_PIXBUF_MODULE_FILE`) |
112 | */ |
113 | |
114 | static gint |
115 | format_check (GdkPixbufModule *module, guchar *buffer, int size) |
116 | { |
117 | int i, j; |
118 | gchar m; |
119 | GdkPixbufModulePattern *pattern; |
120 | gboolean anchored; |
121 | guchar *prefix; |
122 | gchar *mask; |
123 | |
124 | for (pattern = module->info->signature; pattern->prefix; pattern++) { |
125 | if (pattern->mask && pattern->mask[0] == '*') { |
126 | prefix = (guchar *)pattern->prefix + 1; |
127 | mask = pattern->mask + 1; |
128 | anchored = FALSE; |
129 | } |
130 | else { |
131 | prefix = (guchar *)pattern->prefix; |
132 | mask = pattern->mask; |
133 | anchored = TRUE; |
134 | } |
135 | for (i = 0; i < size; i++) { |
136 | for (j = 0; i + j < size && prefix[j] != 0; j++) { |
137 | m = mask ? mask[j] : ' '; |
138 | if (m == ' ') { |
139 | if (buffer[i + j] != prefix[j]) |
140 | break; |
141 | } |
142 | else if (m == '!') { |
143 | if (buffer[i + j] == prefix[j]) |
144 | break; |
145 | } |
146 | else if (m == 'z') { |
147 | if (buffer[i + j] != 0) |
148 | break; |
149 | } |
150 | else if (m == 'n') { |
151 | if (buffer[i + j] == 0) |
152 | break; |
153 | } |
154 | } |
155 | |
156 | if (prefix[j] == 0) |
157 | return pattern->relevance; |
158 | |
159 | if (anchored) |
160 | break; |
161 | } |
162 | } |
163 | return 0; |
164 | } |
165 | |
166 | G_LOCK_DEFINE_STATIC (init_lock); |
167 | |
168 | static gboolean file_formats_inited; |
169 | static GSList *file_formats = NULL; |
170 | |
171 | static gboolean gdk_pixbuf_io_init (void); |
172 | |
173 | static GSList * |
174 | get_file_formats (void) |
175 | { |
176 | G_LOCK (init_lock); |
177 | if (file_formats == NULL || |
178 | !file_formats_inited) |
179 | file_formats_inited = gdk_pixbuf_io_init (); |
180 | G_UNLOCK (init_lock); |
181 | |
182 | return file_formats; |
183 | } |
184 | |
185 | #ifdef GDK_PIXBUF_RELOCATABLE // implies that gdk-pixbuf is built as a dll on windows |
186 | |
187 | #ifdef G_OS_WIN32 |
188 | |
189 | /* DllMain function needed to tuck away the gdk-pixbuf DLL handle */ |
190 | |
191 | static HMODULE gdk_pixbuf_dll; |
192 | |
193 | BOOL WINAPI |
194 | DllMain (HINSTANCE hinstDLL, |
195 | DWORD fdwReason, |
196 | LPVOID lpvReserved) |
197 | { |
198 | switch (fdwReason) { |
199 | case DLL_PROCESS_ATTACH: |
200 | gdk_pixbuf_dll = (HMODULE) hinstDLL; |
201 | break; |
202 | } |
203 | |
204 | return TRUE; |
205 | } |
206 | #endif |
207 | |
208 | gchar * |
209 | gdk_pixbuf_get_toplevel (void) |
210 | { |
211 | static gchar *toplevel = NULL; |
212 | |
213 | if (toplevel == NULL) { |
214 | #if defined(G_OS_WIN32) |
215 | toplevel = g_win32_get_package_installation_directory_of_module (gdk_pixbuf_dll); |
216 | #elif defined(OS_DARWIN) |
217 | char pathbuf[PATH_MAX + 1]; |
218 | uint32_t bufsize = sizeof(pathbuf); |
219 | gchar *bin_dir; |
220 | |
221 | _NSGetExecutablePath(pathbuf, &bufsize); |
222 | bin_dir = g_dirname(pathbuf); |
223 | toplevel = g_build_path (G_DIR_SEPARATOR_S, bin_dir, ".." , NULL); |
224 | g_free (bin_dir); |
225 | #elif defined (OS_LINUX) |
226 | gchar *exe_path, *bin_dir; |
227 | |
228 | exe_path = g_file_read_link ("/proc/self/exe" , NULL); |
229 | bin_dir = g_dirname(exe_path); |
230 | toplevel = g_build_path (G_DIR_SEPARATOR_S, bin_dir, ".." , NULL); |
231 | g_free (exe_path); |
232 | g_free (bin_dir); |
233 | #else |
234 | #error "Relocations not supported for this platform" |
235 | #endif |
236 | } |
237 | return toplevel; |
238 | } |
239 | |
240 | #endif /* GDK_PIXBUF_RELOCATABLE */ |
241 | |
242 | |
243 | #ifdef USE_GMODULE |
244 | |
245 | static gboolean |
246 | scan_string (const char **pos, GString *out) |
247 | { |
248 | const char *p = *pos, *q = *pos; |
249 | char *tmp, *tmp2; |
250 | gboolean quoted; |
251 | |
252 | while (g_ascii_isspace (*p)) |
253 | p++; |
254 | |
255 | if (!*p) |
256 | return FALSE; |
257 | else if (*p == '"') { |
258 | p++; |
259 | quoted = FALSE; |
260 | for (q = p; (*q != '"') || quoted; q++) { |
261 | if (!*q) |
262 | return FALSE; |
263 | quoted = (*q == '\\') && !quoted; |
264 | } |
265 | |
266 | tmp = g_strndup (str: p, n: q - p); |
267 | tmp2 = g_strcompress (source: tmp); |
268 | g_string_truncate (string: out, len: 0); |
269 | g_string_append (string: out, val: tmp2); |
270 | g_free (mem: tmp); |
271 | g_free (mem: tmp2); |
272 | } |
273 | |
274 | q++; |
275 | *pos = q; |
276 | |
277 | return TRUE; |
278 | } |
279 | |
280 | static gboolean |
281 | scan_int (const char **pos, int *out) |
282 | { |
283 | int i = 0; |
284 | char buf[32]; |
285 | const char *p = *pos; |
286 | |
287 | while (g_ascii_isspace (*p)) |
288 | p++; |
289 | |
290 | if (*p < '0' || *p > '9') |
291 | return FALSE; |
292 | |
293 | while ((*p >= '0') && (*p <= '9') && i < sizeof (buf)) { |
294 | buf[i] = *p; |
295 | i++; |
296 | p++; |
297 | } |
298 | |
299 | if (i == sizeof (buf)) |
300 | return FALSE; |
301 | else |
302 | buf[i] = '\0'; |
303 | |
304 | *out = atoi (nptr: buf); |
305 | |
306 | *pos = p; |
307 | |
308 | return TRUE; |
309 | } |
310 | |
311 | static gboolean |
312 | skip_space (const char **pos) |
313 | { |
314 | const char *p = *pos; |
315 | |
316 | while (g_ascii_isspace (*p)) |
317 | p++; |
318 | |
319 | *pos = p; |
320 | |
321 | return !(*p == '\0'); |
322 | } |
323 | |
324 | #ifdef GDK_PIXBUF_RELOCATABLE |
325 | |
326 | static char * |
327 | get_libdir (void) |
328 | { |
329 | static char *libdir = NULL; |
330 | |
331 | if (libdir == NULL) |
332 | libdir = g_build_filename (gdk_pixbuf_get_toplevel (), "lib" , NULL); |
333 | |
334 | return libdir; |
335 | } |
336 | |
337 | #undef GDK_PIXBUF_LIBDIR |
338 | #define GDK_PIXBUF_LIBDIR get_libdir() |
339 | |
340 | #endif /* GDK_PIXBUF_RELOCATABLE */ |
341 | |
342 | /* In case we have a relative module path in the loaders cache |
343 | * prepend the toplevel dir */ |
344 | static gchar * |
345 | build_module_path (const gchar *path) |
346 | { |
347 | #ifdef GDK_PIXBUF_RELOCATABLE |
348 | if (g_path_is_absolute (path)) { |
349 | return g_strdup (path); |
350 | } else { |
351 | return g_build_filename (gdk_pixbuf_get_toplevel (), path, NULL); |
352 | } |
353 | #else |
354 | return g_strdup (str: path); |
355 | #endif |
356 | } |
357 | |
358 | static gchar * |
359 | gdk_pixbuf_get_module_file (void) |
360 | { |
361 | gchar *result = g_strdup (str: g_getenv (variable: "GDK_PIXBUF_MODULE_FILE" )); |
362 | |
363 | if (!result) |
364 | result = g_build_filename (GDK_PIXBUF_LIBDIR, "gdk-pixbuf-2.0" , GDK_PIXBUF_BINARY_VERSION, "loaders.cache" , NULL); |
365 | |
366 | return result; |
367 | } |
368 | |
369 | #endif /* USE_GMODULE */ |
370 | |
371 | |
372 | static gboolean |
373 | gdk_pixbuf_load_module_unlocked (GdkPixbufModule *image_module, |
374 | GError **error); |
375 | |
376 | static gboolean |
377 | gdk_pixbuf_io_init_modules (const char *filename, |
378 | GError **error) |
379 | { |
380 | #ifdef USE_GMODULE |
381 | GIOChannel *channel; |
382 | gchar *line_buf; |
383 | gsize term; |
384 | GString *tmp_buf = g_string_new (NULL); |
385 | gboolean have_error = FALSE; |
386 | GdkPixbufModule *module = NULL; |
387 | int flags = 0; |
388 | int n_patterns = 0; |
389 | GdkPixbufModulePattern *pattern; |
390 | GError *local_error = NULL; |
391 | guint num_formats; |
392 | |
393 | channel = g_io_channel_new_file (filename, mode: "r" , error: &local_error); |
394 | if (!channel) { |
395 | char *filename_utf8 = g_filename_display_name (filename); |
396 | g_set_error (err: error, |
397 | G_IO_ERROR, |
398 | code: G_IO_ERROR_INVALID_ARGUMENT, |
399 | format: "Cannot open pixbuf loader module file '%s': %s\n\n" |
400 | "This likely means that your installation is broken.\n" |
401 | "Try running the command\n" |
402 | " gdk-pixbuf-query-loaders > %s\n" |
403 | "to make things work again for the time being." , |
404 | filename_utf8, local_error->message, filename_utf8); |
405 | g_clear_error (err: &local_error); |
406 | g_string_free (string: tmp_buf, TRUE); |
407 | g_free (mem: filename_utf8); |
408 | return FALSE; |
409 | } |
410 | |
411 | num_formats = g_slist_length (list: file_formats); |
412 | |
413 | while (!have_error && g_io_channel_read_line (channel, str_return: &line_buf, NULL, terminator_pos: &term, NULL) == G_IO_STATUS_NORMAL) { |
414 | const char *p; |
415 | |
416 | p = line_buf; |
417 | |
418 | line_buf[term] = 0; |
419 | |
420 | if (!skip_space (pos: &p)) { |
421 | /* Blank line marking the end of a module */ |
422 | if (module && *p != '#') { |
423 | file_formats = g_slist_prepend (list: file_formats, data: module); |
424 | module = NULL; |
425 | } |
426 | |
427 | goto next_line; |
428 | } |
429 | |
430 | if (*p == '#') |
431 | goto next_line; |
432 | |
433 | if (!module) { |
434 | /* Read a module location */ |
435 | module = g_new0 (GdkPixbufModule, 1); |
436 | n_patterns = 0; |
437 | |
438 | if (!scan_string (pos: &p, out: tmp_buf)) { |
439 | g_warning ("Error parsing loader info in '%s'\n %s" , |
440 | filename, line_buf); |
441 | have_error = TRUE; |
442 | } |
443 | module->module_path = build_module_path (path: tmp_buf->str); |
444 | } |
445 | else if (!module->module_name) { |
446 | module->info = g_new0 (GdkPixbufFormat, 1); |
447 | if (!scan_string (pos: &p, out: tmp_buf)) { |
448 | g_warning ("Error parsing loader info in '%s'\n %s" , |
449 | filename, line_buf); |
450 | have_error = TRUE; |
451 | } |
452 | module->info->name = g_strdup (str: tmp_buf->str); |
453 | module->module_name = module->info->name; |
454 | |
455 | flags = 0; |
456 | if (!scan_int (pos: &p, out: &flags)) { |
457 | g_warning ("Error parsing loader info in '%s'\n %s" , |
458 | filename, line_buf); |
459 | have_error = TRUE; |
460 | } |
461 | module->info->flags = flags; |
462 | |
463 | if (!scan_string (pos: &p, out: tmp_buf)) { |
464 | g_warning ("Error parsing loader info in '%s'\n %s" , |
465 | filename, line_buf); |
466 | have_error = TRUE; |
467 | } |
468 | if (tmp_buf->str[0] != 0) |
469 | module->info->domain = g_strdup (str: tmp_buf->str); |
470 | |
471 | if (!scan_string (pos: &p, out: tmp_buf)) { |
472 | g_warning ("Error parsing loader info in '%s'\n %s" , |
473 | filename, line_buf); |
474 | have_error = TRUE; |
475 | } |
476 | module->info->description = g_strdup (str: tmp_buf->str); |
477 | |
478 | if (scan_string (pos: &p, out: tmp_buf)) { |
479 | module->info->license = g_strdup (str: tmp_buf->str); |
480 | } |
481 | } |
482 | else if (!module->info->mime_types) { |
483 | int n = 1; |
484 | module->info->mime_types = g_new0 (gchar*, 1); |
485 | while (scan_string (pos: &p, out: tmp_buf)) { |
486 | if (tmp_buf->str[0] != 0) { |
487 | module->info->mime_types = |
488 | g_realloc (mem: module->info->mime_types, n_bytes: (n + 1) * sizeof (gchar*)); |
489 | module->info->mime_types[n - 1] = g_strdup (str: tmp_buf->str); |
490 | module->info->mime_types[n] = NULL; |
491 | n++; |
492 | } |
493 | } |
494 | } |
495 | else if (!module->info->extensions) { |
496 | int n = 1; |
497 | module->info->extensions = g_new0 (gchar*, 1); |
498 | while (scan_string (pos: &p, out: tmp_buf)) { |
499 | if (tmp_buf->str[0] != 0) { |
500 | module->info->extensions = |
501 | g_realloc (mem: module->info->extensions, n_bytes: (n + 1) * sizeof (gchar*)); |
502 | module->info->extensions[n - 1] = g_strdup (str: tmp_buf->str); |
503 | module->info->extensions[n] = NULL; |
504 | n++; |
505 | } |
506 | } |
507 | } |
508 | else { |
509 | n_patterns++; |
510 | module->info->signature = (GdkPixbufModulePattern *) |
511 | g_realloc (mem: module->info->signature, n_bytes: (n_patterns + 1) * sizeof (GdkPixbufModulePattern)); |
512 | pattern = module->info->signature + n_patterns; |
513 | pattern->prefix = NULL; |
514 | pattern->mask = NULL; |
515 | pattern->relevance = 0; |
516 | pattern--; |
517 | if (!scan_string (pos: &p, out: tmp_buf)) |
518 | goto context_error; |
519 | pattern->prefix = g_strdup (str: tmp_buf->str); |
520 | |
521 | if (!scan_string (pos: &p, out: tmp_buf)) |
522 | goto context_error; |
523 | if (*tmp_buf->str) |
524 | pattern->mask = g_strdup (str: tmp_buf->str); |
525 | else |
526 | pattern->mask = NULL; |
527 | |
528 | if (!scan_int (pos: &p, out: &pattern->relevance)) |
529 | goto context_error; |
530 | |
531 | goto next_line; |
532 | |
533 | context_error: |
534 | g_free (mem: pattern->prefix); |
535 | g_free (mem: pattern->mask); |
536 | g_free (mem: pattern); |
537 | g_warning ("Error parsing loader info in '%s'\n %s" , |
538 | filename, line_buf); |
539 | have_error = TRUE; |
540 | } |
541 | next_line: |
542 | g_free (mem: line_buf); |
543 | } |
544 | g_string_free (string: tmp_buf, TRUE); |
545 | g_io_channel_unref (channel); |
546 | |
547 | if (g_slist_length (list: file_formats) <= num_formats) { |
548 | char *filename_utf8 = g_filename_display_name (filename); |
549 | g_set_error (err: error, |
550 | G_IO_ERROR, |
551 | code: G_IO_ERROR_NOT_INITIALIZED, |
552 | format: "No new GdkPixbufModule loaded from '%s'" , |
553 | filename_utf8); |
554 | g_free (mem: filename_utf8); |
555 | return FALSE; |
556 | } |
557 | #endif |
558 | return TRUE; |
559 | } |
560 | |
561 | /** |
562 | * gdk_pixbuf_init_modules: |
563 | * @path: Path to directory where the `loaders.cache` is installed |
564 | * @error: return location for a `GError` |
565 | * |
566 | * Initalizes the gdk-pixbuf loader modules referenced by the `loaders.cache` |
567 | * file present inside that directory. |
568 | * |
569 | * This is to be used by applications that want to ship certain loaders |
570 | * in a different location from the system ones. |
571 | * |
572 | * This is needed when the OS or runtime ships a minimal number of loaders |
573 | * so as to reduce the potential attack surface of carefully crafted image |
574 | * files, especially for uncommon file types. Applications that require |
575 | * broader image file types coverage, such as image viewers, would be |
576 | * expected to ship the gdk-pixbuf modules in a separate location, bundled |
577 | * with the application in a separate directory from the OS or runtime- |
578 | * provided modules. |
579 | * |
580 | * Since: 2.40 |
581 | */ |
582 | gboolean |
583 | gdk_pixbuf_init_modules (const char *path, |
584 | GError **error) |
585 | { |
586 | char *filename; |
587 | gboolean ret; |
588 | |
589 | g_return_val_if_fail (path != NULL, FALSE); |
590 | filename = g_build_filename (first_element: path, "loaders.cache" , NULL); |
591 | ret = gdk_pixbuf_io_init_modules (filename, error); |
592 | g_free (mem: filename); |
593 | return ret; |
594 | } |
595 | |
596 | static void |
597 | gdk_pixbuf_io_init_builtin (void) |
598 | { |
599 | #define load_one_builtin_module(format) G_STMT_START { \ |
600 | GdkPixbufModule *__builtin_module = g_new0 (GdkPixbufModule, 1); \ |
601 | __builtin_module->module_name = #format; \ |
602 | if (gdk_pixbuf_load_module_unlocked (__builtin_module, NULL)) \ |
603 | file_formats = g_slist_prepend (file_formats, __builtin_module); \ |
604 | else \ |
605 | g_free (__builtin_module); } G_STMT_END |
606 | |
607 | #ifdef INCLUDE_ani |
608 | load_one_builtin_module (ani); |
609 | #endif |
610 | #ifdef INCLUDE_png |
611 | load_one_builtin_module (png); |
612 | #endif |
613 | #ifdef INCLUDE_bmp |
614 | load_one_builtin_module (bmp); |
615 | #endif |
616 | #ifdef INCLUDE_gif |
617 | load_one_builtin_module (gif); |
618 | #endif |
619 | #ifdef INCLUDE_ico |
620 | load_one_builtin_module (ico); |
621 | #endif |
622 | #ifdef INCLUDE_jpeg |
623 | load_one_builtin_module (jpeg); |
624 | #endif |
625 | #ifdef INCLUDE_pnm |
626 | load_one_builtin_module (pnm); |
627 | #endif |
628 | #ifdef INCLUDE_tiff |
629 | load_one_builtin_module (tiff); |
630 | #endif |
631 | #ifdef INCLUDE_xpm |
632 | load_one_builtin_module (xpm); |
633 | #endif |
634 | #ifdef INCLUDE_xbm |
635 | load_one_builtin_module (xbm); |
636 | #endif |
637 | #ifdef INCLUDE_tga |
638 | load_one_builtin_module (tga); |
639 | #endif |
640 | #ifdef INCLUDE_icns |
641 | load_one_builtin_module (icns); |
642 | #endif |
643 | #ifdef INCLUDE_qtif |
644 | load_one_builtin_module (qtif); |
645 | #endif |
646 | #ifdef INCLUDE_gdiplus |
647 | /* We don't bother having the GDI+ loaders individually selectable |
648 | * for building in or not. |
649 | */ |
650 | load_one_builtin_module (ico); |
651 | load_one_builtin_module (wmf); |
652 | load_one_builtin_module (emf); |
653 | load_one_builtin_module (bmp); |
654 | load_one_builtin_module (gif); |
655 | load_one_builtin_module (jpeg); |
656 | load_one_builtin_module (tiff); |
657 | #endif |
658 | #ifdef INCLUDE_gdip_png |
659 | /* Except the gdip-png loader which normally isn't built at all even */ |
660 | load_one_builtin_module (png); |
661 | #endif |
662 | |
663 | #undef load_one_builtin_module |
664 | } |
665 | |
666 | static gboolean |
667 | gdk_pixbuf_io_init (void) |
668 | { |
669 | char *module_file; |
670 | gboolean ret; |
671 | |
672 | gdk_pixbuf_io_init_builtin (); |
673 | #ifdef USE_GMODULE |
674 | module_file = gdk_pixbuf_get_module_file (); |
675 | #endif |
676 | ret = gdk_pixbuf_io_init_modules (filename: module_file, NULL); |
677 | g_free (mem: module_file); |
678 | return ret; |
679 | } |
680 | |
681 | #define module(type) \ |
682 | extern void _gdk_pixbuf__##type##_fill_info (GdkPixbufFormat *info); \ |
683 | extern void _gdk_pixbuf__##type##_fill_vtable (GdkPixbufModule *module) |
684 | |
685 | module (png); |
686 | module (jpeg); |
687 | module (gif); |
688 | module (ico); |
689 | module (ani); |
690 | module (xpm); |
691 | module (tiff); |
692 | module (pnm); |
693 | module (bmp); |
694 | module (xbm); |
695 | module (tga); |
696 | module (icns); |
697 | module (qtif); |
698 | module (gdip_ico); |
699 | module (gdip_wmf); |
700 | module (gdip_emf); |
701 | module (gdip_bmp); |
702 | module (gdip_gif); |
703 | module (gdip_jpeg); |
704 | module (gdip_png); |
705 | module (gdip_tiff); |
706 | |
707 | #undef module |
708 | |
709 | /* actually load the image handler - gdk_pixbuf_get_module only get a */ |
710 | /* reference to the module to load, it doesn't actually load it */ |
711 | /* perhaps these actions should be combined in one function */ |
712 | static gboolean |
713 | gdk_pixbuf_load_module_unlocked (GdkPixbufModule *image_module, |
714 | GError **error) |
715 | { |
716 | GdkPixbufModuleFillInfoFunc fill_info = NULL; |
717 | GdkPixbufModuleFillVtableFunc fill_vtable = NULL; |
718 | |
719 | if (image_module->module != NULL) |
720 | return TRUE; |
721 | |
722 | #define try_module(format,id) \ |
723 | if (fill_info == NULL && \ |
724 | strcmp (image_module->module_name, #format) == 0) { \ |
725 | fill_info = _gdk_pixbuf__##id##_fill_info; \ |
726 | fill_vtable = _gdk_pixbuf__##id##_fill_vtable; \ |
727 | } |
728 | |
729 | #ifdef INCLUDE_gdiplus |
730 | try_module (ico,gdip_ico); |
731 | try_module (wmf,gdip_wmf); |
732 | try_module (emf,gdip_emf); |
733 | try_module (bmp,gdip_bmp); |
734 | try_module (gif,gdip_gif); |
735 | try_module (jpeg,gdip_jpeg); |
736 | try_module (tiff,gdip_tiff); |
737 | #endif |
738 | #ifdef INCLUDE_gdip_png |
739 | try_module (png,gdip_png); |
740 | #endif |
741 | #ifdef INCLUDE_png |
742 | try_module (png,png); |
743 | #endif |
744 | #ifdef INCLUDE_bmp |
745 | try_module (bmp,bmp); |
746 | #endif |
747 | #ifdef INCLUDE_gif |
748 | try_module (gif,gif); |
749 | #endif |
750 | #ifdef INCLUDE_ico |
751 | try_module (ico,ico); |
752 | #endif |
753 | #ifdef INCLUDE_ani |
754 | try_module (ani,ani); |
755 | #endif |
756 | #ifdef INCLUDE_jpeg |
757 | try_module (jpeg,jpeg); |
758 | #endif |
759 | #ifdef INCLUDE_pnm |
760 | try_module (pnm,pnm); |
761 | #endif |
762 | #ifdef INCLUDE_tiff |
763 | try_module (tiff,tiff); |
764 | #endif |
765 | #ifdef INCLUDE_xpm |
766 | try_module (xpm,xpm); |
767 | #endif |
768 | #ifdef INCLUDE_xbm |
769 | try_module (xbm,xbm); |
770 | #endif |
771 | #ifdef INCLUDE_tga |
772 | try_module (tga,tga); |
773 | #endif |
774 | #ifdef INCLUDE_icns |
775 | try_module (icns,icns); |
776 | #endif |
777 | #ifdef INCLUDE_qtif |
778 | try_module (qtif,qtif); |
779 | #endif |
780 | |
781 | #undef try_module |
782 | |
783 | if (fill_vtable) { |
784 | image_module->module = (void *) 1; |
785 | (* fill_vtable) (image_module); |
786 | if (image_module->info == NULL) { |
787 | image_module->info = g_new0 (GdkPixbufFormat, 1); |
788 | (* fill_info) (image_module->info); |
789 | } |
790 | return TRUE; |
791 | } |
792 | else |
793 | #ifdef USE_GMODULE |
794 | { |
795 | char *path; |
796 | GModule *module; |
797 | gpointer sym; |
798 | |
799 | path = image_module->module_path; |
800 | module = g_module_open (file_name: path, flags: G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); |
801 | |
802 | if (!module) { |
803 | char *path_utf8 = g_filename_display_name (filename: path); |
804 | g_set_error (err: error, |
805 | GDK_PIXBUF_ERROR, |
806 | code: GDK_PIXBUF_ERROR_FAILED, |
807 | _("Unable to load image-loading module: %s: %s" ), |
808 | path_utf8, g_module_error ()); |
809 | g_free (mem: path_utf8); |
810 | return FALSE; |
811 | } |
812 | |
813 | image_module->module = module; |
814 | |
815 | if (g_module_symbol (module, symbol_name: "fill_vtable" , symbol: &sym)) { |
816 | fill_vtable = (GdkPixbufModuleFillVtableFunc) sym; |
817 | (* fill_vtable) (image_module); |
818 | return TRUE; |
819 | } else { |
820 | char *path_utf8 = g_filename_display_name (filename: path); |
821 | g_set_error (err: error, |
822 | GDK_PIXBUF_ERROR, |
823 | code: GDK_PIXBUF_ERROR_FAILED, |
824 | _("Image-loading module %s does not export the proper interface; perhaps it’s from a different gdk-pixbuf version?" ), |
825 | path_utf8); |
826 | g_free (mem: path_utf8); |
827 | return FALSE; |
828 | } |
829 | } |
830 | #else |
831 | g_set_error (error, |
832 | GDK_PIXBUF_ERROR, |
833 | GDK_PIXBUF_ERROR_UNKNOWN_TYPE, |
834 | _("Image type “%s” is not supported" ), |
835 | image_module->module_name); |
836 | return FALSE; |
837 | #endif /* !USE_GMODULE */ |
838 | } |
839 | |
840 | |
841 | gboolean |
842 | _gdk_pixbuf_load_module (GdkPixbufModule *image_module, |
843 | GError **error) |
844 | { |
845 | gboolean ret; |
846 | |
847 | G_LOCK (init_lock); |
848 | |
849 | ret = gdk_pixbuf_load_module_unlocked (image_module, error); |
850 | |
851 | G_UNLOCK (init_lock); |
852 | |
853 | return ret; |
854 | } |
855 | |
856 | |
857 | |
858 | GdkPixbufModule * |
859 | _gdk_pixbuf_get_named_module (const char *name, |
860 | GError **error) |
861 | { |
862 | GSList *modules; |
863 | |
864 | for (modules = get_file_formats (); modules; modules = g_slist_next (modules)) { |
865 | GdkPixbufModule *module = (GdkPixbufModule *)modules->data; |
866 | |
867 | if (module->info->disabled) |
868 | continue; |
869 | |
870 | if (!strcmp (s1: name, s2: module->module_name)) |
871 | return module; |
872 | } |
873 | |
874 | g_set_error (err: error, |
875 | GDK_PIXBUF_ERROR, |
876 | code: GDK_PIXBUF_ERROR_UNKNOWN_TYPE, |
877 | _("Image type “%s” is not supported" ), |
878 | name); |
879 | |
880 | return NULL; |
881 | } |
882 | |
883 | GdkPixbufModule * |
884 | _gdk_pixbuf_get_module (guchar *buffer, guint size, |
885 | const gchar *filename, |
886 | GError **error) |
887 | { |
888 | GSList *modules; |
889 | |
890 | GdkPixbufModule *selected = NULL; |
891 | gchar *display_name = NULL; |
892 | #ifdef GDK_PIXBUF_USE_GIO_MIME |
893 | gchar *mime_type; |
894 | gchar **mimes; |
895 | gchar *type; |
896 | gint j; |
897 | gboolean uncertain; |
898 | |
899 | mime_type = g_content_type_guess (NULL, data: buffer, data_size: size, result_uncertain: &uncertain); |
900 | if ((uncertain || g_str_equal (v1: mime_type, v2: "text/plain" ) || g_str_equal (v1: mime_type, v2: "application/gzip" )) && filename != NULL) { |
901 | g_free (mem: mime_type); |
902 | mime_type = g_content_type_guess (filename, data: buffer, data_size: size, NULL); |
903 | } |
904 | |
905 | for (modules = get_file_formats (); modules; modules = g_slist_next (modules)) { |
906 | GdkPixbufModule *module = (GdkPixbufModule *)modules->data; |
907 | GdkPixbufFormat *info = module->info; |
908 | |
909 | if (info->disabled) |
910 | continue; |
911 | |
912 | mimes = info->mime_types; |
913 | for (j = 0; mimes[j] != NULL; j++) { |
914 | type = g_content_type_from_mime_type (mime_type: mimes[j]); |
915 | if (g_content_type_equals (type1: type, type2: mime_type)) { |
916 | g_free (mem: type); |
917 | selected = module; |
918 | break; |
919 | } |
920 | g_free (mem: type); |
921 | } |
922 | |
923 | if (selected != NULL) |
924 | break; |
925 | |
926 | /* Make sure the builtin GdkPixdata support works even without mime sniffing */ |
927 | if (strcmp (s1: info->name, s2: "GdkPixdata" ) == 0 && |
928 | format_check (module, buffer, size) == 100) { |
929 | selected = module; |
930 | break; |
931 | } |
932 | } |
933 | g_free (mem: mime_type); |
934 | #else |
935 | gint score, best = 0; |
936 | |
937 | for (modules = get_file_formats (); modules; modules = g_slist_next (modules)) { |
938 | GdkPixbufModule *module = (GdkPixbufModule *)modules->data; |
939 | |
940 | if (module->info->disabled) |
941 | continue; |
942 | |
943 | score = format_check (module, buffer, size); |
944 | if (score > best) { |
945 | best = score; |
946 | selected = module; |
947 | } |
948 | if (score >= 100) |
949 | break; |
950 | } |
951 | #endif |
952 | |
953 | if (selected != NULL) |
954 | return selected; |
955 | |
956 | if (filename) |
957 | { |
958 | display_name = g_filename_display_name (filename); |
959 | g_set_error (err: error, |
960 | GDK_PIXBUF_ERROR, |
961 | code: GDK_PIXBUF_ERROR_UNKNOWN_TYPE, |
962 | _("Couldn’t recognize the image file format for file “%s”" ), |
963 | display_name); |
964 | g_free (mem: display_name); |
965 | } |
966 | else |
967 | g_set_error_literal (err: error, |
968 | GDK_PIXBUF_ERROR, |
969 | code: GDK_PIXBUF_ERROR_UNKNOWN_TYPE, |
970 | _("Unrecognized image file format" )); |
971 | |
972 | |
973 | return NULL; |
974 | } |
975 | |
976 | static |
977 | GdkPixbufModule * |
978 | _gdk_pixbuf_get_module_for_file (FILE *f, const gchar *filename, GError **error) |
979 | { |
980 | guchar buffer[SNIFF_BUFFER_SIZE]; |
981 | int size; |
982 | |
983 | size = fread (ptr: &buffer, size: 1, n: sizeof (buffer), stream: f); |
984 | if (size == 0) { |
985 | gchar *display_name; |
986 | display_name = g_filename_display_name (filename); |
987 | g_set_error (err: error, |
988 | GDK_PIXBUF_ERROR, |
989 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
990 | _("Image file “%s” contains no data" ), |
991 | display_name); |
992 | g_free (mem: display_name); |
993 | return NULL; |
994 | } |
995 | |
996 | return _gdk_pixbuf_get_module (buffer, size, filename, error); |
997 | } |
998 | |
999 | static void |
1000 | noop_size_notify (gint *width, |
1001 | gint *height, |
1002 | gpointer data) |
1003 | { |
1004 | } |
1005 | |
1006 | static void |
1007 | prepared_notify (GdkPixbuf *pixbuf, |
1008 | GdkPixbufAnimation *anim, |
1009 | gpointer user_data) |
1010 | { |
1011 | if (pixbuf != NULL) |
1012 | g_object_ref (pixbuf); |
1013 | *((GdkPixbuf **)user_data) = pixbuf; |
1014 | } |
1015 | |
1016 | static void |
1017 | noop_updated_notify (GdkPixbuf *pixbuf, |
1018 | int x, |
1019 | int y, |
1020 | int width, |
1021 | int height, |
1022 | gpointer user_data) |
1023 | { |
1024 | } |
1025 | |
1026 | static GdkPixbuf * |
1027 | generic_load_incrementally (GdkPixbufModule *module, FILE *f, GError **error) |
1028 | { |
1029 | GdkPixbuf *pixbuf = NULL; |
1030 | gpointer context; |
1031 | |
1032 | context = module->begin_load (noop_size_notify, prepared_notify, noop_updated_notify, &pixbuf, error); |
1033 | |
1034 | if (!context) |
1035 | goto out; |
1036 | |
1037 | while (!feof (stream: f) && !ferror (stream: f)) { |
1038 | guchar buffer[LOAD_BUFFER_SIZE]; |
1039 | size_t length; |
1040 | |
1041 | length = fread (ptr: buffer, size: 1, n: sizeof (buffer), stream: f); |
1042 | if (length > 0) { |
1043 | if (!module->load_increment (context, buffer, length, error)) { |
1044 | module->stop_load (context, NULL); |
1045 | if (pixbuf != NULL) { |
1046 | g_object_unref (object: pixbuf); |
1047 | pixbuf = NULL; |
1048 | } |
1049 | goto out; |
1050 | } |
1051 | } |
1052 | } |
1053 | |
1054 | if (!module->stop_load (context, error)) { |
1055 | if (pixbuf != NULL) { |
1056 | g_object_unref (object: pixbuf); |
1057 | pixbuf = NULL; |
1058 | } |
1059 | } |
1060 | |
1061 | out: |
1062 | return pixbuf; |
1063 | } |
1064 | |
1065 | GdkPixbuf * |
1066 | _gdk_pixbuf_generic_image_load (GdkPixbufModule *module, FILE *f, GError **error) |
1067 | { |
1068 | GdkPixbuf *pixbuf = NULL; |
1069 | |
1070 | if (module->load != NULL) { |
1071 | pixbuf = (* module->load) (f, error); |
1072 | } else if (module->begin_load != NULL) { |
1073 | pixbuf = generic_load_incrementally (module, f, error); |
1074 | } else if (module->load_animation != NULL) { |
1075 | GdkPixbufAnimation *animation; |
1076 | |
1077 | animation = (* module->load_animation) (f, error); |
1078 | if (animation != NULL) { |
1079 | pixbuf = gdk_pixbuf_animation_get_static_image (animation); |
1080 | |
1081 | g_object_ref (pixbuf); |
1082 | g_object_unref (object: animation); |
1083 | } |
1084 | } |
1085 | |
1086 | return pixbuf; |
1087 | } |
1088 | |
1089 | /** |
1090 | * gdk_pixbuf_new_from_file: (constructor) |
1091 | * @filename: (type filename): Name of file to load, in the GLib file |
1092 | * name encoding |
1093 | * @error: (out): Return location for an error |
1094 | * |
1095 | * Creates a new pixbuf by loading an image from a file. |
1096 | * |
1097 | * The file format is detected automatically. |
1098 | * |
1099 | * If `NULL` is returned, then @error will be set. Possible errors are: |
1100 | * |
1101 | * - the file could not be opened |
1102 | * - there is no loader for the file's format |
1103 | * - there is not enough memory to allocate the image buffer |
1104 | * - the image buffer contains invalid data |
1105 | * |
1106 | * The error domains are `GDK_PIXBUF_ERROR` and `G_FILE_ERROR`. |
1107 | * |
1108 | * Return value: (transfer full) (nullable): A newly-created pixbuf |
1109 | */ |
1110 | GdkPixbuf * |
1111 | gdk_pixbuf_new_from_file (const char *filename, |
1112 | GError **error) |
1113 | { |
1114 | GdkPixbuf *pixbuf; |
1115 | FILE *f; |
1116 | GdkPixbufModule *image_module; |
1117 | |
1118 | g_return_val_if_fail (filename != NULL, NULL); |
1119 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
1120 | |
1121 | f = g_fopen (filename: filename, modes: "rb" ); |
1122 | if (!f) { |
1123 | gint save_errno = errno; |
1124 | gchar *display_name; |
1125 | display_name = g_filename_display_name (filename); |
1126 | g_set_error (err: error, |
1127 | G_FILE_ERROR, |
1128 | code: g_file_error_from_errno (err_no: save_errno), |
1129 | _("Failed to open file “%s”: %s" ), |
1130 | display_name, |
1131 | g_strerror (errnum: save_errno)); |
1132 | g_free (mem: display_name); |
1133 | return NULL; |
1134 | } |
1135 | |
1136 | image_module = _gdk_pixbuf_get_module_for_file (f, filename, error); |
1137 | if (image_module == NULL) { |
1138 | fclose (stream: f); |
1139 | return NULL; |
1140 | } |
1141 | |
1142 | if (!_gdk_pixbuf_load_module (image_module, error)) { |
1143 | fclose (stream: f); |
1144 | return NULL; |
1145 | } |
1146 | |
1147 | fseek (stream: f, off: 0, SEEK_SET); |
1148 | pixbuf = _gdk_pixbuf_generic_image_load (module: image_module, f, error); |
1149 | fclose (stream: f); |
1150 | |
1151 | if (pixbuf == NULL && error != NULL && *error == NULL) { |
1152 | |
1153 | /* I don't trust these crufty longjmp()'ing image libs |
1154 | * to maintain proper error invariants, and I don't |
1155 | * want user code to segfault as a result. We need to maintain |
1156 | * the invariant that error gets set if NULL is returned. |
1157 | */ |
1158 | |
1159 | gchar *display_name; |
1160 | display_name = g_filename_display_name (filename); |
1161 | g_warning ("Bug! gdk-pixbuf loader '%s' didn't set an error on failure." , image_module->module_name); |
1162 | g_set_error (err: error, |
1163 | GDK_PIXBUF_ERROR, |
1164 | code: GDK_PIXBUF_ERROR_FAILED, |
1165 | _("Failed to load image “%s”: reason not known, probably a corrupt image file" ), |
1166 | display_name); |
1167 | g_free (mem: display_name); |
1168 | } else if (error != NULL && *error != NULL) { |
1169 | /* Add the filename to the error message */ |
1170 | GError *e = *error; |
1171 | gchar *old; |
1172 | gchar *display_name; |
1173 | |
1174 | display_name = g_filename_display_name (filename); |
1175 | old = e->message; |
1176 | e->message = g_strdup_printf (_("Failed to load image “%s”: %s" ), |
1177 | display_name, |
1178 | old); |
1179 | g_free (mem: old); |
1180 | g_free (mem: display_name); |
1181 | } |
1182 | |
1183 | return pixbuf; |
1184 | } |
1185 | |
1186 | #ifdef G_OS_WIN32 |
1187 | |
1188 | /** |
1189 | * gdk_pixbuf_new_from_file_utf8: |
1190 | * @filename: (type filename): Name of file to load, in the GLib file name encoding |
1191 | * @error: Return location for an error |
1192 | * |
1193 | * Same as gdk_pixbuf_new_from_file() |
1194 | * |
1195 | * Return value: A newly-created pixbuf with a reference count of 1, or `NULL` if |
1196 | * any of several error conditions occurred: the file could not be opened, |
1197 | * there was no loader for the file's format, there was not enough memory to |
1198 | * allocate the image buffer, or the image file contained invalid data. |
1199 | **/ |
1200 | GdkPixbuf * |
1201 | gdk_pixbuf_new_from_file_utf8 (const char *filename, |
1202 | GError **error) |
1203 | { |
1204 | return gdk_pixbuf_new_from_file (filename, error); |
1205 | } |
1206 | |
1207 | #endif |
1208 | |
1209 | |
1210 | /** |
1211 | * gdk_pixbuf_new_from_file_at_size: (constructor) |
1212 | * @filename: (type filename): Name of file to load, in the GLib file |
1213 | * name encoding |
1214 | * @width: The width the image should have or -1 to not constrain the width |
1215 | * @height: The height the image should have or -1 to not constrain the height |
1216 | * @error: (out): Return location for an error |
1217 | * |
1218 | * Creates a new pixbuf by loading an image from a file. |
1219 | * |
1220 | * The file format is detected automatically. |
1221 | * |
1222 | * If `NULL` is returned, then @error will be set. Possible errors are: |
1223 | * |
1224 | * - the file could not be opened |
1225 | * - there is no loader for the file's format |
1226 | * - there is not enough memory to allocate the image buffer |
1227 | * - the image buffer contains invalid data |
1228 | * |
1229 | * The error domains are `GDK_PIXBUF_ERROR` and `G_FILE_ERROR`. |
1230 | * |
1231 | * The image will be scaled to fit in the requested size, preserving |
1232 | * the image's aspect ratio. Note that the returned pixbuf may be smaller |
1233 | * than `width` x `height`, if the aspect ratio requires it. To load |
1234 | * and image at the requested size, regardless of aspect ratio, use |
1235 | * [ctor@GdkPixbuf.Pixbuf.new_from_file_at_scale]. |
1236 | * |
1237 | * Return value: (transfer full) (nullable): A newly-created pixbuf |
1238 | * |
1239 | * Since: 2.4 |
1240 | **/ |
1241 | GdkPixbuf * |
1242 | gdk_pixbuf_new_from_file_at_size (const char *filename, |
1243 | int width, |
1244 | int height, |
1245 | GError **error) |
1246 | { |
1247 | return gdk_pixbuf_new_from_file_at_scale (filename, |
1248 | width, height, |
1249 | TRUE, error); |
1250 | } |
1251 | |
1252 | #ifdef G_OS_WIN32 |
1253 | |
1254 | /** |
1255 | * gdk_pixbuf_new_from_file_at_size_utf8: |
1256 | * @filename: (type filename): Name of file to load, in the GLib file name encoding |
1257 | * @width: The width the image should have or -1 to not constrain the width |
1258 | * @height: The height the image should have or -1 to not constrain the height |
1259 | * @error: Return location for an error |
1260 | * |
1261 | * Same as gdk_pixbuf_new_from_file_at_size() |
1262 | * |
1263 | * Return value: A newly-created pixbuf with a reference count of 1, or |
1264 | * `NULL` if any of several error conditions occurred: the file could not |
1265 | * be opened, there was no loader for the file's format, there was not |
1266 | * enough memory to allocate the image buffer, or the image file contained |
1267 | * invalid data. |
1268 | * |
1269 | * Since: 2.4 |
1270 | **/ |
1271 | GdkPixbuf * |
1272 | gdk_pixbuf_new_from_file_at_size_utf8 (const char *filename, |
1273 | int width, |
1274 | int height, |
1275 | GError **error) |
1276 | { |
1277 | return gdk_pixbuf_new_from_file_at_size (filename, width, height, error); |
1278 | } |
1279 | |
1280 | #endif |
1281 | |
1282 | typedef struct { |
1283 | gint width; |
1284 | gint height; |
1285 | gboolean preserve_aspect_ratio; |
1286 | } AtScaleData; |
1287 | |
1288 | static void |
1289 | at_scale_data_async_data_free (AtScaleData *data) |
1290 | { |
1291 | g_slice_free (AtScaleData, data); |
1292 | } |
1293 | |
1294 | static void |
1295 | at_scale_size_prepared_cb (GdkPixbufLoader *loader, |
1296 | int width, |
1297 | int height, |
1298 | gpointer data) |
1299 | { |
1300 | AtScaleData *info = data; |
1301 | |
1302 | g_return_if_fail (width > 0 && height > 0); |
1303 | |
1304 | if (info->preserve_aspect_ratio && |
1305 | (info->width > 0 || info->height > 0)) { |
1306 | if (info->width < 0) |
1307 | { |
1308 | width = width * (double)info->height/(double)height; |
1309 | height = info->height; |
1310 | } |
1311 | else if (info->height < 0) |
1312 | { |
1313 | height = height * (double)info->width/(double)width; |
1314 | width = info->width; |
1315 | } |
1316 | else if ((double)height * (double)info->width > |
1317 | (double)width * (double)info->height) { |
1318 | width = 0.5 + (double)width * (double)info->height / (double)height; |
1319 | height = info->height; |
1320 | } else { |
1321 | height = 0.5 + (double)height * (double)info->width / (double)width; |
1322 | width = info->width; |
1323 | } |
1324 | } else { |
1325 | if (info->width > 0) |
1326 | width = info->width; |
1327 | if (info->height > 0) |
1328 | height = info->height; |
1329 | } |
1330 | |
1331 | width = MAX (width, 1); |
1332 | height = MAX (height, 1); |
1333 | |
1334 | gdk_pixbuf_loader_set_size (loader, width, height); |
1335 | } |
1336 | |
1337 | /** |
1338 | * gdk_pixbuf_new_from_file_at_scale: (constructor) |
1339 | * @filename: (type filename): Name of file to load, in the GLib file |
1340 | * name encoding |
1341 | * @width: The width the image should have or -1 to not constrain the width |
1342 | * @height: The height the image should have or -1 to not constrain the height |
1343 | * @preserve_aspect_ratio: `TRUE` to preserve the image's aspect ratio |
1344 | * @error: Return location for an error |
1345 | * |
1346 | * Creates a new pixbuf by loading an image from a file. |
1347 | * |
1348 | * The file format is detected automatically. |
1349 | * |
1350 | * If `NULL` is returned, then @error will be set. Possible errors are: |
1351 | * |
1352 | * - the file could not be opened |
1353 | * - there is no loader for the file's format |
1354 | * - there is not enough memory to allocate the image buffer |
1355 | * - the image buffer contains invalid data |
1356 | * |
1357 | * The error domains are `GDK_PIXBUF_ERROR` and `G_FILE_ERROR`. |
1358 | * |
1359 | * The image will be scaled to fit in the requested size, optionally preserving |
1360 | * the image's aspect ratio. |
1361 | * |
1362 | * When preserving the aspect ratio, a `width` of -1 will cause the image |
1363 | * to be scaled to the exact given height, and a `height` of -1 will cause |
1364 | * the image to be scaled to the exact given width. When not preserving |
1365 | * aspect ratio, a `width` or `height` of -1 means to not scale the image |
1366 | * at all in that dimension. Negative values for `width` and `height` are |
1367 | * allowed since 2.8. |
1368 | * |
1369 | * Return value: (transfer full) (nullable): A newly-created pixbuf |
1370 | * |
1371 | * Since: 2.6 |
1372 | **/ |
1373 | GdkPixbuf * |
1374 | gdk_pixbuf_new_from_file_at_scale (const char *filename, |
1375 | int width, |
1376 | int height, |
1377 | gboolean preserve_aspect_ratio, |
1378 | GError **error) |
1379 | { |
1380 | |
1381 | GdkPixbufLoader *loader; |
1382 | GdkPixbuf *pixbuf; |
1383 | guchar buffer[LOAD_BUFFER_SIZE]; |
1384 | int length; |
1385 | FILE *f; |
1386 | AtScaleData info; |
1387 | GdkPixbufAnimation *animation; |
1388 | GdkPixbufAnimationIter *iter; |
1389 | gboolean has_frame; |
1390 | |
1391 | g_return_val_if_fail (filename != NULL, NULL); |
1392 | g_return_val_if_fail (width > 0 || width == -1, NULL); |
1393 | g_return_val_if_fail (height > 0 || height == -1, NULL); |
1394 | |
1395 | f = g_fopen (filename: filename, modes: "rb" ); |
1396 | if (!f) { |
1397 | gint save_errno = errno; |
1398 | gchar *display_name = g_filename_display_name (filename); |
1399 | g_set_error (err: error, |
1400 | G_FILE_ERROR, |
1401 | code: g_file_error_from_errno (err_no: save_errno), |
1402 | _("Failed to open file “%s”: %s" ), |
1403 | display_name, |
1404 | g_strerror (errnum: save_errno)); |
1405 | g_free (mem: display_name); |
1406 | return NULL; |
1407 | } |
1408 | |
1409 | loader = _gdk_pixbuf_loader_new_with_filename (filename); |
1410 | |
1411 | info.width = width; |
1412 | info.height = height; |
1413 | info.preserve_aspect_ratio = preserve_aspect_ratio; |
1414 | |
1415 | g_signal_connect (loader, "size-prepared" , |
1416 | G_CALLBACK (at_scale_size_prepared_cb), &info); |
1417 | |
1418 | has_frame = FALSE; |
1419 | while (!has_frame && !feof (stream: f) && !ferror (stream: f)) { |
1420 | length = fread (ptr: buffer, size: 1, n: sizeof (buffer), stream: f); |
1421 | if (length > 0) |
1422 | if (!gdk_pixbuf_loader_write (loader, buf: buffer, count: length, error)) { |
1423 | gdk_pixbuf_loader_close (loader, NULL); |
1424 | fclose (stream: f); |
1425 | g_object_unref (object: loader); |
1426 | return NULL; |
1427 | } |
1428 | |
1429 | animation = gdk_pixbuf_loader_get_animation (loader); |
1430 | if (animation) { |
1431 | iter = gdk_pixbuf_animation_get_iter (animation, NULL); |
1432 | if (!gdk_pixbuf_animation_iter_on_currently_loading_frame (iter)) { |
1433 | has_frame = TRUE; |
1434 | } |
1435 | g_object_unref (object: iter); |
1436 | } |
1437 | } |
1438 | |
1439 | fclose (stream: f); |
1440 | |
1441 | if (!gdk_pixbuf_loader_close (loader, error) && !has_frame) { |
1442 | g_object_unref (object: loader); |
1443 | return NULL; |
1444 | } |
1445 | |
1446 | pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); |
1447 | |
1448 | if (!pixbuf) { |
1449 | gchar *display_name = g_filename_display_name (filename); |
1450 | g_object_unref (object: loader); |
1451 | g_set_error (err: error, |
1452 | GDK_PIXBUF_ERROR, |
1453 | code: GDK_PIXBUF_ERROR_FAILED, |
1454 | _("Failed to load image “%s”: reason not known, probably a corrupt image file" ), |
1455 | display_name); |
1456 | g_free (mem: display_name); |
1457 | return NULL; |
1458 | } |
1459 | |
1460 | g_object_ref (pixbuf); |
1461 | |
1462 | g_object_unref (object: loader); |
1463 | |
1464 | return pixbuf; |
1465 | } |
1466 | |
1467 | #ifdef G_OS_WIN32 |
1468 | |
1469 | /** |
1470 | * gdk_pixbuf_new_from_file_at_scale_utf8: |
1471 | * @filename: (type filename): Name of file to load, in the GLib file name encoding |
1472 | * @width: The width the image should have or -1 to not constrain the width |
1473 | * @height: The height the image should have or -1 to not constrain the height |
1474 | * @preserve_aspect_ratio: `TRUE` to preserve the image's aspect ratio |
1475 | * @error: Return location for an error |
1476 | * |
1477 | * Same as gdk_pixbuf_new_from_file_at_scale(). |
1478 | * |
1479 | * Return value: A newly-created pixbuf with a reference count of 1, or `NULL` |
1480 | * if any of several error conditions occurred: the file could not be opened, |
1481 | * there was no loader for the file's format, there was not enough memory to |
1482 | * allocate the image buffer, or the image file contained invalid data. |
1483 | * |
1484 | * Since: 2.6 |
1485 | **/ |
1486 | GdkPixbuf * |
1487 | gdk_pixbuf_new_from_file_at_scale_utf8 (const char *filename, |
1488 | int width, |
1489 | int height, |
1490 | gboolean preserve_aspect_ratio, |
1491 | GError **error) |
1492 | { |
1493 | return gdk_pixbuf_new_from_file_at_scale (filename, width, height, |
1494 | preserve_aspect_ratio, error); |
1495 | } |
1496 | #endif |
1497 | |
1498 | |
1499 | static GdkPixbuf * |
1500 | load_from_stream (GdkPixbufLoader *loader, |
1501 | GInputStream *stream, |
1502 | GCancellable *cancellable, |
1503 | GError **error) |
1504 | { |
1505 | GdkPixbuf *pixbuf; |
1506 | gssize n_read; |
1507 | guchar buffer[LOAD_BUFFER_SIZE]; |
1508 | |
1509 | while (1) { |
1510 | n_read = g_input_stream_read (stream, |
1511 | buffer, |
1512 | count: sizeof (buffer), |
1513 | cancellable, |
1514 | error); |
1515 | if (n_read < 0) { |
1516 | gdk_pixbuf_loader_close (loader, NULL); |
1517 | return NULL; |
1518 | } |
1519 | |
1520 | if (n_read == 0) |
1521 | break; |
1522 | |
1523 | if (!gdk_pixbuf_loader_write (loader, |
1524 | buf: buffer, |
1525 | count: n_read, |
1526 | error)) { |
1527 | gdk_pixbuf_loader_close (loader, NULL); |
1528 | return NULL; |
1529 | } |
1530 | } |
1531 | |
1532 | if (!gdk_pixbuf_loader_close (loader, error)) |
1533 | return NULL; |
1534 | |
1535 | pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); |
1536 | if (pixbuf == NULL) |
1537 | return NULL; |
1538 | |
1539 | return g_object_ref (pixbuf); |
1540 | } |
1541 | |
1542 | |
1543 | /** |
1544 | * gdk_pixbuf_new_from_stream_at_scale: (constructor) |
1545 | * @stream: a `GInputStream` to load the pixbuf from |
1546 | * @width: The width the image should have or -1 to not constrain the width |
1547 | * @height: The height the image should have or -1 to not constrain the height |
1548 | * @preserve_aspect_ratio: `TRUE` to preserve the image's aspect ratio |
1549 | * @cancellable: (allow-none): optional `GCancellable` object, `NULL` to ignore |
1550 | * @error: Return location for an error |
1551 | * |
1552 | * Creates a new pixbuf by loading an image from an input stream. |
1553 | * |
1554 | * The file format is detected automatically. If `NULL` is returned, then |
1555 | * @error will be set. The @cancellable can be used to abort the operation |
1556 | * from another thread. If the operation was cancelled, the error |
1557 | * `G_IO_ERROR_CANCELLED` will be returned. Other possible errors are in |
1558 | * the `GDK_PIXBUF_ERROR` and `G_IO_ERROR` domains. |
1559 | * |
1560 | * The image will be scaled to fit in the requested size, optionally |
1561 | * preserving the image's aspect ratio. |
1562 | * |
1563 | * When preserving the aspect ratio, a `width` of -1 will cause the image to be |
1564 | * scaled to the exact given height, and a `height` of -1 will cause the image |
1565 | * to be scaled to the exact given width. If both `width` and `height` are |
1566 | * given, this function will behave as if the smaller of the two values |
1567 | * is passed as -1. |
1568 | * |
1569 | * When not preserving aspect ratio, a `width` or `height` of -1 means to not |
1570 | * scale the image at all in that dimension. |
1571 | * |
1572 | * The stream is not closed. |
1573 | * |
1574 | * Return value: (transfer full) (nullable): A newly-created pixbuf |
1575 | * |
1576 | * Since: 2.14 |
1577 | */ |
1578 | GdkPixbuf * |
1579 | gdk_pixbuf_new_from_stream_at_scale (GInputStream *stream, |
1580 | gint width, |
1581 | gint height, |
1582 | gboolean preserve_aspect_ratio, |
1583 | GCancellable *cancellable, |
1584 | GError **error) |
1585 | { |
1586 | GdkPixbufLoader *loader; |
1587 | GdkPixbuf *pixbuf; |
1588 | AtScaleData info; |
1589 | |
1590 | loader = gdk_pixbuf_loader_new (); |
1591 | info.width = width; |
1592 | info.height = height; |
1593 | info.preserve_aspect_ratio = preserve_aspect_ratio; |
1594 | |
1595 | g_signal_connect (loader, "size-prepared" , |
1596 | G_CALLBACK (at_scale_size_prepared_cb), &info); |
1597 | |
1598 | pixbuf = load_from_stream (loader, stream, cancellable, error); |
1599 | g_object_unref (object: loader); |
1600 | |
1601 | return pixbuf; |
1602 | } |
1603 | |
1604 | |
1605 | static void |
1606 | load_from_stream_async_cb (GObject *stream, |
1607 | GAsyncResult *res, |
1608 | gpointer data) |
1609 | { |
1610 | GTask *task = data; |
1611 | GdkPixbufLoader *loader; |
1612 | GdkPixbuf *pixbuf; |
1613 | GError *error = NULL; |
1614 | GBytes *bytes = NULL; |
1615 | |
1616 | loader = g_task_get_task_data (task); |
1617 | |
1618 | bytes = g_input_stream_read_bytes_finish (G_INPUT_STREAM (stream), result: res, error: &error); |
1619 | |
1620 | if (bytes == NULL) { |
1621 | gdk_pixbuf_loader_close (loader, NULL); |
1622 | g_task_return_error (task, error); |
1623 | } else if (g_bytes_get_size (bytes) > 0) { |
1624 | if (!gdk_pixbuf_loader_write (loader, |
1625 | buf: g_bytes_get_data (bytes, NULL), |
1626 | count: g_bytes_get_size (bytes), |
1627 | error: &error)) { |
1628 | gdk_pixbuf_loader_close (loader, NULL); |
1629 | g_task_return_error (task, error); |
1630 | goto out; |
1631 | } |
1632 | g_input_stream_read_bytes_async (G_INPUT_STREAM (stream), |
1633 | LOAD_BUFFER_SIZE, |
1634 | G_PRIORITY_DEFAULT, |
1635 | cancellable: g_task_get_cancellable (task), |
1636 | callback: load_from_stream_async_cb, |
1637 | g_object_ref (task)); |
1638 | |
1639 | } else { |
1640 | if (!gdk_pixbuf_loader_close (loader, error: &error)) { |
1641 | g_task_return_error (task, error); |
1642 | goto out; |
1643 | } |
1644 | |
1645 | pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); |
1646 | g_task_return_pointer (task, g_object_ref (pixbuf), result_destroy: g_object_unref); |
1647 | } |
1648 | |
1649 | out: |
1650 | g_bytes_unref (bytes); |
1651 | g_object_unref (object: task); |
1652 | } |
1653 | |
1654 | |
1655 | /** |
1656 | * gdk_pixbuf_new_from_stream_at_scale_async: |
1657 | * @stream: a `GInputStream` from which to load the pixbuf |
1658 | * @width: the width the image should have or -1 to not constrain the width |
1659 | * @height: the height the image should have or -1 to not constrain the height |
1660 | * @preserve_aspect_ratio: `TRUE` to preserve the image's aspect ratio |
1661 | * @cancellable: (allow-none): optional `GCancellable` object, `NULL` to ignore |
1662 | * @callback: a `GAsyncReadyCallback` to call when the pixbuf is loaded |
1663 | * @user_data: the data to pass to the callback function |
1664 | * |
1665 | * Creates a new pixbuf by asynchronously loading an image from an input stream. |
1666 | * |
1667 | * For more details see gdk_pixbuf_new_from_stream_at_scale(), which is the synchronous |
1668 | * version of this function. |
1669 | * |
1670 | * When the operation is finished, @callback will be called in the main thread. |
1671 | * You can then call gdk_pixbuf_new_from_stream_finish() to get the result of the operation. |
1672 | * |
1673 | * Since: 2.24 |
1674 | **/ |
1675 | void |
1676 | gdk_pixbuf_new_from_stream_at_scale_async (GInputStream *stream, |
1677 | gint width, |
1678 | gint height, |
1679 | gboolean preserve_aspect_ratio, |
1680 | GCancellable *cancellable, |
1681 | GAsyncReadyCallback callback, |
1682 | gpointer user_data) |
1683 | { |
1684 | GTask *task; |
1685 | AtScaleData *data; |
1686 | GdkPixbufLoader *loader; |
1687 | |
1688 | g_return_if_fail (G_IS_INPUT_STREAM (stream)); |
1689 | g_return_if_fail (callback != NULL); |
1690 | g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); |
1691 | |
1692 | data = g_slice_new (AtScaleData); |
1693 | data->width = width; |
1694 | data->height = height; |
1695 | data->preserve_aspect_ratio = preserve_aspect_ratio; |
1696 | |
1697 | loader = gdk_pixbuf_loader_new (); |
1698 | g_signal_connect (loader, "size-prepared" , |
1699 | G_CALLBACK (at_scale_size_prepared_cb), data); |
1700 | g_object_set_data_full (G_OBJECT (loader), |
1701 | key: "gdk-pixbuf-please-kill-me-later" , |
1702 | data, |
1703 | destroy: (GDestroyNotify) at_scale_data_async_data_free); |
1704 | |
1705 | task = g_task_new (source_object: stream, cancellable, callback, callback_data: user_data); |
1706 | g_task_set_source_tag (task, gdk_pixbuf_new_from_stream_at_scale_async); |
1707 | g_task_set_task_data (task, task_data: loader, task_data_destroy: g_object_unref); |
1708 | |
1709 | g_input_stream_read_bytes_async (stream, |
1710 | LOAD_BUFFER_SIZE, |
1711 | G_PRIORITY_DEFAULT, |
1712 | cancellable, |
1713 | callback: load_from_stream_async_cb, |
1714 | user_data: task); |
1715 | } |
1716 | |
1717 | /** |
1718 | * gdk_pixbuf_new_from_stream: (constructor) |
1719 | * @stream: a `GInputStream` to load the pixbuf from |
1720 | * @cancellable: (allow-none): optional `GCancellable` object, `NULL` to ignore |
1721 | * @error: Return location for an error |
1722 | * |
1723 | * Creates a new pixbuf by loading an image from an input stream. |
1724 | * |
1725 | * The file format is detected automatically. |
1726 | * |
1727 | * If `NULL` is returned, then `error` will be set. |
1728 | * |
1729 | * The `cancellable` can be used to abort the operation from another thread. |
1730 | * If the operation was cancelled, the error `G_IO_ERROR_CANCELLED` will be |
1731 | * returned. Other possible errors are in the `GDK_PIXBUF_ERROR` and |
1732 | * `G_IO_ERROR` domains. |
1733 | * |
1734 | * The stream is not closed. |
1735 | * |
1736 | * Return value: (transfer full) (nullable): A newly-created pixbuf |
1737 | * |
1738 | * Since: 2.14 |
1739 | **/ |
1740 | GdkPixbuf * |
1741 | gdk_pixbuf_new_from_stream (GInputStream *stream, |
1742 | GCancellable *cancellable, |
1743 | GError **error) |
1744 | { |
1745 | GdkPixbuf *pixbuf; |
1746 | GdkPixbufLoader *loader; |
1747 | |
1748 | loader = gdk_pixbuf_loader_new (); |
1749 | pixbuf = load_from_stream (loader, stream, cancellable, error); |
1750 | g_object_unref (object: loader); |
1751 | |
1752 | return pixbuf; |
1753 | } |
1754 | |
1755 | GdkPixbuf * |
1756 | _gdk_pixbuf_new_from_resource_try_pixdata (const char *resource_path) |
1757 | { |
1758 | gsize data_size; |
1759 | GBytes *bytes; |
1760 | |
1761 | G_GNUC_BEGIN_IGNORE_DEPRECATIONS |
1762 | /* We specialize GdkPixdata files, making these a reference to the |
1763 | * compiled-in resource data, whether uncompressed and mmap'ed, or |
1764 | * compressed, and uncompressed on-the-fly. |
1765 | */ |
1766 | if (g_resources_get_info (path: resource_path, lookup_flags: 0, size: &data_size, NULL, NULL) && |
1767 | data_size > sizeof(guint32) && |
1768 | (bytes = g_resources_lookup_data (path: resource_path, lookup_flags: 0, NULL)) != NULL) { |
1769 | GdkPixbuf*pixbuf = NULL; |
1770 | const guint8 *stream = g_bytes_get_data (bytes, NULL); |
1771 | GdkPixdata pixdata; |
1772 | guint32 magic; |
1773 | |
1774 | magic = (stream[0] << 24) + (stream[1] << 16) + (stream[2] << 8) + stream[3]; |
1775 | if (magic == GDK_PIXBUF_MAGIC_NUMBER && |
1776 | gdk_pixdata_deserialize (pixdata: &pixdata, stream_length: data_size, stream, NULL)) { |
1777 | pixbuf = gdk_pixbuf_from_pixdata (pixdata: &pixdata, FALSE, NULL); |
1778 | } |
1779 | |
1780 | if (pixbuf) { |
1781 | /* Free the GBytes with the pixbuf */ |
1782 | g_object_set_data_full (G_OBJECT (pixbuf), key: "gdk-pixbuf-resource-bytes" , data: bytes, destroy: (GDestroyNotify) g_bytes_unref); |
1783 | return pixbuf; |
1784 | } else { |
1785 | g_bytes_unref (bytes); |
1786 | } |
1787 | } |
1788 | G_GNUC_END_IGNORE_DEPRECATIONS |
1789 | |
1790 | return NULL; |
1791 | } |
1792 | |
1793 | /** |
1794 | * gdk_pixbuf_new_from_resource: (constructor) |
1795 | * @resource_path: the path of the resource file |
1796 | * @error: Return location for an error |
1797 | * |
1798 | * Creates a new pixbuf by loading an image from an resource. |
1799 | * |
1800 | * The file format is detected automatically. If `NULL` is returned, then |
1801 | * @error will be set. |
1802 | * |
1803 | * Return value: (transfer full) (nullable): A newly-created pixbuf |
1804 | * |
1805 | * Since: 2.26 |
1806 | **/ |
1807 | GdkPixbuf * |
1808 | gdk_pixbuf_new_from_resource (const gchar *resource_path, |
1809 | GError **error) |
1810 | { |
1811 | GInputStream *stream; |
1812 | GdkPixbuf *pixbuf; |
1813 | |
1814 | pixbuf = _gdk_pixbuf_new_from_resource_try_pixdata (resource_path); |
1815 | if (pixbuf) |
1816 | return pixbuf; |
1817 | |
1818 | stream = g_resources_open_stream (path: resource_path, lookup_flags: 0, error); |
1819 | if (stream == NULL) |
1820 | return NULL; |
1821 | |
1822 | pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error); |
1823 | g_object_unref (object: stream); |
1824 | return pixbuf; |
1825 | } |
1826 | |
1827 | /** |
1828 | * gdk_pixbuf_new_from_resource_at_scale: (constructor) |
1829 | * @resource_path: the path of the resource file |
1830 | * @width: The width the image should have or -1 to not constrain the width |
1831 | * @height: The height the image should have or -1 to not constrain the height |
1832 | * @preserve_aspect_ratio: `TRUE` to preserve the image's aspect ratio |
1833 | * @error: Return location for an error |
1834 | * |
1835 | * Creates a new pixbuf by loading an image from an resource. |
1836 | * |
1837 | * The file format is detected automatically. If `NULL` is returned, then |
1838 | * @error will be set. |
1839 | * |
1840 | * The image will be scaled to fit in the requested size, optionally |
1841 | * preserving the image's aspect ratio. When preserving the aspect ratio, |
1842 | * a @width of -1 will cause the image to be scaled to the exact given |
1843 | * height, and a @height of -1 will cause the image to be scaled to the |
1844 | * exact given width. When not preserving aspect ratio, a @width or |
1845 | * @height of -1 means to not scale the image at all in that dimension. |
1846 | * |
1847 | * The stream is not closed. |
1848 | * |
1849 | * Return value: (transfer full) (nullable): A newly-created pixbuf |
1850 | * |
1851 | * Since: 2.26 |
1852 | */ |
1853 | GdkPixbuf * |
1854 | gdk_pixbuf_new_from_resource_at_scale (const char *resource_path, |
1855 | int width, |
1856 | int height, |
1857 | gboolean preserve_aspect_ratio, |
1858 | GError **error) |
1859 | { |
1860 | GInputStream *stream; |
1861 | GdkPixbuf *pixbuf; |
1862 | |
1863 | stream = g_resources_open_stream (path: resource_path, lookup_flags: 0, error); |
1864 | if (stream == NULL) |
1865 | return NULL; |
1866 | |
1867 | pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream, width, height, preserve_aspect_ratio, NULL, error); |
1868 | g_object_unref (object: stream); |
1869 | return pixbuf; |
1870 | } |
1871 | |
1872 | /** |
1873 | * gdk_pixbuf_new_from_stream_async: |
1874 | * @stream: a `GInputStream` from which to load the pixbuf |
1875 | * @cancellable: (allow-none): optional `GCancellable` object, `NULL` to ignore |
1876 | * @callback: a `GAsyncReadyCallback` to call when the pixbuf is loaded |
1877 | * @user_data: the data to pass to the callback function |
1878 | * |
1879 | * Creates a new pixbuf by asynchronously loading an image from an input stream. |
1880 | * |
1881 | * For more details see gdk_pixbuf_new_from_stream(), which is the synchronous |
1882 | * version of this function. |
1883 | * |
1884 | * When the operation is finished, @callback will be called in the main thread. |
1885 | * You can then call gdk_pixbuf_new_from_stream_finish() to get the result of |
1886 | * the operation. |
1887 | * |
1888 | * Since: 2.24 |
1889 | **/ |
1890 | void |
1891 | gdk_pixbuf_new_from_stream_async (GInputStream *stream, |
1892 | GCancellable *cancellable, |
1893 | GAsyncReadyCallback callback, |
1894 | gpointer user_data) |
1895 | { |
1896 | GTask *task; |
1897 | |
1898 | g_return_if_fail (G_IS_INPUT_STREAM (stream)); |
1899 | g_return_if_fail (callback != NULL); |
1900 | g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); |
1901 | |
1902 | task = g_task_new (source_object: stream, cancellable, callback, callback_data: user_data); |
1903 | g_task_set_source_tag (task, gdk_pixbuf_new_from_stream_async); |
1904 | g_task_set_task_data (task, task_data: gdk_pixbuf_loader_new (), task_data_destroy: g_object_unref); |
1905 | |
1906 | g_input_stream_read_bytes_async (stream, |
1907 | LOAD_BUFFER_SIZE, |
1908 | G_PRIORITY_DEFAULT, |
1909 | cancellable, |
1910 | callback: load_from_stream_async_cb, |
1911 | user_data: task); |
1912 | } |
1913 | |
1914 | /** |
1915 | * gdk_pixbuf_new_from_stream_finish: |
1916 | * @async_result: a `GAsyncResult` |
1917 | * @error: a `GError`, or `NULL` |
1918 | * |
1919 | * Finishes an asynchronous pixbuf creation operation started with |
1920 | * gdk_pixbuf_new_from_stream_async(). |
1921 | * |
1922 | * Return value: (transfer full) (nullable): the newly created pixbuf |
1923 | * |
1924 | * Since: 2.24 |
1925 | **/ |
1926 | GdkPixbuf * |
1927 | gdk_pixbuf_new_from_stream_finish (GAsyncResult *async_result, |
1928 | GError **error) |
1929 | { |
1930 | GTask *task; |
1931 | |
1932 | g_return_val_if_fail (G_IS_TASK (async_result), NULL); |
1933 | g_return_val_if_fail (!error || (error && !*error), NULL); |
1934 | |
1935 | task = G_TASK (async_result); |
1936 | |
1937 | g_warn_if_fail (g_task_get_source_tag (task) == gdk_pixbuf_new_from_stream_async || |
1938 | g_task_get_source_tag (task) == gdk_pixbuf_new_from_stream_at_scale_async); |
1939 | |
1940 | return g_task_propagate_pointer (task, error); |
1941 | } |
1942 | |
1943 | static void |
1944 | info_cb (GdkPixbufLoader *loader, |
1945 | int width, |
1946 | int height, |
1947 | gpointer data) |
1948 | { |
1949 | struct { |
1950 | GdkPixbufFormat *format; |
1951 | int width; |
1952 | int height; |
1953 | } *info = data; |
1954 | |
1955 | g_return_if_fail (width > 0 && height > 0); |
1956 | |
1957 | info->format = gdk_pixbuf_loader_get_format (loader); |
1958 | info->width = width; |
1959 | info->height = height; |
1960 | |
1961 | gdk_pixbuf_loader_set_size (loader, width: 0, height: 0); |
1962 | } |
1963 | |
1964 | /** |
1965 | * gdk_pixbuf_get_file_info: |
1966 | * @filename: (type filename): The name of the file to identify. |
1967 | * @width: (optional) (out): Return location for the width of the image |
1968 | * @height: (optional) (out): Return location for the height of the image |
1969 | * |
1970 | * Parses an image file far enough to determine its format and size. |
1971 | * |
1972 | * Returns: (nullable) (transfer none): A `GdkPixbufFormat` describing |
1973 | * the image format of the file |
1974 | * |
1975 | * Since: 2.4 |
1976 | **/ |
1977 | GdkPixbufFormat * |
1978 | gdk_pixbuf_get_file_info (const gchar *filename, |
1979 | gint *width, |
1980 | gint *height) |
1981 | { |
1982 | GdkPixbufLoader *loader; |
1983 | guchar buffer[SNIFF_BUFFER_SIZE]; |
1984 | int length; |
1985 | FILE *f; |
1986 | struct { |
1987 | GdkPixbufFormat *format; |
1988 | gint width; |
1989 | gint height; |
1990 | } info; |
1991 | |
1992 | g_return_val_if_fail (filename != NULL, NULL); |
1993 | |
1994 | f = g_fopen (filename: filename, modes: "rb" ); |
1995 | if (!f) |
1996 | return NULL; |
1997 | |
1998 | loader = _gdk_pixbuf_loader_new_with_filename (filename); |
1999 | |
2000 | info.format = NULL; |
2001 | info.width = -1; |
2002 | info.height = -1; |
2003 | |
2004 | g_signal_connect (loader, "size-prepared" , G_CALLBACK (info_cb), &info); |
2005 | |
2006 | while (!feof (stream: f) && !ferror (stream: f)) { |
2007 | length = fread (ptr: buffer, size: 1, n: sizeof (buffer), stream: f); |
2008 | if (length > 0) { |
2009 | if (!gdk_pixbuf_loader_write (loader, buf: buffer, count: length, NULL)) |
2010 | break; |
2011 | } |
2012 | if (info.format != NULL) |
2013 | break; |
2014 | } |
2015 | |
2016 | fclose (stream: f); |
2017 | gdk_pixbuf_loader_close (loader, NULL); |
2018 | g_object_unref (object: loader); |
2019 | |
2020 | if (width) |
2021 | *width = info.width; |
2022 | if (height) |
2023 | *height = info.height; |
2024 | |
2025 | return info.format; |
2026 | } |
2027 | |
2028 | typedef struct { |
2029 | gchar *filename; |
2030 | gint width; |
2031 | gint height; |
2032 | } GetFileInfoAsyncData; |
2033 | |
2034 | static void |
2035 | get_file_info_async_data_free (GetFileInfoAsyncData *data) |
2036 | { |
2037 | g_free (mem: data->filename); |
2038 | g_slice_free (GetFileInfoAsyncData, data); |
2039 | } |
2040 | |
2041 | static void |
2042 | get_file_info_thread (GTask *task, |
2043 | gpointer source_object, |
2044 | GetFileInfoAsyncData *data, |
2045 | GCancellable *cancellable) |
2046 | { |
2047 | GdkPixbufFormat *format; |
2048 | |
2049 | format = gdk_pixbuf_get_file_info (filename: data->filename, width: &data->width, height: &data->height); |
2050 | if (format == NULL) { |
2051 | g_task_return_new_error (task, |
2052 | GDK_PIXBUF_ERROR, |
2053 | code: GDK_PIXBUF_ERROR_UNKNOWN_TYPE, |
2054 | format: "Failed to recognize image format" ); |
2055 | } else { |
2056 | g_task_return_pointer (task, |
2057 | result: gdk_pixbuf_format_copy (format), |
2058 | result_destroy: (GDestroyNotify) gdk_pixbuf_format_free); |
2059 | } |
2060 | } |
2061 | |
2062 | /** |
2063 | * gdk_pixbuf_get_file_info_async: |
2064 | * @filename: (type filename): The name of the file to identify |
2065 | * @cancellable: (allow-none): optional `GCancellable` object, `NULL` to ignore |
2066 | * @callback: a `GAsyncReadyCallback` to call when the file info is available |
2067 | * @user_data: the data to pass to the callback function |
2068 | * |
2069 | * Asynchronously parses an image file far enough to determine its |
2070 | * format and size. |
2071 | * |
2072 | * For more details see gdk_pixbuf_get_file_info(), which is the synchronous |
2073 | * version of this function. |
2074 | * |
2075 | * When the operation is finished, @callback will be called in the |
2076 | * main thread. You can then call gdk_pixbuf_get_file_info_finish() to |
2077 | * get the result of the operation. |
2078 | * |
2079 | * Since: 2.32 |
2080 | **/ |
2081 | void |
2082 | gdk_pixbuf_get_file_info_async (const gchar *filename, |
2083 | GCancellable *cancellable, |
2084 | GAsyncReadyCallback callback, |
2085 | gpointer user_data) |
2086 | { |
2087 | GetFileInfoAsyncData *data; |
2088 | GTask *task; |
2089 | |
2090 | g_return_if_fail (filename != NULL); |
2091 | g_return_if_fail (callback != NULL); |
2092 | g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); |
2093 | |
2094 | data = g_slice_new0 (GetFileInfoAsyncData); |
2095 | data->filename = g_strdup (str: filename); |
2096 | |
2097 | task = g_task_new (NULL, cancellable, callback, callback_data: user_data); |
2098 | g_task_set_return_on_cancel (task, TRUE); |
2099 | g_task_set_source_tag (task, gdk_pixbuf_get_file_info_async); |
2100 | g_task_set_task_data (task, task_data: data, task_data_destroy: (GDestroyNotify) get_file_info_async_data_free); |
2101 | g_task_run_in_thread (task, task_func: (GTaskThreadFunc) get_file_info_thread); |
2102 | g_object_unref (object: task); |
2103 | } |
2104 | |
2105 | /** |
2106 | * gdk_pixbuf_get_file_info_finish: |
2107 | * @async_result: a `GAsyncResult` |
2108 | * @width: (out): Return location for the width of the image, or `NULL` |
2109 | * @height: (out): Return location for the height of the image, or `NULL` |
2110 | * @error: a `GError`, or `NULL` |
2111 | * |
2112 | * Finishes an asynchronous pixbuf parsing operation started with |
2113 | * gdk_pixbuf_get_file_info_async(). |
2114 | * |
2115 | * Returns: (transfer none) (nullable): A `GdkPixbufFormat` describing the |
2116 | * image format of the file |
2117 | * |
2118 | * Since: 2.32 |
2119 | **/ |
2120 | GdkPixbufFormat * |
2121 | gdk_pixbuf_get_file_info_finish (GAsyncResult *async_result, |
2122 | gint *width, |
2123 | gint *height, |
2124 | GError **error) |
2125 | { |
2126 | GetFileInfoAsyncData *data; |
2127 | GTask *task; |
2128 | |
2129 | g_return_val_if_fail (g_task_is_valid (async_result, NULL), NULL); |
2130 | |
2131 | task = G_TASK (async_result); |
2132 | |
2133 | g_return_val_if_fail (!error || (error && !*error), NULL); |
2134 | g_warn_if_fail (g_task_get_source_tag (task) == gdk_pixbuf_get_file_info_async); |
2135 | |
2136 | data = g_task_get_task_data (task); |
2137 | |
2138 | if (!g_task_had_error (task)) { |
2139 | if (width) |
2140 | *width = data->width; |
2141 | if (height) |
2142 | *height = data->height; |
2143 | } |
2144 | |
2145 | return g_task_propagate_pointer (task, error); |
2146 | } |
2147 | |
2148 | /** |
2149 | * gdk_pixbuf_new_from_xpm_data: |
2150 | * @data: (array zero-terminated=1): Pointer to inline XPM data. |
2151 | * |
2152 | * Creates a new pixbuf by parsing XPM data in memory. |
2153 | * |
2154 | * This data is commonly the result of including an XPM file into a |
2155 | * program's C source. |
2156 | * |
2157 | * Return value: A newly-created pixbuf |
2158 | **/ |
2159 | GdkPixbuf * |
2160 | gdk_pixbuf_new_from_xpm_data (const char **data) |
2161 | { |
2162 | GdkPixbuf *(* load_xpm_data) (const char **data); |
2163 | GdkPixbuf *pixbuf; |
2164 | GError *error = NULL; |
2165 | GdkPixbufModule *xpm_module; |
2166 | |
2167 | g_return_val_if_fail (data != NULL, NULL); |
2168 | |
2169 | xpm_module = _gdk_pixbuf_get_named_module (name: "xpm" , error: &error); |
2170 | if (xpm_module == NULL) { |
2171 | g_warning ("Error loading XPM image loader: %s" , error->message); |
2172 | g_error_free (error); |
2173 | return NULL; |
2174 | } |
2175 | |
2176 | if (!_gdk_pixbuf_load_module (image_module: xpm_module, error: &error)) { |
2177 | g_warning ("Error loading XPM image loader: %s" , error->message); |
2178 | g_error_free (error); |
2179 | return NULL; |
2180 | } |
2181 | |
2182 | if (xpm_module->load_xpm_data == NULL) { |
2183 | g_warning ("gdk-pixbuf XPM module lacks XPM data capability" ); |
2184 | pixbuf = NULL; |
2185 | } else { |
2186 | load_xpm_data = xpm_module->load_xpm_data; |
2187 | pixbuf = (* load_xpm_data) (data); |
2188 | } |
2189 | |
2190 | return pixbuf; |
2191 | } |
2192 | |
2193 | static void |
2194 | collect_save_options (va_list opts, |
2195 | gchar ***keys, |
2196 | gchar ***vals) |
2197 | { |
2198 | gchar *key; |
2199 | gchar *val; |
2200 | gchar *next; |
2201 | gint count; |
2202 | |
2203 | count = 0; |
2204 | *keys = NULL; |
2205 | *vals = NULL; |
2206 | |
2207 | next = va_arg (opts, gchar*); |
2208 | while (next) |
2209 | { |
2210 | key = next; |
2211 | val = va_arg (opts, gchar*); |
2212 | |
2213 | ++count; |
2214 | |
2215 | /* woo, slow */ |
2216 | *keys = g_realloc (mem: *keys, n_bytes: sizeof(gchar*) * (count + 1)); |
2217 | *vals = g_realloc (mem: *vals, n_bytes: sizeof(gchar*) * (count + 1)); |
2218 | |
2219 | (*keys)[count-1] = g_strdup (str: key); |
2220 | (*vals)[count-1] = g_strdup (str: val); |
2221 | |
2222 | (*keys)[count] = NULL; |
2223 | (*vals)[count] = NULL; |
2224 | |
2225 | next = va_arg (opts, gchar*); |
2226 | } |
2227 | } |
2228 | |
2229 | static gboolean |
2230 | save_to_file_callback (const gchar *buf, |
2231 | gsize count, |
2232 | GError **error, |
2233 | gpointer data) |
2234 | { |
2235 | FILE *filehandle = data; |
2236 | gsize n; |
2237 | |
2238 | n = fwrite (ptr: buf, size: 1, n: count, s: filehandle); |
2239 | if (n != count) { |
2240 | gint save_errno = errno; |
2241 | g_set_error (err: error, |
2242 | G_FILE_ERROR, |
2243 | code: g_file_error_from_errno (err_no: save_errno), |
2244 | _("Error writing to image file: %s" ), |
2245 | g_strerror (errnum: save_errno)); |
2246 | return FALSE; |
2247 | } |
2248 | return TRUE; |
2249 | } |
2250 | |
2251 | static gboolean |
2252 | gdk_pixbuf_real_save (GdkPixbuf *pixbuf, |
2253 | FILE *filehandle, |
2254 | const char *type, |
2255 | gchar **keys, |
2256 | gchar **values, |
2257 | GError **error) |
2258 | { |
2259 | gboolean ret; |
2260 | GdkPixbufModule *image_module = NULL; |
2261 | |
2262 | image_module = _gdk_pixbuf_get_named_module (name: type, error); |
2263 | |
2264 | if (image_module == NULL) |
2265 | return FALSE; |
2266 | |
2267 | if (!_gdk_pixbuf_load_module (image_module, error)) |
2268 | return FALSE; |
2269 | |
2270 | if (image_module->save) { |
2271 | /* save normally */ |
2272 | ret = (* image_module->save) (filehandle, pixbuf, |
2273 | keys, values, |
2274 | error); |
2275 | } else if (image_module->save_to_callback) { |
2276 | /* save with simple callback */ |
2277 | ret = (* image_module->save_to_callback) (save_to_file_callback, |
2278 | filehandle, pixbuf, |
2279 | keys, values, |
2280 | error); |
2281 | } else { |
2282 | /* can't save */ |
2283 | g_set_error (err: error, |
2284 | GDK_PIXBUF_ERROR, |
2285 | code: GDK_PIXBUF_ERROR_UNSUPPORTED_OPERATION, |
2286 | _("This build of gdk-pixbuf does not support saving the image format: %s" ), |
2287 | type); |
2288 | ret = FALSE; |
2289 | } |
2290 | |
2291 | return ret; |
2292 | } |
2293 | |
2294 | #define TMP_FILE_BUF_SIZE 4096 |
2295 | |
2296 | static gboolean |
2297 | save_to_callback_with_tmp_file (GdkPixbufModule *image_module, |
2298 | GdkPixbuf *pixbuf, |
2299 | GdkPixbufSaveFunc save_func, |
2300 | gpointer user_data, |
2301 | gchar **keys, |
2302 | gchar **values, |
2303 | GError **error) |
2304 | { |
2305 | int fd; |
2306 | FILE *f = NULL; |
2307 | gboolean retval = FALSE; |
2308 | gchar *buf = NULL; |
2309 | gsize n; |
2310 | gchar *filename = NULL; |
2311 | |
2312 | buf = g_try_malloc (TMP_FILE_BUF_SIZE); |
2313 | if (buf == NULL) { |
2314 | g_set_error_literal (err: error, |
2315 | GDK_PIXBUF_ERROR, |
2316 | code: GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, |
2317 | _("Insufficient memory to save image to callback" )); |
2318 | goto end; |
2319 | } |
2320 | |
2321 | fd = g_file_open_tmp (tmpl: "gdkpixbuf-save-tmp.XXXXXX" , name_used: &filename, error); |
2322 | if (fd == -1) |
2323 | goto end; |
2324 | f = fdopen (fd: fd, modes: "wb+" ); |
2325 | if (f == NULL) { |
2326 | gint save_errno = errno; |
2327 | g_set_error_literal (err: error, |
2328 | G_FILE_ERROR, |
2329 | code: g_file_error_from_errno (err_no: save_errno), |
2330 | _("Failed to open temporary file" )); |
2331 | goto end; |
2332 | } |
2333 | |
2334 | retval = (image_module->save) (f, pixbuf, keys, values, error); |
2335 | if (!retval) |
2336 | goto end; |
2337 | |
2338 | rewind (stream: f); |
2339 | for (;;) { |
2340 | n = fread (ptr: buf, size: 1, TMP_FILE_BUF_SIZE, stream: f); |
2341 | if (n > 0) { |
2342 | if (!save_func (buf, n, error, user_data)) |
2343 | goto end; |
2344 | } |
2345 | if (n != TMP_FILE_BUF_SIZE) |
2346 | break; |
2347 | } |
2348 | if (ferror (stream: f)) { |
2349 | gint save_errno = errno; |
2350 | g_set_error_literal (err: error, |
2351 | G_FILE_ERROR, |
2352 | code: g_file_error_from_errno (err_no: save_errno), |
2353 | _("Failed to read from temporary file" )); |
2354 | goto end; |
2355 | } |
2356 | retval = TRUE; |
2357 | |
2358 | end: |
2359 | /* cleanup and return retval */ |
2360 | if (f) |
2361 | fclose (stream: f); |
2362 | if (filename) { |
2363 | g_unlink (filename); |
2364 | g_free (mem: filename); |
2365 | } |
2366 | g_free (mem: buf); |
2367 | |
2368 | return retval; |
2369 | } |
2370 | |
2371 | static gboolean |
2372 | gdk_pixbuf_real_save_to_callback (GdkPixbuf *pixbuf, |
2373 | GdkPixbufSaveFunc save_func, |
2374 | gpointer user_data, |
2375 | const char *type, |
2376 | gchar **keys, |
2377 | gchar **values, |
2378 | GError **error) |
2379 | { |
2380 | gboolean ret; |
2381 | GdkPixbufModule *image_module = NULL; |
2382 | |
2383 | image_module = _gdk_pixbuf_get_named_module (name: type, error); |
2384 | |
2385 | if (image_module == NULL) |
2386 | return FALSE; |
2387 | |
2388 | if (!_gdk_pixbuf_load_module (image_module, error)) |
2389 | return FALSE; |
2390 | |
2391 | if (image_module->save_to_callback) { |
2392 | /* save normally */ |
2393 | ret = (* image_module->save_to_callback) (save_func, user_data, |
2394 | pixbuf, keys, values, |
2395 | error); |
2396 | } else if (image_module->save) { |
2397 | /* use a temporary file */ |
2398 | ret = save_to_callback_with_tmp_file (image_module, pixbuf, |
2399 | save_func, user_data, |
2400 | keys, values, |
2401 | error); |
2402 | } else { |
2403 | /* can't save */ |
2404 | g_set_error (err: error, |
2405 | GDK_PIXBUF_ERROR, |
2406 | code: GDK_PIXBUF_ERROR_UNSUPPORTED_OPERATION, |
2407 | _("This build of gdk-pixbuf does not support saving the image format: %s" ), |
2408 | type); |
2409 | ret = FALSE; |
2410 | } |
2411 | |
2412 | return ret; |
2413 | } |
2414 | |
2415 | |
2416 | /** |
2417 | * gdk_pixbuf_save: |
2418 | * @pixbuf: a `GdkPixbuf`. |
2419 | * @filename: (type filename): name of file to save. |
2420 | * @type: name of file format. |
2421 | * @error: (nullable): return location for error |
2422 | * @...: list of key-value save options, followed by `NULL` |
2423 | * |
2424 | * Saves pixbuf to a file in format @type. By default, "jpeg", "png", "ico" |
2425 | * and "bmp" are possible file formats to save in, but more formats may be |
2426 | * installed. The list of all writable formats can be determined in the |
2427 | * following way: |
2428 | * |
2429 | * ```c |
2430 | * void add_if_writable (GdkPixbufFormat *data, GSList **list) |
2431 | * { |
2432 | * if (gdk_pixbuf_format_is_writable (data)) |
2433 | * *list = g_slist_prepend (*list, data); |
2434 | * } |
2435 | * |
2436 | * GSList *formats = gdk_pixbuf_get_formats (); |
2437 | * GSList *writable_formats = NULL; |
2438 | * g_slist_foreach (formats, add_if_writable, &writable_formats); |
2439 | * g_slist_free (formats); |
2440 | * ``` |
2441 | * |
2442 | * If `error` is set, `FALSE` will be returned. Possible errors include |
2443 | * those in the `GDK_PIXBUF_ERROR` domain and those in the `G_FILE_ERROR` |
2444 | * domain. |
2445 | * |
2446 | * The variable argument list should be `NULL`-terminated; if not empty, |
2447 | * it should contain pairs of strings that modify the save |
2448 | * parameters. For example: |
2449 | * |
2450 | * ```c |
2451 | * gdk_pixbuf_save (pixbuf, handle, "jpeg", &error, "quality", "100", NULL); |
2452 | * ``` |
2453 | * |
2454 | * Currently only few parameters exist. |
2455 | * |
2456 | * JPEG images can be saved with a "quality" parameter; its value should be |
2457 | * in the range `[0, 100]`. JPEG and PNG density can be set by setting the |
2458 | * "x-dpi" and "y-dpi" parameters to the appropriate values in dots per inch. |
2459 | * |
2460 | * Text chunks can be attached to PNG images by specifying parameters of |
2461 | * the form "tEXt::key", where key is an ASCII string of length 1-79. |
2462 | * The values are UTF-8 encoded strings. The PNG compression level can |
2463 | * be specified using the "compression" parameter; it's value is in an |
2464 | * integer in the range of `[0, 9]`. |
2465 | * |
2466 | * ICC color profiles can also be embedded into PNG, JPEG and TIFF images. |
2467 | * The "icc-profile" value should be the complete ICC profile encoded |
2468 | * into base64. |
2469 | * |
2470 | * ```c |
2471 | * char *contents; |
2472 | * gsize length; |
2473 | * |
2474 | * // icm_path is set elsewhere |
2475 | * g_file_get_contents (icm_path, &contents, &length, NULL); |
2476 | * |
2477 | * char *contents_encode = g_base64_encode ((const guchar *) contents, length); |
2478 | * |
2479 | * gdk_pixbuf_save (pixbuf, handle, "png", &error, "icc-profile", contents_encode, NULL); |
2480 | * ``` |
2481 | * |
2482 | * TIFF images recognize: |
2483 | * |
2484 | * 1. a "bits-per-sample" option (integer) which can be either 1 for saving |
2485 | * bi-level CCITTFAX4 images, or 8 for saving 8-bits per sample |
2486 | * 2. a "compression" option (integer) which can be 1 for no compression, |
2487 | * 2 for Huffman, 5 for LZW, 7 for JPEG and 8 for DEFLATE (see the libtiff |
2488 | * documentation and tiff.h for all supported codec values) |
2489 | * 3. an "icc-profile" option (zero-terminated string) containing a base64 |
2490 | * encoded ICC color profile. |
2491 | * |
2492 | * ICO images can be saved in depth 16, 24, or 32, by using the "depth" |
2493 | * parameter. When the ICO saver is given "x_hot" and "y_hot" parameters, |
2494 | * it produces a CUR instead of an ICO. |
2495 | * |
2496 | * Return value: `TRUE` on success, and `FALSE` otherwise |
2497 | **/ |
2498 | gboolean |
2499 | gdk_pixbuf_save (GdkPixbuf *pixbuf, |
2500 | const char *filename, |
2501 | const char *type, |
2502 | GError **error, |
2503 | ...) |
2504 | { |
2505 | gchar **keys = NULL; |
2506 | gchar **values = NULL; |
2507 | va_list args; |
2508 | gboolean result; |
2509 | |
2510 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); |
2511 | |
2512 | va_start (args, error); |
2513 | |
2514 | collect_save_options (opts: args, keys: &keys, vals: &values); |
2515 | |
2516 | va_end (args); |
2517 | |
2518 | result = gdk_pixbuf_savev (pixbuf, filename, type, |
2519 | option_keys: keys, option_values: values, |
2520 | error); |
2521 | |
2522 | g_strfreev (str_array: keys); |
2523 | g_strfreev (str_array: values); |
2524 | |
2525 | return result; |
2526 | } |
2527 | |
2528 | /** |
2529 | * gdk_pixbuf_savev: |
2530 | * @pixbuf: a `GdkPixbuf`. |
2531 | * @filename: (type filename): name of file to save. |
2532 | * @type: name of file format. |
2533 | * @option_keys: (array zero-terminated=1) (element-type utf8) (nullable): name of options to set |
2534 | * @option_values: (array zero-terminated=1) (element-type utf8) (nullable): values for named options |
2535 | * @error: (allow-none): return location for error, or `NULL` |
2536 | * |
2537 | * Vector version of `gdk_pixbuf_save()`. |
2538 | * |
2539 | * Saves pixbuf to a file in `type`, which is currently "jpeg", "png", "tiff", "ico" or "bmp". |
2540 | * |
2541 | * If @error is set, `FALSE` will be returned. |
2542 | * |
2543 | * See [method@GdkPixbuf.Pixbuf.save] for more details. |
2544 | * |
2545 | * Return value: whether an error was set |
2546 | **/ |
2547 | |
2548 | gboolean |
2549 | gdk_pixbuf_savev (GdkPixbuf *pixbuf, |
2550 | const char *filename, |
2551 | const char *type, |
2552 | char **option_keys, |
2553 | char **option_values, |
2554 | GError **error) |
2555 | { |
2556 | FILE *f = NULL; |
2557 | gboolean result; |
2558 | |
2559 | g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), FALSE); |
2560 | g_return_val_if_fail (gdk_pixbuf_get_width (pixbuf) >= 0, FALSE); |
2561 | g_return_val_if_fail (gdk_pixbuf_get_height (pixbuf) >= 0, FALSE); |
2562 | g_return_val_if_fail (gdk_pixbuf_get_n_channels (pixbuf) >= 0, FALSE); |
2563 | g_return_val_if_fail (filename != NULL, FALSE); |
2564 | g_return_val_if_fail (type != NULL, FALSE); |
2565 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); |
2566 | |
2567 | f = g_fopen (filename: filename, modes: "wb" ); |
2568 | |
2569 | if (f == NULL) { |
2570 | gint save_errno = errno; |
2571 | gchar *display_name = g_filename_display_name (filename); |
2572 | g_set_error (err: error, |
2573 | G_FILE_ERROR, |
2574 | code: g_file_error_from_errno (err_no: save_errno), |
2575 | _("Failed to open “%s” for writing: %s" ), |
2576 | display_name, |
2577 | g_strerror (errnum: save_errno)); |
2578 | g_free (mem: display_name); |
2579 | return FALSE; |
2580 | } |
2581 | |
2582 | |
2583 | result = gdk_pixbuf_real_save (pixbuf, filehandle: f, type, |
2584 | keys: option_keys, values: option_values, |
2585 | error); |
2586 | |
2587 | |
2588 | if (!result) { |
2589 | g_return_val_if_fail (error == NULL || *error != NULL, FALSE); |
2590 | fclose (stream: f); |
2591 | g_unlink (filename); |
2592 | return FALSE; |
2593 | } |
2594 | |
2595 | if (fclose (stream: f) < 0) { |
2596 | gint save_errno = errno; |
2597 | gchar *display_name = g_filename_display_name (filename); |
2598 | g_set_error (err: error, |
2599 | G_FILE_ERROR, |
2600 | code: g_file_error_from_errno (err_no: save_errno), |
2601 | _("Failed to close “%s” while writing image, all data may not have been saved: %s" ), |
2602 | display_name, |
2603 | g_strerror (errnum: save_errno)); |
2604 | g_free (mem: display_name); |
2605 | return FALSE; |
2606 | } |
2607 | |
2608 | return TRUE; |
2609 | } |
2610 | |
2611 | #ifdef G_OS_WIN32 |
2612 | |
2613 | /** |
2614 | * gdk_pixbuf_savev_utf8: |
2615 | * @pixbuf: a `GdkPixbuf`. |
2616 | * @filename: name of file to save. |
2617 | * @type: name of file format. |
2618 | * @option_keys: (array zero-terminated=1) (element-type utf8) (nullable): name of options to set |
2619 | * @option_values: (array zero-terminated=1) (element-type utf8) (nullable): values for named options |
2620 | * @error: (allow-none): return location for error, or `NULL` |
2621 | * |
2622 | * Same as gdk_pixbuf_savev() |
2623 | * |
2624 | * Return value: whether an error was set |
2625 | **/ |
2626 | gboolean |
2627 | gdk_pixbuf_savev_utf8 (GdkPixbuf *pixbuf, |
2628 | const char *filename, |
2629 | const char *type, |
2630 | char **option_keys, |
2631 | char **option_values, |
2632 | GError **error) |
2633 | { |
2634 | return gdk_pixbuf_savev (pixbuf, filename, type, option_keys, |
2635 | option_values, error); |
2636 | } |
2637 | |
2638 | #endif |
2639 | |
2640 | /** |
2641 | * gdk_pixbuf_save_to_callback: |
2642 | * @pixbuf: a `GdkPixbuf`. |
2643 | * @save_func: (scope call): a function that is called to save each block of data that |
2644 | * the save routine generates. |
2645 | * @user_data: user data to pass to the save function. |
2646 | * @type: name of file format. |
2647 | * @error: (allow-none): return location for error, or `NULL` |
2648 | * @...: list of key-value save options |
2649 | * |
2650 | * Saves pixbuf in format `type` by feeding the produced data to a |
2651 | * callback. |
2652 | * |
2653 | * This function can be used when you want to store the image to something |
2654 | * other than a file, such as an in-memory buffer or a socket. |
2655 | * |
2656 | * If @error is set, `FALSE` will be returned. Possible errors |
2657 | * include those in the `GDK_PIXBUF_ERROR` domain and whatever the save |
2658 | * function generates. |
2659 | * |
2660 | * See [method@GdkPixbuf.Pixbuf.save] for more details. |
2661 | * |
2662 | * Return value: whether an error was set |
2663 | * |
2664 | * Since: 2.4 |
2665 | **/ |
2666 | gboolean |
2667 | gdk_pixbuf_save_to_callback (GdkPixbuf *pixbuf, |
2668 | GdkPixbufSaveFunc save_func, |
2669 | gpointer user_data, |
2670 | const char *type, |
2671 | GError **error, |
2672 | ...) |
2673 | { |
2674 | gchar **keys = NULL; |
2675 | gchar **values = NULL; |
2676 | va_list args; |
2677 | gboolean result; |
2678 | |
2679 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); |
2680 | |
2681 | va_start (args, error); |
2682 | |
2683 | collect_save_options (opts: args, keys: &keys, vals: &values); |
2684 | |
2685 | va_end (args); |
2686 | |
2687 | result = gdk_pixbuf_save_to_callbackv (pixbuf, save_func, user_data, |
2688 | type, option_keys: keys, option_values: values, |
2689 | error); |
2690 | |
2691 | g_strfreev (str_array: keys); |
2692 | g_strfreev (str_array: values); |
2693 | |
2694 | return result; |
2695 | } |
2696 | |
2697 | /** |
2698 | * gdk_pixbuf_save_to_callbackv: |
2699 | * @pixbuf: a `GdkPixbuf`. |
2700 | * @save_func: (scope call): a function that is called to save each block of data that |
2701 | * the save routine generates. |
2702 | * @user_data: (closure): user data to pass to the save function. |
2703 | * @type: name of file format. |
2704 | * @option_keys: (array zero-terminated=1) (element-type utf8) (nullable): name of options to set |
2705 | * @option_values: (array zero-terminated=1) (element-type utf8) (nullable): values for named options |
2706 | * @error: (allow-none): return location for error, or `NULL` |
2707 | * |
2708 | * Vector version of `gdk_pixbuf_save_to_callback()`. |
2709 | * |
2710 | * Saves pixbuf to a callback in format @type, which is currently "jpeg", |
2711 | * "png", "tiff", "ico" or "bmp". |
2712 | * |
2713 | * If @error is set, `FALSE` will be returned. |
2714 | * |
2715 | * See [method@GdkPixbuf.Pixbuf.save_to_callback] for more details. |
2716 | * |
2717 | * Return value: whether an error was set |
2718 | * |
2719 | * Since: 2.4 |
2720 | **/ |
2721 | gboolean |
2722 | gdk_pixbuf_save_to_callbackv (GdkPixbuf *pixbuf, |
2723 | GdkPixbufSaveFunc save_func, |
2724 | gpointer user_data, |
2725 | const char *type, |
2726 | char **option_keys, |
2727 | char **option_values, |
2728 | GError **error) |
2729 | { |
2730 | gboolean result; |
2731 | |
2732 | g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), FALSE); |
2733 | g_return_val_if_fail (gdk_pixbuf_get_width (pixbuf) >= 0, FALSE); |
2734 | g_return_val_if_fail (gdk_pixbuf_get_height (pixbuf) >= 0, FALSE); |
2735 | g_return_val_if_fail (gdk_pixbuf_get_n_channels (pixbuf) >= 0, FALSE); |
2736 | g_return_val_if_fail (save_func != NULL, FALSE); |
2737 | g_return_val_if_fail (type != NULL, FALSE); |
2738 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); |
2739 | |
2740 | result = gdk_pixbuf_real_save_to_callback (pixbuf, |
2741 | save_func, user_data, type, |
2742 | keys: option_keys, values: option_values, |
2743 | error); |
2744 | |
2745 | if (!result) { |
2746 | g_return_val_if_fail (error == NULL || *error != NULL, FALSE); |
2747 | return FALSE; |
2748 | } |
2749 | |
2750 | return TRUE; |
2751 | } |
2752 | |
2753 | /** |
2754 | * gdk_pixbuf_save_to_buffer: |
2755 | * @pixbuf: a `GdkPixbuf`. |
2756 | * @buffer: (array length=buffer_size) (out) (element-type guint8): location to receive a pointer |
2757 | * to the new buffer. |
2758 | * @buffer_size: location to receive the size of the new buffer. |
2759 | * @type: name of file format. |
2760 | * @error: (allow-none): return location for error, or `NULL` |
2761 | * @...: list of key-value save options |
2762 | * |
2763 | * Saves pixbuf to a new buffer in format `type`, which is currently "jpeg", |
2764 | * "png", "tiff", "ico" or "bmp". |
2765 | * |
2766 | * This is a convenience function that uses `gdk_pixbuf_save_to_callback()` |
2767 | * to do the real work. |
2768 | * |
2769 | * Note that the buffer is not `NUL`-terminated and may contain embedded `NUL` |
2770 | * characters. |
2771 | * |
2772 | * If @error is set, `FALSE` will be returned and @buffer will be set to |
2773 | * `NULL`. Possible errors include those in the `GDK_PIXBUF_ERROR` |
2774 | * domain. |
2775 | * |
2776 | * See `gdk_pixbuf_save()` for more details. |
2777 | * |
2778 | * Return value: whether an error was set |
2779 | * |
2780 | * Since: 2.4 |
2781 | **/ |
2782 | gboolean |
2783 | gdk_pixbuf_save_to_buffer (GdkPixbuf *pixbuf, |
2784 | gchar **buffer, |
2785 | gsize *buffer_size, |
2786 | const char *type, |
2787 | GError **error, |
2788 | ...) |
2789 | { |
2790 | gchar **keys = NULL; |
2791 | gchar **values = NULL; |
2792 | va_list args; |
2793 | gboolean result; |
2794 | |
2795 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); |
2796 | |
2797 | va_start (args, error); |
2798 | |
2799 | collect_save_options (opts: args, keys: &keys, vals: &values); |
2800 | |
2801 | va_end (args); |
2802 | |
2803 | result = gdk_pixbuf_save_to_bufferv (pixbuf, buffer, buffer_size, |
2804 | type, option_keys: keys, option_values: values, |
2805 | error); |
2806 | |
2807 | g_strfreev (str_array: keys); |
2808 | g_strfreev (str_array: values); |
2809 | |
2810 | return result; |
2811 | } |
2812 | |
2813 | struct SaveToBufferData { |
2814 | gchar *buffer; |
2815 | gsize len, max; |
2816 | }; |
2817 | |
2818 | static gboolean |
2819 | save_to_buffer_callback (const gchar *data, |
2820 | gsize count, |
2821 | GError **error, |
2822 | gpointer user_data) |
2823 | { |
2824 | struct SaveToBufferData *sdata = user_data; |
2825 | gchar *new_buffer; |
2826 | gsize new_max; |
2827 | |
2828 | if (sdata->len + count > sdata->max) { |
2829 | new_max = MAX (sdata->max*2, sdata->len + count); |
2830 | new_buffer = g_try_realloc (mem: sdata->buffer, n_bytes: new_max); |
2831 | if (!new_buffer) { |
2832 | g_set_error_literal (err: error, |
2833 | GDK_PIXBUF_ERROR, |
2834 | code: GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, |
2835 | _("Insufficient memory to save image into a buffer" )); |
2836 | return FALSE; |
2837 | } |
2838 | sdata->buffer = new_buffer; |
2839 | sdata->max = new_max; |
2840 | } |
2841 | memcpy (dest: sdata->buffer + sdata->len, src: data, n: count); |
2842 | sdata->len += count; |
2843 | return TRUE; |
2844 | } |
2845 | |
2846 | /** |
2847 | * gdk_pixbuf_save_to_bufferv: |
2848 | * @pixbuf: a `GdkPixbuf`. |
2849 | * @buffer: (array length=buffer_size) (out) (element-type guint8): |
2850 | * location to receive a pointer to the new buffer. |
2851 | * @buffer_size: location to receive the size of the new buffer. |
2852 | * @type: name of file format. |
2853 | * @option_keys: (array zero-terminated=1) (element-type utf8) (nullable): name of options to set |
2854 | * @option_values: (array zero-terminated=1) (element-type utf8) (nullable): values for named options |
2855 | * @error: (allow-none): return location for error, or `NULL` |
2856 | * |
2857 | * Vector version of `gdk_pixbuf_save_to_buffer()`. |
2858 | * |
2859 | * Saves pixbuf to a new buffer in format @type, which is currently "jpeg", |
2860 | * "tiff", "png", "ico" or "bmp". |
2861 | * |
2862 | * See [method@GdkPixbuf.Pixbuf.save_to_buffer] for more details. |
2863 | * |
2864 | * Return value: whether an error was set |
2865 | * |
2866 | * Since: 2.4 |
2867 | **/ |
2868 | gboolean |
2869 | gdk_pixbuf_save_to_bufferv (GdkPixbuf *pixbuf, |
2870 | gchar **buffer, |
2871 | gsize *buffer_size, |
2872 | const char *type, |
2873 | char **option_keys, |
2874 | char **option_values, |
2875 | GError **error) |
2876 | { |
2877 | static const gint initial_max = 1024; |
2878 | struct SaveToBufferData sdata; |
2879 | |
2880 | *buffer = NULL; |
2881 | *buffer_size = 0; |
2882 | |
2883 | sdata.buffer = g_try_malloc (n_bytes: initial_max); |
2884 | sdata.max = initial_max; |
2885 | sdata.len = 0; |
2886 | if (!sdata.buffer) { |
2887 | g_set_error_literal (err: error, |
2888 | GDK_PIXBUF_ERROR, |
2889 | code: GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, |
2890 | _("Insufficient memory to save image into a buffer" )); |
2891 | return FALSE; |
2892 | } |
2893 | |
2894 | if (!gdk_pixbuf_save_to_callbackv (pixbuf, |
2895 | save_func: save_to_buffer_callback, user_data: &sdata, |
2896 | type, option_keys, option_values, |
2897 | error)) { |
2898 | g_free (mem: sdata.buffer); |
2899 | return FALSE; |
2900 | } |
2901 | |
2902 | *buffer = sdata.buffer; |
2903 | *buffer_size = sdata.len; |
2904 | return TRUE; |
2905 | } |
2906 | |
2907 | typedef struct { |
2908 | GOutputStream *stream; |
2909 | GCancellable *cancellable; |
2910 | } SaveToStreamData; |
2911 | |
2912 | static gboolean |
2913 | save_to_stream (const gchar *buffer, |
2914 | gsize count, |
2915 | GError **error, |
2916 | gpointer data) |
2917 | { |
2918 | SaveToStreamData *sdata = (SaveToStreamData *)data; |
2919 | gsize remaining; |
2920 | gssize written; |
2921 | GError *my_error = NULL; |
2922 | |
2923 | remaining = count; |
2924 | written = 0; |
2925 | while (remaining > 0) { |
2926 | buffer += written; |
2927 | remaining -= written; |
2928 | written = g_output_stream_write (stream: sdata->stream, |
2929 | buffer, count: remaining, |
2930 | cancellable: sdata->cancellable, |
2931 | error: &my_error); |
2932 | if (written < 0) { |
2933 | if (!my_error) { |
2934 | g_set_error_literal (err: error, |
2935 | G_IO_ERROR, code: 0, |
2936 | _("Error writing to image stream" )); |
2937 | } |
2938 | else { |
2939 | g_propagate_error (dest: error, src: my_error); |
2940 | } |
2941 | return FALSE; |
2942 | } |
2943 | } |
2944 | |
2945 | return TRUE; |
2946 | } |
2947 | |
2948 | /** |
2949 | * gdk_pixbuf_save_to_streamv: |
2950 | * @pixbuf: a `GdkPixbuf` |
2951 | * @stream: a `GOutputStream` to save the pixbuf to |
2952 | * @type: name of file format |
2953 | * @option_keys: (array zero-terminated=1) (element-type utf8) (nullable): name of options to set |
2954 | * @option_values: (array zero-terminated=1) (element-type utf8) (nullable): values for named options |
2955 | * @cancellable: (nullable): optional `GCancellable` object, `NULL` to ignore |
2956 | * @error: return location for error |
2957 | * |
2958 | * Saves `pixbuf` to an output stream. |
2959 | * |
2960 | * Supported file formats are currently "jpeg", "tiff", "png", "ico" or |
2961 | * "bmp". |
2962 | * |
2963 | * See [method@GdkPixbuf.Pixbuf.save_to_stream] for more details. |
2964 | * |
2965 | * Returns: `TRUE` if the pixbuf was saved successfully, `FALSE` if an |
2966 | * error was set. |
2967 | * |
2968 | * Since: 2.36 |
2969 | */ |
2970 | gboolean |
2971 | gdk_pixbuf_save_to_streamv (GdkPixbuf *pixbuf, |
2972 | GOutputStream *stream, |
2973 | const char *type, |
2974 | char **option_keys, |
2975 | char **option_values, |
2976 | GCancellable *cancellable, |
2977 | GError **error) |
2978 | { |
2979 | SaveToStreamData data; |
2980 | |
2981 | data.stream = stream; |
2982 | data.cancellable = cancellable; |
2983 | |
2984 | return gdk_pixbuf_save_to_callbackv (pixbuf, save_func: save_to_stream, |
2985 | user_data: &data, type, |
2986 | option_keys, option_values, |
2987 | error); |
2988 | } |
2989 | |
2990 | /** |
2991 | * gdk_pixbuf_save_to_stream: |
2992 | * @pixbuf: a `GdkPixbuf` |
2993 | * @stream: a `GOutputStream` to save the pixbuf to |
2994 | * @type: name of file format |
2995 | * @cancellable: (allow-none): optional `GCancellable` object, `NULL` to ignore |
2996 | * @error: (allow-none): return location for error, or `NULL` |
2997 | * @...: list of key-value save options |
2998 | * |
2999 | * Saves `pixbuf` to an output stream. |
3000 | * |
3001 | * Supported file formats are currently "jpeg", "tiff", "png", "ico" or |
3002 | * "bmp". See `gdk_pixbuf_save_to_buffer()` for more details. |
3003 | * |
3004 | * The `cancellable` can be used to abort the operation from another |
3005 | * thread. If the operation was cancelled, the error `G_IO_ERROR_CANCELLED` |
3006 | * will be returned. Other possible errors are in the `GDK_PIXBUF_ERROR` |
3007 | * and `G_IO_ERROR` domains. |
3008 | * |
3009 | * The stream is not closed at the end of this call. |
3010 | * |
3011 | * Returns: `TRUE` if the pixbuf was saved successfully, `FALSE` if an |
3012 | * error was set. |
3013 | * |
3014 | * Since: 2.14 |
3015 | */ |
3016 | gboolean |
3017 | gdk_pixbuf_save_to_stream (GdkPixbuf *pixbuf, |
3018 | GOutputStream *stream, |
3019 | const char *type, |
3020 | GCancellable *cancellable, |
3021 | GError **error, |
3022 | ...) |
3023 | { |
3024 | gboolean res; |
3025 | gchar **keys = NULL; |
3026 | gchar **values = NULL; |
3027 | va_list args; |
3028 | |
3029 | va_start (args, error); |
3030 | collect_save_options (opts: args, keys: &keys, vals: &values); |
3031 | va_end (args); |
3032 | |
3033 | res = gdk_pixbuf_save_to_streamv (pixbuf, stream, type, |
3034 | option_keys: keys, option_values: values, |
3035 | cancellable, error); |
3036 | |
3037 | g_strfreev (str_array: keys); |
3038 | g_strfreev (str_array: values); |
3039 | |
3040 | return res; |
3041 | } |
3042 | |
3043 | typedef struct { |
3044 | GOutputStream *stream; |
3045 | gchar *type; |
3046 | gchar **keys; |
3047 | gchar **values; |
3048 | } SaveToStreamAsyncData; |
3049 | |
3050 | static void |
3051 | save_to_stream_async_data_free (SaveToStreamAsyncData *data) |
3052 | { |
3053 | if (data->stream) |
3054 | g_object_unref (object: data->stream); |
3055 | g_strfreev (str_array: data->keys); |
3056 | g_strfreev (str_array: data->values); |
3057 | g_free (mem: data->type); |
3058 | g_slice_free (SaveToStreamAsyncData, data); |
3059 | } |
3060 | |
3061 | static void |
3062 | save_to_stream_thread (GTask *task, |
3063 | GdkPixbuf *pixbuf, |
3064 | SaveToStreamAsyncData *data, |
3065 | GCancellable *cancellable) |
3066 | { |
3067 | SaveToStreamData sync_data; |
3068 | gboolean retval; |
3069 | GError *error = NULL; |
3070 | |
3071 | sync_data.stream = data->stream; |
3072 | sync_data.cancellable = cancellable; |
3073 | |
3074 | retval = gdk_pixbuf_save_to_callbackv (pixbuf, save_func: save_to_stream, |
3075 | user_data: &sync_data, type: data->type, |
3076 | option_keys: data->keys, option_values: data->values, |
3077 | error: &error); |
3078 | |
3079 | if (retval == FALSE) { |
3080 | g_task_return_error (task, error); |
3081 | } else { |
3082 | g_task_return_boolean (task, TRUE); |
3083 | } |
3084 | } |
3085 | |
3086 | /** |
3087 | * gdk_pixbuf_save_to_streamv_async: |
3088 | * @pixbuf: a `GdkPixbuf` |
3089 | * @stream: a `GOutputStream` to which to save the pixbuf |
3090 | * @type: name of file format |
3091 | * @option_keys: (array zero-terminated=1) (element-type utf8) (nullable): name of options to set |
3092 | * @option_values: (array zero-terminated=1) (element-type utf8) (nullable): values for named options |
3093 | * @cancellable: (allow-none): optional `GCancellable` object, `NULL` to ignore |
3094 | * @callback: a `GAsyncReadyCallback` to call when the pixbuf is saved |
3095 | * @user_data: the data to pass to the callback function |
3096 | * |
3097 | * Saves `pixbuf` to an output stream asynchronously. |
3098 | * |
3099 | * For more details see gdk_pixbuf_save_to_streamv(), which is the synchronous |
3100 | * version of this function. |
3101 | * |
3102 | * When the operation is finished, `callback` will be called in the main thread. |
3103 | * |
3104 | * You can then call gdk_pixbuf_save_to_stream_finish() to get the result of |
3105 | * the operation. |
3106 | * |
3107 | * Since: 2.36 |
3108 | **/ |
3109 | void |
3110 | gdk_pixbuf_save_to_streamv_async (GdkPixbuf *pixbuf, |
3111 | GOutputStream *stream, |
3112 | const gchar *type, |
3113 | gchar **option_keys, |
3114 | gchar **option_values, |
3115 | GCancellable *cancellable, |
3116 | GAsyncReadyCallback callback, |
3117 | gpointer user_data) |
3118 | { |
3119 | GTask *task; |
3120 | SaveToStreamAsyncData *data; |
3121 | |
3122 | g_return_if_fail (GDK_IS_PIXBUF (pixbuf)); |
3123 | g_return_if_fail (gdk_pixbuf_get_width (pixbuf) >= 0); |
3124 | g_return_if_fail (gdk_pixbuf_get_height (pixbuf) >= 0); |
3125 | g_return_if_fail (gdk_pixbuf_get_n_channels (pixbuf) >= 0); |
3126 | g_return_if_fail (G_IS_OUTPUT_STREAM (stream)); |
3127 | g_return_if_fail (type != NULL); |
3128 | g_return_if_fail (callback != NULL); |
3129 | g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); |
3130 | |
3131 | data = g_slice_new (SaveToStreamAsyncData); |
3132 | data->stream = g_object_ref (stream); |
3133 | data->type = g_strdup (str: type); |
3134 | data->keys = g_strdupv (str_array: option_keys); |
3135 | data->values = g_strdupv (str_array: option_values); |
3136 | |
3137 | task = g_task_new (source_object: pixbuf, cancellable, callback, callback_data: user_data); |
3138 | g_task_set_source_tag (task, gdk_pixbuf_save_to_streamv_async); |
3139 | g_task_set_task_data (task, task_data: data, task_data_destroy: (GDestroyNotify) save_to_stream_async_data_free); |
3140 | g_task_run_in_thread (task, task_func: (GTaskThreadFunc) save_to_stream_thread); |
3141 | g_object_unref (object: task); |
3142 | } |
3143 | |
3144 | /** |
3145 | * gdk_pixbuf_save_to_stream_async: |
3146 | * @pixbuf: a `GdkPixbuf` |
3147 | * @stream: a `GOutputStream` to which to save the pixbuf |
3148 | * @type: name of file format |
3149 | * @cancellable: (allow-none): optional `GCancellable` object, `NULL` to ignore |
3150 | * @callback: a `GAsyncReadyCallback` to call when the pixbuf is saved |
3151 | * @user_data: the data to pass to the callback function |
3152 | * @...: list of key-value save options |
3153 | * |
3154 | * Saves `pixbuf` to an output stream asynchronously. |
3155 | * |
3156 | * For more details see gdk_pixbuf_save_to_stream(), which is the synchronous |
3157 | * version of this function. |
3158 | * |
3159 | * When the operation is finished, `callback` will be called in the main thread. |
3160 | * |
3161 | * You can then call gdk_pixbuf_save_to_stream_finish() to get the result of |
3162 | * the operation. |
3163 | * |
3164 | * Since: 2.24 |
3165 | **/ |
3166 | void |
3167 | gdk_pixbuf_save_to_stream_async (GdkPixbuf *pixbuf, |
3168 | GOutputStream *stream, |
3169 | const gchar *type, |
3170 | GCancellable *cancellable, |
3171 | GAsyncReadyCallback callback, |
3172 | gpointer user_data, |
3173 | ...) |
3174 | { |
3175 | gchar **keys = NULL; |
3176 | gchar **values = NULL; |
3177 | va_list args; |
3178 | |
3179 | g_return_if_fail (GDK_IS_PIXBUF (pixbuf)); |
3180 | g_return_if_fail (gdk_pixbuf_get_width (pixbuf) >= 0); |
3181 | g_return_if_fail (gdk_pixbuf_get_height (pixbuf) >= 0); |
3182 | g_return_if_fail (gdk_pixbuf_get_n_channels (pixbuf) >= 0); |
3183 | g_return_if_fail (G_IS_OUTPUT_STREAM (stream)); |
3184 | g_return_if_fail (type != NULL); |
3185 | g_return_if_fail (callback != NULL); |
3186 | g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); |
3187 | |
3188 | va_start (args, user_data); |
3189 | collect_save_options (opts: args, keys: &keys, vals: &values); |
3190 | va_end (args); |
3191 | |
3192 | gdk_pixbuf_save_to_streamv_async (pixbuf, stream, type, |
3193 | option_keys: keys, option_values: values, |
3194 | cancellable, callback, user_data); |
3195 | g_strfreev (str_array: keys); |
3196 | g_strfreev (str_array: values); |
3197 | } |
3198 | |
3199 | /** |
3200 | * gdk_pixbuf_save_to_stream_finish: |
3201 | * @async_result: a `GAsyncResult` |
3202 | * @error: a `GError`, or `NULL` |
3203 | * |
3204 | * Finishes an asynchronous pixbuf save operation started with |
3205 | * gdk_pixbuf_save_to_stream_async(). |
3206 | * |
3207 | * Return value: `TRUE` if the pixbuf was saved successfully, `FALSE` if an error was set. |
3208 | * |
3209 | * Since: 2.24 |
3210 | **/ |
3211 | gboolean |
3212 | gdk_pixbuf_save_to_stream_finish (GAsyncResult *async_result, |
3213 | GError **error) |
3214 | { |
3215 | GTask *task; |
3216 | |
3217 | /* Can not use g_task_is_valid because our GTask has a |
3218 | * source_object which is not available to us anymore. |
3219 | */ |
3220 | g_return_val_if_fail (G_IS_TASK (async_result), FALSE); |
3221 | |
3222 | task = G_TASK (async_result); |
3223 | |
3224 | g_return_val_if_fail (!error || (error && !*error), FALSE); |
3225 | g_warn_if_fail (g_task_get_source_tag (task) == gdk_pixbuf_save_to_stream_async || |
3226 | g_task_get_source_tag (task) == gdk_pixbuf_save_to_streamv_async); |
3227 | |
3228 | return g_task_propagate_boolean (task, error); |
3229 | } |
3230 | |
3231 | /** |
3232 | * gdk_pixbuf_format_get_name: |
3233 | * @format: a `GdkPixbufFormat` |
3234 | * |
3235 | * Returns the name of the format. |
3236 | * |
3237 | * Return value: the name of the format. |
3238 | * |
3239 | * Since: 2.2 |
3240 | */ |
3241 | gchar * |
3242 | gdk_pixbuf_format_get_name (GdkPixbufFormat *format) |
3243 | { |
3244 | g_return_val_if_fail (format != NULL, NULL); |
3245 | |
3246 | return g_strdup (str: format->name); |
3247 | } |
3248 | |
3249 | /** |
3250 | * gdk_pixbuf_format_get_description: |
3251 | * @format: a `GdkPixbufFormat` |
3252 | * |
3253 | * Returns a description of the format. |
3254 | * |
3255 | * Return value: a description of the format. |
3256 | * |
3257 | * Since: 2.2 |
3258 | */ |
3259 | gchar * |
3260 | gdk_pixbuf_format_get_description (GdkPixbufFormat *format) |
3261 | { |
3262 | gchar *domain; |
3263 | const gchar *description; |
3264 | g_return_val_if_fail (format != NULL, NULL); |
3265 | |
3266 | if (format->domain != NULL) |
3267 | domain = format->domain; |
3268 | else |
3269 | domain = GETTEXT_PACKAGE; |
3270 | description = g_dgettext (domain, msgid: format->description); |
3271 | |
3272 | return g_strdup (str: description); |
3273 | } |
3274 | |
3275 | /** |
3276 | * gdk_pixbuf_format_get_mime_types: |
3277 | * @format: a `GdkPixbufFormat` |
3278 | * |
3279 | * Returns the mime types supported by the format. |
3280 | * |
3281 | * Return value: (transfer full) (array zero-terminated=1): an array of mime types |
3282 | * |
3283 | * Since: 2.2 |
3284 | */ |
3285 | gchar ** |
3286 | gdk_pixbuf_format_get_mime_types (GdkPixbufFormat *format) |
3287 | { |
3288 | g_return_val_if_fail (format != NULL, NULL); |
3289 | |
3290 | return g_strdupv (str_array: format->mime_types); |
3291 | } |
3292 | |
3293 | /** |
3294 | * gdk_pixbuf_format_get_extensions: |
3295 | * @format: a `GdkPixbufFormat` |
3296 | * |
3297 | * Returns the filename extensions typically used for files in the |
3298 | * given format. |
3299 | * |
3300 | * Return value: (transfer full) (array zero-terminated=1): an array of |
3301 | * filename extensions |
3302 | * |
3303 | * Since: 2.2 |
3304 | */ |
3305 | gchar ** |
3306 | gdk_pixbuf_format_get_extensions (GdkPixbufFormat *format) |
3307 | { |
3308 | g_return_val_if_fail (format != NULL, NULL); |
3309 | |
3310 | return g_strdupv (str_array: format->extensions); |
3311 | } |
3312 | |
3313 | /** |
3314 | * gdk_pixbuf_format_is_writable: |
3315 | * @format: a `GdkPixbufFormat` |
3316 | * |
3317 | * Returns whether pixbufs can be saved in the given format. |
3318 | * |
3319 | * Return value: whether pixbufs can be saved in the given format. |
3320 | * |
3321 | * Since: 2.2 |
3322 | */ |
3323 | gboolean |
3324 | gdk_pixbuf_format_is_writable (GdkPixbufFormat *format) |
3325 | { |
3326 | g_return_val_if_fail (format != NULL, FALSE); |
3327 | |
3328 | return (format->flags & GDK_PIXBUF_FORMAT_WRITABLE) != 0; |
3329 | } |
3330 | |
3331 | /** |
3332 | * gdk_pixbuf_format_is_scalable: |
3333 | * @format: a `GdkPixbufFormat` |
3334 | * |
3335 | * Returns whether this image format is scalable. |
3336 | * |
3337 | * If a file is in a scalable format, it is preferable to load it at |
3338 | * the desired size, rather than loading it at the default size and |
3339 | * scaling the resulting pixbuf to the desired size. |
3340 | * |
3341 | * Return value: whether this image format is scalable. |
3342 | * |
3343 | * Since: 2.6 |
3344 | */ |
3345 | gboolean |
3346 | gdk_pixbuf_format_is_scalable (GdkPixbufFormat *format) |
3347 | { |
3348 | g_return_val_if_fail (format != NULL, FALSE); |
3349 | |
3350 | return (format->flags & GDK_PIXBUF_FORMAT_SCALABLE) != 0; |
3351 | } |
3352 | |
3353 | /** |
3354 | * gdk_pixbuf_format_is_disabled: |
3355 | * @format: a `GdkPixbufFormat` |
3356 | * |
3357 | * Returns whether this image format is disabled. |
3358 | * |
3359 | * See gdk_pixbuf_format_set_disabled(). |
3360 | * |
3361 | * Return value: whether this image format is disabled. |
3362 | * |
3363 | * Since: 2.6 |
3364 | */ |
3365 | gboolean |
3366 | gdk_pixbuf_format_is_disabled (GdkPixbufFormat *format) |
3367 | { |
3368 | g_return_val_if_fail (format != NULL, FALSE); |
3369 | |
3370 | return format->disabled; |
3371 | } |
3372 | |
3373 | /** |
3374 | * gdk_pixbuf_format_set_disabled: |
3375 | * @format: a `GdkPixbufFormat` |
3376 | * @disabled: `TRUE` to disable the format @format |
3377 | * |
3378 | * Disables or enables an image format. |
3379 | * |
3380 | * If a format is disabled, GdkPixbuf won't use the image loader for |
3381 | * this format to load images. |
3382 | * |
3383 | * Applications can use this to avoid using image loaders with an |
3384 | * inappropriate license, see gdk_pixbuf_format_get_license(). |
3385 | * |
3386 | * Since: 2.6 |
3387 | */ |
3388 | void |
3389 | gdk_pixbuf_format_set_disabled (GdkPixbufFormat *format, |
3390 | gboolean disabled) |
3391 | { |
3392 | g_return_if_fail (format != NULL); |
3393 | |
3394 | format->disabled = disabled != FALSE; |
3395 | } |
3396 | |
3397 | /** |
3398 | * gdk_pixbuf_format_get_license: |
3399 | * @format: a pixbuf format |
3400 | * |
3401 | * Returns information about the license of the image loader for the format. |
3402 | * |
3403 | * The returned string should be a shorthand for a well known license, e.g. |
3404 | * "LGPL", "GPL", "QPL", "GPL/QPL", or "other" to indicate some other license. |
3405 | * |
3406 | * Returns: (transfer full): a string describing the license of the pixbuf format |
3407 | * |
3408 | * Since: 2.6 |
3409 | */ |
3410 | gchar* |
3411 | gdk_pixbuf_format_get_license (GdkPixbufFormat *format) |
3412 | { |
3413 | g_return_val_if_fail (format != NULL, NULL); |
3414 | |
3415 | return g_strdup (str: format->license); |
3416 | } |
3417 | |
3418 | GdkPixbufFormat * |
3419 | _gdk_pixbuf_get_format (GdkPixbufModule *module) |
3420 | { |
3421 | g_return_val_if_fail (module != NULL, NULL); |
3422 | |
3423 | return module->info; |
3424 | } |
3425 | |
3426 | /** |
3427 | * gdk_pixbuf_get_formats: |
3428 | * |
3429 | * Obtains the available information about the image formats supported |
3430 | * by GdkPixbuf. |
3431 | * |
3432 | * Returns: (transfer container) (element-type GdkPixbufFormat): A list of |
3433 | * support image formats. |
3434 | * |
3435 | * Since: 2.2 |
3436 | */ |
3437 | GSList * |
3438 | gdk_pixbuf_get_formats (void) |
3439 | { |
3440 | GSList *result = NULL; |
3441 | GSList *modules; |
3442 | |
3443 | for (modules = get_file_formats (); modules; modules = g_slist_next (modules)) { |
3444 | GdkPixbufModule *module = (GdkPixbufModule *)modules->data; |
3445 | GdkPixbufFormat *info = _gdk_pixbuf_get_format (module); |
3446 | result = g_slist_prepend (list: result, data: info); |
3447 | } |
3448 | |
3449 | return result; |
3450 | } |
3451 | |
3452 | /** |
3453 | * gdk_pixbuf_format_copy: |
3454 | * @format: a pixbuf format |
3455 | * |
3456 | * Creates a copy of `format`. |
3457 | * |
3458 | * Return value: the newly allocated copy of a `GdkPixbufFormat`. Use |
3459 | * gdk_pixbuf_format_free() to free the resources when done |
3460 | * |
3461 | * Since: 2.22 |
3462 | */ |
3463 | GdkPixbufFormat * |
3464 | gdk_pixbuf_format_copy (const GdkPixbufFormat *format) |
3465 | { |
3466 | if (G_LIKELY (format != NULL)) |
3467 | return g_slice_dup (GdkPixbufFormat, format); |
3468 | |
3469 | return NULL; |
3470 | } |
3471 | |
3472 | /** |
3473 | * gdk_pixbuf_format_free: |
3474 | * @format: a pixbuf format |
3475 | * |
3476 | * Frees the resources allocated when copying a `GdkPixbufFormat` |
3477 | * using gdk_pixbuf_format_copy() |
3478 | * |
3479 | * Since: 2.22 |
3480 | */ |
3481 | void |
3482 | gdk_pixbuf_format_free (GdkPixbufFormat *format) |
3483 | { |
3484 | if (G_LIKELY (format != NULL)) |
3485 | g_slice_free (GdkPixbufFormat, format); |
3486 | } |
3487 | |
3488 | /** |
3489 | * gdk_pixbuf_format_is_save_option_supported: |
3490 | * @format: a pixbuf format |
3491 | * @option_key: the name of an option |
3492 | * |
3493 | * Returns `TRUE` if the save option specified by @option_key is supported when |
3494 | * saving a pixbuf using the module implementing @format. |
3495 | * |
3496 | * See gdk_pixbuf_save() for more information about option keys. |
3497 | * |
3498 | * Returns: `TRUE` if the specified option is supported |
3499 | * |
3500 | * Since: 2.36 |
3501 | */ |
3502 | gboolean |
3503 | gdk_pixbuf_format_is_save_option_supported (GdkPixbufFormat *format, |
3504 | const gchar *option_key) |
3505 | { |
3506 | GdkPixbufModule *module; |
3507 | |
3508 | g_return_val_if_fail (format != NULL, FALSE); |
3509 | g_return_val_if_fail (option_key != NULL, FALSE); |
3510 | |
3511 | module = _gdk_pixbuf_get_named_module (name: format->name, NULL); |
3512 | if (!module) |
3513 | return FALSE; |
3514 | |
3515 | if (!_gdk_pixbuf_load_module (image_module: module, NULL)) |
3516 | return FALSE; |
3517 | |
3518 | if (!module->is_save_option_supported) |
3519 | return FALSE; |
3520 | |
3521 | return (* module->is_save_option_supported) (option_key); |
3522 | } |
3523 | |
3524 | G_DEFINE_BOXED_TYPE (GdkPixbufFormat, gdk_pixbuf_format, |
3525 | gdk_pixbuf_format_copy, |
3526 | gdk_pixbuf_format_free) |
3527 | |