iccprofile: Refactoring
[deark.git] / modules / insetpix.c
blob1580a100794f7ac060e9c08e32cfe5339ef33e12
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // Inset .PIX
7 #include <deark-config.h>
8 #include <deark-private.h>
9 DE_DECLARE_MODULE(de_module_insetpix);
11 typedef struct localctx_struct {
12 i64 item_count;
13 u8 hmode;
14 u8 htype;
15 u8 graphics_type; // 0=character, 1=bitmap
16 u8 board_type;
17 i64 width, height;
18 i64 gfore; // Foreground color bits
19 i64 max_sample_value;
20 i64 num_pal_bits[4]; // 0=intens, 1=red, 2=green, 3=blue
21 i64 haspect, vaspect;
23 i64 page_rows, page_cols;
24 i64 stp_rows, stp_cols;
26 i64 rowspan;
27 i64 compression_bytes_per_row;
28 int is_grayscale;
30 u8 max_pal_intensity, max_pal_sample;
32 i64 pal_entries_used;
33 u32 pal[256];
34 } lctx;
36 static int do_palette(deark *c, lctx *d, i64 pos, i64 len)
38 i64 pal_entries_in_file;
39 i64 i;
40 i64 k;
41 u8 ci1, cr1, cg1, cb1;
42 u8 ci2, cr2, cg2, cb2;
43 int retval = 0;
44 double max_color_sample;
45 double pal_sample_scalefactor[4];
47 de_dbg(c, "palette at %d", (int)pos);
48 de_dbg_indent(c, 1);
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;
61 for(k=1; k<4; k++) {
62 if(d->num_pal_bits[k]>=2)
63 pal_sample_scalefactor[k] = max_color_sample / (double)(d->num_pal_bits[k]-1);
64 else
65 pal_sample_scalefactor[k] = max_color_sample;
68 for(i=0; i<pal_entries_in_file; i++) {
69 char tmps[64];
71 if(i>255) break;
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);
77 if(d->is_grayscale) {
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);
87 else {
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);
91 if(ci1) {
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.
95 cr2 += 85;
96 cg2 += 85;
97 cb2 += 85;
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]");
108 retval = 1;
110 de_dbg_indent(c, -1);
111 return retval;
114 static int do_image_info(deark *c, lctx *d, i64 pos, i64 len)
116 int retval = 0;
118 de_dbg(c, "image information at %d", (int)pos);
119 de_dbg_indent(c, 1);
120 if(len<32) {
121 de_err(c, "Image Information item too small");
122 goto done;
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);
156 retval = 1;
157 done:
158 de_dbg_indent(c, -1);
159 return retval;
162 static int do_tileinfo(deark *c, lctx *d, i64 pos, i64 len)
164 int retval = 0;
166 de_dbg(c, "tile information at %d", (int)pos);
167 de_dbg_indent(c, 1);
168 if(len<8) {
169 de_err(c, "Tile Information item too small");
170 goto done;
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);
183 goto done;
186 retval = 1;
187 done:
188 de_dbg_indent(c, -1);
189 return retval;
192 static u8 getbit(const u8 *m, i64 bitnum)
194 u8 b;
195 b = m[bitnum/8];
196 b = (b>>(7-bitnum%8)) & 0x1;
197 return b;
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)
204 u8 *rowbuf1 = NULL;
205 u8 *rowbuf2 = NULL;
206 u8 *compression_bytes = NULL;
207 i64 pos;
208 i64 i, j;
209 i64 plane;
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);
219 pos = tile_loc;
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);
224 goto done;
227 for(j=0; j<num_rows; j++) {
228 if(j==0) {
229 // First row is stored uncompressed
230 de_read(rowbuf1, pos, d->rowspan);
231 pos += d->rowspan;
232 de_memcpy(rowbuf2, rowbuf1, (size_t)d->rowspan);
234 else {
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++);
244 else {
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);
259 done:
260 de_free(c, compression_bytes);
261 de_free(c, rowbuf1);
262 de_free(c, rowbuf2);
265 static void do_render_tile(deark *c, lctx *d, de_bitmap *img,
266 i64 tile_num, i64 tile_loc, i64 tile_len)
268 i64 i, j;
269 i64 plane;
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;
273 u32 clr;
274 unsigned int palent;
275 u8 b;
276 dbuf *unc_pixels = NULL;
277 i64 nrows_expected;
278 i64 planespan;
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;
310 palent = 0;
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];
317 else clr=0;
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)
328 i64 pos;
329 i64 item;
330 i64 item_id;
331 i64 tile_loc, tile_len;
332 i64 tile_num;
333 de_bitmap *img = NULL;
335 de_dbg(c, "reading image data");
336 de_dbg_indent(c, 1);
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++) {
347 pos = 4 + 8*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);
365 done:
366 de_bitmap_destroy(img);
367 de_dbg_indent(c, -1);
370 static void de_run_insetpix(deark *c, de_module_params *mparams)
372 lctx *d = NULL;
373 i64 pix_version;
374 i64 item;
375 i64 item_id;
376 i64 item_loc, item_len;
377 i64 pos;
378 i64 imginfo_pos=0, imginfo_len=0;
379 i64 pal_pos=0, pal_len=0;
380 i64 tileinfo_pos=0, tileinfo_len=0;
381 int indent_flag = 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.)
394 de_dbg_indent(c, 1);
395 indent_flag = 1;
396 for(item=0; item<d->item_count; item++) {
397 pos = 4 + 8*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);
411 goto done;
414 switch(item_id) {
415 case 0:
416 imginfo_pos = item_loc;
417 imginfo_len = item_len;
418 break;
419 case 1:
420 if(!pal_pos) {
421 pal_pos = item_loc;
422 pal_len = item_len;
424 break;
425 case 2:
426 tileinfo_pos = item_loc;
427 tileinfo_len = item_len;
428 break;
429 case 17: // Printing Options
430 case 0xffff: // Empty item
431 break;
432 default:
433 de_dbg(c, "unknown item type %d", (int)item_id);
436 de_dbg_indent(c, -1);
437 indent_flag = 0;
439 if(!imginfo_pos) {
440 de_err(c, "Missing Image Information item");
441 goto done;
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");
447 goto done;
450 if(!pal_pos) {
451 de_err(c, "Missing palette");
452 goto done;
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);
459 goto done;
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)
465 d->is_grayscale = 1;
468 if(!tileinfo_pos) {
469 de_err(c, "Missing Tile Information item");
470 goto done;
473 if(!do_tileinfo(c, d, tileinfo_pos, tileinfo_len)) goto done;
475 do_bitmap(c, d);
477 done:
478 if(indent_flag) de_dbg_indent(c, -1);
480 de_free(c, d);
483 // Inset PIX is hard to identify.
484 static int de_identify_insetpix(deark *c)
486 i64 pix_version;
487 i64 item_count;
488 i64 item;
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;
511 return 20;
514 void de_module_insetpix(deark *c, struct deark_module_info *mi)
516 mi->id = "insetpix";
517 mi->desc = "Inset .PIX image";
518 mi->run_fn = de_run_insetpix;
519 mi->identify_fn = de_identify_insetpix;