Skip to content

Commit

Permalink
TGA Image Decoder
Browse files Browse the repository at this point in the history
  • Loading branch information
BlzFans committed Jun 3, 2013
1 parent 12a37be commit a7f8a8e
Showing 1 changed file with 330 additions and 0 deletions.
330 changes: 330 additions & 0 deletions Source/WebCore/platform/image-decoders/TGAImageDecoder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,330 @@
#ifndef TGA_IMAGE_DECODER_H
#define TGA_IMAGE_DECODER_H

#include "ImageDecoder.h"
#include <wtf/OwnPtr.h>
#include <wtf/PassOwnPtr.h>

namespace WebCore {

class TGAImageReader;

// This class decodes the tga image format.
class TGAImageDecoder : public ImageDecoder {
public:
TGAImageDecoder(ImageSource::AlphaOption alphaOption, ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption)
:ImageDecoder(alphaOption, gammaAndColorProfileOption)
{
}

// ImageDecoder
virtual String filenameExtension() const { return "tga"; }
virtual void setData(SharedBuffer*, bool allDataReceived);
virtual bool isSizeAvailable();
virtual ImageFrame* frameBufferAtIndex(size_t index);

private:
// Decodes the image. If |onlySize| is true, stops decoding after
// calculating the image size. If decoding fails but there is no more
// data coming, sets the "decode failure" flag.
void decode(bool onlySize);

// The reader used to do most of the tga decoding.
OwnPtr<TGAImageReader> m_reader;
};

class TGAImageReader
{
public:
TGAImageReader(TGAImageDecoder* decoder)
: m_parent(decoder)
, m_readOffset(0)
, m_flipVert(false)
{
}

void setBuffer(ImageFrame* buffer) { m_buffer = buffer; }
void setData(SharedBuffer* data) { m_data = data; }

uint8_t readUint8()
{
uint8_t result;
memcpy(&result, &m_data->data()[m_readOffset], 1);
++m_readOffset;

return result;
}

uint16_t readUint16()
{
uint16_t result;
memcpy(&result, &m_data->data()[m_readOffset], 2);
m_readOffset += 2;

return result;
}

uint32_t readUint32()
{
uint32_t result;
memcpy(&result, &m_data->data()[m_readOffset], 4);
m_readOffset += 4;

return result;
}

enum
{
TGA_Map = 1,
TGA_RGB = 2,
TGA_Mono = 3,
TGA_RLEMap = 9,
TGA_RLERGB = 10,
TGA_RLEMono = 11
};

enum
{
MAXCOLORS = 16384
};

bool decode(bool sizeOnly)
{
m_readOffset = 0;

if (m_data->size() < 18)
return false;

unsigned char idLength = readUint8();
unsigned char colorMapType = readUint8();
unsigned char imageType = readUint8();
unsigned short minPalIndex = readUint16();
unsigned short palLength = readUint16();
unsigned char colorMapSize = readUint8();
unsigned short xOrigin = readUint16();
unsigned short yOrigin = readUint16();
unsigned short width = readUint16();
unsigned short height = readUint16();
unsigned char pixelSize = readUint8();
unsigned char attBits = readUint8();
m_readOffset += idLength;

m_flipVert = (attBits & 0x20) ? false : true;

m_parent->setSize(width, height);
if (sizeOnly)
return true;

bool colormapped = false;
bool rle = false;

switch (imageType)
{
case TGA_Map:
colormapped = true;
rle = false;
break;

case TGA_Mono:
colormapped = false;
rle = false;
break;

case TGA_RGB:
colormapped = false;
rle = false;
break;

case TGA_RLEMap:
colormapped = true;
rle = true;
break;

case TGA_RLEMono:
colormapped = false;
rle = true;
break;

case TGA_RLERGB:
colormapped = false;
rle = true;
break;

default:
return false;
}

if (colormapped || rle)
return false;

if (pixelSize != 32 && pixelSize != 24)
return false;

// Initialize the framebuffer if needed.
ASSERT(m_buffer); // Parent should set this before asking us to decode!

if (m_buffer->status() == ImageFrame::FrameEmpty) {
if (!m_buffer->setSize(width, height))
return m_parent->setFailed(); // Unable to allocate.
m_buffer->setStatus(ImageFrame::FramePartial);
// setSize() calls eraseARGB(), which resets the alpha flag, so we force
// it back to false here. We'll set it true below in all cases where
// these 0s could actually show through.
m_buffer->setHasAlpha(false);

//the frame always fills the entire image.
m_buffer->setOriginalFrameRect(IntRect(0, 0, width, height));
}

if (pixelSize == 32)
{
if (!decode32Bit())
return false;
}
else
{
if (!decode24Bit())
return false;
}

// Done!
m_buffer->setStatus(ImageFrame::FrameComplete);
return true;
}

bool decode32Bit()
{
int width = m_parent->size().width();
int height = m_parent->size().height();

if (m_data->size() < m_readOffset + width * height * 4)
return false;

m_buffer->setHasAlpha(true);

unsigned char r, g, b, a;
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
b = readUint8();
g = readUint8();
r = readUint8();
a = readUint8();

if (m_flipVert)
m_buffer->setRGBA(x, height - y - 1, r, g, b, a);
else
m_buffer->setRGBA(x, y, r, g, b, a);
}
}

return true;
}

bool decode24Bit()
{
int width = m_parent->size().width();
int height = m_parent->size().height();

if (m_data->size() < m_readOffset + width * height * 3)
return false;

m_buffer->setHasAlpha(false);

unsigned char r, g, b;
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
b = readUint8();
g = readUint8();
r = readUint8();

if (m_flipVert)
m_buffer->setRGBA(x, height - y - 1, r, g, b, 255);
else
m_buffer->setRGBA(x, y, r, g, b, 255);
}
}

return true;
}

private:
// The decoder that owns us.
ImageDecoder* m_parent;

// The destination for the pixel data.
ImageFrame* m_buffer;

// An index into |m_data| representing how much we've already decoded.
size_t m_readOffset;

// The file to decode.
RefPtr<SharedBuffer> m_data;

bool m_flipVert;
};

void TGAImageDecoder::setData(SharedBuffer* data, bool allDataReceived)
{
if (failed())
return;

ImageDecoder::setData(data, allDataReceived);
if (m_reader)
m_reader->setData(data);
}

bool TGAImageDecoder::isSizeAvailable()
{
if (!ImageDecoder::isSizeAvailable())
decode(true);

return ImageDecoder::isSizeAvailable();
}

ImageFrame* TGAImageDecoder::frameBufferAtIndex(size_t index)
{
if (index)
return 0;

if (m_frameBufferCache.isEmpty()) {
m_frameBufferCache.resize(1);
m_frameBufferCache[0].setPremultiplyAlpha(m_premultiplyAlpha);
}

ImageFrame* buffer = &m_frameBufferCache.first();
if (buffer->status() != ImageFrame::FrameComplete)
decode(false);
return buffer;
}

void TGAImageDecoder::decode(bool onlySize)
{
if (failed())
return;

if (!m_reader)
{
m_reader = adoptPtr(new TGAImageReader(this));
m_reader->setData(m_data.get());
}

if (!m_frameBufferCache.isEmpty())
m_reader->setBuffer(&m_frameBufferCache.first());

// If we couldn't decode the image but we've received all the data, decoding
// has failed.
if (!m_reader->decode(onlySize) && isAllDataReceived())
setFailed();
// If we're done decoding the image, we don't need the TGAImageReader
// anymore. (If we failed, |m_reader| has already been cleared.)
else if (!m_frameBufferCache.isEmpty() && (m_frameBufferCache[0].status() == ImageFrame::FrameComplete))
m_reader.clear();
}

} // namespace WebCore

#endif

0 comments on commit a7f8a8e

Please sign in to comment.