bmp: Rewrote the RLE decompressor
[deark.git] / modules / pkm.c
blob79851a6c1c3d804898f049daef4e293642bf5c6f
1 // This file is part of Deark.
2 // Copyright (C) 2021 Jason Summers
3 // See the file COPYING for terms of use.
5 // PKM (GrafX2)
7 #include <deark-private.h>
8 DE_DECLARE_MODULE(de_module_pkm);
10 struct pkm_ctx {
11 de_encoding input_encoding;
12 u8 respect_trns;
13 u8 pack_byte;
14 u8 pack_word;
15 i64 w, h;
16 i64 ph_size;
17 i64 unc_image_size;
18 i64 orig_w, orig_h;
19 u8 has_back_clr;
20 u8 back_clr;
21 dbuf *unc_pixels;
22 de_color pal[256];
25 static void pkm_decompress_image(deark *c, struct pkm_ctx *d, i64 pos1)
27 i64 pos = pos1;
28 i64 nbytes_dcmpr = 0;
30 de_dbg(c, "compressed image at %"I64_FMT, pos1);
31 de_dbg_indent(c, 1);
33 while(1) {
34 u8 b;
35 u8 b2;
36 i64 count;
38 if(nbytes_dcmpr >= d->unc_image_size) goto done; // Sufficient output
39 if(pos >= c->infile->len) goto done; // No more input
41 b = de_getbyte_p(&pos);
42 if(b==d->pack_byte) {
43 b2 = de_getbyte_p(&pos);
44 count = (i64)de_getbyte_p(&pos);
45 dbuf_write_run(d->unc_pixels, b2, count);
46 nbytes_dcmpr += count;
48 else if(b==d->pack_word) {
49 b2 = de_getbyte_p(&pos);
50 count = de_getu16be_p(&pos);
51 dbuf_write_run(d->unc_pixels, b2, count);
52 nbytes_dcmpr += count;
54 else {
55 dbuf_writebyte(d->unc_pixels, b);
56 nbytes_dcmpr++;
60 done:
61 de_dbg(c, "decompressed %"I64_FMT" bytes to %"I64_FMT, pos-pos1, nbytes_dcmpr);
62 de_dbg_indent(c, -1);
65 static void pkm_read_comment(deark *c, struct pkm_ctx *d, i64 pos, i64 len)
67 de_ucstring *s = NULL;
68 s = ucstring_create(c);
69 dbuf_read_to_ucstring(c->infile, pos, len, s, 0, d->input_encoding);
70 de_dbg(c, "comment: \"%s\"", ucstring_getpsz_d(s));
71 ucstring_destroy(s);
74 static void pkm_read_orig_screen_size(deark *c, struct pkm_ctx *d, i64 pos, i64 len)
76 // TODO: Maybe this could be used to set the density, but I suspect it's
77 // not reliable.
78 if(len!=4) return;
79 d->orig_w = de_getu16le(pos);
80 d->orig_h = de_getu16le(pos+2);
81 de_dbg(c, "original dimensions: %u"DE_CHAR_TIMES"%u", (UI)d->orig_w, (UI)d->orig_h);
84 static void pkm_read_back_color(deark *c, struct pkm_ctx *d, i64 pos, i64 len)
86 if(len!=1) return;
87 d->back_clr = de_getbyte(pos);
88 d->has_back_clr = 1;
89 de_dbg(c, "back color: 0x%02x", (UI)d->back_clr);
92 static void pkm_read_postheader(deark *c, struct pkm_ctx *d, i64 pos1)
94 i64 pos = pos1;
95 i64 endpos = pos1 + d->ph_size;
96 int saved_indent_level;
98 de_dbg(c, "post-header at %"I64_FMT, pos1);
100 de_dbg_indent_save(c, &saved_indent_level);
101 de_dbg_indent(c, 1);
103 while(1) {
104 u8 id;
105 i64 field_pos;
106 i64 field_size;
108 field_pos = pos;
109 if(field_pos+2 > endpos) goto done;
110 id = de_getbyte_p(&pos);
111 field_size = (i64)de_getbyte_p(&pos);
112 de_dbg(c, "field at %"I64_FMT", type=%u, dlen=%u", field_pos, (UI)id,
113 (UI)field_size);
114 de_dbg_indent(c, 1);
115 switch(id) {
116 case 0:
117 pkm_read_comment(c, d, pos, field_size);
118 break;
119 case 1:
120 pkm_read_orig_screen_size(c, d, pos, field_size);
121 break;
122 case 2:
123 pkm_read_back_color(c, d, pos, field_size);
124 break;
125 default:
126 de_dbg_hexdump(c, c->infile, pos, field_size, 256, NULL, 0x1);
128 de_dbg_indent(c, -1);
129 pos += field_size;
132 done:
133 de_dbg_indent_restore(c, saved_indent_level);
136 static void de_run_pkm(deark *c, de_module_params *mparams)
138 struct pkm_ctx *d = NULL;
139 de_bitmap *img = NULL;
140 i64 pos = 0;
141 int bypp;
142 int saved_indent_level;
144 de_dbg_indent_save(c, &saved_indent_level);
145 d = de_malloc(c, sizeof(struct pkm_ctx));
146 d->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_CP437);
147 d->respect_trns = (u8)de_get_ext_option_bool(c, "pkm:trans", 0);
149 de_dbg(c, "header at %"I64_FMT, pos);
150 de_dbg_indent(c, 1);
151 pos += 3; // signature
152 pos += 1; // version
153 d->pack_byte = de_getbyte_p(&pos);
154 d->pack_word = de_getbyte_p(&pos);
156 d->w = de_getu16le_p(&pos);
157 d->h = de_getu16le_p(&pos);
158 de_dbg_dimensions(c, d->w, d->h);
159 if(!de_good_image_dimensions(c, d->w, d->h)) goto done;
161 de_read_simple_palette(c, c->infile, pos, 256, 3, d->pal, 256, DE_RDPALTYPE_VGA18BIT, 0);
162 pos += 3*256;
164 d->ph_size = de_getu16le_p(&pos);
165 de_dbg(c, "post-header size: %u", (UI)d->ph_size);
166 de_dbg_indent(c, -1);
168 pkm_read_postheader(c, d, pos);
169 pos += d->ph_size;
171 d->unc_image_size = d->w * d->h;
172 d->unc_pixels = dbuf_create_membuf(c, d->unc_image_size, 0x1);
173 dbuf_enable_wbuffer(d->unc_pixels);
174 pkm_decompress_image(c, d, pos);
175 dbuf_flush(d->unc_pixels);
177 if(d->respect_trns && d->has_back_clr) {
178 bypp = 4;
179 // TODO: Understand the "background/transparent" color field better.
180 // Making it transparent messes up some images.
181 d->pal[(UI)d->back_clr] = DE_SET_ALPHA(d->pal[(UI)d->back_clr], 0);
183 else {
184 bypp = 3;
186 img = de_bitmap_create(c, d->w, d->h, bypp);
187 de_convert_image_paletted(d->unc_pixels, 0, 8, d->w, d->pal, img, 0);
188 de_bitmap_write_to_file(img, NULL, 0);
190 done:
191 de_bitmap_destroy(img);
192 if(d) {
193 dbuf_close(d->unc_pixels);
194 de_free(c, d);
196 de_dbg_indent_restore(c, saved_indent_level);
199 static int de_identify_pkm(deark *c)
201 if(!dbuf_memcmp(c->infile, 0, "PKM\0", 4))
202 return 100;
203 return 0;
206 static void de_help_pkm(deark *c)
208 de_msg(c, "-opt pkm:trans : Make the background color transparent");
211 void de_module_pkm(deark *c, struct deark_module_info *mi)
213 mi->id = "pkm";
214 mi->desc = "PKM (GrafX2)";
215 mi->run_fn = de_run_pkm;
216 mi->identify_fn = de_identify_pkm;
217 mi->help_fn = de_help_pkm;