bmp: Rewrote the RLE decompressor
[deark.git] / modules / bmi.c
blob7cc448a39697d11458b9a784d177c90c4dbc281a
1 // This file is part of Deark.
2 // Copyright (C) 2018 Jason Summers
3 // See the file COPYING for terms of use.
5 // Zoner BMI bitmap image
7 // Warning: This code is not based on any written specifications, so it may be
8 // wrong or misleading. Read it at your own risk.
10 #include <deark-config.h>
11 #include <deark-private.h>
12 #include <deark-fmtutil.h>
13 DE_DECLARE_MODULE(de_module_bmi);
15 struct table_item {
16 unsigned int tag_num;
17 i64 tag_offs;
20 struct imageinfo {
21 i64 w, h;
22 unsigned int palmode;
23 i64 bpp;
24 i64 num_pal_entries;
25 u32 pal[256];
28 typedef struct localctx_struct {
29 int input_encoding;
30 i64 fixed_header_size;
31 i64 num_table_items;
32 struct table_item *table;
33 struct imageinfo globalimg;
34 } lctx;
36 static void read_palette(deark *c, lctx *d, struct imageinfo *ii, i64 pos1)
38 if(ii->num_pal_entries<1) return;
39 de_dbg(c, "palette at %"I64_FMT", %d entries", pos1, (int)ii->num_pal_entries);
40 de_dbg_indent(c, 1);
41 de_read_palette_rgb(c->infile, pos1, ii->num_pal_entries, 4,
42 ii->pal, 256, DE_GETRGBFLAG_BGR);
43 de_dbg_indent(c, -1);
46 // Read the fixed part of the header
47 static int do_header(deark *c, lctx *d, i64 pos1)
49 i64 pos = pos1;
50 int retval = 0;
52 de_dbg(c, "header at %d", (int)pos1);
53 de_dbg_indent(c, 1);
55 pos += 9; // signature
57 d->globalimg.w = de_getu16le_p(&pos);
58 d->globalimg.h = de_getu16le_p(&pos);
59 de_dbg_dimensions(c, d->globalimg.w, d->globalimg.h);
61 d->globalimg.palmode = (unsigned int)de_getu16le_p(&pos);
62 de_dbg(c, "palette mode: %u", d->globalimg.palmode);
64 d->globalimg.bpp = de_getu16le_p(&pos);
65 de_dbg(c, "bits/pixel: %d", (int)d->globalimg.bpp);
67 if(d->globalimg.palmode && d->globalimg.bpp>=1 && d->globalimg.bpp<=8) {
68 d->globalimg.num_pal_entries = de_pow2(d->globalimg.bpp);
71 pos += 2;
73 d->num_table_items = de_getu16le_p(&pos);
74 if(d->num_table_items>100) goto done;
76 d->fixed_header_size = pos - pos1;
77 retval = 1;
79 done:
80 if(!retval) {
81 de_err(c, "Error reading header");
83 de_dbg_indent(c, -1);
84 return retval;
87 static int do_read_table(deark *c, lctx *d, i64 pos1)
89 i64 pos = pos1;
90 i64 k;
92 de_dbg(c, "table at %d, %d items", (int)pos1, (int)d->num_table_items);
93 d->table = de_mallocarray(c, d->num_table_items, sizeof(struct table_item));
95 de_dbg_indent(c, 1);
97 for(k=0; k<d->num_table_items; k++) {
98 d->table[k].tag_num = (unsigned int)de_getu16le_p(&pos);
99 d->table[k].tag_offs = de_getu32le_p(&pos);
100 de_dbg(c, "item[%d]: tag=0x%x, offset=%"I64_FMT, (int)k,
101 d->table[k].tag_num, d->table[k].tag_offs);
104 de_dbg_indent(c, -1);
105 return 1;
108 static void do_bitmap(deark *c, lctx *d, i64 pos1)
110 int saved_indent_level;
111 i64 pos = pos1;
112 i64 unc_data_size_reported;
113 i64 unc_data_size_calc;
114 i64 max_uncmpr_block_size;
115 i64 rowspan;
116 de_bitmap *img = NULL;
117 dbuf *unc_pixels = NULL;
118 struct imageinfo ii;
119 const u32 *pal_to_use = d->globalimg.pal;
121 de_zeromem(&ii, sizeof(struct imageinfo));
123 de_dbg_indent_save(c, &saved_indent_level);
125 de_dbg(c, "bitmap at %"I64_FMT, pos1);
126 de_dbg_indent(c, 1);
127 ii.w = de_getu16le_p(&pos);
128 ii.h = de_getu16le_p(&pos);
129 de_dbg_dimensions(c, ii.w, ii.h);
131 ii.bpp = de_getu16le_p(&pos);
132 de_dbg(c, "bits/pixel: %d", (int)ii.bpp);
134 ii.palmode = (unsigned int)de_getu16le_p(&pos);
135 de_dbg(c, "palette mode: %u", ii.palmode);
137 if(ii.palmode) {
138 pal_to_use = ii.pal;
141 if(ii.palmode && ii.bpp>=1 && ii.bpp<=8) {
142 ii.num_pal_entries = de_pow2(ii.bpp);
145 pos += 2;
147 unc_data_size_reported = de_getu32le_p(&pos);
148 de_dbg(c, "uncmpr data size (reported): %"I64_FMT, unc_data_size_reported);
150 rowspan = de_pad_to_n(ii.w*ii.bpp, 32)/8;
151 unc_data_size_calc = rowspan * ii.h;
152 de_dbg(c, "uncmpr data size (calculated): %"I64_FMT, unc_data_size_calc);
154 if(unc_data_size_reported>DE_MAX_SANE_OBJECT_SIZE) goto done;
156 max_uncmpr_block_size = de_getu16le_p(&pos);
157 de_dbg(c, "max uncmpr block size: %d", (int)max_uncmpr_block_size);
158 if(max_uncmpr_block_size > unc_data_size_calc) {
159 max_uncmpr_block_size = unc_data_size_calc;
162 if(ii.num_pal_entries>0) {
163 read_palette(c, d, &ii, pos);
164 pos += 4*ii.num_pal_entries;
167 if(!de_good_image_dimensions(c, ii.w, ii.h)) goto done;
168 if(ii.bpp!=24 && ii.bpp!=8 && ii.bpp!=4 && ii.bpp!=1) {
169 de_err(c, "Unsupported image type");
170 goto done;
173 unc_pixels = dbuf_create_membuf(c, unc_data_size_calc, 1);
175 while(1) {
176 i64 blen;
178 if(unc_pixels->len >= unc_data_size_reported) break;
179 if(pos >= c->infile->len) goto done;
181 de_dbg(c, "block at %d", (int)pos);
182 de_dbg_indent(c, 1);
183 blen = de_getu16le_p(&pos);
184 de_dbg(c, "block len: %d", (int)blen);
185 pos++;
186 if(pos+blen > c->infile->len) goto done;
187 if(blen>max_uncmpr_block_size) goto done;
189 if(unc_pixels->len < unc_data_size_calc) {
190 i64 len_before = unc_pixels->len;
192 if(!fmtutil_decompress_deflate(c->infile, pos, blen, unc_pixels,
193 max_uncmpr_block_size, NULL,
194 DE_DEFLATEFLAG_ISZLIB|DE_DEFLATEFLAG_USEMAXUNCMPRSIZE))
196 goto done;
199 de_dbg(c, "decompressed to: %"I64_FMT" (total=%"I64_FMT")",
200 unc_pixels->len - len_before, unc_pixels->len);
202 de_dbg_indent(c, -1);
204 pos += blen;
207 img = de_bitmap_create(c, ii.w, ii.h, 3);
209 if(ii.bpp==24) {
210 de_convert_image_rgb(unc_pixels, 0, rowspan, 3, img, DE_GETRGBFLAG_BGR);
212 else {
213 de_convert_image_paletted(unc_pixels, 0, ii.bpp, rowspan, pal_to_use, img, 0);
216 de_bitmap_write_to_file(img, NULL, DE_CREATEFLAG_OPT_IMAGE);
218 done:
219 de_bitmap_destroy(img);
220 dbuf_close(unc_pixels);
221 de_dbg_indent_restore(c, saved_indent_level);
224 static void do_bitmaps(deark *c, lctx *d)
226 i64 k;
228 for(k=0; k<d->num_table_items; k++) {
229 if(d->table[k].tag_num==0x0001) {
230 do_bitmap(c, d, d->table[k].tag_offs);
235 static void do_comment(deark *c, lctx *d, i64 idx, i64 pos1)
237 de_ucstring *s = NULL;
238 i64 cmt_len;
239 i64 pos = pos1;
241 pos += 2;
242 cmt_len = de_getu32le_p(&pos);
243 pos += 2;
245 s = ucstring_create(c);
246 dbuf_read_to_ucstring_n(c->infile, pos, cmt_len, DE_DBG_MAX_STRLEN, s,
247 DE_CONVFLAG_STOP_AT_NUL, d->input_encoding);
248 de_dbg(c, "comment (item[%d]): \"%s\"", (int)idx, ucstring_getpsz_d(s));
249 ucstring_destroy(s);
252 static void do_comments(deark *c, lctx *d)
254 i64 k;
256 for(k=0; k<d->num_table_items; k++) {
257 if(d->table[k].tag_num==0x0003) {
258 do_comment(c, d, k, d->table[k].tag_offs);
263 static void de_run_bmi(deark *c, de_module_params *mparams)
265 lctx *d = NULL;
266 i64 pos = 0;
268 d = de_malloc(c, sizeof(lctx));
270 d->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_WINDOWS1252);
272 if(!do_header(c, d, pos)) goto done;
273 pos += d->fixed_header_size;
275 if(d->globalimg.num_pal_entries>0) {
276 read_palette(c, d, &d->globalimg, pos);
277 pos += 4*d->globalimg.num_pal_entries;
280 if(!do_read_table(c, d, pos)) goto done;
281 do_comments(c, d);
282 do_bitmaps(c, d);
284 done:
285 if(d) {
286 de_free(c, d->table);
287 de_free(c, d);
291 static int de_identify_bmi(deark *c)
293 if(!dbuf_memcmp(c->infile, 0, "ZonerBMIa", 9))
294 return 100;
295 return 0;
298 void de_module_bmi(deark *c, struct deark_module_info *mi)
300 mi->id = "bmi";
301 mi->desc = "Zoner BMI bitmap";
302 mi->run_fn = de_run_bmi;
303 mi->identify_fn = de_identify_bmi;