Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(858)

Unified Diff: Source/core/platform/image-decoders/gif/GIFImageReader.cpp

Issue 23646005: Improve GIF decoding performance (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: test data moved to Source/core/platform/image-decoders/testing Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: Source/core/platform/image-decoders/gif/GIFImageReader.cpp
diff --git a/Source/core/platform/image-decoders/gif/GIFImageReader.cpp b/Source/core/platform/image-decoders/gif/GIFImageReader.cpp
index bf18bea6229c14235b8fe8dda21906dea097b8cd..b933855fa4c0398ac8e6e91c9b77f6633db5c8dd 100644
--- a/Source/core/platform/image-decoders/gif/GIFImageReader.cpp
+++ b/Source/core/platform/image-decoders/gif/GIFImageReader.cpp
@@ -98,7 +98,7 @@ using WebCore::GIFImageDecoder;
#define GETINT16(p) ((p)[1]<<8|(p)[0])
// Send the data to the display front-end.
-bool GIFLZWContext::outputRow()
+bool GIFLZWContext::outputRow(GIFRow::const_iterator rowBegin)
{
int drowStart = irow;
int drowEnd = irow;
@@ -148,7 +148,7 @@ bool GIFLZWContext::outputRow()
return true;
// CALLBACK: Let the client know we have decoded a row.
- if (!m_client->haveDecodedRow(m_frameContext->frameId(), rowBuffer, m_frameContext->width(),
+ if (!m_client->haveDecodedRow(m_frameContext->frameId(), rowBegin, m_frameContext->width(),
drowStart, drowEnd - drowStart + 1, m_frameContext->progressiveDisplay() && m_frameContext->interlaced() && ipass > 1))
return false;
@@ -202,24 +202,12 @@ bool GIFLZWContext::outputRow()
// Otherwise, decoding failed; returns false in this case, which will always cause the GIFImageReader to set the "decode failed" flag.
bool GIFLZWContext::doLZW(const unsigned char* block, size_t bytesInBlock)
{
- int code;
- int incode;
- const unsigned char *ch;
+ const size_t width = m_frameContext->width();
if (rowIter == rowBuffer.end())
return true;
-#define OUTPUT_ROW \
- do { \
- if (!outputRow()) \
- return false; \
- rowsRemaining--; \
- rowIter = rowBuffer.begin(); \
- if (!rowsRemaining) \
- return true; \
- } while (0)
-
- for (ch = block; bytesInBlock-- > 0; ch++) {
+ for (const unsigned char* ch = block; bytesInBlock-- > 0; ch++) {
// Feed the next byte into the decoder's 32-bit input buffer.
datum += ((int) *ch) << bits;
bits += 8;
@@ -227,7 +215,7 @@ bool GIFLZWContext::doLZW(const unsigned char* block, size_t bytesInBlock)
// Check for underflow of decoder's 32-bit input buffer.
while (bits >= codesize) {
// Get the leading variable-length symbol from the data stream.
- code = datum & codemask;
+ int code = datum & codemask;
datum >>= codesize;
bits -= codesize;
@@ -248,66 +236,72 @@ bool GIFLZWContext::doLZW(const unsigned char* block, size_t bytesInBlock)
return false;
}
- if (oldcode == -1) {
- *rowIter++ = suffix[code];
- if (rowIter == rowBuffer.end())
- OUTPUT_ROW;
-
- firstchar = oldcode = code;
- continue;
- }
-
- incode = code;
- if (code >= avail) {
- stack[stackp++] = firstchar;
+ const int tempCode = code;
+ unsigned short codeLength = 0;
+ if (code < avail) {
+ // This is a pre-existing code, so we already know what it
+ // encodes.
+ codeLength = suffixLength[code];
+ rowIter += codeLength;
+ } else if (code == avail && oldcode != -1) {
+ // This is a new code just being added to the dictionary.
+ // It must encode the contents of the previous code, plus
+ // the first character of the previous code again.
+ codeLength = suffixLength[oldcode] + 1;
+ rowIter += codeLength;
+ *--rowIter = firstchar;
code = oldcode;
-
- if (stackp == MAX_BYTES)
- return false;
+ } else {
+ // This is an invalid code. The dictionary is just initialized
+ // and the code is incomplete. We don't know how to handle
+ // this case.
+ return false;
}
while (code >= clearCode) {
- if (code >= MAX_BYTES || code == prefix[code])
- return false;
-
- // Even though suffix[] only holds characters through suffix[avail - 1],
- // allowing code >= avail here lets us be more tolerant of malformed
- // data. As long as code < MAX_BYTES, the only risk is a garbled image,
- // which is no worse than refusing to display it.
- stack[stackp++] = suffix[code];
+ *--rowIter = suffix[code];
code = prefix[code];
-
- if (stackp == MAX_BYTES)
- return false;
}
- stack[stackp++] = firstchar = suffix[code];
+ *--rowIter = firstchar = suffix[code];
- // Define a new codeword in the dictionary.
- if (avail < 4096) {
+ // Define a new codeword in the dictionary as long as we've read
+ // more than one value from the stream.
+ if (avail < MAX_DICTIONARY_ENTRIES && oldcode != -1) {
prefix[avail] = oldcode;
suffix[avail] = firstchar;
- avail++;
+ suffixLength[avail] = suffixLength[oldcode] + 1;
+ ++avail;
// If we've used up all the codewords of a given length
// increase the length of codewords by one bit, but don't
- // exceed the specified maximum codeword size of 12 bits.
- if ((!(avail & codemask)) && (avail < 4096)) {
- codesize++;
+ // exceed the specified maximum codeword size.
+ if (!(avail & codemask) && avail < MAX_DICTIONARY_ENTRIES) {
+ ++codesize;
codemask += avail;
}
}
- oldcode = incode;
-
- // Copy the decoded data out to the scanline buffer.
- do {
- *rowIter++ = stack[--stackp];
- if (rowIter == rowBuffer.end())
- OUTPUT_ROW;
- } while (stackp > 0);
+ oldcode = tempCode;
+ rowIter += codeLength;
+
+ // Output as many rows as possible.
+ GIFRow::iterator rowBegin = rowBuffer.begin();
+ for (; rowBegin + width <= rowIter; rowBegin += width) {
+ if (!outputRow(rowBegin))
+ return false;
+ rowsRemaining--;
+ if (!rowsRemaining)
+ return true;
+ }
+
+ if (rowBegin != rowBuffer.begin()) {
+ // Move the remaining bytes to the beginning of the buffer.
+ const size_t bytesToCopy = rowIter - rowBegin;
+ memcpy(rowBuffer.begin(), rowBegin, bytesToCopy);
+ rowIter = rowBuffer.begin() + bytesToCopy;
+ }
}
}
-
return true;
}
@@ -316,12 +310,12 @@ void GIFColorMap::buildTable(const unsigned char* data, size_t length)
if (!m_isDefined || !m_table.isEmpty())
return;
- RELEASE_ASSERT(m_position + m_colors * GIF_COLORS <= length);
+ RELEASE_ASSERT(m_position + m_colors * BYTES_PER_COLORMAP_ENTRY <= length);
const unsigned char* srcColormap = data + m_position;
m_table.resize(m_colors);
for (Table::iterator iter = m_table.begin(); iter != m_table.end(); ++iter) {
*iter = SkPackARGB32NoCheck(255, srcColormap[0], srcColormap[1], srcColormap[2]);
- srcColormap += GIF_COLORS;
+ srcColormap += BYTES_PER_COLORMAP_ENTRY;
}
}
@@ -456,7 +450,7 @@ bool GIFImageReader::parseData(size_t dataPosition, size_t len, GIFImageDecoder:
if ((currentComponent[4] & 0x80) && globalColorMapColors > 0) { /* global map */
m_globalColorMap.setTablePositionAndSize(dataPosition, globalColorMapColors);
- GETN(GIF_COLORS * globalColorMapColors, GIFGlobalColormap);
+ GETN(BYTES_PER_COLORMAP_ENTRY * globalColorMapColors, GIFGlobalColormap);
break;
}
@@ -696,7 +690,7 @@ bool GIFImageReader::parseData(size_t dataPosition, size_t len, GIFImageDecoder:
// The three low-order bits of currentComponent[8] specify the bits per pixel.
const size_t numColors = 2 << (currentComponent[8] & 0x7);
currentFrame->localColorMap().setTablePositionAndSize(dataPosition, numColors);
- GETN(GIF_COLORS * numColors, GIFImageColormap);
+ GETN(BYTES_PER_COLORMAP_ENTRY * numColors, GIFImageColormap);
break;
}
@@ -760,14 +754,10 @@ bool GIFLZWContext::prepareToDecode()
ASSERT(m_frameContext->isDataSizeDefined() && m_frameContext->isHeaderDefined());
// Since we use a codesize of 1 more than the datasize, we need to ensure
- // that our datasize is strictly less than the MAX_LZW_BITS value (12).
- // This sets the largest possible codemask correctly at 4095.
- if (m_frameContext->dataSize() >= MAX_LZW_BITS)
+ // that our datasize is strictly less than the MAX_DICTIONARY_ENTRY_BITS.
+ if (m_frameContext->dataSize() >= MAX_DICTIONARY_ENTRY_BITS)
return false;
clearCode = 1 << m_frameContext->dataSize();
- if (clearCode >= MAX_BYTES)
- return false;
-
avail = clearCode + 2;
oldcode = -1;
codesize = m_frameContext->dataSize() + 1;
@@ -776,18 +766,36 @@ bool GIFLZWContext::prepareToDecode()
ipass = m_frameContext->interlaced() ? 1 : 0;
irow = 0;
- // Initialize output row buffer.
- rowBuffer.resize(m_frameContext->width());
+ // We want to know the longest sequence encodable by a dictionary with
+ // MAX_DICTIONARY_ENTRIES entries. If we ignore the need to encode the base
+ // values themselves at the beginning of the dictionary, as well as the need
+ // for a clear code or a termination code, we could use every entry to
+ // encode a series of multiple values. If the input value stream looked
+ // like "AAAAA..." (a long string of just one value), the first dictionary
+ // entry would encode AA, the next AAA, the next AAAA, and so forth. Thus
+ // the longest sequence would be MAX_DICTIONARY_ENTRIES + 1 values.
+ //
+ // However, we have to account for reserved entries. The first |datasize|
+ // bits are reserved for the base values, and the next two entries are
+ // reserved for the clear code and termination code. In theory a GIF can
+ // set the datasize to 0, meaning we have just two reserved entries, making
+ // the longest sequence (MAX_DICTIONARY_ENTIRES + 1) - 2 values long. Since
+ // each value is a byte, this is also the number of bytes in the longest
+ // encodable sequence.
+ const size_t maxBytes = MAX_DICTIONARY_ENTRIES - 1;
+
+ // Now allocate the output buffer. We decode directly into this buffer
+ // until we have at least one row worth of data, then call outputRow().
+ // This means worst case we may have (row width - 1) bytes in the buffer
+ // and then decode a sequence |maxBytes| long to append.
+ rowBuffer.resize(m_frameContext->width() - 1 + maxBytes);
rowIter = rowBuffer.begin();
rowsRemaining = m_frameContext->height();
// Clearing the whole suffix table lets us be more tolerant of bad data.
- memset(suffix, 0, sizeof(suffix));
-
- // Clearing the whole prefix table to prevent uninitialized access.
- memset(prefix, 0, sizeof(prefix));
- for (int i = 0; i < clearCode; i++)
+ for (int i = 0; i < clearCode; ++i) {
suffix[i] = i;
- stackp = 0;
+ suffixLength[i] = 1;
+ }
return true;
}

Powered by Google App Engine
This is Rietveld 408576698