Refactoring the iff decoder
[deark.git] / modules / tga.c
blob5627f63a415813a2cace51e0db6cfc44516599e5
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 #include <deark-config.h>
6 #include <deark-private.h>
7 #include <deark-fmtutil.h>
8 DE_DECLARE_MODULE(de_module_tga);
10 struct tgaimginfo {
11 i64 width, height;
12 i64 img_size_in_bytes;
13 int is_thumb;
14 int respect_alpha_channel;
17 typedef struct localctx_struct {
18 i64 id_field_len;
19 #define FMT_TGA 0
20 #define FMT_VST 1
21 int file_format;
22 u8 color_map_type;
23 u8 img_type;
24 struct tgaimginfo main_image;
25 struct tgaimginfo thumbnail_image;
26 i64 cmap_start;
27 i64 cmap_length;
28 i64 cmap_depth;
29 i64 pixel_depth;
30 u8 image_descriptor;
31 i64 num_attribute_bits;
32 u8 attributes_type;
33 u8 top_down, right_to_left;
34 u8 interleave_mode;
35 int has_signature;
36 int has_extension_area;
37 #define TGA_CMPR_UNKNOWN 0
38 #define TGA_CMPR_NONE 1
39 #define TGA_CMPR_RLE 2
40 int cmpr_type;
41 #define TGA_CLRTYPE_UNKNOWN 0
42 #define TGA_CLRTYPE_PALETTE 1
43 #define TGA_CLRTYPE_TRUECOLOR 2
44 #define TGA_CLRTYPE_GRAYSCALE 3
45 int color_type;
46 const char *cmpr_name;
47 const char *clrtype_name;
48 i64 bytes_per_pixel; // May be meaningless if pixel_depth is not a multiple of 8
49 i64 bytes_per_pal_entry;
50 i64 pal_size_in_bytes;
51 i64 aspect_ratio_num, aspect_ratio_den;
52 i64 thumbnail_offset;
53 u32 pal[256];
54 struct de_timestamp mod_time;
55 } lctx;
57 // Figure out if the image has transparency, and emit a warning or other message if
58 // appropriate.
59 // TGA transparency is kind of a mess. Multiple ways of labeling it, some of
60 // which are ambiguous... Files that have inconsistent labels... Files that
61 // claim to have transparency but don't, or that claim not to but do...
62 static int should_use_alpha_channel(deark *c, lctx *d, struct tgaimginfo *imginfo,
63 int has_alpha_0, int has_alpha_partial, int has_alpha_255)
65 if(imginfo->is_thumb) {
66 // Do the same thing with the thumbnail as we did with the main image.
67 return d->main_image.respect_alpha_channel;
70 if(d->pixel_depth!=32 || d->color_type!=TGA_CLRTYPE_TRUECOLOR) {
71 return 0;
74 if(d->num_attribute_bits!=0 && d->num_attribute_bits!=8) {
75 de_warn(c, "%d-bit attribute channel not supported. Transparency disabled.",
76 (int)d->num_attribute_bits);
77 return 0;
80 if(d->has_extension_area) {
81 if(d->attributes_type==0) {
82 // attributes_type==0 technically also means there is no transparency,
83 // but this cannot be trusted.
86 if(d->attributes_type==1 || d->attributes_type==2) {
87 // Indicates that attribute data can be ignored.
88 return 0;
90 else if(d->attributes_type==3 && d->num_attribute_bits==8) {
91 // Alpha channel seems to be labeled correctly.
92 // Trust it.
93 return 1;
95 else if(d->attributes_type==4) {
96 // Sigh. The spec shows that Field 24 (Attributes Type) == 4 is for
97 // pre-multiplied alpha. Then the discussion section says that
98 // Field *23* ("Attributes Type") == *3* is for premultiplied alpha.
99 // I have to guess that the "23" and "3" are clerical errors, but that
100 // doesn't do me much good unless all TGA developers made the same guess.
101 de_warn(c, "Pre-multiplied alpha is not supported. Disabling transparency.");
102 return 0;
106 if(has_alpha_partial || (has_alpha_0 && has_alpha_255)) {
107 if(d->num_attribute_bits==0) {
108 de_warn(c, "Detected likely alpha channel. Enabling transparency, even though "
109 "the image is labeled as non-transparent.");
111 return 1;
113 else if(has_alpha_0) { // All 0x00
114 if(d->num_attribute_bits!=0) {
115 de_warn(c, "Non-visible image detected. Disabling transparency.");
117 else {
118 de_dbg(c, "potential alpha channel ignored: all 0 bits");
120 return 0;
122 else { // All 0xff
123 de_dbg(c, "potential alpha channel is moot: all 1 bits");
124 return 0;
128 static void do_decode_image(deark *c, lctx *d, struct tgaimginfo *imginfo, dbuf *unc_pixels,
129 const char *token, unsigned int createflags)
131 de_bitmap *img = NULL;
132 de_finfo *fi = NULL;
133 i64 i, j;
134 i64 pdwidth;
135 u8 b;
136 u32 clr;
137 u8 a;
138 i64 rowspan;
139 int output_bypp;
140 unsigned int getrgbflags;
141 i64 interleave_stride;
142 i64 interleave_pass;
143 i64 cur_rownum; // 0-based, does not account for bottom-up orientation
144 int has_alpha_0 = 0;
145 int has_alpha_partial = 0;
146 int has_alpha_255 = 0;
148 fi = de_finfo_create(c);
150 if(token) {
151 de_finfo_set_name_from_sz(c, fi, token, 0, DE_ENCODING_LATIN1);
153 if(d->mod_time.is_valid) {
154 fi->internal_mod_time = d->mod_time;
157 pdwidth = imginfo->width;
158 if(d->pixel_depth==1) {
159 de_warn(c, "1-bit TGA images are not portable, and may not be decoded correctly");
160 pdwidth = de_pad_to_n(imginfo->width, 8);
161 rowspan = pdwidth/8;
163 else {
164 rowspan = imginfo->width*d->bytes_per_pixel;
167 if(d->color_type==TGA_CLRTYPE_GRAYSCALE || d->pixel_depth==1)
168 output_bypp=1;
169 else if(d->pixel_depth==32)
170 output_bypp=4;
171 else
172 output_bypp=3;
174 if(d->file_format==FMT_VST)
175 getrgbflags = 0;
176 else
177 getrgbflags = DE_GETRGBFLAG_BGR;
179 img = de_bitmap_create2(c, imginfo->width, pdwidth, imginfo->height, output_bypp);
181 switch(d->interleave_mode) {
182 case 1: interleave_stride = 2; break;
183 case 2: interleave_stride = 4; break;
184 default: interleave_stride = 1;
187 cur_rownum = 0;
188 interleave_pass = 0;
190 for(j=0; j<imginfo->height; j++) {
191 i64 j_adj;
193 if(d->top_down)
194 j_adj = cur_rownum;
195 else
196 j_adj = imginfo->height-1-cur_rownum;
198 // Update the row number for next time
199 cur_rownum += interleave_stride;
200 if(cur_rownum >= imginfo->height) {
201 // Went past the end of the image; move back to near the start.
202 interleave_pass++;
203 cur_rownum = interleave_pass;
206 if(d->pixel_depth==1) {
207 de_convert_row_bilevel(unc_pixels, j*rowspan, img, j_adj, 0);
208 continue;
211 for(i=0; i<pdwidth; i++) {
212 i64 i_adj;
214 if(d->right_to_left)
215 i_adj = imginfo->width-1-i;
216 else
217 i_adj = i;
219 if(d->color_type==TGA_CLRTYPE_TRUECOLOR && (d->pixel_depth==15 || d->pixel_depth==16)) {
220 clr = (u32)dbuf_getu16le(unc_pixels, j*rowspan + i*d->bytes_per_pixel);
221 clr = de_rgb555_to_888(clr);
222 de_bitmap_setpixel_rgb(img, i_adj, j_adj, clr);
224 else if(d->color_type==TGA_CLRTYPE_TRUECOLOR) {
225 clr = dbuf_getRGB(unc_pixels, j*rowspan + i*d->bytes_per_pixel, getrgbflags);
226 if(d->pixel_depth==32) {
227 a = dbuf_getbyte(unc_pixels, j*rowspan + i*d->bytes_per_pixel+3);
228 de_bitmap_setpixel_rgba(img, i_adj, j_adj, DE_SET_ALPHA(clr, a));
230 // Collect metrics that we may need, to decide whether to keep the
231 // might-be-alpha channel.
232 if(a==0) {
233 has_alpha_0 = 1;
235 else if(a==0xff) {
236 has_alpha_255 = 1;
238 else {
239 has_alpha_partial = 1;
242 else {
243 de_bitmap_setpixel_rgb(img, i_adj, j_adj, clr);
246 else if(d->color_type==TGA_CLRTYPE_GRAYSCALE) {
247 b = dbuf_getbyte(unc_pixels, j*rowspan + i*d->bytes_per_pixel);
248 de_bitmap_setpixel_gray(img, i_adj, j_adj, b);
250 else if(d->color_type==TGA_CLRTYPE_PALETTE) {
251 b = dbuf_getbyte(unc_pixels, j*rowspan + i*d->bytes_per_pixel);
252 de_bitmap_setpixel_rgb(img, i_adj, j_adj, d->pal[(unsigned int)b]);
257 // TODO: 16-bit images could theoretically have a transparency bit, but I don't
258 // know how to detect that.
259 if(d->pixel_depth==32) {
260 imginfo->respect_alpha_channel = should_use_alpha_channel(c, d, imginfo,
261 has_alpha_0, has_alpha_partial, has_alpha_255);
263 if(!imginfo->is_thumb) {
264 de_dbg(c, "using alpha channel: %s", imginfo->respect_alpha_channel?"yes":"no");
267 if(!imginfo->respect_alpha_channel) {
268 de_bitmap_remove_alpha(img);
272 de_bitmap_write_to_file_finfo(img, fi, createflags);
274 de_bitmap_destroy(img);
275 de_finfo_destroy(c, fi);
278 static void do_decode_rle_internal(deark *c1, struct de_dfilter_in_params *dcmpri,
279 struct de_dfilter_out_params *dcmpro, struct de_dfilter_results *dres,
280 i64 bytes_per_pixel)
282 u8 b;
283 i64 count;
284 i64 k;
285 u8 buf[8];
286 i64 pos = dcmpri->pos;
288 if(bytes_per_pixel<1 || bytes_per_pixel>8) return;
290 while(1) {
291 if(pos >= dcmpri->pos + dcmpri->len) break;
292 if(dcmpro->f->len >= dcmpro->expected_len) break;
294 b = dbuf_getbyte(dcmpri->f, pos);
295 pos++;
297 if(b & 0x80) { // RLE block
298 count = (i64)(b - 0x80) + 1;
299 dbuf_read(dcmpri->f, buf, pos, bytes_per_pixel);
300 pos += bytes_per_pixel;
301 for(k=0; k<count; k++) {
302 dbuf_write(dcmpro->f, buf, bytes_per_pixel);
305 else { // uncompressed block
306 count = (i64)(b) + 1;
307 dbuf_copy(dcmpri->f, pos, count * bytes_per_pixel, dcmpro->f);
308 pos += count * bytes_per_pixel;
311 dres->bytes_consumed = pos - dcmpri->pos;
312 dres->bytes_consumed_valid = 1;
315 static int do_decode_rle(deark *c, lctx *d, i64 pos1, dbuf *unc_pixels)
317 struct de_dfilter_in_params dcmpri;
318 struct de_dfilter_out_params dcmpro;
319 struct de_dfilter_results dres;
321 de_dfilter_init_objects(c, &dcmpri, &dcmpro, &dres);
322 dcmpri.f = c->infile;
323 dcmpri.pos = pos1;
324 dcmpri.len = c->infile->len - pos1;
325 dcmpro.f = unc_pixels;
326 dcmpro.len_known = 1;
327 dcmpro.expected_len = d->main_image.img_size_in_bytes;
329 do_decode_rle_internal(c, &dcmpri, &dcmpro, &dres, d->bytes_per_pixel);
331 if(dres.errcode) {
332 de_err(c, "%s", de_dfilter_get_errmsg(c, &dres));
333 return 0;
335 de_dbg(c, "decompressed %"I64_FMT" bytes to %"I64_FMT" bytes",
336 dres.bytes_consumed, unc_pixels->len);
337 return 1;
340 static void do_decode_thumbnail(deark *c, lctx *d)
342 dbuf *unc_pixels = NULL;
343 i64 hdrsize = 2;
345 de_dbg(c, "thumbnail image at %d", (int)d->thumbnail_offset);
346 de_dbg_indent(c, 1);
348 // The thumbnail image is supposed to use the same format as the main image,
349 // except without compression. (And the dimensions are obviously different.)
350 // Presumably this means the origin, palette, etc. will be the same.
351 // But based on the few TGA thumbnails we've seen, nobody reads the spec, and
352 // it's anybody's guess what format the thumbnail will use.
354 // TGA 2.0 spec says the dimensions are one *byte* each.
355 d->thumbnail_image.width = (i64)de_getbyte(d->thumbnail_offset);
356 d->thumbnail_image.height = (i64)de_getbyte(d->thumbnail_offset+1);
357 de_dbg(c, "thumbnail dimensions: %d"DE_CHAR_TIMES"%d", (int)d->thumbnail_image.width, (int)d->thumbnail_image.height);
359 if(d->thumbnail_image.width!=0 && d->thumbnail_image.height==0) {
360 de_warn(c, "Thumbnail image height is 0. Assuming the file incorrectly uses "
361 "16-bit thumbnail dimensions, instead of 8.");
362 d->thumbnail_image.width = de_getu16le(d->thumbnail_offset);
363 d->thumbnail_image.height = de_getu16le(d->thumbnail_offset+2);
364 de_dbg(c, "revised thumbnail dimensions: %d"DE_CHAR_TIMES"%d", (int)d->thumbnail_image.width, (int)d->thumbnail_image.height);
365 hdrsize = 4;
367 if(!de_good_image_dimensions(c, d->thumbnail_image.width, d->thumbnail_image.height)) goto done;
369 d->thumbnail_image.img_size_in_bytes = d->thumbnail_image.height * d->thumbnail_image.width * d->bytes_per_pixel;
370 unc_pixels = dbuf_open_input_subfile(c->infile, d->thumbnail_offset+hdrsize, d->thumbnail_image.img_size_in_bytes);
372 do_decode_image(c, d, &d->thumbnail_image, unc_pixels, "thumb", DE_CREATEFLAG_IS_AUX);
374 done:
375 dbuf_close(unc_pixels);
376 de_dbg_indent(c, -1);
379 static int do_read_palette(deark *c, lctx *d, i64 pos)
381 i64 i;
382 i64 idx;
383 unsigned int getrgbflags;
385 if(d->color_type != TGA_CLRTYPE_PALETTE) {
386 return 1; // don't care about the palette
389 if(d->cmap_depth != 24) {
390 de_err(c, "Palettes with depth=%d are not supported.", (int)d->cmap_depth);
391 return 0;
393 if(d->pixel_depth != 8) {
394 de_err(c, "Paletted images with depth=%d are not supported.", (int)d->pixel_depth);
395 return 0;
398 if(d->file_format==FMT_VST)
399 getrgbflags = 0;
400 else
401 getrgbflags = DE_GETRGBFLAG_BGR;
403 for(i=0; i<d->cmap_length; i++) {
404 idx = d->cmap_start + i;
405 if(idx<0 || idx>255) continue;
406 d->pal[idx] = dbuf_getRGB(c->infile, pos + i*d->bytes_per_pal_entry, getrgbflags);
407 de_dbg_pal_entry(c, idx, d->pal[idx]);
409 return 1;
412 static void do_read_extension_area(deark *c, lctx *d, i64 pos)
414 i64 ext_area_size;
415 i64 k;
416 int has_date;
417 de_ucstring *s = NULL;
418 i64 val[6];
420 de_dbg(c, "extension area at %d", (int)pos);
421 if(pos > c->infile->len - 2) {
422 de_warn(c, "Bad extension area offset: %u", (unsigned int)pos);
423 return;
426 de_dbg_indent(c, 1);
428 s = ucstring_create(c);
430 ext_area_size = de_getu16le(pos);
431 de_dbg(c, "extension area size: %d", (int)ext_area_size);
432 if(ext_area_size<495) goto done;
434 d->has_extension_area = 1;
436 ucstring_empty(s);
437 dbuf_read_to_ucstring(c->infile, pos+2, 41, s, DE_CONVFLAG_STOP_AT_NUL, DE_ENCODING_ASCII);
438 ucstring_strip_trailing_spaces(s);
439 de_dbg(c, "author: \"%s\"", ucstring_getpsz_d(s));
441 for(k=0; k<4; k++) {
442 ucstring_empty(s);
443 dbuf_read_to_ucstring(c->infile, pos+43+81*k, 81, s, DE_CONVFLAG_STOP_AT_NUL, DE_ENCODING_ASCII);
444 ucstring_strip_trailing_spaces(s);
445 de_dbg(c, "comment line %d: \"%s\"", (int)k, ucstring_getpsz_d(s));
448 // date/time: pos=367, size=12
449 has_date = 0;
450 for(k=0; k<6; k++) {
451 val[k] = de_getu16le(pos+367+2*k);
452 if(val[k]!=0) has_date = 1;
454 if(has_date) {
455 char timestamp_buf[64];
457 de_make_timestamp(&d->mod_time, val[2], val[0], val[1], val[3], val[4], val[5]);
458 d->mod_time.tzcode = DE_TZCODE_LOCAL;
459 de_timestamp_to_string(&d->mod_time, timestamp_buf, sizeof(timestamp_buf), 0);
460 de_dbg(c, "timestamp: %s", timestamp_buf);
463 // Job name: pos=379, size=41 (not implemented)
464 // Job time: pos=420, size=6 (not implemented)
466 ucstring_empty(s);
467 dbuf_read_to_ucstring(c->infile, pos+426, 41, s, DE_CONVFLAG_STOP_AT_NUL, DE_ENCODING_ASCII);
468 ucstring_strip_trailing_spaces(s);
469 de_dbg(c, "software id: \"%s\"", ucstring_getpsz_d(s));
471 val[0] = de_getu16le(pos+467);
472 val[1] = (i64)de_getbyte(pos+469);
473 if(val[0]!=0 || val[1]!=32) {
474 de_dbg(c, "software version: %u,%u,%u",
475 (unsigned int)(val[0]/100), (unsigned int)(val[0]%100),
476 (unsigned int)val[1]);
479 val[0] = de_getu32le(pos+470);
480 if(val[0]!=0) {
481 de_dbg(c, "background color: 0x%08x", (unsigned int)val[0]);
484 // TODO: Retain the aspect ratio. (Need sample files. Nobody seems to use this field.)
485 d->aspect_ratio_num = de_getu16le(pos+474);
486 d->aspect_ratio_den = de_getu16le(pos+476);
487 if(d->aspect_ratio_den!=0) {
488 de_dbg(c, "aspect ratio: %d/%d", (int)d->aspect_ratio_num, (int)d->aspect_ratio_den);
491 // Gamma: pos=478, size=4 (not implemented)
492 // Color correction table offset: pos=482, size=4 (not implemented)
494 d->thumbnail_offset = de_getu32le(pos+486);
495 de_dbg(c, "thumbnail image offset: %d", (int)d->thumbnail_offset);
497 val[0] = de_getu32le(pos+490);
498 de_dbg(c, "scan line table offset: %"I64_FMT, val[0]);
500 d->attributes_type = de_getbyte(pos+494);
501 de_dbg(c, "attributes type: %d", (int)d->attributes_type);
502 if(d->attributes_type==0 && d->num_attribute_bits!=0) {
503 de_warn(c, "Incompatible \"number of attribute bits\" (%d) and \"attributes type\" "
504 "(%d) fields. Transparency may not be handled correctly.",
505 (int)d->num_attribute_bits, (int)d->attributes_type);
508 done:
509 de_dbg_indent(c, -1);
510 ucstring_destroy(s);
513 static void do_read_developer_area(deark *c, lctx *d, i64 pos)
515 i64 num_tags;
516 i64 i;
517 i64 tag_id, tag_data_pos, tag_data_size;
519 de_dbg(c, "developer area at %d", (int)pos);
520 if(pos > c->infile->len - 2) {
521 de_warn(c, "Bad developer area offset: %u", (unsigned int)pos);
522 return;
525 de_dbg_indent(c, 1);
526 num_tags = de_getu16le(pos);
527 de_dbg(c, "number of tags: %d", (int)num_tags);
528 for(i=0; i<num_tags; i++) {
529 if(i>=200) break;
530 tag_id = de_getu16le(pos + 2 + 10*i);
531 tag_data_pos = de_getu32le(pos + 2 + 10*i + 2);
532 tag_data_size = de_getu32le(pos + 2 + 10*i + 6);
533 de_dbg(c, "tag #%d: id=%d, pos=%d, size=%d", (int)i, (int)tag_id,
534 (int)tag_data_pos, (int)tag_data_size);
536 if(tag_id==20) {
537 // Tag 20 seems to contain Photoshop resources, though this is unconfirmed.
538 de_dbg_indent(c, 1);
539 // TODO: We could retrieve the pixel density settings from the Photoshop data,
540 // but it's not clear whether they are ever useful.
541 fmtutil_handle_photoshop_rsrc(c, c->infile, tag_data_pos, tag_data_size, 0x0);
542 de_dbg_indent(c, -1);
545 de_dbg_indent(c, -1);
548 static void do_read_footer(deark *c, lctx *d)
550 i64 footerpos;
551 i64 ext_offset, dev_offset;
553 footerpos = c->infile->len - 26;
554 de_dbg(c, "v2 footer at %d", (int)footerpos);
555 de_dbg_indent(c, 1);
556 ext_offset = de_getu32le(footerpos);
557 de_dbg(c, "extension area offset: %d", (int)ext_offset);
558 dev_offset = de_getu32le(footerpos+4);
559 de_dbg(c, "developer area offset: %d", (int)dev_offset);
560 de_dbg_indent(c, -1);
562 if(ext_offset!=0) {
563 do_read_extension_area(c, d, ext_offset);
566 if(dev_offset!=0) {
567 do_read_developer_area(c, d, dev_offset);
571 static void do_read_image_descriptor(deark *c, lctx *d)
573 d->image_descriptor = de_getbyte(17);
574 de_dbg(c, "descriptor: 0x%02x", (unsigned int)d->image_descriptor);
576 de_dbg_indent(c, 1);
577 d->num_attribute_bits = (i64)(d->image_descriptor & 0x0f);
578 de_dbg(c, "number of %s bits: %d",
579 d->file_format==FMT_VST?"alpha channel":"attribute",
580 (int)d->num_attribute_bits);
582 d->right_to_left = (d->image_descriptor>>4)&0x01;
583 d->top_down = (d->image_descriptor>>5)&0x01;
584 de_dbg(c, "right-to-left flag: %d", (int)d->right_to_left);
585 de_dbg(c, "top-down flag: %d", (int)d->top_down);
587 d->interleave_mode = d->image_descriptor >> 6;
588 if(d->interleave_mode != 0) {
589 de_dbg(c, "interleaving: %d", (int)d->interleave_mode);
591 de_dbg_indent(c, -1);
594 static int do_read_tga_headers(deark *c, lctx *d)
596 int retval = 0;
598 de_dbg(c, "header at %d", 0);
599 de_dbg_indent(c, 1);
601 d->id_field_len = (i64)de_getbyte(0);
602 d->color_map_type = de_getbyte(1);
603 de_dbg(c, "color map type: %d", (int)d->color_map_type);
604 d->img_type = de_getbyte(2);
605 de_dbg(c, "image type: %d", (int)d->img_type);
607 switch(d->img_type) {
608 case 1: case 9:
609 case 32: case 33:
610 d->color_type = TGA_CLRTYPE_PALETTE;
611 d->clrtype_name = "palette";
612 break;
613 case 2: case 10:
614 d->color_type = TGA_CLRTYPE_TRUECOLOR;
615 d->clrtype_name = "truecolor";
616 break;
617 case 3: case 11:
618 d->color_type = TGA_CLRTYPE_GRAYSCALE;
619 d->clrtype_name = "grayscale";
620 break;
621 default:
622 d->color_type = TGA_CLRTYPE_UNKNOWN;
623 d->clrtype_name = "unknown";
626 switch(d->img_type) {
627 case 1: case 2: case 3:
628 d->cmpr_type = TGA_CMPR_NONE;
629 d->cmpr_name = "none";
630 break;
631 case 9: case 10: case 11:
632 d->cmpr_type = TGA_CMPR_RLE;
633 d->cmpr_name = "RLE";
634 break;
635 default:
636 d->cmpr_type = TGA_CMPR_UNKNOWN;
637 d->cmpr_name = "unknown";
640 de_dbg_indent(c, 1);
641 de_dbg(c, "color type: %d (%s)", (int)d->color_type, d->clrtype_name);
642 de_dbg(c, "compression: %d (%s)", (int)d->cmpr_type, d->cmpr_name);
643 de_dbg_indent(c, -1);
645 if(d->color_map_type != 0) {
646 d->cmap_start = de_getu16le(3);
647 d->cmap_length = de_getu16le(5);
648 d->cmap_depth = (i64)de_getbyte(7);
649 de_dbg(c, "color map start: %d, len: %d, depth: %d", (int)d->cmap_start,
650 (int)d->cmap_length, (int)d->cmap_depth);
653 d->main_image.width = de_getu16le(12);
654 d->main_image.height = de_getu16le(14);
655 de_dbg_dimensions(c, d->main_image.width, d->main_image.height);
657 d->pixel_depth = (i64)de_getbyte(16);
658 de_dbg(c, "pixel depth: %d", (int)d->pixel_depth);
660 do_read_image_descriptor(c, d);
662 de_dbg_indent(c, -1);
664 if(d->has_signature) {
665 do_read_footer(c, d);
668 if(!de_good_image_dimensions(c, d->main_image.width, d->main_image.height)) goto done;
670 retval = 1;
671 done:
672 return retval;
675 // This .vst (TrueVista) decoder is based on guesswork, on the limited information
676 // in the TGA spec, and on the behavior of XnView. It may not be correct.
677 static int do_read_vst_headers(deark *c, lctx *d)
679 int retval = 0;
681 de_dbg(c, "header at %d", 0);
682 de_dbg_indent(c, 1);
684 d->id_field_len = (i64)de_getbyte(0);
686 if(d->id_field_len==0) {
687 // ??? XnView seems to do something like this.
688 d->id_field_len=18;
691 d->cmpr_type = TGA_CMPR_NONE;
692 d->cmpr_name = "none";
694 d->main_image.width = de_getu16le(12);
695 d->main_image.height = de_getu16le(14);
696 de_dbg_dimensions(c, d->main_image.width, d->main_image.height);
698 d->pixel_depth = (i64)de_getbyte(16);
699 de_dbg(c, "pixel depth: %d", (int)d->pixel_depth);
700 if(d->pixel_depth==8) {
701 d->color_map_type = 1;
702 d->color_type = TGA_CLRTYPE_PALETTE;
703 d->clrtype_name = "palette";
705 else {
706 d->color_type = TGA_CLRTYPE_TRUECOLOR;
707 d->clrtype_name = "truecolor";
710 if(d->color_type==TGA_CLRTYPE_PALETTE) {
711 d->cmap_start = 0;
712 d->cmap_length = 256;
713 d->cmap_depth = 24;
716 do_read_image_descriptor(c, d);
718 de_dbg_indent(c, -1);
720 if(!de_good_image_dimensions(c, d->main_image.width, d->main_image.height)) goto done;
722 retval = 1;
723 done:
724 return retval;
727 static int has_signature(deark *c)
729 if(c->infile->len<18+26) return 0;
730 if(!dbuf_memcmp(c->infile, c->infile->len-18, "TRUEVISION-XFILE.\0", 18)) {
731 return 1;
733 return 0;
736 // Sets d->file_format and d->has_signature
737 static void detect_file_format(deark *c, lctx *d)
739 int has_igch;
740 u8 img_type;
742 d->has_signature = has_signature(c);
743 de_dbg(c, "has v2 signature: %s", d->has_signature?"yes":"no");
744 if(d->has_signature) {
745 d->file_format = FMT_TGA;
746 return;
749 img_type = de_getbyte(2);
750 if(img_type==0) {
751 has_igch = !dbuf_memcmp(c->infile, 20, "IGCH", 4);
752 if(has_igch) {
753 d->file_format = FMT_VST;
754 return;
758 d->file_format = FMT_TGA;
761 static void de_run_tga(deark *c, de_module_params *mparams)
763 lctx *d = NULL;
764 i64 pos;
765 dbuf *unc_pixels = NULL;
766 int saved_indent_level;
767 i64 rowspan_tmp;
769 de_dbg_indent_save(c, &saved_indent_level);
770 d = de_malloc(c, sizeof(lctx));
772 detect_file_format(c, d);
774 if(d->file_format==FMT_VST) {
775 de_declare_fmt(c, "TrueVista");
777 else {
778 de_declare_fmt(c, "TGA");
781 d->thumbnail_image.is_thumb = 1;
783 pos = 0;
785 if(d->file_format==FMT_VST) {
786 if(!do_read_vst_headers(c, d)) goto done;
788 else {
789 if(!do_read_tga_headers(c, d)) goto done;
791 pos += 18;
793 if(d->id_field_len>0) {
794 de_dbg(c, "image ID at %d (len=%d)", (int)pos, (int)d->id_field_len);
795 pos += d->id_field_len;
798 if(d->color_map_type!=0) {
799 d->bytes_per_pal_entry = (d->cmap_depth+7)/8;
800 d->pal_size_in_bytes = d->cmap_length * d->bytes_per_pal_entry;
801 de_dbg(c, "color map at %d (%d colors, %d bytes)", (int)pos,
802 (int)d->cmap_length, (int)d->pal_size_in_bytes);
804 de_dbg_indent(c, 1);
805 if(!do_read_palette(c, d, pos)) goto done;
806 de_dbg_indent(c, -1);
808 pos += d->pal_size_in_bytes;
811 de_dbg(c, "bitmap at %d", (int)pos);
812 de_dbg_indent(c, 1);
814 d->bytes_per_pixel = ((d->pixel_depth+7)/8);
815 if(d->pixel_depth==1) {
816 rowspan_tmp = (d->main_image.width+7)/8;
818 else {
819 rowspan_tmp = d->main_image.width * d->bytes_per_pixel;
821 d->main_image.img_size_in_bytes = d->main_image.height * rowspan_tmp;
823 if(d->color_type!=TGA_CLRTYPE_PALETTE && d->color_type!=TGA_CLRTYPE_TRUECOLOR &&
824 d->color_type!=TGA_CLRTYPE_GRAYSCALE)
826 de_err(c, "Unsupported color type (%d: %s)", (int)d->color_type, d->clrtype_name);
827 goto done;
830 if( (d->color_type==TGA_CLRTYPE_PALETTE && d->pixel_depth==8) ||
831 (d->color_type==TGA_CLRTYPE_TRUECOLOR && d->pixel_depth==15) ||
832 (d->color_type==TGA_CLRTYPE_TRUECOLOR && d->pixel_depth==16) ||
833 (d->color_type==TGA_CLRTYPE_TRUECOLOR && d->pixel_depth==24) ||
834 (d->color_type==TGA_CLRTYPE_TRUECOLOR && d->pixel_depth==32) ||
835 (d->color_type==TGA_CLRTYPE_GRAYSCALE && d->pixel_depth==1) ||
836 (d->color_type==TGA_CLRTYPE_GRAYSCALE && d->pixel_depth==8) )
840 else {
841 de_err(c, "Unsupported TGA image type (%s, depth=%d)", d->clrtype_name,
842 (int)d->pixel_depth);
843 goto done;
846 if(d->cmpr_type==TGA_CMPR_RLE) {
847 if(d->pixel_depth<8) {
848 de_err(c, "RLE compression not supported when depth (%d) is less than 8",
849 (int)d->pixel_depth);
850 goto done;
852 unc_pixels = dbuf_create_membuf(c, d->main_image.img_size_in_bytes, 1);
853 if(!do_decode_rle(c, d, pos, unc_pixels)) goto done;
855 else if(d->cmpr_type==TGA_CMPR_NONE) {
856 unc_pixels = dbuf_open_input_subfile(c->infile, pos, d->main_image.img_size_in_bytes);
858 else {
859 de_err(c, "Unsupported compression type (%d, %s)", (int)d->cmpr_type, d->cmpr_name);
860 goto done;
863 do_decode_image(c, d, &d->main_image, unc_pixels, NULL, 0);
865 de_dbg_indent(c, -1);
867 if(d->thumbnail_offset!=0) {
868 do_decode_thumbnail(c, d);
871 done:
872 dbuf_close(unc_pixels);
873 de_dbg_indent_restore(c, saved_indent_level);
874 de_free(c, d);
877 static int de_identify_tga(deark *c)
879 u8 b[18];
880 u8 x;
881 int has_tga_ext;
883 if(has_signature(c)) {
884 return 100;
887 // TGA v1 format has no signature, but there are only a few common types of
888 // it. We'll at least try to identify anything that we support.
889 de_read(b, 0, 18);
891 if(b[1]>1) return 0; // Color map type should be 0 or 1.
893 // bits/pixel:
894 if(b[16]!=1 && b[16]!=8 && b[16]!=15 && b[16]!=16 && b[16]!=24 && b[16]!=32) return 0;
896 if(b[2]!=0 && b[2]!=1 && b[2]!=2 && b[2]!=3 &&
897 b[2]!=9 && b[2]!=10 && b[2]!=11 && b[2]!=32 && b[2]!=33)
899 return 0; // Unknown image type
902 if(b[12]==0 && b[13]==0) return 0; // Width can't be 0.
903 if(b[14]==0 && b[15]==0) return 0; // Height can't be 0.
905 // Bits per palette entry. Supposed to be 0 if there is no palette, but
906 // in practice it may be 24 instead.
907 if((b[1]==0 && b[7]==0) || b[7]==15 || b[7]==16 || b[7]==24 || b[7]==32) {
910 else {
911 return 0;
914 has_tga_ext = de_input_file_has_ext(c, "tga");
916 x = b[17]&0x0f; // Number of attribute bits
917 if(x!=0 && x!=1 && x!=8 && !has_tga_ext) return 0;
919 if(has_tga_ext) {
920 return 100;
922 if(de_input_file_has_ext(c, "vst")) {
923 return 40;
925 return 8;
928 void de_module_tga(deark *c, struct deark_module_info *mi)
930 mi->id = "tga";
931 mi->desc = "Truevision TGA, a.k.a. TARGA";
932 mi->run_fn = de_run_tga;
933 mi->identify_fn = de_identify_tga;