dolphin/Externals/wxWidgets/src/common/imagtga.cpp

751 lines
19 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: imagtga.cpp
// Purpose: wxImage TGA handler
// Author: Seth Jackson
// CVS-ID: $Id: imagtga.cpp 43681 2006-11-27 15:01:58Z VZ $
// Copyright: (c) 2005 Seth Jackson
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#if wxUSE_IMAGE && wxUSE_TGA
#ifndef WX_PRECOMP
#include "wx/palette.h"
#endif
#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
};
// TGA header bytes.
enum
{
HDR_OFFSET = 0,
HDR_COLORTYPE = 1,
HDR_IMAGETYPE = 2,
HDR_PALETTESTART = 3,
HDR_PALETTELENGTH = 5,
HDR_PALETTEBITS = 7,
HDR_XORIGIN = 8,
HDR_YORIGIN = 10,
HDR_WIDTH = 12,
HDR_HEIGHT = 14,
HDR_BPP = 16,
HDR_ORIENTATION = 17,
HDR_SIZE
};
// TGA color types.
enum
{
wxTGA_UNMAPPED = 0,
wxTGA_MAPPED = 1
};
// ============================================================================
// implementation
// ============================================================================
IMPLEMENT_DYNAMIC_CLASS(wxTGAHandler, wxImageHandler)
#if wxUSE_STREAMS
// ----------------------------------------------------------------------------
// worker functions
// ----------------------------------------------------------------------------
static
void FlipTGA(unsigned char* imageData, int width, int height, short pixelSize)
{
int lineLength = width * pixelSize;
unsigned char *line1 = imageData;
unsigned char *line2 = &imageData[lineLength * (height - 1)];
unsigned char temp;
for ( ; line1 < line2; line2 -= (lineLength * 2))
{
for (int index = 0; index < lineLength; line1++, line2++, index++)
{
temp = *line1;
*line1 = *line2;
*line2 = temp;
}
}
}
static
void DecodeRLE(unsigned char* imageData, unsigned long imageSize,
short pixelSize, wxInputStream& stream)
{
unsigned long index = 0;
unsigned char current;
unsigned int length;
unsigned char buf[4];
while (index < imageSize)
{
current = stream.GetC();
// RLE packet.
if ( current & 0x80 )
{
// Get the run length of the packet.
current &= 0x7f;
current++;
length = current;
index += current * pixelSize;
// Repeat the pixel length times.
stream.Read(buf, pixelSize);
for (unsigned int i = 0; i < length; i++)
{
memcpy(imageData, buf, pixelSize);
imageData += pixelSize;
}
}
else // Raw packet.
{
// Get the run length of the packet.
current++;
length = current * pixelSize;
index += length;
// Write the next length pixels directly to the image data.
stream.Read(imageData, length);
imageData += length;
}
}
}
static
int ReadTGA(wxImage* image, wxInputStream& stream)
{
// Read in the TGA header
unsigned char hdr[HDR_SIZE];
stream.Read(hdr, HDR_SIZE);
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];
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]) -
(hdr[HDR_YORIGIN] + 256 * hdr[HDR_YORIGIN + 1]);
short bpp = hdr[HDR_BPP];
short orientation = hdr[HDR_ORIENTATION] & 0x20;
image->Create(width, height);
if (!image->Ok())
{
return wxTGA_MEMERR;
}
const short pixelSize = bpp / 8;
const unsigned long imageSize = width * height * pixelSize;
unsigned char *imageData = (unsigned char* )malloc(imageSize);
if (!imageData)
{
return wxTGA_MEMERR;
}
wxON_BLOCK_EXIT1(free, imageData);
unsigned char *dst = image->GetData();
unsigned char* alpha = NULL;
if (bpp == 16 || bpp == 32)
{
image->SetAlpha();
alpha = image->GetAlpha();
}
// Seek from the offset we got from the TGA header.
stream.SeekI(offset, wxFromStart);
// 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];
for (int i = 0; i < paletteLength; i++)
{
stream.Read(buf, 3);
r[i] = buf[2];
g[i] = buf[1];
b[i] = buf[0];
}
#if wxUSE_PALETTE
// Set the palette of the image.
image->SetPalette(wxPalette(paletteLength, r, g, b));
#endif // wxUSE_PALETTE
delete[] r;
delete[] g;
delete[] b;
}
// Handle the various TGA formats we support.
switch (imageType)
{
#if wxUSE_PALETTE
// Raw indexed.
case 1:
{
const wxPalette& palette = image->GetPalette();
unsigned char r;
unsigned char g;
unsigned char b;
// No compression read the data directly to imageData.
stream.Read(imageData, imageSize);
// If orientation == 0, then the image is stored upside down.
// We need to store it right side up.
if (orientation == 0)
{
FlipTGA(imageData, width, height, pixelSize);
}
// Handle the different pixel depths.
switch (bpp)
{
// 8 bpp.
case 8:
{
for (unsigned long index = 0; index < imageSize; index += pixelSize)
{
palette.GetRGB(imageData[index], &r, &g, &b);
*(dst++) = r;
*(dst++) = g;
*(dst++) = b;
}
}
break;
// 16 bpp.
case 16:
{
for (unsigned long index = 0; index < imageSize; index += pixelSize)
{
palette.GetRGB(imageData[index], &r, &g, &b);
*(dst++) = r;
*(dst++) = g;
*(dst++) = b;
*(alpha++) = (imageData[index + 1] & 0x80) ? 0 : 255;
}
}
break;
default:
return wxTGA_INVFORMAT;
}
}
break;
#endif // wxUSE_PALETTE
// Raw RGB.
case 2:
{
// No compression read the data directly to imageData.
stream.Read(imageData, imageSize);
// If orientation == 0, then the image is stored upside down.
// We need to store it right side up.
if (orientation == 0)
{
FlipTGA(imageData, width, height, pixelSize);
}
// Handle the different pixel depths.
switch (bpp)
{
//16 bpp.
case 16:
{
unsigned char temp;
for (unsigned long index = 0; index < imageSize; index += pixelSize)
{
temp = (imageData[index + 1] & 0x7c) << 1;
temp |= temp >> 5;
*(dst++) = temp;
temp = ((imageData[index + 1] & 0x03) << 6) | ((imageData[index] & 0xe0) >> 2);
temp |= temp >> 5;
*(dst++) = temp;
temp = (imageData[index] & 0x1f) << 3;
temp |= temp >> 5;
*(dst++) = temp;
*(alpha++) = (imageData[index + 1] & 0x80) ? 0 : 255;
}
}
break;
// 24 bpp.
case 24:
{
for (unsigned long index = 0; index < imageSize; index += pixelSize)
{
*(dst++) = imageData[index + 2];
*(dst++) = imageData[index + 1];
*(dst++) = imageData[index];
}
}
break;
// 32 bpp.
case 32:
{
for (unsigned long index = 0; index < imageSize; index += pixelSize)
{
*(dst++) = imageData[index + 2];
*(dst++) = imageData[index + 1];
*(dst++) = imageData[index];
*(alpha++) = imageData[index + 3];
}
}
break;
default:
return wxTGA_INVFORMAT;
}
}
break;
// Raw grayscale.
case 3:
{
// No compression read the data directly to imageData.
stream.Read(imageData, imageSize);
// If orientation == 0, then the image is stored upside down.
// We need to store it right side up.
if (orientation == 0)
{
FlipTGA(imageData, width, height, pixelSize);
}
// Handle the different pixel depths.
switch (bpp)
{
// 8 bpp.
case 8:
{
for (unsigned long index = 0; index < imageSize; index += pixelSize)
{
*(dst++) = imageData[index];
*(dst++) = imageData[index];
*(dst++) = imageData[index];
}
}
break;
// 16 bpp.
case 16:
{
for (unsigned long index = 0; index < imageSize; index += pixelSize)
{
*(dst++) = imageData[index];
*(dst++) = imageData[index];
*(dst++) = imageData[index];
*(alpha++) = imageData[index + 1];
}
}
break;
default:
return wxTGA_INVFORMAT;
}
}
break;
#if wxUSE_PALETTE
// RLE indexed.
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);
// If orientation == 0, then the image is stored upside down.
// We need to store it right side up.
if (orientation == 0)
{
FlipTGA(imageData, width, height, pixelSize);
}
// Handle the different pixel depths.
switch (bpp)
{
// 8 bpp.
case 8:
{
for (unsigned long index = 0; index < imageSize; index += pixelSize)
{
palette.GetRGB(imageData[index], &r, &g, &b);
*(dst++) = r;
*(dst++) = g;
*(dst++) = b;
}
}
break;
// 16 bpp.
case 16:
{
for (unsigned long index = 0; index < imageSize; index += pixelSize)
{
palette.GetRGB(imageData[index], &r, &g, &b);
*(dst++) = r;
*(dst++) = g;
*(dst++) = b;
*(alpha++) = (imageData[index + 1] & 0x80) ? 0 : 255;
}
}
break;
default:
return wxTGA_INVFORMAT;
}
}
break;
#endif // wxUSE_PALETTE
// RLE RGB.
case 10:
{
// Decode the RLE data.
DecodeRLE(imageData, imageSize, pixelSize, stream);
// If orientation == 0, then the image is stored upside down.
// We need to store it right side up.
if (orientation == 0)
{
FlipTGA(imageData, width, height, pixelSize);
}
// Handle the different pixel depths.
switch (bpp)
{
//16 bpp.
case 16:
{
unsigned char temp;
for (unsigned long index = 0; index < imageSize; index += pixelSize)
{
temp = (imageData[index + 1] & 0x7c) << 1;
temp |= temp >> 5;
*(dst++) = temp;
temp = ((imageData[index + 1] & 0x03) << 6) | ((imageData[index] & 0xe0) >> 2);
temp |= temp >> 5;
*(dst++) = temp;
temp = (imageData[index] & 0x1f) << 3;
temp |= temp >> 5;
*(dst++) = temp;
*(alpha++) = (imageData[index + 1] & 0x80) ? 0 : 255;
}
}
break;
// 24 bpp.
case 24:
{
for (unsigned long index = 0; index < imageSize; index += pixelSize)
{
*(dst++) = imageData[index + 2];
*(dst++) = imageData[index + 1];
*(dst++) = imageData[index];
}
}
break;
// 32 bpp.
case 32:
{
for (unsigned long index = 0; index < imageSize; index += pixelSize)
{
*(dst++) = imageData[index + 2];
*(dst++) = imageData[index + 1];
*(dst++) = imageData[index];
*(alpha++) = imageData[index + 3];
}
}
break;
default:
return wxTGA_INVFORMAT;
}
}
break;
// RLE grayscale.
case 11:
{
// Decode the RLE data.
DecodeRLE(imageData, imageSize, pixelSize, stream);
// If orientation == 0, then the image is stored upside down.
// We need to store it right side up.
if (orientation == 0)
{
FlipTGA(imageData, width, height, pixelSize);
}
// Handle the different pixel depths.
switch (bpp)
{
// 8 bpp.
case 8:
{
for (unsigned long index = 0; index < imageSize; index += pixelSize)
{
*(dst++) = imageData[index];
*(dst++) = imageData[index];
*(dst++) = imageData[index];
}
}
break;
// 16 bpp.
case 16:
{
for (unsigned long index = 0; index < imageSize; index += pixelSize)
{
*(dst++) = imageData[index];
*(dst++) = imageData[index];
*(dst++) = imageData[index];
*(alpha++) = imageData[index + 1];
}
}
break;
default:
return wxTGA_INVFORMAT;
}
}
break;
default:
return wxTGA_INVFORMAT;
}
return wxTGA_OK;
}
static
int SaveTGA(wxImage* WXUNUSED(image), wxOutputStream& WXUNUSED(stream))
{
wxLogError(wxT("Saving in TGA format is not implemented."));
return wxTGA_OK;
}
// ----------------------------------------------------------------------------
// wxTGAHandler
// ----------------------------------------------------------------------------
bool wxTGAHandler::LoadFile(wxImage* image,
wxInputStream& stream,
bool verbose,
int WXUNUSED(index))
{
if ( !CanRead(stream) )
{
if ( verbose )
wxLogError(wxT("TGA: this is not a TGA file."));
return false;
}
image->Destroy();
int error = ReadTGA(image, stream);
if ( error != wxTGA_OK )
{
if ( verbose )
{
switch ( error )
{
case wxTGA_INVFORMAT:
wxLogError(wxT("TGA: image format unsupported."));
break;
case wxTGA_MEMERR:
wxLogError(wxT("TGA: couldn't allocate memory."));
break;
default:
wxLogError(wxT("TGA: unknown error!"));
}
}
image->Destroy();
return false;
}
return true;
}
bool wxTGAHandler::SaveFile(wxImage* image, wxOutputStream& stream, bool verbose)
{
int error = SaveTGA(image, stream);
if ( error != wxTGA_OK )
{
if ( verbose )
{
switch ( error )
{
case wxTGA_INVFORMAT:
wxLogError(wxT("TGA: invalid image."));
break;
case wxTGA_MEMERR:
wxLogError(wxT("TGA: couldn't allocate memory."));
break;
default:
wxLogError(wxT("TGA: unknown error!"));
}
}
return false;
}
return true;
}
bool wxTGAHandler::DoCanRead(wxInputStream& stream)
{
// read the fixed-size TGA headers
unsigned char hdr[HDR_SIZE];
stream.Read(hdr, HDR_SIZE);
// Check wether we can read the file or not.
short colorType = hdr[HDR_COLORTYPE];
if ( colorType != wxTGA_UNMAPPED && colorType != wxTGA_MAPPED )
{
return false;
}
short imageType = hdr[HDR_IMAGETYPE];
if ( imageType == 0 || imageType == 32 || imageType == 33 )
{
return false;
}
short bpp = hdr[HDR_BPP];
if ( bpp != 8 && bpp != 16 && bpp != 24 && bpp != 32 )
{
return false;
}
return true;
}
#endif // wxUSE_STREAMS
#endif // wxUSE_IMAGE && wxUSE_TGA