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
{
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
[] = {
68 // I have some mode-107 files, but I don't know how standard this is.
78 i64 first_bit
, last_bit
;
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
;
88 i64 num_padding_pixels_at_start_of_row
;
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
99 int has_custom_palette
;
100 i64 custom_palette_pos
;
101 i64 custom_palette_ncolors
;
105 typedef struct localctx_struct
{
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;
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;
130 static u32
getpal256(int k
)
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);
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
;
154 is_grayscale
= de_is_grayscale_palette(pg
->pal
, ((i64
)1)<<pg
->fgbpp
);
160 bypp
= is_grayscale
?1:3;
161 if(pg
->has_mask
) bypp
++;
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
;
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
;
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
;
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
);
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
++) {
207 i_adj
= i
+pg
->x_idx_of_first_src_fg_pixel_to_convert
;
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
);
217 n
= de_get_bits_symbol_lsb(c
->infile
, pg
->fgbpp
, pg
->image_offset
+ 4*pg
->width_in_words
*j
,
219 clr
= DE_MAKE_OPAQUE(pg
->pal
[(int)n
]);
222 de_bitmap_setpixel_rgba(img
, i
, j
, clr
);
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");
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
,
249 if(pg
->mask_type
==MASK_TYPE_OLD
|| pg
->mask_type
==MASK_TYPE_NEW_1
) {
255 else if(pg
->mask_type
==MASK_TYPE_NEW_8
) {
258 de_bitmap_setpixel_gray(mask
, dst_xpos
, j
, a
);
262 de_bitmap_apply_mask(img
, mask
, 0);
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
)
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
)
284 u32 clr1
, clr2
, clr3
;
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
);
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
301 // TODO: Figure out what to do if they are different. For now, we'll
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);
307 de_dbg_pal_entry(c
, k
, clr1
);
312 clr3
= average_color(clr1
, clr2
);
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
);
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
;
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);
357 static void do_sprite(deark
*c
, lctx
*d
, i64 index
,
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
);
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
);
395 new_img_type
= (pg
->mode
&0x78000000U
)>>27;
397 de_dbg(c
, "old format screen mode: %d", (int)pg
->mode
);
399 de_dbg(c
, "new format image type: %d", (int)new_img_type
);
401 if(new_img_type
==0) {
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
;
415 de_err(c
, "Screen mode %d not supported", (int)pg
->mode
);
419 if(pg
->fgbpp
>8 && pg
->has_mask
) {
420 de_err(c
, "Transparency not supported for this image format");
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");
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
) {
455 //case 7: 32bpp CMYK (TODO)
456 //case 8: 24bpp (TODO)
458 de_err(c
, "New format type %d not supported", (int)new_img_type
);
463 pg
->mask_type
= (pg
->mode
&0x80000000U
) ? MASK_TYPE_NEW_8
: MASK_TYPE_NEW_1
;
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
);
502 de_dbg_indent_restore(c
, saved_indent_level
);
503 de_finfo_destroy(c
, fi
);
507 static void de_run_rosprite(deark
*c
, de_module_params
*mparams
)
512 i64 first_sprite_offset
;
513 i64 implied_file_size
;
516 d
= de_malloc(c
, sizeof(lctx
));
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
);
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;
539 do_sprite(c
, d
, k
, pos
, sprite_size
);
540 de_dbg_indent(c
, -1);
547 static int de_identify_rosprite(deark
*c
)
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;
562 void de_module_rosprite(deark
*c
, struct deark_module_info
*mi
)
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
;