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

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: bad code initial dictionary Created 7 years, 3 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) {
Peter Kasting 2013/09/03 18:49:45 Why use >= here instead of ==? This allows an att
Alpha Left Google 2013/09/04 23:47:28 Nice catch. I didn't realize tempCode would mess u
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;
Peter Kasting 2013/09/03 18:49:45 Nit: This subtraction isn't necessary... maybe co
Alpha Left Google 2013/09/04 23:47:28 I think my original code was the second one but I
Peter Kasting 2013/09/05 06:13:01 OK. I'm not sure how any of these reflect the com
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 only if the
287 if (avail < 4096) { 269 // dictionary is not in initial state.
Peter Kasting 2013/09/03 18:49:45 Nit: This comment isn't actually correct (checking
Alpha Left Google 2013/09/04 23:47:28 Done.
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)) {
Peter Kasting 2013/09/03 18:49:45 Nit: No parens necessary around unary operator. I
Alpha Left Google 2013/09/04 23:47:28 Done.
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 217 matching lines...) Expand 10 before | Expand all | Expand 10 after
687 // frame can be progressively displayed. 681 // frame can be progressively displayed.
688 // FIXME: It is possible that a non-transparent frame 682 // FIXME: It is possible that a non-transparent frame
689 // can be interlaced and progressively displayed. 683 // can be interlaced and progressively displayed.
690 currentFrame->setProgressiveDisplay(currentFrameIsFirstFrame()); 684 currentFrame->setProgressiveDisplay(currentFrameIsFirstFrame());
691 685
692 const bool isLocalColormapDefined = currentComponent[8] & 0x80; 686 const bool isLocalColormapDefined = currentComponent[8] & 0x80;
693 if (isLocalColormapDefined) { 687 if (isLocalColormapDefined) {
694 // The three low-order bits of currentComponent[8] specify the b its per pixel. 688 // The three low-order bits of currentComponent[8] specify the b its per pixel.
695 const size_t numColors = 2 << (currentComponent[8] & 0x7); 689 const size_t numColors = 2 << (currentComponent[8] & 0x7);
696 currentFrame->localColorMap().setTablePositionAndSize(dataPositi on, numColors); 690 currentFrame->localColorMap().setTablePositionAndSize(dataPositi on, numColors);
697 GETN(GIF_COLORS * numColors, GIFImageColormap); 691 GETN(BYTES_PER_COLORMAP_ENTRY * numColors, GIFImageColormap);
698 break; 692 break;
699 } 693 }
700 694
701 GETN(1, GIFLZWStart); 695 GETN(1, GIFLZWStart);
702 break; 696 break;
703 } 697 }
704 698
705 case GIFImageColormap: { 699 case GIFImageColormap: {
706 ASSERT(!m_frames.isEmpty()); 700 ASSERT(!m_frames.isEmpty());
707 m_frames.last()->localColorMap().setDefined(); 701 m_frames.last()->localColorMap().setDefined();
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
751 if (m_frames.isEmpty() || m_frames.last()->isComplete()) 745 if (m_frames.isEmpty() || m_frames.last()->isComplete())
752 m_frames.append(adoptPtr(new GIFFrameContext(m_frames.size()))); 746 m_frames.append(adoptPtr(new GIFFrameContext(m_frames.size())));
753 } 747 }
754 748
755 // FIXME: Move this method to close to doLZW(). 749 // FIXME: Move this method to close to doLZW().
756 bool GIFLZWContext::prepareToDecode() 750 bool GIFLZWContext::prepareToDecode()
757 { 751 {
758 ASSERT(m_frameContext->isDataSizeDefined() && m_frameContext->isHeaderDefine d()); 752 ASSERT(m_frameContext->isDataSizeDefined() && m_frameContext->isHeaderDefine d());
759 753
760 // Since we use a codesize of 1 more than the datasize, we need to ensure 754 // Since we use a codesize of 1 more than the datasize, we need to ensure
761 // that our datasize is strictly less than the MAX_LZW_BITS value (12). 755 // that our datasize is strictly less than the MAX_DICTIONARY_ENTRY_BITS.
762 // This sets the largest possible codemask correctly at 4095. 756 if (m_frameContext->dataSize() >= MAX_DICTIONARY_ENTRY_BITS)
763 if (m_frameContext->dataSize() >= MAX_LZW_BITS)
764 return false; 757 return false;
765 clearCode = 1 << m_frameContext->dataSize(); 758 clearCode = 1 << m_frameContext->dataSize();
766 if (clearCode >= MAX_BYTES)
767 return false;
768
769 avail = clearCode + 2; 759 avail = clearCode + 2;
770 oldcode = -1; 760 oldcode = -1;
771 codesize = m_frameContext->dataSize() + 1; 761 codesize = m_frameContext->dataSize() + 1;
772 codemask = (1 << codesize) - 1; 762 codemask = (1 << codesize) - 1;
773 datum = bits = 0; 763 datum = bits = 0;
774 ipass = m_frameContext->interlaced() ? 1 : 0; 764 ipass = m_frameContext->interlaced() ? 1 : 0;
775 irow = 0; 765 irow = 0;
776 766
777 // Initialize output row buffer. 767 // We want to know the longest sequence encodable by a dictionary with
778 rowBuffer.resize(m_frameContext->width()); 768 // MAX_DICTIONARY_ENTRIES entries. If we ignore the need to encode the base
769 // values themselves at the beginning of the dictionary, as well as the need
770 // for a clear code or a termination code, we could use every entry to
771 // encode a series of multiple values. If the input value stream looked
772 // like "AAAAA..." (a long string of just one value), the first dictionary
773 // entry would encode AA, the next AAA, the next AAAA, and so forth. Thus
774 // the longest sequence would be MAX_DICTIONARY_ENTRIES + 1 values.
775 //
776 // However, we have to account for reserved entries. The first |datasize|
777 // bits are reserved for the base values, and the next two entries are
778 // reserved for the clear code and termination code. In theory a GIF can
779 // set the datasize to 0, meaning we have just two reserved entries, making
780 // the longest sequence (MAX_DICTIONARY_ENTIRES + 1) - 2 values long. Since
781 // each value is a byte, this is also the number of bytes in the longest
782 // encodable sequence.
783 const size_t maxBytes = MAX_DICTIONARY_ENTRIES - 1;
784
785 // Now allocate the output buffer. We decode directly into this buffer
786 // until we have at least one row worth of data, then call outputRow().
787 // This means worst case we may have (row width - 1) bytes in the buffer
788 // and then decode a sequence |maxBytes| long to append.
789 rowBuffer.resize(m_frameContext->width() - 1 + maxBytes);
779 rowIter = rowBuffer.begin(); 790 rowIter = rowBuffer.begin();
780 rowsRemaining = m_frameContext->height(); 791 rowsRemaining = m_frameContext->height();
781 792
782 // Clearing the whole suffix table lets us be more tolerant of bad data. 793 // Clearing the whole suffix table lets us be more tolerant of bad data.
783 memset(suffix, 0, sizeof(suffix)); 794 for (int i = 0; i < clearCode; ++i) {
784
785 // Clearing the whole prefix table to prevent uninitialized access.
786 memset(prefix, 0, sizeof(prefix));
787 for (int i = 0; i < clearCode; i++)
788 suffix[i] = i; 795 suffix[i] = i;
789 stackp = 0; 796 suffixLength[i] = 1;
797 }
790 return true; 798 return true;
791 } 799 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698