/////////////////////////////////////////////////////////////////////////////
-// Name: imagtga.cpp
+// Name: src/common/imagtga.cpp
// Purpose: wxImage TGA handler
// Author: Seth Jackson
// CVS-ID: $Id$
#include "wx/imagtga.h"
#include "wx/log.h"
+#include "wx/scopeguard.h"
// ----------------------------------------------------------------------------
// constants
// TGA error codes.
enum
{
- wxTGA_OK = 0,
- wxTGA_INVFORMAT = 1,
- wxTGA_MEMERR = 2
+ wxTGA_OK,
+ wxTGA_INVFORMAT,
+ wxTGA_MEMERR,
+ wxTGA_IOERR
};
// TGA header bytes.
}
}
+// return wxTGA_OK or wxTGA_IOERR
static
-void DecodeRLE(unsigned char* imageData, unsigned long imageSize,
+int DecodeRLE(unsigned char* imageData, unsigned long imageSize,
short pixelSize, wxInputStream& stream)
{
unsigned long index = 0;
while (index < imageSize)
{
- current = stream.GetC();
+ int ch = stream.GetC();
+ if ( ch == wxEOF )
+ return wxTGA_IOERR;
+
+ current = ch;
// RLE packet.
if ( current & 0x80 )
index += current * pixelSize;
+ if (index >= imageSize)
+ {
+ return wxTGA_IOERR;
+ }
+
// Repeat the pixel length times.
- stream.Read(buf, pixelSize);
+ if ( !stream.Read(buf, pixelSize) )
+ return wxTGA_IOERR;
for (unsigned int i = 0; i < length; i++)
{
index += length;
+ if (index >= imageSize)
+ {
+ return wxTGA_IOERR;
+ }
+
// Write the next length pixels directly to the image data.
- stream.Read(imageData, length);
+ if ( !stream.Read(imageData, length) )
+ return wxTGA_IOERR;
imageData += length;
}
}
+
+ return wxTGA_OK;
+}
+
+/*
+Mimic the behaviour of wxPalette.GetRGB and the way the TGA image handler
+used it. That is: don't check the return value of GetRGB and continue decoding
+using previous RGB values.
+
+It might be better to check for palette index bounds and stop decoding if
+it's out of range (and add something like wxTGA_DATAERR to indicate unexpected
+pixel data).
+*/
+static
+void Palette_GetRGB(const unsigned char *palette, unsigned int paletteCount,
+ unsigned int index,
+ unsigned char *red, unsigned char *green, unsigned char *blue)
+{
+ if (index >= paletteCount)
+ {
+ return;
+ }
+
+ *red = palette[index];
+ *green = palette[(paletteCount * 1) + index];
+ *blue = palette[(paletteCount * 2) + index];
+}
+
+static
+void Palette_SetRGB(unsigned char *palette, unsigned int paletteCount,
+ unsigned int index,
+ unsigned char red, unsigned char green, unsigned char blue)
+{
+ palette[index] = red;
+ palette[(paletteCount * 1) + index] = green;
+ palette[(paletteCount * 2) + index] = blue;
}
static
short offset = hdr[HDR_OFFSET] + HDR_SIZE;
short colorType = hdr[HDR_COLORTYPE];
short imageType = hdr[HDR_IMAGETYPE];
- int paletteLength = hdr[HDR_PALETTELENGTH] + 256 * hdr[HDR_PALETTELENGTH + 1];
+ unsigned int paletteLength = hdr[HDR_PALETTELENGTH]
+ + 256 * hdr[HDR_PALETTELENGTH + 1];
int width = (hdr[HDR_WIDTH] + 256 * hdr[HDR_WIDTH + 1]) -
(hdr[HDR_XORIGIN] + 256 * hdr[HDR_XORIGIN + 1]);
int height = (hdr[HDR_HEIGHT] + 256 * hdr[HDR_HEIGHT + 1]) -
image->Create(width, height);
- if (!image->Ok())
+ if (!image->IsOk())
{
return wxTGA_MEMERR;
}
return wxTGA_MEMERR;
}
+ wxON_BLOCK_EXIT1(free, imageData);
+
unsigned char *dst = image->GetData();
unsigned char* alpha = NULL;
}
// Seek from the offset we got from the TGA header.
- stream.SeekI(offset, wxFromStart);
+ if (stream.SeekI(offset, wxFromStart) == wxInvalidOffset)
+ return wxTGA_INVFORMAT;
+ unsigned char *palette = NULL;
// Load a palette if we have one.
if (colorType == wxTGA_MAPPED)
{
unsigned char buf[3];
- unsigned char* r = new unsigned char[paletteLength];
- unsigned char* g = new unsigned char[paletteLength];
- unsigned char* b = new unsigned char[paletteLength];
+ palette = (unsigned char *) malloc(paletteLength * 3);
- for (int i = 0; i < paletteLength; i++)
+ for (unsigned int i = 0; i < paletteLength; i++)
{
stream.Read(buf, 3);
- r[i] = buf[2];
- g[i] = buf[1];
- b[i] = buf[0];
+ Palette_SetRGB(palette, paletteLength, i, buf[2], buf[1], buf[0]);
}
- #if wxUSE_PALETTE
-
+#if wxUSE_PALETTE
// Set the palette of the image.
+ image->SetPalette(wxPalette((int) paletteLength, &palette[0],
+ &palette[paletteLength * 1], &palette[paletteLength * 2]));
+#endif // wxUSE_PALETTE
- image->SetPalette(wxPalette(paletteLength, r, g, b));
-
- #endif // wxUSE_PALETTE
-
- delete[] r;
- delete[] g;
- delete[] b;
}
+ wxON_BLOCK_EXIT1(free, palette);
+
// Handle the various TGA formats we support.
switch (imageType)
case 1:
{
- const wxPalette& palette = image->GetPalette();
unsigned char r;
unsigned char g;
unsigned char b;
{
for (unsigned long index = 0; index < imageSize; index += pixelSize)
{
- palette.GetRGB(imageData[index], &r, &g, &b);
+ Palette_GetRGB(palette, paletteLength,
+ imageData[index], &r, &g, &b);
*(dst++) = r;
*(dst++) = g;
{
for (unsigned long index = 0; index < imageSize; index += pixelSize)
{
- palette.GetRGB(imageData[index], &r, &g, &b);
+ Palette_GetRGB(palette, paletteLength,
+ imageData[index], &r, &g, &b);
*(dst++) = r;
*(dst++) = g;
case 9:
{
- const wxPalette& palette = image->GetPalette();
unsigned char r;
unsigned char g;
unsigned char b;
// Decode the RLE data.
- DecodeRLE(imageData, imageSize, pixelSize, stream);
+ int rc = DecodeRLE(imageData, imageSize, pixelSize, stream);
+ if ( rc != wxTGA_OK )
+ return rc;
// If orientation == 0, then the image is stored upside down.
// We need to store it right side up.
{
for (unsigned long index = 0; index < imageSize; index += pixelSize)
{
- palette.GetRGB(imageData[index], &r, &g, &b);
+ Palette_GetRGB(palette, paletteLength,
+ imageData[index], &r, &g, &b);
*(dst++) = r;
*(dst++) = g;
{
for (unsigned long index = 0; index < imageSize; index += pixelSize)
{
- palette.GetRGB(imageData[index], &r, &g, &b);
+ Palette_GetRGB(palette, paletteLength,
+ imageData[index], &r, &g, &b);
*(dst++) = r;
*(dst++) = g;
{
// Decode the RLE data.
- DecodeRLE(imageData, imageSize, pixelSize, stream);
+ int rc = DecodeRLE(imageData, imageSize, pixelSize, stream);
+ if ( rc != wxTGA_OK )
+ return rc;
// If orientation == 0, then the image is stored upside down.
// We need to store it right side up.
{
// Decode the RLE data.
- DecodeRLE(imageData, imageSize, pixelSize, stream);
+ int rc = DecodeRLE(imageData, imageSize, pixelSize, stream);
+ if ( rc != wxTGA_OK )
+ return rc;
// If orientation == 0, then the image is stored upside down.
// We need to store it right side up.
return wxTGA_INVFORMAT;
}
- free(imageData);
-
return wxTGA_OK;
}
static
-int SaveTGA(wxImage* WXUNUSED(image), wxOutputStream& WXUNUSED(stream))
+int SaveTGA(const wxImage& image, wxOutputStream *stream)
{
- wxLogError(wxT("Saving in TGA format is not implemented."));
+ bool hasAlpha = image.HasAlpha();
+ unsigned bytesPerPixel = 3 + (hasAlpha ? 1 : 0);
+ wxSize size = image.GetSize();
+ size_t scanlineSize = size.x * bytesPerPixel;
+ unsigned char *scanlineData = (unsigned char *) malloc(scanlineSize);
+ if (!scanlineData)
+ {
+ return wxTGA_MEMERR;
+ }
+
+ wxON_BLOCK_EXIT1(free, scanlineData);
+
+ // Compose and write the TGA header
+ unsigned char hdr[HDR_SIZE];
+ (void) memset(&hdr, 0, HDR_SIZE);
+
+ hdr[HDR_COLORTYPE] = wxTGA_UNMAPPED;
+ hdr[HDR_IMAGETYPE] = 2 /* Uncompressed truecolour */;
+
+ hdr[HDR_WIDTH] = size.x & 0xFF;
+ hdr[HDR_WIDTH + 1] = (size.x >> 8) & 0xFF;
+
+ hdr[HDR_HEIGHT] = size.y & 0xFF;
+ hdr[HDR_HEIGHT + 1] = (size.y >> 8) & 0xFF;
+
+ hdr[HDR_BPP] = hasAlpha ? 32 : 24;
+ hdr[HDR_ORIENTATION] = 1 << 5; // set bit to indicate top-down order
+ if (hasAlpha)
+ {
+ hdr[HDR_ORIENTATION] |= 8; // number of alpha bits
+ }
+
+ if ( !stream->Write(hdr, HDR_SIZE) )
+ {
+ return wxTGA_IOERR;
+ }
+
+
+ // Write image data, converting RGB to BGR and adding alpha if applicable
+
+ unsigned char *src = image.GetData();
+ unsigned char *alpha = image.GetAlpha();
+ for (int y = 0; y < size.y; ++y)
+ {
+ unsigned char *dst = scanlineData;
+ for (int x = 0; x < size.x; ++x)
+ {
+ dst[0] = src[2];
+ dst[1] = src[1];
+ dst[2] = src[0];
+ if (alpha)
+ {
+ dst[3] = *(alpha++);
+ }
+ src += 3;
+ dst += bytesPerPixel;
+ }
+ if ( !stream->Write(scanlineData, scanlineSize) )
+ {
+ return wxTGA_IOERR;
+ }
+ }
return wxTGA_OK;
}
if ( !CanRead(stream) )
{
if ( verbose )
+ {
wxLogError(wxT("TGA: this is not a TGA file."));
+ }
return false;
}
wxLogError(wxT("TGA: couldn't allocate memory."));
break;
+ case wxTGA_IOERR:
+ wxLogError(wxT("TGA: couldn't read image data."));
+ break;
+
default:
wxLogError(wxT("TGA: unknown error!"));
}
bool wxTGAHandler::SaveFile(wxImage* image, wxOutputStream& stream, bool verbose)
{
- int error = SaveTGA(image, stream);
+ int error = SaveTGA(*image, &stream);
if ( error != wxTGA_OK )
{
{
switch ( error )
{
- case wxTGA_INVFORMAT:
- wxLogError(wxT("TGA: invalid image."));
- break;
-
case wxTGA_MEMERR:
wxLogError(wxT("TGA: couldn't allocate memory."));
break;
+ case wxTGA_IOERR:
+ wxLogError(wxT("TGA: couldn't write image data."));
+ break;
+
default:
wxLogError(wxT("TGA: unknown error!"));
}
{
// read the fixed-size TGA headers
unsigned char hdr[HDR_SIZE];
- stream.Read(hdr, HDR_SIZE);
+ stream.Read(hdr, HDR_SIZE); // it's ok to modify the stream position here
- // Check wether we can read the file or not.
+ // Check whether we can read the file or not.
short colorType = hdr[HDR_COLORTYPE];
if ( colorType != wxTGA_UNMAPPED && colorType != wxTGA_MAPPED )