1
2
3 /* ---------------------------------------------------------------------------
4 * Truevision Targa Reader/Writer
5 * Copyright (C) 2001-2003, Emil Mikulic.
6 *
7 * Source and binary redistribution of this code, with or without changes, for
8 * free or for profit, is allowed as long as this copyright notice is kept
9 * intact. Modified versions must be clearly marked as modified.
10 *
11 * This code is provided without any warranty. The copyright holder is
12 * not liable for anything bad that might happen as a result of the
13 * code.
14 * -------------------------------------------------------------------------*/
15
16 /*
17 * preso da http://dmr.ath.cx/gfx/targa/
18 * modificato da gmt
19 *
20 */
21
22 /*@unused@*/ static const char rcsid[] =
23 "$Id: targa.c,v 1.7 2003/06/21 09:30:53 emikulic Exp $";
24
25 #define TGA_KEEP_MACROS /* BIT, htole16, letoh16 */
26 #include "targa.h"
27 #include <stdlib.h>
28 #include <string.h> /* memcpy, memcmp */
29
30 #define SANE_DEPTH(x) ((x) == 8 || (x) == 16 || (x) == 24 || (x) == 32)
31 #define UNMAP_DEPTH(x) ((x) == 16 || (x) == 24 || (x) == 32)
32
33 static const char tga_id[] =
34 "\0\0\0\0" /* extension area offset */
35 "\0\0\0\0" /* developer directory offset */
36 "TRUEVISION-XFILE.";
37
38 static const size_t tga_id_length = 26; /* tga_id + \0 */
39
40 /* helpers */
41 static tga_result tga_read_rle(tga_image *dest, FILE *fp);
42 static tga_result tga_write_row_RLE(FILE *fp, const tga_image *src,
43 const uint8_t *row);
44 typedef enum { RAW, RLE } packet_type;
45 static packet_type rle_packet_type(const uint8_t *row, const uint16_t pos,
46 const uint16_t width, const uint16_t bpp);
47 static uint8_t rle_packet_len(const uint8_t *row, const uint16_t pos,
48 const uint16_t width, const uint16_t bpp,
49 const packet_type type);
50
tga_get_attribute_bits(const tga_image * tga)51 uint8_t tga_get_attribute_bits(const tga_image *tga) {
52 return tga->image_descriptor & TGA_ATTRIB_BITS;
53 }
54
tga_is_right_to_left(const tga_image * tga)55 int tga_is_right_to_left(const tga_image *tga) {
56 return (tga->image_descriptor & TGA_R_TO_L_BIT) != 0;
57 }
58
tga_is_top_to_bottom(const tga_image * tga)59 int tga_is_top_to_bottom(const tga_image *tga) {
60 return (tga->image_descriptor & TGA_T_TO_B_BIT) != 0;
61 }
62
tga_is_colormapped(const tga_image * tga)63 int tga_is_colormapped(const tga_image *tga) {
64 return (tga->image_type == TGA_IMAGE_TYPE_COLORMAP ||
65 tga->image_type == TGA_IMAGE_TYPE_COLORMAP_RLE);
66 }
67
tga_is_rle(const tga_image * tga)68 int tga_is_rle(const tga_image *tga) {
69 return (tga->image_type == TGA_IMAGE_TYPE_COLORMAP_RLE ||
70 tga->image_type == TGA_IMAGE_TYPE_BGR_RLE ||
71 tga->image_type == TGA_IMAGE_TYPE_MONO_RLE);
72 }
73
tga_is_mono(const tga_image * tga)74 int tga_is_mono(const tga_image *tga) {
75 return (tga->image_type == TGA_IMAGE_TYPE_MONO ||
76 tga->image_type == TGA_IMAGE_TYPE_MONO_RLE);
77 }
78
79 /* ---------------------------------------------------------------------------
80 * Convert the numerical <errcode> into a verbose error string.
81 *
82 * Returns: an error string
83 */
tga_error(const tga_result errcode)84 const char *tga_error(const tga_result errcode) {
85 switch (errcode) {
86 case TGA_NOERR:
87 return "no error";
88 case TGAERR_FOPEN:
89 return "error opening file";
90 case TGAERR_EOF:
91 return "premature end of file";
92 case TGAERR_WRITE:
93 return "error writing to file";
94 case TGAERR_CMAP_TYPE:
95 return "invalid color map type";
96 case TGAERR_IMG_TYPE:
97 return "invalid image type";
98 case TGAERR_NO_IMG:
99 return "no image data included";
100 case TGAERR_CMAP_MISSING:
101 return "color-mapped image without color map";
102 case TGAERR_CMAP_PRESENT:
103 return "non-color-mapped image with extraneous color map";
104 case TGAERR_CMAP_LENGTH:
105 return "color map has zero length";
106 case TGAERR_CMAP_DEPTH:
107 return "invalid color map depth";
108 case TGAERR_ZERO_SIZE:
109 return "the image dimensions are zero";
110 case TGAERR_PIXEL_DEPTH:
111 return "invalid pixel depth";
112 case TGAERR_NO_MEM:
113 return "out of memory";
114 case TGAERR_NOT_CMAP:
115 return "image is not color mapped";
116 case TGAERR_RLE:
117 return "RLE data is corrupt";
118 case TGAERR_INDEX_RANGE:
119 return "color map index out of range";
120 case TGAERR_MONO:
121 return "image is mono";
122 default:
123 return "unknown error code";
124 }
125 }
126
127 /* ---------------------------------------------------------------------------
128 * Read a Targa image from a file named <filename> to <dest>. This is just a
129 * wrapper around tga_read_from_FILE().
130 *
131 * Returns: TGA_NOERR on success, or a matching TGAERR_* code on failure.
132 */
tga_read(tga_image * dest,const char * filename)133 tga_result tga_read(tga_image *dest, const char *filename) {
134 tga_result result;
135 FILE *fp = fopen(filename, "rb");
136 if (fp == NULL) return TGAERR_FOPEN;
137 result = tga_read_from_FILE(dest, fp);
138 fclose(fp);
139 return result;
140 }
141
142 /* ---------------------------------------------------------------------------
143 * Read a Targa image from <fp> to <dest>.
144 *
145 * Returns: TGA_NOERR on success, or a TGAERR_* code on failure. In the
146 * case of failure, the contents of dest are not guaranteed to be
147 * valid.
148 */
tga_read_from_FILE(tga_image * dest,FILE * fp)149 tga_result tga_read_from_FILE(tga_image *dest, FILE *fp) {
150 #define BARF(errcode) \
151 { \
152 tga_free_buffers(dest); \
153 return errcode; \
154 }
155
156 #define READ(destptr, size) \
157 if (fread(destptr, size, 1, fp) != 1) BARF(TGAERR_EOF)
158
159 #define READ16(dest) \
160 { \
161 if (fread(&(dest), 2, 1, fp) != 1) BARF(TGAERR_EOF); \
162 dest = letoh16(dest); \
163 }
164
165 dest->image_id = NULL;
166 dest->color_map_data = NULL;
167 dest->image_data = NULL;
168
169 READ(&dest->image_id_length, 1);
170 READ(&dest->color_map_type, 1);
171 if (dest->color_map_type != TGA_COLOR_MAP_ABSENT &&
172 dest->color_map_type != TGA_COLOR_MAP_PRESENT)
173 BARF(TGAERR_CMAP_TYPE);
174
175 READ(&dest->image_type, 1);
176 if (dest->image_type == TGA_IMAGE_TYPE_NONE) BARF(TGAERR_NO_IMG);
177
178 if (dest->image_type != TGA_IMAGE_TYPE_COLORMAP &&
179 dest->image_type != TGA_IMAGE_TYPE_BGR &&
180 dest->image_type != TGA_IMAGE_TYPE_MONO &&
181 dest->image_type != TGA_IMAGE_TYPE_COLORMAP_RLE &&
182 dest->image_type != TGA_IMAGE_TYPE_BGR_RLE &&
183 dest->image_type != TGA_IMAGE_TYPE_MONO_RLE)
184 BARF(TGAERR_IMG_TYPE);
185
186 if (tga_is_colormapped(dest) && dest->color_map_type == TGA_COLOR_MAP_ABSENT)
187 BARF(TGAERR_CMAP_MISSING);
188
189 if (!tga_is_colormapped(dest) &&
190 dest->color_map_type == TGA_COLOR_MAP_PRESENT)
191 BARF(TGAERR_CMAP_PRESENT);
192
193 READ16(dest->color_map_origin);
194 READ16(dest->color_map_length);
195 READ(&dest->color_map_depth, 1);
196 if (dest->color_map_type == TGA_COLOR_MAP_PRESENT) {
197 if (dest->color_map_length == 0) BARF(TGAERR_CMAP_LENGTH);
198
199 if (!UNMAP_DEPTH(dest->color_map_depth)) BARF(TGAERR_CMAP_DEPTH);
200 }
201
202 READ16(dest->origin_x);
203 READ16(dest->origin_y);
204 READ16(dest->width);
205 READ16(dest->height);
206
207 if (dest->width == 0 || dest->height == 0) BARF(TGAERR_ZERO_SIZE);
208
209 READ(&dest->pixel_depth, 1);
210 if (!SANE_DEPTH(dest->pixel_depth) ||
211 (dest->pixel_depth != 8 && tga_is_colormapped(dest)))
212 BARF(TGAERR_PIXEL_DEPTH);
213
214 READ(&dest->image_descriptor, 1);
215
216 if (dest->image_id_length > 0) {
217 dest->image_id = (uint8_t *)malloc(dest->image_id_length);
218 if (dest->image_id == NULL) BARF(TGAERR_NO_MEM);
219 READ(dest->image_id, dest->image_id_length);
220 }
221
222 if (dest->color_map_type == TGA_COLOR_MAP_PRESENT) {
223 dest->color_map_data =
224 (uint8_t *)malloc((dest->color_map_origin + dest->color_map_length) *
225 dest->color_map_depth / 8);
226 if (dest->color_map_data == NULL) BARF(TGAERR_NO_MEM);
227 READ(dest->color_map_data +
228 (dest->color_map_origin * dest->color_map_depth / 8),
229 dest->color_map_length * dest->color_map_depth / 8);
230 }
231
232 dest->image_data =
233 (uint8_t *)malloc(dest->width * dest->height * dest->pixel_depth / 8);
234 if (dest->image_data == NULL) BARF(TGAERR_NO_MEM);
235
236 if (tga_is_rle(dest)) {
237 /* read RLE */
238 tga_result result = tga_read_rle(dest, fp);
239 if (result != TGA_NOERR) BARF(result);
240 } else {
241 /* uncompressed */
242 READ(dest->image_data, dest->width * dest->height * dest->pixel_depth / 8);
243 }
244
245 return TGA_NOERR;
246 #undef BARF
247 #undef READ
248 #undef READ16
249 }
250
251 /* ---------------------------------------------------------------------------
252 * Helper function for tga_read_from_FILE(). Decompresses RLE image data from
253 * <fp>. Assumes <dest> header fields are set correctly.
254 */
tga_read_rle(tga_image * dest,FILE * fp)255 static tga_result tga_read_rle(tga_image *dest, FILE *fp) {
256 #define RLE_BIT BIT(7)
257 #define READ(dest, size) \
258 if (fread(dest, size, 1, fp) != 1) return TGAERR_EOF
259
260 uint8_t *pos;
261 uint32_t p_loaded = 0, p_expected = dest->width * dest->height;
262 uint8_t bpp = dest->pixel_depth / 8; /* bytes per pixel */
263
264 pos = dest->image_data;
265
266 while ((p_loaded < p_expected) && !feof(fp)) {
267 uint8_t b;
268 READ(&b, 1);
269 if (b & RLE_BIT) {
270 /* is an RLE packet */
271 uint8_t count, tmp[4], i;
272
273 count = (b & ~RLE_BIT) + 1;
274 READ(tmp, bpp);
275
276 for (i = 0; i < count; i++) {
277 p_loaded++;
278 if (p_loaded > p_expected) return TGAERR_RLE;
279 memcpy(pos, tmp, bpp);
280 pos += bpp;
281 }
282 } else /* RAW packet */
283 {
284 uint8_t count;
285
286 count = (b & ~RLE_BIT) + 1;
287 if (p_loaded + count > p_expected) return TGAERR_RLE;
288
289 p_loaded += count;
290 READ(pos, bpp * count);
291 pos += count * bpp;
292 }
293 }
294 return TGA_NOERR;
295 #undef RLE_BIT
296 #undef READ
297 }
298
299 /* ---------------------------------------------------------------------------
300 * Write a Targa image to a file named <filename> from <src>. This is just a
301 * wrapper around tga_write_to_FILE().
302 *
303 * Returns: TGA_NOERR on success, or a matching TGAERR_* code on failure.
304 */
tga_write(const char * filename,const tga_image * src)305 tga_result tga_write(const char *filename, const tga_image *src) {
306 tga_result result;
307 FILE *fp = fopen(filename, "wb");
308 if (fp == NULL) return TGAERR_FOPEN;
309 result = tga_write_to_FILE(fp, src);
310 fclose(fp);
311 return result;
312 }
313
314 /* ---------------------------------------------------------------------------
315 * Write one row of an image to <fp> using RLE. This is a helper function
316 * called from tga_write_to_FILE(). It assumes that <src> has its header
317 * fields set up correctly.
318 */
319 #define PIXEL(ofs) (row + (ofs)*bpp)
tga_write_row_RLE(FILE * fp,const tga_image * src,const uint8_t * row)320 static tga_result tga_write_row_RLE(FILE *fp, const tga_image *src,
321 const uint8_t *row) {
322 #define WRITE(src, size) \
323 if (fwrite(src, size, 1, fp) != 1) return TGAERR_WRITE
324
325 uint16_t pos = 0;
326 uint16_t bpp = src->pixel_depth / 8;
327
328 while (pos < src->width) {
329 packet_type type = rle_packet_type(row, pos, src->width, bpp);
330 uint8_t len = rle_packet_len(row, pos, src->width, bpp, type);
331 uint8_t packet_header;
332
333 packet_header = len - 1;
334 if (type == RLE) packet_header |= BIT(7);
335
336 WRITE(&packet_header, 1);
337 if (type == RLE) {
338 WRITE(PIXEL(pos), bpp);
339 } else /* type == RAW */
340 {
341 WRITE(PIXEL(pos), bpp * len);
342 }
343
344 pos += len;
345 }
346
347 return TGA_NOERR;
348 #undef WRITE
349 }
350
351 /* ---------------------------------------------------------------------------
352 * Determine whether the next packet should be RAW or RLE for maximum
353 * efficiency. This is a helper function called from rle_packet_len() and
354 * tga_write_row_RLE().
355 */
356 #define SAME(ofs1, ofs2) (memcmp(PIXEL(ofs1), PIXEL(ofs2), bpp) == 0)
357
rle_packet_type(const uint8_t * row,const uint16_t pos,const uint16_t width,const uint16_t bpp)358 static packet_type rle_packet_type(const uint8_t *row, const uint16_t pos,
359 const uint16_t width, const uint16_t bpp) {
360 if (pos == width - 1) return RAW; /* one pixel */
361 if (SAME(pos, pos + 1)) /* dupe pixel */
362 {
363 if (bpp > 1) return RLE; /* inefficient for bpp=1 */
364
365 /* three repeats makes the bpp=1 case efficient enough */
366 if ((pos < width - 2) && SAME(pos + 1, pos + 2)) return RLE;
367 }
368 return RAW;
369 }
370
371 /* ---------------------------------------------------------------------------
372 * Find the length of the current RLE packet. This is a helper function
373 * called from tga_write_row_RLE().
374 */
rle_packet_len(const uint8_t * row,const uint16_t pos,const uint16_t width,const uint16_t bpp,const packet_type type)375 static uint8_t rle_packet_len(const uint8_t *row, const uint16_t pos,
376 const uint16_t width, const uint16_t bpp,
377 const packet_type type) {
378 uint8_t len = 2;
379
380 if (pos == width - 1) return 1;
381 if (pos == width - 2) return 2;
382
383 if (type == RLE) {
384 while (pos + len < width) {
385 if (SAME(pos, pos + len))
386 len++;
387 else
388 return len;
389
390 if (len == 128) return 128;
391 }
392 } else /* type == RAW */
393 {
394 while (pos + len < width) {
395 if (rle_packet_type(row, pos + len, width, bpp) == RAW)
396 len++;
397 else
398 return len;
399 if (len == 128) return 128;
400 }
401 }
402 return len; /* hit end of row (width) */
403 }
404 #undef SAME
405 #undef PIXEL
406
407 /* ---------------------------------------------------------------------------
408 * Writes a Targa image to <fp> from <src>.
409 *
410 * Returns: TGA_NOERR on success, or a TGAERR_* code on failure.
411 * On failure, the contents of the file are not guaranteed
412 * to be valid.
413 */
tga_write_to_FILE(FILE * fp,const tga_image * src)414 tga_result tga_write_to_FILE(FILE *fp, const tga_image *src) {
415 #define WRITE(srcptr, size) \
416 if (fwrite(srcptr, size, 1, fp) != 1) return TGAERR_WRITE
417
418 #define WRITE16(src) \
419 { \
420 uint16_t _temp = htole16(src); \
421 if (fwrite(&_temp, 2, 1, fp) != 1) return TGAERR_WRITE; \
422 }
423
424 WRITE(&src->image_id_length, 1);
425
426 if (src->color_map_type != TGA_COLOR_MAP_ABSENT &&
427 src->color_map_type != TGA_COLOR_MAP_PRESENT)
428 return TGAERR_CMAP_TYPE;
429 WRITE(&src->color_map_type, 1);
430
431 if (src->image_type == TGA_IMAGE_TYPE_NONE) return TGAERR_NO_IMG;
432 if (src->image_type != TGA_IMAGE_TYPE_COLORMAP &&
433 src->image_type != TGA_IMAGE_TYPE_BGR &&
434 src->image_type != TGA_IMAGE_TYPE_MONO &&
435 src->image_type != TGA_IMAGE_TYPE_COLORMAP_RLE &&
436 src->image_type != TGA_IMAGE_TYPE_BGR_RLE &&
437 src->image_type != TGA_IMAGE_TYPE_MONO_RLE)
438 return TGAERR_IMG_TYPE;
439 WRITE(&src->image_type, 1);
440
441 if (tga_is_colormapped(src) && src->color_map_type == TGA_COLOR_MAP_ABSENT)
442 return TGAERR_CMAP_MISSING;
443 if (!tga_is_colormapped(src) && src->color_map_type == TGA_COLOR_MAP_PRESENT)
444 return TGAERR_CMAP_PRESENT;
445 if (src->color_map_type == TGA_COLOR_MAP_PRESENT) {
446 if (src->color_map_length == 0) return TGAERR_CMAP_LENGTH;
447
448 if (!UNMAP_DEPTH(src->color_map_depth)) return TGAERR_CMAP_DEPTH;
449 }
450 WRITE16(src->color_map_origin);
451 WRITE16(src->color_map_length);
452 WRITE(&src->color_map_depth, 1);
453
454 WRITE16(src->origin_x);
455 WRITE16(src->origin_y);
456
457 if (src->width == 0 || src->height == 0) return TGAERR_ZERO_SIZE;
458 WRITE16(src->width);
459 WRITE16(src->height);
460
461 if (!SANE_DEPTH(src->pixel_depth) ||
462 (src->pixel_depth != 8 && tga_is_colormapped(src)))
463 return TGAERR_PIXEL_DEPTH;
464 WRITE(&src->pixel_depth, 1);
465
466 WRITE(&src->image_descriptor, 1);
467
468 if (src->image_id_length > 0) WRITE(&src->image_id, src->image_id_length);
469
470 if (src->color_map_type == TGA_COLOR_MAP_PRESENT)
471 WRITE(src->color_map_data +
472 (src->color_map_origin * src->color_map_depth / 8),
473 src->color_map_length * src->color_map_depth / 8);
474
475 if (tga_is_rle(src)) {
476 uint16_t row;
477 for (row = 0; row < src->height; row++) {
478 tga_result result = tga_write_row_RLE(
479 fp, src, src->image_data + row * src->width * src->pixel_depth / 8);
480 if (result != TGA_NOERR) return result;
481 }
482 } else {
483 /* uncompressed */
484 WRITE(src->image_data, src->width * src->height * src->pixel_depth / 8);
485 }
486
487 WRITE(tga_id, tga_id_length);
488
489 return TGA_NOERR;
490 #undef WRITE
491 #undef WRITE16
492 }
493
494 /* Convenient writing functions --------------------------------------------*/
495
496 /*
497 * This is just a helper function to initialise the header fields in a
498 * tga_image struct.
499 */
init_tga_image(tga_image * img,uint8_t * image,const uint16_t width,const uint16_t height,const uint8_t depth)500 static void init_tga_image(tga_image *img, uint8_t *image, const uint16_t width,
501 const uint16_t height, const uint8_t depth) {
502 img->image_id_length = 0;
503 img->color_map_type = TGA_COLOR_MAP_ABSENT;
504 img->image_type = TGA_IMAGE_TYPE_NONE; /* override this below! */
505 img->color_map_origin = 0;
506 img->color_map_length = 0;
507 img->color_map_depth = 0;
508 img->origin_x = 0;
509 img->origin_y = 0;
510 img->width = width;
511 img->height = height;
512 img->pixel_depth = depth;
513 img->image_descriptor = TGA_T_TO_B_BIT;
514 img->image_id = NULL;
515 img->color_map_data = NULL;
516 img->image_data = image;
517 }
518
tga_write_mono(const char * filename,uint8_t * image,const uint16_t width,const uint16_t height)519 tga_result tga_write_mono(const char *filename, uint8_t *image,
520 const uint16_t width, const uint16_t height) {
521 tga_image img;
522 init_tga_image(&img, image, width, height, 8);
523 img.image_type = TGA_IMAGE_TYPE_MONO;
524 return tga_write(filename, &img);
525 }
526
tga_write_mono_rle(const char * filename,uint8_t * image,const uint16_t width,const uint16_t height)527 tga_result tga_write_mono_rle(const char *filename, uint8_t *image,
528 const uint16_t width, const uint16_t height) {
529 tga_image img;
530 init_tga_image(&img, image, width, height, 8);
531 img.image_type = TGA_IMAGE_TYPE_MONO_RLE;
532 return tga_write(filename, &img);
533 }
534
tga_write_bgr(const char * filename,uint8_t * image,const uint16_t width,const uint16_t height,const uint8_t depth)535 tga_result tga_write_bgr(const char *filename, uint8_t *image,
536 const uint16_t width, const uint16_t height,
537 const uint8_t depth) {
538 tga_image img;
539 init_tga_image(&img, image, width, height, depth);
540 img.image_type = TGA_IMAGE_TYPE_BGR;
541 return tga_write(filename, &img);
542 }
543
tga_write_bgr_rle(const char * filename,uint8_t * image,const uint16_t width,const uint16_t height,const uint8_t depth)544 tga_result tga_write_bgr_rle(const char *filename, uint8_t *image,
545 const uint16_t width, const uint16_t height,
546 const uint8_t depth) {
547 tga_image img;
548 init_tga_image(&img, image, width, height, depth);
549 img.image_type = TGA_IMAGE_TYPE_BGR_RLE;
550 return tga_write(filename, &img);
551 }
552
553 /* Note: this function will MODIFY <image> */
tga_write_rgb(const char * filename,uint8_t * image,const uint16_t width,const uint16_t height,const uint8_t depth)554 tga_result tga_write_rgb(const char *filename, uint8_t *image,
555 const uint16_t width, const uint16_t height,
556 const uint8_t depth) {
557 tga_image img;
558 init_tga_image(&img, image, width, height, depth);
559 img.image_type = TGA_IMAGE_TYPE_BGR;
560 (void)tga_swap_red_blue(&img);
561 return tga_write(filename, &img);
562 }
563
564 /* Note: this function will MODIFY <image> */
tga_write_rgb_rle(const char * filename,uint8_t * image,const uint16_t width,const uint16_t height,const uint8_t depth)565 tga_result tga_write_rgb_rle(const char *filename, uint8_t *image,
566 const uint16_t width, const uint16_t height,
567 const uint8_t depth) {
568 tga_image img;
569 init_tga_image(&img, image, width, height, depth);
570 img.image_type = TGA_IMAGE_TYPE_BGR_RLE;
571 (void)tga_swap_red_blue(&img);
572 return tga_write(filename, &img);
573 }
574
575 /* Convenient manipulation functions ---------------------------------------*/
576
577 /* ---------------------------------------------------------------------------
578 * Horizontally flip the image in place. Reverses the right-to-left bit in
579 * the image descriptor.
580 */
tga_flip_horiz(tga_image * img)581 tga_result tga_flip_horiz(tga_image *img) {
582 uint16_t row;
583 size_t bpp;
584 uint8_t *left, *right;
585 int r_to_l;
586
587 if (!SANE_DEPTH(img->pixel_depth)) return TGAERR_PIXEL_DEPTH;
588 bpp = (size_t)(img->pixel_depth / 8); /* bytes per pixel */
589
590 for (row = 0; row < img->height; row++) {
591 left = img->image_data + row * img->width * bpp;
592 right = left + (img->width - 1) * bpp;
593
594 /* reverse from left to right */
595 while (left < right) {
596 uint8_t buffer[4];
597
598 /* swap */
599 memcpy(buffer, left, bpp);
600 memcpy(left, right, bpp);
601 memcpy(right, buffer, bpp);
602
603 left += bpp;
604 right -= bpp;
605 }
606 }
607
608 /* Correct image_descriptor's left-to-right-ness. */
609 r_to_l = tga_is_right_to_left(img);
610 img->image_descriptor &= ~TGA_R_TO_L_BIT; /* mask out r-to-l bit */
611 if (!r_to_l) /* was l-to-r, need to set r_to_l */
612 img->image_descriptor |= TGA_R_TO_L_BIT;
613 /* else bit is already rubbed out */
614
615 return TGA_NOERR;
616 }
617
618 /* ---------------------------------------------------------------------------
619 * Vertically flip the image in place. Reverses the top-to-bottom bit in
620 * the image descriptor.
621 */
tga_flip_vert(tga_image * img)622 tga_result tga_flip_vert(tga_image *img) {
623 uint16_t col;
624 size_t bpp, line;
625 uint8_t *top, *bottom;
626 int t_to_b;
627
628 if (!SANE_DEPTH(img->pixel_depth)) return TGAERR_PIXEL_DEPTH;
629 bpp = (size_t)(img->pixel_depth / 8); /* bytes per pixel */
630 line = bpp * img->width; /* bytes per line */
631
632 for (col = 0; col < img->width; col++) {
633 top = img->image_data + col * bpp;
634 bottom = top + (img->height - 1) * line;
635
636 /* reverse from top to bottom */
637 while (top < bottom) {
638 uint8_t buffer[4];
639
640 /* swap */
641 memcpy(buffer, top, bpp);
642 memcpy(top, bottom, bpp);
643 memcpy(bottom, buffer, bpp);
644
645 top += line;
646 bottom -= line;
647 }
648 }
649
650 /* Correct image_descriptor's top-to-bottom-ness. */
651 t_to_b = tga_is_top_to_bottom(img);
652 img->image_descriptor &= ~TGA_T_TO_B_BIT; /* mask out t-to-b bit */
653 if (!t_to_b) /* was b-to-t, need to set t_to_b */
654 img->image_descriptor |= TGA_T_TO_B_BIT;
655 /* else bit is already rubbed out */
656
657 return TGA_NOERR;
658 }
659
660 /* ---------------------------------------------------------------------------
661 * Convert a color-mapped image to unmapped BGR. Reallocates image_data to a
662 * bigger size, then converts the image backwards to avoid using a secondary
663 * buffer. Alters the necessary header fields and deallocates the color map.
664 */
tga_color_unmap(tga_image * img)665 tga_result tga_color_unmap(tga_image *img) {
666 uint8_t bpp = img->color_map_depth / 8; /* bytes per pixel */
667 int pos;
668 void *tmp;
669
670 if (!tga_is_colormapped(img)) return TGAERR_NOT_CMAP;
671 if (img->pixel_depth != 8) return TGAERR_PIXEL_DEPTH;
672 if (!SANE_DEPTH(img->color_map_depth)) return TGAERR_CMAP_DEPTH;
673
674 tmp = realloc(img->image_data, img->width * img->height * bpp);
675 if (tmp == NULL) return TGAERR_NO_MEM;
676 img->image_data = (uint8_t *)tmp;
677
678 for (pos = img->width * img->height - 1; pos >= 0; pos--) {
679 uint8_t c_index = img->image_data[pos];
680 uint8_t *c_bgr = img->color_map_data + (c_index * bpp);
681
682 if (c_index >= img->color_map_origin + img->color_map_length)
683 return TGAERR_INDEX_RANGE;
684
685 memcpy(img->image_data + (pos * bpp), c_bgr, (size_t)bpp);
686 }
687
688 /* clean up */
689 img->image_type = TGA_IMAGE_TYPE_BGR;
690 img->pixel_depth = img->color_map_depth;
691
692 free(img->color_map_data);
693 img->color_map_data = NULL;
694 img->color_map_type = TGA_COLOR_MAP_ABSENT;
695 img->color_map_origin = 0;
696 img->color_map_length = 0;
697 img->color_map_depth = 0;
698
699 return TGA_NOERR;
700 }
701
702 /* ---------------------------------------------------------------------------
703 * Return a pointer to a given pixel. Accounts for image orientation (T_TO_B,
704 * R_TO_L, etc). Returns NULL if the pixel is out of range.
705 */
tga_find_pixel(const tga_image * img,uint16_t x,uint16_t y)706 uint8_t *tga_find_pixel(const tga_image *img, uint16_t x, uint16_t y) {
707 if (x >= img->width || y >= img->height) return NULL;
708
709 if (!tga_is_top_to_bottom(img)) y = img->height - 1 - y;
710 if (tga_is_right_to_left(img)) x = img->width - 1 - x;
711 return img->image_data + (x + y * img->width) * img->pixel_depth / 8;
712 }
713
714 /* ---------------------------------------------------------------------------
715 * Unpack the pixel at the src pointer according to bits. Any of b,g,r,a can
716 * be set to NULL if not wanted. Returns TGAERR_PIXEL_DEPTH if a stupid
717 * number of bits is given.
718 */
tga_unpack_pixel(const uint8_t * src,const uint8_t bits,uint8_t * b,uint8_t * g,uint8_t * r,uint8_t * a)719 tga_result tga_unpack_pixel(const uint8_t *src, const uint8_t bits, uint8_t *b,
720 uint8_t *g, uint8_t *r, uint8_t *a) {
721 switch (bits) {
722 case 32:
723 if (b) *b = src[0];
724 if (g) *g = src[1];
725 if (r) *r = src[2];
726 if (a) *a = src[3];
727 break;
728
729 case 24:
730 if (b) *b = src[0];
731 if (g) *g = src[1];
732 if (r) *r = src[2];
733 if (a) *a = 0;
734 break;
735
736 case 16: {
737 uint16_t src16 = (uint16_t)(src[1] << 8) | (uint16_t)src[0];
738
739 #define FIVE_BITS (BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4))
740 if (b) *b = ((src16)&FIVE_BITS) << 3;
741 if (g) *g = ((src16 >> 5) & FIVE_BITS) << 3;
742 if (r) *r = ((src16 >> 10) & FIVE_BITS) << 3;
743 if (a) *a = (uint8_t)((src16 & BIT(15)) ? 255 : 0);
744 #undef FIVE_BITS
745 break;
746 }
747
748 case 8:
749 if (b) *b = *src;
750 if (g) *g = *src;
751 if (r) *r = *src;
752 if (a) *a = 0;
753 break;
754
755 default:
756 return TGAERR_PIXEL_DEPTH;
757 }
758 return TGA_NOERR;
759 }
760
761 /* ---------------------------------------------------------------------------
762 * Pack the pixel at the dest pointer according to bits. Returns
763 * TGAERR_PIXEL_DEPTH if a stupid number of bits is given.
764 */
tga_pack_pixel(uint8_t * dest,const uint8_t bits,const uint8_t b,const uint8_t g,const uint8_t r,const uint8_t a)765 tga_result tga_pack_pixel(uint8_t *dest, const uint8_t bits, const uint8_t b,
766 const uint8_t g, const uint8_t r, const uint8_t a) {
767 switch (bits) {
768 case 32:
769 dest[0] = b;
770 dest[1] = g;
771 dest[2] = r;
772 dest[3] = a;
773 break;
774
775 case 24:
776 dest[0] = b;
777 dest[1] = g;
778 dest[2] = r;
779 break;
780
781 case 16: {
782 uint16_t tmp;
783
784 #define FIVE_BITS (BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4))
785 tmp = (b >> 3) & FIVE_BITS;
786 tmp |= ((g >> 3) & FIVE_BITS) << 5;
787 tmp |= ((r >> 3) & FIVE_BITS) << 10;
788 if (a > 127) tmp |= BIT(15);
789 #undef FIVE_BITS
790
791 dest[0] = (uint8_t)(tmp & 0x00FF);
792 dest[1] = (uint8_t)((tmp & 0xFF00) >> 8);
793 break;
794 }
795
796 default:
797 return TGAERR_PIXEL_DEPTH;
798 }
799 return TGA_NOERR;
800 }
801
802 /* ---------------------------------------------------------------------------
803 * Desaturate the specified Targa using the specified coefficients:
804 * output = ( red * cr + green * cg + blue * cb ) / dv
805 */
tga_desaturate(tga_image * img,const int cr,const int cg,const int cb,const int dv)806 tga_result tga_desaturate(tga_image *img, const int cr, const int cg,
807 const int cb, const int dv) {
808 uint8_t bpp = img->pixel_depth / 8; /* bytes per pixel */
809 uint8_t *dest, *src, *tmp;
810
811 if (tga_is_mono(img)) return TGAERR_MONO;
812 if (tga_is_colormapped(img)) {
813 tga_result result = tga_color_unmap(img);
814 if (result != TGA_NOERR) return result;
815 }
816 if (!UNMAP_DEPTH(img->pixel_depth)) return TGAERR_PIXEL_DEPTH;
817
818 dest = img->image_data;
819 for (src = img->image_data;
820 src < img->image_data + img->width * img->height * bpp; src += bpp) {
821 uint8_t b, g, r;
822 (void)tga_unpack_pixel(src, img->pixel_depth, &b, &g, &r, NULL);
823
824 *dest = (uint8_t)(((int)b * cb + (int)g * cg + (int)r * cr) / dv);
825 dest++;
826 }
827
828 /* shrink */
829 tmp = realloc(img->image_data, img->width * img->height);
830 if (tmp == NULL) return TGAERR_NO_MEM;
831 img->image_data = tmp;
832
833 img->pixel_depth = 8;
834 img->image_type = TGA_IMAGE_TYPE_MONO;
835 return TGA_NOERR;
836 }
837
tga_desaturate_rec_601_1(tga_image * img)838 tga_result tga_desaturate_rec_601_1(tga_image *img) {
839 return tga_desaturate(img, 2989, 5866, 1145, 10000);
840 }
841
tga_desaturate_rec_709(tga_image * img)842 tga_result tga_desaturate_rec_709(tga_image *img) {
843 return tga_desaturate(img, 2126, 7152, 722, 10000);
844 }
845
tga_desaturate_itu(tga_image * img)846 tga_result tga_desaturate_itu(tga_image *img) {
847 return tga_desaturate(img, 2220, 7067, 713, 10000);
848 }
849
tga_desaturate_avg(tga_image * img)850 tga_result tga_desaturate_avg(tga_image *img) {
851 return tga_desaturate(img, 1, 1, 1, 3);
852 }
853
854 /* ---------------------------------------------------------------------------
855 * Convert an image to the given pixel depth. (one of 32, 24, 16) Avoids
856 * using a secondary buffer to do the conversion.
857 */
tga_convert_depth(tga_image * img,const uint8_t bits)858 tga_result tga_convert_depth(tga_image *img, const uint8_t bits) {
859 size_t src_size, dest_size;
860 uint8_t src_bpp, dest_bpp;
861 uint8_t *src, *dest;
862
863 if (!UNMAP_DEPTH(bits) || !SANE_DEPTH(img->pixel_depth))
864 return TGAERR_PIXEL_DEPTH;
865
866 if (tga_is_colormapped(img)) {
867 tga_result result = tga_color_unmap(img);
868 if (result != TGA_NOERR) return result;
869 }
870
871 if (img->pixel_depth == bits) return TGA_NOERR; /* no op, no err */
872
873 src_bpp = img->pixel_depth / 8;
874 dest_bpp = bits / 8;
875
876 src_size = (size_t)(img->width * img->height * src_bpp);
877 dest_size = (size_t)(img->width * img->height * dest_bpp);
878
879 if (src_size > dest_size) {
880 void *tmp;
881
882 /* convert forwards */
883 dest = img->image_data;
884 for (src = img->image_data;
885 src < img->image_data + img->width * img->height * src_bpp;
886 src += src_bpp) {
887 uint8_t r, g, b, a;
888 (void)tga_unpack_pixel(src, img->pixel_depth, &r, &g, &b, &a);
889 (void)tga_pack_pixel(dest, bits, r, g, b, a);
890 dest += dest_bpp;
891 }
892
893 /* shrink */
894 tmp = realloc(img->image_data, img->width * img->height * dest_bpp);
895 if (tmp == NULL) return TGAERR_NO_MEM;
896 img->image_data = tmp;
897 } else {
898 /* expand */
899 void *tmp = realloc(img->image_data, img->width * img->height * dest_bpp);
900 if (tmp == NULL) return TGAERR_NO_MEM;
901 img->image_data = (uint8_t *)tmp;
902
903 /* convert backwards */
904 dest = img->image_data + (img->width * img->height - 1) * dest_bpp;
905 for (src = img->image_data + (img->width * img->height - 1) * src_bpp;
906 src >= img->image_data; src -= src_bpp) {
907 uint8_t r, g, b, a;
908 (void)tga_unpack_pixel(src, img->pixel_depth, &r, &g, &b, &a);
909 (void)tga_pack_pixel(dest, bits, r, g, b, a);
910 dest -= dest_bpp;
911 }
912 }
913
914 img->pixel_depth = bits;
915 return TGA_NOERR;
916 }
917
918 /* ---------------------------------------------------------------------------
919 * Swap red and blue (RGB becomes BGR and vice verse). (in-place)
920 */
tga_swap_red_blue(tga_image * img)921 tga_result tga_swap_red_blue(tga_image *img) {
922 uint8_t *ptr;
923 uint8_t bpp = img->pixel_depth / 8;
924
925 if (!UNMAP_DEPTH(img->pixel_depth)) return TGAERR_PIXEL_DEPTH;
926
927 for (ptr = img->image_data;
928 ptr < img->image_data + (img->width * img->height - 1) * bpp;
929 ptr += bpp) {
930 uint8_t r, g, b, a;
931 (void)tga_unpack_pixel(ptr, img->pixel_depth, &b, &g, &r, &a);
932 (void)tga_pack_pixel(ptr, img->pixel_depth, r, g, b, a);
933 }
934 return TGA_NOERR;
935 }
936
937 /* ---------------------------------------------------------------------------
938 * Free the image_id, color_map_data and image_data buffers of the specified
939 * tga_image, if they're not already NULL.
940 */
tga_free_buffers(tga_image * img)941 void tga_free_buffers(tga_image *img) {
942 if (img->image_id != NULL) {
943 free(img->image_id);
944 img->image_id = NULL;
945 }
946 if (img->color_map_data != NULL) {
947 free(img->color_map_data);
948 img->color_map_data = NULL;
949 }
950 if (img->image_data != NULL) {
951 free(img->image_data);
952 img->image_data = NULL;
953 }
954 }
955
956 /* vim:set tabstop=4 shiftwidth=4 textwidth=78 expandtab: */
957