Targa reader/writer

I grew up with Targa; this probably had something to do with my dad having a Truevision Vista card when I was a kid. Targa is my favorite image format. This is my second attempt at writing a proper, generalized, stable, error-handling Targa reader/writer. I worked from the official Targa 2.0 specification.

Quick feature list:

I have successfully tested this code on the following platforms:

I haven't had the chance to test it with other Win32 compilers or any older DOS compilers. If you get the chance to do this, please mail me with your results.

Pinnacle

Apparently, Truevision (the company that created the Targa format) was acquired by Pinnacle at some point. For some reason, I can't find the TGA Format Specs on ftp.truevision.com anymore.

License

Copyright © 2001-2004 Emil Mikulic.

Source and binary redistribution of this code, with or without changes, for free or for profit, is allowed as long as this copyright notice is kept intact. Modified versions have to be clearly marked as modified.

This code is provided without any warranty. The copyright holder is not liable for anything bad that might happen as a result of the code.

Source Code

targa-1.tar.gz (8KB)

Or separately:
targa.c (30KB)
targa.h (6KB)

oldtarga.tar.gz - my first attempt

Examples

A 400x300 grayscale image drawn into an array of bytes. Use tga_write_mono to write it to a top-down oriented Targa file:

#include "targa.h"

int main()
{
    uint8_t image[300][400];
    /* draw to image */
    tga_write_mono("output.tga", image, 400, 300);
    return 0;
}

How you go about handling errors is up to you, but the following example should at least demonstrate how to catch an error and format it into a humanly-readable error string:

tga_result result;
result = tga_write_mono("output.tga", image, 400, 300);
if (result != TGA_NOERR)
{
    fprintf(stderr, "Error writing targa: %s\n", tga_error(result));
    exit(EXIT_FAILURE);
}

A quick run-down of all the convenience writing functions:

Depth is given in bits. Be aware that the RGB writing functions will modify your image data, flipping the red and blue components.

A reading example. The following code will read an image, convert it into top-down and left-to-right stored order, make it grayscale and write it to another file:

tga_image img;
tga_read(&img, "input.tga");
if (!tga_is_top_to_bottom(&img)) tga_flip_vert(&img);
if (tga_is_right_to_left(&img)) tga_flip_horiz(&img);
if (tga_is_colormapped(&img)) tga_color_unmap(&img);
if (!tga_is_mono(&img)) tga_desaturate_avg(&img);
tga_write("output.tga", &img);

Every manipulation and loading/saving function used above could potentially return an error. In a more mission-critical application, you would probably want to handle these errors.

Reference Documentation

How to use the Targa code

The Targa reading and writing code consists of two files: targa.c and targa.h. Both are released under a BSD equivalent license. I don't really expect this code to become a shared library. It's quite small and any program that uses it should assimilate (bwahahaha) the Targa code into its own source tree. I won't feel bad if you decide to make a shared library out of it, it's just not the way I envisioned this code would be used.

uint8_t tga_get_attribute_bits(const tga_image *tga);

Returns the number of bits per pixel allocated to the attribute field. (the alpha component) This is pretty useless; included for completeness only.

tga_image img;
tga_read(&img, "input.tga");
printf("%d attribute bits\n", tga_get_attribute_bits(&img));
int tga_is_right_to_left(const tga_image *tga);

Returns 1 if the Targa is stored in right-to-left order, or 0 if left-to-right order. Be aware that some Targa readers don't honor right-to-left storage order.

tga_image img;
tga_read(&img, "input.tga");
if (tga_is_right_to_left(&img)) tga_flip_horiz(&img);
int tga_is_top_to_bottom(const tga_image *tga);

Returns 1 if the Targa is stored in top-to-bottom order, or 0 if bottom-to-top order. By default, Targa images are stored in bottom-to-top order so that the positive y-axis points upwards as it usually does in mathematics.

tga_image img;
tga_read(&img, "input.tga");
if (!tga_is_top_to_bottom(&img)) tga_flip_vert(&img);
int tga_is_colormapped(const tga_image *tga);

Returns 1 if the Targa is colormapped, or 0 if it's grayscale or truecolor.

tga_image img;
tga_read(&img, "input.tga");
if (tga_is_colormapped(&img)) tga_color_unmap(&img);
int tga_is_rle(const tga_image *tga);

Returns 1 if the image_type field of the tga_image struct is of an RLE type, 0 otherwise. Targa images which have been read from a file will have this set if the file was RLE. If the image_type is set to an RLE type before writing a Targa to a file, it will be written as a compressed targa.

tga_image img;
tga_read(&img, "input.tga");
if (tga_is_rle(&img)) printf("input file is compressed.\n");
int tga_is_mono(const tga_image *tga);

Returns 1 if the Targe is an 8-bit grayscale image, or 0 otherwise.

tga_image img;
tga_read(&img, "input.tga");
if (!tga_is_mono(&img)) tga_desaturate_avg(&img);
const char *tga_error(const tga_result errcode);

Formats errcode into a humanly readable error code.

tga_image img;
tga_result result;
result = tga_read(&img, "input.tga");
if (result != TGA_NOERR)
{
    fprintf(stderr, "Error writing targa: %s\n", tga_error(result));
    exit(EXIT_FAILURE);
}
tga_result tga_read(tga_image *dest, const char *filename);

Reads filename into a tga_image structure pointed to by dest. Returns TGA_NOERR if successful, or an appropriate error code if not.

tga_image img;
tga_result result;
result = tga_read(&img, "input.tga");
if (result != TGA_NOERR) printf("error!\n");
tga_result tga_read_from_FILE(tga_image *dest, FILE *fp);

Reads from fp into dest. Refer to tga_read().

tga_result tga_write(const char *filename, const tga_image *src);

Writes a Targa image specified by src to filename. Returns TGA_NOERR if successful, or an appropriate error code if not.

uint8_t image[300][400];
tga_image img;
tga_result result;

img.image_id_length = 0;
img.image_id = NULL;

img.color_map_type = TGA_COLOR_MAP_ABSENT;
img.color_map_origin = 0;
img.color_map_length = 0;
img.color_map_depth = 0;
img.color_map_data = NULL;

img.image_type = TGA_IMAGE_TYPE_MONO_RLE;

img.origin_x = 0;
img.origin_y = 0;
img.width = 400;
img.height = 300;
img.pixel_depth = 8;

/* image array is in top-to-bottom order */
img.image_descriptor = TGA_T_TO_B_BIT;

img.image_data = image;

result = tga_write("output.tga", &img);
if (result != TGA_NOERR) printf("error!\n");
tga_result tga_write_to_FILE(FILE *fp, const tga_image *src);

Writes a Targa image, src, to fp. Refer to tga_write().

tga_result tga_write_mono(const char *filename, uint8_t *image, const uint16_t width, const uint16_t height);

Write to filename a plain memory array, image, of dimensions <width>x<height>. The array is taken to be in top-down, left-to-right order. This is a convenience function that sets up a tga_image struct for you and writes it using tga_write. Returns TGA_NOERR if successful, or an appropriate error code if not.

uint8_t image[300][400];
tga_write_mono("output.tga", image, 400, 300);
tga_result tga_write_mono_rle(const char *filename, uint8_t *image, const uint16_t width, const uint16_t height);

Same as tga_write_mono() but uses RLE compression.

tga_result tga_write_bgr(const char *filename, uint8_t *image, const uint16_t width, const uint16_t height, const uint8_t depth);

Same as tga_write_mono() but writes out a truecolor (BGR) image. You must specify the depth in bits - 16, 24, or 32.

uint8_t image[300][400][3];
tga_write_bgr("output.tga", image, 400, 300, 24);
tga_result tga_write_bgr_rle(const char *filename, uint8_t *image, const uint16_t width, const uint16_t height, const uint8_t depth);

Same as tga_write_bgr() but uses RLE compression.

tga_result tga_write_rgb(const char *filename, uint8_t *image, const uint16_t width, const uint16_t height, const uint8_t depth);

Same as tga_write_bgr() but flips red and blue components in the image. Be aware that calling this function will modify the image data you pass to it.

tga_result tga_write_rgb_rle(const char *filename, uint8_t *image, const uint16_t width, const uint16_t height, const uint8_t depth);

Same as tga_write_rgb() but uses RLE compression.

tga_result tga_flip_horiz(tga_image *img);

Horizontally flips the internal storage order of img.image_data and also flips the horizontal order flag in img.image_descriptor. The flipping is done in place; no working memory is allocated. Be aware that some Targa readers don't honor right-to-left order. This function returns TGA_NOERR on success or an error code if the img fields are in error.

/* given: tga_image img; */
tga_flip_horiz(&img);
tga_result tga_flip_vert(tga_image *img);

Vertically flips the internal storage order of img.image_data and also flips the vertical order flag in img.image_descriptor. The flipping is done in place; no working memory is allocated. By default, Targa images are stored in bottom-to-top order so that the positive y-axis points upwards as it usually does in mathematics. This function returns TGA_NOERR on success or an appropriate error code if the img fields are in error.

/* given: tga_image img; */
tga_flip_vert(&img);
tga_result tga_color_unmap(tga_image *img);

Converts a color-mapped Targa into a truecolor one. The image_data field is reallocated to fit the truecolor image, then unmapping is done backwards so that the process can be done in place. No working memory beyond the final size of the image data will be allocated. This function returns TGA_NOERR on success or an appropriate error code if it runs out of memory or the img fields are in error.

/* given: tga_image img; */
tga_color_unmap(&img);
uint8_t *tga_find_pixel(const tga_image *img, uint16_t x, uint16_t y);

Returns a pointer to the pixel (x, y) in image img or NULL if the specified pixel is outside of the range (0, 0) - (width-1, height-1).

Positive x is right, positive y is down. This function accounts for the internal storage order of the image data given in the image_descriptor field of img.

/* given: tga_image img; */
uint8_t *p = tga_find_pixel(&img, 18, 64);
tga_result tga_unpack_pixel(const uint8_t *src, const uint8_t bits, uint8_t *b, uint8_t *g, uint8_t *r, uint8_t *a);

Unpacks the pixel pointed at by src (refer to tga_find_pixel()) into bytes pointed to by b, g, r, a. If you are uninterested in any one of these four components, you can set it to NULL. You must specify the pixel depth of the image; there is a pixel_depth field in the tga_image struct which can supply this value for you.

Returns TGA_NOERR if successful, or TGAERR_PIXEL_DEPTH if you pass it an illegal bit depth.

/* given: tga_image img; */
uint8_t b, g, r;
uint8_t *p = tga_find_pixel(&img, 18, 64);
tga_unpack_pixel(p, img.pixel_depth, &b, &g, &r, NULL);
tga_result 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);

Packs b, g, r, a into the pixel pointed at by dest. (refer to tga_find_pixel()) You must specify the pixel depth of the image; there is a pixel_depth field in the tga_image struct which can supply this value for you.

Returns TGA_NOERR if successful, or TGAERR_PIXEL_DEPTH if you pass it an illegal bit depth.

/* given: tga_image img; */
uint8_t *p = tga_find_pixel(&img, 18, 64);
tga_pack_pixel(p, img.pixel_depth, 200, 100, 50, 0);
tga_result tga_desaturate(tga_image *img, const int cr, const int cg, const int cb, const int dv);

Desaturates a truecolor image into a grayscale one. The conversion is done in-place, then img.image_data is reallocated to the new, smaller size. Returns TGA_NOERR if successful, or an appropriate error code if the img fields are in error.

The desaturation formula is:
out = ( r * cr + g * cg + b * cb ) / dv

/* given: tga_image img; */
tga_desaturate(&img, 2, 3, 4, 9);
tga_result tga_desaturate_rec_601_1(tga_image *img);

Wrapper around tga_desaturate() using the weighting given in Rec.601-1:
y = 0.2989r + 0.5866g + 0.1145b

tga_result tga_desaturate_rec_709(tga_image *img);

Wrapper around tga_desaturate() using the weighting given in Rec.709:
y = 0.2126r + 0.7152g + 0.0722b

tga_result tga_desaturate_itu(tga_image *img);

Wrapper around tga_desaturate() using the ITU weighting:
y = 0.2220r + 0.7067g + 0.0713b

tga_result tga_desaturate_avg(tga_image *img);

Wrapper around tga_desaturate() using equal weights:
y = (r + g + b) / 3

tga_result tga_convert_depth(tga_image *img, const uint8_t bits);

Converts any type of image (colormapped, grayscale, truecolor) into a truecolor image with the given bit depth, bits. The conversion is done in place as far as that is possible. The function avoids allocating working memory.

Returns TGA_NOERR on success, or an appropriate error code on failure.

/* given: tga_image img; */
tga_convert_depth(&img, 24);
tga_result tga_swap_red_blue(tga_image *img);

Swaps the red and blue components of a truecolor image. Returns TGA_NOERR on success, or TGAERR_PIXEL_DEPTH if img.pixel_depth is set to an illegal value.

/* given: tga_image img; */
tga_swap_red_blue(&img);
void tga_free_buffers(tga_image *img);

Frees the image_id, color_map_data and image_data fields of img and sets them to NULL. If any of the fields are already NULL, the function will not try to free them.

tga_image img;
/* set up header */
img.image_data = (uint8_t*)malloc(400*300*3);
/* draw image */
/* write targa */
tga_free_buffers(&img);

Valid XHTML 1.1
copyright © 2001-2004 Emil Mikulic
$Date: 2006/04/22 08:27:56 $