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

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: fixed comments 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
« no previous file with comments | « Source/core/platform/image-decoders/gif/GIFImageReader.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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++) {
Peter Kasting 2013/08/31 00:08:49 Nit: Put * on typename
Alpha Left Google 2013/09/01 01:40:21 Done.
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 if (oldcode == -1) {
Peter Kasting 2013/08/31 00:08:49 I just realized that because we don't check that (
Peter Kasting 2013/08/31 02:07:20 Update: The old code defended against this with th
Alpha Left Google 2013/09/01 01:40:21 I removed this branch and let the other branch han
240 // The previous code is not available either because this is the
241 // first code in the stream or the dictionary was reset.
242 // This code must therefore just be a single character, which we
243 // can write directly to the stream.
244 firstchar = oldcode = code;
245 if (rowIter >= rowBuffer.end())
Peter Kasting 2013/08/31 02:07:20 How can this condition or either of the ones below
Alpha Left Google 2013/09/01 01:40:21 That is true. Since we can be sure there's no cycl
246 return false;
252 *rowIter++ = suffix[code]; 247 *rowIter++ = suffix[code];
253 if (rowIter == rowBuffer.end()) 248 } else {
254 OUTPUT_ROW; 249 int tempCode = code;
250 unsigned short codeLength = 0;
251 if (code < avail) {
252 // This is a pre-existing code, so we already know what it
253 // encodes.
254 codeLength = suffixLength[code];
255 rowIter += codeLength;
256 if (rowIter > rowBuffer.end())
257 return false;
258 } else if (code == avail) {
259 // This is a new code just being added to the dictionary.
260 // It must encode the contents of the previous code, plus
261 // the first character of the previous code again.
262 codeLength = suffixLength[oldcode] + 1;
263 rowIter += codeLength;
264 if (rowIter > rowBuffer.end())
265 return false;
266 *--rowIter = firstchar;
267 code = oldcode;
268 } else {
269 // This is an invalid code.
270 return false;
271 }
255 272
256 firstchar = oldcode = code; 273 while (code >= clearCode) {
257 continue; 274 *--rowIter = suffix[code];
275 code = prefix[code];
276 }
277
278 *--rowIter = firstchar = suffix[code];
279
280 // Define a new codeword in the dictionary.
281 if (avail < MAX_DICTIONARY_ENTRIES) {
282 prefix[avail] = oldcode;
283 suffix[avail] = firstchar;
284 suffixLength[avail] = suffixLength[oldcode] + 1;
285 ++avail;
286
287 // If we've used up all the codewords of a given length
288 // increase the length of codewords by one bit, but don't
289 // exceed the specified maximum codeword size.
290 if ((!(avail & codemask)) && (avail < MAX_DICTIONARY_ENTRIES )) {
291 ++codesize;
292 codemask += avail;
293 }
294 }
295 oldcode = tempCode;
296 rowIter += codeLength;
258 } 297 }
259 298
260 incode = code; 299 // Output as many rows as possible.
261 if (code >= avail) { 300 GIFRow::iterator rowBegin = rowBuffer.begin();
262 stack[stackp++] = firstchar; 301 for (; rowBegin + width <= rowIter; rowBegin += width) {
263 code = oldcode; 302 if (!outputRow(rowBegin))
264
265 if (stackp == MAX_BYTES)
266 return false; 303 return false;
304 rowsRemaining--;
305 if (!rowsRemaining)
306 return true;
267 } 307 }
268 308
269 while (code >= clearCode) { 309 if (rowBegin != rowBuffer.begin()) {
270 if (code >= MAX_BYTES || code == prefix[code]) 310 // Move the remaining bytes to the beginning of buffer.
Peter Kasting 2013/08/31 00:08:49 Nit: buffer -> the buffer
Alpha Left Google 2013/09/01 01:40:21 Done.
271 return false; 311 size_t bytesToCopy = rowIter - rowBegin;
Peter Kasting 2013/08/31 00:08:49 Nit: Can be const (since you seem to be willing to
Alpha Left Google 2013/09/01 01:40:21 Done.
272 312 memcpy(rowBuffer.begin(), rowBegin, bytesToCopy);
273 // Even though suffix[] only holds characters through suffix[ava il - 1], 313 rowIter = rowBuffer.begin() + bytesToCopy;
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];
279
280 if (stackp == MAX_BYTES)
281 return false;
282 } 314 }
283
284 stack[stackp++] = firstchar = suffix[code];
285
286 // Define a new codeword in the dictionary.
287 if (avail < 4096) {
288 prefix[avail] = oldcode;
289 suffix[avail] = firstchar;
290 avail++;
291
292 // If we've used up all the codewords of a given length
293 // increase the length of codewords by one bit, but don't
294 // exceed the specified maximum codeword size of 12 bits.
295 if ((!(avail & codemask)) && (avail < 4096)) {
296 codesize++;
297 codemask += avail;
298 }
299 }
300 oldcode = incode;
301
302 // Copy the decoded data out to the scanline buffer.
303 do {
304 *rowIter++ = stack[--stackp];
305 if (rowIter == rowBuffer.end())
306 OUTPUT_ROW;
307 } while (stackp > 0);
308 } 315 }
309 } 316 }
310
311 return true; 317 return true;
312 } 318 }
313 319
314 void GIFColorMap::buildTable(const unsigned char* data, size_t length) 320 void GIFColorMap::buildTable(const unsigned char* data, size_t length)
315 { 321 {
316 if (!m_isDefined || !m_table.isEmpty()) 322 if (!m_isDefined || !m_table.isEmpty())
317 return; 323 return;
318 324
319 RELEASE_ASSERT(m_position + m_colors * GIF_COLORS <= length); 325 RELEASE_ASSERT(m_position + m_colors * BYTES_PER_COLORMAP_ENTRY <= length);
320 const unsigned char* srcColormap = data + m_position; 326 const unsigned char* srcColormap = data + m_position;
321 m_table.resize(m_colors); 327 m_table.resize(m_colors);
322 for (Table::iterator iter = m_table.begin(); iter != m_table.end(); ++iter) { 328 for (Table::iterator iter = m_table.begin(); iter != m_table.end(); ++iter) {
323 *iter = SkPackARGB32NoCheck(255, srcColormap[0], srcColormap[1], srcColo rmap[2]); 329 *iter = SkPackARGB32NoCheck(255, srcColormap[0], srcColormap[1], srcColo rmap[2]);
324 srcColormap += GIF_COLORS; 330 srcColormap += BYTES_PER_COLORMAP_ENTRY;
325 } 331 }
326 } 332 }
327 333
328 // Perform decoding for this frame. frameDecoded will be true if the entire fram e is decoded. 334 // 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. 335 // 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. 336 // 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) 337 bool GIFFrameContext::decode(const unsigned char* data, size_t length, WebCore:: GIFImageDecoder* client, bool* frameDecoded)
332 { 338 {
333 m_localColorMap.buildTable(data, length); 339 m_localColorMap.buildTable(data, length);
334 340
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
449 455
450 // CALLBACK: Inform the decoderplugin of our size. 456 // CALLBACK: Inform the decoderplugin of our size.
451 // Note: A subsequent frame might have dimensions larger than the "s creen" dimensions. 457 // Note: A subsequent frame might have dimensions larger than the "s creen" dimensions.
452 if (m_client && !m_client->setSize(m_screenWidth, m_screenHeight)) 458 if (m_client && !m_client->setSize(m_screenWidth, m_screenHeight))
453 return false; 459 return false;
454 460
455 const size_t globalColorMapColors = 2 << (currentComponent[4] & 0x07 ); 461 const size_t globalColorMapColors = 2 << (currentComponent[4] & 0x07 );
456 462
457 if ((currentComponent[4] & 0x80) && globalColorMapColors > 0) { /* g lobal map */ 463 if ((currentComponent[4] & 0x80) && globalColorMapColors > 0) { /* g lobal map */
458 m_globalColorMap.setTablePositionAndSize(dataPosition, globalCol orMapColors); 464 m_globalColorMap.setTablePositionAndSize(dataPosition, globalCol orMapColors);
459 GETN(GIF_COLORS * globalColorMapColors, GIFGlobalColormap); 465 GETN(BYTES_PER_COLORMAP_ENTRY * globalColorMapColors, GIFGlobalC olormap);
460 break; 466 break;
461 } 467 }
462 468
463 GETN(1, GIFImageStart); 469 GETN(1, GIFImageStart);
464 break; 470 break;
465 } 471 }
466 472
467 case GIFGlobalColormap: { 473 case GIFGlobalColormap: {
468 m_globalColorMap.setDefined(); 474 m_globalColorMap.setDefined();
469 GETN(1, GIFImageStart); 475 GETN(1, GIFImageStart);
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after
687 // frame can be progressively displayed. 693 // frame can be progressively displayed.
688 // FIXME: It is possible that a non-transparent frame 694 // FIXME: It is possible that a non-transparent frame
689 // can be interlaced and progressively displayed. 695 // can be interlaced and progressively displayed.
690 currentFrame->setProgressiveDisplay(currentFrameIsFirstFrame()); 696 currentFrame->setProgressiveDisplay(currentFrameIsFirstFrame());
691 697
692 const bool isLocalColormapDefined = currentComponent[8] & 0x80; 698 const bool isLocalColormapDefined = currentComponent[8] & 0x80;
693 if (isLocalColormapDefined) { 699 if (isLocalColormapDefined) {
694 // The three low-order bits of currentComponent[8] specify the b its per pixel. 700 // The three low-order bits of currentComponent[8] specify the b its per pixel.
695 const size_t numColors = 2 << (currentComponent[8] & 0x7); 701 const size_t numColors = 2 << (currentComponent[8] & 0x7);
696 currentFrame->localColorMap().setTablePositionAndSize(dataPositi on, numColors); 702 currentFrame->localColorMap().setTablePositionAndSize(dataPositi on, numColors);
697 GETN(GIF_COLORS * numColors, GIFImageColormap); 703 GETN(BYTES_PER_COLORMAP_ENTRY * numColors, GIFImageColormap);
698 break; 704 break;
699 } 705 }
700 706
701 GETN(1, GIFLZWStart); 707 GETN(1, GIFLZWStart);
702 break; 708 break;
703 } 709 }
704 710
705 case GIFImageColormap: { 711 case GIFImageColormap: {
706 ASSERT(!m_frames.isEmpty()); 712 ASSERT(!m_frames.isEmpty());
707 m_frames.last()->localColorMap().setDefined(); 713 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()) 757 if (m_frames.isEmpty() || m_frames.last()->isComplete())
752 m_frames.append(adoptPtr(new GIFFrameContext(m_frames.size()))); 758 m_frames.append(adoptPtr(new GIFFrameContext(m_frames.size())));
753 } 759 }
754 760
755 // FIXME: Move this method to close to doLZW(). 761 // FIXME: Move this method to close to doLZW().
756 bool GIFLZWContext::prepareToDecode() 762 bool GIFLZWContext::prepareToDecode()
757 { 763 {
758 ASSERT(m_frameContext->isDataSizeDefined() && m_frameContext->isHeaderDefine d()); 764 ASSERT(m_frameContext->isDataSizeDefined() && m_frameContext->isHeaderDefine d());
759 765
760 // Since we use a codesize of 1 more than the datasize, we need to ensure 766 // 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). 767 // that our datasize is strictly less than the MAX_DICTIONARY_ENTRY_BITS.
762 // This sets the largest possible codemask correctly at 4095. 768 if (m_frameContext->dataSize() >= MAX_DICTIONARY_ENTRY_BITS)
763 if (m_frameContext->dataSize() >= MAX_LZW_BITS)
764 return false; 769 return false;
765 clearCode = 1 << m_frameContext->dataSize(); 770 clearCode = 1 << m_frameContext->dataSize();
766 if (clearCode >= MAX_BYTES)
767 return false;
768
769 avail = clearCode + 2; 771 avail = clearCode + 2;
770 oldcode = -1; 772 oldcode = -1;
771 codesize = m_frameContext->dataSize() + 1; 773 codesize = m_frameContext->dataSize() + 1;
772 codemask = (1 << codesize) - 1; 774 codemask = (1 << codesize) - 1;
773 datum = bits = 0; 775 datum = bits = 0;
774 ipass = m_frameContext->interlaced() ? 1 : 0; 776 ipass = m_frameContext->interlaced() ? 1 : 0;
775 irow = 0; 777 irow = 0;
776 778
777 // Initialize output row buffer. 779 // We want to know the longest sequence encodable by a dictionary with
778 rowBuffer.resize(m_frameContext->width()); 780 // MAX_DICTIONARY_ENTRIES entries. If we ignore the need to encode the base
781 // values themselves at the beginning of the dictionary, as well as the need
782 // for a clear code or a termination code, we could use every entry to
783 // encode a series of multiple values. If the input value stream looked
784 // like "AAAAA..." (a long string of just one value), the first dictionary
785 // entry would encode AA, the next AAA, the next AAAA, and so forth. Thus
786 // the longest sequence would be MAX_DICTIONARY_ENTRIES + 1 values.
787 //
788 // However, we have to account for reserved entries. The first |datasize|
789 // bits are reserved for the base values, and the next two entries are
790 // reserved for the clear code and termination code. In theory a GIF can
791 // set the datasize to 0, meaning we have just two reserved entries, making
792 // the longest sequence (MAX_DICTIONARY_ENTIRES + 1) - 2 values long. Since
793 // each value is a byte, this is also the number of bytes in the longest
794 // encodable sequence.
795 const size_t maxBytes = MAX_DICTIONARY_ENTRIES - 1;
796
797 // Now allocate the output buffer. We decode directly into this buffer
798 // until we have at least one row worth of data, then call outputRow().
799 // This means worst case we may have (row width - 1) bytes in the buffer
800 // and then decode a sequence |maxBytes| long to append.
801 rowBuffer.resize(m_frameContext->width() - 1 + maxBytes);
779 rowIter = rowBuffer.begin(); 802 rowIter = rowBuffer.begin();
780 rowsRemaining = m_frameContext->height(); 803 rowsRemaining = m_frameContext->height();
781 804
782 // Clearing the whole suffix table lets us be more tolerant of bad data. 805 // Clearing the whole suffix table lets us be more tolerant of bad data.
Peter Kasting 2013/08/31 00:08:49 I think this comment and the memset can just be nu
Alpha Left Google 2013/09/01 01:40:21 Thinking about this a bit more since we're now mor
783 memset(suffix, 0, sizeof(suffix)); 806 for (int i = 0; i < clearCode; ++i)
807 suffix[i] = i;
808 memset(suffix + clearCode, 0, sizeof(suffix) - clearCode);
809
810 for (int i = 0; i < MAX_DICTIONARY_ENTRIES; i++)
811 suffixLength[i] = 1;
784 812
785 // Clearing the whole prefix table to prevent uninitialized access. 813 // Clearing the whole prefix table to prevent uninitialized access.
786 memset(prefix, 0, sizeof(prefix)); 814 memset(prefix, 0, sizeof(prefix));
787 for (int i = 0; i < clearCode; i++)
788 suffix[i] = i;
789 stackp = 0;
790 return true; 815 return true;
791 } 816 }
OLDNEW
« no previous file with comments | « Source/core/platform/image-decoders/gif/GIFImageReader.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698