Alien-FreeImage
view release on metacpan or search on metacpan
src/Source/FreeImage/PluginTARGA.cpp view on Meta::CPAN
// ==========================================================
// TARGA Loader and Writer
//
// Design and implementation by
// - Floris van den Berg (flvdberg@wxs.nl)
// - Jani Kajala (janik@remedy.fi)
// - Martin Weber (martweb@gmx.net)
// - Machiel ten Brinke (brinkem@uni-one.nl)
// - Peter Lemmens (peter.lemmens@planetinternet.be)
// - Hervé Drolon (drolon@infonie.fr)
// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
//
// This file is part of FreeImage 3
//
// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
// THIS DISCLAIMER.
//
// Use at your own risk!
// ==========================================================
#include "FreeImage.h"
#include "Utilities.h"
// ----------------------------------------------------------
// Constants + headers
// ----------------------------------------------------------
#ifdef _WIN32
#pragma pack(push, 1)
#else
#pragma pack(1)
#endif
typedef struct tagTGAHEADER {
BYTE id_length; //! length of the image ID field
BYTE color_map_type; //! whether a color map is included
BYTE image_type; //! compression and color types
WORD cm_first_entry; //! first entry index (offset into the color map table)
WORD cm_length; //! color map length (number of entries)
BYTE cm_size; //! color map entry size, in bits (number of bits per pixel)
WORD is_xorigin; //! X-origin of image (absolute coordinate of lower-left corner for displays where origin is at the lower left)
WORD is_yorigin; //! Y-origin of image (as for X-origin)
WORD is_width; //! image width
WORD is_height; //! image height
BYTE is_pixel_depth; //! bits per pixel
BYTE is_image_descriptor; //! image descriptor, bits 3-0 give the alpha channel depth, bits 5-4 give direction
} TGAHEADER;
typedef struct tagTGAEXTENSIONAREA {
WORD extension_size; // Size in bytes of the extension area, always 495
char author_name[41]; // Name of the author. If not used, bytes should be set to NULL (\0) or spaces
char author_comments[324]; // A comment, organized as four lines, each consisting of 80 characters plus a NULL
WORD datetime_stamp[6]; // Date and time at which the image was created
char job_name[41]; // Job ID
WORD job_time[3]; // Hours, minutes and seconds spent creating the file (for billing, etc.)
char software_id[41]; // The application that created the file
BYTE software_version[3];
DWORD key_color;
WORD pixel_aspect_ratio[2];
WORD gamma_value[2];
DWORD color_correction_offset; // Number of bytes from the beginning of the file to the color correction table if present
DWORD postage_stamp_offset; // Number of bytes from the beginning of the file to the postage stamp image if present
DWORD scan_line_offset; // Number of bytes from the beginning of the file to the scan lines table if present
BYTE attributes_type; // Specifies the alpha channel
} TGAEXTENSIONAREA;
typedef struct tagTGAFOOTER {
DWORD extension_offset; // extension area offset : offset in bytes from the beginning of the file
DWORD developer_offset; // developer directory offset : offset in bytes from the beginning of the file
char signature[18]; // signature string : contains "TRUEVISION-XFILE.\0"
} TGAFOOTER;
#ifdef _WIN32
#pragma pack(pop)
#else
#pragma pack()
#endif
static const char *FI_MSG_ERROR_CORRUPTED = "Image data corrupted";
// ----------------------------------------------------------
// Image type
//
#define TGA_NULL 0 // no image data included
#define TGA_CMAP 1 // uncompressed, color-mapped image
#define TGA_RGB 2 // uncompressed, true-color image
#define TGA_MONO 3 // uncompressed, black-and-white image
#define TGA_RLECMAP 9 // run-length encoded, color-mapped image
#define TGA_RLERGB 10 // run-length encoded, true-color image
#define TGA_RLEMONO 11 // run-length encoded, black-and-white image
#define TGA_CMPCMAP 32 // compressed (Huffman/Delta/RLE) color-mapped image (e.g., VDA/D) - Obsolete
#define TGA_CMPCMAP4 33 // compressed (Huffman/Delta/RLE) color-mapped four pass image (e.g., VDA/D) - Obsolete
// ==========================================================
// Thumbnail functions
// ==========================================================
class TargaThumbnail
{
public:
TargaThumbnail() : _w(0), _h(0), _depth(0), _data(NULL) {
}
~TargaThumbnail() {
if(_data) {
free(_data);
}
}
BOOL isNull() const {
return (_data == NULL);
}
BOOL read(FreeImageIO *io, fi_handle handle, size_t size) {
io->read_proc(&_w, 1, 1, handle);
io->read_proc(&_h, 1, 1, handle);
const size_t sizeofData = size - 2;
_data = (BYTE*)malloc(sizeofData);
if(_data) {
return (io->read_proc(_data, 1, (unsigned)sizeofData, handle) == sizeofData);
}
return FALSE;
}
void setDepth(BYTE dp) {
_depth = dp;
}
FIBITMAP* toFIBITMAP();
private:
BYTE _w;
BYTE _h;
BYTE _depth;
BYTE* _data;
};
#ifdef FREEIMAGE_BIGENDIAN
static void
swapShortPixels(FIBITMAP* dib) {
if(FreeImage_GetImageType(dib) != FIT_BITMAP) {
return;
}
const unsigned Bpp = FreeImage_GetBPP(dib)/8;
if(Bpp != 2) {
return;
}
BYTE* bits = FreeImage_GetBits(dib);
src/Source/FreeImage/PluginTARGA.cpp view on Meta::CPAN
BOOL isNull() { return _begin == NULL;}
inline
BYTE getByte() {
if (_ptr >= _end) {
// need refill
_ptr = _begin;
_io->read_proc(_ptr, sizeof(BYTE), (unsigned)_size, _handle); //### EOF - no problem?
}
BYTE result = *_ptr;
_ptr++;
return result;
}
inline
BYTE* getBytes(size_t count /*must be < _size!*/) {
if (_ptr + count >= _end) {
// need refill
// 'count' bytes might span two cache bounds,
// SEEK back to add the remains of the current cache again into the new one
long read = long(_ptr - _begin);
long remaining = long(_size - read);
_io->seek_proc(_handle, -remaining, SEEK_CUR);
_ptr = _begin;
_io->read_proc(_ptr, sizeof(BYTE), (unsigned)_size, _handle); //### EOF - no problem?
}
BYTE *result = _ptr;
_ptr += count;
return result;
}
private:
IOCache& operator=(const IOCache& src); // deleted
IOCache(const IOCache& other); // deleted
private:
BYTE *_ptr;
BYTE *_begin;
BYTE *_end;
const size_t _size;
const FreeImageIO *_io;
const fi_handle _handle;
};
#ifdef FREEIMAGE_BIGENDIAN
static void
SwapHeader(TGAHEADER *header) {
SwapShort(&header->cm_first_entry);
SwapShort(&header->cm_length);
SwapShort(&header->is_xorigin);
SwapShort(&header->is_yorigin);
SwapShort(&header->is_width);
SwapShort(&header->is_height);
}
static void
SwapExtensionArea(TGAEXTENSIONAREA *ex) {
SwapShort(&ex->extension_size);
SwapShort(&ex->datetime_stamp[0]);
SwapShort(&ex->datetime_stamp[1]);
SwapShort(&ex->datetime_stamp[2]);
SwapShort(&ex->datetime_stamp[3]);
SwapShort(&ex->datetime_stamp[4]);
SwapShort(&ex->datetime_stamp[5]);
SwapShort(&ex->job_time[0]);
SwapShort(&ex->job_time[1]);
SwapShort(&ex->job_time[2]);
SwapLong (&ex->key_color);
SwapShort(&ex->pixel_aspect_ratio[0]);
SwapShort(&ex->pixel_aspect_ratio[1]);
SwapShort(&ex->gamma_value[0]);
SwapShort(&ex->gamma_value[1]);
SwapLong (&ex->color_correction_offset);
SwapLong (&ex->postage_stamp_offset);
SwapLong (&ex->scan_line_offset);
}
static void
SwapFooter(TGAFOOTER *footer) {
SwapLong(&footer->extension_offset);
SwapLong(&footer->developer_offset);
}
#endif // FREEIMAGE_BIGENDIAN
// ==========================================================
// Plugin Interface
// ==========================================================
static int s_format_id;
// ==========================================================
// Plugin Implementation
// ==========================================================
static const char * DLL_CALLCONV
Format() {
return "TARGA";
}
static const char * DLL_CALLCONV
Description() {
return "Truevision Targa";
}
static const char * DLL_CALLCONV
Extension() {
return "tga,targa";
}
static const char * DLL_CALLCONV
RegExpr() {
return NULL;
}
static const char * DLL_CALLCONV
MimeType() {
return "image/x-tga";
}
static BOOL
isTARGA20(FreeImageIO *io, fi_handle handle) {
const unsigned sizeofSig = 18;
BYTE signature[sizeofSig];
// tga_signature = "TRUEVISION-XFILE." (TGA 2.0 only)
BYTE tga_signature[sizeofSig] = { 84, 82, 85, 69, 86, 73, 83, 73, 79, 78, 45, 88, 70, 73, 76, 69, 46, 0 };
// get the start offset
const long start_offset = io->tell_proc(handle);
// get the end-of-file
io->seek_proc(handle, 0, SEEK_END);
const long eof = io->tell_proc(handle);
// read the signature
io->seek_proc(handle, start_offset + eof - sizeofSig, SEEK_SET);
io->read_proc(&signature, 1, sizeofSig, handle);
// rewind
io->seek_proc(handle, start_offset, SEEK_SET);
return (memcmp(tga_signature, signature, sizeofSig) == 0);
}
static BOOL DLL_CALLCONV
Validate(FreeImageIO *io, fi_handle handle) {
if(isTARGA20(io, handle)) {
return TRUE;
}
// not a 2.0 image, try testing if it's a valid TGA anyway (not robust)
{
const long start_offset = io->tell_proc(handle);
// get the header
TGAHEADER header;
io->read_proc(&header, sizeof(tagTGAHEADER), 1, handle);
#ifdef FREEIMAGE_BIGENDIAN
SwapHeader(&header);
#endif
// rewind
io->seek_proc(handle, start_offset, SEEK_SET);
// the color map type should be a 0 or a 1...
if(header.color_map_type != 0 && header.color_map_type != 1) {
return FALSE;
}
// if the color map type is 1 then we validate the map entry information...
if(header.color_map_type > 0) {
// it doesn't make any sense if the first entry is larger than the color map table
if(header.cm_first_entry >= header.cm_length) {
return FALSE;
}
// check header.cm_size, don't allow 0 or anything bigger than 32
if(header.cm_size == 0 || header.cm_size > 32) {
return FALSE;
}
}
// the width/height shouldn't be 0, right ?
if(header.is_width == 0 || header.is_height == 0) {
return FALSE;
}
// let's now verify all the types that are supported by FreeImage (this is our final verification)
switch(header.image_type) {
case TGA_CMAP:
case TGA_RGB:
case TGA_MONO:
case TGA_RLECMAP:
case TGA_RLERGB:
case TGA_RLEMONO:
switch(header.is_pixel_depth) {
case 8 :
case 16:
case 24:
case 32:
return TRUE;
default:
return FALSE;
}
break;
default:
return FALSE;
}
}
}
static BOOL DLL_CALLCONV
SupportsExportDepth(int depth) {
return (
(depth == 8) ||
(depth == 16) ||
(depth == 24) ||
(depth == 32)
);
}
static BOOL DLL_CALLCONV
SupportsExportType(FREE_IMAGE_TYPE type) {
return (type == FIT_BITMAP) ? TRUE : FALSE;
}
static BOOL DLL_CALLCONV
SupportsNoPixels() {
return TRUE;
}
// ----------------------------------------------------------
/**
Used for all 32 and 24 bit loading of uncompressed images
*/
src/Source/FreeImage/PluginTARGA.cpp view on Meta::CPAN
long start_offset = io->tell_proc(handle);
// remember end-of-file (used for RLE cache)
io->seek_proc(handle, 0, SEEK_END);
long eof = io->tell_proc(handle);
io->seek_proc(handle, start_offset, SEEK_SET);
// read and process the bitmap's footer
TargaThumbnail thumbnail;
if(isTARGA20(io, handle)) {
TGAFOOTER footer;
const long footer_offset = start_offset + eof - sizeof(footer);
io->seek_proc(handle, footer_offset, SEEK_SET);
io->read_proc(&footer, sizeof(tagTGAFOOTER), 1, handle);
#ifdef FREEIMAGE_BIGENDIAN
SwapFooter(&footer);
#endif
BOOL hasExtensionArea = footer.extension_offset > 0;
if(hasExtensionArea) {
TGAEXTENSIONAREA extensionarea;
io->seek_proc(handle, footer.extension_offset, SEEK_SET);
io->read_proc(&extensionarea, sizeof(extensionarea), 1, handle);
#ifdef FREEIMAGE_BIGENDIAN
SwapExtensionArea(&extensionarea);
#endif
DWORD postage_stamp_offset = extensionarea.postage_stamp_offset;
BOOL hasThumbnail = (postage_stamp_offset > 0) && (postage_stamp_offset < (DWORD)footer_offset);
if(hasThumbnail) {
io->seek_proc(handle, postage_stamp_offset, SEEK_SET);
thumbnail.read(io, handle, footer_offset - postage_stamp_offset);
}
}
}
// read and process the bitmap's header
TGAHEADER header;
io->seek_proc(handle, start_offset, SEEK_SET);
io->read_proc(&header, sizeof(tagTGAHEADER), 1, handle);
#ifdef FREEIMAGE_BIGENDIAN
SwapHeader(&header);
#endif
thumbnail.setDepth(header.is_pixel_depth);
const int line = CalculateLine(header.is_width, header.is_pixel_depth);
const int pixel_bits = header.is_pixel_depth;
const int pixel_size = pixel_bits/8;
int fliphoriz = (header.is_image_descriptor & 0x10) ? 1 : 0;
int flipvert = (header.is_image_descriptor & 0x20) ? 1 : 0;
// skip comment
io->seek_proc(handle, header.id_length, SEEK_CUR);
switch (header.is_pixel_depth) {
case 8 : {
dib = FreeImage_AllocateHeader(header_only, header.is_width, header.is_height, 8);
if (dib == NULL) {
throw FI_MSG_ERROR_DIB_MEMORY;
}
// read the palette (even if header only)
RGBQUAD *palette = FreeImage_GetPalette(dib);
if (header.color_map_type > 0) {
unsigned count, csize;
// calculate the color map size
csize = header.cm_length * header.cm_size / 8;
// read the color map
BYTE *cmap = (BYTE*)malloc(csize * sizeof(BYTE));
if (cmap == NULL) {
throw FI_MSG_ERROR_DIB_MEMORY;
}
io->read_proc(cmap, sizeof(BYTE), csize, handle);
// build the palette
switch (header.cm_size) {
case 16: {
WORD *rgb555 = (WORD*)&cmap[0];
unsigned start = (unsigned)header.cm_first_entry;
unsigned stop = MIN((unsigned)256, (unsigned)header.cm_length);
for (count = start; count < stop; count++) {
palette[count].rgbRed = (BYTE)((((*rgb555 & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F);
palette[count].rgbGreen = (BYTE)((((*rgb555 & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F);
palette[count].rgbBlue = (BYTE)((((*rgb555 & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F);
rgb555++;
}
}
break;
case 24: {
FILE_BGR *bgr = (FILE_BGR*)&cmap[0];
unsigned start = (unsigned)header.cm_first_entry;
unsigned stop = MIN((unsigned)256, (unsigned)header.cm_length);
for (count = start; count < stop; count++) {
palette[count].rgbBlue = bgr->b;
palette[count].rgbGreen = bgr->g;
palette[count].rgbRed = bgr->r;
bgr++;
}
}
break;
case 32: {
BYTE trns[256];
// clear the transparency table
memset(trns, 0xFF, 256);
FILE_BGRA *bgra = (FILE_BGRA*)&cmap[0];
unsigned start = (unsigned)header.cm_first_entry;
unsigned stop = MIN((unsigned)256, (unsigned)header.cm_length);
for (count = start; count < stop; count++) {
palette[count].rgbBlue = bgra->b;
palette[count].rgbGreen = bgra->g;
palette[count].rgbRed = bgra->r;
// alpha
trns[count] = bgra->a;
bgra++;
}
// set the tranparency table
FreeImage_SetTransparencyTable(dib, trns, 256);
}
break;
} // switch(header.cm_size)
free(cmap);
}
// handle thumbnail
FIBITMAP* th = thumbnail.toFIBITMAP();
if(th) {
RGBQUAD* pal = FreeImage_GetPalette(dib);
RGBQUAD* dst_pal = FreeImage_GetPalette(th);
if(dst_pal && pal) {
for(unsigned i = 0; i < FreeImage_GetColorsUsed(dib); i++) {
dst_pal[i] = pal[i];
}
}
FreeImage_SetTransparencyTable(th, FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib));
FreeImage_SetThumbnail(dib, th);
FreeImage_Unload(th);
}
if(header_only) {
return dib;
}
// read in the bitmap bits
switch (header.image_type) {
case TGA_CMAP:
case TGA_MONO: {
BYTE *bits = NULL;
for (unsigned count = 0; count < header.is_height; count++) {
bits = FreeImage_GetScanLine(dib, count);
io->read_proc(bits, sizeof(BYTE), line, handle);
}
}
break;
case TGA_RLECMAP:
case TGA_RLEMONO: { //(8 bit)
loadRLE<8>(dib, header.is_width, header.is_height, io, handle, eof, FALSE);
}
break;
default :
FreeImage_Unload(dib);
return NULL;
}
}
break; // header.is_pixel_depth == 8
case 15 :
case 16 : {
int pixel_bits = 16;
// allocate the dib
if (TARGA_LOAD_RGB888 & flags) {
pixel_bits = 24;
dib = FreeImage_AllocateHeader(header_only, header.is_width, header.is_height, pixel_bits, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
} else {
dib = FreeImage_AllocateHeader(header_only, header.is_width, header.is_height, pixel_bits, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK);
}
if (dib == NULL) {
throw FI_MSG_ERROR_DIB_MEMORY;
}
// handle thumbnail
FIBITMAP* th = thumbnail.toFIBITMAP();
if(th) {
if(TARGA_LOAD_RGB888 & flags) {
FIBITMAP* t = FreeImage_ConvertTo24Bits(th);
FreeImage_Unload(th);
th = t;
}
FreeImage_SetThumbnail(dib, th);
FreeImage_Unload(th);
}
if(header_only) {
return dib;
}
int line = CalculateLine(header.is_width, pixel_bits);
const unsigned pixel_size = unsigned(pixel_bits) / 8;
const unsigned src_pixel_size = sizeof(WORD);
// note header.cm_size is a misleading name, it should be seen as header.cm_bits
// ignore current position in file and set filepointer explicitly from the beginning of the file
int garblen = 0;
if (header.color_map_type != 0) {
garblen = (int)((header.cm_size + 7) / 8) * header.cm_length; /* should byte align */
} else {
garblen = 0;
}
io->seek_proc(handle, start_offset, SEEK_SET);
io->seek_proc(handle, sizeof(tagTGAHEADER) + header.id_length + garblen, SEEK_SET);
// read in the bitmap bits
switch (header.image_type) {
case TGA_RGB: { //(16 bit)
// input line cache
BYTE *in_line = (BYTE*)malloc(header.is_width * sizeof(WORD));
if (!in_line)
throw FI_MSG_ERROR_MEMORY;
const int h = header.is_height;
for (int y = 0; y < h; y++) {
BYTE *bits = FreeImage_GetScanLine(dib, y);
io->read_proc(in_line, src_pixel_size, header.is_width, handle);
BYTE *val = in_line;
for (int x = 0; x < line; x += pixel_size) {
_assignPixel<16>(bits+x, val, TARGA_LOAD_RGB888 & flags);
val += src_pixel_size;
}
}
free(in_line);
}
break;
case TGA_RLERGB: { //(16 bit)
loadRLE<16>(dib, header.is_width, header.is_height, io, handle, eof, TARGA_LOAD_RGB888 & flags);
}
break;
default :
FreeImage_Unload(dib);
return NULL;
}
}
break; // header.is_pixel_depth == 15 or 16
case 24 : {
dib = FreeImage_AllocateHeader(header_only, header.is_width, header.is_height, pixel_bits, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
if (dib == NULL) {
throw FI_MSG_ERROR_DIB_MEMORY;
}
FIBITMAP* th = thumbnail.toFIBITMAP();
if(th) {
FreeImage_SetThumbnail(dib, th);
FreeImage_Unload(th);
}
if(header_only) {
return dib;
}
src/Source/FreeImage/PluginTARGA.cpp view on Meta::CPAN
// otherwise do nothing. We will just increase the count at the end
} else {
// no rle
if(has_rle) {
// flush rle packet
// include current pixel first
assert(packet_count < max_packet_size);
++packet_count;
flushPacket(line, pixel_size, packet_begin, packet, packet_count, has_rle);
// start anew on the next pixel
continue;
} else {
writeToPacket(packet, current, pixel_size);
packet += pixel_size;
}
}
// increase counter on every pixel
++packet_count;
if(packet_count == max_packet_size) {
flushPacket(line, pixel_size, packet_begin, packet, packet_count, has_rle);
}
}//for width
// write line to disk
io->write_proc(line_begin, 1, (unsigned)(line - line_begin), handle);
}//for height
free(line_begin);
free(packet_begin);
free(current);
free(next);
}
static BOOL DLL_CALLCONV
Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
if ((dib == NULL) || (handle == NULL)) {
return FALSE;
}
RGBQUAD *palette = FreeImage_GetPalette(dib);
const unsigned bpp = FreeImage_GetBPP(dib);
// write the file header
TGAHEADER header;
header.id_length = 0;
header.cm_first_entry = 0;
header.is_xorigin = 0;
header.is_yorigin = 0;
header.is_width = (WORD)FreeImage_GetWidth(dib);
header.is_height = (WORD)FreeImage_GetHeight(dib);
header.is_pixel_depth = (BYTE)bpp;
header.is_image_descriptor = (bpp == 32 ? 8 : 0);
if (palette) {
header.color_map_type = 1;
header.image_type = (TARGA_SAVE_RLE & flags) ? TGA_RLECMAP : TGA_CMAP;
header.cm_length = (WORD)(1 << bpp);
if (FreeImage_IsTransparent(dib)) {
header.cm_size = 32;
} else {
header.cm_size = 24;
}
} else {
header.color_map_type = 0;
header.image_type = (TARGA_SAVE_RLE & flags) ? TGA_RLERGB : TGA_RGB;
header.cm_length = 0;
header.cm_size = 0;
}
// write the header
#ifdef FREEIMAGE_BIGENDIAN
SwapHeader(&header);
#endif
io->write_proc(&header, sizeof(header), 1, handle);
#ifdef FREEIMAGE_BIGENDIAN
SwapHeader(&header);
#endif
// write the palette
if (palette) {
if (FreeImage_IsTransparent(dib)) {
FILE_BGRA *bgra_pal = (FILE_BGRA*)malloc(header.cm_length * sizeof(FILE_BGRA));
// get the transparency table
BYTE *trns = FreeImage_GetTransparencyTable(dib);
for (unsigned i = 0; i < header.cm_length; i++) {
bgra_pal[i].b = palette[i].rgbBlue;
bgra_pal[i].g = palette[i].rgbGreen;
bgra_pal[i].r = palette[i].rgbRed;
bgra_pal[i].a = trns[i];
}
io->write_proc(bgra_pal, sizeof(FILE_BGRA), header.cm_length, handle);
free(bgra_pal);
} else {
FILE_BGR *bgr_pal = (FILE_BGR*)malloc(header.cm_length * sizeof(FILE_BGR));
for (unsigned i = 0; i < header.cm_length; i++) {
bgr_pal[i].b = palette[i].rgbBlue;
bgr_pal[i].g = palette[i].rgbGreen;
bgr_pal[i].r = palette[i].rgbRed;
}
io->write_proc(bgr_pal, sizeof(FILE_BGR), header.cm_length, handle);
free(bgr_pal);
}
}
// write the data bits
if (TARGA_SAVE_RLE & flags) {
saveRLE(dib, io, handle);
} else {
// -- no rle compression --
const unsigned width = header.is_width;
const unsigned height = header.is_height;
const unsigned pixel_size = bpp/8;
BYTE *line, *const line_begin = (BYTE*)malloc(width * pixel_size);
BYTE *line_source = line_begin;
for (unsigned y = 0; y < height; y++) {
BYTE *scanline = FreeImage_GetScanLine(dib, y);
// rewind the line pointer
line = line_begin;
switch (bpp) {
case 8: {
// don't copy line, read straight from dib
line_source = scanline;
}
break;
case 16: {
for (unsigned x = 0; x < width; x++) {
WORD pixel = *(((WORD *)scanline) + x);
#ifdef FREEIMAGE_BIGENDIAN
SwapShort(&pixel);
#endif
*(WORD*)line = pixel;
line += pixel_size;
}
}
break;
case 24: {
#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
line_source = scanline;
#else
for (unsigned x = 0; x < width; ++x) {
RGBTRIPLE* trip = ((RGBTRIPLE *)scanline) + x;
line[0] = trip->rgbtBlue;
line[1] = trip->rgbtGreen;
line[2] = trip->rgbtRed;
( run in 0.768 second using v1.01-cache-2.11-cpan-119454b85a5 )