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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK ***** 2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * 4 *
5 * The contents of this file are subject to the Mozilla Public License Version 5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with 6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at 7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/ 8 * http://www.mozilla.org/MPL/
9 * 9 *
10 * Software distributed under the License is distributed on an "AS IS" basis, 10 * Software distributed under the License is distributed on an "AS IS" basis,
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
91 #define GETN(n, s) \ 91 #define GETN(n, s) \
92 do { \ 92 do { \
93 m_bytesToConsume = (n); \ 93 m_bytesToConsume = (n); \
94 m_state = (s); \ 94 m_state = (s); \
95 } while (0) 95 } while (0)
96 96
97 // Get a 16-bit value stored in little-endian format. 97 // Get a 16-bit value stored in little-endian format.
98 #define GETINT16(p) ((p)[1]<<8|(p)[0]) 98 #define GETINT16(p) ((p)[1]<<8|(p)[0])
99 99
100 // Send the data to the display front-end. 100 // Send the data to the display front-end.
101 bool GIFLZWContext::outputRow() 101 bool GIFLZWContext::outputRow(GIFRow::const_iterator rowBegin)
102 { 102 {
103 int drowStart = irow; 103 int drowStart = irow;
104 int drowEnd = irow; 104 int drowEnd = irow;
105 105
106 // Haeberli-inspired hack for interlaced GIFs: Replicate lines while 106 // Haeberli-inspired hack for interlaced GIFs: Replicate lines while
107 // displaying to diminish the "venetian-blind" effect as the image is 107 // displaying to diminish the "venetian-blind" effect as the image is
108 // loaded. Adjust pixel vertical positions to avoid the appearance of the 108 // loaded. Adjust pixel vertical positions to avoid the appearance of the
109 // image crawling up the screen as successive passes are drawn. 109 // image crawling up the screen as successive passes are drawn.
110 if (m_frameContext->progressiveDisplay() && m_frameContext->interlaced() && ipass < 4) { 110 if (m_frameContext->progressiveDisplay() && m_frameContext->interlaced() && ipass < 4) {
111 unsigned rowDup = 0; 111 unsigned rowDup = 0;
(...skipping 29 matching lines...) Expand all
141 141
142 if ((unsigned)drowEnd >= m_frameContext->height()) 142 if ((unsigned)drowEnd >= m_frameContext->height())
143 drowEnd = m_frameContext->height() - 1; 143 drowEnd = m_frameContext->height() - 1;
144 } 144 }
145 145
146 // Protect against too much image data. 146 // Protect against too much image data.
147 if ((unsigned)drowStart >= m_frameContext->height()) 147 if ((unsigned)drowStart >= m_frameContext->height())
148 return true; 148 return true;
149 149
150 // CALLBACK: Let the client know we have decoded a row. 150 // CALLBACK: Let the client know we have decoded a row.
151 if (!m_client->haveDecodedRow(m_frameContext->frameId(), rowBuffer, m_frameC ontext->width(), 151 if (!m_client->haveDecodedRow(m_frameContext->frameId(), rowBegin, m_frameCo ntext->width(),
152 drowStart, drowEnd - drowStart + 1, m_frameContext->progressiveDisplay() && m_frameContext->interlaced() && ipass > 1)) 152 drowStart, drowEnd - drowStart + 1, m_frameContext->progressiveDisplay() && m_frameContext->interlaced() && ipass > 1))
153 return false; 153 return false;
154 154
155 if (!m_frameContext->interlaced()) 155 if (!m_frameContext->interlaced())
156 irow++; 156 irow++;
157 else { 157 else {
158 do { 158 do {
159 switch (ipass) { 159 switch (ipass) {
160 case 1: 160 case 1:
161 irow += 8; 161 irow += 8;
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
195 } while (irow > (m_frameContext->height() - 1)); 195 } while (irow > (m_frameContext->height() - 1));
196 } 196 }
197 return true; 197 return true;
198 } 198 }
199 199
200 // Perform Lempel-Ziv-Welch decoding. 200 // Perform Lempel-Ziv-Welch decoding.
201 // Returns true if decoding was successful. In this case the block will have bee n completely consumed and/or rowsRemaining will be 0. 201 // Returns true if decoding was successful. In this case the block will have bee n completely consumed and/or rowsRemaining will be 0.
202 // Otherwise, decoding failed; returns false in this case, which will always cau se the GIFImageReader to set the "decode failed" flag. 202 // Otherwise, decoding failed; returns false in this case, which will always cau se the GIFImageReader to set the "decode failed" flag.
203 bool GIFLZWContext::doLZW(const unsigned char* block, size_t bytesInBlock) 203 bool GIFLZWContext::doLZW(const unsigned char* block, size_t bytesInBlock)
204 { 204 {
205 int code; 205 const size_t width = m_frameContext->width();
206 int incode;
207 const unsigned char *ch;
208 206
209 if (rowIter == rowBuffer.end()) 207 if (rowIter == rowBuffer.end())
210 return true; 208 return true;
211 209
212 #define OUTPUT_ROW \ 210 for (const unsigned char* ch = block; bytesInBlock-- > 0; ch++) {
213 do { \
214 if (!outputRow()) \
215 return false; \
216 rowsRemaining--; \
217 rowIter = rowBuffer.begin(); \
218 if (!rowsRemaining) \
219 return true; \
220 } while (0)
221
222 for (ch = block; bytesInBlock-- > 0; ch++) {
223 // Feed the next byte into the decoder's 32-bit input buffer. 211 // Feed the next byte into the decoder's 32-bit input buffer.
224 datum += ((int) *ch) << bits; 212 datum += ((int) *ch) << bits;
225 bits += 8; 213 bits += 8;
226 214
227 // Check for underflow of decoder's 32-bit input buffer. 215 // Check for underflow of decoder's 32-bit input buffer.
228 while (bits >= codesize) { 216 while (bits >= codesize) {
229 // Get the leading variable-length symbol from the data stream. 217 // Get the leading variable-length symbol from the data stream.
230 code = datum & codemask; 218 int code = datum & codemask;
231 datum >>= codesize; 219 datum >>= codesize;
232 bits -= codesize; 220 bits -= codesize;
233 221
234 // Reset the dictionary to its original state, if requested. 222 // Reset the dictionary to its original state, if requested.
235 if (code == clearCode) { 223 if (code == clearCode) {
236 codesize = m_frameContext->dataSize() + 1; 224 codesize = m_frameContext->dataSize() + 1;
237 codemask = (1 << codesize) - 1; 225 codemask = (1 << codesize) - 1;
238 avail = clearCode + 2; 226 avail = clearCode + 2;
239 oldcode = -1; 227 oldcode = -1;
240 continue; 228 continue;
241 } 229 }
242 230
243 // Check for explicit end-of-stream code. 231 // Check for explicit end-of-stream code.
244 if (code == (clearCode + 1)) { 232 if (code == (clearCode + 1)) {
245 // end-of-stream should only appear after all image data. 233 // end-of-stream should only appear after all image data.
246 if (!rowsRemaining) 234 if (!rowsRemaining)
247 return true; 235 return true;
248 return false; 236 return false;
249 } 237 }
250 238
251 if (oldcode == -1) { 239 const int tempCode = code;
252 *rowIter++ = suffix[code]; 240 unsigned short codeLength = 0;
253 if (rowIter == rowBuffer.end()) 241 if (code < avail) {
254 OUTPUT_ROW; 242 // This is a pre-existing code, so we already know what it
255 243 // encodes.
256 firstchar = oldcode = code; 244 codeLength = suffixLength[code];
257 continue; 245 rowIter += codeLength;
258 } 246 } else if (code == avail && oldcode != -1) {
259 247 // This is a new code just being added to the dictionary.
260 incode = code; 248 // It must encode the contents of the previous code, plus
261 if (code >= avail) { 249 // the first character of the previous code again.
262 stack[stackp++] = firstchar; 250 codeLength = suffixLength[oldcode] + 1;
251 rowIter += codeLength;
252 *--rowIter = firstchar;
263 code = oldcode; 253 code = oldcode;
264 254 } else {
265 if (stackp == MAX_BYTES) 255 // This is an invalid code. The dictionary is just initialized
266 return false; 256 // and the code is incomplete. We don't know how to handle
257 // this case.
258 return false;
267 } 259 }
268 260
269 while (code >= clearCode) { 261 while (code >= clearCode) {
270 if (code >= MAX_BYTES || code == prefix[code]) 262 *--rowIter = suffix[code];
271 return false;
272
273 // Even though suffix[] only holds characters through suffix[ava il - 1],
274 // allowing code >= avail here lets us be more tolerant of malfo rmed
275 // data. As long as code < MAX_BYTES, the only risk is a garbled image,
276 // which is no worse than refusing to display it.
277 stack[stackp++] = suffix[code];
278 code = prefix[code]; 263 code = prefix[code];
279
280 if (stackp == MAX_BYTES)
281 return false;
282 } 264 }
283 265
284 stack[stackp++] = firstchar = suffix[code]; 266 *--rowIter = firstchar = suffix[code];
285 267
286 // Define a new codeword in the dictionary. 268 // Define a new codeword in the dictionary as long as we've read
287 if (avail < 4096) { 269 // more than one value from the stream.
270 if (avail < MAX_DICTIONARY_ENTRIES && oldcode != -1) {
288 prefix[avail] = oldcode; 271 prefix[avail] = oldcode;
289 suffix[avail] = firstchar; 272 suffix[avail] = firstchar;
290 avail++; 273 suffixLength[avail] = suffixLength[oldcode] + 1;
274 ++avail;
291 275
292 // If we've used up all the codewords of a given length 276 // If we've used up all the codewords of a given length
293 // increase the length of codewords by one bit, but don't 277 // increase the length of codewords by one bit, but don't
294 // exceed the specified maximum codeword size of 12 bits. 278 // exceed the specified maximum codeword size.
295 if ((!(avail & codemask)) && (avail < 4096)) { 279 if (!(avail & codemask) && avail < MAX_DICTIONARY_ENTRIES) {
296 codesize++; 280 ++codesize;
297 codemask += avail; 281 codemask += avail;
298 } 282 }
299 } 283 }
300 oldcode = incode; 284 oldcode = tempCode;
285 rowIter += codeLength;
301 286
302 // Copy the decoded data out to the scanline buffer. 287 // Output as many rows as possible.
303 do { 288 GIFRow::iterator rowBegin = rowBuffer.begin();
304 *rowIter++ = stack[--stackp]; 289 for (; rowBegin + width <= rowIter; rowBegin += width) {
305 if (rowIter == rowBuffer.end()) 290 if (!outputRow(rowBegin))
306 OUTPUT_ROW; 291 return false;
307 } while (stackp > 0); 292 rowsRemaining--;
293 if (!rowsRemaining)
294 return true;
295 }
296
297 if (rowBegin != rowBuffer.begin()) {
298 // Move the remaining bytes to the beginning of the buffer.
299 const size_t bytesToCopy = rowIter - rowBegin;
300 memcpy(rowBuffer.begin(), rowBegin, bytesToCopy);
301 rowIter = rowBuffer.begin() + bytesToCopy;
302 }
308 } 303 }
309 } 304 }
310
311 return true; 305 return true;
312 } 306 }
313 307
314 void GIFColorMap::buildTable(const unsigned char* data, size_t length) 308 void GIFColorMap::buildTable(const unsigned char* data, size_t length)
315 { 309 {
316 if (!m_isDefined || !m_table.isEmpty()) 310 if (!m_isDefined || !m_table.isEmpty())
317 return; 311 return;
318 312
319 RELEASE_ASSERT(m_position + m_colors * GIF_COLORS <= length); 313 RELEASE_ASSERT(m_position + m_colors * BYTES_PER_COLORMAP_ENTRY <= length);
320 const unsigned char* srcColormap = data + m_position; 314 const unsigned char* srcColormap = data + m_position;
321 m_table.resize(m_colors); 315 m_table.resize(m_colors);
322 for (Table::iterator iter = m_table.begin(); iter != m_table.end(); ++iter) { 316 for (Table::iterator iter = m_table.begin(); iter != m_table.end(); ++iter) {
323 *iter = SkPackARGB32NoCheck(255, srcColormap[0], srcColormap[1], srcColo rmap[2]); 317 *iter = SkPackARGB32NoCheck(255, srcColormap[0], srcColormap[1], srcColo rmap[2]);
324 srcColormap += GIF_COLORS; 318 srcColormap += BYTES_PER_COLORMAP_ENTRY;
325 } 319 }
326 } 320 }
327 321
328 // Perform decoding for this frame. frameDecoded will be true if the entire fram e is decoded. 322 // Perform decoding for this frame. frameDecoded will be true if the entire fram e is decoded.
329 // Returns false if a decoding error occurred. This is a fatal error and causes the GIFImageReader to set the "decode failed" flag. 323 // Returns false if a decoding error occurred. This is a fatal error and causes the GIFImageReader to set the "decode failed" flag.
330 // Otherwise, either not enough data is available to decode further than before, or the new data has been decoded successfully; returns true in this case. 324 // Otherwise, either not enough data is available to decode further than before, or the new data has been decoded successfully; returns true in this case.
331 bool GIFFrameContext::decode(const unsigned char* data, size_t length, WebCore:: GIFImageDecoder* client, bool* frameDecoded) 325 bool GIFFrameContext::decode(const unsigned char* data, size_t length, WebCore:: GIFImageDecoder* client, bool* frameDecoded)
332 { 326 {
333 m_localColorMap.buildTable(data, length); 327 m_localColorMap.buildTable(data, length);
334 328
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
449 443
450 // CALLBACK: Inform the decoderplugin of our size. 444 // CALLBACK: Inform the decoderplugin of our size.
451 // Note: A subsequent frame might have dimensions larger than the "s creen" dimensions. 445 // Note: A subsequent frame might have dimensions larger than the "s creen" dimensions.
452 if (m_client && !m_client->setSize(m_screenWidth, m_screenHeight)) 446 if (m_client && !m_client->setSize(m_screenWidth, m_screenHeight))
453 return false; 447 return false;
454 448
455 const size_t globalColorMapColors = 2 << (currentComponent[4] & 0x07 ); 449 const size_t globalColorMapColors = 2 << (currentComponent[4] & 0x07 );
456 450
457 if ((currentComponent[4] & 0x80) && globalColorMapColors > 0) { /* g lobal map */ 451 if ((currentComponent[4] & 0x80) && globalColorMapColors > 0) { /* g lobal map */
458 m_globalColorMap.setTablePositionAndSize(dataPosition, globalCol orMapColors); 452 m_globalColorMap.setTablePositionAndSize(dataPosition, globalCol orMapColors);
459 GETN(GIF_COLORS * globalColorMapColors, GIFGlobalColormap); 453 GETN(BYTES_PER_COLORMAP_ENTRY * globalColorMapColors, GIFGlobalC olormap);
460 break; 454 break;
461 } 455 }
462 456
463 GETN(1, GIFImageStart); 457 GETN(1, GIFImageStart);
464 break; 458 break;
465 } 459 }
466 460
467 case GIFGlobalColormap: { 461 case GIFGlobalColormap: {
468 m_globalColorMap.setDefined(); 462 m_globalColorMap.setDefined();
469 GETN(1, GIFImageStart); 463 GETN(1, GIFImageStart);
(...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after
689 // frame can be progressively displayed. 683 // frame can be progressively displayed.
690 // FIXME: It is possible that a non-transparent frame 684 // FIXME: It is possible that a non-transparent frame
691 // can be interlaced and progressively displayed. 685 // can be interlaced and progressively displayed.
692 currentFrame->setProgressiveDisplay(currentFrameIsFirstFrame()); 686 currentFrame->setProgressiveDisplay(currentFrameIsFirstFrame());
693 687
694 const bool isLocalColormapDefined = currentComponent[8] & 0x80; 688 const bool isLocalColormapDefined = currentComponent[8] & 0x80;
695 if (isLocalColormapDefined) { 689 if (isLocalColormapDefined) {
696 // The three low-order bits of currentComponent[8] specify the b its per pixel. 690 // The three low-order bits of currentComponent[8] specify the b its per pixel.
697 const size_t numColors = 2 << (currentComponent[8] & 0x7); 691 const size_t numColors = 2 << (currentComponent[8] & 0x7);
698 currentFrame->localColorMap().setTablePositionAndSize(dataPositi on, numColors); 692 currentFrame->localColorMap().setTablePositionAndSize(dataPositi on, numColors);
699 GETN(GIF_COLORS * numColors, GIFImageColormap); 693 GETN(BYTES_PER_COLORMAP_ENTRY * numColors, GIFImageColormap);
700 break; 694 break;
701 } 695 }
702 696
703 GETN(1, GIFLZWStart); 697 GETN(1, GIFLZWStart);
704 break; 698 break;
705 } 699 }
706 700
707 case GIFImageColormap: { 701 case GIFImageColormap: {
708 ASSERT(!m_frames.isEmpty()); 702 ASSERT(!m_frames.isEmpty());
709 m_frames.last()->localColorMap().setDefined(); 703 m_frames.last()->localColorMap().setDefined();
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
753 if (m_frames.isEmpty() || m_frames.last()->isComplete()) 747 if (m_frames.isEmpty() || m_frames.last()->isComplete())
754 m_frames.append(adoptPtr(new GIFFrameContext(m_frames.size()))); 748 m_frames.append(adoptPtr(new GIFFrameContext(m_frames.size())));
755 } 749 }
756 750
757 // FIXME: Move this method to close to doLZW(). 751 // FIXME: Move this method to close to doLZW().
758 bool GIFLZWContext::prepareToDecode() 752 bool GIFLZWContext::prepareToDecode()
759 { 753 {
760 ASSERT(m_frameContext->isDataSizeDefined() && m_frameContext->isHeaderDefine d()); 754 ASSERT(m_frameContext->isDataSizeDefined() && m_frameContext->isHeaderDefine d());
761 755
762 // Since we use a codesize of 1 more than the datasize, we need to ensure 756 // Since we use a codesize of 1 more than the datasize, we need to ensure
763 // that our datasize is strictly less than the MAX_LZW_BITS value (12). 757 // that our datasize is strictly less than the MAX_DICTIONARY_ENTRY_BITS.
764 // This sets the largest possible codemask correctly at 4095. 758 if (m_frameContext->dataSize() >= MAX_DICTIONARY_ENTRY_BITS)
765 if (m_frameContext->dataSize() >= MAX_LZW_BITS)
766 return false; 759 return false;
767 clearCode = 1 << m_frameContext->dataSize(); 760 clearCode = 1 << m_frameContext->dataSize();
768 if (clearCode >= MAX_BYTES)
769 return false;
770
771 avail = clearCode + 2; 761 avail = clearCode + 2;
772 oldcode = -1; 762 oldcode = -1;
773 codesize = m_frameContext->dataSize() + 1; 763 codesize = m_frameContext->dataSize() + 1;
774 codemask = (1 << codesize) - 1; 764 codemask = (1 << codesize) - 1;
775 datum = bits = 0; 765 datum = bits = 0;
776 ipass = m_frameContext->interlaced() ? 1 : 0; 766 ipass = m_frameContext->interlaced() ? 1 : 0;
777 irow = 0; 767 irow = 0;
778 768
779 // Initialize output row buffer. 769 // We want to know the longest sequence encodable by a dictionary with
780 rowBuffer.resize(m_frameContext->width()); 770 // MAX_DICTIONARY_ENTRIES entries. If we ignore the need to encode the base
771 // values themselves at the beginning of the dictionary, as well as the need
772 // for a clear code or a termination code, we could use every entry to
773 // encode a series of multiple values. If the input value stream looked
774 // like "AAAAA..." (a long string of just one value), the first dictionary
775 // entry would encode AA, the next AAA, the next AAAA, and so forth. Thus
776 // the longest sequence would be MAX_DICTIONARY_ENTRIES + 1 values.
777 //
778 // However, we have to account for reserved entries. The first |datasize|
779 // bits are reserved for the base values, and the next two entries are
780 // reserved for the clear code and termination code. In theory a GIF can
781 // set the datasize to 0, meaning we have just two reserved entries, making
782 // the longest sequence (MAX_DICTIONARY_ENTIRES + 1) - 2 values long. Since
783 // each value is a byte, this is also the number of bytes in the longest
784 // encodable sequence.
785 const size_t maxBytes = MAX_DICTIONARY_ENTRIES - 1;
786
787 // Now allocate the output buffer. We decode directly into this buffer
788 // until we have at least one row worth of data, then call outputRow().
789 // This means worst case we may have (row width - 1) bytes in the buffer
790 // and then decode a sequence |maxBytes| long to append.
791 rowBuffer.resize(m_frameContext->width() - 1 + maxBytes);
781 rowIter = rowBuffer.begin(); 792 rowIter = rowBuffer.begin();
782 rowsRemaining = m_frameContext->height(); 793 rowsRemaining = m_frameContext->height();
783 794
784 // Clearing the whole suffix table lets us be more tolerant of bad data. 795 // Clearing the whole suffix table lets us be more tolerant of bad data.
785 memset(suffix, 0, sizeof(suffix)); 796 for (int i = 0; i < clearCode; ++i) {
786
787 // Clearing the whole prefix table to prevent uninitialized access.
788 memset(prefix, 0, sizeof(prefix));
789 for (int i = 0; i < clearCode; i++)
790 suffix[i] = i; 797 suffix[i] = i;
791 stackp = 0; 798 suffixLength[i] = 1;
799 }
792 return true; 800 return true;
793 } 801 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698