1 // This file is part of Deark.
2 // Copyright (C) 2022 Jason Summers
3 // See the file COPYING for terms of use.
5 // SGI image / RGB / IRIS
7 #include <deark-private.h>
8 DE_DECLARE_MODULE(de_module_sgiimage
);
12 #define CMI_PALETTED 2
13 #define CMI_PALETTE_ONLY 3
15 struct sgiimage_offsettab_entry
{
21 de_encoding input_encoding
;
26 u32 colormap_id
; // CMI_*
40 static const char *get_cmprtype_name(u8 n
)
42 const char *name
= NULL
;
45 case 0: name
= "none"; break;
46 case 1: name
= "RLE"; break;
51 static const char *get_cmi_name(u32 n
)
53 const char *name
= NULL
;
56 case CMI_NORMAL
: name
= "normal image"; break;
57 case CMI_RGB332
: name
= "RGB332"; break;
58 case CMI_PALETTED
: name
= "paletted"; break;
59 case CMI_PALETTE_ONLY
: name
= "palette only"; break;
64 static UI
sgiimage_getsample_p(struct sgiimage_ctx
*d
, dbuf
*f
, i64
*ppos
)
68 if(d
->bytes_per_sample
==2) {
69 s
= (UI
)dbuf_getu16be_p(f
, ppos
);
72 s
= dbuf_getbyte_p(f
, ppos
);
77 static void sgiimage_decode_image(deark
*c
, struct sgiimage_ctx
*d
,
78 dbuf
*inf
, i64 pos1
, de_bitmap
*img
, de_bitmap
*imglo
)
84 for(pn
=0; pn
<d
->num_channels
; pn
++) {
87 if(d
->has_alpha
&& pn
==(d
->num_channels
-1))
92 for(j
=0; j
<d
->height
; j
++) {
93 for(i
=0; i
<d
->width
; i
++) {
96 s
= sgiimage_getsample_p(d
, inf
, &pos
);
99 de_bitmap_setsample(img
, i
, j
, samplenum
, (de_colorsample
)(s
>>8));
100 de_bitmap_setsample(imglo
, i
, j
, samplenum
, (s
&0xff));
103 de_bitmap_setsample(img
, i
, j
, samplenum
, (de_colorsample
)s
);
110 static void sgiimage_decompress_rle_scanline(deark
*c
,
111 struct sgiimage_ctx
*d
, i64 scanline_num
, i64 pos1
, i64 len
,
116 i64 num_dcmpr_bytes
= 0;
121 // Some files seem to set the offset to 0 for a nonexistent alpha channel.
123 if(!d
->warned_bad_offset
) {
124 de_warn(c
, "Bad offset at scanline %"I64_FMT
": %"I64_FMT
,
126 d
->warned_bad_offset
= 1;
135 if(curpos
>= endpos
) break; // end of input
136 if(num_dcmpr_bytes
>= d
->rowspan
) break; // sufficient output
138 n
= sgiimage_getsample_p(d
, c
->infile
, &curpos
);
139 count
= (i64
)(n
& 0x7f);
141 if(n
& 0x80) { // noncompressed run
142 dbuf_copy(c
->infile
, curpos
, count
*d
->bytes_per_sample
, unc_pixels
);
143 curpos
+= count
*d
->bytes_per_sample
;
148 n2
= sgiimage_getsample_p(d
, c
->infile
, &curpos
);
149 if(d
->bytes_per_sample
==2) {
152 for(k2
=0; k2
<count
; k2
++) {
153 dbuf_writeu16be(unc_pixels
, (i64
)n2
);
157 dbuf_write_run(unc_pixels
, (u8
)n2
, count
);
160 num_dcmpr_bytes
+= count
*d
->bytes_per_sample
;
167 static void sgiimage_decompress_rle(deark
*c
, struct sgiimage_ctx
*d
,
170 struct sgiimage_offsettab_entry
*offsettab
= NULL
;
173 i64 first_alpha_scanline
;
175 offsettab
= de_mallocarray(c
, d
->num_scanlines
,
176 sizeof(struct sgiimage_offsettab_entry
));
179 de_dbg(c
, "scanline table at %"I64_FMT
, pos
);
182 de_dbg(c
, "offsets at %"I64_FMT
, pos
);
183 for(i
=0; i
<d
->num_scanlines
; i
++) {
184 offsettab
[i
].pos
= (u32
)de_getu32be_p(&pos
);
186 de_dbg(c
, "lengths at %"I64_FMT
, pos
);
187 for(i
=0; i
<d
->num_scanlines
; i
++) {
188 offsettab
[i
].len
= (u32
)de_getu32be_p(&pos
);
190 de_dbg(c
, "table end: %"I64_FMT
, pos
);
192 if(c
->debug_level
>=2) {
193 for(i
=0; i
<d
->num_scanlines
; i
++) {
194 de_dbg2(c
, "scanline[%"I64_FMT
"]: offs=%u len=%u", i
,
195 (UI
)offsettab
[i
].pos
, (UI
)offsettab
[i
].len
);
198 de_dbg_indent(c
, -1);
201 first_alpha_scanline
= d
->height
* (d
->num_channels
-1);
204 first_alpha_scanline
= d
->num_scanlines
;
207 for(i
=0; i
<d
->num_scanlines
; i
++) {
211 sgiimage_decompress_rle_scanline(c
, d
, i
,
212 (i64
)offsettab
[i
].pos
, (i64
)offsettab
[i
].len
, unc_pixels
);
214 expected_ulen
= (i
+1) * d
->rowspan
;
215 actual_ulen
= dbuf_get_length(unc_pixels
);
216 if(actual_ulen
!= expected_ulen
) {
217 if((actual_ulen
< expected_ulen
) && i
>=first_alpha_scanline
) {
218 // If we didn't decompress enough bytes, don't default to 0 (invisible)
219 // if this is the alpha channel.
220 dbuf_write_run(unc_pixels
, 0xff, expected_ulen
- actual_ulen
);
223 dbuf_truncate(unc_pixels
, expected_ulen
);
228 dbuf_flush(unc_pixels
);
229 de_free(c
, offsettab
);
232 static void do_sgiimage_image(deark
*c
, struct sgiimage_ctx
*d
)
234 dbuf
*unc_pixels
= NULL
;
235 de_bitmap
*img
= NULL
;
236 de_bitmap
*imglo
= NULL
;
238 d
->is_grayscale
= (d
->num_channels
<=2);
239 d
->has_alpha
= (d
->num_channels
==2 || d
->num_channels
==4);
241 if(!de_good_image_dimensions(c
, d
->width
, d
->height
)) goto done
;
244 (d
->bytes_per_sample
==1 && d
->pix_max
<24) ||
245 (d
->bytes_per_sample
==2 && d
->pix_max
<24*256)))
247 // If pix_max is to be believed, this image likely needs its brightness
249 de_warn(c
, "This image might need special processing (not supported).");
252 img
= de_bitmap_create(c
, d
->width
, d
->height
, (int)d
->num_channels
);
253 if(d
->bytes_per_sample
==2) {
254 imglo
= de_bitmap_create(c
, d
->width
, d
->height
, (int)d
->num_channels
);
257 d
->rowspan
= d
->width
* d
->bytes_per_sample
;
258 d
->num_scanlines
= d
->height
* d
->num_channels
;
259 d
->total_unc_size
= d
->num_scanlines
* d
->rowspan
;
261 if(d
->storage_fmt
==0) { // Uncompressed
262 sgiimage_decode_image(c
, d
, c
->infile
, 512, img
, imglo
);
265 unc_pixels
= dbuf_create_membuf(c
, d
->total_unc_size
, 0);
266 dbuf_enable_wbuffer(unc_pixels
);
267 sgiimage_decompress_rle(c
, d
, unc_pixels
);
268 sgiimage_decode_image(c
, d
, unc_pixels
, 0, img
, imglo
);
271 // Remove the alpha channel if it seems bad
273 de_bitmap_optimize_alpha(img
, 0x4 | 0x2);
276 de_bitmap16_write_to_file_finfo(img
, imglo
, NULL
, DE_CREATEFLAG_FLIP_IMAGE
|
277 DE_CREATEFLAG_OPT_IMAGE
);
280 dbuf_close(unc_pixels
);
281 de_bitmap_destroy(img
);
282 de_bitmap_destroy(imglo
);
285 static void de_run_sgiimage(deark
*c
, de_module_params
*mparams
)
287 struct sgiimage_ctx
*d
= NULL
;
289 UI min_dim_count_expected
;
292 d
= de_malloc(c
, sizeof(struct sgiimage_ctx
));
293 d
->input_encoding
= de_get_input_encoding(c
, NULL
, DE_ENCODING_ASCII
);
296 d
->storage_fmt
= de_getbyte_p(&pos
);
297 de_dbg(c
, "compression: %u (%s)", (UI
)d
->storage_fmt
,
298 get_cmprtype_name(d
->storage_fmt
));
300 d
->bytes_per_sample
= (UI
)de_getbyte_p(&pos
);
301 de_dbg(c
, "bytes/sample: %u", d
->bytes_per_sample
);
303 d
->dimension_count
= (UI
)de_getu16be_p(&pos
);
304 de_dbg(c
, "dimension count: %u", d
->dimension_count
);
306 d
->width
= de_getu16be_p(&pos
);
307 de_dbg(c
, "x-size: %"I64_FMT
, d
->width
);
308 d
->height
= de_getu16be_p(&pos
);
309 de_dbg(c
, "y-size: %"I64_FMT
, d
->height
);
310 d
->num_channels
= de_getu16be_p(&pos
);
311 de_dbg(c
, "z-size: %u", (UI
)d
->num_channels
);
313 d
->pix_min
= (u32
)de_getu32be_p(&pos
);
314 d
->pix_max
= (u32
)de_getu32be_p(&pos
);
315 de_dbg(c
, "pix min, max: %u, %u", (UI
)d
->pix_min
, (UI
)d
->pix_max
);
316 // TODO?: Support normalizing the image brightness/contrast.
317 // Unfortunately, the spec is ambiguous about the meaning of these fields,
318 // and there's no simple logic that would always do the right thing.
322 d
->name
= ucstring_create(c
);
323 dbuf_read_to_ucstring(c
->infile
, pos
, 80, d
->name
, DE_CONVFLAG_STOP_AT_NUL
,
325 de_dbg(c
, "name: \"%s\"", ucstring_getpsz_d(d
->name
));
328 d
->colormap_id
= (u32
)de_getu32be_p(&pos
);
329 de_dbg(c
, "colormap code: %u (%s)", (UI
)d
->colormap_id
,
330 get_cmi_name(d
->colormap_id
));
332 if(d
->storage_fmt
>1) {
337 if(d
->colormap_id
!=CMI_NORMAL
) {
338 // TODO: Support other image types?
343 if(d
->bytes_per_sample
!=1 && d
->bytes_per_sample
!=2) {
348 // We're hoping that unused fields will be set to 0 or 1.
349 if(d
->width
==0) d
->width
= 1;
350 if(d
->height
==0) d
->height
= 1;
351 if(d
->num_channels
==0) d
->num_channels
= 1;
353 if(d
->num_channels
>1) min_dim_count_expected
= 3;
354 else if(d
->height
>1) min_dim_count_expected
= 2;
355 else min_dim_count_expected
= 1;
357 if(d
->dimension_count
<min_dim_count_expected
|| d
->dimension_count
>3) {
358 de_warn(c
, "Likely bad dimension count (is %u, assuming it should be %u)",
359 d
->dimension_count
, min_dim_count_expected
);
361 // Note that, other than for the above warning, we ignore dimension_count.
362 // The x-size, y-size, z-size fields seem to be more reliable than it is.
364 if(d
->num_channels
<1 || d
->num_channels
>4) {
369 do_sgiimage_image(c
, d
);
373 de_err(c
, "This type of SGI image is not supported");
376 ucstring_destroy(d
->name
);
381 static int de_identify_sgiimage(deark
*c
)
385 if(c
->infile
->len
<513) return 0;
387 n
= (UI
)de_getu16be(0); // MAGIC
390 n
= (UI
)de_getbyte(2); // "STORAGE"
393 n
= (UI
)de_getbyte(3); // "BPC"
394 if(n
<1 || n
>2) return 0;
396 n
= (UI
)de_getu16be(4); // "DIMENSION"
397 // Only 1-3 are legal, but we allow 0-4 because this field is confusing
398 // and underspecified.
401 n
= (UI
)de_getu16be(10); // "ZSIZE"
402 // Allow 0 because this field may be unused for some image types.
408 void de_module_sgiimage(deark
*c
, struct deark_module_info
*mi
)
411 mi
->desc
= "SGI image";
412 mi
->run_fn
= de_run_sgiimage
;
413 mi
->identify_fn
= de_identify_sgiimage
;