|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* ---------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
* Truevision Targa Reader/Writer
|
|
Toshihiro Shimizu |
890ddd |
* Copyright (C) 2001-2003, Emil Mikulic.
|
|
Toshihiro Shimizu |
890ddd |
*
|
|
Toshihiro Shimizu |
890ddd |
* Source and binary redistribution of this code, with or without changes, for
|
|
Toshihiro Shimizu |
890ddd |
* free or for profit, is allowed as long as this copyright notice is kept
|
|
Toshihiro Shimizu |
890ddd |
* intact. Modified versions must be clearly marked as modified.
|
|
Toshihiro Shimizu |
890ddd |
*
|
|
Toshihiro Shimizu |
890ddd |
* This code is provided without any warranty. The copyright holder is
|
|
Toshihiro Shimizu |
890ddd |
* not liable for anything bad that might happen as a result of the
|
|
Toshihiro Shimizu |
890ddd |
* code.
|
|
Toshihiro Shimizu |
890ddd |
* -------------------------------------------------------------------------*/
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/*
|
|
Toshihiro Shimizu |
890ddd |
* preso da http://dmr.ath.cx/gfx/targa/
|
|
Toshihiro Shimizu |
890ddd |
* modificato da gmt
|
|
Toshihiro Shimizu |
890ddd |
*
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/*@unused@*/ static const char rcsid[] =
|
|
Shinya Kitaoka |
120a6e |
"$Id: targa.c,v 1.7 2003/06/21 09:30:53 emikulic Exp $";
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
#define TGA_KEEP_MACROS /* BIT, htole16, letoh16 */
|
|
Toshihiro Shimizu |
890ddd |
#include "targa.h"
|
|
Toshihiro Shimizu |
890ddd |
#include <stdlib.h></stdlib.h>
|
|
Toshihiro Shimizu |
890ddd |
#include <string.h> /* memcpy, memcmp */</string.h>
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
#define SANE_DEPTH(x) ((x) == 8 || (x) == 16 || (x) == 24 || (x) == 32)
|
|
Toshihiro Shimizu |
890ddd |
#define UNMAP_DEPTH(x) ((x) == 16 || (x) == 24 || (x) == 32)
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
static const char tga_id[] =
|
|
Shinya Kitaoka |
120a6e |
"\0\0\0\0" /* extension area offset */
|
|
Shinya Kitaoka |
120a6e |
"\0\0\0\0" /* developer directory offset */
|
|
Shinya Kitaoka |
120a6e |
"TRUEVISION-XFILE.";
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
static const size_t tga_id_length = 26; /* tga_id + \0 */
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* helpers */
|
|
Toshihiro Shimizu |
890ddd |
static tga_result tga_read_rle(tga_image *dest, FILE *fp);
|
|
Shinya Kitaoka |
120a6e |
static tga_result tga_write_row_RLE(FILE *fp, const tga_image *src,
|
|
Shinya Kitaoka |
120a6e |
const uint8_t *row);
|
|
Shinya Kitaoka |
120a6e |
typedef enum { RAW, RLE } packet_type;
|
|
Toshihiro Shimizu |
890ddd |
static packet_type rle_packet_type(const uint8_t *row, const uint16_t pos,
|
|
Shinya Kitaoka |
120a6e |
const uint16_t width, const uint16_t bpp);
|
|
Toshihiro Shimizu |
890ddd |
static uint8_t rle_packet_len(const uint8_t *row, const uint16_t pos,
|
|
Shinya Kitaoka |
120a6e |
const uint16_t width, const uint16_t bpp,
|
|
Shinya Kitaoka |
120a6e |
const packet_type type);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
uint8_t tga_get_attribute_bits(const tga_image *tga) {
|
|
Shinya Kitaoka |
120a6e |
return tga->image_descriptor & TGA_ATTRIB_BITS;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
int tga_is_right_to_left(const tga_image *tga) {
|
|
Shinya Kitaoka |
120a6e |
return (tga->image_descriptor & TGA_R_TO_L_BIT) != 0;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
int tga_is_top_to_bottom(const tga_image *tga) {
|
|
Shinya Kitaoka |
120a6e |
return (tga->image_descriptor & TGA_T_TO_B_BIT) != 0;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
int tga_is_colormapped(const tga_image *tga) {
|
|
Shinya Kitaoka |
120a6e |
return (tga->image_type == TGA_IMAGE_TYPE_COLORMAP ||
|
|
Shinya Kitaoka |
120a6e |
tga->image_type == TGA_IMAGE_TYPE_COLORMAP_RLE);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
int tga_is_rle(const tga_image *tga) {
|
|
Shinya Kitaoka |
120a6e |
return (tga->image_type == TGA_IMAGE_TYPE_COLORMAP_RLE ||
|
|
Shinya Kitaoka |
120a6e |
tga->image_type == TGA_IMAGE_TYPE_BGR_RLE ||
|
|
Shinya Kitaoka |
120a6e |
tga->image_type == TGA_IMAGE_TYPE_MONO_RLE);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
int tga_is_mono(const tga_image *tga) {
|
|
Shinya Kitaoka |
120a6e |
return (tga->image_type == TGA_IMAGE_TYPE_MONO ||
|
|
Shinya Kitaoka |
120a6e |
tga->image_type == TGA_IMAGE_TYPE_MONO_RLE);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* ---------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
* Convert the numerical <errcode> into a verbose error string.</errcode>
|
|
Toshihiro Shimizu |
890ddd |
*
|
|
Toshihiro Shimizu |
890ddd |
* Returns: an error string
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Shinya Kitaoka |
120a6e |
const char *tga_error(const tga_result errcode) {
|
|
Shinya Kitaoka |
120a6e |
switch (errcode) {
|
|
Shinya Kitaoka |
120a6e |
case TGA_NOERR:
|
|
Shinya Kitaoka |
120a6e |
return "no error";
|
|
Shinya Kitaoka |
120a6e |
case TGAERR_FOPEN:
|
|
Shinya Kitaoka |
120a6e |
return "error opening file";
|
|
Shinya Kitaoka |
120a6e |
case TGAERR_EOF:
|
|
Shinya Kitaoka |
120a6e |
return "premature end of file";
|
|
Shinya Kitaoka |
120a6e |
case TGAERR_WRITE:
|
|
Shinya Kitaoka |
120a6e |
return "error writing to file";
|
|
Shinya Kitaoka |
120a6e |
case TGAERR_CMAP_TYPE:
|
|
Shinya Kitaoka |
120a6e |
return "invalid color map type";
|
|
Shinya Kitaoka |
120a6e |
case TGAERR_IMG_TYPE:
|
|
Shinya Kitaoka |
120a6e |
return "invalid image type";
|
|
Shinya Kitaoka |
120a6e |
case TGAERR_NO_IMG:
|
|
Shinya Kitaoka |
120a6e |
return "no image data included";
|
|
Shinya Kitaoka |
120a6e |
case TGAERR_CMAP_MISSING:
|
|
Shinya Kitaoka |
120a6e |
return "color-mapped image without color map";
|
|
Shinya Kitaoka |
120a6e |
case TGAERR_CMAP_PRESENT:
|
|
Shinya Kitaoka |
120a6e |
return "non-color-mapped image with extraneous color map";
|
|
Shinya Kitaoka |
120a6e |
case TGAERR_CMAP_LENGTH:
|
|
Shinya Kitaoka |
120a6e |
return "color map has zero length";
|
|
Shinya Kitaoka |
120a6e |
case TGAERR_CMAP_DEPTH:
|
|
Shinya Kitaoka |
120a6e |
return "invalid color map depth";
|
|
Shinya Kitaoka |
120a6e |
case TGAERR_ZERO_SIZE:
|
|
Shinya Kitaoka |
120a6e |
return "the image dimensions are zero";
|
|
Shinya Kitaoka |
120a6e |
case TGAERR_PIXEL_DEPTH:
|
|
Shinya Kitaoka |
120a6e |
return "invalid pixel depth";
|
|
Shinya Kitaoka |
120a6e |
case TGAERR_NO_MEM:
|
|
Shinya Kitaoka |
120a6e |
return "out of memory";
|
|
Shinya Kitaoka |
120a6e |
case TGAERR_NOT_CMAP:
|
|
Shinya Kitaoka |
120a6e |
return "image is not color mapped";
|
|
Shinya Kitaoka |
120a6e |
case TGAERR_RLE:
|
|
Shinya Kitaoka |
120a6e |
return "RLE data is corrupt";
|
|
Shinya Kitaoka |
120a6e |
case TGAERR_INDEX_RANGE:
|
|
Shinya Kitaoka |
120a6e |
return "color map index out of range";
|
|
Shinya Kitaoka |
120a6e |
case TGAERR_MONO:
|
|
Shinya Kitaoka |
120a6e |
return "image is mono";
|
|
Shinya Kitaoka |
120a6e |
default:
|
|
Shinya Kitaoka |
120a6e |
return "unknown error code";
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* ---------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
* Read a Targa image from a file named <filename> to <dest>. This is just a</dest></filename>
|
|
Toshihiro Shimizu |
890ddd |
* wrapper around tga_read_from_FILE().
|
|
Toshihiro Shimizu |
890ddd |
*
|
|
Toshihiro Shimizu |
890ddd |
* Returns: TGA_NOERR on success, or a matching TGAERR_* code on failure.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Shinya Kitaoka |
120a6e |
tga_result tga_read(tga_image *dest, const char *filename) {
|
|
Shinya Kitaoka |
120a6e |
tga_result result;
|
|
Shinya Kitaoka |
120a6e |
FILE *fp = fopen(filename, "rb");
|
|
Shinya Kitaoka |
120a6e |
if (fp == NULL) return TGAERR_FOPEN;
|
|
Shinya Kitaoka |
120a6e |
result = tga_read_from_FILE(dest, fp);
|
|
Shinya Kitaoka |
120a6e |
fclose(fp);
|
|
Shinya Kitaoka |
120a6e |
return result;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* ---------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
* Read a Targa image from <fp> to <dest>.</dest></fp>
|
|
Toshihiro Shimizu |
890ddd |
*
|
|
Toshihiro Shimizu |
890ddd |
* Returns: TGA_NOERR on success, or a TGAERR_* code on failure. In the
|
|
Toshihiro Shimizu |
890ddd |
* case of failure, the contents of dest are not guaranteed to be
|
|
Toshihiro Shimizu |
890ddd |
* valid.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Shinya Kitaoka |
120a6e |
tga_result tga_read_from_FILE(tga_image *dest, FILE *fp) {
|
|
Shinya Kitaoka |
120a6e |
#define BARF(errcode) \
|
|
Shinya Kitaoka |
120a6e |
{ \
|
|
Shinya Kitaoka |
120a6e |
tga_free_buffers(dest); \
|
|
Shinya Kitaoka |
120a6e |
return errcode; \
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
#define READ(destptr, size) \
|
|
Shinya Kitaoka |
120a6e |
if (fread(destptr, size, 1, fp) != 1) BARF(TGAERR_EOF)
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
#define READ16(dest) \
|
|
Shinya Kitaoka |
120a6e |
{ \
|
|
Shinya Kitaoka |
120a6e |
if (fread(&(dest), 2, 1, fp) != 1) BARF(TGAERR_EOF); \
|
|
Shinya Kitaoka |
120a6e |
dest = letoh16(dest); \
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
dest->image_id = NULL;
|
|
Shinya Kitaoka |
120a6e |
dest->color_map_data = NULL;
|
|
Shinya Kitaoka |
120a6e |
dest->image_data = NULL;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
READ(&dest->image_id_length, 1);
|
|
Shinya Kitaoka |
120a6e |
READ(&dest->color_map_type, 1);
|
|
Shinya Kitaoka |
120a6e |
if (dest->color_map_type != TGA_COLOR_MAP_ABSENT &&
|
|
Shinya Kitaoka |
120a6e |
dest->color_map_type != TGA_COLOR_MAP_PRESENT)
|
|
Shinya Kitaoka |
120a6e |
BARF(TGAERR_CMAP_TYPE);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
READ(&dest->image_type, 1);
|
|
Shinya Kitaoka |
120a6e |
if (dest->image_type == TGA_IMAGE_TYPE_NONE) BARF(TGAERR_NO_IMG);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (dest->image_type != TGA_IMAGE_TYPE_COLORMAP &&
|
|
Shinya Kitaoka |
120a6e |
dest->image_type != TGA_IMAGE_TYPE_BGR &&
|
|
Shinya Kitaoka |
120a6e |
dest->image_type != TGA_IMAGE_TYPE_MONO &&
|
|
Shinya Kitaoka |
120a6e |
dest->image_type != TGA_IMAGE_TYPE_COLORMAP_RLE &&
|
|
Shinya Kitaoka |
120a6e |
dest->image_type != TGA_IMAGE_TYPE_BGR_RLE &&
|
|
Shinya Kitaoka |
120a6e |
dest->image_type != TGA_IMAGE_TYPE_MONO_RLE)
|
|
Shinya Kitaoka |
120a6e |
BARF(TGAERR_IMG_TYPE);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (tga_is_colormapped(dest) && dest->color_map_type == TGA_COLOR_MAP_ABSENT)
|
|
Shinya Kitaoka |
120a6e |
BARF(TGAERR_CMAP_MISSING);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (!tga_is_colormapped(dest) &&
|
|
Shinya Kitaoka |
120a6e |
dest->color_map_type == TGA_COLOR_MAP_PRESENT)
|
|
Shinya Kitaoka |
120a6e |
BARF(TGAERR_CMAP_PRESENT);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
READ16(dest->color_map_origin);
|
|
Shinya Kitaoka |
120a6e |
READ16(dest->color_map_length);
|
|
Shinya Kitaoka |
120a6e |
READ(&dest->color_map_depth, 1);
|
|
Shinya Kitaoka |
120a6e |
if (dest->color_map_type == TGA_COLOR_MAP_PRESENT) {
|
|
Shinya Kitaoka |
120a6e |
if (dest->color_map_length == 0) BARF(TGAERR_CMAP_LENGTH);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (!UNMAP_DEPTH(dest->color_map_depth)) BARF(TGAERR_CMAP_DEPTH);
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
READ16(dest->origin_x);
|
|
Shinya Kitaoka |
120a6e |
READ16(dest->origin_y);
|
|
Shinya Kitaoka |
120a6e |
READ16(dest->width);
|
|
Shinya Kitaoka |
120a6e |
READ16(dest->height);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (dest->width == 0 || dest->height == 0) BARF(TGAERR_ZERO_SIZE);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
READ(&dest->pixel_depth, 1);
|
|
Shinya Kitaoka |
120a6e |
if (!SANE_DEPTH(dest->pixel_depth) ||
|
|
Shinya Kitaoka |
120a6e |
(dest->pixel_depth != 8 && tga_is_colormapped(dest)))
|
|
Shinya Kitaoka |
120a6e |
BARF(TGAERR_PIXEL_DEPTH);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
READ(&dest->image_descriptor, 1);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (dest->image_id_length > 0) {
|
|
Shinya Kitaoka |
120a6e |
dest->image_id = (uint8_t *)malloc(dest->image_id_length);
|
|
Shinya Kitaoka |
120a6e |
if (dest->image_id == NULL) BARF(TGAERR_NO_MEM);
|
|
Shinya Kitaoka |
120a6e |
READ(dest->image_id, dest->image_id_length);
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (dest->color_map_type == TGA_COLOR_MAP_PRESENT) {
|
|
Shinya Kitaoka |
120a6e |
dest->color_map_data =
|
|
Shinya Kitaoka |
120a6e |
(uint8_t *)malloc((dest->color_map_origin + dest->color_map_length) *
|
|
Shinya Kitaoka |
120a6e |
dest->color_map_depth / 8);
|
|
Shinya Kitaoka |
120a6e |
if (dest->color_map_data == NULL) BARF(TGAERR_NO_MEM);
|
|
Shinya Kitaoka |
120a6e |
READ(dest->color_map_data +
|
|
Shinya Kitaoka |
120a6e |
(dest->color_map_origin * dest->color_map_depth / 8),
|
|
Shinya Kitaoka |
120a6e |
dest->color_map_length * dest->color_map_depth / 8);
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
dest->image_data =
|
|
Shinya Kitaoka |
120a6e |
(uint8_t *)malloc(dest->width * dest->height * dest->pixel_depth / 8);
|
|
Shinya Kitaoka |
120a6e |
if (dest->image_data == NULL) BARF(TGAERR_NO_MEM);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (tga_is_rle(dest)) {
|
|
Shinya Kitaoka |
120a6e |
/* read RLE */
|
|
Shinya Kitaoka |
120a6e |
tga_result result = tga_read_rle(dest, fp);
|
|
Shinya Kitaoka |
120a6e |
if (result != TGA_NOERR) BARF(result);
|
|
Shinya Kitaoka |
120a6e |
} else {
|
|
Shinya Kitaoka |
120a6e |
/* uncompressed */
|
|
Shinya Kitaoka |
120a6e |
READ(dest->image_data, dest->width * dest->height * dest->pixel_depth / 8);
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
return TGA_NOERR;
|
|
Toshihiro Shimizu |
890ddd |
#undef BARF
|
|
Toshihiro Shimizu |
890ddd |
#undef READ
|
|
Toshihiro Shimizu |
890ddd |
#undef READ16
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* ---------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
* Helper function for tga_read_from_FILE(). Decompresses RLE image data from
|
|
Toshihiro Shimizu |
890ddd |
* <fp>. Assumes <dest> header fields are set correctly.</dest></fp>
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Shinya Kitaoka |
120a6e |
static tga_result tga_read_rle(tga_image *dest, FILE *fp) {
|
|
Toshihiro Shimizu |
890ddd |
#define RLE_BIT BIT(7)
|
|
Shinya Kitaoka |
120a6e |
#define READ(dest, size) \
|
|
Shinya Kitaoka |
120a6e |
if (fread(dest, size, 1, fp) != 1) return TGAERR_EOF
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
uint8_t *pos;
|
|
Shinya Kitaoka |
120a6e |
uint32_t p_loaded = 0, p_expected = dest->width * dest->height;
|
|
Shinya Kitaoka |
120a6e |
uint8_t bpp = dest->pixel_depth / 8; /* bytes per pixel */
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
pos = dest->image_data;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
while ((p_loaded < p_expected) && !feof(fp)) {
|
|
Shinya Kitaoka |
120a6e |
uint8_t b;
|
|
Shinya Kitaoka |
120a6e |
READ(&b, 1);
|
|
Shinya Kitaoka |
120a6e |
if (b & RLE_BIT) {
|
|
Shinya Kitaoka |
120a6e |
/* is an RLE packet */
|
|
Shinya Kitaoka |
120a6e |
uint8_t count, tmp[4], i;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
count = (b & ~RLE_BIT) + 1;
|
|
Shinya Kitaoka |
120a6e |
READ(tmp, bpp);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
for (i = 0; i < count; i++) {
|
|
Shinya Kitaoka |
120a6e |
p_loaded++;
|
|
Shinya Kitaoka |
120a6e |
if (p_loaded > p_expected) return TGAERR_RLE;
|
|
Shinya Kitaoka |
120a6e |
memcpy(pos, tmp, bpp);
|
|
Shinya Kitaoka |
120a6e |
pos += bpp;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
} else /* RAW packet */
|
|
Shinya Kitaoka |
120a6e |
{
|
|
Shinya Kitaoka |
120a6e |
uint8_t count;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
count = (b & ~RLE_BIT) + 1;
|
|
Shinya Kitaoka |
120a6e |
if (p_loaded + count > p_expected) return TGAERR_RLE;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
p_loaded += count;
|
|
Shinya Kitaoka |
120a6e |
READ(pos, bpp * count);
|
|
Shinya Kitaoka |
120a6e |
pos += count * bpp;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
return TGA_NOERR;
|
|
Toshihiro Shimizu |
890ddd |
#undef RLE_BIT
|
|
Toshihiro Shimizu |
890ddd |
#undef READ
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* ---------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
* Write a Targa image to a file named <filename> from <src>. This is just a</src></filename>
|
|
Toshihiro Shimizu |
890ddd |
* wrapper around tga_write_to_FILE().
|
|
Toshihiro Shimizu |
890ddd |
*
|
|
Toshihiro Shimizu |
890ddd |
* Returns: TGA_NOERR on success, or a matching TGAERR_* code on failure.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Shinya Kitaoka |
120a6e |
tga_result tga_write(const char *filename, const tga_image *src) {
|
|
Shinya Kitaoka |
120a6e |
tga_result result;
|
|
Shinya Kitaoka |
120a6e |
FILE *fp = fopen(filename, "wb");
|
|
Shinya Kitaoka |
120a6e |
if (fp == NULL) return TGAERR_FOPEN;
|
|
Shinya Kitaoka |
120a6e |
result = tga_write_to_FILE(fp, src);
|
|
Shinya Kitaoka |
120a6e |
fclose(fp);
|
|
Shinya Kitaoka |
120a6e |
return result;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* ---------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
* Write one row of an image to <fp> using RLE. This is a helper function</fp>
|
|
Toshihiro Shimizu |
890ddd |
* called from tga_write_to_FILE(). It assumes that <src> has its header</src>
|
|
Toshihiro Shimizu |
890ddd |
* fields set up correctly.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Toshihiro Shimizu |
890ddd |
#define PIXEL(ofs) (row + (ofs)*bpp)
|
|
Shinya Kitaoka |
120a6e |
static tga_result tga_write_row_RLE(FILE *fp, const tga_image *src,
|
|
Shinya Kitaoka |
120a6e |
const uint8_t *row) {
|
|
Shinya Kitaoka |
120a6e |
#define WRITE(src, size) \
|
|
Shinya Kitaoka |
120a6e |
if (fwrite(src, size, 1, fp) != 1) return TGAERR_WRITE
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
uint16_t pos = 0;
|
|
Shinya Kitaoka |
120a6e |
uint16_t bpp = src->pixel_depth / 8;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
while (pos < src->width) {
|
|
Shinya Kitaoka |
120a6e |
packet_type type = rle_packet_type(row, pos, src->width, bpp);
|
|
Shinya Kitaoka |
120a6e |
uint8_t len = rle_packet_len(row, pos, src->width, bpp, type);
|
|
Shinya Kitaoka |
120a6e |
uint8_t packet_header;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
packet_header = len - 1;
|
|
Shinya Kitaoka |
120a6e |
if (type == RLE) packet_header |= BIT(7);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
WRITE(&packet_header, 1);
|
|
Shinya Kitaoka |
120a6e |
if (type == RLE) {
|
|
Shinya Kitaoka |
120a6e |
WRITE(PIXEL(pos), bpp);
|
|
Shinya Kitaoka |
120a6e |
} else /* type == RAW */
|
|
Shinya Kitaoka |
120a6e |
{
|
|
Shinya Kitaoka |
120a6e |
WRITE(PIXEL(pos), bpp * len);
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
pos += len;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
return TGA_NOERR;
|
|
Toshihiro Shimizu |
890ddd |
#undef WRITE
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* ---------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
* Determine whether the next packet should be RAW or RLE for maximum
|
|
Toshihiro Shimizu |
890ddd |
* efficiency. This is a helper function called from rle_packet_len() and
|
|
Toshihiro Shimizu |
890ddd |
* tga_write_row_RLE().
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Toshihiro Shimizu |
890ddd |
#define SAME(ofs1, ofs2) (memcmp(PIXEL(ofs1), PIXEL(ofs2), bpp) == 0)
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
static packet_type rle_packet_type(const uint8_t *row, const uint16_t pos,
|
|
Shinya Kitaoka |
120a6e |
const uint16_t width, const uint16_t bpp) {
|
|
Shinya Kitaoka |
120a6e |
if (pos == width - 1) return RAW; /* one pixel */
|
|
Shinya Kitaoka |
120a6e |
if (SAME(pos, pos + 1)) /* dupe pixel */
|
|
Shinya Kitaoka |
120a6e |
{
|
|
Shinya Kitaoka |
120a6e |
if (bpp > 1) return RLE; /* inefficient for bpp=1 */
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
/* three repeats makes the bpp=1 case efficient enough */
|
|
Shinya Kitaoka |
120a6e |
if ((pos < width - 2) && SAME(pos + 1, pos + 2)) return RLE;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
return RAW;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* ---------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
* Find the length of the current RLE packet. This is a helper function
|
|
Toshihiro Shimizu |
890ddd |
* called from tga_write_row_RLE().
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Toshihiro Shimizu |
890ddd |
static uint8_t rle_packet_len(const uint8_t *row, const uint16_t pos,
|
|
Shinya Kitaoka |
120a6e |
const uint16_t width, const uint16_t bpp,
|
|
Shinya Kitaoka |
120a6e |
const packet_type type) {
|
|
Shinya Kitaoka |
120a6e |
uint8_t len = 2;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (pos == width - 1) return 1;
|
|
Shinya Kitaoka |
120a6e |
if (pos == width - 2) return 2;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (type == RLE) {
|
|
Shinya Kitaoka |
120a6e |
while (pos + len < width) {
|
|
Shinya Kitaoka |
120a6e |
if (SAME(pos, pos + len))
|
|
Shinya Kitaoka |
120a6e |
len++;
|
|
Shinya Kitaoka |
120a6e |
else
|
|
Shinya Kitaoka |
120a6e |
return len;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (len == 128) return 128;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
} else /* type == RAW */
|
|
Shinya Kitaoka |
120a6e |
{
|
|
Shinya Kitaoka |
120a6e |
while (pos + len < width) {
|
|
Shinya Kitaoka |
120a6e |
if (rle_packet_type(row, pos + len, width, bpp) == RAW)
|
|
Shinya Kitaoka |
120a6e |
len++;
|
|
Shinya Kitaoka |
120a6e |
else
|
|
Shinya Kitaoka |
120a6e |
return len;
|
|
Shinya Kitaoka |
120a6e |
if (len == 128) return 128;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
return len; /* hit end of row (width) */
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
#undef SAME
|
|
Toshihiro Shimizu |
890ddd |
#undef PIXEL
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* ---------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
* Writes a Targa image to <fp> from <src>.</src></fp>
|
|
Toshihiro Shimizu |
890ddd |
*
|
|
Toshihiro Shimizu |
890ddd |
* Returns: TGA_NOERR on success, or a TGAERR_* code on failure.
|
|
Toshihiro Shimizu |
890ddd |
* On failure, the contents of the file are not guaranteed
|
|
Toshihiro Shimizu |
890ddd |
* to be valid.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Shinya Kitaoka |
120a6e |
tga_result tga_write_to_FILE(FILE *fp, const tga_image *src) {
|
|
Shinya Kitaoka |
120a6e |
#define WRITE(srcptr, size) \
|
|
Shinya Kitaoka |
120a6e |
if (fwrite(srcptr, size, 1, fp) != 1) return TGAERR_WRITE
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
#define WRITE16(src) \
|
|
Shinya Kitaoka |
120a6e |
{ \
|
|
Shinya Kitaoka |
120a6e |
uint16_t _temp = htole16(src); \
|
|
Shinya Kitaoka |
120a6e |
if (fwrite(&_temp, 2, 1, fp) != 1) return TGAERR_WRITE; \
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
WRITE(&src->image_id_length, 1);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (src->color_map_type != TGA_COLOR_MAP_ABSENT &&
|
|
Shinya Kitaoka |
120a6e |
src->color_map_type != TGA_COLOR_MAP_PRESENT)
|
|
Shinya Kitaoka |
120a6e |
return TGAERR_CMAP_TYPE;
|
|
Shinya Kitaoka |
120a6e |
WRITE(&src->color_map_type, 1);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (src->image_type == TGA_IMAGE_TYPE_NONE) return TGAERR_NO_IMG;
|
|
Shinya Kitaoka |
120a6e |
if (src->image_type != TGA_IMAGE_TYPE_COLORMAP &&
|
|
Shinya Kitaoka |
120a6e |
src->image_type != TGA_IMAGE_TYPE_BGR &&
|
|
Shinya Kitaoka |
120a6e |
src->image_type != TGA_IMAGE_TYPE_MONO &&
|
|
Shinya Kitaoka |
120a6e |
src->image_type != TGA_IMAGE_TYPE_COLORMAP_RLE &&
|
|
Shinya Kitaoka |
120a6e |
src->image_type != TGA_IMAGE_TYPE_BGR_RLE &&
|
|
Shinya Kitaoka |
120a6e |
src->image_type != TGA_IMAGE_TYPE_MONO_RLE)
|
|
Shinya Kitaoka |
120a6e |
return TGAERR_IMG_TYPE;
|
|
Shinya Kitaoka |
120a6e |
WRITE(&src->image_type, 1);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (tga_is_colormapped(src) && src->color_map_type == TGA_COLOR_MAP_ABSENT)
|
|
Shinya Kitaoka |
120a6e |
return TGAERR_CMAP_MISSING;
|
|
Shinya Kitaoka |
120a6e |
if (!tga_is_colormapped(src) && src->color_map_type == TGA_COLOR_MAP_PRESENT)
|
|
Shinya Kitaoka |
120a6e |
return TGAERR_CMAP_PRESENT;
|
|
Shinya Kitaoka |
120a6e |
if (src->color_map_type == TGA_COLOR_MAP_PRESENT) {
|
|
Shinya Kitaoka |
120a6e |
if (src->color_map_length == 0) return TGAERR_CMAP_LENGTH;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (!UNMAP_DEPTH(src->color_map_depth)) return TGAERR_CMAP_DEPTH;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
WRITE16(src->color_map_origin);
|
|
Shinya Kitaoka |
120a6e |
WRITE16(src->color_map_length);
|
|
Shinya Kitaoka |
120a6e |
WRITE(&src->color_map_depth, 1);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
WRITE16(src->origin_x);
|
|
Shinya Kitaoka |
120a6e |
WRITE16(src->origin_y);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (src->width == 0 || src->height == 0) return TGAERR_ZERO_SIZE;
|
|
Shinya Kitaoka |
120a6e |
WRITE16(src->width);
|
|
Shinya Kitaoka |
120a6e |
WRITE16(src->height);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (!SANE_DEPTH(src->pixel_depth) ||
|
|
Shinya Kitaoka |
120a6e |
(src->pixel_depth != 8 && tga_is_colormapped(src)))
|
|
Shinya Kitaoka |
120a6e |
return TGAERR_PIXEL_DEPTH;
|
|
Shinya Kitaoka |
120a6e |
WRITE(&src->pixel_depth, 1);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
WRITE(&src->image_descriptor, 1);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (src->image_id_length > 0) WRITE(&src->image_id, src->image_id_length);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (src->color_map_type == TGA_COLOR_MAP_PRESENT)
|
|
Shinya Kitaoka |
120a6e |
WRITE(src->color_map_data +
|
|
Shinya Kitaoka |
120a6e |
(src->color_map_origin * src->color_map_depth / 8),
|
|
Shinya Kitaoka |
120a6e |
src->color_map_length * src->color_map_depth / 8);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (tga_is_rle(src)) {
|
|
Shinya Kitaoka |
120a6e |
uint16_t row;
|
|
Shinya Kitaoka |
120a6e |
for (row = 0; row < src->height; row++) {
|
|
Shinya Kitaoka |
120a6e |
tga_result result = tga_write_row_RLE(
|
|
Shinya Kitaoka |
120a6e |
fp, src, src->image_data + row * src->width * src->pixel_depth / 8);
|
|
Shinya Kitaoka |
120a6e |
if (result != TGA_NOERR) return result;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
} else {
|
|
Shinya Kitaoka |
120a6e |
/* uncompressed */
|
|
Shinya Kitaoka |
120a6e |
WRITE(src->image_data, src->width * src->height * src->pixel_depth / 8);
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
WRITE(tga_id, tga_id_length);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
return TGA_NOERR;
|
|
Toshihiro Shimizu |
890ddd |
#undef WRITE
|
|
Toshihiro Shimizu |
890ddd |
#undef WRITE16
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* Convenient writing functions --------------------------------------------*/
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/*
|
|
Toshihiro Shimizu |
890ddd |
* This is just a helper function to initialise the header fields in a
|
|
Toshihiro Shimizu |
890ddd |
* tga_image struct.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Shinya Kitaoka |
120a6e |
static void init_tga_image(tga_image *img, uint8_t *image, const uint16_t width,
|
|
Shinya Kitaoka |
120a6e |
const uint16_t height, const uint8_t depth) {
|
|
Shinya Kitaoka |
120a6e |
img->image_id_length = 0;
|
|
Shinya Kitaoka |
120a6e |
img->color_map_type = TGA_COLOR_MAP_ABSENT;
|
|
Shinya Kitaoka |
120a6e |
img->image_type = TGA_IMAGE_TYPE_NONE; /* override this below! */
|
|
Shinya Kitaoka |
120a6e |
img->color_map_origin = 0;
|
|
Shinya Kitaoka |
120a6e |
img->color_map_length = 0;
|
|
Shinya Kitaoka |
120a6e |
img->color_map_depth = 0;
|
|
Shinya Kitaoka |
120a6e |
img->origin_x = 0;
|
|
Shinya Kitaoka |
120a6e |
img->origin_y = 0;
|
|
Shinya Kitaoka |
120a6e |
img->width = width;
|
|
Shinya Kitaoka |
120a6e |
img->height = height;
|
|
Shinya Kitaoka |
120a6e |
img->pixel_depth = depth;
|
|
Shinya Kitaoka |
120a6e |
img->image_descriptor = TGA_T_TO_B_BIT;
|
|
Shinya Kitaoka |
120a6e |
img->image_id = NULL;
|
|
Shinya Kitaoka |
120a6e |
img->color_map_data = NULL;
|
|
Shinya Kitaoka |
120a6e |
img->image_data = image;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
tga_result tga_write_mono(const char *filename, uint8_t *image,
|
|
Shinya Kitaoka |
120a6e |
const uint16_t width, const uint16_t height) {
|
|
Shinya Kitaoka |
120a6e |
tga_image img;
|
|
Shinya Kitaoka |
120a6e |
init_tga_image(&img, image, width, height, 8);
|
|
Shinya Kitaoka |
120a6e |
img.image_type = TGA_IMAGE_TYPE_MONO;
|
|
Shinya Kitaoka |
120a6e |
return tga_write(filename, &img);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
tga_result tga_write_mono_rle(const char *filename, uint8_t *image,
|
|
Shinya Kitaoka |
120a6e |
const uint16_t width, const uint16_t height) {
|
|
Shinya Kitaoka |
120a6e |
tga_image img;
|
|
Shinya Kitaoka |
120a6e |
init_tga_image(&img, image, width, height, 8);
|
|
Shinya Kitaoka |
120a6e |
img.image_type = TGA_IMAGE_TYPE_MONO_RLE;
|
|
Shinya Kitaoka |
120a6e |
return tga_write(filename, &img);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
tga_result tga_write_bgr(const char *filename, uint8_t *image,
|
|
Shinya Kitaoka |
120a6e |
const uint16_t width, const uint16_t height,
|
|
Shinya Kitaoka |
120a6e |
const uint8_t depth) {
|
|
Shinya Kitaoka |
120a6e |
tga_image img;
|
|
Shinya Kitaoka |
120a6e |
init_tga_image(&img, image, width, height, depth);
|
|
Shinya Kitaoka |
120a6e |
img.image_type = TGA_IMAGE_TYPE_BGR;
|
|
Shinya Kitaoka |
120a6e |
return tga_write(filename, &img);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
tga_result tga_write_bgr_rle(const char *filename, uint8_t *image,
|
|
Shinya Kitaoka |
120a6e |
const uint16_t width, const uint16_t height,
|
|
Shinya Kitaoka |
120a6e |
const uint8_t depth) {
|
|
Shinya Kitaoka |
120a6e |
tga_image img;
|
|
Shinya Kitaoka |
120a6e |
init_tga_image(&img, image, width, height, depth);
|
|
Shinya Kitaoka |
120a6e |
img.image_type = TGA_IMAGE_TYPE_BGR_RLE;
|
|
Shinya Kitaoka |
120a6e |
return tga_write(filename, &img);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* Note: this function will MODIFY */
|
|
Toshihiro Shimizu |
890ddd |
tga_result tga_write_rgb(const char *filename, uint8_t *image,
|
|
Shinya Kitaoka |
120a6e |
const uint16_t width, const uint16_t height,
|
|
Shinya Kitaoka |
120a6e |
const uint8_t depth) {
|
|
Shinya Kitaoka |
120a6e |
tga_image img;
|
|
Shinya Kitaoka |
120a6e |
init_tga_image(&img, image, width, height, depth);
|
|
Shinya Kitaoka |
120a6e |
img.image_type = TGA_IMAGE_TYPE_BGR;
|
|
Shinya Kitaoka |
120a6e |
(void)tga_swap_red_blue(&img);
|
|
Shinya Kitaoka |
120a6e |
return tga_write(filename, &img);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* Note: this function will MODIFY */
|
|
Toshihiro Shimizu |
890ddd |
tga_result tga_write_rgb_rle(const char *filename, uint8_t *image,
|
|
Shinya Kitaoka |
120a6e |
const uint16_t width, const uint16_t height,
|
|
Shinya Kitaoka |
120a6e |
const uint8_t depth) {
|
|
Shinya Kitaoka |
120a6e |
tga_image img;
|
|
Shinya Kitaoka |
120a6e |
init_tga_image(&img, image, width, height, depth);
|
|
Shinya Kitaoka |
120a6e |
img.image_type = TGA_IMAGE_TYPE_BGR_RLE;
|
|
Shinya Kitaoka |
120a6e |
(void)tga_swap_red_blue(&img);
|
|
Shinya Kitaoka |
120a6e |
return tga_write(filename, &img);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* Convenient manipulation functions ---------------------------------------*/
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* ---------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
* Horizontally flip the image in place. Reverses the right-to-left bit in
|
|
Toshihiro Shimizu |
890ddd |
* the image descriptor.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Shinya Kitaoka |
120a6e |
tga_result tga_flip_horiz(tga_image *img) {
|
|
Shinya Kitaoka |
120a6e |
uint16_t row;
|
|
Shinya Kitaoka |
120a6e |
size_t bpp;
|
|
Shinya Kitaoka |
120a6e |
uint8_t *left, *right;
|
|
Shinya Kitaoka |
120a6e |
int r_to_l;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (!SANE_DEPTH(img->pixel_depth)) return TGAERR_PIXEL_DEPTH;
|
|
Shinya Kitaoka |
120a6e |
bpp = (size_t)(img->pixel_depth / 8); /* bytes per pixel */
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
for (row = 0; row < img->height; row++) {
|
|
Shinya Kitaoka |
120a6e |
left = img->image_data + row * img->width * bpp;
|
|
Shinya Kitaoka |
120a6e |
right = left + (img->width - 1) * bpp;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
/* reverse from left to right */
|
|
Shinya Kitaoka |
120a6e |
while (left < right) {
|
|
Shinya Kitaoka |
120a6e |
uint8_t buffer[4];
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
/* swap */
|
|
Shinya Kitaoka |
120a6e |
memcpy(buffer, left, bpp);
|
|
Shinya Kitaoka |
120a6e |
memcpy(left, right, bpp);
|
|
Shinya Kitaoka |
120a6e |
memcpy(right, buffer, bpp);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
left += bpp;
|
|
Shinya Kitaoka |
120a6e |
right -= bpp;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
/* Correct image_descriptor's left-to-right-ness. */
|
|
Shinya Kitaoka |
120a6e |
r_to_l = tga_is_right_to_left(img);
|
|
Shinya Kitaoka |
120a6e |
img->image_descriptor &= ~TGA_R_TO_L_BIT; /* mask out r-to-l bit */
|
|
Shinya Kitaoka |
120a6e |
if (!r_to_l) /* was l-to-r, need to set r_to_l */
|
|
Shinya Kitaoka |
120a6e |
img->image_descriptor |= TGA_R_TO_L_BIT;
|
|
Shinya Kitaoka |
120a6e |
/* else bit is already rubbed out */
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
return TGA_NOERR;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* ---------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
* Vertically flip the image in place. Reverses the top-to-bottom bit in
|
|
Toshihiro Shimizu |
890ddd |
* the image descriptor.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Shinya Kitaoka |
120a6e |
tga_result tga_flip_vert(tga_image *img) {
|
|
Shinya Kitaoka |
120a6e |
uint16_t col;
|
|
Shinya Kitaoka |
120a6e |
size_t bpp, line;
|
|
Shinya Kitaoka |
120a6e |
uint8_t *top, *bottom;
|
|
Shinya Kitaoka |
120a6e |
int t_to_b;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (!SANE_DEPTH(img->pixel_depth)) return TGAERR_PIXEL_DEPTH;
|
|
Shinya Kitaoka |
120a6e |
bpp = (size_t)(img->pixel_depth / 8); /* bytes per pixel */
|
|
Shinya Kitaoka |
120a6e |
line = bpp * img->width; /* bytes per line */
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
for (col = 0; col < img->width; col++) {
|
|
Shinya Kitaoka |
120a6e |
top = img->image_data + col * bpp;
|
|
Shinya Kitaoka |
120a6e |
bottom = top + (img->height - 1) * line;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
/* reverse from top to bottom */
|
|
Shinya Kitaoka |
120a6e |
while (top < bottom) {
|
|
Shinya Kitaoka |
120a6e |
uint8_t buffer[4];
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
/* swap */
|
|
Shinya Kitaoka |
120a6e |
memcpy(buffer, top, bpp);
|
|
Shinya Kitaoka |
120a6e |
memcpy(top, bottom, bpp);
|
|
Shinya Kitaoka |
120a6e |
memcpy(bottom, buffer, bpp);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
top += line;
|
|
Shinya Kitaoka |
120a6e |
bottom -= line;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
/* Correct image_descriptor's top-to-bottom-ness. */
|
|
Shinya Kitaoka |
120a6e |
t_to_b = tga_is_top_to_bottom(img);
|
|
Shinya Kitaoka |
120a6e |
img->image_descriptor &= ~TGA_T_TO_B_BIT; /* mask out t-to-b bit */
|
|
Shinya Kitaoka |
120a6e |
if (!t_to_b) /* was b-to-t, need to set t_to_b */
|
|
Shinya Kitaoka |
120a6e |
img->image_descriptor |= TGA_T_TO_B_BIT;
|
|
Shinya Kitaoka |
120a6e |
/* else bit is already rubbed out */
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
return TGA_NOERR;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* ---------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
* Convert a color-mapped image to unmapped BGR. Reallocates image_data to a
|
|
Toshihiro Shimizu |
890ddd |
* bigger size, then converts the image backwards to avoid using a secondary
|
|
Toshihiro Shimizu |
890ddd |
* buffer. Alters the necessary header fields and deallocates the color map.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Shinya Kitaoka |
120a6e |
tga_result tga_color_unmap(tga_image *img) {
|
|
Shinya Kitaoka |
120a6e |
uint8_t bpp = img->color_map_depth / 8; /* bytes per pixel */
|
|
Shinya Kitaoka |
120a6e |
int pos;
|
|
Shinya Kitaoka |
120a6e |
void *tmp;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (!tga_is_colormapped(img)) return TGAERR_NOT_CMAP;
|
|
Shinya Kitaoka |
120a6e |
if (img->pixel_depth != 8) return TGAERR_PIXEL_DEPTH;
|
|
Shinya Kitaoka |
120a6e |
if (!SANE_DEPTH(img->color_map_depth)) return TGAERR_CMAP_DEPTH;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
tmp = realloc(img->image_data, img->width * img->height * bpp);
|
|
Shinya Kitaoka |
120a6e |
if (tmp == NULL) return TGAERR_NO_MEM;
|
|
Shinya Kitaoka |
120a6e |
img->image_data = (uint8_t *)tmp;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
for (pos = img->width * img->height - 1; pos >= 0; pos--) {
|
|
Shinya Kitaoka |
120a6e |
uint8_t c_index = img->image_data[pos];
|
|
Shinya Kitaoka |
120a6e |
uint8_t *c_bgr = img->color_map_data + (c_index * bpp);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (c_index >= img->color_map_origin + img->color_map_length)
|
|
Shinya Kitaoka |
120a6e |
return TGAERR_INDEX_RANGE;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
memcpy(img->image_data + (pos * bpp), c_bgr, (size_t)bpp);
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
/* clean up */
|
|
Shinya Kitaoka |
120a6e |
img->image_type = TGA_IMAGE_TYPE_BGR;
|
|
Shinya Kitaoka |
120a6e |
img->pixel_depth = img->color_map_depth;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
free(img->color_map_data);
|
|
Shinya Kitaoka |
120a6e |
img->color_map_data = NULL;
|
|
Shinya Kitaoka |
120a6e |
img->color_map_type = TGA_COLOR_MAP_ABSENT;
|
|
Shinya Kitaoka |
120a6e |
img->color_map_origin = 0;
|
|
Shinya Kitaoka |
120a6e |
img->color_map_length = 0;
|
|
Shinya Kitaoka |
120a6e |
img->color_map_depth = 0;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
return TGA_NOERR;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* ---------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
* Return a pointer to a given pixel. Accounts for image orientation (T_TO_B,
|
|
Toshihiro Shimizu |
890ddd |
* R_TO_L, etc). Returns NULL if the pixel is out of range.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Shinya Kitaoka |
120a6e |
uint8_t *tga_find_pixel(const tga_image *img, uint16_t x, uint16_t y) {
|
|
Shinya Kitaoka |
120a6e |
if (x >= img->width || y >= img->height) return NULL;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (!tga_is_top_to_bottom(img)) y = img->height - 1 - y;
|
|
Shinya Kitaoka |
120a6e |
if (tga_is_right_to_left(img)) x = img->width - 1 - x;
|
|
Shinya Kitaoka |
120a6e |
return img->image_data + (x + y * img->width) * img->pixel_depth / 8;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* ---------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
* Unpack the pixel at the src pointer according to bits. Any of b,g,r,a can
|
|
Toshihiro Shimizu |
890ddd |
* be set to NULL if not wanted. Returns TGAERR_PIXEL_DEPTH if a stupid
|
|
Toshihiro Shimizu |
890ddd |
* number of bits is given.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Shinya Kitaoka |
120a6e |
tga_result tga_unpack_pixel(const uint8_t *src, const uint8_t bits, uint8_t *b,
|
|
Shinya Kitaoka |
120a6e |
uint8_t *g, uint8_t *r, uint8_t *a) {
|
|
Shinya Kitaoka |
120a6e |
switch (bits) {
|
|
Shinya Kitaoka |
120a6e |
case 32:
|
|
Shinya Kitaoka |
120a6e |
if (b) *b = src[0];
|
|
Shinya Kitaoka |
120a6e |
if (g) *g = src[1];
|
|
Shinya Kitaoka |
120a6e |
if (r) *r = src[2];
|
|
Shinya Kitaoka |
120a6e |
if (a) *a = src[3];
|
|
Shinya Kitaoka |
120a6e |
break;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
case 24:
|
|
Shinya Kitaoka |
120a6e |
if (b) *b = src[0];
|
|
Shinya Kitaoka |
120a6e |
if (g) *g = src[1];
|
|
Shinya Kitaoka |
120a6e |
if (r) *r = src[2];
|
|
Shinya Kitaoka |
120a6e |
if (a) *a = 0;
|
|
Shinya Kitaoka |
120a6e |
break;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
case 16: {
|
|
Shinya Kitaoka |
120a6e |
uint16_t src16 = (uint16_t)(src[1] << 8) | (uint16_t)src[0];
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
#define FIVE_BITS (BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4))
|
|
Shinya Kitaoka |
120a6e |
if (b) *b = ((src16)&FIVE_BITS) << 3;
|
|
Shinya Kitaoka |
120a6e |
if (g) *g = ((src16 >> 5) & FIVE_BITS) << 3;
|
|
Shinya Kitaoka |
120a6e |
if (r) *r = ((src16 >> 10) & FIVE_BITS) << 3;
|
|
Shinya Kitaoka |
120a6e |
if (a) *a = (uint8_t)((src16 & BIT(15)) ? 255 : 0);
|
|
Toshihiro Shimizu |
890ddd |
#undef FIVE_BITS
|
|
Shinya Kitaoka |
120a6e |
break;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
case 8:
|
|
Shinya Kitaoka |
120a6e |
if (b) *b = *src;
|
|
Shinya Kitaoka |
120a6e |
if (g) *g = *src;
|
|
Shinya Kitaoka |
120a6e |
if (r) *r = *src;
|
|
Shinya Kitaoka |
120a6e |
if (a) *a = 0;
|
|
Shinya Kitaoka |
120a6e |
break;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
default:
|
|
Shinya Kitaoka |
120a6e |
return TGAERR_PIXEL_DEPTH;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
return TGA_NOERR;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* ---------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
* Pack the pixel at the dest pointer according to bits. Returns
|
|
Toshihiro Shimizu |
890ddd |
* TGAERR_PIXEL_DEPTH if a stupid number of bits is given.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Shinya Kitaoka |
120a6e |
tga_result tga_pack_pixel(uint8_t *dest, const uint8_t bits, const uint8_t b,
|
|
Shinya Kitaoka |
120a6e |
const uint8_t g, const uint8_t r, const uint8_t a) {
|
|
Shinya Kitaoka |
120a6e |
switch (bits) {
|
|
Shinya Kitaoka |
120a6e |
case 32:
|
|
Shinya Kitaoka |
120a6e |
dest[0] = b;
|
|
Shinya Kitaoka |
120a6e |
dest[1] = g;
|
|
Shinya Kitaoka |
120a6e |
dest[2] = r;
|
|
Shinya Kitaoka |
120a6e |
dest[3] = a;
|
|
Shinya Kitaoka |
120a6e |
break;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
case 24:
|
|
Shinya Kitaoka |
120a6e |
dest[0] = b;
|
|
Shinya Kitaoka |
120a6e |
dest[1] = g;
|
|
Shinya Kitaoka |
120a6e |
dest[2] = r;
|
|
Shinya Kitaoka |
120a6e |
break;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
case 16: {
|
|
Shinya Kitaoka |
120a6e |
uint16_t tmp;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
#define FIVE_BITS (BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4))
|
|
Shinya Kitaoka |
120a6e |
tmp = (b >> 3) & FIVE_BITS;
|
|
Shinya Kitaoka |
120a6e |
tmp |= ((g >> 3) & FIVE_BITS) << 5;
|
|
Shinya Kitaoka |
120a6e |
tmp |= ((r >> 3) & FIVE_BITS) << 10;
|
|
Shinya Kitaoka |
120a6e |
if (a > 127) tmp |= BIT(15);
|
|
Toshihiro Shimizu |
890ddd |
#undef FIVE_BITS
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
dest[0] = (uint8_t)(tmp & 0x00FF);
|
|
Shinya Kitaoka |
120a6e |
dest[1] = (uint8_t)((tmp & 0xFF00) >> 8);
|
|
Shinya Kitaoka |
120a6e |
break;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
default:
|
|
Shinya Kitaoka |
120a6e |
return TGAERR_PIXEL_DEPTH;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
return TGA_NOERR;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* ---------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
* Desaturate the specified Targa using the specified coefficients:
|
|
Toshihiro Shimizu |
890ddd |
* output = ( red * cr + green * cg + blue * cb ) / dv
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Toshihiro Shimizu |
890ddd |
tga_result tga_desaturate(tga_image *img, const int cr, const int cg,
|
|
Shinya Kitaoka |
120a6e |
const int cb, const int dv) {
|
|
Shinya Kitaoka |
120a6e |
uint8_t bpp = img->pixel_depth / 8; /* bytes per pixel */
|
|
Shinya Kitaoka |
120a6e |
uint8_t *dest, *src, *tmp;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (tga_is_mono(img)) return TGAERR_MONO;
|
|
Shinya Kitaoka |
120a6e |
if (tga_is_colormapped(img)) {
|
|
Shinya Kitaoka |
120a6e |
tga_result result = tga_color_unmap(img);
|
|
Shinya Kitaoka |
120a6e |
if (result != TGA_NOERR) return result;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
if (!UNMAP_DEPTH(img->pixel_depth)) return TGAERR_PIXEL_DEPTH;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
dest = img->image_data;
|
|
Shinya Kitaoka |
120a6e |
for (src = img->image_data;
|
|
Shinya Kitaoka |
120a6e |
src < img->image_data + img->width * img->height * bpp; src += bpp) {
|
|
Shinya Kitaoka |
120a6e |
uint8_t b, g, r;
|
|
Shinya Kitaoka |
120a6e |
(void)tga_unpack_pixel(src, img->pixel_depth, &b, &g, &r, NULL);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
*dest = (uint8_t)(((int)b * cb + (int)g * cg + (int)r * cr) / dv);
|
|
Shinya Kitaoka |
120a6e |
dest++;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
/* shrink */
|
|
Shinya Kitaoka |
120a6e |
tmp = realloc(img->image_data, img->width * img->height);
|
|
Shinya Kitaoka |
120a6e |
if (tmp == NULL) return TGAERR_NO_MEM;
|
|
Shinya Kitaoka |
120a6e |
img->image_data = tmp;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
img->pixel_depth = 8;
|
|
Shinya Kitaoka |
120a6e |
img->image_type = TGA_IMAGE_TYPE_MONO;
|
|
Shinya Kitaoka |
120a6e |
return TGA_NOERR;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
tga_result tga_desaturate_rec_601_1(tga_image *img) {
|
|
Shinya Kitaoka |
120a6e |
return tga_desaturate(img, 2989, 5866, 1145, 10000);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
tga_result tga_desaturate_rec_709(tga_image *img) {
|
|
Shinya Kitaoka |
120a6e |
return tga_desaturate(img, 2126, 7152, 722, 10000);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
tga_result tga_desaturate_itu(tga_image *img) {
|
|
Shinya Kitaoka |
120a6e |
return tga_desaturate(img, 2220, 7067, 713, 10000);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
tga_result tga_desaturate_avg(tga_image *img) {
|
|
Shinya Kitaoka |
120a6e |
return tga_desaturate(img, 1, 1, 1, 3);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* ---------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
* Convert an image to the given pixel depth. (one of 32, 24, 16) Avoids
|
|
Toshihiro Shimizu |
890ddd |
* using a secondary buffer to do the conversion.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Shinya Kitaoka |
120a6e |
tga_result tga_convert_depth(tga_image *img, const uint8_t bits) {
|
|
Shinya Kitaoka |
120a6e |
size_t src_size, dest_size;
|
|
Shinya Kitaoka |
120a6e |
uint8_t src_bpp, dest_bpp;
|
|
Shinya Kitaoka |
120a6e |
uint8_t *src, *dest;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (!UNMAP_DEPTH(bits) || !SANE_DEPTH(img->pixel_depth))
|
|
Shinya Kitaoka |
120a6e |
return TGAERR_PIXEL_DEPTH;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (tga_is_colormapped(img)) {
|
|
Shinya Kitaoka |
120a6e |
tga_result result = tga_color_unmap(img);
|
|
Shinya Kitaoka |
120a6e |
if (result != TGA_NOERR) return result;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (img->pixel_depth == bits) return TGA_NOERR; /* no op, no err */
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
src_bpp = img->pixel_depth / 8;
|
|
Shinya Kitaoka |
120a6e |
dest_bpp = bits / 8;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
src_size = (size_t)(img->width * img->height * src_bpp);
|
|
Shinya Kitaoka |
120a6e |
dest_size = (size_t)(img->width * img->height * dest_bpp);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (src_size > dest_size) {
|
|
Shinya Kitaoka |
120a6e |
void *tmp;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
/* convert forwards */
|
|
Shinya Kitaoka |
120a6e |
dest = img->image_data;
|
|
Shinya Kitaoka |
120a6e |
for (src = img->image_data;
|
|
Shinya Kitaoka |
120a6e |
src < img->image_data + img->width * img->height * src_bpp;
|
|
Shinya Kitaoka |
120a6e |
src += src_bpp) {
|
|
Shinya Kitaoka |
120a6e |
uint8_t r, g, b, a;
|
|
Shinya Kitaoka |
120a6e |
(void)tga_unpack_pixel(src, img->pixel_depth, &r, &g, &b, &a);
|
|
Shinya Kitaoka |
120a6e |
(void)tga_pack_pixel(dest, bits, r, g, b, a);
|
|
Shinya Kitaoka |
120a6e |
dest += dest_bpp;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
/* shrink */
|
|
Shinya Kitaoka |
120a6e |
tmp = realloc(img->image_data, img->width * img->height * dest_bpp);
|
|
Shinya Kitaoka |
120a6e |
if (tmp == NULL) return TGAERR_NO_MEM;
|
|
Shinya Kitaoka |
120a6e |
img->image_data = tmp;
|
|
Shinya Kitaoka |
120a6e |
} else {
|
|
Shinya Kitaoka |
120a6e |
/* expand */
|
|
Shinya Kitaoka |
120a6e |
void *tmp = realloc(img->image_data, img->width * img->height * dest_bpp);
|
|
Shinya Kitaoka |
120a6e |
if (tmp == NULL) return TGAERR_NO_MEM;
|
|
Shinya Kitaoka |
120a6e |
img->image_data = (uint8_t *)tmp;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
/* convert backwards */
|
|
Shinya Kitaoka |
120a6e |
dest = img->image_data + (img->width * img->height - 1) * dest_bpp;
|
|
Shinya Kitaoka |
120a6e |
for (src = img->image_data + (img->width * img->height - 1) * src_bpp;
|
|
Shinya Kitaoka |
120a6e |
src >= img->image_data; src -= src_bpp) {
|
|
Shinya Kitaoka |
120a6e |
uint8_t r, g, b, a;
|
|
Shinya Kitaoka |
120a6e |
(void)tga_unpack_pixel(src, img->pixel_depth, &r, &g, &b, &a);
|
|
Shinya Kitaoka |
120a6e |
(void)tga_pack_pixel(dest, bits, r, g, b, a);
|
|
Shinya Kitaoka |
120a6e |
dest -= dest_bpp;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
img->pixel_depth = bits;
|
|
Shinya Kitaoka |
120a6e |
return TGA_NOERR;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* ---------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
* Swap red and blue (RGB becomes BGR and vice verse). (in-place)
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Shinya Kitaoka |
120a6e |
tga_result tga_swap_red_blue(tga_image *img) {
|
|
Shinya Kitaoka |
120a6e |
uint8_t *ptr;
|
|
Shinya Kitaoka |
120a6e |
uint8_t bpp = img->pixel_depth / 8;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (!UNMAP_DEPTH(img->pixel_depth)) return TGAERR_PIXEL_DEPTH;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
for (ptr = img->image_data;
|
|
Shinya Kitaoka |
120a6e |
ptr < img->image_data + (img->width * img->height - 1) * bpp;
|
|
Shinya Kitaoka |
120a6e |
ptr += bpp) {
|
|
Shinya Kitaoka |
120a6e |
uint8_t r, g, b, a;
|
|
Shinya Kitaoka |
120a6e |
(void)tga_unpack_pixel(ptr, img->pixel_depth, &b, &g, &r, &a);
|
|
Shinya Kitaoka |
120a6e |
(void)tga_pack_pixel(ptr, img->pixel_depth, r, g, b, a);
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
return TGA_NOERR;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* ---------------------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
* Free the image_id, color_map_data and image_data buffers of the specified
|
|
Toshihiro Shimizu |
890ddd |
* tga_image, if they're not already NULL.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Shinya Kitaoka |
120a6e |
void tga_free_buffers(tga_image *img) {
|
|
Shinya Kitaoka |
120a6e |
if (img->image_id != NULL) {
|
|
Shinya Kitaoka |
120a6e |
free(img->image_id);
|
|
Shinya Kitaoka |
120a6e |
img->image_id = NULL;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
if (img->color_map_data != NULL) {
|
|
Shinya Kitaoka |
120a6e |
free(img->color_map_data);
|
|
Shinya Kitaoka |
120a6e |
img->color_map_data = NULL;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
if (img->image_data != NULL) {
|
|
Shinya Kitaoka |
120a6e |
free(img->image_data);
|
|
Shinya Kitaoka |
120a6e |
img->image_data = NULL;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/* vim:set tabstop=4 shiftwidth=4 textwidth=78 expandtab: */
|