1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // PCX (PC Paintbrush) and DCX (multi-image PCX)
7 #include <deark-config.h>
8 #include <deark-private.h>
9 DE_DECLARE_MODULE(de_module_pcx
);
10 DE_DECLARE_MODULE(de_module_mswordscr
);
11 DE_DECLARE_MODULE(de_module_dcx
);
13 #define PCX_HDRSIZE 128
19 RESMODE_SCREENDIMENSIONS
22 typedef struct localctx_struct
{
25 enum resmode_type resmode
;
28 i64 margin_L
, margin_T
, margin_R
, margin_B
;
40 // Identifier of the palette to use, if there is no palette in the file
49 static void simplify_dens(i64
*pxdens
, i64
*pydens
, i64 factor
)
51 while(*pxdens
>factor
&& *pydens
>factor
&&
52 (*pxdens
%factor
==0) && (*pydens
%factor
==0))
59 static void set_density_from_screen_res(deark
*c
, lctx
*d
, i64 hres
, i64 vres
)
63 d
->fi
->density
.code
= DE_DENSITY_UNK_UNITS
;
64 xdens
= hres
*3; // Assume 4:3 screen
67 simplify_dens(&xdens
, &ydens
, 2);
68 simplify_dens(&xdens
, &ydens
, 3);
69 simplify_dens(&xdens
, &ydens
, 5);
71 d
->fi
->density
.xdens
= (double)xdens
;
72 d
->fi
->density
.ydens
= (double)ydens
;
75 // The resolution field is unreliable. It might contain:
78 // * The pixel dimensions of the target screen mode
79 // * The dimensions of the image itself
80 // * A corrupted attempt at one of the above things (perhaps copied from an
81 // older version of the image)
82 static void do_decode_resolution(deark
*c
, lctx
*d
, i64 hres
, i64 vres
)
84 enum resmode_type resmode
= d
->resmode
;
86 if(hres
==0 || vres
==0) return;
88 if(resmode
==RESMODE_AUTO
) {
89 if((hres
==320 && vres
==200) ||
90 (hres
==640 && vres
==480) ||
91 (hres
==640 && vres
==350) ||
92 (hres
==640 && vres
==200) ||
93 (hres
==800 && vres
==600) ||
94 (hres
==1024 && vres
==768))
96 if(d
->width
<=hres
&& d
->height
<=hres
) {
97 // Looks like screen dimensions, and image fits on the screen
98 resmode
= RESMODE_SCREENDIMENSIONS
;
101 else if(hres
==d
->width
&& vres
==d
->height
) {
105 if(hres
==vres
&& hres
>=50 && hres
<=600) {
106 resmode
= RESMODE_DPI
;
111 if(resmode
==RESMODE_DPI
) { // dpi
112 d
->fi
->density
.code
= DE_DENSITY_DPI
;
113 d
->fi
->density
.xdens
= (double)hres
;
114 d
->fi
->density
.ydens
= (double)vres
;
116 else if(resmode
==RESMODE_SCREENDIMENSIONS
) {
117 set_density_from_screen_res(c
, d
, hres
, vres
);
121 static int do_read_header(deark
*c
, lctx
*d
)
125 const char *imgtypename
= "";
127 de_dbg(c
, "header at %d", 0);
130 d
->version
= de_getbyte(1);
131 d
->encoding
= de_getbyte(2);
132 d
->bits
= (i64
)de_getbyte(3); // Bits per pixel per plane
133 d
->margin_L
= de_getu16le(4);
134 d
->margin_T
= de_getu16le(6);
135 d
->margin_R
= de_getu16le(8);
136 d
->margin_B
= de_getu16le(10);
138 hres
= de_getu16le(12);
139 vres
= de_getu16le(14);
141 // The palette (offset 16-63) will be read later.
143 // For older versions of PCX, this field might be useful to help identify
144 // the intended video mode. Documentation is lacking, though.
145 d
->reserved1
= de_getbyte(64);
147 d
->planes
= (i64
)de_getbyte(65);
148 d
->rowspan_raw
= de_getu16le(66);
149 d
->palette_info
= de_getbyte(68);
151 de_dbg(c
, "format version: %d, encoding: %d, planes: %d, bits: %d", (int)d
->version
,
152 (int)d
->encoding
, (int)d
->planes
, (int)d
->bits
);
153 de_dbg(c
, "bytes/plane/row: %d, palette info: %d, vmode: 0x%02x", (int)d
->rowspan_raw
,
154 (int)d
->palette_info
, (unsigned int)d
->reserved1
);
155 de_dbg(c
, "margins: %d, %d, %d, %d", (int)d
->margin_L
, (int)d
->margin_T
,
156 (int)d
->margin_R
, (int)d
->margin_B
);
158 de_dbg(c
, "resolution: %d"DE_CHAR_TIMES
"%d", (int)hres
, (int)vres
);
160 d
->width
= d
->margin_R
- d
->margin_L
+1;
161 d
->height
= d
->margin_B
- d
->margin_T
+1;
162 de_dbg_dimensions(c
, d
->width
, d
->height
);
163 if(!de_good_image_dimensions(c
, d
->width
, d
->height
)) goto done
;
165 d
->rowspan
= d
->rowspan_raw
* d
->planes
;
166 de_dbg(c
, "calculated bytes/row: %d", (int)d
->rowspan
);
168 d
->bits_per_pixel
= d
->bits
* d
->planes
;
170 if(d
->encoding
!=0 && d
->encoding
!=1) {
171 de_err(c
, "Unsupported compression type: %d", (int)d
->encoding
);
175 // Enumerate the known PCX image types.
176 if(d
->planes
==1 && d
->bits
==1) {
177 imgtypename
= "2-color";
180 //else if(d->planes==2 && d->bits==1) {
183 else if(d
->planes
==1 && d
->bits
==2) {
184 imgtypename
= "4-color";
187 else if(d
->planes
==3 && d
->bits
==1) {
188 imgtypename
= "8-color";
191 else if(d
->planes
==4 && d
->bits
==1) {
192 imgtypename
= "16-color";
195 //else if(d->planes==1 && d->bits==4) {
198 //else if(d->planes==4 && d->bits==2) {
199 // d->ncolors = 16; (?)
201 else if(d
->planes
==1 && d
->bits
==8) {
202 imgtypename
= "256-color";
205 //else if(d->planes==4 && d->bits==4) {
206 // d->ncolors = 4096;
208 else if(d
->planes
==3 && d
->bits
==8) {
209 imgtypename
= "truecolor";
210 d
->ncolors
= 16777216;
212 else if(d
->planes
==4 && d
->bits
==8) {
213 // I can't find a PCX spec that mentions 32-bit RGBA images, but
214 // ImageMagick and Wikipedia act like they're perfectly normal.
215 imgtypename
= "truecolor+alpha";
216 d
->ncolors
= 16777216;
217 d
->has_transparency
= 1;
220 de_err(c
, "Unsupported image type (bits=%d, planes=%d)",
221 (int)d
->bits
, (int)d
->planes
);
225 de_dbg(c
, "image type: %s", imgtypename
);
228 if(d
->rowspan
> d
->width
* 4 + 100) {
229 de_err(c
, "Bad bytes/line (%d)", (int)d
->rowspan_raw
);
233 do_decode_resolution(c
, d
, hres
, vres
);
237 de_dbg_indent(c
, -1);
241 static int do_read_vga_palette(deark
*c
, lctx
*d
)
245 if(d
->version
<5) return 0;
246 if(d
->ncolors
!=256) return 0;
247 pos
= c
->infile
->len
- 769;
248 if(pos
<PCX_HDRSIZE
) return 0;
250 if(de_getbyte(pos
) != 0x0c) {
254 de_dbg(c
, "VGA palette at %d", (int)pos
);
258 de_read_palette_rgb(c
->infile
, pos
, 256, 3, d
->pal
, 256, 0);
259 de_dbg_indent(c
, -1);
264 // Maybe read the palette from a separate file.
265 // Returns 1 if the palette was read.
266 static int do_read_alt_palette_file(deark
*c
, lctx
*d
)
269 dbuf
*palfile
= NULL
;
277 palfn
= de_get_ext_option(c
, "file2");
278 if(!palfn
) goto done
;
280 palfile
= dbuf_open_input_file(c
, palfn
);
281 if(!palfile
) goto done
;
282 de_dbg(c
, "using palette from separate file");
284 if(palfile
->len
!= d
->ncolors
*3) {
289 for(k
=0; k
<d
->ncolors
&& k
*3<palfile
->len
; k
++) {
290 dbuf_read(palfile
, b1
, 3*k
, 3);
292 if(b1
[z
]>0x3f) badflag
= 1;
293 b2
[z
] = de_scale_63_to_255(b1
[z
]);
295 d
->pal
[k
] = DE_MAKE_RGB(b2
[0],b2
[1],b2
[2]);
297 de_snprintf(tmps
, sizeof(tmps
), "(%2d,%2d,%2d) "DE_CHAR_RIGHTARROW
" ",
298 (int)b1
[0], (int)b1
[1], (int)b1
[2]);
299 de_dbg_pal_entry2(c
, k
, d
->pal
[k
], tmps
, NULL
, NULL
);
301 de_dbg_indent(c
, -1);
304 de_warn(c
, "%s doesn't look like the right kind of palette file", palfn
);
314 // 16-color palettes to use, if there is no palette in the file.
315 // (8-color version-3 PCXs apparently use only the first 8 colors of the
317 static const u32 ega16pal
[2][16] = {
318 // This palette seems to be correct for at least some files.
319 {0x000000,0x000080,0x008000,0x008080,0x800000,0x800080,0x808000,0x808080,
320 0xc0c0c0,0x0000ff,0x00ff00,0x00ffff,0xff0000,0xff00ff,0xffff00,0xffffff},
322 // This is the "default EGA palette" used by several PCX viewers.
323 // I don't know its origin.
324 {0x000000,0xbf0000,0x00bf00,0xbfbf00,0x0000bf,0xbf00bf,0x00bfbf,0xc0c0c0,
325 0x808080,0xff0000,0x00ff00,0xffff00,0x0000ff,0xff00ff,0x00ffff,0xffffff}
328 static void do_palette_stuff(deark
*c
, lctx
*d
)
336 if(d
->ncolors
==256) {
337 // For 256-color images, start with a default grayscale palette.
338 for(k
=0; k
<256; k
++) {
339 d
->pal
[k
] = DE_MAKE_GRAY((unsigned int)k
);
343 if(do_read_alt_palette_file(c
, d
)) {
348 // TODO: Allegedly, some 2-color PCXs are not simply white-on-black,
349 // and at least the foreground color can be something other than white.
350 // The color information would be stored in the palette area, but
351 // different files use different ways of conveying that information,
352 // and it seems hopeless to reliably determine the correct format.
356 if(d
->version
==3 && d
->ncolors
>=8 && d
->ncolors
<=16) {
357 if(!d
->default_pal_set
) {
358 de_info(c
, "Note: This paletted PCX file does not contain a palette. "
359 "If it is not decoded correctly, try \"-opt pcx:pal=1\".");
361 de_dbg(c
, "using a default EGA palette");
362 for(k
=0; k
<16; k
++) {
363 d
->pal
[k
] = ega16pal
[d
->default_pal_num
][k
];
368 if(d
->version
>=5 && d
->ncolors
==256) {
369 if(do_read_vga_palette(c
, d
)) {
372 de_warn(c
, "Expected VGA palette was not found");
373 // (Use the grayscale palette created earlier, as a last resort.)
379 unsigned int bgcolor
;
382 de_warn(c
, "4-color PCX images might not be supported correctly");
388 de_dbg(c
, "using a CGA palette: palette #%d, bkgd color %d", (int)fgpal
, (int)bgcolor
);
390 // Set first pal entry to background color
391 d
->pal
[0] = de_palette_pc16(bgcolor
);
393 // TODO: These palettes are quite possibly incorrect. I can't find good
394 // information about them.
396 case 0: case 2: // C=0 P=? I=0
397 d
->pal
[1]=0x00aaaa; d
->pal
[2]=0xaa0000; d
->pal
[3]=0xaaaaaa; break;
398 case 1: case 3: // C=0 P=? I=1
399 d
->pal
[1]=0x55ffff; d
->pal
[2]=0xff5555; d
->pal
[3]=0xffffff; break;
400 case 4: // C=1 P=0 I=0
401 d
->pal
[1]=0x00aa00; d
->pal
[2]=0xaa0000; d
->pal
[3]=0xaa5500; break;
402 case 5: // C=1 P=0 I=1
403 d
->pal
[1]=0x55ff55; d
->pal
[2]=0xff5555; d
->pal
[3]=0xffff55; break;
404 case 6: // C=1 P=1 I=0
405 d
->pal
[1]=0x00aaaa; d
->pal
[2]=0xaa00aa; d
->pal
[3]=0xaaaaaa; break;
406 case 7: // C=1 P=1 I=1
407 d
->pal
[1]=0x55ffff; d
->pal
[2]=0xff55ff; d
->pal
[3]=0xffffff; break;
412 if(d
->ncolors
>16 && d
->ncolors
<=256) {
413 de_warn(c
, "No suitable palette found");
416 de_dbg(c
, "using 16-color palette from header");
419 de_read_palette_rgb(c
->infile
, 16, 16, 3, d
->pal
, 256, 0);
420 de_dbg_indent(c
, -1);
423 static int do_uncompress(deark
*c
, lctx
*d
)
432 de_dbg(c
, "compressed bitmap at %d", (int)pos
);
434 expected_bytes
= d
->rowspan
* d
->height
;
435 d
->unc_pixels
= dbuf_create_membuf(c
, expected_bytes
, 0);
437 endpos
= c
->infile
->len
;
439 // The last 769 bytes of this file are reserved for the palette.
440 // Don't try to decode them as pixels.
446 break; // Reached the end of source data
448 if(d
->unc_pixels
->len
>= expected_bytes
) {
449 break; // Reached the end of the image
451 b
= de_getbyte(pos
++);
454 count
= (i64
)(b
&0x3f);
455 b2
= de_getbyte(pos
++);
456 dbuf_write_run(d
->unc_pixels
, b2
, count
);
459 dbuf_writebyte(d
->unc_pixels
, b
);
463 if(d
->unc_pixels
->len
< expected_bytes
) {
464 de_warn(c
, "Expected %d bytes of image data, only found %d",
465 (int)expected_bytes
, (int)d
->unc_pixels
->len
);
471 static void do_bitmap_1bpp(deark
*c
, lctx
*d
)
473 // The paletted algorithm would work here (if we construct a palette),
474 // but this special case is easy and efficient.
475 de_convert_and_write_image_bilevel2(d
->unc_pixels
, 0,
476 d
->width
, d
->height
, d
->rowspan_raw
, 0, d
->fi
, 0);
479 static void do_bitmap_paletted(deark
*c
, lctx
*d
)
481 de_bitmap
*img
= NULL
;
488 // bits_per_plane(_per_row) / bits_per_pixel_per_plane
489 pdwidth
= (d
->rowspan_raw
*8) / d
->bits
;
491 img
= de_bitmap_create2(c
, d
->width
, pdwidth
, d
->height
, 3);
493 for(j
=0; j
<d
->height
; j
++) {
494 for(i
=0; i
<pdwidth
; i
++) {
496 for(plane
=0; plane
<d
->planes
; plane
++) {
497 b
= de_get_bits_symbol(d
->unc_pixels
, d
->bits
,
498 j
*d
->rowspan
+ plane
*d
->rowspan_raw
, i
);
499 palent
|= b
<<(plane
*d
->bits
);
501 if(palent
>255) palent
=0; // Should be impossible.
502 de_bitmap_setpixel_rgb(img
, i
, j
, d
->pal
[palent
]);
506 de_bitmap_write_to_file_finfo(img
, d
->fi
, 0);
507 de_bitmap_destroy(img
);
510 static void do_bitmap_24bpp(deark
*c
, lctx
*d
)
512 de_bitmap
*img
= NULL
;
518 de_memset(s
, 0xff, sizeof(s
));
519 pdwidth
= (d
->rowspan_raw
*8) / d
->bits
;
520 img
= de_bitmap_create2(c
, d
->width
, pdwidth
, d
->height
, d
->has_transparency
?4:3);
522 for(j
=0; j
<d
->height
; j
++) {
523 for(i
=0; i
<pdwidth
; i
++) {
524 for(plane
=0; plane
<d
->planes
; plane
++) {
525 s
[plane
] = dbuf_getbyte(d
->unc_pixels
, j
*d
->rowspan
+ plane
*d
->rowspan_raw
+i
);
527 de_bitmap_setpixel_rgba(img
, i
, j
, DE_MAKE_RGBA(s
[0], s
[1], s
[2], s
[3]));
531 de_bitmap_write_to_file_finfo(img
, d
->fi
, 0);
532 de_bitmap_destroy(img
);
535 static void do_bitmap(deark
*c
, lctx
*d
)
537 if(d
->bits_per_pixel
==1) {
538 do_bitmap_1bpp(c
, d
);
540 else if(d
->bits_per_pixel
<=8) {
541 do_bitmap_paletted(c
, d
);
543 else if(d
->bits_per_pixel
>=24) {
544 do_bitmap_24bpp(c
, d
);
547 de_err(c
, "Unsupported bits/pixel: %d", (int)d
->bits_per_pixel
);
551 static void de_run_pcx_internal(deark
*c
, lctx
*d
, de_module_params
*mparams
)
555 s
= de_get_ext_option(c
, "pcx:pal");
557 d
->default_pal_num
= de_atoi(s
);
558 if(d
->default_pal_num
<0 || d
->default_pal_num
>1) {
559 d
->default_pal_num
= 0;
561 d
->default_pal_set
= 1;
564 d
->resmode
= RESMODE_AUTO
;
565 s
= de_get_ext_option(c
, "pcx:resmode");
567 if(!de_strcmp(s
, "auto")) {
568 d
->resmode
= RESMODE_AUTO
;
570 else if(!de_strcmp(s
, "dpi")) {
571 d
->resmode
= RESMODE_DPI
;
573 else if(!de_strcmp(s
, "screen")) {
574 d
->resmode
= RESMODE_SCREENDIMENSIONS
;
578 d
->fi
= de_finfo_create(c
);
580 if(!do_read_header(c
, d
)) {
584 do_palette_stuff(c
, d
);
587 // Uncompressed PCXs are probably not standard, but support for them is not
588 // uncommon. Imagemagick, for example, will create them if you ask it to.
589 de_dbg(c
, "uncompressed bitmap at %d", (int)PCX_HDRSIZE
);
590 d
->unc_pixels
= dbuf_open_input_subfile(c
->infile
,
591 PCX_HDRSIZE
, c
->infile
->len
-PCX_HDRSIZE
);
594 if(!do_uncompress(c
, d
)) {
602 dbuf_close(d
->unc_pixels
);
603 d
->unc_pixels
= NULL
;
604 de_finfo_destroy(c
, d
->fi
);
608 static void de_run_pcx(deark
*c
, de_module_params
*mparams
)
612 d
= de_malloc(c
, sizeof(lctx
));
613 de_run_pcx_internal(c
, d
, mparams
);
617 static int de_identify_pcx(deark
*c
)
622 if(buf
[0]==0x0a && (buf
[1]==0 || buf
[1]==2 || buf
[1]==3
623 || buf
[1]==4 || buf
[1]==5) &&
624 (buf
[2]==0 || buf
[2]==1) )
626 if(de_input_file_has_ext(c
, "pcx"))
634 static void de_help_pcx(deark
*c
)
636 de_msg(c
, "-opt pcx:pal=<0|1> : Code for the predefined palette to use, "
637 "if there is no palette in the file");
638 de_msg(c
, "-opt pcx:resmode=<ignore|dpi|screen|auto> : How to interpret the "
639 "\"resolution\" field");
640 de_msg(c
, "-file2 <file.p13> : Read the palette from a separate file");
643 void de_module_pcx(deark
*c
, struct deark_module_info
*mi
)
646 mi
->desc
= "PCX image";
647 mi
->run_fn
= de_run_pcx
;
648 mi
->identify_fn
= de_identify_pcx
;
649 mi
->help_fn
= de_help_pcx
;
652 // **************************************************************************
653 // MS Word for DOS Screen Capture
654 // **************************************************************************
656 static void de_run_mswordscr(deark
*c
, de_module_params
*mparams
)
660 d
= de_malloc(c
, sizeof(lctx
));
662 de_run_pcx_internal(c
, d
, mparams
);
666 static int de_identify_mswordscr(deark
*c
)
671 if(buf
[0]==0xcd && (buf
[1]==0 || buf
[1]==2 || buf
[1]==3
672 || buf
[1]==4 || buf
[1]==5) &&
675 if(de_input_file_has_ext(c
, "scr") || de_input_file_has_ext(c
, "mwg"))
683 void de_module_mswordscr(deark
*c
, struct deark_module_info
*mi
)
685 mi
->id
= "mswordscr";
686 mi
->desc
= "MS Word for DOS Screen Capture";
687 mi
->run_fn
= de_run_mswordscr
;
688 mi
->identify_fn
= de_identify_mswordscr
;
691 // **************************************************************************
693 // **************************************************************************
695 static void de_run_dcx(deark
*c
, de_module_params
*mparams
)
702 page_offset
= de_mallocarray(c
, 1023, sizeof(u32
));
704 while(num_pages
< 1023) {
705 page_offset
[num_pages
] = (u32
)de_getu32le(4 + 4*num_pages
);
706 if(page_offset
[num_pages
]==0)
711 de_dbg(c
, "number of pages: %d", (int)num_pages
);
713 for(page
=0; page
<num_pages
; page
++) {
714 if(page
== num_pages
-1) {
715 // Last page. Asssume it goes to the end of file.
716 page_size
= c
->infile
->len
- page_offset
[page
];
719 page_size
= page_offset
[page
+1] - page_offset
[page
];
721 if(page_size
<0) page_size
=0;
722 de_dbg(c
, "page %d at %d, size=%d", (int)page
, (int)page_offset
[page
],
725 dbuf_create_file_from_slice(c
->infile
, page_offset
[page
], page_size
, "pcx", NULL
, 0);
729 static int de_identify_dcx(deark
*c
)
731 if(!dbuf_memcmp(c
->infile
, 0, "\xb1\x68\xde\x3a", 4))
736 void de_module_dcx(deark
*c
, struct deark_module_info
*mi
)
739 mi
->desc
= "DCX (multi-image PCX)";
740 mi
->run_fn
= de_run_dcx
;
741 mi
->identify_fn
= de_identify_dcx
;