zoo: Various improvements
[deark.git] / modules / gemras.c
blob14145bb647bebbd87b8264e491e8b14b51e959fc
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // GEM VDI Bit Image / Gem Raster
7 #include <deark-config.h>
8 #include <deark-private.h>
9 #include <deark-fmtutil.h>
10 DE_DECLARE_MODULE(de_module_gemraster);
12 typedef struct localctx_struct {
13 int is_ximg;
14 i64 npwidth, h;
15 i64 pdwidth;
16 i64 nplanes;
17 i64 patlen;
18 i64 rowspan_per_plane;
19 i64 rowspan_total;
20 i64 pixwidth, pixheight;
21 i64 header_size_in_words;
22 i64 header_size_in_bytes;
23 u8 *pattern_buf;
24 u32 pal[256];
25 } lctx;
27 // Caller must initialize *repeat_count.
28 static void uncompress_line(deark *c, lctx *d, dbuf *unc_line,
29 i64 pos1, i64 rownum,
30 i64 *bytes_consumed, i64 *repeat_count)
32 i64 pos;
33 u8 b0, b1;
34 u8 val;
35 i64 count;
36 i64 k;
37 i64 tmp_repeat_count;
38 i64 unc_line_len_orig;
40 *bytes_consumed = 0;
41 pos = pos1;
42 unc_line_len_orig = unc_line->len;
44 while(1) {
45 if(pos >= c->infile->len) break;
46 if(unc_line->len - unc_line_len_orig >= d->rowspan_per_plane) break;
48 b0 = de_getbyte(pos++);
50 if(b0==0) { // Pattern run or scanline run
51 b1 = de_getbyte(pos++);
52 if(b1>0) { // pattern run
53 de_read(d->pattern_buf, pos, d->patlen);
54 pos += d->patlen;
55 count = (i64)b1;
56 for(k=0; k<count; k++) {
57 dbuf_write(unc_line, d->pattern_buf, d->patlen);
60 else { // (b1==0) scanline run
61 u8 flagbyte;
62 flagbyte = de_getbyte(pos);
63 if(flagbyte==0xff) {
64 pos++;
65 tmp_repeat_count = (i64)de_getbyte(pos++);
66 if(tmp_repeat_count == 0) {
67 de_dbg(c, "row %d: bad repeat count", (int)rownum);
69 else {
70 *repeat_count = tmp_repeat_count;
73 else {
74 de_dbg(c, "row %d: bad scanline run marker: 0x%02x",
75 (int)rownum, (unsigned int)flagbyte);
79 else if(b0==0x80) { // "Uncompressed bit string"
80 count = (i64)de_getbyte(pos++);
81 dbuf_copy(c->infile, pos, count, unc_line);
82 pos += count;
84 else { // "solid run"
85 val = (b0&0x80) ? 0xff : 0x00;
86 count = (i64)(b0 & 0x7f);
87 dbuf_write_run(unc_line, val, count);
91 *bytes_consumed = pos - pos1;
94 static void uncompress_pixels(deark *c, lctx *d, dbuf *unc_pixels,
95 i64 pos1, i64 len)
97 i64 bytes_consumed;
98 i64 pos;
99 i64 ypos;
100 i64 repeat_count;
101 i64 k;
102 i64 plane;
103 dbuf *unc_line = NULL;
105 d->pattern_buf = de_malloc(c, d->patlen);
106 unc_line = dbuf_create_membuf(c, d->rowspan_total, 0);
108 pos = pos1;
110 ypos = 0;
111 while(1) {
112 if(ypos >= d->h) break;
114 repeat_count = 1;
116 dbuf_empty(unc_line);
117 for(plane=0; plane<d->nplanes; plane++) {
118 uncompress_line(c, d, unc_line,
119 pos, ypos, &bytes_consumed, &repeat_count);
120 pos+=bytes_consumed;
121 if(bytes_consumed<1) goto done1;
124 for(k=0; k<repeat_count; k++) {
125 if(ypos >= d->h) break;
126 dbuf_copy(unc_line, 0, d->rowspan_total, unc_pixels);
127 ypos++;
130 done1:
131 dbuf_close(unc_line);
132 de_free(c, d->pattern_buf);
133 d->pattern_buf = NULL;
136 static void set_density(deark *c, lctx *d, de_finfo *fi)
138 if(d->pixwidth>0 && d->pixheight>0) {
139 fi->density.code = DE_DENSITY_DPI;
140 fi->density.xdens = 25400.0/(double)d->pixwidth;
141 fi->density.ydens = 25400.0/(double)d->pixheight;
145 static void read_paletted_image(deark *c, lctx *d, dbuf *unc_pixels, de_bitmap *img)
147 i64 i, j, plane;
148 unsigned int n;
149 u8 x;
151 if(d->nplanes<1 || d->nplanes>8) return;
153 for(j=0; j<d->h; j++) {
154 for(i=0; i<d->pdwidth; i++) {
155 n = 0;
156 for(plane=0; plane<d->nplanes; plane++) {
157 x = de_get_bits_symbol(unc_pixels, 1, j*d->rowspan_total + plane*d->rowspan_per_plane, i);
158 if(x) n |= 1<<plane;
161 de_bitmap_setpixel_rgb(img, i, j, d->pal[n]);
166 static void read_rgb_image(deark *c, lctx *d, dbuf *unc_pixels, de_bitmap *img)
168 // Not implemented
171 // These palettes are based on Image Alchemy's interpretation of GEM raster files.
172 static const u32 pal3bit[8] = {
173 0xffffff,0x00ffff,0xff00ff,0xffff00,0x0000ff,0x00ff00,0xff0000,0x000000
176 static const u32 pal4bit[16] = {
177 0xffffff,0x00ffff,0xff00ff,0xffff00,0x0000ff,0x00ff00,0xff0000,0xc0c0c0,
178 0x808080,0x008080,0x800080,0x808000,0x000080,0x008000,0x800000,0x000000
181 static int do_gem_img(deark *c, lctx *d)
183 dbuf *unc_pixels = NULL;
184 de_bitmap *img = NULL;
185 de_finfo *fi = NULL;
186 int is_color = 0;
187 i64 k;
189 if(d->header_size_in_words==9 && (d->nplanes==3 || d->nplanes==4)) {
190 i64 x;
191 x = de_getu16be(8*2);
192 if(x==0) {
193 is_color = 1;
197 de_dbg(c, "image at %d", (int)d->header_size_in_bytes);
199 unc_pixels = dbuf_create_membuf(c, d->rowspan_total*d->h, 0);
201 uncompress_pixels(c, d, unc_pixels, d->header_size_in_bytes, c->infile->len-d->header_size_in_bytes);
203 img = de_bitmap_create2(c, d->npwidth, d->pdwidth, d->h, is_color?3:1);
205 fi = de_finfo_create(c);
206 set_density(c, d, fi);
208 if(d->nplanes==1) {
209 de_convert_image_bilevel(unc_pixels, 0, d->rowspan_per_plane, img, DE_CVTF_WHITEISZERO);
211 else if(is_color && d->nplanes==3) {
212 for(k=0; k<8; k++) {
213 d->pal[k] = pal3bit[k];
215 read_paletted_image(c, d, unc_pixels, img);
217 else if(is_color && d->nplanes==4) {
218 for(k=0; k<16; k++) {
219 d->pal[k] = pal4bit[k];
221 read_paletted_image(c, d, unc_pixels, img);
223 else {
224 de_make_grayscale_palette(d->pal, ((i64)1)<<((unsigned int)d->nplanes), 1);
225 read_paletted_image(c, d, unc_pixels, img);
228 de_bitmap_write_to_file_finfo(img, fi, 0);
230 de_bitmap_destroy(img);
231 de_finfo_destroy(c, fi);
232 dbuf_close(unc_pixels);
233 return 1;
236 static void read_palette_ximg(deark *c, lctx *d)
238 i64 pal_entries_in_file;
239 i64 pal_entries_to_read;
240 i64 i;
241 i64 cr1, cg1, cb1;
242 u8 cr, cg, cb;
243 int range_warned = 0;
244 char tmps[64];
246 pal_entries_in_file = (d->header_size_in_bytes-22)/3;
247 if(pal_entries_in_file<1) return;
248 if(d->nplanes<=8)
249 pal_entries_to_read = de_pow2(d->nplanes);
250 else
251 pal_entries_to_read = 0;
252 if(pal_entries_to_read>pal_entries_in_file)
253 pal_entries_to_read = pal_entries_in_file;
254 if(pal_entries_to_read>256)
255 pal_entries_to_read = 256;
257 if(pal_entries_in_file<1) return;
259 de_dbg(c, "palette at %d", 22);
260 de_dbg_indent(c, 1);
261 for(i=0; i<pal_entries_to_read; i++) {
262 cr1 = de_getu16be(22 + 6*i);
263 cg1 = de_getu16be(22 + 6*i + 2);
264 cb1 = de_getu16be(22 + 6*i + 4);
266 cr = de_scale_1000_to_255(cr1);
267 cg = de_scale_1000_to_255(cg1);
268 cb = de_scale_1000_to_255(cb1);
270 d->pal[i] = DE_MAKE_RGB(cr, cg, cb);
272 de_snprintf(tmps, sizeof(tmps), "(%4d,%4d,%4d) "DE_CHAR_RIGHTARROW" ",
273 (int)cr1, (int)cg1, (int)cb1);
274 de_dbg_pal_entry2(c, (int)i, d->pal[i], tmps, NULL, NULL);
276 // TODO: Maybe some out-of-range colors have special meaning?
277 if(!range_warned && (cr1>1000 || cg1>1000 || cb1>1000)) {
278 de_warn(c, "Bad palette color #%d: is (%d,%d,%d), max=(1000,1000,1000).",
279 (int)i, (int)cr1, (int)cg1, (int)cb1);
280 range_warned=1;
283 de_dbg_indent(c, -1);
286 // XIMG and similar formats.
287 // TODO: Should this function be merged with do_gem_img()?
288 static int do_gem_ximg(deark *c, lctx *d)
290 dbuf *unc_pixels = NULL;
291 de_bitmap *img = NULL;
292 de_finfo *fi = NULL;
293 int retval = 0;
294 int saved_indent_level;
296 de_dbg_indent_save(c, &saved_indent_level);
298 de_dbg(c, "header (continued) at %d", 8*2);
299 de_dbg_indent(c, 1);
301 if((d->nplanes>=1 && d->nplanes<=8) /* || d->nplanes==24 */) {
304 else {
305 if(d->is_ximg)
306 de_err(c, "%d-plane XIMG images are not supported", (int)d->nplanes);
307 else
308 de_err(c, "This type of %d-plane image is not supported", (int)d->nplanes);
309 goto done;
312 if(d->header_size_in_words==25 && !d->is_ximg) {
313 i64 pal_pos = d->header_size_in_bytes-32;
314 de_dbg(c, "palette at %d", (int)pal_pos);
315 de_dbg_indent(c, 1);
316 fmtutil_read_atari_palette(c, c->infile, pal_pos,
317 d->pal, 16, ((i64)1)<<d->nplanes, 0);
318 de_dbg_indent(c, -1);
320 else {
321 read_palette_ximg(c, d);
324 if(d->nplanes==1 && d->pal[0]==d->pal[1]) {
325 de_dbg(c, "Palette doesn't seem to be present. Using a default palette.");
326 d->pal[0] = DE_STOCKCOLOR_WHITE;
327 d->pal[1] = DE_STOCKCOLOR_BLACK;
330 de_dbg_indent(c, -1);
332 de_dbg(c, "image at %d", (int)d->header_size_in_bytes);
334 unc_pixels = dbuf_create_membuf(c, d->rowspan_total*d->h, 0);
335 uncompress_pixels(c, d, unc_pixels, d->header_size_in_bytes, c->infile->len-d->header_size_in_bytes);
337 img = de_bitmap_create2(c, d->npwidth, d->pdwidth, d->h, 3);
339 fi = de_finfo_create(c);
340 set_density(c, d, fi);
342 if(d->nplanes>8) {
343 read_rgb_image(c, d, unc_pixels, img);
345 else {
346 read_paletted_image(c, d, unc_pixels, img);
349 de_bitmap_write_to_file_finfo(img, fi, 0);
351 retval = 1;
353 done:
354 de_bitmap_destroy(img);
355 de_finfo_destroy(c, fi);
356 dbuf_close(unc_pixels);
357 de_dbg_indent_restore(c, saved_indent_level);
358 return retval;
361 static void de_run_gemraster(deark *c, de_module_params *mparams)
363 i64 ver;
364 i64 ext_word0 = 0;
365 lctx *d = NULL;
366 int need_format_warning = 0;
367 int saved_indent_level;
369 de_dbg_indent_save(c, &saved_indent_level);
370 d = de_malloc(c, sizeof(lctx));
372 de_dbg(c, "header (base part) at %d", 0);
373 de_dbg_indent(c, 1);
374 ver = de_getu16be(0);
375 de_dbg(c, "version: %d", (int)ver);
376 d->header_size_in_words = de_getu16be(2);
377 d->header_size_in_bytes = d->header_size_in_words*2;
378 de_dbg(c, "header size: %d words (%d bytes)", (int)d->header_size_in_words,
379 (int)d->header_size_in_bytes);
380 d->nplanes = de_getu16be(4);
381 de_dbg(c, "planes: %d", (int)d->nplanes);
383 if(d->header_size_in_words>=11) {
384 d->is_ximg = !dbuf_memcmp(c->infile, 16, "XIMG", 4);
387 d->patlen = de_getu16be(6);
388 de_dbg(c, "pattern def len: %d", (int)d->patlen);
389 d->pixwidth = de_getu16be(8);
390 d->pixheight = de_getu16be(10);
391 de_dbg(c, "pixel size: %d"DE_CHAR_TIMES"%d microns", (int)d->pixwidth, (int)d->pixheight);
392 d->npwidth = de_getu16be(12);
393 d->pdwidth = de_pad_to_n(d->npwidth, 8);
394 d->h = de_getu16be(14);
395 de_dbg_dimensions(c, d->npwidth, d->h);
396 de_dbg_indent(c, -1);
398 if(d->header_size_in_words>=9) {
399 // This may help to detect the image format.
400 ext_word0 = de_getu16be(16);
403 if(ver>2) {
404 de_err(c, "This version of GEM Raster (%d) is not supported.", (int)ver);
405 goto done;
408 if(d->is_ximg) {
409 de_declare_fmt(c, "GEM VDI Bit Image, XIMG extension");
411 else if(d->header_size_in_words==25 && d->patlen==2 && ext_word0==0x0080) {
412 de_declare_fmt(c, "GEM VDI Bit Image, Hyperpaint extension");
414 else if(d->header_size_in_words==8 && d->nplanes==1) {
417 else if(d->header_size_in_words==8 && (d->nplanes>=2 && d->nplanes<=8)) {
418 need_format_warning = 1;
420 else if(d->header_size_in_words==9 && (d->nplanes>=1 && d->nplanes<=8)) {
421 need_format_warning = 1;
423 else {
424 if(d->header_size_in_words==27 && ext_word0==0x5354) {
425 de_declare_fmt(c, "GEM VDI Bit Image, STTT extension");
427 de_err(c, "This version of GEM Raster is not supported.");
428 goto done;
431 if(need_format_warning) {
432 de_warn(c, "This type of GEM Raster image is not very portable, and might "
433 "not be handled correctly.");
436 if(!de_good_image_dimensions(c, d->npwidth, d->h)) goto done;
438 d->rowspan_per_plane = d->pdwidth/8;
439 d->rowspan_total = d->rowspan_per_plane * d->nplanes;
441 // If we haven't declared the format yet, do so.
442 de_declare_fmt(c, "GEM VDI Bit Image");
444 if(d->is_ximg) {
445 do_gem_ximg(c, d);
447 else if(d->header_size_in_words==25) {
448 do_gem_ximg(c, d);
450 else {
451 do_gem_img(c, d);
454 done:
455 de_dbg_indent_restore(c, saved_indent_level);
456 de_free(c, d);
459 static int de_identify_gemraster(deark *c)
461 i64 ver, x2;
462 i64 nplanes;
464 if(!de_input_file_has_ext(c, "img") &&
465 !de_input_file_has_ext(c, "ximg"))
467 return 0;
469 ver = de_getu16be(0);
470 if(ver!=1 && ver!=2 && ver!=3) return 0;
471 x2 = de_getu16be(2);
472 if(x2<0x0008 || x2>0x0800) return 0;
473 nplanes = de_getu16be(4);
474 if(!(nplanes>=1 && nplanes<=8) && nplanes!=15 && nplanes!=16 && nplanes!=24 &&
475 nplanes!=32)
477 return 0;
479 if(ver==1 && x2==0x08) return 70;
480 if(!dbuf_memcmp(c->infile, 16, "XIMG", 4)) {
481 return 100;
483 return 10;
486 static void de_help_gemraster(deark *c)
488 fmtutil_atari_help_palbits(c);
491 void de_module_gemraster(deark *c, struct deark_module_info *mi)
493 mi->id = "gemraster";
494 mi->desc = "GEM VDI Bit Image, a.k.a. GEM Raster";
495 mi->run_fn = de_run_gemraster;
496 mi->identify_fn = de_identify_gemraster;
497 mi->help_fn = de_help_gemraster;