iccprofile: Refactoring
[deark.git] / modules / rosprite.c
blob6d989953125578f8ffe7b617f6ccbe005727228a
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // Acorn Sprite / RISC OS Sprite
7 #include <deark-config.h>
8 #include <deark-private.h>
9 DE_DECLARE_MODULE(de_module_rosprite);
11 struct old_mode_info {
12 u32 mode;
13 int fgbpp;
14 int xdpi;
15 int ydpi;
17 // Screen mode list at: http://www.riscos.com/support/users/userguide3/book3b/book3_17.html
18 // TODO: Find reliable information about DPI fields.
19 static const struct old_mode_info old_mode_info_arr[] = {
20 {0, 1, 90, 45},
21 {1, 2, 45, 45},
22 {2, 4, 0, 0},
23 {4, 1, 45, 45},
24 {5, 2, 0, 0},
25 {8, 2, 90, 45},
26 {9, 4, 45, 45},
27 {10, 8, 0, 0},
28 {11, 2, 0, 0},
29 {12, 4, 90, 45},
30 {13, 8, 45, 45},
31 {14, 4, 0, 0},
32 {15, 8, 90, 45},
33 {16, 4, 0, 0},
34 {17, 4, 0, 0},
35 {18, 1, 90, 90},
36 {19, 2, 90, 90},
37 {20, 4, 90, 90},
38 {21, 8, 90, 90},
39 {22, 4, 0, 0},
40 {23, 1, 0, 0},
41 {24, 8, 0, 0},
42 {25, 1, 0, 0},
43 {26, 2, 0, 0},
44 {27, 4, 90, 90},
45 {28, 8, 90, 90},
46 {29, 1, 0, 0},
47 {30, 2, 0, 0},
48 {31, 4, 90, 90},
49 {32, 8, 90, 90},
50 {33, 1, 0, 0},
51 {34, 2, 0, 0},
52 {35, 4, 0, 0},
53 {36, 8, 90, 45},
54 {37, 1, 0, 0},
55 {38, 2, 0, 0},
56 {39, 4, 0, 0},
57 {40, 8, 0, 0},
58 {41, 1, 0, 0},
59 {42, 2, 0, 0},
60 {43, 4, 0, 0},
61 {44, 1, 0, 0},
62 {45, 2, 0, 0},
63 {46, 4, 0, 0},
64 {47, 8, 0, 0},
65 {48, 4, 0, 0},
66 {49, 8, 0, 0},
68 // I have some mode-107 files, but I don't know how standard this is.
69 {107, 16, 0, 0},
71 {1000, 0, 0, 0}
74 struct page_ctx {
75 i64 fgbpp;
76 i64 maskbpp;
77 i64 width_in_words;
78 i64 first_bit, last_bit;
79 i64 width, height;
81 i64 x_idx_of_first_src_fg_pixel_to_convert; // dstfg[0] <- srcfg[x.i.o.f.s.f.p.t.c]
82 i64 num_fg_x_pixels_to_convert;
83 i64 x_idx_of_first_src_mask_pixel_to_convert;
84 i64 mask_x_offset; // dstmask[m.x.o] <- srcmask[x.i.o.f.s.m.p.t.c]
85 i64 num_mask_x_pixels_to_convert;
87 i64 xdpi, ydpi;
88 i64 num_padding_pixels_at_start_of_row;
89 u32 mode;
90 int has_mask;
91 #define MASK_TYPE_OLD 1 // Binary transparency, fgbpp bits/pixel
92 #define MASK_TYPE_NEW_1 2 // Binary transparency, 8 bits/pixel
93 #define MASK_TYPE_NEW_8 3 // Alpha transparency, 8 bits/pixel
94 int mask_type;
95 i64 mask_rowspan;
96 i64 image_offset;
97 i64 mask_offset;
99 int has_custom_palette;
100 i64 custom_palette_pos;
101 i64 custom_palette_ncolors;
102 u32 pal[256];
105 typedef struct localctx_struct {
106 i64 num_images;
107 } lctx;
109 static const u32 pal4[4] = {
110 0xffffff,0xbbbbbb,0x777777,0x000000
113 static u32 getpal4(int k)
115 if(k<0 || k>3) return 0;
116 return pal4[k];
119 static const u32 pal16[16] = {
120 0xffffff,0xdddddd,0xbbbbbb,0x999999,0x777777,0x555555,0x333333,0x000000,
121 0x4499ff,0xeeee00,0x00cc00,0xdd0000,0xeeeebb,0x558800,0xffbb00,0x00bbff
124 static u32 getpal16(int k)
126 if(k<0 || k>15) return 0;
127 return pal16[k];
130 static u32 getpal256(int k)
132 u8 r, g, b;
133 if(k<0 || k>255) return 0;
134 r = k%8 + ((k%32)/16)*8;
135 g = k%4 + ((k%128)/32)*4;
136 b = (u8)(k%4 + ((k%16)/8)*4 + (k/128)*8);
137 r = (r<<4)|r;
138 g = (g<<4)|g;
139 b = (b<<4)|b;
140 return DE_MAKE_RGB(r,g,b);
143 static void do_image(deark *c, lctx *d, struct page_ctx *pg, de_finfo *fi)
145 de_bitmap *img = NULL;
146 de_bitmap *mask = NULL;
147 i64 i, j;
148 u8 n;
149 de_color clr;
150 int is_grayscale;
151 int bypp;
153 if(pg->fgbpp<=8) {
154 is_grayscale = de_is_grayscale_palette(pg->pal, ((i64)1)<<pg->fgbpp);
156 else {
157 is_grayscale = 0;
160 bypp = is_grayscale?1:3;
161 if(pg->has_mask) bypp++;
163 if(c->padpix) {
164 pg->num_fg_x_pixels_to_convert = (pg->width_in_words*32)/pg->fgbpp;
165 pg->x_idx_of_first_src_fg_pixel_to_convert = 0;
166 if(pg->mask_type==MASK_TYPE_OLD) {
167 pg->x_idx_of_first_src_mask_pixel_to_convert = 0;
168 pg->mask_x_offset = 0;
169 pg->num_mask_x_pixels_to_convert = pg->num_fg_x_pixels_to_convert;
171 else {
172 pg->x_idx_of_first_src_mask_pixel_to_convert = 0;
173 // Best guess is that new-style masks don't have initial padding pixels
174 pg->mask_x_offset = pg->num_padding_pixels_at_start_of_row;
175 pg->num_mask_x_pixels_to_convert = pg->mask_rowspan;
178 else {
179 pg->num_fg_x_pixels_to_convert = pg->width;
180 pg->x_idx_of_first_src_fg_pixel_to_convert = pg->num_padding_pixels_at_start_of_row;
181 if(pg->mask_type==MASK_TYPE_OLD) {
182 pg->x_idx_of_first_src_mask_pixel_to_convert = pg->x_idx_of_first_src_fg_pixel_to_convert;
183 pg->mask_x_offset = 0;
184 pg->num_mask_x_pixels_to_convert = pg->num_fg_x_pixels_to_convert;
186 else {
187 pg->x_idx_of_first_src_mask_pixel_to_convert = 0;
188 pg->mask_x_offset = 0;
189 pg->num_mask_x_pixels_to_convert = pg->num_fg_x_pixels_to_convert;
193 img = de_bitmap_create(c, pg->num_fg_x_pixels_to_convert, pg->height, bypp);
195 if(pg->xdpi>0) {
196 fi->density.code = DE_DENSITY_DPI;
197 fi->density.xdens = (double)pg->xdpi;
198 fi->density.ydens = (double)pg->ydpi;
201 de_dbg(c, "image data at %d", (int)pg->image_offset);
203 for(j=0; j<pg->height; j++) {
204 for(i=0; i<pg->num_fg_x_pixels_to_convert; i++) {
205 i64 i_adj;
207 i_adj = i+pg->x_idx_of_first_src_fg_pixel_to_convert;
209 if(pg->fgbpp==32) {
210 clr = dbuf_getRGB(c->infile, pg->image_offset + 4*pg->width_in_words*j + 4*i_adj, 0);
212 else if(pg->fgbpp==16) {
213 clr = (de_color)de_getu16le(pg->image_offset + 4*pg->width_in_words*j + i_adj*2);
214 clr = de_bgr555_to_888(clr);
216 else {
217 n = de_get_bits_symbol_lsb(c->infile, pg->fgbpp, pg->image_offset + 4*pg->width_in_words*j,
218 i_adj);
219 clr = DE_MAKE_OPAQUE(pg->pal[(int)n]);
222 de_bitmap_setpixel_rgba(img, i, j, clr);
226 if(pg->has_mask) {
227 de_dbg(c, "transparency mask at %"I64_FMT, pg->mask_offset);
229 if(pg->maskbpp<1 || pg->maskbpp>8) {
230 de_warn(c, "This type of transparency mask is not supported");
231 goto after_mask;
234 mask = de_bitmap_create(c, pg->num_fg_x_pixels_to_convert, pg->height, 1);
235 // Start with an opaque mask.
236 de_bitmap_rect(mask, 0, 0, mask->width, mask->height, DE_STOCKCOLOR_WHITE, 0);
238 for(j=0; j<pg->height; j++) {
239 for(i=0; i<pg->num_mask_x_pixels_to_convert; i++) {
240 i64 src_xpos, dst_xpos;
241 de_colorsample a = 255;
243 src_xpos = i + pg->x_idx_of_first_src_mask_pixel_to_convert;
244 dst_xpos = i + pg->mask_x_offset;
246 n = de_get_bits_symbol_lsb(c->infile, pg->maskbpp, pg->mask_offset + pg->mask_rowspan*j,
247 src_xpos);
249 if(pg->mask_type==MASK_TYPE_OLD || pg->mask_type==MASK_TYPE_NEW_1) {
250 if(n==0)
251 a = 0;
252 else
253 a = 255;
255 else if(pg->mask_type==MASK_TYPE_NEW_8) {
256 a = n;
258 de_bitmap_setpixel_gray(mask, dst_xpos, j, a);
262 de_bitmap_apply_mask(img, mask, 0);
264 after_mask:
266 de_bitmap_write_to_file_finfo(img, fi, 0);
267 de_bitmap_destroy(img);
268 if(mask) de_bitmap_destroy(mask);
271 static u32 average_color(u32 c1, u32 c2)
273 u8 a, r, g, b;
274 a = ((u32)DE_COLOR_A(c1) + DE_COLOR_A(c2))/2;
275 r = ((u32)DE_COLOR_R(c1) + DE_COLOR_R(c2))/2;
276 g = ((u32)DE_COLOR_G(c1) + DE_COLOR_G(c2))/2;
277 b = ((u32)DE_COLOR_B(c1) + DE_COLOR_B(c2))/2;
278 return DE_MAKE_RGBA(r,g,b,a);
281 static void do_setup_palette(deark *c, lctx *d, struct page_ctx *pg)
283 i64 k;
284 u32 clr1, clr2, clr3;
286 if(pg->fgbpp>8) {
287 return;
290 if(pg->has_custom_palette) {
291 de_dbg(c, "custom palette at %d, %d entries", (int)pg->custom_palette_pos,
292 (int)pg->custom_palette_ncolors);
294 de_dbg_indent(c, 1);
296 for(k=0; k<256; k++) {
297 if(pg->has_custom_palette) {
298 if(k<pg->custom_palette_ncolors) {
299 // Each palette entry has two colors, which are usually but not always
300 // the same.
301 // TODO: Figure out what to do if they are different. For now, we'll
302 // average them.
303 clr1 = dbuf_getRGB(c->infile, pg->custom_palette_pos + 8*k + 1, 0);
304 clr2 = dbuf_getRGB(c->infile, pg->custom_palette_pos + 8*k + 4 + 1, 0);
305 if(clr1==clr2) {
306 pg->pal[k] = clr1;
307 de_dbg_pal_entry(c, k, clr1);
309 else {
310 char tmps[64];
312 clr3 = average_color(clr1, clr2);
313 pg->pal[k] = clr3;
314 de_snprintf(tmps, sizeof(tmps), "(%3d,%3d,%3d),(%3d,%3d,%3d) "DE_CHAR_RIGHTARROW" ",
315 (int)DE_COLOR_R(clr1), (int)DE_COLOR_G(clr1), (int)DE_COLOR_B(clr1),
316 (int)DE_COLOR_R(clr2), (int)DE_COLOR_G(clr2), (int)DE_COLOR_B(clr2));
317 de_dbg_pal_entry2(c, k, clr3, tmps, NULL, NULL);
320 else {
321 pg->pal[k] = getpal256((int)k);
324 else if(pg->fgbpp==4 && k<16) {
325 pg->pal[k] = getpal16((int)k);
327 else if(pg->fgbpp==2 && k<4) {
328 pg->pal[k] = getpal4((int)k);
330 else if(pg->fgbpp==1 && k<2) {
331 pg->pal[k] = (k==0)?DE_STOCKCOLOR_WHITE:DE_STOCKCOLOR_BLACK;
333 else {
334 pg->pal[k] = getpal256((int)k);
338 de_dbg_indent(c, -1);
341 static void read_sprite_name(deark *c, lctx *d, de_finfo *fi, i64 pos)
343 de_ucstring *s = NULL;
344 if(c->debug_level<1 && !c->filenames_from_file) return;
346 s = ucstring_create(c);
347 dbuf_read_to_ucstring(c->infile, pos, 12, s, DE_CONVFLAG_STOP_AT_NUL, DE_ENCODING_RISCOS);
348 de_dbg(c, "sprite name: \"%s\"", ucstring_getpsz(s));
350 if(c->filenames_from_file) {
351 de_finfo_set_name_from_ucstring(c, fi, s, 0);
354 ucstring_destroy(s);
357 static void do_sprite(deark *c, lctx *d, i64 index,
358 i64 pos1, i64 len)
360 i64 new_img_type;
361 de_finfo *fi = NULL;
362 int saved_indent_level;
363 struct page_ctx *pg = NULL;
365 de_dbg_indent_save(c, &saved_indent_level);
366 pg = de_malloc(c, sizeof(struct page_ctx));
368 de_dbg(c, "image header at %d", (int)pos1);
369 de_dbg_indent(c, 1);
371 // Name at pos 4, len=12
372 fi = de_finfo_create(c);
374 read_sprite_name(c, d, fi, pos1+4);
376 pg->width_in_words = de_getu32le(pos1+16) +1;
377 pg->height = de_getu32le(pos1+20) +1;
378 de_dbg(c, "width-in-words: %d, height: %d", (int)pg->width_in_words, (int)pg->height);
380 pg->first_bit = de_getu32le(pos1+24);
381 if(pg->first_bit>31) pg->first_bit=31;
382 pg->last_bit = de_getu32le(pos1+28);
383 if(pg->last_bit>31) pg->last_bit=31;
384 pg->image_offset = de_getu32le(pos1+32) + pos1;
385 pg->mask_offset = de_getu32le(pos1+36) + pos1;
386 pg->has_mask = (pg->mask_offset != pg->image_offset);
387 de_dbg(c, "first bit: %d, last bit: %d", (int)pg->first_bit, (int)pg->last_bit);
388 de_dbg(c, "image offset: %d, mask_offset: %d", (int)pg->image_offset, (int)pg->mask_offset);
390 pg->mode = (u32)de_getu32le(pos1+40);
391 de_dbg(c, "mode: 0x%08x", (unsigned int)pg->mode);
393 de_dbg_indent(c, 1);
395 new_img_type = (pg->mode&0x78000000U)>>27;
396 if(new_img_type==0)
397 de_dbg(c, "old format screen mode: %d", (int)pg->mode);
398 else
399 de_dbg(c, "new format image type: %d", (int)new_img_type);
401 if(new_img_type==0) {
402 // old format
403 int x;
405 for(x=0; old_mode_info_arr[x].mode<1000; x++) {
406 if(pg->mode == old_mode_info_arr[x].mode) {
407 pg->fgbpp = (i64)old_mode_info_arr[x].fgbpp;
408 pg->xdpi = (i64)old_mode_info_arr[x].xdpi;
409 pg->ydpi = (i64)old_mode_info_arr[x].ydpi;
410 break;
414 if(pg->fgbpp==0) {
415 de_err(c, "Screen mode %d not supported", (int)pg->mode);
416 goto done;
419 if(pg->fgbpp>8 && pg->has_mask) {
420 de_err(c, "Transparency not supported for this image format");
421 goto done;
424 if(pg->has_mask) {
425 pg->mask_type = MASK_TYPE_OLD;
426 pg->mask_rowspan = 4*pg->width_in_words;
427 pg->maskbpp = pg->fgbpp;
428 de_dbg(c, "mask type: old");
431 else {
432 // new format
433 pg->xdpi = (pg->mode&0x07ffc000)>>14;
434 pg->ydpi = (pg->mode&0x00003ffe)>>1;
435 de_dbg(c, "xdpi: %d, ydpi: %d", (int)pg->xdpi, (int)pg->ydpi);
436 switch(new_img_type) {
437 case 1:
438 pg->fgbpp = 1;
439 break;
440 case 2:
441 pg->fgbpp = 2;
442 break;
443 case 3:
444 pg->fgbpp = 4;
445 break;
446 case 4:
447 pg->fgbpp = 8;
448 break;
449 case 5:
450 pg->fgbpp = 16;
451 break;
452 case 6:
453 pg->fgbpp = 32;
454 break;
455 //case 7: 32bpp CMYK (TODO)
456 //case 8: 24bpp (TODO)
457 default:
458 de_err(c, "New format type %d not supported", (int)new_img_type);
459 goto done;
462 if(pg->has_mask) {
463 pg->mask_type = (pg->mode&0x80000000U) ? MASK_TYPE_NEW_8 : MASK_TYPE_NEW_1;
464 pg->maskbpp = 8;
465 de_dbg(c, "mask type: new - %s", pg->mask_type==MASK_TYPE_NEW_8 ? "alpha" : "binary");
469 de_dbg(c, "foreground bits/pixel: %d", (int)pg->fgbpp);
471 de_dbg_indent(c, -1);
473 pg->width = ((pg->width_in_words-1) * 4 * 8 + (pg->last_bit+1)) / pg->fgbpp;
474 pg->num_padding_pixels_at_start_of_row = pg->first_bit / pg->fgbpp;
475 pg->width -= pg->num_padding_pixels_at_start_of_row;
476 de_dbg(c, "calculated width: %d", (int)pg->width);
478 if(!de_good_image_dimensions(c, pg->width, pg->height)) goto done;
480 if(pg->mask_type==MASK_TYPE_NEW_1 || pg->mask_type==MASK_TYPE_NEW_8) {
481 if(pg->num_padding_pixels_at_start_of_row>0) {
482 de_warn(c, "This image has a new-style transparency mask, and a "
483 "nonzero \"first bit\" field. This combination might not be "
484 "handled correctly.");
486 pg->mask_rowspan = ((pg->width+31)/32)*4;
489 de_dbg_indent(c, -1);
491 pg->custom_palette_pos = pos1 + 44;
492 if(pg->image_offset >= pg->custom_palette_pos+8 && pg->fgbpp<=8) {
493 pg->has_custom_palette = 1;
494 pg->custom_palette_ncolors = (pg->image_offset - (pos1+44))/8;
495 if(pg->custom_palette_ncolors>256) pg->custom_palette_ncolors=256;
498 do_setup_palette(c, d, pg);
500 do_image(c, d, pg, fi);
501 done:
502 de_dbg_indent_restore(c, saved_indent_level);
503 de_finfo_destroy(c, fi);
504 de_free(c, pg);
507 static void de_run_rosprite(deark *c, de_module_params *mparams)
509 lctx *d = NULL;
510 i64 pos;
511 i64 sprite_size;
512 i64 first_sprite_offset;
513 i64 implied_file_size;
514 i64 k;
516 d = de_malloc(c, sizeof(lctx));
518 pos = 0;
520 d->num_images = de_getu32le(pos);
521 de_dbg(c, "number of images: %d", (int)d->num_images);
522 first_sprite_offset = de_getu32le(pos+4) - 4;
523 de_dbg(c, "first sprite offset: %d", (int)first_sprite_offset);
524 implied_file_size = de_getu32le(pos+8) - 4;
525 de_dbg(c, "reported file size: %d", (int)implied_file_size);
526 if(implied_file_size != c->infile->len) {
527 de_warn(c, "The \"first free word\" field implies the file size is %d, but it "
528 "is actually %d. This may not be a sprite file.",
529 (int)implied_file_size, (int)c->infile->len);
532 pos = first_sprite_offset;
533 for(k=0; k<d->num_images; k++) {
534 if(pos>=c->infile->len) break;
535 sprite_size = de_getu32le(pos);
536 de_dbg(c, "image #%d at %d, size=%d", (int)k, (int)pos, (int)sprite_size);
537 if(sprite_size<1) break;
538 de_dbg_indent(c, 1);
539 do_sprite(c, d, k, pos, sprite_size);
540 de_dbg_indent(c, -1);
541 pos += sprite_size;
544 de_free(c, d);
547 static int de_identify_rosprite(deark *c)
549 i64 h0, h1, h2;
550 h0 = de_getu32le(0);
551 h1 = de_getu32le(4);
552 h2 = de_getu32le(8);
554 if(h0<1 || h0>10000) return 0;
555 if(h1-4<12) return 0;
556 if(h1-4 >= c->infile->len) return 0;
557 if(h2-4 != c->infile->len) return 0;
559 return 80;
562 void de_module_rosprite(deark *c, struct deark_module_info *mi)
564 mi->id = "rosprite";
565 mi->desc = "RISC OS Sprite, a.k.a. Acorn Sprite";
566 mi->run_fn = de_run_rosprite;
567 mi->identify_fn = de_identify_rosprite;