zoo: Various improvements
[deark.git] / modules / sunras.c
blobb67cbb2849d79acfbff36cbdb29661de92b7738f
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // Sun Raster image format
7 #include <deark-config.h>
8 #include <deark-private.h>
9 DE_DECLARE_MODULE(de_module_sunras);
11 struct color32desc_type {
12 u8 channel_shift[4];
13 u8 has_alpha; // 0=no, 1=yes, 2=autodetect
14 const char *name;
17 typedef struct localctx_struct {
18 i64 npwidth, height;
19 i64 pdwidth;
20 i64 depth;
22 #define RT_OLD 0
23 #define RT_STANDARD 1
24 #define RT_BYTE_ENCODED 2
25 #define RT_FORMAT_RGB 3
26 #define RT_FORMAT_TIFF 4
27 #define RT_FORMAT_IFF 5
28 i64 imgtype;
29 int is_compressed;
30 int is_rgb_order;
31 int user_set_fmt32;
33 struct color32desc_type color32desc;
35 i64 imglen;
37 #define RMT_NONE 0
38 #define RMT_EQUAL_RGB 1
39 #define RMT_RAW 2
40 i64 maptype;
42 i64 maplen;
44 i64 rowspan;
45 i64 unc_pixels_size;
46 int is_paletted;
47 int is_grayscale;
49 u32 pal[256];
50 } lctx;
52 static void do_read_palette(deark *c, lctx *d, i64 pos)
54 i64 num_entries;
55 i64 num_entries_to_read;
56 i64 k;
57 u8 r, g, b;
59 num_entries = d->maplen/3;
60 num_entries_to_read = num_entries;
61 if(num_entries_to_read>256) num_entries_to_read = 256;
63 for(k=0; k<num_entries_to_read; k++) {
64 r = de_getbyte(pos + k);
65 g = de_getbyte(pos+num_entries + k);
66 b = de_getbyte(pos+num_entries*2 + k);
67 d->pal[k] = DE_MAKE_RGB(r, g, b);
68 de_dbg_pal_entry(c, k, d->pal[k]);
72 static void do_image(deark *c, lctx *d, dbuf *unc_pixels)
74 de_bitmap *img = NULL;
75 u32 clr;
76 u8 b;
77 i64 i, j;
78 i64 src_bypp, dst_bypp;
79 unsigned int getrgbflags;
81 if(d->depth!=1 && d->depth!=4 && d->depth!=8 && d->depth!=24 && d->depth!=32) {
82 de_err(c, "Bit depth %d not supported", (int)d->depth);
83 goto done;
85 if(d->depth==32 && !d->user_set_fmt32) {
86 // Some apps think the extra channel comes first (e.g. xBGR); others
87 // think it comes last (BGRx).
88 // Some apps think the extra channel is for alpha; others think it is
89 // unused.
90 // Some apps think the color channels are always in BGR order; others
91 // think the order is RGB for RT_FORMAT_RGB format.
93 // By default we use ARGB for RT_FORMAT_RGB, and ABGR otherwise, with
94 // alpha autodetected (alpha channel is ignored if all values are 0).
96 de_warn(c, "32-bit Sun Raster files are not portable. You may have to use "
97 "\"-opt sunras:fmt32=...\".");
99 d->color32desc.channel_shift[0] = 24; // A or x
100 d->color32desc.channel_shift[2] = 8; // G
101 if(d->is_rgb_order) { // xrgb or argb
102 d->color32desc.channel_shift[1] = 16; // R
103 d->color32desc.channel_shift[3] = 0; // B
105 else { // xbgr or abgr
106 d->color32desc.channel_shift[1] = 0; // B
107 d->color32desc.channel_shift[3] = 16; // R
110 d->color32desc.has_alpha = 2;
113 if(!de_good_image_dimensions(c, d->npwidth, d->height)) goto done;
115 src_bypp = d->depth/8;
117 if(d->is_paletted) {
118 dst_bypp = 3;
120 else if(d->is_grayscale) {
121 dst_bypp = 1;
123 else if(d->depth==32) {
124 if(d->color32desc.has_alpha==0) {
125 dst_bypp = 3;
127 else {
128 dst_bypp = 4;
131 else {
132 dst_bypp = 3;
135 if(d->is_rgb_order) {
136 getrgbflags = 0;
138 else {
139 getrgbflags = DE_GETRGBFLAG_BGR;
142 img = de_bitmap_create2(c, d->npwidth, d->pdwidth, d->height, (int)dst_bypp);
144 for(j=0; j<d->height; j++) {
145 for(i=0; i<d->pdwidth; i++) {
146 if(d->is_paletted || d->is_grayscale) {
147 b = de_get_bits_symbol(unc_pixels, d->depth, d->rowspan*j, i);
148 clr = d->pal[(unsigned int)b];
149 de_bitmap_setpixel_rgb(img, i, j, clr);
151 else if(d->depth==24) {
152 clr = dbuf_getRGB(unc_pixels, d->rowspan*j+i*src_bypp, getrgbflags);
153 de_bitmap_setpixel_rgb(img, i, j, clr);
155 else if(d->depth==32) {
156 u8 pixbuf[4];
157 dbuf_read(unc_pixels, pixbuf, d->rowspan*j+i*src_bypp, 4);
158 clr =
159 ((unsigned int)pixbuf[0] << d->color32desc.channel_shift[0]) |
160 ((unsigned int)pixbuf[1] << d->color32desc.channel_shift[1]) |
161 ((unsigned int)pixbuf[2] << d->color32desc.channel_shift[2]) |
162 ((unsigned int)pixbuf[3] << d->color32desc.channel_shift[3]);
163 de_bitmap_setpixel_rgba(img, i, j, clr);
168 if(d->depth==32 && d->color32desc.has_alpha==2) { // autodetect alpha
169 de_bitmap_optimize_alpha(img, 0x1);
172 de_bitmap_write_to_file(img, NULL, 0);
174 done:
175 de_bitmap_destroy(img);
178 static const char *get_image_type_name(i64 t)
180 const char *name;
182 switch(t) {
183 case RT_OLD: name="old"; break;
184 case RT_STANDARD: name="standard"; break;
185 case RT_BYTE_ENCODED: name="RLE"; break;
186 case RT_FORMAT_RGB: name="RGB"; break;
187 case RT_FORMAT_TIFF: name="TIFF"; break;
188 case RT_FORMAT_IFF: name="IFF"; break;
189 case 0xffff: name="experimental"; break;
190 default: name="?";
192 return name;
195 static const char *get_map_type_name(i64 t)
197 const char *name;
199 switch(t) {
200 case RMT_NONE: name="NONE"; break;
201 case RMT_EQUAL_RGB: name="EQUAL_RGB"; break;
202 case RMT_RAW: name="RAW"; break;
203 default: name="?";
205 return name;
208 static void read_header(deark *c, lctx *d, i64 pos)
210 de_dbg(c, "header at %d", (int)pos);
211 de_dbg_indent(c, 1);
213 d->npwidth = de_getu32be(pos+4);
214 d->height = de_getu32be(pos+8);
215 de_dbg_dimensions(c, d->npwidth, d->height);
217 d->depth = de_getu32be(pos+12);
218 de_dbg(c, "depth: %d", (int)d->depth);
220 d->imglen = de_getu32be(pos+16);
221 d->imgtype = de_getu32be(pos+20);
222 de_dbg(c, "image type=%d (%s), len=%d", (int)d->imgtype,
223 get_image_type_name(d->imgtype), (int)d->imglen);
224 if(d->imgtype==RT_BYTE_ENCODED) {
225 d->is_compressed = 1;
227 if(d->imgtype==RT_FORMAT_RGB) {
228 d->is_rgb_order = 1;
231 d->maptype = de_getu32be(pos+24);
232 d->maplen = de_getu32be(pos+28);
233 de_dbg(c, "map type=%d (%s), len=%d", (int)d->maptype,
234 get_map_type_name(d->maptype), (int)d->maplen);
236 de_dbg_indent(c, -1);
239 static void do_uncompress_image(deark *c, lctx *d, i64 pos1, i64 len, dbuf *unc_pixels)
241 i64 pos = pos1;
243 while(1) {
244 u8 b0, b1, b2;
246 // Stop if we reach the end of the input file.
247 if(pos >= c->infile->len) break;
249 b0 = de_getbyte(pos++);
250 if(b0==0x80) {
251 b1 = de_getbyte(pos++);
252 if(b1==0x00) { // An escaped 0x80 byte
253 dbuf_writebyte(unc_pixels, 0x80);
255 else { // A compressed run
256 b2 = de_getbyte(pos++);
257 dbuf_write_run(unc_pixels, b2, (i64)b1+1);
260 else { // An uncompressed byte
261 dbuf_writebyte(unc_pixels, b0);
266 static void handle_options(deark *c, lctx *d)
268 const char *fmt32;
269 // Table of user-configurable color "descriptors" for 32-bit images.
270 static const struct color32desc_type color32desc_arr[] = {
271 { { 0, 8, 16, 24 }, 0, "bgrx" },
272 { { 0, 8, 16, 24 }, 1, "bgra" },
273 { {16, 8, 0, 24 }, 0, "rgbx" },
274 { {16, 8, 0, 24 }, 1, "rgba" },
275 { {24, 0, 8, 16 }, 0, "xbgr" },
276 { {24, 0, 8, 16 }, 1, "abgr" },
277 { {24, 16, 8, 0 }, 0, "xrgb" },
278 { {24, 16, 8, 0 }, 1, "argb" }
281 fmt32 = de_get_ext_option(c, "sunras:fmt32");
282 if(fmt32) {
283 size_t k;
284 for(k=0; k<DE_ARRAYCOUNT(color32desc_arr); k++) {
285 if(!de_strcmp(fmt32, color32desc_arr[k].name)) {
286 d->color32desc = color32desc_arr[k]; // struct copy
287 d->user_set_fmt32 = 1;
288 break;
294 static void de_run_sunras(deark *c, de_module_params *mparams)
296 lctx *d = NULL;
297 dbuf *unc_pixels = NULL;
298 i64 pos;
299 i64 bits_per_row;
300 int saved_indent_level;
301 de_dbg_indent_save(c, &saved_indent_level);
303 d = de_malloc(c, sizeof(lctx));
304 handle_options(c, d);
306 pos = 0;
307 read_header(c, d, pos);
308 pos += 32;
310 if(pos >= c->infile->len) goto done;
312 if(d->maplen > 0)
313 de_dbg(c, "colormap at %d", (int)pos);
315 de_dbg_indent(c, 1);
317 if(d->maptype==RMT_EQUAL_RGB) {
318 if(d->depth<=8) {
319 d->is_paletted = 1;
320 do_read_palette(c, d, pos);
322 else {
323 de_err(c, "This type of image is not supported");
324 goto done;
327 else if(d->maptype==RMT_NONE) {
328 if(d->depth<=8) {
329 d->is_grayscale = 1;
330 de_make_grayscale_palette(d->pal, ((i64)1)<<d->depth, d->depth==1 ? 1 : 0);
333 else {
334 // TODO: Support RMT_RAW
335 de_err(c, "Colormap type (%d) is not supported", (int)d->maptype);
336 goto done;
338 pos += d->maplen;
339 de_dbg_indent(c, -1);
341 if(pos >= c->infile->len) goto done;
342 de_dbg(c, "image data at %d", (int)pos);
343 de_dbg_indent(c, 1);
345 bits_per_row = de_pad_to_n(d->npwidth * d->depth, 16);
346 d->rowspan = bits_per_row / 8;
347 d->pdwidth = bits_per_row / d->depth;
348 d->unc_pixels_size = d->rowspan * d->height;
350 if(d->imgtype>5) {
351 de_err(c, "This type of image (%d) is not supported", (int)d->imgtype);
352 goto done;
355 if((d->imgtype==RT_STANDARD || d->imgtype==RT_FORMAT_RGB) && d->imglen!=d->unc_pixels_size) {
356 de_warn(c, "Inconsistent image length: reported=%d, calculated=%d",
357 (int)d->imglen, (int)d->unc_pixels_size);
360 if(d->is_compressed) {
361 unc_pixels = dbuf_create_membuf(c, d->unc_pixels_size, 0x1);
362 do_uncompress_image(c, d, pos, c->infile->len - pos, unc_pixels);
364 else {
365 unc_pixels = dbuf_open_input_subfile(c->infile, pos, c->infile->len - pos);
368 do_image(c, d, unc_pixels);
369 de_dbg_indent(c, -1);
371 done:
372 dbuf_close(unc_pixels);
373 de_free(c, d);
374 de_dbg_indent_restore(c, saved_indent_level);
377 static int de_identify_sunras(deark *c)
379 if(!dbuf_memcmp(c->infile, 0, "\x59\xa6\x6a\x95", 4))
380 return 100;
381 return 0;
384 static void de_help_sunras(deark *c)
386 de_msg(c, "-opt sunras:fmt32=<"
387 "xbgr|abgr|xrgb|argb|"
388 "bgrx|bgra|rgbx|rgba> : The interpretation of a 32-bit pixel");
391 void de_module_sunras(deark *c, struct deark_module_info *mi)
393 mi->id = "sunras";
394 mi->desc = "Sun Raster";
395 mi->run_fn = de_run_sunras;
396 mi->identify_fn = de_identify_sunras;
397 mi->help_fn = de_help_sunras;