XBMImageDecoder.cpp [plain text]
#include "config.h"
#include "XBMImageDecoder.h"
#include <algorithm>
namespace WebCore {
XBMImageDecoder::XBMImageDecoder()
: m_decodeOffset(0)
, m_allDataReceived(false)
, m_decodedHeader(false)
, m_dataType(Unknown)
, m_bitsDecoded(0)
{
}
void XBMImageDecoder::setData(SharedBuffer* data, bool allDataReceived)
{
ImageDecoder::setData(data, allDataReceived);
const Vector<char>& buf = m_data->buffer();
if (buf.size() > m_xbmString.size())
m_xbmString.append(&buf[m_xbmString.size()], buf.size() - m_xbmString.size());
m_allDataReceived = allDataReceived;
}
bool XBMImageDecoder::isSizeAvailable() const
{
if (!ImageDecoder::isSizeAvailable() && !m_failed)
const_cast<XBMImageDecoder*>(this)->decodeXBM(true);
return !m_failed && ImageDecoder::isSizeAvailable();
}
RGBA32Buffer* XBMImageDecoder::frameBufferAtIndex(size_t index)
{
if (m_frameBufferCache.isEmpty())
m_frameBufferCache.resize(1);
RGBA32Buffer& frame = m_frameBufferCache[0];
if (!ImageDecoder::isSizeAvailable())
decodeXBM(true);
if (ImageDecoder::isSizeAvailable() &&
frame.status() == RGBA32Buffer::FrameEmpty) {
if (!frame.setSize(size().width(), size().height())) {
m_failed = true;
frame.setStatus(RGBA32Buffer::FrameComplete);
return 0;
}
frame.setStatus(RGBA32Buffer::FramePartial);
}
if (frame.status() != RGBA32Buffer::FrameComplete)
decodeXBM(false);
return &frame;
}
bool XBMImageDecoder::decodeHeader()
{
ASSERT(m_decodeOffset <= m_xbmString.size());
ASSERT(!m_decodedHeader);
const char* input = m_xbmString.c_str();
int width, height;
if (!ImageDecoder::isSizeAvailable()) {
int count;
if (sscanf(&input[m_decodeOffset], "#define %*s %i #define %*s %i%n",
&width, &height, &count) != 2)
return false;
if (width < 0 || width > maxDimension || height < 0 || height > maxDimension) {
setFailed();
return false;
}
if (!setSize(width, height)) {
setFailed();
return false;
}
m_decodeOffset += count;
ASSERT(m_decodeOffset <= m_xbmString.size());
}
ASSERT(ImageDecoder::isSizeAvailable());
if (m_dataType == Unknown) {
const char* x11hint = " char ";
const char* x11HintLocation = strstr(&input[m_decodeOffset], x11hint);
if (x11HintLocation) {
m_dataType = X11;
m_decodeOffset += ((x11HintLocation - &input[m_decodeOffset]) + strlen(x11hint));
} else {
const char* x10hint = " short ";
const char* x10HintLocation = strstr(&input[m_decodeOffset], x10hint);
if (x10HintLocation) {
m_dataType = X10;
m_decodeOffset += ((x10HintLocation - &input[m_decodeOffset]) + strlen(x10hint));
} else
return false;
}
ASSERT(m_decodeOffset <= m_xbmString.size());
}
const char* found = strchr(&input[m_decodeOffset], '{');
if (!found)
return false;
m_decodeOffset += ((found - &input[m_decodeOffset]) + 1);
ASSERT(m_decodeOffset <= m_xbmString.size());
m_decodedHeader = true;
return true;
}
bool XBMImageDecoder::decodeDatum(uint16_t* result)
{
const char* input = m_xbmString.c_str();
char* endPtr;
const uint16_t value = strtoul(&input[m_decodeOffset], &endPtr, 0);
if (endPtr == &input[m_decodeOffset] || !*endPtr)
return false;
if (value == 0 && (*endPtr == 'x' || *endPtr == 'X'))
return false;
while (*endPtr && isspace(*endPtr))
++endPtr;
if (!*endPtr)
return false;
if (*endPtr != ',' && *endPtr != '}') {
setFailed();
return false;
}
*result = value;
m_decodeOffset += ((endPtr - &input[m_decodeOffset]) + 1);
ASSERT(m_decodeOffset <= m_xbmString.size());
return true;
}
bool XBMImageDecoder::decodeData()
{
ASSERT(m_decodeOffset <= m_xbmString.size());
ASSERT(m_decodedHeader && !m_frameBufferCache.isEmpty());
RGBA32Buffer& frame = m_frameBufferCache[0];
ASSERT(frame.status() == RGBA32Buffer::FramePartial);
const int bitsPerRow = size().width();
ASSERT(m_dataType != Unknown);
while (m_bitsDecoded < (size().width() * size().height())) {
uint16_t value;
if (!decodeDatum(&value))
return false;
int x = m_bitsDecoded % bitsPerRow;
const int y = m_bitsDecoded / bitsPerRow;
const int bits = std::min(bitsPerRow - x, (m_dataType == X11) ? 8 : 16);
for (int i = 0; i < bits; ++i)
frame.setRGBA(x++, y, 0, 0, 0, value & (1 << i) ? 255 : 0);
m_bitsDecoded += bits;
}
frame.setStatus(RGBA32Buffer::FrameComplete);
return true;
}
void XBMImageDecoder::decodeXBM(bool sizeOnly)
{
if (failed())
return;
bool decodeResult = false;
if (!m_decodedHeader)
decodeResult = decodeHeader();
if (m_decodedHeader && !sizeOnly)
decodeResult = decodeData();
if (!decodeResult && m_allDataReceived)
setFailed();
}
}