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.
76 i64 first_bit
, last_bit
;
78 i64 width_1
; // Width of intermediate bitmap. Includes left-padding, but not right-padding.
81 i64 num_padding_pixels_at_start_of_row
;
82 i64 num_padding_pixels_at_end_of_row
;
85 UI new_img_type
; // 0 if old format
88 #define MASK_TYPE_OLD 1 // Binary transparency, fgbpp bits/pixel
89 #define MASK_TYPE_NEW_1 2 // Binary transparency, 8 bits/pixel
90 #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
;
103 de_color maskpal
[256];
106 typedef struct localctx_struct
{
110 static const de_color pal4
[4] = {
111 0xffffffffU
,0xffbbbbbbU
,0xff777777U
,0xff000000U
114 static de_color
getpal4(int k
)
116 if(k
<0 || k
>3) return 0;
120 static const de_color pal16
[16] = {
121 0xffffffffU
,0xffddddddU
,0xffbbbbbbU
,0xff999999U
,0xff777777U
,0xff555555U
,0xff333333U
,0xff000000U
,
122 0xff4499ffU
,0xffeeee00U
,0xff00cc00U
,0xffdd0000U
,0xffeeeebbU
,0xff558800U
,0xffffbb00U
,0xff00bbffU
125 static de_color
getpal16(int k
)
127 if(k
<0 || k
>15) return 0;
131 static de_color
getpal256(int k
)
134 if(k
<0 || k
>255) return 0;
135 r
= k
%8 + ((k
%32)/16)*8;
136 g
= k
%4 + ((k
%128)/32)*4;
137 b
= (u8
)(k
%4 + ((k
%16)/8)*4 + (k
/128)*8);
141 return DE_MAKE_RGB(r
,g
,b
);
144 static void remove_left_padding(deark
*c
, struct page_ctx
*pg
)
148 if(pg
->num_padding_pixels_at_start_of_row
<1) return;
150 // Create a replacement bitmap, and copy the important pixels from the old
152 imgtmp
= de_bitmap_create(c
, pg
->npwidth
, pg
->height
, pg
->img
->bytes_per_pixel
);
153 de_bitmap_copy_rect(pg
->img
, imgtmp
, pg
->num_padding_pixels_at_start_of_row
, 0,
154 pg
->npwidth
, pg
->height
, 0, 0, 0);
156 de_bitmap_destroy(pg
->img
);
161 static void convert_image_16bit(deark
*c
, lctx
*d
, struct page_ctx
*pg
, de_bitmap
*img
)
165 for(j
=0; j
<pg
->height
; j
++) {
166 for(i
=0; i
<pg
->width_1
; i
++) {
169 clr
= (de_color
)de_getu16le(pg
->image_offset
+ 4*pg
->width_in_words
*j
+ i
*2);
170 clr
= de_bgr555_to_888(clr
);
171 de_bitmap_setpixel_rgba(pg
->img
, i
, j
, clr
);
176 static void do_image(deark
*c
, lctx
*d
, struct page_ctx
*pg
, de_finfo
*fi
)
182 is_grayscale
= de_is_grayscale_palette(pg
->pal
, ((i64
)1)<<pg
->fgbpp
);
188 bypp
= is_grayscale
?1:3;
189 if(pg
->has_mask
) bypp
++;
191 if(pg
->mask_type
==MASK_TYPE_OLD
) {
192 pg
->mask_width_1
= pg
->width_1
;
194 else if(pg
->mask_type
==MASK_TYPE_NEW_1
|| pg
->mask_type
==MASK_TYPE_NEW_8
) {
195 pg
->mask_width_1
= pg
->npwidth
;
198 pg
->img
= de_bitmap_create(c
, pg
->width_1
, pg
->height
, bypp
);
201 fi
->density
.code
= DE_DENSITY_DPI
;
202 fi
->density
.xdens
= (double)pg
->xdpi
;
203 fi
->density
.ydens
= (double)pg
->ydpi
;
206 de_dbg(c
, "image data at %"I64_FMT
, pg
->image_offset
);
209 de_convert_image_rgb(c
->infile
, pg
->image_offset
, 4*pg
->width_in_words
, 4, pg
->img
, 0);
211 else if(pg
->fgbpp
==16) {
212 convert_image_16bit(c
, d
, pg
, pg
->img
);
215 de_convert_image_paletted(c
->infile
, pg
->image_offset
, pg
->fgbpp
, 4*pg
->width_in_words
,
216 pg
->pal
, pg
->img
, 0x1);
219 if(pg
->has_mask
&& pg
->use_mask
) {
220 de_dbg(c
, "transparency mask at %"I64_FMT
, pg
->mask_offset
);
222 if(pg
->maskbpp
<1 || pg
->maskbpp
>8) {
223 de_warn(c
, "This type of transparency mask is not supported");
227 pg
->mask
= de_bitmap_create(c
, pg
->mask_width_1
, pg
->height
, 1);
229 // Make a palette to use with de_convert_image_paletted().
230 if(pg
->mask_type
==MASK_TYPE_NEW_8
) {
231 de_make_grayscale_palette(pg
->maskpal
, 256, 0);
234 // Supposedly, anything nonzero is opaque.
235 de_memset(pg
->maskpal
, 0xff, sizeof(pg
->maskpal
));
236 pg
->maskpal
[0] = DE_STOCKCOLOR_BLACK
;
239 // Start with an opaque mask.
240 de_bitmap_rect(pg
->mask
, 0, 0, pg
->mask
->width
, pg
->mask
->height
, DE_STOCKCOLOR_WHITE
, 0);
242 de_convert_image_paletted(c
->infile
, pg
->mask_offset
, pg
->maskbpp
, pg
->mask_rowspan
,
243 pg
->maskpal
, pg
->mask
, 0x1);
245 de_bitmap_apply_mask(pg
->img
, pg
->mask
, 0);
246 de_bitmap_destroy(pg
->mask
);
251 if(pg
->num_padding_pixels_at_start_of_row
>0 && !c
->padpix
) {
252 remove_left_padding(c
, pg
);
255 de_bitmap_write_to_file_finfo(pg
->img
, fi
, DE_CREATEFLAG_OPT_IMAGE
);
258 static de_color
average_color(de_color c1
, de_color c2
)
261 a
= ((de_color
)DE_COLOR_A(c1
) + DE_COLOR_A(c2
))/2;
262 r
= ((de_color
)DE_COLOR_R(c1
) + DE_COLOR_R(c2
))/2;
263 g
= ((de_color
)DE_COLOR_G(c1
) + DE_COLOR_G(c2
))/2;
264 b
= ((de_color
)DE_COLOR_B(c1
) + DE_COLOR_B(c2
))/2;
265 return DE_MAKE_RGBA(r
,g
,b
,a
);
268 static void do_setup_palette(deark
*c
, lctx
*d
, struct page_ctx
*pg
)
271 de_color clr1
, clr2
, clr3
;
277 if(pg
->has_custom_palette
) {
278 de_dbg(c
, "custom palette at %d, %d entries", (int)pg
->custom_palette_pos
,
279 (int)pg
->custom_palette_ncolors
);
283 for(k
=0; k
<256; k
++) {
284 if(pg
->has_custom_palette
) {
285 if(k
<pg
->custom_palette_ncolors
) {
286 // Each palette entry has two colors, which are usually but not always
288 // TODO: Figure out what to do if they are different. For now, we'll
290 clr1
= dbuf_getRGB(c
->infile
, pg
->custom_palette_pos
+ 8*k
+ 1, 0);
291 clr2
= dbuf_getRGB(c
->infile
, pg
->custom_palette_pos
+ 8*k
+ 4 + 1, 0);
294 de_dbg_pal_entry(c
, k
, clr1
);
299 clr3
= average_color(clr1
, clr2
);
301 de_snprintf(tmps
, sizeof(tmps
), "(%3d,%3d,%3d),(%3d,%3d,%3d) "DE_CHAR_RIGHTARROW
" ",
302 (int)DE_COLOR_R(clr1
), (int)DE_COLOR_G(clr1
), (int)DE_COLOR_B(clr1
),
303 (int)DE_COLOR_R(clr2
), (int)DE_COLOR_G(clr2
), (int)DE_COLOR_B(clr2
));
304 de_dbg_pal_entry2(c
, k
, clr3
, tmps
, NULL
, NULL
);
308 pg
->pal
[k
] = getpal256((int)k
);
311 else if(pg
->fgbpp
==4 && k
<16) {
312 pg
->pal
[k
] = getpal16((int)k
);
314 else if(pg
->fgbpp
==2 && k
<4) {
315 pg
->pal
[k
] = getpal4((int)k
);
317 else if(pg
->fgbpp
==1 && k
<2) {
318 pg
->pal
[k
] = (k
==0)?DE_STOCKCOLOR_WHITE
:DE_STOCKCOLOR_BLACK
;
321 pg
->pal
[k
] = getpal256((int)k
);
325 de_dbg_indent(c
, -1);
328 static void read_sprite_name(deark
*c
, lctx
*d
, de_finfo
*fi
, i64 pos
)
330 de_ucstring
*s
= NULL
;
331 if(c
->debug_level
<1 && !c
->filenames_from_file
) return;
333 s
= ucstring_create(c
);
334 dbuf_read_to_ucstring(c
->infile
, pos
, 12, s
, DE_CONVFLAG_STOP_AT_NUL
, DE_ENCODING_RISCOS
);
335 de_dbg(c
, "sprite name: \"%s\"", ucstring_getpsz(s
));
337 if(c
->filenames_from_file
) {
338 de_finfo_set_name_from_ucstring(c
, fi
, s
, 0);
344 static void do_sprite(deark
*c
, lctx
*d
, i64 index
,
348 struct page_ctx
*pg
= NULL
;
349 i64 image_offset_raw
, mask_offset_raw
;
351 int saved_indent_level
;
354 de_dbg_indent_save(c
, &saved_indent_level
);
355 pg
= de_malloc(c
, sizeof(struct page_ctx
));
357 de_dbg(c
, "image header at %"I64_FMT
, pos1
);
360 fi
= de_finfo_create(c
);
363 read_sprite_name(c
, d
, fi
, pos
);
366 pg
->width_in_words
= de_getu32le_p(&pos
) +1;
367 pg
->height
= de_getu32le_p(&pos
) +1;
368 de_dbg(c
, "width in words: %"I64_FMT
, pg
->width_in_words
);
369 de_dbg(c
, "height: %"I64_FMT
, pg
->height
);
371 pg
->first_bit
= de_getu32le_p(&pos
);
372 if(pg
->first_bit
>31) pg
->first_bit
=31;
373 pg
->last_bit
= de_getu32le_p(&pos
);
374 if(pg
->last_bit
>31) pg
->last_bit
=31;
375 de_dbg(c
, "first bit: %u", (UI
)pg
->first_bit
);
376 de_dbg(c
, "last bit: %u", (UI
)pg
->last_bit
);
378 image_offset_raw
= de_getu32le_p(&pos
);
379 pg
->image_offset
= pos1
+ image_offset_raw
;
380 de_dbg(c
, "image offset: %"I64_FMT
" ("DE_CHAR_RIGHTARROW
"%"I64_FMT
")",
381 image_offset_raw
, pg
->image_offset
);
383 mask_offset_raw
= de_getu32le_p(&pos
);
384 pg
->mask_offset
= pos1
+ mask_offset_raw
;
385 if(mask_offset_raw
&& (mask_offset_raw
!=image_offset_raw
)) {
387 pg
->use_mask
= 1; // Default
390 de_snprintf(descr
, sizeof(descr
), DE_CHAR_RIGHTARROW
"%"I64_FMT
,
394 de_strlcpy(descr
, "no mask", sizeof(descr
));
396 de_dbg(c
, "mask offset: %"I64_FMT
" (%s)", mask_offset_raw
, descr
);
398 pg
->mode
= (u32
)de_getu32le_p(&pos
);
399 de_dbg(c
, "mode: 0x%08x", (unsigned int)pg
->mode
);
403 pg
->new_img_type
= (pg
->mode
&0x78000000U
)>>27;
404 de_dbg(c
, "format version: %s", (pg
->new_img_type
?"new":"old"));
405 if(pg
->new_img_type
==0)
406 de_dbg(c
, "old format screen mode: %u", (UI
)pg
->mode
);
408 de_dbg(c
, "new format image type: %u", (UI
)pg
->new_img_type
);
410 if(pg
->new_img_type
!=0 && pg
->first_bit
!=0) {
411 de_warn(c
, "Invalid \"first bit\" value for new image format");
415 if(pg
->new_img_type
==0) {
419 for(x
=0; x
<DE_ARRAYCOUNT(old_mode_info_arr
); x
++) {
420 if(pg
->mode
== (u32
)old_mode_info_arr
[x
].mode
) {
421 pg
->fgbpp
= (i64
)old_mode_info_arr
[x
].fgbpp
;
422 pg
->xdpi
= (i64
)old_mode_info_arr
[x
].xdpi
;
423 pg
->ydpi
= (i64
)old_mode_info_arr
[x
].ydpi
;
429 de_err(c
, "Screen mode %u not supported", (UI
)pg
->mode
);
434 pg
->mask_type
= MASK_TYPE_OLD
;
435 pg
->mask_rowspan
= 4*pg
->width_in_words
;
436 pg
->maskbpp
= pg
->fgbpp
;
437 de_dbg(c
, "mask type: old");
440 if(pg
->fgbpp
>8 && pg
->has_mask
) {
441 de_warn(c
, "Transparency not supported for this image type");
448 pg
->xdpi
= (pg
->mode
&0x07ffc000)>>14;
449 pg
->ydpi
= (pg
->mode
&0x00003ffe)>>1;
450 de_dbg(c
, "dpi: %d"DE_CHAR_TIMES
"%d", (int)pg
->xdpi
, (int)pg
->ydpi
);
451 switch(pg
->new_img_type
) {
458 pg
->fgbpp
= 1LL<<(pg
->new_img_type
-1);
460 //case 7: 32bpp CMYK (TODO)
461 //case 8: 24bpp (TODO)
463 de_err(c
, "New format type %u not supported", (UI
)pg
->new_img_type
);
468 if(pg
->mode
&0x80000000U
) {
469 pg
->mask_type
= MASK_TYPE_NEW_8
;
473 pg
->mask_type
= MASK_TYPE_NEW_1
;
476 de_dbg(c
, "mask type: new - %s", pg
->mask_type
==MASK_TYPE_NEW_8
? "alpha" : "binary");
480 de_dbg(c
, "foreground bits/pixel: %d", (int)pg
->fgbpp
);
482 de_dbg_indent(c
, -1);
484 pg
->pdwidth
= (pg
->width_in_words
* 32)/pg
->fgbpp
;
485 pg
->num_padding_pixels_at_start_of_row
= pg
->first_bit
/ pg
->fgbpp
;
486 pg
->num_padding_pixels_at_end_of_row
= (32 - (pg
->last_bit
+1)) / pg
->fgbpp
;
487 pg
->npwidth
= pg
->pdwidth
- pg
->num_padding_pixels_at_start_of_row
-
488 pg
->num_padding_pixels_at_end_of_row
;
490 pg
->width_1
= pg
->pdwidth
;
493 pg
->width_1
= pg
->pdwidth
- pg
->num_padding_pixels_at_end_of_row
;
495 de_dbg(c
, "width (calculated): %"I64_FMT
, pg
->npwidth
);
497 if(!de_good_image_dimensions(c
, pg
->npwidth
, pg
->height
)) goto done
;
498 if(pg
->width_1
<1) goto done
;
500 if(pg
->mask_type
==MASK_TYPE_NEW_1
|| pg
->mask_type
==MASK_TYPE_NEW_8
) {
501 pg
->mask_rowspan
= de_pad_to_n(pg
->maskbpp
*pg
->npwidth
, 32) / 8;
504 de_dbg_indent(c
, -1);
506 pg
->custom_palette_pos
= pos1
+ 44;
507 if(pg
->image_offset
>= pg
->custom_palette_pos
+8 && pg
->fgbpp
<=8) {
508 pg
->has_custom_palette
= 1;
509 pg
->custom_palette_ncolors
= (pg
->image_offset
- (pos1
+44))/8;
510 if(pg
->custom_palette_ncolors
>256) pg
->custom_palette_ncolors
=256;
513 do_setup_palette(c
, d
, pg
);
515 do_image(c
, d
, pg
, fi
);
517 de_dbg_indent_restore(c
, saved_indent_level
);
518 de_finfo_destroy(c
, fi
);
520 if(pg
->img
) de_bitmap_destroy(pg
->img
);
521 if(pg
->mask
) de_bitmap_destroy(pg
->mask
);
526 static void de_run_rosprite(deark
*c
, de_module_params
*mparams
)
531 i64 first_sprite_offset_raw
;
532 i64 first_sprite_offset
;
533 i64 implied_file_size_raw
;
534 i64 implied_file_size
;
536 i64 sprite_count
= 0;
538 d
= de_malloc(c
, sizeof(lctx
));
541 d
->num_images
= de_getu32le_p(&pos
);
542 de_dbg(c
, "number of images: %"I64_FMT
, d
->num_images
);
543 first_sprite_offset_raw
= de_getu32le_p(&pos
);
544 first_sprite_offset
= first_sprite_offset_raw
- 4;
545 de_dbg(c
, "first sprite offset: %"I64_FMT
" ("DE_CHAR_RIGHTARROW
"%"I64_FMT
")",
546 first_sprite_offset_raw
, first_sprite_offset
);
547 implied_file_size_raw
= de_getu32le_p(&pos
);
548 implied_file_size
= implied_file_size_raw
- 4;
549 de_dbg(c
, "reported file size: %"I64_FMT
" ("DE_CHAR_RIGHTARROW
"%"I64_FMT
")",
550 implied_file_size_raw
, implied_file_size
);
551 if(implied_file_size
!= c
->infile
->len
) {
552 de_warn(c
, "Reported and actual file sizes differ "
553 "(%"I64_FMT
", %"I64_FMT
")", implied_file_size
, c
->infile
->len
);
556 pos
= first_sprite_offset
;
557 for(k
=0; k
<d
->num_images
; k
++) {
558 if(pos
>=c
->infile
->len
) goto done
;
559 sprite_size
= de_getu32le(pos
);
560 de_dbg(c
, "image #%d at %"I64_FMT
", size=%"I64_FMT
, (int)k
, pos
, sprite_size
);
561 if(sprite_size
<1) goto done
;
562 // We intentionally allow sprite_size to be set wrong, because
563 // such files exist, and we don't need it for *this* sprite.
565 do_sprite(c
, d
, k
, pos
, sprite_size
);
567 de_dbg_indent(c
, -1);
568 if(sprite_size
<40) goto done
;
573 if(sprite_count
< d
->num_images
) {
574 de_warn(c
, "Expected %"I64_FMT
" images, only found %"I64_FMT
,
575 d
->num_images
, sprite_count
);
580 static int de_identify_rosprite(deark
*c
)
582 i64 num_images
, first_sprite_offset
, implied_file_size
;
585 num_images
= de_getu32le(0);
586 if(num_images
<1 || num_images
>1000) return 0;
588 first_sprite_offset
= de_getu32le(4) - 4;
589 // I've only ever seen this be 12 or (rarely) 28, though it's legal
590 // for it to have other values
591 if(first_sprite_offset
!=12 && first_sprite_offset
!=28) return 0;
592 if(first_sprite_offset
+48 > c
->infile
->len
) return 0;
594 implied_file_size
= de_getu32le(8) - 4;
595 if(implied_file_size
> c
->infile
->len
) return 0;
598 // TODO?: A multi-sprite file with extra junk at EOF will not be detected.
599 // To better detect multi-sprite files, we'd probably have to walk through
601 if(implied_file_size
!= c
->infile
->len
) return 0;
605 offset2
= de_getu32le(first_sprite_offset
);
606 if(offset2
==implied_file_size
&& implied_file_size
==c
->infile
->len
) {
607 // Found some bad files where this pointer is absolute when it should be
611 offset2
+= first_sprite_offset
;
612 if(offset2
!= implied_file_size
) return 0;
613 if(implied_file_size
== c
->infile
->len
) return 80;
617 void de_module_rosprite(deark
*c
, struct deark_module_info
*mi
)
620 mi
->desc
= "RISC OS Sprite, a.k.a. Acorn Sprite";
621 mi
->run_fn
= de_run_rosprite
;
622 mi
->identify_fn
= de_identify_rosprite
;