4 /* define generic and easily accessible image type, used
5 for targa conversion */
7 typedef struct ImageData
{
16 #define TARGA_EXPORT extern
18 Targa_writefile(const char *name
, ImageData
* d
, unsigned char *palette
);
20 Targa_readfile(const char *name
, ImageData
*idata
, int skip_palette
);
28 short colourmaporigin
;
29 short colourmaplength
;
39 #define THO(f) THO_ ## f
40 enum TargaHeader_offsets
{
42 THO(colourmaptype
) = 1,
43 THO(datatypecode
) = 2,
44 THO(colourmaporigin
) = 3,
45 THO(colourmaplength
) = 5,
46 THO(colourmapdepth
) = 7,
51 THO(bitsperpixel
) = 16,
52 THO(imagedescriptor
) = 17,
55 static inline uint8_t read_header_field1(unsigned char *hdr
, unsigned offset
) {
59 static inline uint16_t read_header_field2(unsigned char *hdr
, unsigned offset
) {
61 memcpy(&tmp
, hdr
+offset
, 2);
62 return end_le16toh(tmp
);
65 static void TargaHeader_from_buf(struct TargaHeader
*hdr
, unsigned char* hdr_buf
) {
66 hdr
->idlength
= read_header_field1(hdr_buf
, THO(idlength
));
67 hdr
->colourmaptype
= read_header_field1(hdr_buf
, THO(colourmaptype
));
68 hdr
->datatypecode
= read_header_field1(hdr_buf
, THO(datatypecode
));
69 hdr
->colourmaporigin
= read_header_field2(hdr_buf
, THO(colourmaporigin
));
70 hdr
->colourmaplength
= read_header_field2(hdr_buf
, THO(colourmaplength
));
71 hdr
->colourmapdepth
= read_header_field1(hdr_buf
, THO(colourmapdepth
));
72 hdr
->x_origin
= read_header_field2(hdr_buf
, THO(x_origin
));
73 hdr
->y_origin
= read_header_field2(hdr_buf
, THO(y_origin
));
74 hdr
->width
= read_header_field2(hdr_buf
, THO(width
));
75 hdr
->height
= read_header_field2(hdr_buf
, THO(height
));
76 hdr
->bitsperpixel
= read_header_field1(hdr_buf
, THO(bitsperpixel
));
77 hdr
->imagedescriptor
= read_header_field1(hdr_buf
, THO(imagedescriptor
));
80 static inline void write_header_field1(unsigned char* buf
, unsigned off
, uint8_t v
) {
83 static inline void write_header_field2(unsigned char* buf
, unsigned off
, uint16_t v
) {
84 uint16_t tmp
= end_htole16(v
);
85 memcpy(buf
+off
, &tmp
, 2);
88 static void TargaHeader_to_buf(struct TargaHeader
*hdr
, unsigned char* hdr_buf
) {
89 write_header_field1(hdr_buf
, THO(idlength
), hdr
->idlength
);
90 write_header_field1(hdr_buf
, THO(colourmaptype
), hdr
->colourmaptype
);
91 write_header_field1(hdr_buf
, THO(datatypecode
), hdr
->datatypecode
);
92 write_header_field2(hdr_buf
, THO(colourmaporigin
), hdr
->colourmaporigin
);
93 write_header_field2(hdr_buf
, THO(colourmaplength
), hdr
->colourmaplength
);
94 write_header_field1(hdr_buf
, THO(colourmapdepth
), hdr
->colourmapdepth
);
95 write_header_field2(hdr_buf
, THO(x_origin
), hdr
->x_origin
);
96 write_header_field2(hdr_buf
, THO(y_origin
), hdr
->y_origin
);
97 write_header_field2(hdr_buf
, THO(width
), hdr
->width
);
98 write_header_field2(hdr_buf
, THO(height
), hdr
->height
);
99 write_header_field1(hdr_buf
, THO(bitsperpixel
), hdr
->bitsperpixel
);
100 write_header_field1(hdr_buf
, THO(imagedescriptor
), hdr
->imagedescriptor
);
103 enum TargaImageType
{
104 TIT_COLOR_MAPPED
= 1,
107 TIT_RLE_COLOR_MAPPED
= 9,
108 TIT_RLE_TRUE_COLOR
= 10,
109 TIT_RLE_BLACK_WHITE
= 11,
113 unsigned extensionareaoffset
;
114 unsigned developerdirectoryoffset
;
121 #define STATIC_ASSERT(COND) static char static_assert_ ## __LINE__ [COND ? 1 : -1]
122 //STATIC_ASSERT(sizeof(struct TargaHeader) == 18);
125 #define TARGA_EXPORT static
128 #define TARGA_FOOTER_SIGNATURE "TRUEVISION-XFILE"
135 #include "endianness.h"
137 #define rle_read_col(OUT, IDX) \
138 for(OUT=0, i=0; i<bpp;++i) {OUT |= (q[bpp*(IDX)+i] << (i*8));}
139 static unsigned rle_encode(
140 unsigned char *data
, unsigned data_size
,
141 unsigned bpp
, unsigned char** result
)
143 /* worst case length: entire file consisting of sequences of 2
144 identical, and one different pixel, resulting in
145 1 byte flag + 1 pixel + 1 byte flag + 1 pixel. in the case of
146 8 bit, that's 1 byte overhead every 3 pixels. */
147 unsigned char *out
= malloc(data_size
+ 1 + (data_size
/bpp
/3));
149 unsigned char *p
= out
, *q
= data
;
150 unsigned i
, count
= 0, togo
= data_size
/bpp
, repcol
= 0;
151 unsigned mode
= 0; /* 0: stateless, 1: series: 2: repetition */
155 unsigned col
[2] = {0};
157 rle_read_col(col
[0], count
);
158 if(togo
>1 && mode
< 2) rle_read_col(col
[1], count
+1);
160 if(count
) goto write_series
;
166 if(col
[0] == col
[1]) {
181 if(col
[0] == col
[1]) {
188 *(p
++) = ((mode
- 1) << 7) | (count
- 1);
189 if(mode
== 1) for(i
=0;i
<count
*bpp
;++i
)
192 for(i
=0;i
<bpp
*8;i
+=8)
193 *(p
++) = (repcol
& (0xff << i
)) >> i
;
197 if(jump_flag
== 1) goto start_rep
;
198 if(jump_flag
== 2) goto start_series
;
206 if(col
[0] == repcol
) goto advance
;
220 /* caller needs to provide result buffer of w*h*bpp size. */
221 static void rle_decode(
222 unsigned char *data
, unsigned data_size
,
223 unsigned bpp
, unsigned char* result
, unsigned result_size
) {
225 *p
= result
, *p_e
= p
+ result_size
,
226 *q
= data
, *q_e
= q
+ data_size
;
227 while(q
+1+bpp
<= q_e
) {
228 unsigned count
= (*q
& 127) + 1;
229 unsigned rep
= *q
& 128;
230 unsigned color
= 0, i
, j
;
233 for(i
= 0; i
< bpp
; ++i
)
234 color
= (color
<< 8) | *(q
++);
235 for(i
= 0; i
< count
&& p
+bpp
<= p_e
; ++i
)
237 *(p
++) = (color
>> ((bpp
-j
-1)*8)) & 0xff;
239 for(i
= 0; i
< count
&& p
+bpp
<= p_e
&& q
+bpp
<= q_e
; ++i
)
240 for(j
=0; j
<bpp
; j
++) *(p
++) = *(q
++);
246 static void rgb565_to_888(unsigned lo
, unsigned hi
, unsigned *r
, unsigned *g
, unsigned *b
)
248 *r
= (hi
& ~7) | (hi
>> 5);
249 *g
= ((hi
& 7) << 5) | ((lo
& 224) >> 3) | ((hi
& 7) >> 1);
250 *b
= ((lo
& 31) << 3) | ((lo
& 28) >> 2);
253 static int ImageData_convert_16_to_24(ImageData
*d
) {
254 size_t outsz
= d
->width
*d
->height
*3UL;
255 unsigned char *out
= malloc(outsz
),
256 *p
= out
, *pe
= out
+ outsz
,
260 unsigned r
,g
,b
,lo
,hi
;
263 rgb565_to_888(lo
, hi
, &r
, &g
, &b
);
270 d
->data_size
= outsz
;
271 d
->bytesperpixel
= 3;
275 static int lookup_palette(unsigned color
, unsigned *palette
, int ncols
)
278 for(i
=0; i
<ncols
; ++i
)
279 if(palette
[i
] == color
) return i
;
283 static int ImageData_create_palette_pic(const ImageData
* d
, unsigned *palette
, unsigned char **data
)
286 unsigned char *p
= d
->data
, *q
= p
+ d
->data_size
;
287 *data
= malloc(d
->width
* d
->height
);
288 unsigned char *o
= *data
, *e
= o
+ (d
->width
* d
->height
);
290 while(p
< q
&& o
< e
) {
291 unsigned col
, r
, g
, b
;
292 switch(d
->bytesperpixel
) {
294 unsigned lo
= *(p
++);
295 unsigned hi
= *(p
++);
296 rgb565_to_888(lo
, hi
, &r
, &g
, &b
);
309 default: b
=g
=r
=0; break;
311 if(a
!= 0xff) goto nope
;
312 col
= a
<< 24 | r
<< 16 | g
<< 8 | b
;
313 int n
= lookup_palette(col
, palette
, ret
);
322 palette
[ret
++] = col
;
329 static void convert_bottom_left_tga(ImageData
*d
) {
330 size_t y
, w
= d
->width
*d
->bytesperpixel
;
331 unsigned char *swp
= malloc(w
);
333 for(y
= 0; y
< d
->height
/2; ++y
) {
334 size_t to
= w
*y
, bo
= (d
->height
-1-y
)*w
;
335 memcpy(swp
, d
->data
+ to
, w
);
336 memcpy(d
->data
+ to
, d
->data
+ bo
, w
);
337 memcpy(d
->data
+ bo
, swp
, w
);
346 Targa_readfile(const char *name
, ImageData
*idata
, int skip_palette
) {
347 struct TargaHeader hdr
; unsigned char hdr_buf
[18];
348 unsigned char ftr_buf
[18+8];
349 FILE *f
= fopen(name
, "rb");
351 fread(&hdr_buf
, 1, sizeof(hdr_buf
), f
);
352 fseek(f
, 0, SEEK_END
);
353 off_t fs
= ftello(f
);
354 if(fs
> sizeof ftr_buf
) {
355 fseek(f
, 0-sizeof ftr_buf
, SEEK_END
);
356 fread(ftr_buf
, 1, sizeof ftr_buf
, f
);
357 if(!memcmp(ftr_buf
+8, TARGA_FOOTER_SIGNATURE
, 16))
358 fs
-= sizeof ftr_buf
;
360 TargaHeader_from_buf(&hdr
, hdr_buf
);
361 fseek(f
, sizeof(hdr_buf
) + hdr
.idlength
, SEEK_SET
);
363 unsigned char *data
= malloc(fs
), *palette
= 0;
365 fread(data
, 1, fs
, f
);
366 if(hdr
.colourmaptype
) {
368 palsz
= hdr
.colourmaplength
* hdr
.colourmapdepth
/8;
369 if(fs
<= palsz
) return 0;
373 unsigned char *workdata
= 0;
375 switch(hdr
.datatypecode
) {
376 case TIT_RLE_COLOR_MAPPED
:
377 case TIT_RLE_TRUE_COLOR
:
378 tmp
= hdr
.width
*hdr
.height
*hdr
.bitsperpixel
/8;
379 workdata
= malloc(tmp
);
380 rle_decode(data
, fs
, hdr
.bitsperpixel
/8, workdata
, tmp
);
382 case TIT_COLOR_MAPPED
:
389 idata
->width
= hdr
.width
;
390 idata
->height
= hdr
.height
;
392 idata
->bytesperpixel
= hdr
.bitsperpixel
/8;
394 idata
->bytesperpixel
= hdr
.colourmapdepth
? hdr
.colourmapdepth
/8 : hdr
.bitsperpixel
/8;
396 tmp
= idata
->width
*idata
->height
*idata
->bytesperpixel
;
397 idata
->data_size
= tmp
;
398 idata
->data
= malloc(tmp
);
399 if(palette
&& !skip_palette
) {
400 unsigned i
, j
, bpp
= hdr
.colourmapdepth
/8;
401 unsigned char *p
= idata
->data
, *q
= workdata
;
402 for(i
=0; i
< idata
->width
*idata
->height
; ++i
) {
403 unsigned idx
= *(q
++);
404 if(idx
>= hdr
.colourmaplength
) return 0;
405 for(j
=0; j
< bpp
; ++j
)
406 *(p
++) = palette
[idx
*bpp
+j
];
409 memcpy(idata
->data
, workdata
, tmp
);
411 if(workdata
!= data
) free(workdata
);
412 if(palette
) free(palette
);
414 if(hdr
.y_origin
== 0) convert_bottom_left_tga(idata
);
419 /* palette needs to be either 24bit rgb data of 256*3 bytes, or NULL.
420 it's only used if imagedata.bytesperpixel is 1. in this case, if
421 NULL, a random palette is used. */
423 Targa_writefile(const char *name
, ImageData
* d
, unsigned char *palette
)
426 unsigned char *paldata
= 0, *data
= d
->data
;
427 unsigned bpp
= d
->bytesperpixel
;
428 unsigned data_size
= d
->data_size
;
432 FILE *f
= fopen(name
, "wb");
434 fprintf(stderr
, "error opening %s\n", name
);
439 if(!palette
) for(i
=0; i
<256; ++i
) pal
[i
] = rand();
440 else for(i
=0; i
<256; ++i
) {
441 unsigned r
= *(palette
++);
442 unsigned g
= *(palette
++);
443 unsigned b
= *(palette
++);
444 pal
[i
] = r
<< 16 | g
<< 8 | b
;
446 } else if( /* bpp != 2 && */
447 (palcount
= ImageData_create_palette_pic(d
, pal
, &paldata
)) > 0) {
448 /* can be saved as 8 bit palettized image */
451 data_size
= d
->width
*d
->height
;
452 } else if(bpp
== 2 && ImageData_convert_16_to_24(d
)) {
455 data_size
= d
->data_size
;
457 unsigned char *rle_data
= 0;
458 unsigned rle_data_size
= rle_encode(data
, data_size
, bpp
, &rle_data
);
460 if(rle_data
&& rle_data_size
< data_size
) {
461 data_size
= rle_data_size
;
465 struct TargaHeader hdr
= {
467 .colourmaptype
= bpp
== 1 ? 1 : 0,
468 .datatypecode
= bpp
== 1 ?
469 (use_rle
? TIT_RLE_COLOR_MAPPED
: TIT_COLOR_MAPPED
) :
470 (use_rle
? TIT_RLE_TRUE_COLOR
: TIT_TRUE_COLOR
),
471 .colourmaporigin
= 0,
472 .colourmaplength
= bpp
== 1 ? palcount
: 0,
473 .colourmapdepth
= bpp
== 1 ? 24 : 0,
475 .y_origin
= d
->height
, /* image starts at the top */
478 .bitsperpixel
= bpp
*8,
479 .imagedescriptor
= 0x20, /* image starts at the top */
481 unsigned char hdr_buf
[18];
482 TargaHeader_to_buf(&hdr
, hdr_buf
);
483 fwrite(&hdr_buf
, 1, sizeof hdr_buf
, f
);
485 if(bpp
== 1) for(i
=0; i
<palcount
; ++i
) {
486 tmp
= end_htole32(pal
[i
]);
487 fwrite(&tmp
, 1, 3, f
);
489 fwrite(data
, 1, data_size
, f
);
496 #endif /* TARGA_IMPL */