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
{
18 i64 rowspan_per_plane
;
20 i64 pixwidth
, pixheight
;
21 i64 header_size_in_words
;
22 i64 header_size_in_bytes
;
27 // Caller must initialize *repeat_count.
28 static void uncompress_line(deark
*c
, lctx
*d
, dbuf
*unc_line
,
30 i64
*bytes_consumed
, i64
*repeat_count
)
38 i64 unc_line_len_orig
;
42 unc_line_len_orig
= unc_line
->len
;
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
);
56 for(k
=0; k
<count
; k
++) {
57 dbuf_write(unc_line
, d
->pattern_buf
, d
->patlen
);
60 else { // (b1==0) scanline run
62 flagbyte
= de_getbyte(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
);
70 *repeat_count
= tmp_repeat_count
;
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
);
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
,
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);
112 if(ypos
>= d
->h
) break;
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
);
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
);
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
)
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
++) {
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
);
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
)
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
;
189 if(d
->header_size_in_words
==9 && (d
->nplanes
==3 || d
->nplanes
==4)) {
191 x
= de_getu16be(8*2);
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
);
209 de_convert_image_bilevel(unc_pixels
, 0, d
->rowspan_per_plane
, img
, DE_CVTF_WHITEISZERO
);
211 else if(is_color
&& d
->nplanes
==3) {
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
);
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
);
236 static void read_palette_ximg(deark
*c
, lctx
*d
)
238 i64 pal_entries_in_file
;
239 i64 pal_entries_to_read
;
243 int range_warned
= 0;
246 pal_entries_in_file
= (d
->header_size_in_bytes
-22)/3;
247 if(pal_entries_in_file
<1) return;
249 pal_entries_to_read
= de_pow2(d
->nplanes
);
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);
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
);
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
;
294 int saved_indent_level
;
296 de_dbg_indent_save(c
, &saved_indent_level
);
298 de_dbg(c
, "header (continued) at %d", 8*2);
301 if((d
->nplanes
>=1 && d
->nplanes
<=8) /* || d->nplanes==24 */) {
306 de_err(c
, "%d-plane XIMG images are not supported", (int)d
->nplanes
);
308 de_err(c
, "This type of %d-plane image is not supported", (int)d
->nplanes
);
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
);
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);
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
);
343 read_rgb_image(c
, d
, unc_pixels
, img
);
346 read_paletted_image(c
, d
, unc_pixels
, img
);
349 de_bitmap_write_to_file_finfo(img
, fi
, 0);
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
);
361 static void de_run_gemraster(deark
*c
, de_module_params
*mparams
)
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);
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);
404 de_err(c
, "This version of GEM Raster (%d) is not supported.", (int)ver
);
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;
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.");
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");
447 else if(d
->header_size_in_words
==25) {
455 de_dbg_indent_restore(c
, saved_indent_level
);
459 static int de_identify_gemraster(deark
*c
)
464 if(!de_input_file_has_ext(c
, "img") &&
465 !de_input_file_has_ext(c
, "ximg"))
469 ver
= de_getu16be(0);
470 if(ver
!=1 && ver
!=2 && ver
!=3) return 0;
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 &&
479 if(ver
==1 && x2
==0x08) return 70;
480 if(!dbuf_memcmp(c
->infile
, 16, "XIMG", 4)) {
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
;