fnt: Improved error handling, etc.
[deark.git] / modules / xwd.c
blob12c767000783bf8bdf6edfb1c785f6a69fd21b87
1 // This file is part of Deark.
2 // Copyright (C) 2024 Jason Summers
3 // See the file COPYING for terms of use.
5 // XWD - X-Windows screen dump
7 #include <deark-private.h>
8 DE_DECLARE_MODULE(de_module_xwd);
10 typedef struct localctx_struct_xwd {
11 u8 errflag;
12 u8 need_errmsg;
13 #define HF_HSIZE 0
14 #define HF_VER 1
15 #define HF_PIXFMT 2
16 #define HF_DEPTH 3
17 #define HF_WIDTH 4
18 #define HF_HEIGHT 5
19 #define HF_BYTE_ORDER 7
20 #define HF_BITMAP_UNIT 8
21 #define HF_BIT_ORDER 9
22 #define HF_SCANLINE_PAD 10
23 #define HF_BITS_PER_PIX 11
24 #define HF_BYTES_PER_LINE 12
25 #define HF_VCLASS 13
26 #define HF_RMASK 14
27 #define HF_GMASK 15
28 #define HF_BMASK 16
29 #define HF_BITS_PER_RGB 17
30 #define HF_CMAP_NUM_ENTRIES 18
31 #define HF_NCOLORS 19
32 UI hf[25];
34 UI vclass_adj;
35 i64 cmap_pos;
36 i64 cmap_num_entries;
37 i64 cmap_size_in_bytes;
38 i64 imgpos;
39 i64 expected_image_size;
40 i64 actual_image_size;
41 int pixel_byte_order; // 1 if LE
42 i64 bytes_per_pixel; // Not used by every image type
43 i64 bits_per_pixel; // Not used by every image type
44 i64 width, height;
45 i64 rowspan;
46 i64 nplanes;
47 UI cmpr_meth;
49 #define XWD_IMGTYPE_PAL_OR_GRAY 1
50 #define XWD_IMGTYPE_RGB 3
51 int imgtype;
53 UI sample_bit_shift[3];
54 i64 sample_maxval[3];
55 de_color pal[256];
56 } lctx;
58 static void read_or_construct_colormap(deark *c, lctx *d)
60 UI k;
61 UI num_entries_to_read;
62 int saved_indent_level;
63 i64 pos;
65 de_dbg_indent_save(c, &saved_indent_level);
66 if((d->imgtype==XWD_IMGTYPE_PAL_OR_GRAY) &&
67 (d->bits_per_pixel>=1 && d->bits_per_pixel<=8))
69 de_make_grayscale_palette(d->pal, 1ULL<<d->bits_per_pixel, 0);
72 if(d->cmap_size_in_bytes<1) goto done;
74 de_dbg(c, "colormap at %"I64_FMT, d->cmap_pos);
75 de_dbg_indent(c, 1);
77 num_entries_to_read = d->hf[HF_CMAP_NUM_ENTRIES];
78 if(num_entries_to_read>256) num_entries_to_read = 256;
80 pos = d->cmap_pos;
81 for(k=0; k<num_entries_to_read; k++) {
82 UI s;
83 u8 samp[3];
85 pos += 4;
86 for(s=0; s<3; s++) {
87 samp[s] = de_getbyte_p(&pos);
88 pos++;
90 d->pal[k] = DE_MAKE_RGB(samp[0], samp[1], samp[2]);
91 de_dbg_pal_entry(c, (i64)k, d->pal[k]);
92 pos += 2;
95 done:
96 de_dbg_indent_restore(c, saved_indent_level);
99 static const char *hnames[25] = {
100 "hsize", "ver", "pix fmt", "depth", "width",
101 "height", "xoffs", "byte order", "bitmap unit", "bitmap bit order",
102 "scanline pad", "bits/pixel", "bytes/line", "visual class", "R mask",
103 "G mask", "B mask", "bits/rgb", "cmap num entries", "ncolors",
104 "window width", "window height", "window x", "window y", "window bdrwidth" };
106 static const char *get_pixfmt_name(UI x)
108 const char *name = NULL;
110 switch(x) {
111 case 0: name = "1-bit"; break;
112 case 1: name = "1-plane"; break;
113 case 2: name = "multi-plane"; break;
115 return name?name:"?";
118 static const char *get_vclass_name(UI x)
120 const char *name = NULL;
122 switch(x) {
123 case 0: name = "grayscale"; break;
124 case 2: name = "colormapped"; break;
125 case 4: name = "truecolor"; break;
127 return name?name:"?";
130 // Always sets shift to a number 0 to 31.
131 // Always sets maxval to a positive number, not too large.
132 static void decode_bitfield(UI n, UI *pshift, i64 *pmaxval)
134 u8 ok = 0;
135 UI sh = 0;
136 i64 mv = 0;
137 UI count_of_1_bits = 0;
138 UI k;
140 for(k=0; k<=31; k++) {
141 if(n & (1U<<k)) {
142 if(count_of_1_bits==0) {
143 sh = k;
145 count_of_1_bits++;
146 mv = (mv<<1U) | 0x1;
150 if(count_of_1_bits==0 || count_of_1_bits>16) goto done;
151 ok = 1;
153 done:
154 if(ok) {
155 *pshift = sh;
156 *pmaxval = mv;
158 else {
159 *pshift = 0;
160 *pmaxval = 255;
164 static void interpret_header(deark *c, lctx *d)
166 u8 need_fixup_warning = 0;
167 u8 depth_1248;
168 UI k;
170 d->cmap_pos = (i64)d->hf[HF_HSIZE];
171 d->width = (i64)d->hf[HF_WIDTH];
172 d->height = (i64)d->hf[HF_HEIGHT];
173 d->pixel_byte_order = !d->hf[HF_BYTE_ORDER];
174 d->nplanes = 1; // tentative
175 d->rowspan = (i64)d->hf[HF_BYTES_PER_LINE];
177 depth_1248 = (d->hf[HF_DEPTH]==8 || d->hf[HF_DEPTH]==4 ||
178 d->hf[HF_DEPTH]==2 || d->hf[HF_DEPTH]==1);
180 // paletted or grayscale, planar
181 if(d->imgtype==0 &&
182 (d->vclass_adj==0 || d->vclass_adj==2) &&
183 d->hf[HF_PIXFMT]==1 &&
184 d->hf[HF_BITS_PER_PIX]==1 &&
185 (d->hf[HF_DEPTH]>=1 && d->hf[HF_DEPTH]<=8))
187 d->imgtype = XWD_IMGTYPE_PAL_OR_GRAY;
188 d->bits_per_pixel = (i64)d->hf[HF_DEPTH];
189 d->nplanes = (i64)d->hf[HF_DEPTH];
192 // paletted or grayscale, typical
193 if(d->imgtype==0 &&
194 (d->vclass_adj==0 || d->vclass_adj==2) &&
195 depth_1248 &&
196 d->hf[HF_BITS_PER_PIX]==d->hf[HF_DEPTH])
198 d->imgtype = XWD_IMGTYPE_PAL_OR_GRAY;
199 d->bits_per_pixel = (i64)d->hf[HF_BITS_PER_PIX];
202 // paletted or grayscale, with unused bits (?)
203 if(d->imgtype==0 && (d->vclass_adj==0 || d->vclass_adj==2) &&
204 depth_1248 &&
205 d->hf[HF_BITS_PER_PIX]>d->hf[HF_DEPTH] &&
206 d->hf[HF_BITS_PER_PIX]==8)
208 d->imgtype = XWD_IMGTYPE_PAL_OR_GRAY;
209 d->bits_per_pixel = (i64)d->hf[HF_BITS_PER_PIX];
212 // RGB 24 or 32 bits/pixel
213 if(d->imgtype==0 && d->vclass_adj==4 &&
214 (d->hf[HF_BITS_PER_PIX]==24 || d->hf[HF_BITS_PER_PIX]==32) &&
215 (d->hf[HF_DEPTH]==24 || d->hf[HF_DEPTH]==32))
217 d->imgtype = XWD_IMGTYPE_RGB;
218 // HF_BITS_PER_PIX is usually correct. We'll change it later if
219 // it seems wrong.
220 d->bits_per_pixel = (i64)d->hf[HF_BITS_PER_PIX];
221 d->bytes_per_pixel = d->bits_per_pixel/8;
224 // RGB 16bits/pixel
225 if(d->imgtype==0 && d->vclass_adj==4 &&
226 d->hf[HF_BITS_PER_PIX]==16 &&
227 d->hf[HF_DEPTH]==16)
229 d->imgtype = XWD_IMGTYPE_RGB;
230 d->bytes_per_pixel = 2;
233 // e.g. "MARBLES.XWD"
234 if(d->imgtype==XWD_IMGTYPE_RGB &&
235 d->bytes_per_pixel==3 &&
236 d->hf[HF_SCANLINE_PAD]==8 &&
237 d->hf[HF_WIDTH]*4 == d->hf[HF_BYTES_PER_LINE])
239 d->bytes_per_pixel = 4;
240 d->bits_per_pixel = 0;
241 d->imgtype = XWD_IMGTYPE_RGB;
242 need_fixup_warning = 1;
245 // Decode masks if needed
246 if(d->imgtype==XWD_IMGTYPE_RGB) {
247 for(k=0; k<3; k++) {
248 decode_bitfield(d->hf[HF_RMASK+k], &d->sample_bit_shift[k],
249 &d->sample_maxval[k]);
253 if(need_fixup_warning) {
254 de_warn(c, "Inconsistent or unusual image parameters. Attempting to correct.");
257 d->expected_image_size = d->height * d->rowspan * d->nplanes;
259 if(d->bits_per_pixel==0) {
260 d->bits_per_pixel = 8*d->bytes_per_pixel;
262 if(d->imgtype!=0) {
263 de_dbg(c, "interpreted bits/pixel: %d", (int)d->bits_per_pixel);
267 // Try to figure out the colormap size, and consequently the image position.
268 static void find_cmap_and_image(deark *c, lctx *d)
270 u8 need_cmap_warning = 0;
271 i64 size1, size2;
272 i64 avail_size;
274 d->cmap_num_entries = (i64)d->hf[HF_CMAP_NUM_ENTRIES];
275 d->cmap_size_in_bytes = d->cmap_num_entries * 12;
277 // It's critical that we know exactly how many entries are in the colormap.
278 // Sometimes its in one field, sometimes in another, and it's not clear
279 // how to tell which.
281 // We hope these two fields are the same.
282 if(d->hf[HF_CMAP_NUM_ENTRIES] == d->hf[HF_NCOLORS]) goto done;
284 if(d->hf[HF_NCOLORS] > d->hf[HF_CMAP_NUM_ENTRIES]) {
285 // I haven't seen this happen.
286 goto done;
289 // d->hf[HF_NCOLORS] < d->hf[HF_CMAP_NUM_ENTRIES]
291 size1 = (i64)d->hf[HF_NCOLORS] * 12; // Note, size1 is smaller than size2
292 size2 = (i64)d->hf[HF_CMAP_NUM_ENTRIES] * 12;
293 avail_size = c->infile->len - d->expected_image_size - d->cmap_pos;
295 if(size2==avail_size) {
296 goto done;
298 if(size1==avail_size) {
299 d->cmap_num_entries = (i64)d->hf[HF_NCOLORS];
300 goto done;
302 if(size2>avail_size && size1<avail_size) {
303 d->cmap_num_entries = (i64)d->hf[HF_NCOLORS];
304 need_cmap_warning = 1;
305 goto done;
308 need_cmap_warning = 1;
310 done:
311 if(need_cmap_warning) {
312 de_warn(c, "Can't reliably locate the image. Might not be decoded correctly.");
314 d->cmap_size_in_bytes = d->cmap_num_entries * 12;
315 d->imgpos = d->cmap_pos + d->cmap_size_in_bytes;
316 d->actual_image_size = c->infile->len - d->imgpos;
319 static void do_header(deark *c, lctx *d)
321 i64 pos;
322 int saved_indent_level;
323 UI k;
325 de_dbg_indent_save(c, &saved_indent_level);
326 pos = 0;
327 de_dbg(c, "header at %"I64_FMT, pos);
328 de_dbg_indent(c, 1);
330 for(k=0; k<25; k++) {
331 const char *name = NULL;
333 d->hf[k] = (UI)de_getu32be_p(&pos);
335 if(k==HF_PIXFMT) {
336 name = get_pixfmt_name(d->hf[k]);
338 else if(k==HF_BYTE_ORDER) {
339 if(d->hf[k]==0) name = "LE";
340 else name = "BE";
342 else if(k==HF_BIT_ORDER) {
343 if(d->hf[k]==0) name = "lsb first";
344 else name = "msb first";
346 else if(k==HF_VCLASS) {
347 d->vclass_adj = d->hf[k] & 0xfffffffeU;
348 name = get_vclass_name(d->vclass_adj);
351 if(k>=HF_RMASK && k<=HF_BMASK) {
352 de_dbg(c, "%s: 0x%08x", hnames[k], d->hf[k]);
354 else {
355 if(name) {
356 de_dbg(c, "%s: %u (%s)", hnames[k], d->hf[k], name);
358 else {
359 de_dbg(c, "%s: %u", hnames[k], d->hf[k]);
364 if(d->hf[HF_HSIZE]<100) {
365 d->errflag = 1;
366 d->need_errmsg = 1;
367 goto done;
370 if(d->hf[HF_VER]!=7) {
371 d->errflag = 1;
372 d->need_errmsg = 1;
373 goto done;
376 if(d->hf[HF_WIDTH]>1000000 || d->hf[HF_HEIGHT]>1000000 ||
377 d->hf[HF_BYTES_PER_LINE]>1000000)
379 d->errflag = 1;
380 d->need_errmsg = 1;
381 goto done;
384 interpret_header(c, d);
385 if(d->errflag) goto done;
386 find_cmap_and_image(c, d);
388 done:
389 de_dbg_indent_restore(c, saved_indent_level);
392 static void read_image_rgb(deark *c, lctx *d, dbuf *inf, i64 inf_pos1,
393 de_bitmap *img)
395 i64 i, j;
397 for(j=0; j<d->height; j++) {
398 i64 rowpos;
400 rowpos = inf_pos1 + j*d->rowspan;
402 for(i=0; i<d->width; i++) {
403 de_color clr = 0;
404 u32 v;
405 u32 cs[3];
406 UI k;
408 if(d->bytes_per_pixel==4) {
409 v = (u32)dbuf_getu32x(inf, rowpos+i*d->bytes_per_pixel, d->pixel_byte_order);
411 else {
412 v = (u32)dbuf_getint_ext(inf, rowpos+i*d->bytes_per_pixel,
413 (UI)d->bytes_per_pixel, d->pixel_byte_order, 0);
415 for(k=0; k<3; k++) {
416 UI v2;
418 v2 = v & d->hf[HF_RMASK+k];
419 v2 = v2 >> d->sample_bit_shift[k];
421 if(d->sample_maxval[k]==255) {
422 cs[k] = (u8)v2;
424 else {
425 cs[k] = de_scale_n_to_255(d->sample_maxval[k], v2);
428 clr = DE_MAKE_RGB(cs[0], cs[1], cs[2]);
429 de_bitmap_setpixel_rgb(img, i, j, clr);
434 static void read_image_colormapped(deark *c, lctx *d, dbuf *inf, i64 inf_pos,
435 de_bitmap *img)
437 UI flags = 0;
439 if(d->bits_per_pixel<1 || d->bits_per_pixel>8) return;
441 if(d->nplanes<=1) {
442 if(d->hf[HF_BIT_ORDER]==0) {
443 flags |= 0x01;
446 de_convert_image_paletted(inf, inf_pos, d->bits_per_pixel,
447 d->rowspan, d->pal, img, flags);
449 else {
450 if(d->hf[HF_BIT_ORDER]==0) {
451 flags |= 0x01;
454 de_convert_image_paletted_planar(inf, inf_pos, d->nplanes,
455 d->rowspan, d->rowspan*d->height, d->pal, img, flags);
459 static void decompress_pvwave_rle(deark *c, lctx *d, dbuf *unc_pixels)
461 i64 pos = d->imgpos;
462 i64 endpos = c->infile->len;
463 i64 row_padding;
464 i64 nbytes_written = 0;
465 i64 xpos = 0;
467 row_padding = d->rowspan - d->width;
468 if(row_padding<0) goto done;
470 while(1) {
471 i64 count;
472 u8 b;
474 if(pos+3 > endpos) goto done;
475 if(nbytes_written >= d->expected_image_size) goto done;
477 count = de_getu16be_p(&pos);
478 b = de_getbyte_p(&pos);
479 dbuf_write_run(unc_pixels, b, count);
480 nbytes_written += count;
481 xpos += count;
483 if(xpos >= d->width) {
484 if(row_padding!=0 && xpos==d->width) {
485 // It's stupid that we have to do this.
486 dbuf_write_run(unc_pixels, 0x00, row_padding);
487 nbytes_written += row_padding;
489 xpos = 0;
492 done:
493 de_dbg(c, "decompressed %"I64_FMT" bytes to %"I64_FMT,
494 pos-d->imgpos, nbytes_written);
495 dbuf_flush(unc_pixels);
498 static void detect_compression(deark *c, lctx *d)
500 i64 count;
502 if(d->actual_image_size == d->expected_image_size) goto done;
503 if(d->actual_image_size%3 != 0) goto done;
505 if(d->hf[HF_PIXFMT]==2 &&
506 d->hf[HF_DEPTH]==8 &&
507 d->hf[HF_BYTE_ORDER]==0 &&
508 d->hf[HF_BITMAP_UNIT]==32 &&
509 d->hf[HF_BIT_ORDER]==0 &&
510 d->hf[HF_SCANLINE_PAD]==32 &&
511 d->hf[HF_BITS_PER_PIX]==8 &&
512 d->hf[HF_VCLASS]==3 &&
513 d->hf[HF_BITS_PER_RGB]==8)
517 else {
518 goto done;
521 // TODO: We could do better by checking more than just the first
522 // compression code.
523 count = de_getu16be(d->imgpos);
524 if(count<1 || count>d->width) {
525 goto done;
528 d->cmpr_meth = 100;
529 done:
530 if(d->cmpr_meth==100) {
531 de_dbg(c, "detected PV-Wave RLE compression");
535 static void do_xwd_image(deark *c, lctx *d)
537 int bypp;
538 de_bitmap *img = NULL;
539 dbuf *unc_pixels = NULL;
540 dbuf *inf = c->infile; // This is a copy -- do not close
541 i64 inf_pos = d->imgpos;
542 int saved_indent_level;
544 de_dbg_indent_save(c, &saved_indent_level);
545 de_dbg(c, "image at %"I64_FMT, d->imgpos);
546 de_dbg_indent(c, 1);
547 if(!de_good_image_dimensions(c, d->width, d->height)) goto done;
549 detect_compression(c, d);
551 if(d->cmpr_meth!=0) {
552 unc_pixels = dbuf_create_membuf(c, d->expected_image_size, 0x1);
553 dbuf_enable_wbuffer(unc_pixels);
554 decompress_pvwave_rle(c, d, unc_pixels);
555 inf = unc_pixels;
556 inf_pos = 0;
559 if(d->cmpr_meth==0) {
560 if(d->imgpos + d->expected_image_size > c->infile->len+16) {
561 de_err(c, "Bad or truncated XWD file");
562 d->errflag = 1;
563 goto done;
567 if(d->imgtype==XWD_IMGTYPE_RGB &&
568 (d->bytes_per_pixel==2 || d->bytes_per_pixel==3 || d->bytes_per_pixel==4))
572 else if(d->imgtype==XWD_IMGTYPE_PAL_OR_GRAY &&
573 d->bits_per_pixel>0)
577 else {
578 d->errflag = 1;
579 d->need_errmsg = 1;
580 goto done;
583 bypp = 3;
584 if(d->imgtype==XWD_IMGTYPE_PAL_OR_GRAY) {
585 if(de_is_grayscale_palette(d->pal, 256)) {
586 bypp = 1;
589 img = de_bitmap_create(c, d->width, d->height, bypp);
591 if(d->imgtype==XWD_IMGTYPE_RGB) {
592 read_image_rgb(c, d, inf, inf_pos, img);
594 else if(d->imgtype==XWD_IMGTYPE_PAL_OR_GRAY) {
595 read_image_colormapped(c, d, inf, inf_pos, img);
598 if(d->errflag) goto done;
600 de_bitmap_write_to_file(img, NULL, DE_CREATEFLAG_OPT_IMAGE);
602 done:
603 if(d->need_errmsg) {
604 de_err(c, "Unsupported image type");
605 d->need_errmsg = 0;
607 de_bitmap_destroy(img);
608 dbuf_close(unc_pixels);
609 de_dbg_indent_restore(c, saved_indent_level);
612 static void de_run_xwd(deark *c, de_module_params *mparams)
614 lctx *d = NULL;
616 d = de_malloc(c, sizeof(lctx));
617 do_header(c, d);
618 if(d->errflag) goto done;
619 // TODO?: Do something with the name that may appear after the header.
621 read_or_construct_colormap(c, d);
623 do_xwd_image(c, d);
625 done:
626 if(d) {
627 if(d->need_errmsg) {
628 de_err(c, "Bad or unsupported XWD file");
630 de_free(c, d);
634 static int de_identify_xwd(deark *c)
636 int has_ext = 0;
637 i64 hdrsize;
638 i64 cmapn;
639 UI n;
641 // TODO: Identification could be improved.
643 n = (UI)de_getu32be(4); // version
644 if (n!=7) return 0;
646 hdrsize = (UI)de_getu32be(0);
647 if(hdrsize<100 || hdrsize>500) return 0;
649 n = (UI)de_getu32be(8); // pixfmt
650 if(n>2) return 0;
652 n = (UI)de_getu32be(12); // depth
653 if(n<1 || n>32) return 0;
655 cmapn = de_getu32be(76);
656 if(cmapn>512) return 0;
657 if(hdrsize + 12*cmapn > c->infile->len) return 0;
659 has_ext = de_input_file_has_ext(c, "xwd");
660 if(has_ext) return 100;
661 return 75;
664 void de_module_xwd(deark *c, struct deark_module_info *mi)
666 mi->id = "xwd";
667 mi->desc = "X-Windows screen dump";
668 mi->run_fn = de_run_xwd;
669 mi->identify_fn = de_identify_xwd;