1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
7 #include <deark-config.h>
8 #include <deark-private.h>
9 DE_DECLARE_MODULE(de_module_insetpix
);
11 typedef struct localctx_struct
{
15 u8 graphics_type
; // 0=character, 1=bitmap
18 i64 gfore
; // Foreground color bits
20 i64 num_pal_bits
[4]; // 0=intens, 1=red, 2=green, 3=blue
23 i64 page_rows
, page_cols
;
24 i64 stp_rows
, stp_cols
;
27 i64 compression_bytes_per_row
;
30 u8 max_pal_intensity
, max_pal_sample
;
36 static int do_palette(deark
*c
, lctx
*d
, i64 pos
, i64 len
)
38 i64 pal_entries_in_file
;
41 u8 ci1
, cr1
, cg1
, cb1
;
42 u8 ci2
, cr2
, cg2
, cb2
;
44 double max_color_sample
;
45 double pal_sample_scalefactor
[4];
47 de_dbg(c
, "palette at %d", (int)pos
);
50 pal_entries_in_file
= len
/4;
51 de_dbg(c
, "number of palette colors: %d", (int)pal_entries_in_file
);
53 d
->pal_entries_used
= d
->max_sample_value
+1;
54 if(d
->pal_entries_used
> pal_entries_in_file
) d
->pal_entries_used
= pal_entries_in_file
;
56 // If intensity bits are used, make the initial colors darker, so that the
57 // intensity bits can lighten them.
58 if(d
->num_pal_bits
[0]==0) max_color_sample
=255.0;
59 else max_color_sample
=170.0;
62 if(d
->num_pal_bits
[k
]>=2)
63 pal_sample_scalefactor
[k
] = max_color_sample
/ (double)(d
->num_pal_bits
[k
]-1);
65 pal_sample_scalefactor
[k
] = max_color_sample
;
68 for(i
=0; i
<pal_entries_in_file
; i
++) {
72 ci1
= de_getbyte(pos
+4*i
);
73 cr1
= de_getbyte(pos
+4*i
+1);
74 cg1
= de_getbyte(pos
+4*i
+2);
75 cb1
= de_getbyte(pos
+4*i
+3);
78 // This is untested. I can't find any grayscale PIX images.
79 // The spec says you can make a bilevel image with "palette intensity
80 // bits" set to 1, which makes it clear that that field really is a
81 // number of bits, not a number of sample values.
82 // But color images evidently use the "number of bits" fields to store
83 // the number of sample values.
84 ci2
= de_sample_nbit_to_8bit(d
->num_pal_bits
[0], ci1
);
85 d
->pal
[i
] = DE_MAKE_GRAY(ci2
);
88 cr2
= (u8
)(0.5+ pal_sample_scalefactor
[1] * (double)cr1
);
89 cg2
= (u8
)(0.5+ pal_sample_scalefactor
[2] * (double)cg1
);
90 cb2
= (u8
)(0.5+ pal_sample_scalefactor
[3] * (double)cb1
);
92 // This is just a guess. The spec doesn't say what intensity bits do.
93 // This is pretty much what old PC graphics cards do when the
94 // intensity bit is set.
99 d
->pal
[i
] = DE_MAKE_RGB(cr2
,cg2
,cb2
);
102 de_snprintf(tmps
, sizeof(tmps
), "(%d,%d,%d,intens=%d) "DE_CHAR_RIGHTARROW
" ",
103 (int)cr1
, (int)cg1
, (int)cb1
, (int)ci1
);
104 de_dbg_pal_entry2(c
, i
, d
->pal
[i
], tmps
, NULL
,
105 i
<d
->pal_entries_used
? "":" [unused]");
110 de_dbg_indent(c
, -1);
114 static int do_image_info(deark
*c
, lctx
*d
, i64 pos
, i64 len
)
118 de_dbg(c
, "image information at %d", (int)pos
);
121 de_err(c
, "Image Information item too small");
125 d
->hmode
= de_getbyte(pos
);
126 de_dbg(c
, "hardware mode: %d", (int)d
->hmode
);
128 d
->htype
= de_getbyte(pos
+1);
129 d
->graphics_type
= d
->htype
& 0x01;
130 d
->board_type
= d
->htype
& 0xfe;
132 de_dbg(c
, "graphics type: %d (%s)", (int)d
->graphics_type
,
133 d
->graphics_type
?"bitmap":"character");
134 de_dbg(c
, "board type: %d", (int)d
->board_type
);
136 d
->width
= de_getu16le(pos
+18);
137 d
->height
= de_getu16le(pos
+20);
138 de_dbg_dimensions(c
, d
->width
, d
->height
);
140 d
->gfore
= (i64
)de_getbyte(pos
+22);
141 de_dbg(c
, "foreground color bits: %d", (int)d
->gfore
);
142 d
->max_sample_value
= de_pow2(d
->gfore
) -1;
144 d
->num_pal_bits
[0] = (i64
)de_getbyte(pos
+25);
145 d
->num_pal_bits
[1] = (i64
)de_getbyte(pos
+26);
146 d
->num_pal_bits
[2] = (i64
)de_getbyte(pos
+27);
147 d
->num_pal_bits
[3] = (i64
)de_getbyte(pos
+28);
148 de_dbg(c
, "\"number of palette bits\" (IRGB): %d,%d,%d,%d",
149 (int)d
->num_pal_bits
[0], (int)d
->num_pal_bits
[1],
150 (int)d
->num_pal_bits
[2], (int)d
->num_pal_bits
[3] );
152 d
->haspect
= de_getbyte(pos
+30);
153 d
->vaspect
= de_getbyte(pos
+31);
154 de_dbg(c
, "aspect ratio: %d"DE_CHAR_TIMES
"%d", (int)d
->haspect
, (int)d
->vaspect
);
158 de_dbg_indent(c
, -1);
162 static int do_tileinfo(deark
*c
, lctx
*d
, i64 pos
, i64 len
)
166 de_dbg(c
, "tile information at %d", (int)pos
);
169 de_err(c
, "Tile Information item too small");
173 d
->page_rows
= de_getu16le(pos
+0);
174 d
->page_cols
= de_getu16le(pos
+2);
175 d
->stp_rows
= de_getu16le(pos
+4);
176 d
->stp_cols
= de_getu16le(pos
+6);
178 de_dbg(c
, "page_rows=%d, page_cols=%d", (int)d
->page_rows
, (int)d
->page_cols
);
179 de_dbg(c
, "strip_rows=%d, strip_cols=%d", (int)d
->stp_rows
, (int)d
->stp_cols
);
181 if(d
->page_cols
%8 != 0) {
182 de_err(c
, "page_cols must be a multiple of 8 (is %d)", (int)d
->page_cols
);
188 de_dbg_indent(c
, -1);
192 static u8
getbit(const u8
*m
, i64 bitnum
)
196 b
= (b
>>(7-bitnum
%8)) & 0x1;
200 static void do_uncompress_tile(deark
*c
, lctx
*d
, i64 tile_num
,
201 i64 tile_loc
, i64 tile_len
,
202 dbuf
*unc_pixels
, i64 num_rows
)
206 u8
*compression_bytes
= NULL
;
211 // There are d->gfore planes (1-bpp images). The first row of each plane is
212 // uncompressed. The rest are compressed with a delta compression algorithm.
213 // There are d->page_rows rows in each plane.
215 rowbuf1
= de_malloc(c
, d
->rowspan
);
216 rowbuf2
= de_malloc(c
, d
->rowspan
);
217 compression_bytes
= de_malloc(c
, d
->compression_bytes_per_row
);
221 for(plane
=0; plane
<d
->gfore
; plane
++) {
222 if(pos
>= tile_loc
+ tile_len
) {
223 de_warn(c
, "Not enough data in tile %d", (int)tile_num
);
227 for(j
=0; j
<num_rows
; j
++) {
229 // First row is stored uncompressed
230 de_read(rowbuf1
, pos
, d
->rowspan
);
232 de_memcpy(rowbuf2
, rowbuf1
, (size_t)d
->rowspan
);
235 de_read(compression_bytes
, pos
, d
->compression_bytes_per_row
);
236 pos
+= d
->compression_bytes_per_row
;
238 // For every 1 bit in the compression_bytes array, read a byte from the file.
239 // For every 0 bit, copy the byte from the previous row.
240 for(i
=0; i
<d
->rowspan
; i
++) {
241 if(getbit(compression_bytes
, i
)) {
242 rowbuf2
[i
] = de_getbyte(pos
++);
245 rowbuf2
[i
] = rowbuf1
[i
];
250 // TODO: Maybe instead of having separate rowbufs, we should read back what
251 // we wrote to unc_pixels.
252 dbuf_write(unc_pixels
, rowbuf2
, d
->rowspan
);
254 // Remember the previous row
255 de_memcpy(rowbuf1
, rowbuf2
, (size_t)d
->rowspan
);
260 de_free(c
, compression_bytes
);
265 static void do_render_tile(deark
*c
, lctx
*d
, de_bitmap
*img
,
266 i64 tile_num
, i64 tile_loc
, i64 tile_len
)
270 i64 x_pos_in_tiles
, y_pos_in_tiles
;
271 i64 x_origin_in_pixels
, y_origin_in_pixels
;
272 i64 x_pos_in_pixels
, y_pos_in_pixels
;
276 dbuf
*unc_pixels
= NULL
;
280 x_pos_in_tiles
= tile_num
% d
->stp_cols
;
281 y_pos_in_tiles
= tile_num
/ d
->stp_cols
;
283 x_origin_in_pixels
= x_pos_in_tiles
* d
->page_cols
;
284 y_origin_in_pixels
= y_pos_in_tiles
* d
->page_rows
;
286 // "If the actual row bound of the tile exceeds the image, the extra
287 // rows are not present."
288 nrows_expected
= d
->height
- y_origin_in_pixels
;
289 if(nrows_expected
> d
->page_rows
) nrows_expected
= d
->page_rows
;
290 planespan
= nrows_expected
* d
->rowspan
;
292 de_dbg(c
, "tile (%d,%d), pixel position (%d,%d), size %d"DE_CHAR_TIMES
"%d",
293 (int)x_pos_in_tiles
, (int)y_pos_in_tiles
,
294 (int)x_origin_in_pixels
, (int)y_origin_in_pixels
,
295 (int)d
->page_cols
, (int)nrows_expected
);
297 unc_pixels
= dbuf_create_membuf(c
, 4096, 0);
299 do_uncompress_tile(c
, d
, tile_num
, tile_loc
, tile_len
, unc_pixels
, nrows_expected
);
301 // Paint the tile into the bitmap.
302 for(j
=0; j
<d
->page_rows
; j
++) {
303 y_pos_in_pixels
= y_origin_in_pixels
+j
;
304 if(y_pos_in_pixels
>= d
->height
) break;
306 for(i
=0; i
<d
->page_cols
; i
++) {
307 x_pos_in_pixels
= x_origin_in_pixels
+i
;
308 if(x_pos_in_pixels
>= d
->width
) break;
311 for(plane
=0; plane
<d
->gfore
; plane
++) {
312 b
= de_get_bits_symbol(unc_pixels
, 1, plane
*planespan
+ j
*d
->rowspan
, i
);
313 if(b
) palent
|= (1<<plane
);
316 if(palent
<=255) clr
= d
->pal
[palent
];
319 de_bitmap_setpixel_rgb(img
, x_pos_in_pixels
, y_pos_in_pixels
, clr
);
323 dbuf_close(unc_pixels
);
326 static void do_bitmap(deark
*c
, lctx
*d
)
331 i64 tile_loc
, tile_len
;
333 de_bitmap
*img
= NULL
;
335 de_dbg(c
, "reading image data");
338 if(!de_good_image_dimensions(c
, d
->width
, d
->height
)) goto done
;
340 d
->rowspan
= d
->page_cols
/8;
341 d
->compression_bytes_per_row
= (d
->rowspan
+7)/8; // Just a guess. Spec doesn't say.
343 img
= de_bitmap_create(c
, d
->width
, d
->height
, d
->is_grayscale
?1:3);
345 // Read through the items again, this time looking only at the image tiles.
346 for(item
=0; item
<d
->item_count
; item
++) {
348 if(pos
+8 > c
->infile
->len
) break;
350 item_id
= de_getu16le(pos
);
351 if(item_id
<0x8000 || item_id
==0xffff) continue;
353 tile_len
= de_getu16le(pos
+2);
354 tile_loc
= de_getu32le(pos
+4);
356 tile_num
= item_id
-0x8000;
357 de_dbg(c
, "item #%d: tile #%d: loc=%d, len=%d", (int)item
, (int)tile_num
,
358 (int)tile_loc
, (int)tile_len
);
360 do_render_tile(c
, d
, img
, tile_num
, tile_loc
, tile_len
);
363 de_bitmap_write_to_file(img
, NULL
, 0);
366 de_bitmap_destroy(img
);
367 de_dbg_indent(c
, -1);
370 static void de_run_insetpix(deark
*c
, de_module_params
*mparams
)
376 i64 item_loc
, item_len
;
378 i64 imginfo_pos
=0, imginfo_len
=0;
379 i64 pal_pos
=0, pal_len
=0;
380 i64 tileinfo_pos
=0, tileinfo_len
=0;
383 d
= de_malloc(c
, sizeof(lctx
));
385 de_warn(c
, "The Inset PIX module is experimental, and may not work correctly.");
387 pix_version
= de_getu16le(0);
388 d
->item_count
= de_getu16le(2);
389 de_dbg(c
, "version: %d", (int)pix_version
);
390 de_dbg(c
, "index at 4, %d items", (int)d
->item_count
);
392 // Scan the index, and record the location of items we care about.
393 // (The index will be read again when converting the image bitmap.)
396 for(item
=0; item
<d
->item_count
; item
++) {
398 if(pos
+8 > c
->infile
->len
) break;
400 item_id
= de_getu16le(pos
);
401 if(item_id
>=0x8000) continue; // Skip "tile" items for now
403 item_len
= de_getu16le(pos
+2);
404 item_loc
= de_getu32le(pos
+4);
405 de_dbg(c
, "item #%d: id=%d, loc=%d, len=%d", (int)item
,
406 (int)item_id
, (int)item_loc
, (int)item_len
);
408 if(item_loc
+ item_len
> c
->infile
->len
) {
409 de_err(c
, "Item #%d (ID %d) goes beyond end of file",
410 (int)item
, (int)item_id
);
416 imginfo_pos
= item_loc
;
417 imginfo_len
= item_len
;
426 tileinfo_pos
= item_loc
;
427 tileinfo_len
= item_len
;
429 case 17: // Printing Options
430 case 0xffff: // Empty item
433 de_dbg(c
, "unknown item type %d", (int)item_id
);
436 de_dbg_indent(c
, -1);
440 de_err(c
, "Missing Image Information item");
443 if(!do_image_info(c
, d
, imginfo_pos
, imginfo_len
)) goto done
;
445 if(d
->graphics_type
==0) {
446 de_err(c
, "Inset PIX character graphics not supported");
451 de_err(c
, "Missing palette");
455 if(!do_palette(c
, d
, pal_pos
, pal_len
)) goto done
;
457 if(d
->gfore
<1 || d
->gfore
>8) {
458 de_err(c
, "Inset PIX with %d bits/pixel are not supported", (int)d
->gfore
);
462 if(d
->num_pal_bits
[0]!=0 && d
->num_pal_bits
[1]==0 &&
463 d
->num_pal_bits
[2]==0 && d
->num_pal_bits
[3]==0)
469 de_err(c
, "Missing Tile Information item");
473 if(!do_tileinfo(c
, d
, tileinfo_pos
, tileinfo_len
)) goto done
;
478 if(indent_flag
) de_dbg_indent(c
, -1);
483 // Inset PIX is hard to identify.
484 static int de_identify_insetpix(deark
*c
)
489 i64 item_loc
, item_len
;
491 if(!de_input_file_has_ext(c
, "pix")) return 0;
493 pix_version
= de_getu16le(0);
494 // The only version number I know of is 3, but I don't know what other
495 // versions may exist.
496 if(pix_version
<1 || pix_version
>4) return 0;
498 item_count
= de_getu16le(2);
499 // Need at least 4 items (image info, palette info, tile info, and 1 tile).
500 if(item_count
<4) return 0;
502 if(4 + 8*item_count
>= c
->infile
->len
) return 0;
504 for(item
=0; item
<item_count
&& item
<16; item
++) {
505 item_len
= de_getu16le(4+8*item
+2);
506 item_loc
= de_getu32le(4+8*item
+4);
507 if(item_loc
< 4 + 8*item_count
) return 0;
508 if(item_loc
+item_len
> c
->infile
->len
) return 0;
514 void de_module_insetpix(deark
*c
, struct deark_module_info
*mi
)
517 mi
->desc
= "Inset .PIX image";
518 mi
->run_fn
= de_run_insetpix
;
519 mi
->identify_fn
= de_identify_insetpix
;