hbmap: fix iterator truncation when size_t < 32bit
[rofl0r-agsutils.git] / Targa.h
blobfcc0bf81edc3c2652f7732d41955579b67e43552
1 #ifndef TARGA_H
2 #define TARGA_H
4 /* define generic and easily accessible image type, used
5 for targa conversion */
7 typedef struct ImageData {
8 short width;
9 short height;
10 short bytesperpixel;
11 unsigned data_size;
12 unsigned char* data;
13 } ImageData;
15 #ifndef TARGA_IMPL
16 #define TARGA_EXPORT extern
17 TARGA_EXPORT int
18 Targa_writefile(const char *name, ImageData* d, unsigned char *palette);
19 TARGA_EXPORT int
20 Targa_readfile(const char *name, ImageData *idata, int skip_palette);
22 #else
24 struct TargaHeader {
25 char idlength;
26 char colourmaptype;
27 char datatypecode;
28 short colourmaporigin;
29 short colourmaplength;
30 char colourmapdepth;
31 short x_origin;
32 short y_origin;
33 short width;
34 short height;
35 char bitsperpixel;
36 char imagedescriptor;
39 #define THO(f) THO_ ## f
40 enum TargaHeader_offsets {
41 THO(idlength) = 0,
42 THO(colourmaptype) = 1,
43 THO(datatypecode) = 2,
44 THO(colourmaporigin) = 3,
45 THO(colourmaplength) = 5,
46 THO(colourmapdepth) = 7,
47 THO(x_origin) = 8,
48 THO(y_origin) = 10,
49 THO(width) = 12,
50 THO(height) = 14,
51 THO(bitsperpixel) = 16,
52 THO(imagedescriptor) = 17,
55 static inline uint8_t read_header_field1(unsigned char *hdr, unsigned offset) {
56 return hdr[offset];
59 static inline uint16_t read_header_field2(unsigned char *hdr, unsigned offset) {
60 uint16_t tmp;
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) {
81 buf[off] = 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,
105 TIT_TRUE_COLOR = 2,
106 TIT_BLACK_WHITE = 3,
107 TIT_RLE_COLOR_MAPPED = 9,
108 TIT_RLE_TRUE_COLOR = 10,
109 TIT_RLE_BLACK_WHITE = 11,
112 struct TargaFooter {
113 unsigned extensionareaoffset;
114 unsigned developerdirectoryoffset;
115 char signature[16];
116 char dot;
117 char null;
121 #define STATIC_ASSERT(COND) static char static_assert_ ## __LINE__ [COND ? 1 : -1]
122 //STATIC_ASSERT(sizeof(struct TargaHeader) == 18);
124 #ifndef TARGA_EXPORT
125 #define TARGA_EXPORT static
126 #endif
128 #define TARGA_FOOTER_SIGNATURE "TRUEVISION-XFILE"
131 /* helper funcs */
133 #include <stdlib.h>
134 #include <stdio.h>
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));
148 if(!out) return 0;
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 */
152 unsigned jump_flag;
153 while(1) {
154 jump_flag = 0;
155 unsigned col[2] = {0};
156 if(togo) {
157 rle_read_col(col[0], count);
158 if(togo>1 && mode < 2) rle_read_col(col[1], count+1);
159 } else {
160 if(count) goto write_series;
161 else break;
163 switch(mode) {
164 case 0:
165 if(togo>1) {
166 if(col[0] == col[1]) {
167 start_rep:
168 mode = 2;
169 repcol = col[0];
170 } else {
171 start_series:
172 mode = 1;
174 count = 1;
175 } else {
176 goto start_series;
178 break;
179 case 1:
180 if(togo>1) {
181 if(col[0] == col[1]) {
182 jump_flag = 1;
183 goto write_series;
184 } else {
185 advance:
186 if(++count == 128) {
187 write_series:
188 *(p++) = ((mode - 1) << 7) | (count - 1);
189 if(mode == 1) for(i=0;i<count*bpp;++i)
190 *(p++) = *(q++);
191 else {
192 for(i=0;i<bpp*8;i+=8)
193 *(p++) = (repcol & (0xff << i)) >> i;
194 q += count * bpp;
196 if(!togo) goto done;
197 if(jump_flag == 1) goto start_rep;
198 if(jump_flag == 2) goto start_series;
199 mode = 0;
200 count = 0;
203 } else goto advance;
204 break;
205 case 2:
206 if(col[0] == repcol) goto advance;
207 else {
208 jump_flag = 2;
209 goto write_series;
212 togo--;
214 done:
215 *result = out;
216 return p-out;
218 #undef rle_read_col
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) {
224 unsigned char
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;
231 ++q;
232 if(rep) {
233 for(i = 0; i < bpp; ++i)
234 color = (color << 8) | *(q++);
235 for(i = 0; i < count && p+bpp <= p_e; ++i)
236 for(j=0; j<bpp; j++)
237 *(p++) = (color >> ((bpp-j-1)*8)) & 0xff;
238 } else {
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,
257 *q = d->data;
258 if(!out) return 0;
259 while(p < pe) {
260 unsigned r,g,b,lo,hi;
261 lo = *(q++);
262 hi = *(q++);
263 rgb565_to_888(lo, hi, &r, &g, &b);
264 *(p++) = b;
265 *(p++) = g;
266 *(p++) = r;
268 free(d->data);
269 d->data = out;
270 d->data_size = outsz;
271 d->bytesperpixel = 3;
272 return 1;
275 static int lookup_palette(unsigned color, unsigned *palette, int ncols)
277 int i;
278 for(i=0; i<ncols; ++i)
279 if(palette[i] == color) return i;
280 return -1;
283 static int ImageData_create_palette_pic(const ImageData* d, unsigned *palette, unsigned char **data)
285 int ret = 0;
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);
289 unsigned a = 0xff;
290 while(p < q && o < e) {
291 unsigned col, r, g, b;
292 switch(d->bytesperpixel) {
293 case 2: {
294 unsigned lo = *(p++);
295 unsigned hi = *(p++);
296 rgb565_to_888(lo, hi, &r, &g, &b);
297 break; }
298 case 3:
299 b = *(p++);
300 g = *(p++);
301 r = *(p++);
302 break;
303 case 4:
304 b = *(p++);
305 g = *(p++);
306 r = *(p++);
307 a = *(p++);
308 break;
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);
314 if(n < 0) {
315 if(ret == 256) {
316 nope:
317 free(*data);
318 *data = 0;
319 return -1;
321 n = ret;
322 palette[ret++] = col;
324 *(o++) = n;
326 return ret;
329 static void convert_bottom_left_tga(ImageData *d) {
330 size_t y, w = d->width*d->bytesperpixel;
331 unsigned char *swp = malloc(w);
332 if(!swp) return;
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);
339 free(swp);
344 /* exports */
345 TARGA_EXPORT int
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");
350 if(!f) return 0;
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);
362 fs -= ftello(f);
363 unsigned char *data = malloc(fs), *palette = 0;
364 unsigned palsz = 0;
365 fread(data, 1, fs, f);
366 if(hdr.colourmaptype) {
367 palette = data;
368 palsz = hdr.colourmaplength * hdr.colourmapdepth/8;
369 if(fs <= palsz) return 0;
370 data += palsz;
371 fs -= palsz;
373 unsigned char *workdata = 0;
374 unsigned tmp;
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);
381 break;
382 case TIT_COLOR_MAPPED:
383 case TIT_TRUE_COLOR:
384 workdata = data;
385 break;
386 default:
387 return 0;
389 idata->width = hdr.width;
390 idata->height = hdr.height;
391 if(skip_palette)
392 idata->bytesperpixel = hdr.bitsperpixel/8;
393 else
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];
408 } else {
409 memcpy(idata->data, workdata, tmp);
411 if(workdata != data) free(workdata);
412 if(palette) free(palette);
413 else free(data);
414 if(hdr.y_origin == 0) convert_bottom_left_tga(idata);
415 fclose(f);
416 return 1;
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. */
422 TARGA_EXPORT int
423 Targa_writefile(const char *name, ImageData* d, unsigned char *palette)
425 unsigned pal[256];
426 unsigned char *paldata = 0, *data = d->data;
427 unsigned bpp = d->bytesperpixel;
428 unsigned data_size = d->data_size;
429 int palcount = 256;
430 int i;
432 FILE *f = fopen(name, "wb");
433 if(!f) {
434 fprintf(stderr, "error opening %s\n", name);
435 return 0;
438 if(bpp == 1) {
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 */
449 bpp = 1;
450 data = paldata;
451 data_size = d->width*d->height;
452 } else if(bpp == 2 && ImageData_convert_16_to_24(d)) {
453 bpp = 3;
454 data = d->data;
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);
459 int use_rle = 0;
460 if(rle_data && rle_data_size < data_size) {
461 data_size = rle_data_size;
462 data = rle_data;
463 use_rle = 1;
465 struct TargaHeader hdr = {
466 .idlength = 0,
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,
474 .x_origin = 0,
475 .y_origin = d->height, /* image starts at the top */
476 .width = d->width,
477 .height = d->height,
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);
484 unsigned tmp;
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);
490 fclose(f);
491 free(paldata);
492 free(rle_data);
493 return 1;
496 #endif /* TARGA_IMPL */
499 #endif