aboutsummaryrefslogtreecommitdiff
path: root/KaplaDemo/samples/sampleViewer3/TgaFile.cpp
diff options
context:
space:
mode:
authorgit perforce import user <a@b>2016-10-25 12:29:14 -0600
committerSheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees>2016-10-25 18:56:37 -0500
commit3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch)
treefa6485c169e50d7415a651bf838f5bcd0fd3bfbd /KaplaDemo/samples/sampleViewer3/TgaFile.cpp
downloadphysx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.tar.xz
physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.zip
Initial commit:
PhysX 3.4.0 Update @ 21294896 APEX 1.4.0 Update @ 21275617 [CL 21300167]
Diffstat (limited to 'KaplaDemo/samples/sampleViewer3/TgaFile.cpp')
-rw-r--r--KaplaDemo/samples/sampleViewer3/TgaFile.cpp1212
1 files changed, 1212 insertions, 0 deletions
diff --git a/KaplaDemo/samples/sampleViewer3/TgaFile.cpp b/KaplaDemo/samples/sampleViewer3/TgaFile.cpp
new file mode 100644
index 00000000..f085230b
--- /dev/null
+++ b/KaplaDemo/samples/sampleViewer3/TgaFile.cpp
@@ -0,0 +1,1212 @@
+/* ---------------------------------------------------------------------------
+* Truevision Targa Reader/Writer
+* Copyright (C) 2001-2003, 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 must 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.
+*
+*
+* This version modified by NVIDIA Corporation to prevent compilation on
+* platforms other than Windows.
+* -------------------------------------------------------------------------*/
+
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef __GNUC__
+#define UNUSED_VAR __attribute__((unused))
+#else
+#define UNUSED_VAR
+#endif
+
+/*@unused@*/// static const char rcsid[] UNUSED_VAR =
+// "$Id: targa.c,v 1.7 2003/06/21 09:30:53 emikulic Exp $";
+
+#define TGA_KEEP_MACROS /* BIT, htole16, letoh16 */
+
+#include "TgaFile.h"
+
+#define SANE_DEPTH(x) ((x) == 8 || (x) == 16 || (x) == 24 || (x) == 32)
+#define UNMAP_DEPTH(x) ((x) == 16 || (x) == 24 || (x) == 32)
+
+#ifdef _MSC_VER
+#pragma warning(disable:4244)
+#if _MSC_VER >= 1800
+#pragma warning(disable:4388)
+#endif
+#endif
+
+static const char tga_id[] =
+"\0\0\0\0" /* extension area offset */
+"\0\0\0\0" /* developer directory offset */
+"TRUEVISION-XFILE.";
+
+static const size_t tga_id_length = 26; /* tga_id + \0 */
+
+//Some platfroms do not define fopen_s
+#if !defined( __STDC_WANT_SECURE_LIB__ ) && !defined(__ORBIS__)
+static int fopen_s(FILE ** stream, const char * filename, const char * mode)
+{
+ FILE* fp = fopen(filename, mode);
+ return fp ? *stream = fp, 0 : -1;
+}
+#endif
+
+#define TGA_FOPEN_S(fops, stream, filename, mode) (fops ? fops->m_fopen_s(stream, filename, mode) : fopen_s(reinterpret_cast<FILE**>(stream), filename, mode))
+#define TGA_FREAD(fops, ptr, size, count, stream) (fops ? fops->m_fread(ptr, size, count, stream) : fread(ptr, size, count, reinterpret_cast<FILE*>(stream)))
+#define TGA_FWRITE(fops, ptr, size, count, stream) (fops ? fops->m_fwrite(ptr, size ,count, stream) : fwrite(ptr, size, count, reinterpret_cast<FILE*>(stream)))
+#define TGA_FEOF(fops, stream) (fops ? fops->m_feof(stream) : feof(reinterpret_cast<FILE*>(stream)))
+#define TGA_FCLOSE(fops, stream) (fops ? fops->m_fclose(stream) : fclose(reinterpret_cast<FILE*>(stream)))
+
+/* helpers */
+static tga_result tga_read_rle(tga_image *dest, TGA_FP *fp, tgaFileOperations* fops);
+static tga_result tga_write_row_RLE(TGA_FP *fp,
+ const tga_image *src, const uint8_t *row, tgaFileOperations* fops);
+typedef enum { RAW, RLE } packet_type;
+static packet_type rle_packet_type(const uint8_t *row, const uint16_t pos,
+ const uint16_t width, const uint16_t bpp);
+static uint8_t rle_packet_len(const uint8_t *row, const uint16_t pos,
+ const uint16_t width, const uint16_t bpp, const packet_type type);
+
+uint8_t tga_get_attribute_bits(const tga_image *tga)
+{
+ return tga->image_descriptor & TGA_ATTRIB_BITS;
+}
+
+int tga_is_right_to_left(const tga_image *tga)
+{
+ return (tga->image_descriptor & TGA_R_TO_L_BIT) != 0;
+}
+
+int tga_is_top_to_bottom(const tga_image *tga)
+{
+ return (tga->image_descriptor & TGA_T_TO_B_BIT) != 0;
+}
+
+int tga_is_colormapped(const tga_image *tga)
+{
+ return (
+ tga->image_type == TGA_IMAGE_TYPE_COLORMAP ||
+ tga->image_type == TGA_IMAGE_TYPE_COLORMAP_RLE
+ );
+}
+
+int tga_is_rle(const tga_image *tga)
+{
+ return (
+ tga->image_type == TGA_IMAGE_TYPE_COLORMAP_RLE ||
+ tga->image_type == TGA_IMAGE_TYPE_BGR_RLE ||
+ tga->image_type == TGA_IMAGE_TYPE_MONO_RLE
+ );
+}
+
+int tga_is_mono(const tga_image *tga)
+{
+ return (
+ tga->image_type == TGA_IMAGE_TYPE_MONO ||
+ tga->image_type == TGA_IMAGE_TYPE_MONO_RLE
+ );
+}
+
+
+
+/* ---------------------------------------------------------------------------
+* Convert the numerical <errcode> into a verbose error string.
+*
+* Returns: an error string
+*/
+const char *tga_error(const tga_result errcode)
+{
+ switch (errcode)
+ {
+ case TGA_NOERR:
+ return "no error";
+ case TGAERR_FOPEN:
+ return "error opening file";
+ case TGAERR_EOF:
+ return "premature end of file";
+ case TGAERR_WRITE:
+ return "error writing to file";
+ case TGAERR_CMAP_TYPE:
+ return "invalid color map type";
+ case TGAERR_IMG_TYPE:
+ return "invalid image type";
+ case TGAERR_NO_IMG:
+ return "no image data included";
+ case TGAERR_CMAP_MISSING:
+ return "color-mapped image without color map";
+ case TGAERR_CMAP_PRESENT:
+ return "non-color-mapped image with extraneous color map";
+ case TGAERR_CMAP_LENGTH:
+ return "color map has zero length";
+ case TGAERR_CMAP_DEPTH:
+ return "invalid color map depth";
+ case TGAERR_ZERO_SIZE:
+ return "the image dimensions are zero";
+ case TGAERR_PIXEL_DEPTH:
+ return "invalid pixel depth";
+ case TGAERR_NO_MEM:
+ return "out of memory";
+ case TGAERR_NOT_CMAP:
+ return "image is not color mapped";
+ case TGAERR_RLE:
+ return "RLE data is corrupt";
+ case TGAERR_INDEX_RANGE:
+ return "color map index out of range";
+ case TGAERR_MONO:
+ return "image is mono";
+ default:
+ return "unknown error code";
+ }
+}
+
+
+
+/* ---------------------------------------------------------------------------
+* Read a Targa image from a file named <filename> to <dest>. This is just a
+* wrapper around tga_read_from_FILE().
+*
+* Returns: TGA_NOERR on success, or a matching TGAERR_* code on failure.
+*/
+tga_result tga_read(tga_image *dest, const char *filename, tgaFileOperations* fops)
+{
+ tga_result result;
+
+ TGA_FP *fp = NULL;
+ if (0 != TGA_FOPEN_S(fops, &fp, filename, "rb")) return TGAERR_FOPEN;
+ result = tga_read_from_FILE(dest, fp, fops);
+ TGA_FCLOSE(fops, fp);
+ return result;
+}
+
+/* ---------------------------------------------------------------------------
+* Read a Targa image from <fp> to <dest>.
+*
+* Returns: TGA_NOERR on success, or a TGAERR_* code on failure. In the
+* case of failure, the contents of dest are not guaranteed to be
+* valid.
+*/
+tga_result tga_read_from_FILE(tga_image *dest, TGA_FP *fp, tgaFileOperations* fops)
+{
+#define BARF(errcode) \
+ { tga_free_buffers(dest); return errcode; }
+
+#define READ(destptr, size) \
+ if (TGA_FREAD(fops, destptr, 1, size, fp) != size) BARF(TGAERR_EOF)
+
+#define READ16(dest) \
+ { if (TGA_FREAD(fops, &(dest), 1, 2, fp) != 2) BARF(TGAERR_EOF); \
+ dest = letoh16(dest); }
+
+ dest->image_id = NULL;
+ dest->color_map_data = NULL;
+ dest->image_data = NULL;
+
+ READ(&dest->image_id_length, 1);
+ READ(&dest->color_map_type, 1);
+ if (dest->color_map_type != TGA_COLOR_MAP_ABSENT &&
+ dest->color_map_type != TGA_COLOR_MAP_PRESENT)
+ BARF(TGAERR_CMAP_TYPE);
+
+ READ(&dest->image_type, 1);
+ if (dest->image_type == TGA_IMAGE_TYPE_NONE)
+ BARF(TGAERR_NO_IMG);
+
+ if (dest->image_type != TGA_IMAGE_TYPE_COLORMAP &&
+ dest->image_type != TGA_IMAGE_TYPE_BGR &&
+ dest->image_type != TGA_IMAGE_TYPE_MONO &&
+ dest->image_type != TGA_IMAGE_TYPE_COLORMAP_RLE &&
+ dest->image_type != TGA_IMAGE_TYPE_BGR_RLE &&
+ dest->image_type != TGA_IMAGE_TYPE_MONO_RLE)
+ BARF(TGAERR_IMG_TYPE);
+
+ if (tga_is_colormapped(dest) &&
+ dest->color_map_type == TGA_COLOR_MAP_ABSENT)
+ BARF(TGAERR_CMAP_MISSING);
+
+ if (!tga_is_colormapped(dest) &&
+ dest->color_map_type == TGA_COLOR_MAP_PRESENT)
+ BARF(TGAERR_CMAP_PRESENT);
+
+ READ16(dest->color_map_origin);
+ READ16(dest->color_map_length);
+ READ(&dest->color_map_depth, 1);
+ if (dest->color_map_type == TGA_COLOR_MAP_PRESENT)
+ {
+ if (dest->color_map_length == 0)
+ BARF(TGAERR_CMAP_LENGTH);
+
+ if (!UNMAP_DEPTH(dest->color_map_depth))
+ BARF(TGAERR_CMAP_DEPTH);
+ }
+
+ READ16(dest->origin_x);
+ READ16(dest->origin_y);
+ READ16(dest->width);
+ READ16(dest->height);
+
+ if (dest->width == 0 || dest->height == 0)
+ BARF(TGAERR_ZERO_SIZE);
+
+ READ(&dest->pixel_depth, 1);
+ if (!SANE_DEPTH(dest->pixel_depth) ||
+ (dest->pixel_depth != 8 && tga_is_colormapped(dest)))
+ BARF(TGAERR_PIXEL_DEPTH);
+
+ READ(&dest->image_descriptor, 1);
+
+ if (dest->image_id_length > 0)
+ {
+ dest->image_id = reinterpret_cast<uint8_t*>(malloc(dest->image_id_length));
+ if (dest->image_id == NULL) BARF(TGAERR_NO_MEM);
+ READ(dest->image_id, dest->image_id_length);
+ }
+
+ if (dest->color_map_type == TGA_COLOR_MAP_PRESENT)
+ {
+ dest->color_map_data = reinterpret_cast<uint8_t*>(malloc(
+ (dest->color_map_origin + dest->color_map_length) *
+ dest->color_map_depth / 8));
+ if (dest->color_map_data == NULL) BARF(TGAERR_NO_MEM);
+ READ(dest->color_map_data +
+ (dest->color_map_origin * dest->color_map_depth / 8),
+ dest->color_map_length * dest->color_map_depth / 8);
+ }
+
+ dest->image_data = reinterpret_cast<uint8_t*>(malloc(
+ dest->width * dest->height * dest->pixel_depth / 8));
+ if (dest->image_data == NULL)
+ BARF(TGAERR_NO_MEM);
+
+ if (tga_is_rle(dest))
+ {
+ /* read RLE */
+ tga_result result = tga_read_rle(dest, fp, fops);
+ if (result != TGA_NOERR) BARF(result);
+ }
+ else
+ {
+ /* uncompressed */
+ READ(dest->image_data,
+ size_t(dest->width * dest->height * dest->pixel_depth / 8));
+ }
+
+ return TGA_NOERR;
+#undef BARF
+#undef READ
+#undef READ16
+}
+
+static tga_result tga_read_rle(tga_image *dest, TGA_FP *fp, tgaFileOperations* fops)
+{
+#define RLE_BIT BIT(7)
+#define READ(dest, size) \
+ if (TGA_FREAD(fops, dest, 1, size, fp) != size) return TGAERR_EOF
+
+ uint8_t *pos;
+ uint32_t p_loaded = 0,
+ p_expected = dest->width * dest->height;
+ uint8_t bpp = dest->pixel_depth / 8; /* bytes per pixel */
+
+ pos = dest->image_data;
+
+ while ((p_loaded < p_expected) && !TGA_FEOF(fops, fp))
+ {
+ uint8_t b;
+ READ(&b, 1);
+ if (b & RLE_BIT)
+ {
+ /* is an RLE packet */
+ uint8_t count, tmp[4], i;
+
+ count = (b & ~RLE_BIT) + 1;
+ READ(tmp, bpp);
+
+ for (i = 0; i<count; i++)
+ {
+ p_loaded++;
+ if (p_loaded > p_expected) return TGAERR_RLE;
+ memcpy(pos, tmp, bpp);
+ pos += bpp;
+ }
+ }
+ else /* RAW packet */
+ {
+ uint8_t count;
+
+ count = (b & ~RLE_BIT) + 1;
+ if (p_loaded + count > p_expected) return TGAERR_RLE;
+
+ p_loaded += count;
+ READ(pos, bpp*count);
+ pos += count * bpp;
+ }
+ }
+ return TGA_NOERR;
+#undef RLE_BIT
+#undef READ
+}
+
+
+
+/* ---------------------------------------------------------------------------
+* Write a Targa image to a file named <filename> from <src>. This is just a
+* wrapper around tga_write_to_FILE().
+*
+* Returns: TGA_NOERR on success, or a matching TGAERR_* code on failure.
+*/
+tga_result tga_write(const char *filename, const tga_image *src, tgaFileOperations* fops)
+{
+ tga_result result;
+ TGA_FP *fp = NULL;
+ if (0 != TGA_FOPEN_S(fops, &fp, filename, "rb")) return TGAERR_FOPEN;
+
+ result = tga_write_to_FILE(fp, src, fops);
+ TGA_FCLOSE(fops, fp);
+ return result;
+}
+
+
+
+/* ---------------------------------------------------------------------------
+* Write one row of an image to <fp> using RLE. This is a helper function
+* called from tga_write_to_FILE(). It assumes that <src> has its header
+* fields set up correctly.
+*/
+#define PIXEL(ofs) ( row + (ofs)*bpp )
+static tga_result tga_write_row_RLE(TGA_FP *fp,
+ const tga_image *src, const uint8_t *row, tgaFileOperations* fops)
+{
+#define WRITE(src, size) \
+ if (TGA_FWRITE(fops, src, 1, size, fp) != size) return TGAERR_WRITE
+
+ uint16_t pos = 0;
+ uint16_t bpp = src->pixel_depth / 8;
+
+ while (pos < src->width)
+ {
+ packet_type type = rle_packet_type(row, pos, src->width, bpp);
+ uint8_t len = rle_packet_len(row, pos, src->width, bpp, type);
+ uint8_t packet_header;
+
+ packet_header = len - 1;
+ if (type == RLE) packet_header |= BIT(7);
+
+ WRITE(&packet_header, 1);
+ if (type == RLE)
+ {
+ WRITE(PIXEL(pos), bpp);
+ }
+ else /* type == RAW */
+ {
+ WRITE(PIXEL(pos), bpp*len);
+ }
+
+ pos += len;
+ }
+
+ return TGA_NOERR;
+#undef WRITE
+}
+
+
+
+/* ---------------------------------------------------------------------------
+* Determine whether the next packet should be RAW or RLE for maximum
+* efficiency. This is a helper function called from rle_packet_len() and
+* tga_write_row_RLE().
+*/
+#define SAME(ofs1, ofs2) (memcmp(PIXEL(ofs1), PIXEL(ofs2), bpp) == 0)
+
+static packet_type rle_packet_type(const uint8_t *row, const uint16_t pos,
+ const uint16_t width, const uint16_t bpp)
+{
+ if (pos == width - 1) return RAW; /* one pixel */
+ if (SAME(pos, pos + 1)) /* dupe pixel */
+ {
+ if (bpp > 1) return RLE; /* inefficient for bpp=1 */
+
+ /* three repeats makes the bpp=1 case efficient enough */
+ if ((pos < width - 2) && SAME(pos + 1, pos + 2)) return RLE;
+ }
+ return RAW;
+}
+
+
+
+/* ---------------------------------------------------------------------------
+* Find the length of the current RLE packet. This is a helper function
+* called from tga_write_row_RLE().
+*/
+static uint8_t rle_packet_len(const uint8_t *row, const uint16_t pos,
+ const uint16_t width, const uint16_t bpp, const packet_type type)
+{
+ uint8_t len = 2;
+
+ if (pos == width - 1) return 1;
+ if (pos == width - 2) return 2;
+
+ if (type == RLE)
+ {
+ while (pos + len < width)
+ {
+ if (SAME(pos, pos + len))
+ len++;
+ else
+ return len;
+
+ if (len == 128) return 128;
+ }
+ }
+ else /* type == RAW */
+ {
+ while (pos + len < width)
+ {
+ if (rle_packet_type(row, pos + len, width, bpp) == RAW)
+ len++;
+ else
+ return len;
+ if (len == 128) return 128;
+ }
+ }
+ return len; /* hit end of row (width) */
+}
+#undef SAME
+#undef PIXEL
+
+
+
+/* ---------------------------------------------------------------------------
+* Writes a Targa image to <fp> from <src>.
+*
+* Returns: TGA_NOERR on success, or a TGAERR_* code on failure.
+* On failure, the contents of the file are not guaranteed
+* to be valid.
+*/
+tga_result tga_write_to_FILE(TGA_FP *fp, const tga_image *src, tgaFileOperations* fops)
+{
+#define WRITE(srcptr, size) \
+ if (TGA_FWRITE(fops, srcptr, 1, size, fp) != size) return TGAERR_WRITE
+
+#define WRITE16(src) \
+ { uint16_t _temp = htole16(src); \
+ if (TGA_FWRITE(fops, &_temp, 1, 2, fp) != 2) return TGAERR_WRITE; }
+
+ WRITE(&src->image_id_length, 1);
+
+ if (src->color_map_type != TGA_COLOR_MAP_ABSENT &&
+ src->color_map_type != TGA_COLOR_MAP_PRESENT)
+ return TGAERR_CMAP_TYPE;
+ WRITE(&src->color_map_type, 1);
+
+ if (src->image_type == TGA_IMAGE_TYPE_NONE)
+ return TGAERR_NO_IMG;
+ if (src->image_type != TGA_IMAGE_TYPE_COLORMAP &&
+ src->image_type != TGA_IMAGE_TYPE_BGR &&
+ src->image_type != TGA_IMAGE_TYPE_MONO &&
+ src->image_type != TGA_IMAGE_TYPE_COLORMAP_RLE &&
+ src->image_type != TGA_IMAGE_TYPE_BGR_RLE &&
+ src->image_type != TGA_IMAGE_TYPE_MONO_RLE)
+ return TGAERR_IMG_TYPE;
+ WRITE(&src->image_type, 1);
+
+ if (tga_is_colormapped(src) &&
+ src->color_map_type == TGA_COLOR_MAP_ABSENT)
+ return TGAERR_CMAP_MISSING;
+ if (!tga_is_colormapped(src) &&
+ src->color_map_type == TGA_COLOR_MAP_PRESENT)
+ return TGAERR_CMAP_PRESENT;
+ if (src->color_map_type == TGA_COLOR_MAP_PRESENT)
+ {
+ if (src->color_map_length == 0)
+ return TGAERR_CMAP_LENGTH;
+
+ if (!UNMAP_DEPTH(src->color_map_depth))
+ return TGAERR_CMAP_DEPTH;
+ }
+ WRITE16(src->color_map_origin);
+ WRITE16(src->color_map_length);
+ WRITE(&src->color_map_depth, 1);
+
+ WRITE16(src->origin_x);
+ WRITE16(src->origin_y);
+
+ if (src->width == 0 || src->height == 0)
+ return TGAERR_ZERO_SIZE;
+ WRITE16(src->width);
+ WRITE16(src->height);
+
+ if (!SANE_DEPTH(src->pixel_depth) ||
+ (src->pixel_depth != 8 && tga_is_colormapped(src)))
+ return TGAERR_PIXEL_DEPTH;
+ WRITE(&src->pixel_depth, 1);
+
+ WRITE(&src->image_descriptor, 1);
+
+ if (src->image_id_length > 0)
+ WRITE(&src->image_id, src->image_id_length);
+
+ if (src->color_map_type == TGA_COLOR_MAP_PRESENT)
+ WRITE(src->color_map_data +
+ (src->color_map_origin * src->color_map_depth / 8),
+ src->color_map_length * src->color_map_depth / 8);
+
+ if (tga_is_rle(src))
+ {
+ uint16_t row;
+ for (row = 0; row<src->height; row++)
+ {
+ tga_result result = tga_write_row_RLE(fp, src,
+ src->image_data + row*src->width*src->pixel_depth / 8, fops);
+ if (result != TGA_NOERR) return result;
+ }
+ }
+ else
+ {
+ /* uncompressed */
+ WRITE(src->image_data,
+ size_t(src->width * src->height * src->pixel_depth / 8));
+ }
+
+ WRITE(tga_id, tga_id_length);
+
+ return TGA_NOERR;
+#undef WRITE
+#undef WRITE16
+}
+
+
+
+/* Convenient writing functions --------------------------------------------*/
+
+/*
+* This is just a helper function to initialise the header fields in a
+* tga_image struct.
+*/
+static void init_tga_image(tga_image *img, uint8_t *image,
+ const uint16_t width, const uint16_t height, const uint8_t depth)
+{
+ img->image_id_length = 0;
+ img->color_map_type = TGA_COLOR_MAP_ABSENT;
+ img->image_type = TGA_IMAGE_TYPE_NONE; /* override this below! */
+ img->color_map_origin = 0;
+ img->color_map_length = 0;
+ img->color_map_depth = 0;
+ img->origin_x = 0;
+ img->origin_y = 0;
+ img->width = width;
+ img->height = height;
+ img->pixel_depth = depth;
+ img->image_descriptor = TGA_T_TO_B_BIT;
+ img->image_id = NULL;
+ img->color_map_data = NULL;
+ img->image_data = image;
+}
+
+
+
+tga_result tga_write_mono(const char *filename, uint8_t *image,
+ const uint16_t width, const uint16_t height, tgaFileOperations* fops)
+{
+ tga_image img;
+ init_tga_image(&img, image, width, height, 8);
+ img.image_type = TGA_IMAGE_TYPE_MONO;
+ return tga_write(filename, &img, fops);
+}
+
+
+
+tga_result tga_write_mono_rle(const char *filename, uint8_t *image,
+ const uint16_t width, const uint16_t height, tgaFileOperations* fops)
+{
+ tga_image img;
+ init_tga_image(&img, image, width, height, 8);
+ img.image_type = TGA_IMAGE_TYPE_MONO_RLE;
+ return tga_write(filename, &img, fops);
+}
+
+
+
+tga_result tga_write_bgr(const char *filename, uint8_t *image,
+ const uint16_t width, const uint16_t height, const uint8_t depth, tgaFileOperations* fops)
+{
+ tga_image img;
+ init_tga_image(&img, image, width, height, depth);
+ img.image_type = TGA_IMAGE_TYPE_BGR;
+ return tga_write(filename, &img, fops);
+}
+
+
+
+tga_result tga_write_bgr_rle(const char *filename, uint8_t *image,
+ const uint16_t width, const uint16_t height, const uint8_t depth, tgaFileOperations* fops)
+{
+ tga_image img;
+ init_tga_image(&img, image, width, height, depth);
+ img.image_type = TGA_IMAGE_TYPE_BGR_RLE;
+ return tga_write(filename, &img, fops);
+}
+
+
+
+/* Note: this function will MODIFY <image> */
+tga_result tga_write_rgb(const char *filename, uint8_t *image,
+ const uint16_t width, const uint16_t height, const uint8_t depth, tgaFileOperations* fops)
+{
+ tga_image img;
+ init_tga_image(&img, image, width, height, depth);
+ img.image_type = TGA_IMAGE_TYPE_BGR;
+ (void)tga_swap_red_blue(&img);
+ return tga_write(filename, &img, fops);
+}
+
+
+
+/* Note: this function will MODIFY <image> */
+tga_result tga_write_rgb_rle(const char *filename, uint8_t *image,
+ const uint16_t width, const uint16_t height, const uint8_t depth, tgaFileOperations* fops)
+{
+ tga_image img;
+ init_tga_image(&img, image, width, height, depth);
+ img.image_type = TGA_IMAGE_TYPE_BGR_RLE;
+ (void)tga_swap_red_blue(&img);
+ return tga_write(filename, &img, fops);
+}
+
+
+
+/* Convenient manipulation functions ---------------------------------------*/
+
+/* ---------------------------------------------------------------------------
+* Horizontally flip the image in place. Reverses the right-to-left bit in
+* the image descriptor.
+*/
+tga_result tga_flip_horiz(tga_image *img)
+{
+ uint16_t row;
+ size_t bpp;
+ uint8_t *left, *right;
+ int r_to_l;
+
+ if (!SANE_DEPTH(img->pixel_depth)) return TGAERR_PIXEL_DEPTH;
+ bpp = size_t(img->pixel_depth / 8); /* bytes per pixel */
+
+ for (row = 0; row<img->height; row++)
+ {
+ left = img->image_data + row * img->width * bpp;
+ right = left + (img->width - 1) * bpp;
+
+ /* reverse from left to right */
+ while (left < right)
+ {
+ uint8_t buffer[4];
+
+ /* swap */
+ memcpy(buffer, left, bpp);
+ memcpy(left, right, bpp);
+ memcpy(right, buffer, bpp);
+
+ left += bpp;
+ right -= bpp;
+ }
+ }
+
+ /* Correct image_descriptor's left-to-right-ness. */
+ r_to_l = tga_is_right_to_left(img);
+ img->image_descriptor &= ~TGA_R_TO_L_BIT; /* mask out r-to-l bit */
+ if (!r_to_l)
+ /* was l-to-r, need to set r_to_l */
+ img->image_descriptor |= TGA_R_TO_L_BIT;
+ /* else bit is already rubbed out */
+
+ return TGA_NOERR;
+}
+
+
+
+/* ---------------------------------------------------------------------------
+* Vertically flip the image in place. Reverses the top-to-bottom bit in
+* the image descriptor.
+*/
+tga_result tga_flip_vert(tga_image *img)
+{
+ uint16_t col;
+ uint16_t row;
+ size_t bpp, line;
+ uint8_t *top, *bottom;
+ int t_to_b;
+
+ if (!SANE_DEPTH(img->pixel_depth)) return TGAERR_PIXEL_DEPTH;
+ bpp = size_t(img->pixel_depth / 8); /* bytes per pixel */
+ line = bpp * img->width; /* bytes per line */
+
+ for (col = 0; col < img->width; col++)
+ {
+ for (row = 0; row < img->height; row++)
+ {
+ uint8_t* pixel = &img->image_data[(col*img->height + row)*bpp];
+ uint8_t tmp = pixel[0];
+ pixel[0] = pixel[2];
+ pixel[2] = tmp;
+ }
+ }
+
+ for (col = 0; col<img->width; col++)
+ {
+ top = img->image_data + col * bpp;
+ bottom = top + (img->height - 1) * line;
+
+ /* reverse from top to bottom */
+ while (top < bottom)
+ {
+ uint8_t buffer[4];
+
+ /* swap */
+ memcpy(buffer, top, bpp);
+ memcpy(top, bottom, bpp);
+ memcpy(bottom, buffer, bpp);
+
+ top += line;
+ bottom -= line;
+ }
+ }
+
+ /* Correct image_descriptor's top-to-bottom-ness. */
+ t_to_b = tga_is_top_to_bottom(img);
+ img->image_descriptor &= ~TGA_T_TO_B_BIT; /* mask out t-to-b bit */
+ if (!t_to_b)
+ /* was b-to-t, need to set t_to_b */
+ img->image_descriptor |= TGA_T_TO_B_BIT;
+ /* else bit is already rubbed out */
+
+ return TGA_NOERR;
+}
+
+
+
+/* ---------------------------------------------------------------------------
+* Convert a color-mapped image to unmapped BGR. Reallocates image_data to a
+* bigger size, then converts the image backwards to avoid using a secondary
+* buffer. Alters the necessary header fields and deallocates the color map.
+*/
+tga_result tga_color_unmap(tga_image *img)
+{
+ uint8_t bpp = img->color_map_depth / 8; /* bytes per pixel */
+ int pos;
+ void *tmp;
+
+ if (!tga_is_colormapped(img)) return TGAERR_NOT_CMAP;
+ if (img->pixel_depth != 8) return TGAERR_PIXEL_DEPTH;
+ if (!SANE_DEPTH(img->color_map_depth)) return TGAERR_CMAP_DEPTH;
+
+ tmp = realloc(img->image_data, img->width * img->height * bpp);
+ if (tmp == NULL) return TGAERR_NO_MEM;
+ img->image_data = reinterpret_cast<uint8_t*>(tmp);
+
+ for (pos = img->width * img->height - 1; pos >= 0; pos--)
+ {
+ uint8_t c_index = img->image_data[pos];
+ uint8_t *c_bgr = img->color_map_data + (c_index * bpp);
+
+ if (c_index >= img->color_map_origin + img->color_map_length)
+ return TGAERR_INDEX_RANGE;
+
+ memcpy(img->image_data + (pos*bpp), c_bgr, size_t(bpp));
+ }
+
+ /* clean up */
+ img->image_type = TGA_IMAGE_TYPE_BGR;
+ img->pixel_depth = img->color_map_depth;
+
+ free(img->color_map_data);
+ img->color_map_data = NULL;
+ img->color_map_type = TGA_COLOR_MAP_ABSENT;
+ img->color_map_origin = 0;
+ img->color_map_length = 0;
+ img->color_map_depth = 0;
+
+ return TGA_NOERR;
+}
+
+
+
+/* ---------------------------------------------------------------------------
+* Return a pointer to a given pixel. Accounts for image orientation (T_TO_B,
+* R_TO_L, etc). Returns NULL if the pixel is out of range.
+*/
+uint8_t *tga_find_pixel(const tga_image *img, uint16_t x, uint16_t y)
+{
+ if (x >= img->width || y >= img->height)
+ return NULL;
+
+ if (!tga_is_top_to_bottom(img)) y = img->height - 1 - y;
+ if (tga_is_right_to_left(img)) x = img->width - 1 - x;
+ return img->image_data + (x + y * img->width) * img->pixel_depth / 8;
+}
+
+
+
+/* ---------------------------------------------------------------------------
+* Unpack the pixel at the src pointer according to bits. Any of b,g,r,a can
+* be set to NULL if not wanted. Returns TGAERR_PIXEL_DEPTH if a bad
+* number of bits is given.
+*/
+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)
+{
+ switch (bits)
+ {
+ case 32:
+ if (b) *b = src[0];
+ if (g) *g = src[1];
+ if (r) *r = src[2];
+ if (a) *a = src[3];
+ break;
+
+ case 24:
+ if (b) *b = src[0];
+ if (g) *g = src[1];
+ if (r) *r = src[2];
+ if (a) *a = 0;
+ break;
+
+ case 16:
+ {
+ uint16_t src16 = uint16_t(src[1] << 8) | uint16_t(src[0]);
+
+#define FIVE_BITS (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4))
+ if (b) *b = uint8_t(((src16)& FIVE_BITS) << 3);
+ if (g) *g = uint8_t(((src16 >> 5) & FIVE_BITS) << 3);
+ if (r) *r = uint8_t(((src16 >> 10) & FIVE_BITS) << 3);
+ if (a) *a = uint8_t((src16 & BIT(15)) ? 255 : 0);
+#undef FIVE_BITS
+ break;
+ }
+
+ case 8:
+ if (b) *b = *src;
+ if (g) *g = *src;
+ if (r) *r = *src;
+ if (a) *a = 0;
+ break;
+
+ default:
+ return TGAERR_PIXEL_DEPTH;
+ }
+ return TGA_NOERR;
+}
+
+
+
+/* ---------------------------------------------------------------------------
+* Pack the pixel at the dest pointer according to bits. Returns
+* TGAERR_PIXEL_DEPTH if a bad number of bits is given.
+*/
+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)
+{
+ switch (bits)
+ {
+ case 32:
+ dest[0] = b;
+ dest[1] = g;
+ dest[2] = r;
+ dest[3] = a;
+ break;
+
+ case 24:
+ dest[0] = b;
+ dest[1] = g;
+ dest[2] = r;
+ break;
+
+ case 16:
+ {
+ uint16_t tmp;
+
+#define FIVE_BITS (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4))
+ tmp = uint16_t((b >> 3) & FIVE_BITS);
+ tmp |= ((g >> 3) & FIVE_BITS) << 5;
+ tmp |= ((r >> 3) & FIVE_BITS) << 10;
+ if (a > 127) tmp |= BIT(15);
+#undef FIVE_BITS
+
+ dest[0] = uint8_t(tmp & 0x00FF);
+ dest[1] = uint8_t((tmp & 0xFF00) >> 8);
+ break;
+ }
+
+ default:
+ return TGAERR_PIXEL_DEPTH;
+ }
+ return TGA_NOERR;
+}
+
+
+
+/* ---------------------------------------------------------------------------
+* Desaturate the specified Targa using the specified coefficients:
+* output = ( red * cr + green * cg + blue * cb ) / dv
+*/
+tga_result tga_desaturate(tga_image *img, const int cr, const int cg,
+ const int cb, const int dv)
+{
+ uint8_t bpp = img->pixel_depth / 8; /* bytes per pixel */
+ uint8_t *dest, *src, *tmp;
+
+ if (tga_is_mono(img)) return TGAERR_MONO;
+ if (tga_is_colormapped(img))
+ {
+ tga_result result = tga_color_unmap(img);
+ if (result != TGA_NOERR) return result;
+ }
+ if (!UNMAP_DEPTH(img->pixel_depth)) return TGAERR_PIXEL_DEPTH;
+
+ dest = img->image_data;
+ for (src = img->image_data;
+ src < img->image_data + img->width*img->height*bpp;
+ src += bpp)
+ {
+ uint8_t b = 0, g = 0, r = 0;
+ (void)tga_unpack_pixel(src, img->pixel_depth, &b, &g, &r, NULL);
+
+ *dest = uint8_t((int(b) * cb +
+ int(g) * cg +
+ int(r) * cr) / dv);
+ dest++;
+ }
+
+ /* shrink */
+ tmp = reinterpret_cast<uint8_t*>(realloc(img->image_data, img->width * img->height));
+ if (tmp == NULL) return TGAERR_NO_MEM;
+ img->image_data = reinterpret_cast<uint8_t*>(tmp);
+
+ img->pixel_depth = 8;
+ img->image_type = TGA_IMAGE_TYPE_MONO;
+ return TGA_NOERR;
+}
+
+tga_result tga_desaturate_rec_601_1(tga_image *img)
+{
+ return tga_desaturate(img, 2989, 5866, 1145, 10000);
+}
+
+tga_result tga_desaturate_rec_709(tga_image *img)
+{
+ return tga_desaturate(img, 2126, 7152, 722, 10000);
+}
+
+tga_result tga_desaturate_itu(tga_image *img)
+{
+ return tga_desaturate(img, 2220, 7067, 713, 10000);
+}
+
+tga_result tga_desaturate_avg(tga_image *img)
+{
+ return tga_desaturate(img, 1, 1, 1, 3);
+}
+
+
+
+/* ---------------------------------------------------------------------------
+* Convert an image to the given pixel depth. (one of 32, 24, 16) Avoids
+* using a secondary buffer to do the conversion.
+*/
+tga_result tga_convert_depth(tga_image *img, const uint8_t bits)
+{
+ size_t src_size, dest_size;
+ uint8_t src_bpp, dest_bpp;
+ uint8_t *src, *dest;
+
+ if (!UNMAP_DEPTH(bits) ||
+ !SANE_DEPTH(img->pixel_depth)
+ ) return TGAERR_PIXEL_DEPTH;
+
+ if (tga_is_colormapped(img))
+ {
+ tga_result result = tga_color_unmap(img);
+ if (result != TGA_NOERR) return result;
+ }
+
+ if (img->pixel_depth == bits) return TGA_NOERR; /* no op, no err */
+
+ src_bpp = img->pixel_depth / 8;
+ dest_bpp = bits / 8;
+
+ src_size = size_t(img->width * img->height * src_bpp);
+ dest_size = size_t(img->width * img->height * dest_bpp);
+
+ if (src_size > dest_size)
+ {
+ void *tmp;
+
+ /* convert forwards */
+ dest = img->image_data;
+ for (src = img->image_data;
+ src < img->image_data + img->width * img->height * src_bpp;
+ src += src_bpp)
+ {
+ uint8_t r = 0, g = 0, b = 0, a = 0;
+ (void)tga_unpack_pixel(src, img->pixel_depth, &r, &g, &b, &a);
+ (void)tga_pack_pixel(dest, bits, r, g, b, a);
+ dest += dest_bpp;
+ }
+
+ /* shrink */
+ tmp = realloc(img->image_data, img->width * img->height * dest_bpp);
+ if (tmp == NULL) return TGAERR_NO_MEM;
+ img->image_data = reinterpret_cast<uint8_t*>(tmp);
+ }
+ else
+ {
+ /* expand */
+ void *tmp = realloc(img->image_data,
+ img->width * img->height * dest_bpp);
+ if (tmp == NULL) return TGAERR_NO_MEM;
+ img->image_data = reinterpret_cast<uint8_t*>(tmp);
+
+ /* convert backwards */
+ dest = img->image_data + (img->width*img->height - 1) * dest_bpp;
+ for (src = img->image_data + (img->width*img->height - 1) * src_bpp;
+ src >= img->image_data;
+ src -= src_bpp)
+ {
+ uint8_t r = 0, g = 0, b = 0, a = 0;
+ (void)tga_unpack_pixel(src, img->pixel_depth, &r, &g, &b, &a);
+ (void)tga_pack_pixel(dest, bits, r, g, b, a);
+ dest -= dest_bpp;
+ }
+ }
+
+ img->pixel_depth = bits;
+ return TGA_NOERR;
+}
+
+
+
+/* ---------------------------------------------------------------------------
+* Swap red and blue (RGB becomes BGR and vice verse). (in-place)
+*/
+tga_result tga_swap_red_blue(tga_image *img)
+{
+ uint8_t *ptr;
+ uint8_t bpp = img->pixel_depth / 8;
+
+ if (!UNMAP_DEPTH(img->pixel_depth)) return TGAERR_PIXEL_DEPTH;
+
+ for (ptr = img->image_data;
+ ptr < img->image_data + (img->width * img->height - 1) * bpp;
+ ptr += bpp)
+ {
+ uint8_t r = 0, g = 0, b = 0, a = 0;
+ (void)tga_unpack_pixel(ptr, img->pixel_depth, &b, &g, &r, &a);
+ (void)tga_pack_pixel(ptr, img->pixel_depth, r, g, b, a);
+ }
+ return TGA_NOERR;
+}
+
+
+
+/* ---------------------------------------------------------------------------
+* Free the image_id, color_map_data and image_data buffers of the specified
+* tga_image, if they're not already NULL.
+*/
+void tga_free_buffers(tga_image *img)
+{
+ if (img->image_id != NULL)
+ {
+ free(img->image_id);
+ img->image_id = NULL;
+ }
+ if (img->color_map_data != NULL)
+ {
+ free(img->color_map_data);
+ img->color_map_data = NULL;
+ }
+ if (img->image_data != NULL)
+ {
+ free(img->image_data);
+ img->image_data = NULL;
+ }
+}
+
+
+/* vim:set tabstop=4 shiftwidth=4 textwidth=78 expandtab: */
+
+
+bool LoadTGAFile(const char *filename, TGAFILE *tgaFile)
+{
+ FILE *filePtr;
+ unsigned char ucharBad;
+ short int sintBad;
+ long imageSize;
+ int colorMode;
+ unsigned char colorSwap;
+
+ // Open the TGA file.
+ filePtr = fopen(filename, "rb");
+ if (filePtr == NULL)
+ {
+ return false;
+ }
+
+ // Read the two first bytes we don't need.
+ fread(&ucharBad, sizeof(unsigned char), 1, filePtr);
+ fread(&ucharBad, sizeof(unsigned char), 1, filePtr);
+
+ // Which type of image gets stored in imageTypeCode.
+ fread(&tgaFile->imageTypeCode, sizeof(unsigned char), 1, filePtr);
+
+ // For our purposes, the type code should be 2 (uncompressed RGB image)
+ // or 3 (uncompressed black-and-white images).
+ if (tgaFile->imageTypeCode != 2 && tgaFile->imageTypeCode != 3)
+ {
+ fclose(filePtr);
+ return false;
+ }
+
+ // Read 13 bytes of data we don't need.
+ fread(&sintBad, sizeof(short int), 1, filePtr);
+ fread(&sintBad, sizeof(short int), 1, filePtr);
+ fread(&ucharBad, sizeof(unsigned char), 1, filePtr);
+ fread(&sintBad, sizeof(short int), 1, filePtr);
+ fread(&sintBad, sizeof(short int), 1, filePtr);
+
+ // Read the image's width and height.
+ fread(&tgaFile->imageWidth, sizeof(short int), 1, filePtr);
+ fread(&tgaFile->imageHeight, sizeof(short int), 1, filePtr);
+
+ // Read the bit depth.
+ fread(&tgaFile->bitCount, sizeof(unsigned char), 1, filePtr);
+
+ // Read one byte of data we don't need.
+ fread(&ucharBad, sizeof(unsigned char), 1, filePtr);
+
+ // Color mode -> 3 = BGR, 4 = BGRA.
+ colorMode = tgaFile->bitCount / 8;
+ imageSize = tgaFile->imageWidth * tgaFile->imageHeight * colorMode;
+
+ // Allocate memory for the image data.
+ tgaFile->imageData = (unsigned char*)malloc(sizeof(unsigned char)*imageSize);
+
+ // Read the image data.
+ fread(tgaFile->imageData, sizeof(unsigned char), imageSize, filePtr);
+
+ // Change from BGR to RGB so OpenGL can read the image data.
+ for (int imageIdx = 0; imageIdx < imageSize; imageIdx += colorMode)
+ {
+ colorSwap = tgaFile->imageData[imageIdx];
+ tgaFile->imageData[imageIdx] = tgaFile->imageData[imageIdx + 2];
+ tgaFile->imageData[imageIdx + 2] = colorSwap;
+ }
+
+ fclose(filePtr);
+ return true;
+}
+
+