]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-tga.c
Robustness fixes and test images for the jpeg, tiff, pnm, gif, xpm and tga
[~andy/gtk] / gdk-pixbuf / io-tga.c
1 /* 
2  * GdkPixbuf library - TGA image loader
3  * Copyright (C) 1999 Nicola Girardi <nikke@swlibero.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  */
21
22 /*
23  * Some NOTES about the TGA loader (2001/06/07, nikke@swlibero.org)
24  *
25  * - The module doesn't currently provide support for TGA images where the
26  *   order of the pixels isn't left-to-right and top-to-bottom.  I plan to
27  *   add support for those files as soon as I get one of them.  I haven't
28  *   run into one yet.  (And I don't seem to be able to create it with GIMP.)
29  *
30  * - The TGAFooter isn't present in all TGA files.  In fact, there's an older
31  *   format specification, still in use, which doesn't cover the TGAFooter.
32  *   Actually, most TGA files I have are of the older type.  Anyway I put the 
33  *   struct declaration here for completeness.
34  *
35  * - Error handling was designed to be very paranoid.
36  */
37
38 #include <stdio.h>
39 #include <string.h>
40
41 #include "gdk-pixbuf.h"
42 #include "gdk-pixbuf-io.h"
43 #include "gdk-pixbuf-private.h"
44
45 #define TGA_INTERLEAVE_MASK     0xc0
46 #define TGA_INTERLEAVE_NONE     0x00
47 #define TGA_INTERLEAVE_2WAY     0x40
48 #define TGA_INTERLEAVE_4WAY     0x80
49
50 #define TGA_ORIGIN_MASK         0x30
51 #define TGA_ORIGIN_LEFT         0x00
52 #define TGA_ORIGIN_RIGHT        0x10
53 #define TGA_ORIGIN_LOWER        0x00
54 #define TGA_ORIGIN_UPPER        0x20
55
56 enum {
57         TGA_TYPE_NODATA = 0,
58         TGA_TYPE_PSEUDOCOLOR = 1,
59         TGA_TYPE_TRUECOLOR = 2,
60         TGA_TYPE_GRAYSCALE = 3,
61         TGA_TYPE_RLE_PSEUDOCOLOR = 9,
62         TGA_TYPE_RLE_TRUECOLOR = 10,
63         TGA_TYPE_RLE_GRAYSCALE = 11
64 };
65
66 #define LE16(p) ((p)[0] + ((p)[1] << 8))
67
68 typedef struct _IOBuffer IOBuffer;
69
70 typedef struct _TGAHeader TGAHeader;
71 typedef struct _TGAFooter TGAFooter;
72
73 typedef struct _TGAColormap TGAColormap;
74 typedef struct _TGAColor TGAColor;
75
76 typedef struct _TGAContext TGAContext;
77
78 struct _TGAHeader {
79         guint8 infolen;
80         guint8 has_cmap;
81         guint8 type;
82         
83         guint8 cmap_start[2];
84         guint8 cmap_n_colors[2];
85         guint8 cmap_bpp;
86         
87         guint8 x_origin[2];
88         guint8 y_origin[2];
89         
90         guint8 width[2];
91         guint8 height[2];
92         guint8 bpp;
93         
94         guint8 flags;
95 };
96
97 struct _TGAFooter {
98         guint32 extension_area_offset;
99         guint32 developer_directory_offset;
100
101         /* Standard TGA signature, "TRUEVISION-XFILE.\0". */
102         union {
103                 gchar sig_full[18];
104                 struct {
105                         gchar sig_chunk[16];
106                         gchar dot, null;
107                 } sig_struct;
108         } sig;
109 };
110
111 struct _TGAColormap {
112         gint size;
113         TGAColor *cols;
114 };
115
116 struct _TGAColor {
117         guchar r, g, b, a;
118 };
119
120 struct _TGAContext {
121         TGAHeader *hdr;
122         guint rowstride;
123         guint completed_lines;
124         gboolean run_length_encoded;
125
126         TGAColormap *cmap;
127         guint cmap_size;
128
129         GdkPixbuf *pbuf;
130         guint pbuf_bytes;
131         guint pbuf_bytes_done;
132         guchar *pptr;
133
134         IOBuffer *in;
135
136         gboolean skipped_info;
137         gboolean prepared;
138         gboolean done;
139
140         ModulePreparedNotifyFunc pfunc;
141         ModuleUpdatedNotifyFunc ufunc;
142         gpointer udata;
143 };
144
145 struct _IOBuffer {
146         guchar *data;
147         guint size;
148 };
149
150 static IOBuffer *io_buffer_new(GError **err)
151 {
152         IOBuffer *buffer;
153         buffer = g_try_malloc(sizeof(IOBuffer));
154         if (!buffer) {
155                 g_set_error(err, GDK_PIXBUF_ERROR,
156                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
157                             _("Can't allocate memory for IOBuffer struct"));
158                 return NULL;
159         }
160         buffer->data = NULL;
161         buffer->size = 0;
162         return buffer;
163 }
164
165 static IOBuffer *io_buffer_append(IOBuffer *buffer, 
166                                   const guchar *data, guint len, 
167                                   GError **err)
168 {
169         if (!buffer)
170                 return NULL;
171         if (!buffer->data) {
172                 buffer->data = g_try_malloc(len);
173                 if (!buffer->data) {
174                         g_set_error(err, GDK_PIXBUF_ERROR,
175                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
176                                     _("Can't allocate memory for IOBuffer data"));
177                         g_free(buffer);
178                         return NULL;
179                 }
180                 g_memmove(buffer->data, data, len);
181                 buffer->size = len;
182         } else {
183                 buffer->data = g_try_realloc(buffer->data, buffer->size + len);
184                 if (!buffer->data) {
185                         g_set_error(err, GDK_PIXBUF_ERROR,
186                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
187                                     _("Can't realloc IOBuffer data"));
188                         g_free(buffer);
189                         return NULL;
190                 }
191                 g_memmove(&buffer->data[buffer->size], data, len);
192                 buffer->size += len;
193         }
194         return buffer;
195 }
196
197 static IOBuffer *io_buffer_free_segment(IOBuffer *buffer, 
198                                         guint count,
199                                         GError **err)
200 {
201         g_return_val_if_fail(buffer != NULL, NULL);
202         g_return_val_if_fail(buffer->data != NULL, NULL);
203         if (count == buffer->size) {
204                 g_free(buffer->data);
205                 buffer->data = NULL;
206                 buffer->size = 0;
207         } else {
208                 guchar *new_buf;
209                 guint new_size;
210
211                 new_size = buffer->size - count;
212                 new_buf = g_try_malloc(new_size);
213                 if (!new_buf) {
214                         g_set_error(err, GDK_PIXBUF_ERROR,
215                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
216                                     _("Can't allocate temporary IOBuffer data"));
217                         g_free(buffer->data);
218                         g_free(buffer);
219                         return NULL;
220                 }
221
222                 g_memmove(new_buf, &buffer->data[count], new_size);
223                 g_free(buffer->data);
224                 buffer->data = new_buf;
225                 buffer->size = new_size;
226         }
227         return buffer;
228 }
229
230 static void io_buffer_free(IOBuffer *buffer)
231 {
232         g_return_if_fail(buffer != NULL);
233         if (buffer->data)
234                 g_free(buffer->data);
235         g_free(buffer);
236 }
237
238 static void free_buffer(guchar *pixels, gpointer data)
239 {
240         g_free(pixels);
241 }
242
243 static gboolean fread_check(gpointer dest, 
244                             size_t size, size_t count, 
245                             FILE *f, GError **err)
246 {
247         if (fread(dest, size, count, f) != count) {
248                 g_set_error(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED,
249                             _("fread() failed -- premature end-of-file probably encountered"));
250                 return FALSE;
251         }
252         return TRUE;
253 }
254
255 static gboolean fseek_check(FILE *f, glong offset, gint whence, GError **err)
256 {
257         if (fseek(f, offset, whence) != 0) {
258                 g_set_error(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED,
259                             _("fseek() failed -- premature end-of-file probably encountered"));
260                 return FALSE;
261         }
262         return TRUE;
263 }
264
265 static gboolean fill_in_context(TGAContext *ctx, GError **err)
266 {
267         gboolean alpha;
268
269         g_return_val_if_fail(ctx != NULL, FALSE);
270
271         ctx->run_length_encoded =
272                 ((ctx->hdr->type == TGA_TYPE_RLE_PSEUDOCOLOR)
273                  || (ctx->hdr->type == TGA_TYPE_RLE_TRUECOLOR)
274                  || (ctx->hdr->type == TGA_TYPE_RLE_GRAYSCALE));
275
276         if (ctx->hdr->has_cmap)
277                 ctx->cmap_size = ((ctx->hdr->cmap_bpp + 7) >> 3) *
278                         LE16(ctx->hdr->cmap_n_colors);
279
280         alpha = ((ctx->hdr->bpp == 32) ||
281                  (ctx->hdr->has_cmap && (ctx->hdr->cmap_bpp == 32)));
282
283         ctx->pbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, alpha, 8,
284                                    LE16(ctx->hdr->width),
285                                    LE16(ctx->hdr->height));
286         if (!ctx->pbuf) {
287                 g_set_error(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
288                             _("Can't allocate new pixbuf"));
289                 return FALSE;
290         }
291         ctx->pbuf_bytes = ctx->pbuf->rowstride * ctx->pbuf->height;
292         ctx->pptr = ctx->pbuf->pixels;
293
294         if ((ctx->hdr->type == TGA_TYPE_PSEUDOCOLOR) ||
295             (ctx->hdr->type == TGA_TYPE_GRAYSCALE))
296                 ctx->rowstride = ctx->pbuf->width;
297         else if (ctx->hdr->type == TGA_TYPE_TRUECOLOR)
298                 ctx->rowstride = ctx->pbuf->rowstride;
299
300         ctx->completed_lines = 0;
301         return TRUE;
302 }
303
304 static void parse_data_for_row_pseudocolor(TGAContext *ctx)
305 {
306         guchar *s = ctx->in->data;
307         guint upper_bound = ctx->pbuf->width;
308
309         for (; upper_bound; upper_bound--, s++) {
310                 *ctx->pptr++ = ctx->cmap->cols[*s].r;
311                 *ctx->pptr++ = ctx->cmap->cols[*s].g;
312                 *ctx->pptr++ = ctx->cmap->cols[*s].b;
313                 if (ctx->hdr->cmap_bpp == 32)
314                         *ctx->pptr++ = ctx->cmap->cols[*s].a;
315         }
316         ctx->pbuf_bytes_done += ctx->pbuf->n_channels * ctx->pbuf->width;
317         if (ctx->pbuf_bytes_done == ctx->pbuf_bytes)
318                 ctx->done = TRUE;
319 }
320
321 static void swap_channels(TGAContext *ctx)
322 {
323         register guchar swap;
324         register guint count;
325         for (count = ctx->pbuf->width; count; count--) {
326                 swap = ctx->pptr[0];
327                 ctx->pptr[0] = ctx->pptr[2];
328                 ctx->pptr[2] = swap;
329                 ctx->pptr += ctx->pbuf->n_channels;
330         }
331 }
332
333 static void parse_data_for_row_truecolor(TGAContext *ctx)
334 {
335         g_memmove(ctx->pptr, ctx->in->data, ctx->pbuf->rowstride);
336         swap_channels(ctx);
337         ctx->pbuf_bytes_done += ctx->pbuf->rowstride;
338         if (ctx->pbuf_bytes_done == ctx->pbuf_bytes)
339                 ctx->done = TRUE;
340 }
341
342 static void parse_data_for_row_grayscale(TGAContext *ctx)
343 {
344         guchar *s = ctx->in->data;
345         guint upper_bound = ctx->pbuf->width;
346
347         for (; upper_bound; upper_bound--) {
348                 ctx->pptr[0] = ctx->pptr[1] = ctx->pptr[2] = *s++;
349                 ctx->pptr += 3;
350         }
351         ctx->pbuf_bytes_done = ctx->pbuf->width * 3;
352         if (ctx->pbuf_bytes_done == ctx->pbuf_bytes)
353                 ctx->done = TRUE;
354 }
355
356 static gboolean parse_data_for_row(TGAContext *ctx, GError **err)
357 {
358         if (ctx->hdr->type == TGA_TYPE_PSEUDOCOLOR)
359                 parse_data_for_row_pseudocolor(ctx);
360         else if (ctx->hdr->type == TGA_TYPE_TRUECOLOR)
361                 parse_data_for_row_truecolor(ctx);
362         else if (ctx->hdr->type == TGA_TYPE_GRAYSCALE)
363                 parse_data_for_row_grayscale(ctx);
364         ctx->in = io_buffer_free_segment(ctx->in, ctx->rowstride, err);
365         if (!ctx->in)
366                 return FALSE;
367         (*ctx->ufunc) (ctx->pbuf, 0,
368                        (ctx->pbuf_bytes_done / ctx->pbuf->rowstride) - 1,
369                        ctx->pbuf->width, 1, ctx->udata);
370         return TRUE;
371 }
372
373 static void write_rle_data(TGAContext *ctx, TGAColor *color, guint *rle_count)
374 {
375         ctx->pbuf_bytes_done += ctx->pbuf->n_channels * (*rle_count);
376         for (; *rle_count; (*rle_count)--) {
377                 g_memmove(ctx->pptr, (guchar *) color, ctx->pbuf->n_channels);
378                 ctx->pptr += ctx->pbuf->n_channels;
379         }
380 }
381
382 static guint parse_rle_data_pseudocolor(TGAContext *ctx)
383 {
384         guint rle_num, raw_num;
385         guchar *s, tag;
386         guint n;
387
388         g_return_val_if_fail(ctx->in->size > 0, 0);
389         s = ctx->in->data;
390
391         for (n = 0; n < ctx->in->size; ) {
392                 tag = *s;
393                 s++, n++;
394                 if (tag & 0x80) {
395                         if (n == ctx->in->size) {
396                                 return --n;
397                         } else {
398                                 rle_num = (tag & 0x7f) + 1;
399                                 write_rle_data(ctx, &ctx->cmap->cols[*s], &rle_num);
400                                 s++, n++;
401                         }
402                 } else {
403                         raw_num = tag + 1;
404                         if (n + raw_num >= ctx->in->size) {
405                                 return --n;
406                         } else {
407                                 for (; raw_num; raw_num--) {
408                                         *ctx->pptr++ =
409                                                 ctx->cmap->cols[*s].r;
410                                         *ctx->pptr++ =
411                                                 ctx->cmap->cols[*s].g;
412                                         *ctx->pptr++ =
413                                                 ctx->cmap->cols[*s].b;
414                                         if (ctx->pbuf->n_channels == 4)
415                                                 *ctx->pptr++ = ctx->cmap->cols[*s].a;
416                                         s++, n++;
417                                         ctx->pbuf_bytes_done += ctx->pbuf->n_channels;
418                                 }
419                         }
420                 }
421         }
422         if (ctx->pbuf_bytes_done == ctx->pbuf_bytes)
423                 ctx->done = TRUE;
424         return n;
425 }
426
427 static void swap_channels_rle(TGAContext *ctx, guint count)
428 {
429         register guchar swap;
430         for (; count; count--) {
431                 swap = ctx->pptr[0];
432                 ctx->pptr[0] = ctx->pptr[2];
433                 ctx->pptr[2] = swap;
434                 ctx->pptr += ctx->pbuf->n_channels;
435         }
436 }
437
438 static guint parse_rle_data_truecolor(TGAContext *ctx)
439 {
440         TGAColor col;
441         guint rle_num, raw_num;
442         guchar *s, tag;
443         guint n = 0;
444
445         g_return_val_if_fail(ctx->in->size > 0, 0);
446         s = ctx->in->data;
447
448         for (n = 0; n < ctx->in->size; ) {
449                 tag = *s;
450                 s++, n++;
451                 if (tag & 0x80) {
452                         if (n + ctx->pbuf->n_channels >= ctx->in->size) {
453                                 return --n;
454                         } else {
455                                 rle_num = (tag & 0x7f) + 1;
456                                 col.b = *s++;
457                                 col.g = *s++;
458                                 col.r = *s++;
459                                 if (ctx->hdr->bpp == 32)
460                                         col.a = *s++;
461                                 write_rle_data(ctx, &col, &rle_num);
462                                 n += ctx->pbuf->n_channels;
463                         }
464                 } else {
465                         raw_num = tag + 1;
466                         if (n + (raw_num * ctx->pbuf->n_channels) >= ctx->in->size) {
467                                 return --n;
468                         } else {
469                                 g_memmove(ctx->pptr, s, raw_num * ctx->pbuf->n_channels);
470                                 swap_channels_rle(ctx, raw_num);
471                                 s += raw_num * ctx->pbuf->n_channels;
472                                 n += raw_num * ctx->pbuf->n_channels;
473                                 ctx->pbuf_bytes_done += raw_num * ctx->pbuf->n_channels;
474                         }
475                 }
476         }
477         if (ctx->pbuf_bytes_done == ctx->pbuf_bytes)
478                 ctx->done = TRUE;
479         return n;
480 }
481
482 static guint parse_rle_data_grayscale(TGAContext *ctx)
483 {
484         TGAColor tone;
485         guint rle_num, raw_num;
486         guchar *s, tag;
487         guint n;
488
489         g_return_val_if_fail(ctx->in->size > 0, 0);
490         s = ctx->in->data;
491
492         for (n = 0; n < ctx->in->size; ) {
493                 tag = *s;
494                 s++, n++;
495                 if (tag & 0x80) {
496                         if (n == ctx->in->size) {
497                                 return --n;
498                         } else {
499                                 rle_num = (tag & 0x7f) + 1;
500                                 tone.r = tone.g = tone.b = *s;
501                                 s++, n++;
502                                 write_rle_data(ctx, &tone, &rle_num);
503                         }
504                 } else {
505                         raw_num = tag + 1;
506                         if (n + raw_num >= ctx->in->size) {
507                                 return --n;
508                         } else {
509                                 for (; raw_num; raw_num--) {
510                                         ctx->pptr[0] = ctx->pptr[1] = ctx->pptr[2] = *s;
511                                         s++, n++;
512                                         ctx->pptr += 3;
513                                         ctx->pbuf_bytes_done += 3;
514                                 }
515                         }
516                 }
517         }
518         if (ctx->pbuf_bytes_done == ctx->pbuf_bytes)
519                 ctx->done = TRUE;
520         return n;
521 }
522
523 static gboolean parse_rle_data(TGAContext *ctx, GError **err)
524 {
525         guint count = 0;
526         guint pbuf_count = 0;
527         if (ctx->hdr->type == TGA_TYPE_RLE_PSEUDOCOLOR) {
528                 count = parse_rle_data_pseudocolor(ctx);
529                 pbuf_count = count *ctx->pbuf->n_channels;
530         } else if (ctx->hdr->type == TGA_TYPE_RLE_TRUECOLOR) {
531                 count = parse_rle_data_truecolor(ctx);
532                 pbuf_count = count;
533         } else if (ctx->hdr->type == TGA_TYPE_RLE_GRAYSCALE) {
534                 count = parse_rle_data_grayscale(ctx);
535                 pbuf_count = count * 3;
536         }
537         ctx->in = io_buffer_free_segment(ctx->in, count, err);
538         if (!ctx->in)
539                 return FALSE;
540         (*ctx->ufunc) (ctx->pbuf, 0, ctx->pbuf_bytes_done / ctx->pbuf->rowstride,
541                        ctx->pbuf->width, pbuf_count / ctx->pbuf->rowstride,
542                        ctx->udata);
543         return TRUE;
544 }
545
546 static gboolean try_colormap(TGAContext *ctx, GError **err)
547 {
548         static guchar *p;
549         static guint n;
550
551         g_return_val_if_fail(ctx != NULL, FALSE);
552         g_return_val_if_fail(ctx->cmap_size > 0, TRUE);
553
554         ctx->cmap = g_try_malloc(sizeof(TGAColormap));
555         if (!ctx->cmap) {
556                 g_set_error(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
557                             _("Can't allocate colormap structure"));
558                 return FALSE;
559         }
560         ctx->cmap->size = LE16(ctx->hdr->cmap_n_colors);
561         ctx->cmap->cols = g_try_malloc(sizeof(TGAColor) * ctx->cmap->size);
562         if (!ctx->cmap->cols) {
563                 g_set_error(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
564                             _("Can't allocate colormap entries"));
565                 g_free(ctx->cmap);
566                 return FALSE;
567         }
568
569         p = ctx->in->data;
570         for (n = 0; n < ctx->cmap->size; n++) {
571                 if ((ctx->hdr->cmap_bpp == 15) || (ctx->hdr->cmap_bpp == 16)) {
572                         guint16 col = p[0] + (p[1] << 8);
573                         ctx->cmap->cols[n].b = (col >> 7) & 0xf8;
574                         ctx->cmap->cols[n].g = (col >> 2) & 0xf8;
575                         ctx->cmap->cols[n].r = col << 3;
576                         p += 2;
577                 }
578                 else if ((ctx->hdr->cmap_bpp == 24) || (ctx->hdr->cmap_bpp == 32)) {
579                         ctx->cmap->cols[n].b = *p++;
580                         ctx->cmap->cols[n].g = *p++;
581                         ctx->cmap->cols[n].r = *p++;
582                         if (ctx->hdr->cmap_bpp == 32)
583                                 ctx->cmap->cols[n].a = *p++;
584                 } else {
585                         g_set_error(err, GDK_PIXBUF_ERROR, 
586                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
587                                     _("Unexpected bitdepth for colormap entries"));
588                         g_free(ctx->cmap->cols);
589                         g_free(ctx->cmap);
590                         return FALSE;
591                 }
592         }
593         ctx->in = io_buffer_free_segment(ctx->in, ctx->cmap_size, err);
594         if (!ctx->in)
595                 return FALSE;
596         return TRUE;
597 }
598
599 static gboolean try_preload(TGAContext *ctx, GError **err)
600 {
601         if (!ctx->hdr) {
602                 if (ctx->in->size >= sizeof(TGAHeader)) {
603                         ctx->hdr = g_try_malloc(sizeof(TGAHeader));
604                         if (!ctx->hdr) {
605                                 g_set_error(err, GDK_PIXBUF_ERROR,
606                                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
607                                             _("Can't allocate TGA header memory"));
608                                 return FALSE;
609                         }
610                         g_memmove(ctx->hdr, ctx->in->data, sizeof(TGAHeader));
611                         ctx->in = io_buffer_free_segment(ctx->in, sizeof(TGAHeader), err);
612                         if (!ctx->in)
613                                 return FALSE;
614                         if (!fill_in_context(ctx, err))
615                                 return FALSE;
616                         if (ctx->hdr->infolen > 255) {
617                                 g_set_error(err, GDK_PIXBUF_ERROR,
618                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
619                                             _("TGA image comment length is too long"));
620                                 return FALSE;
621                         }
622                         if ((ctx->hdr->flags & TGA_INTERLEAVE_MASK) != 
623                             TGA_INTERLEAVE_NONE ||
624                             ctx->hdr->flags & TGA_ORIGIN_RIGHT || 
625                             ctx->hdr->flags & TGA_ORIGIN_LOWER) {
626                                 g_set_error(err, GDK_PIXBUF_ERROR, 
627                                             GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
628                                             _("TGA image type not supported"));
629                                 return FALSE;
630                         }
631                 } else {
632                         return TRUE;
633                 }
634         }
635         if (!ctx->skipped_info) {
636                 if (ctx->in->size >= ctx->hdr->infolen) {
637                         ctx->in = io_buffer_free_segment(ctx->in, ctx->hdr->infolen, err);
638                         if (!ctx->in)
639                                 return FALSE;
640                         ctx->skipped_info = TRUE;
641                 } else {
642                         return TRUE;
643                 }
644         }
645         if (ctx->hdr->has_cmap && !ctx->cmap) {
646                 if (ctx->in->size >= ctx->cmap_size) {
647                         if (!try_colormap(ctx, err))
648                                 return FALSE;
649                 } else {
650                         return TRUE;
651                 }
652         }
653         if (!ctx->prepared) {
654                 (*ctx->pfunc) (ctx->pbuf, NULL, ctx->udata);
655                 ctx->prepared = TRUE;
656         }
657         /* We shouldn't get here anyway. */
658         return TRUE;
659 }
660
661 static gpointer gdk_pixbuf__tga_begin_load(ModulePreparedNotifyFunc f1,
662                                            ModuleUpdatedNotifyFunc f2,
663                                            gpointer udata, GError **err)
664 {
665         TGAContext *ctx;
666
667         ctx = g_try_malloc(sizeof(TGAContext));
668         if (!ctx) {
669                 g_set_error(err, GDK_PIXBUF_ERROR, 
670                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
671                             _("Can't allocate memory for TGA context struct"));
672                 return NULL;
673         }
674
675         ctx->hdr = NULL;
676         ctx->rowstride = 0;
677         ctx->run_length_encoded = FALSE;
678
679         ctx->cmap = NULL;
680         ctx->cmap_size = 0;
681
682         ctx->pbuf = NULL;
683         ctx->pbuf_bytes = 0;
684         ctx->pbuf_bytes_done = 0;
685         ctx->pptr = NULL;
686
687         ctx->in = io_buffer_new(err);
688         if (!ctx->in) {
689                 g_free(ctx);
690                 return NULL;
691         }
692
693         ctx->skipped_info = FALSE;
694         ctx->prepared = FALSE;
695         ctx->done = FALSE;
696
697         ctx->pfunc = f1;
698         ctx->ufunc = f2;
699         ctx->udata = udata;
700
701         return ctx;
702 }
703
704 static gboolean gdk_pixbuf__tga_load_increment(gpointer data,
705                                                const guchar *buffer,
706                                                guint size,
707                                                GError **err)
708 {
709         TGAContext *ctx = (TGAContext*) data;
710         g_return_val_if_fail(ctx != NULL, FALSE);
711
712         if (ctx->done)
713                 return TRUE;
714
715         g_return_val_if_fail(buffer != NULL, TRUE);
716         ctx->in = io_buffer_append(ctx->in, buffer, size, err);
717         if (!ctx->in)
718                 return FALSE;
719         if (!ctx->prepared) {
720                 if (!try_preload(ctx, err))
721                         return FALSE;
722                 if (!ctx->prepared)
723                         return TRUE;
724                 if (ctx->in->size == 0)
725                         return TRUE;
726         }
727
728         if (ctx->run_length_encoded) {
729                 if (!parse_rle_data(ctx, err))
730                         return FALSE;
731         } else {
732                 while (ctx->in->size >= ctx->rowstride) {
733                         if (ctx->completed_lines >= ctx->pbuf->height) {
734                                 g_set_error(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED,
735                                             _("Excess data in file"));
736                                 return FALSE;
737                         }
738                         if (!parse_data_for_row(ctx, err))
739                                 return FALSE;
740                         ctx->completed_lines++;
741                 }
742         }
743
744         return TRUE;
745 }
746
747 static gboolean gdk_pixbuf__tga_stop_load(gpointer data, GError **err)
748 {
749         TGAContext *ctx = (TGAContext *) data;
750         g_return_val_if_fail(ctx != NULL, FALSE);
751
752         g_free(ctx->hdr);
753         if (ctx->cmap)
754                 g_free(ctx->cmap);
755         gdk_pixbuf_unref(ctx->pbuf);
756         if (ctx->in->size)
757                 ctx->in = io_buffer_free_segment(ctx->in, ctx->in->size, err);
758         if (!ctx->in) {
759                 g_free(ctx);
760                 return FALSE;
761         }
762         io_buffer_free(ctx->in);
763         g_free(ctx);
764         return TRUE;
765 }
766
767 static TGAHeader *get_header_from_file(FILE *f, GError **err)
768 {
769         TGAHeader *hdr;
770
771         if (!fseek_check(f, 0, SEEK_SET, err))
772                 return NULL;
773         if (!(hdr = g_try_malloc(sizeof(TGAHeader)))) {
774                 g_set_error(err, GDK_PIXBUF_ERROR, 
775                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
776                             _("Can't allocate memory for TGA header"));
777                 return NULL;
778         }
779         if (!fread_check(hdr, sizeof(TGAHeader), 1, f, err)) {
780                 g_free(hdr);
781                 return NULL;
782         }
783         if (hdr->infolen > 255) {
784                 g_set_error(err, GDK_PIXBUF_ERROR, 
785                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
786                             _("Too big value in the infolen field of TGA header."));
787                 g_free(hdr);
788                 return NULL;
789         }
790
791         return hdr;
792 }
793
794 static TGAColormap *get_colormap_from_file(FILE *f, 
795                                            TGAHeader *hdr,
796                                            GError **err)
797 {
798         TGAColormap *cmap;
799         guchar *pal_buf, *p;
800         guint n, pal_size;
801   
802         if (!fseek_check(f, sizeof(TGAHeader) + hdr->infolen, SEEK_SET, err))
803                 return NULL;
804
805         pal_size = LE16(hdr->cmap_n_colors) * ((hdr->cmap_bpp + 7) >> 3);
806         pal_buf = g_try_malloc(pal_size);
807         if (!pal_buf) {
808                 g_set_error(err, GDK_PIXBUF_ERROR, 
809                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
810                             _("Can't allocate memory for TGA cmap temporary buffer"));
811                 return NULL;
812         }
813         if (!fread_check(pal_buf, pal_size, 1, f, err)) {
814                 g_free(pal_buf);
815                 return NULL;
816         }
817         p = pal_buf;
818
819         if (!(cmap = g_try_malloc(sizeof(TGAColormap)))) {
820                 g_set_error(err, GDK_PIXBUF_ERROR, 
821                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
822                             _("Can't allocate memory for TGA colormap struct"));
823                 g_free(pal_buf);
824                 return NULL;
825         }
826         cmap->size = LE16(hdr->cmap_n_colors);
827         cmap->cols = g_try_malloc(sizeof(TGAColor) * cmap->size);
828         if (!cmap->cols) {
829                 g_set_error(err, GDK_PIXBUF_ERROR, 
830                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
831                             _("Can't allocate memory for TGA colormap entries"));
832                 g_free(pal_buf);
833                 g_free(cmap);
834                 return NULL;
835         }
836
837         if (hdr->cmap_bpp != 15 && hdr->cmap_bpp != 16 &&
838             hdr->cmap_bpp != 24 && hdr->cmap_bpp != 32) {
839                 g_set_error(err, GDK_PIXBUF_ERROR, 
840                             GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
841                             _("Unexpected bitdepth for TGA colormap"));
842                 g_free(pal_buf);
843                 g_free(cmap->cols);
844                 g_free(cmap);
845                 return NULL;
846         }
847
848         for (n = 0; n < cmap->size; n++) {
849                 if ((hdr->cmap_bpp == 15) || (hdr->cmap_bpp == 16)) {
850                         guint16 col = p[0] + (p[1] << 8);
851                         p += 2;
852                         cmap->cols[n].b = (col >> 7) & 0xf8;
853                         cmap->cols[n].g = (col >> 2) & 0xf8;
854                         cmap->cols[n].r = col << 3;
855                 } else if ((hdr->cmap_bpp == 24) || (hdr->cmap_bpp == 32)) {
856                         cmap->cols[n].b = *p++;
857                         cmap->cols[n].g = *p++;
858                         cmap->cols[n].r = *p++;
859                         if (hdr->cmap_bpp == 32)
860                                 cmap->cols[n].a = *p++;
861                 }
862         }
863
864         g_free(pal_buf);
865         return cmap;
866 }
867
868 static GdkPixbuf *get_image_pseudocolor(FILE *f, TGAHeader *hdr,
869                                         TGAColormap *cmap, gboolean rle,
870                                         GError **err)
871 {
872         GdkPixbuf *pbuf;
873         guchar *p, color, tag;
874         glong n, image_offset;
875         guint count;
876
877         image_offset = sizeof(TGAHeader) + hdr->infolen;
878         if (!hdr->has_cmap) {
879                 g_set_error(err, GDK_PIXBUF_ERROR, 
880                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
881                             _("Pseudo-Color image without colormap"));
882                 return NULL;
883         } else {
884                 image_offset += cmap->size * ((hdr->cmap_bpp + 7) >> 3);
885         }
886         if (!fseek_check(f, image_offset, SEEK_SET, err)) {
887                 g_set_error(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED,
888                             _("Can't seek to image offset -- end-of-file probably encountered"));
889                 return NULL;
890         }
891
892         pbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, (hdr->cmap_bpp == 32), 8,
893                               LE16(hdr->width), LE16(hdr->height));
894         if (!pbuf) {
895                 g_set_error(err, GDK_PIXBUF_ERROR, 
896                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
897                             _("Can't allocate pixbuf"));
898                 return NULL;
899         }
900         pbuf->destroy_fn = free_buffer;
901         pbuf->destroy_fn_data = NULL;
902         p = pbuf->pixels;
903
904         if (rle) {
905                 n = count = 0;
906                 for (; n < pbuf->width * pbuf->height;) {
907                         if (!fread_check(&tag, 1, 1, f, err)) {
908                                 gdk_pixbuf_unref(pbuf);
909                                 return NULL;
910                         }
911                         if (tag & 0x80) {
912                                 count = (tag & 0x7f) + 1;
913                                 n += count;
914                                 if (!fread_check(&color, 1, 1, f, err)) {
915                                         gdk_pixbuf_unref(pbuf);
916                                         return NULL;
917                                 }
918                                 for (; count; count--) {
919                                         *p++ = cmap->cols[color].r;
920                                         *p++ = cmap->cols[color].g;
921                                         *p++ = cmap->cols[color].b;
922                                         if (hdr->cmap_bpp == 32)
923                                                 *p++ = cmap->cols[color].a;
924                                 }
925                         } else {
926                                 count = tag + 1;
927                                 n += count;
928                                 for (; count; count--) {
929                                         if (!fread_check(&color, 1, 1, f, err)) {
930                                                 gdk_pixbuf_unref(pbuf);
931                                                 return NULL;
932                                         }
933                                         *p++ = cmap->cols[color].r;
934                                         *p++ = cmap->cols[color].g;
935                                         *p++ = cmap->cols[color].b;
936                                         if (hdr->cmap_bpp == 32)
937                                                 *p++ = cmap->cols[color].a;
938                                 }
939                         }
940                 }
941         } else {
942                 for (n = 0; n < pbuf->width * pbuf->height; n++) {
943                         if (!fread_check(&color, 1, 1, f, err)) {
944                                 gdk_pixbuf_unref(pbuf);
945                                 return NULL;
946                         }
947                         *p++ = cmap->cols[color].r;
948                         *p++ = cmap->cols[color].g;
949                         *p++ = cmap->cols[color].b;
950                         if (hdr->cmap_bpp == 32)
951                                 *p++ = cmap->cols[color].a;
952                 }
953         }
954
955         return pbuf;
956 }
957
958 static void swap_channels_pixbuf(GdkPixbuf *pbuf)
959 {
960         guchar *p, swap;
961         glong n;
962
963         p = pbuf->pixels;
964         for (n = 0; n < pbuf->width * pbuf->height; n++) {
965                 swap = p[0];
966                 p[0] = p[2];
967                 p[2] = swap;
968                 p += pbuf->n_channels;
969         }
970 }
971
972 static GdkPixbuf *get_image_truecolor(FILE *f, TGAHeader *hdr,
973                                       gboolean rle, GError **err)
974 {
975         GdkPixbuf *pbuf;
976         guchar *p, tag;
977         glong n, image_offset;
978         guint32 pixel;
979         guint count;
980
981         image_offset = sizeof(TGAHeader) + hdr->infolen;
982         /* A truecolor image shouldn't actually have a colormap. */
983         if (hdr->has_cmap)
984                 image_offset += LE16(hdr->cmap_n_colors) * ((hdr->cmap_bpp + 7) >> 3);
985         if (!fseek_check(f, image_offset, SEEK_SET, err))
986                 return NULL;
987
988         pbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, (hdr->bpp == 32), 8,
989                               LE16(hdr->width), LE16(hdr->height));
990         if (!pbuf) {
991                 g_set_error(err, GDK_PIXBUF_ERROR, 
992                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
993                             _("Can't allocate pixbuf"));
994                 return NULL;
995         }
996         pbuf->destroy_fn = free_buffer;
997         pbuf->destroy_fn_data = NULL;
998         p = pbuf->pixels;
999
1000         if (rle) {
1001                 n = count = 0;
1002                 for (; n < pbuf->width * pbuf->height;) {
1003                         if (!fread_check(&tag, 1, 1, f, err)) {
1004                                 gdk_pixbuf_unref(pbuf);
1005                                 return NULL;
1006                         }
1007                         if (tag & 0x80) {
1008                                 count = (tag & 0x7f) + 1;
1009                                 n += count;
1010                                 if (!fread_check(&pixel, pbuf->n_channels, 1, f, err)) {
1011                                         gdk_pixbuf_unref(pbuf);
1012                                         return NULL;
1013                                 }
1014                                 for (; count; count--) {
1015                                         g_memmove(p, &pixel, pbuf->n_channels);
1016                                         p += pbuf->n_channels;
1017                                 }
1018                         } else {
1019                                 count = tag + 1;
1020                                 n += count;
1021                                 if (!fread_check(p, pbuf->n_channels * count, 1, f, err)) {
1022                                         gdk_pixbuf_unref(pbuf);
1023                                         return NULL;
1024                                 }
1025                                 p += pbuf->n_channels * count;
1026                         }
1027                 }
1028         } else {
1029                 if (!fread_check(p, pbuf->rowstride * pbuf->height, 1, f, err)) {
1030                         gdk_pixbuf_unref(pbuf);
1031                         return NULL;
1032                 }
1033         }
1034
1035         swap_channels_pixbuf(pbuf);
1036         return pbuf;
1037 }
1038
1039 static GdkPixbuf *get_image_grayscale(FILE *f, TGAHeader *hdr,
1040                                       gboolean rle, GError **err)
1041 {
1042         GdkPixbuf *pbuf;
1043         glong n, image_offset;
1044         guchar *p, color, tag;
1045         guint count;
1046
1047         image_offset = sizeof(TGAHeader) + hdr->infolen;
1048         /* A grayscale image shouldn't actually have a colormap. */
1049         if (hdr->has_cmap)
1050                 image_offset += LE16(hdr->cmap_n_colors) * ((hdr->cmap_bpp + 7) >> 3);
1051         if (!fseek_check(f, image_offset, SEEK_SET, err))
1052                 return NULL;
1053
1054         pbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
1055                               LE16(hdr->width), LE16(hdr->height));
1056         if (!pbuf) {
1057                 g_set_error(err, GDK_PIXBUF_ERROR, 
1058                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
1059                             _("Can't allocate pixbuf"));
1060                 return NULL;
1061         }
1062         pbuf->destroy_fn = free_buffer;
1063         pbuf->destroy_fn_data = NULL;
1064         p = pbuf->pixels;
1065
1066         if (rle) {
1067                 n = count = 0;
1068                 for (; n < pbuf->width * pbuf->height;) {
1069                         if (!fread_check(&tag, 1, 1, f, err)) {
1070                                 gdk_pixbuf_unref(pbuf);
1071                                 return NULL;
1072                         }
1073                         if (tag & 0x80) {
1074                                 count = (tag & 0x7f) + 1;
1075                                 n += count;
1076                                 if (!fread_check(&color, 1, 1, f, err)) {
1077                                         gdk_pixbuf_unref(pbuf);
1078                                         return NULL;
1079                                 }
1080                                 for (; count; count--) {
1081                                         p[0] = p[1] = p[2] = color;
1082                                         p += 3;
1083                                 }
1084                         } else {
1085                                 count = tag + 1;
1086                                 n += count;
1087                                 for (; count; count--) {
1088                                         if (!fread_check(&color, 1, 1, f, err)) {
1089                                                 gdk_pixbuf_unref(pbuf);
1090                                                 return NULL;
1091                                         }
1092                                         p[0] = p[1] = p[2] = color;
1093                                         p += 3;
1094                                 }
1095                         }
1096                 }
1097         } else {
1098                 for (n = 0; n < pbuf->width * pbuf->height; n++) {
1099                         if (!fread_check(&color, 1, 1, f, err)) {
1100                                 gdk_pixbuf_unref(pbuf);
1101                                 return NULL;
1102                         }
1103                         p[0] = p[1] = p[2] = color;
1104                         p += 3;
1105                 }
1106         }
1107
1108         return pbuf;
1109 }
1110
1111 static GdkPixbuf *gdk_pixbuf__tga_load(FILE *f, GError **err)
1112 {
1113         TGAHeader *hdr;
1114         TGAColormap *cmap;
1115         GdkPixbuf *pbuf;
1116
1117         cmap = NULL;
1118         hdr = get_header_from_file(f, err);
1119         if (!hdr)
1120                 return NULL;
1121         if ((hdr->flags & TGA_INTERLEAVE_MASK) != TGA_INTERLEAVE_NONE ||
1122             hdr->flags & TGA_ORIGIN_RIGHT || hdr->flags & TGA_ORIGIN_LOWER) {
1123                 g_set_error(err, GDK_PIXBUF_ERROR, 
1124                             GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
1125                             _("Unsupported TGA image type"));
1126                 g_free(hdr);
1127                 return NULL;
1128         }
1129   
1130         if (hdr->has_cmap && ((hdr->type == TGA_TYPE_PSEUDOCOLOR) || 
1131                               (hdr->type == TGA_TYPE_RLE_PSEUDOCOLOR))) {
1132                 cmap = get_colormap_from_file(f, hdr, err);
1133                 if (!cmap) {
1134                         g_free(hdr);
1135                         return NULL;
1136                 }
1137         }
1138
1139         if (hdr->type == TGA_TYPE_PSEUDOCOLOR)
1140                 pbuf = get_image_pseudocolor(f, hdr, cmap, FALSE, err);
1141         else if (hdr->type == TGA_TYPE_RLE_PSEUDOCOLOR)
1142                 pbuf = get_image_pseudocolor(f, hdr, cmap, TRUE, err);
1143         else if (hdr->type == TGA_TYPE_TRUECOLOR)
1144                 pbuf = get_image_truecolor(f, hdr, FALSE, err);
1145         else if (hdr->type == TGA_TYPE_RLE_TRUECOLOR)
1146                 pbuf = get_image_truecolor(f, hdr, TRUE, err);
1147         else if (hdr->type == TGA_TYPE_GRAYSCALE)
1148                 pbuf = get_image_grayscale(f, hdr, FALSE, err);
1149         else if (hdr->type == TGA_TYPE_RLE_GRAYSCALE)
1150                 pbuf = get_image_grayscale(f, hdr, TRUE, err);
1151         else {
1152                 g_set_error(err, GDK_PIXBUF_ERROR, 
1153                             GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
1154                             _("Unsupported TGA image type"));
1155                 pbuf = NULL;
1156         }
1157   
1158         if (cmap) {
1159                 g_free(cmap->cols);
1160                 g_free(cmap);
1161         }
1162         g_free(hdr);
1163   
1164         return pbuf;
1165 }
1166
1167 void
1168 gdk_pixbuf__tga_fill_vtable (GdkPixbufModule *module)
1169 {
1170         module->load = gdk_pixbuf__tga_load;
1171         module->begin_load = gdk_pixbuf__tga_begin_load;
1172         module->stop_load = gdk_pixbuf__tga_stop_load;
1173         module->load_increment = gdk_pixbuf__tga_load_increment;
1174 }