1/* -*- mode: C; c-file-style: "linux" -*- */
2/*
3 * GdkPixbuf library - TGA image loader
4 * Copyright (C) 1999 Nicola Girardi <nikke@swlibero.org>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21/*
22 * Some NOTES about the TGA loader (2001/06/07, nikke@swlibero.org)
23 *
24 * - The TGAFooter isn't present in all TGA files. In fact, there's an older
25 * format specification, still in use, which doesn't cover the TGAFooter.
26 * Actually, most TGA files I have are of the older type. Anyway I put the
27 * struct declaration here for completeness.
28 *
29 * - Error handling was designed to be very paranoid.
30 */
31
32#include "config.h"
33#include <stdio.h>
34#include <string.h>
35#include <glib-object.h>
36#include <glib/gi18n-lib.h>
37
38#include "gdk-pixbuf-core.h"
39#include "gdk-pixbuf-io.h"
40#include "gdk-pixbuf-buffer-queue-private.h"
41
42#undef DEBUG_TGA
43
44#define TGA_INTERLEAVE_MASK 0xc0
45#define TGA_INTERLEAVE_NONE 0x00
46#define TGA_INTERLEAVE_2WAY 0x40
47#define TGA_INTERLEAVE_4WAY 0x80
48
49#define TGA_ORIGIN_MASK 0x30
50#define TGA_ORIGIN_RIGHT 0x10
51#define TGA_ORIGIN_UPPER 0x20
52
53enum {
54 TGA_TYPE_NODATA = 0,
55 TGA_TYPE_PSEUDOCOLOR = 1,
56 TGA_TYPE_TRUECOLOR = 2,
57 TGA_TYPE_GRAYSCALE = 3,
58 TGA_TYPE_RLE_PSEUDOCOLOR = 9,
59 TGA_TYPE_RLE_TRUECOLOR = 10,
60 TGA_TYPE_RLE_GRAYSCALE = 11
61};
62
63#define LE16(p) ((p)[0] + ((p)[1] << 8))
64
65typedef struct _TGAHeader TGAHeader;
66typedef struct _TGAFooter TGAFooter;
67
68typedef struct _TGAColor TGAColor;
69typedef struct _TGAColormap TGAColormap;
70
71typedef struct _TGAContext TGAContext;
72
73struct _TGAHeader {
74 guint8 infolen;
75 guint8 has_cmap;
76 guint8 type;
77
78 guint8 cmap_start[2];
79 guint8 cmap_n_colors[2];
80 guint8 cmap_bpp;
81
82 guint8 x_origin[2];
83 guint8 y_origin[2];
84
85 guint8 width[2];
86 guint8 height[2];
87 guint8 bpp;
88
89 guint8 flags;
90};
91
92struct _TGAFooter {
93 guint32 extension_area_offset;
94 guint32 developer_directory_offset;
95
96 /* Standard TGA signature, "TRUEVISION-XFILE.\0". */
97 union {
98 gchar sig_full[18];
99 struct {
100 gchar sig_chunk[16];
101 gchar dot, null;
102 } sig_struct;
103 } sig;
104};
105
106struct _TGAColor {
107 guchar r, g, b, a;
108};
109
110struct _TGAColormap {
111 guint n_colors;
112 TGAColor colors[1];
113};
114
115typedef gboolean (* TGAProcessFunc) (TGAContext *ctx, GError **error);
116
117struct _TGAContext {
118 TGAHeader *hdr;
119
120 TGAColormap *cmap;
121 guint cmap_size;
122
123 GdkPixbuf *pbuf;
124 int pbuf_x;
125 int pbuf_y;
126 int pbuf_y_notified;
127
128 GdkPixbufBufferQueue *input;
129
130 TGAProcessFunc process;
131
132 GdkPixbufModuleSizeFunc size_func;
133 GdkPixbufModulePreparedFunc prepared_func;
134 GdkPixbufModuleUpdatedFunc updated_func;
135 gpointer user_data;
136};
137
138static TGAColormap *
139colormap_new (guint n_colors)
140{
141 TGAColormap *cmap;
142
143 g_assert (n_colors <= G_MAXUINT16);
144
145 cmap = g_try_malloc0 (n_bytes: sizeof (TGAColormap) + (MAX (n_colors, 1) - 1) * sizeof (TGAColor));
146 if (cmap == NULL)
147 return NULL;
148
149 cmap->n_colors = n_colors;
150
151 return cmap;
152}
153
154static const TGAColor *
155colormap_get_color (TGAColormap *cmap,
156 guint id)
157{
158 static const TGAColor transparent_black = { 0, 0, 0, 0 };
159
160 if (id >= cmap->n_colors)
161 return &transparent_black;
162
163 return &cmap->colors[id];
164}
165
166static void
167colormap_set_color (TGAColormap *cmap,
168 guint id,
169 const TGAColor *color)
170{
171 if (id >= cmap->n_colors)
172 return;
173
174 cmap->colors[id] = *color;
175}
176
177static void
178colormap_free (TGAColormap *cmap)
179{
180 g_free (mem: cmap);
181}
182
183static gboolean
184tga_skip_rest_of_image (TGAContext *ctx,
185 GError **err)
186{
187 gdk_pixbuf_buffer_queue_flush (queue: ctx->input, n_bytes: gdk_pixbuf_buffer_queue_get_size (queue: ctx->input));
188
189 return TRUE;
190}
191
192static inline void
193tga_write_pixel (TGAContext *ctx,
194 const TGAColor *color)
195{
196 gint width = gdk_pixbuf_get_width (pixbuf: ctx->pbuf);
197 gint height = gdk_pixbuf_get_height (pixbuf: ctx->pbuf);
198 gint rowstride = gdk_pixbuf_get_rowstride (pixbuf: ctx->pbuf);
199 gint n_channels = gdk_pixbuf_get_n_channels (pixbuf: ctx->pbuf);
200
201 guint x = (ctx->hdr->flags & TGA_ORIGIN_RIGHT) ? width - ctx->pbuf_x - 1 : ctx->pbuf_x;
202 guint y = (ctx->hdr->flags & TGA_ORIGIN_UPPER) ? ctx->pbuf_y : height - ctx->pbuf_y - 1;
203
204 memcpy (dest: gdk_pixbuf_get_pixels (pixbuf: ctx->pbuf) + y * rowstride + x * n_channels, src: color, n: n_channels);
205
206 ctx->pbuf_x++;
207 if (ctx->pbuf_x >= width)
208 {
209 ctx->pbuf_x = 0;
210 ctx->pbuf_y++;
211 }
212}
213
214static gsize
215tga_pixels_remaining (TGAContext *ctx)
216{
217 gint width = gdk_pixbuf_get_width (pixbuf: ctx->pbuf);
218 gint height = gdk_pixbuf_get_height (pixbuf: ctx->pbuf);
219
220 return width * (height - ctx->pbuf_y) - ctx->pbuf_x;
221}
222
223static gboolean
224tga_all_pixels_written (TGAContext *ctx)
225{
226 gint height = gdk_pixbuf_get_height (pixbuf: ctx->pbuf);
227
228 return ctx->pbuf_y >= height;
229}
230
231static void
232tga_emit_update (TGAContext *ctx)
233{
234 gint width = gdk_pixbuf_get_width (pixbuf: ctx->pbuf);
235 gint height = gdk_pixbuf_get_height (pixbuf: ctx->pbuf);
236
237 /* We only notify row-by-row for now.
238 * I was too lazy to handle line-breaks.
239 */
240 if (ctx->pbuf_y_notified == ctx->pbuf_y)
241 return;
242
243 if (ctx->hdr->flags & TGA_ORIGIN_UPPER)
244 (*ctx->updated_func) (ctx->pbuf,
245 0, ctx->pbuf_y_notified,
246 width, ctx->pbuf_y - ctx->pbuf_y_notified,
247 ctx->user_data);
248 else
249 (*ctx->updated_func) (ctx->pbuf,
250 0, height - ctx->pbuf_y,
251 width, ctx->pbuf_y - ctx->pbuf_y_notified,
252 ctx->user_data);
253
254 ctx->pbuf_y_notified = ctx->pbuf_y;
255}
256
257static gboolean
258tga_format_supported (guint type,
259 guint bits_per_pixel)
260{
261 switch (type)
262 {
263 case TGA_TYPE_PSEUDOCOLOR:
264 case TGA_TYPE_RLE_PSEUDOCOLOR:
265 return bits_per_pixel == 8;
266
267 case TGA_TYPE_TRUECOLOR:
268 case TGA_TYPE_RLE_TRUECOLOR:
269 return bits_per_pixel == 16
270 || bits_per_pixel == 24
271 || bits_per_pixel == 32;
272
273 case TGA_TYPE_GRAYSCALE:
274 case TGA_TYPE_RLE_GRAYSCALE:
275 return bits_per_pixel == 8
276 || bits_per_pixel == 16;
277
278 default:
279 return FALSE;
280 }
281}
282
283static inline void
284tga_read_pixel (TGAContext *ctx,
285 const guchar *data,
286 TGAColor *color)
287{
288 switch (ctx->hdr->type)
289 {
290 case TGA_TYPE_PSEUDOCOLOR:
291 case TGA_TYPE_RLE_PSEUDOCOLOR:
292 *color = *colormap_get_color (cmap: ctx->cmap, id: data[0]);
293 break;
294
295 case TGA_TYPE_TRUECOLOR:
296 case TGA_TYPE_RLE_TRUECOLOR:
297 if (ctx->hdr->bpp == 16)
298 {
299 guint16 col = data[0] + (data[1] << 8);
300 color->r = (col >> 7) & 0xf8;
301 color->r |= color->r >> 5;
302 color->g = (col >> 2) & 0xf8;
303 color->g |= color->g >> 5;
304 color->b = col << 3;
305 color->b |= color->b >> 5;
306 color->a = 255;
307 }
308 else
309 {
310 color->b = data[0];
311 color->g = data[1];
312 color->r = data[2];
313 if (ctx->hdr->bpp == 32)
314 color->a = data[3];
315 else
316 color->a = 255;
317 }
318 break;
319
320 case TGA_TYPE_GRAYSCALE:
321 case TGA_TYPE_RLE_GRAYSCALE:
322 color->r = color->g = color->b = data[0];
323 if (ctx->hdr->bpp == 16)
324 color->a = data[1];
325 else
326 color->a = 255;
327 break;
328
329 default:
330 g_assert_not_reached ();
331 }
332}
333
334static gboolean fill_in_context(TGAContext *ctx, GError **err)
335{
336 gboolean alpha;
337 guint w, h;
338
339 g_return_val_if_fail(ctx != NULL, FALSE);
340
341 ctx->cmap_size = ((ctx->hdr->cmap_bpp + 7) >> 3) *
342 LE16(ctx->hdr->cmap_n_colors);
343 ctx->cmap = colormap_new (LE16(ctx->hdr->cmap_n_colors));
344 if (!ctx->cmap) {
345 g_set_error_literal(err, GDK_PIXBUF_ERROR, code: GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
346 _("Cannot allocate colormap"));
347 return FALSE;
348 }
349
350 alpha = ((ctx->hdr->bpp == 16) ||
351 (ctx->hdr->bpp == 32) ||
352 (ctx->hdr->has_cmap && (ctx->hdr->cmap_bpp == 32)));
353
354 w = LE16(ctx->hdr->width);
355 h = LE16(ctx->hdr->height);
356
357 {
358 gint wi = w;
359 gint hi = h;
360
361 (*ctx->size_func) (&wi, &hi, ctx->user_data);
362
363 if (wi == 0 || hi == 0)
364 return FALSE;
365 }
366
367 ctx->pbuf = gdk_pixbuf_new (colorspace: GDK_COLORSPACE_RGB, has_alpha: alpha, bits_per_sample: 8, width: w, height: h);
368
369 if (!ctx->pbuf) {
370 g_set_error_literal(err, GDK_PIXBUF_ERROR, code: GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
371 _("Cannot allocate new pixbuf"));
372 return FALSE;
373 }
374
375 return TRUE;
376}
377
378static gboolean
379tga_load_image (TGAContext *ctx,
380 GError **err)
381{
382 TGAColor color;
383 GBytes *bytes;
384 gsize i, size, bytes_per_pixel;
385 const guchar *data;
386
387 bytes_per_pixel = (ctx->hdr->bpp + 7) / 8;
388 size = gdk_pixbuf_buffer_queue_get_size (queue: ctx->input) / bytes_per_pixel;
389 size = MIN (size, tga_pixels_remaining (ctx));
390
391 bytes = gdk_pixbuf_buffer_queue_pull (queue: ctx->input, length: size * bytes_per_pixel);
392 g_assert (bytes != NULL);
393
394 data = g_bytes_get_data (bytes, NULL);
395
396 for (i = 0; i < size; i++)
397 {
398 tga_read_pixel (ctx, data, color: &color);
399 tga_write_pixel (ctx, color: &color);
400 data += bytes_per_pixel;
401 }
402
403 g_bytes_unref (bytes);
404
405 tga_emit_update (ctx);
406
407 if (tga_all_pixels_written (ctx))
408 ctx->process = tga_skip_rest_of_image;
409 return TRUE;
410}
411
412static gboolean
413tga_load_rle_image (TGAContext *ctx,
414 GError **err)
415{
416 GBytes *bytes;
417 TGAColor color;
418 guint rle_num, raw_num;
419 const guchar *s;
420 guchar tag;
421 gsize n, size, bytes_per_pixel;
422
423 bytes_per_pixel = (ctx->hdr->bpp + 7) / 8;
424 bytes = gdk_pixbuf_buffer_queue_peek (queue: ctx->input, length: gdk_pixbuf_buffer_queue_get_size (queue: ctx->input));
425 s = g_bytes_get_data (bytes, size: &size);
426
427 for (n = 0; n < size; ) {
428 tag = *s;
429 s++, n++;
430 if (tag & 0x80) {
431 if (n + bytes_per_pixel > size) {
432 --n;
433 break;
434 } else {
435 rle_num = (tag & 0x7f) + 1;
436 tga_read_pixel (ctx, data: s, color: &color);
437 s += bytes_per_pixel;
438 n += bytes_per_pixel;
439 rle_num = MIN (rle_num, tga_pixels_remaining (ctx));
440 for (; rle_num; rle_num--)
441 {
442 tga_write_pixel (ctx, color: &color);
443 }
444 if (tga_all_pixels_written (ctx))
445 break;
446 }
447 } else {
448 raw_num = tag + 1;
449 if (n + (raw_num * bytes_per_pixel) > size) {
450 --n;
451 break;
452 } else {
453 raw_num = MIN (raw_num, tga_pixels_remaining (ctx));
454 for (; raw_num; raw_num--) {
455 tga_read_pixel (ctx, data: s, color: &color);
456 s += bytes_per_pixel;
457 n += bytes_per_pixel;
458 tga_write_pixel (ctx, color: &color);
459 }
460
461 if (tga_all_pixels_written (ctx))
462 break;
463 }
464 }
465 }
466
467 g_bytes_unref (bytes);
468 gdk_pixbuf_buffer_queue_flush (queue: ctx->input, n_bytes: n);
469
470 tga_emit_update (ctx);
471
472 if (tga_all_pixels_written (ctx))
473 ctx->process = tga_skip_rest_of_image;
474 return TRUE;
475}
476
477static gboolean
478tga_load_colormap (TGAContext *ctx,
479 GError **err)
480{
481 GBytes *bytes;
482 TGAColor color;
483 const guchar *p;
484 guint i, n_colors;
485
486 if (ctx->hdr->has_cmap)
487 {
488 bytes = gdk_pixbuf_buffer_queue_pull (queue: ctx->input, length: ctx->cmap_size);
489 if (bytes == NULL)
490 return TRUE;
491
492 n_colors = LE16(ctx->hdr->cmap_n_colors);
493
494 p = g_bytes_get_data (bytes, NULL);
495 color.a = 255;
496
497 for (i = 0; i < n_colors; i++)
498 {
499 if ((ctx->hdr->cmap_bpp == 15) || (ctx->hdr->cmap_bpp == 16))
500 {
501 guint16 col = p[0] + (p[1] << 8);
502 color.b = (col >> 7) & 0xf8;
503 color.g = (col >> 2) & 0xf8;
504 color.r = col << 3;
505 p += 2;
506 }
507 else if ((ctx->hdr->cmap_bpp == 24) || (ctx->hdr->cmap_bpp == 32))
508 {
509 color.b = *p++;
510 color.g = *p++;
511 color.r = *p++;
512 if (ctx->hdr->cmap_bpp == 32)
513 color.a = *p++;
514 }
515 else
516 {
517 g_set_error_literal (err, GDK_PIXBUF_ERROR,
518 code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
519 _("Unexpected bitdepth for colormap entries"));
520 g_bytes_unref (bytes);
521 return FALSE;
522 }
523 colormap_set_color (cmap: ctx->cmap, id: i, color: &color);
524 }
525
526 g_bytes_unref (bytes);
527 }
528 else
529 {
530 if ((ctx->hdr->type == TGA_TYPE_PSEUDOCOLOR)
531 || (ctx->hdr->type == TGA_TYPE_RLE_PSEUDOCOLOR))
532 {
533 g_set_error_literal (err, GDK_PIXBUF_ERROR,
534 code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
535 _("Pseudocolor image does not contain a colormap"));
536 return FALSE;
537 }
538 }
539
540 if ((ctx->hdr->type == TGA_TYPE_RLE_PSEUDOCOLOR)
541 || (ctx->hdr->type == TGA_TYPE_RLE_TRUECOLOR)
542 || (ctx->hdr->type == TGA_TYPE_RLE_GRAYSCALE))
543 ctx->process = tga_load_rle_image;
544 else
545 ctx->process = tga_load_image;
546
547 return TRUE;
548}
549
550static gboolean
551tga_read_info (TGAContext *ctx,
552 GError **err)
553{
554 if (gdk_pixbuf_buffer_queue_get_size (queue: ctx->input) < ctx->hdr->infolen)
555 return TRUE;
556
557 gdk_pixbuf_buffer_queue_flush (queue: ctx->input, n_bytes: ctx->hdr->infolen);
558
559 ctx->process = tga_load_colormap;
560 return TRUE;
561}
562
563static gboolean
564tga_load_header (TGAContext *ctx,
565 GError **err)
566{
567 GBytes *bytes;
568
569 bytes = gdk_pixbuf_buffer_queue_pull (queue: ctx->input, length: sizeof (TGAHeader));
570 if (bytes == NULL)
571 return TRUE;
572
573 ctx->hdr = g_try_malloc (n_bytes: sizeof (TGAHeader));
574 if (!ctx->hdr)
575 {
576 g_set_error_literal (err, GDK_PIXBUF_ERROR,
577 code: GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
578 _("Cannot allocate TGA header memory"));
579 return FALSE;
580 }
581 memmove(dest: ctx->hdr, src: g_bytes_get_data (bytes, NULL), n: sizeof(TGAHeader));
582 g_bytes_unref (bytes);
583#ifdef DEBUG_TGA
584 g_print ("infolen %d "
585 "has_cmap %d "
586 "type %d "
587 "cmap_start %d "
588 "cmap_n_colors %d "
589 "cmap_bpp %d "
590 "x %d y %d width %d height %d bpp %d "
591 "flags %#x",
592 ctx->hdr->infolen,
593 ctx->hdr->has_cmap,
594 ctx->hdr->type,
595 LE16(ctx->hdr->cmap_start),
596 LE16(ctx->hdr->cmap_n_colors),
597 ctx->hdr->cmap_bpp,
598 LE16(ctx->hdr->x_origin),
599 LE16(ctx->hdr->y_origin),
600 LE16(ctx->hdr->width),
601 LE16(ctx->hdr->height),
602 ctx->hdr->bpp,
603 ctx->hdr->flags);
604#endif
605 if (LE16(ctx->hdr->width) == 0 ||
606 LE16(ctx->hdr->height) == 0) {
607 g_set_error_literal(err, GDK_PIXBUF_ERROR,
608 code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
609 _("TGA image has invalid dimensions"));
610 return FALSE;
611 }
612 if ((ctx->hdr->flags & TGA_INTERLEAVE_MASK) != TGA_INTERLEAVE_NONE) {
613 g_set_error_literal(err, GDK_PIXBUF_ERROR,
614 code: GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
615 _("TGA image type not supported"));
616 return FALSE;
617 }
618 if (!tga_format_supported (type: ctx->hdr->type, bits_per_pixel: ctx->hdr->bpp))
619 {
620 g_set_error_literal(err, GDK_PIXBUF_ERROR,
621 code: GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
622 _("TGA image type not supported"));
623 return FALSE;
624 }
625
626 if (!fill_in_context(ctx, err))
627 return FALSE;
628
629 (*ctx->prepared_func) (ctx->pbuf, NULL, ctx->user_data);
630
631 ctx->process = tga_read_info;
632 return TRUE;
633}
634
635static gpointer gdk_pixbuf__tga_begin_load(GdkPixbufModuleSizeFunc size_func,
636 GdkPixbufModulePreparedFunc prepared_func,
637 GdkPixbufModuleUpdatedFunc updated_func,
638 gpointer user_data, GError **err)
639{
640 TGAContext *ctx;
641
642 g_assert (size_func != NULL);
643 g_assert (prepared_func != NULL);
644 g_assert (updated_func != NULL);
645
646 ctx = g_try_malloc(n_bytes: sizeof(TGAContext));
647 if (!ctx) {
648 g_set_error_literal(err, GDK_PIXBUF_ERROR,
649 code: GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
650 _("Cannot allocate memory for TGA context struct"));
651 return NULL;
652 }
653
654 ctx->hdr = NULL;
655
656 ctx->cmap = NULL;
657 ctx->cmap_size = 0;
658
659 ctx->pbuf = NULL;
660 ctx->pbuf_x = 0;
661 ctx->pbuf_y = 0;
662 ctx->pbuf_y_notified = 0;
663
664 ctx->input = gdk_pixbuf_buffer_queue_new ();
665
666 ctx->process = tga_load_header;
667
668 ctx->size_func = size_func;
669 ctx->prepared_func = prepared_func;
670 ctx->updated_func = updated_func;
671
672 ctx->user_data = user_data;
673
674 return ctx;
675}
676
677static gboolean gdk_pixbuf__tga_load_increment(gpointer data,
678 const guchar *buffer,
679 guint size,
680 GError **err)
681{
682 TGAContext *ctx = (TGAContext*) data;
683 TGAProcessFunc process;
684
685 g_return_val_if_fail(buffer != NULL, TRUE);
686 gdk_pixbuf_buffer_queue_push (queue: ctx->input, buffer: g_bytes_new (data: buffer, size));
687
688 do
689 {
690 process = ctx->process;
691
692 if (!process (ctx, err))
693 return FALSE;
694 }
695 while (process != ctx->process);
696
697 return TRUE;
698}
699
700static gboolean gdk_pixbuf__tga_stop_load(gpointer data, GError **err)
701{
702 TGAContext *ctx = (TGAContext *) data;
703 gboolean result = TRUE;
704
705 g_return_val_if_fail (ctx != NULL, FALSE);
706
707 if (ctx->pbuf == NULL || tga_pixels_remaining (ctx))
708 {
709 g_set_error_literal (err,
710 GDK_PIXBUF_ERROR,
711 code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
712 _("TGA image was truncated or incomplete."));
713
714 result = FALSE;
715 }
716
717 g_free (mem: ctx->hdr);
718 if (ctx->cmap)
719 colormap_free (cmap: ctx->cmap);
720 if (ctx->pbuf)
721 g_object_unref (object: ctx->pbuf);
722 gdk_pixbuf_buffer_queue_unref (queue: ctx->input);
723 g_free (mem: ctx);
724
725 return result;
726}
727
728#ifndef INCLUDE_tga
729#define MODULE_ENTRY(function) G_MODULE_EXPORT void function
730#else
731#define MODULE_ENTRY(function) void _gdk_pixbuf__tga_ ## function
732#endif
733
734MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module)
735{
736 module->begin_load = gdk_pixbuf__tga_begin_load;
737 module->stop_load = gdk_pixbuf__tga_stop_load;
738 module->load_increment = gdk_pixbuf__tga_load_increment;
739}
740
741MODULE_ENTRY (fill_info) (GdkPixbufFormat *info)
742{
743 static const GdkPixbufModulePattern signature[] = {
744 { " \x1\x1", "x ", 100 },
745 { " \x1\x9", "x ", 100 },
746 { " \x2", "xz ", 99 }, /* only 99 since .CUR also matches this */
747 { " \x3", "xz ", 100 },
748 { " \xa", "xz ", 100 },
749 { " \xb", "xz ", 100 },
750 { NULL, NULL, 0 }
751 };
752 static const gchar *mime_types[] = {
753 "image/x-tga",
754 NULL
755 };
756 static const gchar *extensions[] = {
757 "tga",
758 "targa",
759 NULL
760 };
761
762 info->name = "tga";
763 info->signature = (GdkPixbufModulePattern *) signature;
764 info->description = NC_("image format", "Targa");
765 info->mime_types = (gchar **) mime_types;
766 info->extensions = (gchar **) extensions;
767 info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
768 info->license = "LGPL";
769}
770

source code of gtk/subprojects/gdk-pixbuf/gdk-pixbuf/io-tga.c