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 | |
53 | enum { |
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 | |
65 | typedef struct _TGAHeader ; |
66 | typedef struct _TGAFooter ; |
67 | |
68 | typedef struct _TGAColor TGAColor; |
69 | typedef struct _TGAColormap TGAColormap; |
70 | |
71 | typedef struct _TGAContext TGAContext; |
72 | |
73 | struct { |
74 | guint8 ; |
75 | guint8 ; |
76 | guint8 ; |
77 | |
78 | guint8 [2]; |
79 | guint8 [2]; |
80 | guint8 ; |
81 | |
82 | guint8 [2]; |
83 | guint8 [2]; |
84 | |
85 | guint8 [2]; |
86 | guint8 [2]; |
87 | guint8 ; |
88 | |
89 | guint8 ; |
90 | }; |
91 | |
92 | struct { |
93 | guint32 ; |
94 | guint32 ; |
95 | |
96 | /* Standard TGA signature, "TRUEVISION-XFILE.\0". */ |
97 | union { |
98 | gchar [18]; |
99 | struct { |
100 | gchar [16]; |
101 | gchar , ; |
102 | } ; |
103 | } ; |
104 | }; |
105 | |
106 | struct _TGAColor { |
107 | guchar r, g, b, a; |
108 | }; |
109 | |
110 | struct _TGAColormap { |
111 | guint n_colors; |
112 | TGAColor colors[1]; |
113 | }; |
114 | |
115 | typedef gboolean (* TGAProcessFunc) (TGAContext *ctx, GError **error); |
116 | |
117 | struct _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 | |
138 | static TGAColormap * |
139 | colormap_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 | |
154 | static const TGAColor * |
155 | colormap_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 | |
166 | static void |
167 | colormap_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 | |
177 | static void |
178 | colormap_free (TGAColormap *cmap) |
179 | { |
180 | g_free (mem: cmap); |
181 | } |
182 | |
183 | static gboolean |
184 | tga_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 | |
192 | static inline void |
193 | tga_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 | |
214 | static gsize |
215 | tga_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 | |
223 | static gboolean |
224 | tga_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 | |
231 | static void |
232 | tga_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 | |
257 | static gboolean |
258 | tga_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 | |
283 | static inline void |
284 | tga_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 | |
334 | static 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 | |
378 | static gboolean |
379 | tga_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 | |
412 | static gboolean |
413 | tga_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 | |
477 | static gboolean |
478 | tga_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 | |
550 | static gboolean |
551 | tga_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 | |
563 | static gboolean |
564 | (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 | |
635 | static 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 | |
677 | static 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 | |
700 | static 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 | |
734 | MODULE_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 | |
741 | MODULE_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 | |