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

Side by Side Diff: third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp

Issue 1460523002: GIF decoding to Index8, unit tests and misusing unit test as benchmark (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years 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
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. 2 * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 1. Redistributions of source code must retain the above copyright 7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright 9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
(...skipping 15 matching lines...) Expand all
26 #include "config.h" 26 #include "config.h"
27 #include "platform/image-decoders/gif/GIFImageDecoder.h" 27 #include "platform/image-decoders/gif/GIFImageDecoder.h"
28 28
29 #include <limits> 29 #include <limits>
30 #include "platform/image-decoders/gif/GIFImageReader.h" 30 #include "platform/image-decoders/gif/GIFImageReader.h"
31 #include "wtf/NotFound.h" 31 #include "wtf/NotFound.h"
32 #include "wtf/PassOwnPtr.h" 32 #include "wtf/PassOwnPtr.h"
33 33
34 namespace blink { 34 namespace blink {
35 35
36 GIFImageDecoder::GIFImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOp tion colorOptions, size_t maxDecodedBytes) 36 GIFImageDecoder::GIFImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOp tion colorOptions, size_t maxDecodedBytes, ImageFrame::ColorType colorType)
37 : ImageDecoder(alphaOption, colorOptions, maxDecodedBytes) 37 : ImageDecoder(alphaOption, colorOptions, maxDecodedBytes)
38 , phaveDecodedRow(&GIFImageDecoder::haveDecodedRowIndex8)
scroggo_chromium 2016/01/06 21:50:40 If the requested color type is N32, shouldn't this
aleksandar.stojiljkovic 2016/01/18 13:58:50 Done. Removed the line since the initialization or
38 , m_repetitionCount(cAnimationLoopOnce) 39 , m_repetitionCount(cAnimationLoopOnce)
40 , m_colorMode(colorType)
39 { 41 {
40 } 42 }
41 43
42 GIFImageDecoder::~GIFImageDecoder() 44 GIFImageDecoder::~GIFImageDecoder()
43 { 45 {
44 } 46 }
45 47
46 void GIFImageDecoder::onSetData(SharedBuffer* data) 48 void GIFImageDecoder::onSetData(SharedBuffer* data)
47 { 49 {
48 if (m_reader) 50 if (m_reader)
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
95 m_reader->frameContext(index)->isHeaderDefined()) ? 97 m_reader->frameContext(index)->isHeaderDefined()) ?
96 m_reader->frameContext(index)->delayTime() : 0; 98 m_reader->frameContext(index)->delayTime() : 0;
97 } 99 }
98 100
99 bool GIFImageDecoder::setFailed() 101 bool GIFImageDecoder::setFailed()
100 { 102 {
101 m_reader.clear(); 103 m_reader.clear();
102 return ImageDecoder::setFailed(); 104 return ImageDecoder::setFailed();
103 } 105 }
104 106
105 bool GIFImageDecoder::haveDecodedRow(size_t frameIndex, GIFRow::const_iterator r owBegin, size_t width, size_t rowNumber, unsigned repeatCount, bool writeTranspa rentPixels) 107 static inline const GIFColorMap& getColorMap(const GIFFrameContext& frameContext , const GIFImageReader& reader)
106 { 108 {
107 const GIFFrameContext* frameContext = m_reader->frameContext(frameIndex); 109 return (frameContext.localColorMap().isDefined() ? frameContext.localColorMa p() : reader.globalColorMap());
110 }
111
112 bool GIFImageDecoder::haveDecodedRowN32(const GIFFrameContext& frameContext, GIF Row::const_iterator rowBegin, size_t rowNumber, unsigned repeatCount, bool write TransparentPixels)
113 {
114 ASSERT(m_frameBufferCache[frameContext.frameId()].bitmap().colorType() == kN 32_SkColorType);
115
108 // The pixel data and coordinates supplied to us are relative to the frame's 116 // The pixel data and coordinates supplied to us are relative to the frame's
109 // origin within the entire image size, i.e. 117 // origin within the entire image size, i.e.
110 // (frameContext->xOffset, frameContext->yOffset). There is no guarantee 118 // (frameContext->xOffset, frameContext->yOffset). There is no guarantee
111 // that width == (size().width() - frameContext->xOffset), so 119 // that width == (size().width() - frameContext->xOffset), so
112 // we must ensure we don't run off the end of either the source data or the 120 // we must ensure we don't run off the end of either the source data or the
113 // row's X-coordinates. 121 // row's X-coordinates.
114 const int xBegin = frameContext->xOffset(); 122 const size_t width = frameContext.width();
115 const int yBegin = frameContext->yOffset() + rowNumber; 123 const int xBegin = frameContext.xOffset();
116 const int xEnd = std::min(static_cast<int>(frameContext->xOffset() + width), size().width()); 124 const int yBegin = frameContext.yOffset() + rowNumber;
117 const int yEnd = std::min(static_cast<int>(frameContext->yOffset() + rowNumb er + repeatCount), size().height()); 125 const int xEnd = std::min(static_cast<int>(xBegin + width), size().width());
126 const int yEnd = std::min(static_cast<int>(yBegin + repeatCount), size().hei ght());
118 if (!width || (xBegin < 0) || (yBegin < 0) || (xEnd <= xBegin) || (yEnd <= y Begin)) 127 if (!width || (xBegin < 0) || (yBegin < 0) || (xEnd <= xBegin) || (yEnd <= y Begin))
119 return true; 128 return true;
120 129
121 const GIFColorMap::Table& colorTable = frameContext->localColorMap().isDefin ed() ? frameContext->localColorMap().table() : m_reader->globalColorMap().table( ); 130 const GIFColorMap::Table& colorTable = getColorMap(frameContext, *m_reader). table();
122 131
123 if (colorTable.isEmpty()) 132 if (colorTable.isEmpty())
124 return true; 133 return true;
125 134
126 GIFColorMap::Table::const_iterator colorTableIter = colorTable.begin(); 135 GIFColorMap::Table::const_iterator colorTableIter = colorTable.begin();
127 136
128 // Initialize the frame if necessary. 137 ImageFrame& buffer = m_frameBufferCache[frameContext.frameId()];
129 ImageFrame& buffer = m_frameBufferCache[frameIndex]; 138 const size_t transparentPixel = frameContext.transparentPixel();
130 if ((buffer.status() == ImageFrame::FrameEmpty) && !initFrameBuffer(frameInd ex))
131 return false;
132
133 const size_t transparentPixel = frameContext->transparentPixel();
134 GIFRow::const_iterator rowEnd = rowBegin + (xEnd - xBegin); 139 GIFRow::const_iterator rowEnd = rowBegin + (xEnd - xBegin);
135 ImageFrame::PixelData* currentAddress = buffer.getAddr(xBegin, yBegin); 140 ImageFrame::PixelData* currentAddress = buffer.getAddr(xBegin, yBegin);
136 141
137 // We may or may not need to write transparent pixels to the buffer. 142 // We may or may not need to write transparent pixels to the buffer.
138 // If we're compositing against a previous image, it's wrong, and if 143 // If we're compositing against a previous image, it's wrong, and if
139 // we're writing atop a cleared, fully transparent buffer, it's 144 // we're writing atop a cleared, fully transparent buffer, it's
140 // unnecessary; but if we're decoding an interlaced gif and 145 // unnecessary; but if we're decoding an interlaced gif and
141 // displaying it "Haeberli"-style, we must write these for passes 146 // displaying it "Haeberli"-style, we must write these for passes
142 // beyond the first, or the initial passes will "show through" the 147 // beyond the first, or the initial passes will "show through" the
143 // later ones. 148 // later ones.
(...skipping 23 matching lines...) Expand all
167 } 172 }
168 173
169 // Tell the frame to copy the row data if need be. 174 // Tell the frame to copy the row data if need be.
170 if (repeatCount > 1) 175 if (repeatCount > 1)
171 buffer.copyRowNTimes(xBegin, xEnd, yBegin, yEnd); 176 buffer.copyRowNTimes(xBegin, xEnd, yBegin, yEnd);
172 177
173 buffer.setPixelsChanged(true); 178 buffer.setPixelsChanged(true);
174 return true; 179 return true;
175 } 180 }
176 181
182 bool GIFImageDecoder::haveDecodedRowIndex8(const GIFFrameContext& frameContext, GIFRow::const_iterator rowBegin, size_t rowNumber, unsigned repeatCount, bool wr iteTransparentPixels)
scroggo_chromium 2016/01/06 21:50:40 Is there a way to share more code here?
aleksandar.stojiljkovic 2016/01/18 13:58:50 Done.
183 {
184 ASSERT(m_frameBufferCache[frameContext.frameId()].bitmap().colorType() == kI ndex_8_SkColorType);
185
186 // The pixel data and coordinates supplied to us are relative to the frame's
187 // origin within the entire image size, i.e.
188 // (frameContext.xOffset, frameContext.yOffset). There is no guarantee
189 // that width == (size().width() - frameContext.xOffset), so
190 // we must ensure we don't run off the end of either the source data or the
191 // row's X-coordinates.
192 const size_t width = frameContext.width();
193 const int xBegin = frameContext.xOffset();
194 const int yBegin = frameContext.yOffset() + rowNumber;
195 const int xEnd = std::min(static_cast<int>(frameContext.xOffset() + width), size().width());
196 const int yEnd = std::min(static_cast<int>(frameContext.yOffset() + rowNumbe r + repeatCount), size().height());
197 if (!width || (xBegin < 0) || (yBegin < 0) || (xEnd <= xBegin) || (yEnd <= y Begin))
198 return true;
199
200 const GIFColorMap::Table& colorTable = getColorMap(frameContext, *m_reader). table();
201
202 if (colorTable.isEmpty())
203 return true;
204
205
206 ImageFrame& buffer = m_frameBufferCache[frameContext.frameId()];
207 const size_t transparentPixel = frameContext.transparentPixel();
208 GIFRow::const_iterator rowEnd = rowBegin + (xEnd - xBegin);
209 ImageFrame::PixelData8* currentAddress = buffer.getAddr8(xBegin, yBegin);
210
211 bool opaque = true;
212 // writeTransparentPixels is writing without check, but calculates if there is transparency
213 // (m_currentBufferSawAlpha). If transparency was found in previous rows, no need to calculate here.
214 writeTransparentPixels = writeTransparentPixels || buffer.requiredPreviousFr ameIndex() == kNotFound;
215 if (transparentPixel < colorTable.size() && !(writeTransparentPixels && m_cu rrentBufferSawAlpha)) {
216 if (writeTransparentPixels) {
217 while (rowBegin != rowEnd) {
218 opaque = opaque && (*rowBegin ^ transparentPixel);
219 *currentAddress++ = *rowBegin++;
220 }
221 } else {
222 for (; rowBegin != rowEnd; ++rowBegin, ++currentAddress) {
223 size_t index = *rowBegin;
224 if (index == transparentPixel) {
225 opaque = false;
226 } else {
227 *currentAddress = index;
228 }
229 }
230 }
231 } else {
232 // No transparency to deal with.
233 if (xEnd - xBegin == size().width()) {
234 size_t* destination = (size_t*)currentAddress;
235 const size_t* source = (const size_t*)rowBegin;
236 const size_t count = (rowEnd - rowBegin + sizeof(size_t) - 1) / size of(size_t);
237 const size_t* sourceEnd = source + count;
238 while (source != sourceEnd)
239 *destination++ = *source++;
240 } else {
241 while (rowBegin != rowEnd)
242 *currentAddress++ = *rowBegin++;
243 }
244 }
245
246 if (!opaque)
247 m_currentBufferSawAlpha = true;
248
249 // Tell the frame to copy the row data if specified.
250 if (repeatCount > 1) {
251 const int rowBytes = (xEnd - xBegin) * sizeof(uint8_t);
252 const ImageFrame::PixelData8* const startAddr = buffer.getAddr8(xBegin, yBegin);
253 for (int destY = yBegin + 1; destY < yEnd; ++destY)
254 memcpy(buffer.getAddr8(xBegin, destY), startAddr, rowBytes);
255 }
256
257 buffer.setPixelsChanged(true);
258 return true;
259 }
260
177 bool GIFImageDecoder::parseCompleted() const 261 bool GIFImageDecoder::parseCompleted() const
178 { 262 {
179 return m_reader && m_reader->parseCompleted(); 263 return m_reader && m_reader->parseCompleted();
180 } 264 }
181 265
182 bool GIFImageDecoder::frameComplete(size_t frameIndex) 266 bool GIFImageDecoder::frameComplete(size_t frameIndex)
183 { 267 {
184 // Initialize the frame if necessary. Some GIFs insert do-nothing frames, 268 // Initialize the frame if necessary. Some GIFs insert do-nothing frames,
185 // in which case we never reach haveDecodedRow() before getting here. 269 // in which case we never reach haveDecodedRow() before getting here.
186 ImageFrame& buffer = m_frameBufferCache[frameIndex]; 270 ImageFrame& buffer = m_frameBufferCache[frameIndex];
187 if ((buffer.status() == ImageFrame::FrameEmpty) && !initFrameBuffer(frameInd ex)) 271 if ((buffer.status() == ImageFrame::FrameEmpty) && !initFrameBuffer(frameInd ex))
188 return false; // initFrameBuffer() has already called setFailed(). 272 return false; // initFrameBuffer() has already called setFailed().
189 273
190 buffer.setStatus(ImageFrame::FrameComplete); 274 buffer.setStatus(ImageFrame::FrameComplete);
191 275
192 if (!m_currentBufferSawAlpha) { 276 if (!m_currentBufferSawAlpha) {
193 // The whole frame was non-transparent, so it's possible that the entire 277 // The whole frame was non-transparent, so it's possible that the entire
194 // resulting buffer was non-transparent, and we can setHasAlpha(false). 278 // resulting buffer was non-transparent, and we can setHasAlpha(false).
195 if (buffer.originalFrameRect().contains(IntRect(IntPoint(), size()))) { 279 if (buffer.originalFrameRect().contains(IntRect(IntPoint(), size()))) {
196 buffer.setHasAlpha(false); 280 buffer.setHasAlpha(false);
197 buffer.setRequiredPreviousFrameIndex(kNotFound); 281 buffer.setRequiredPreviousFrameIndex(kNotFound);
198 } else if (buffer.requiredPreviousFrameIndex() != kNotFound) { 282 } else if (buffer.requiredPreviousFrameIndex() != kNotFound) {
199 // Tricky case. This frame does not have alpha only if everywhere 283 // Tricky case. This frame does not have alpha only if everywhere
200 // outside its rect doesn't have alpha. To know whether this is 284 // outside its rect doesn't have alpha. To know whether this is
201 // true, we check the start state of the frame -- if it doesn't have 285 // true, we check the start state of the frame -- if it doesn't have
202 // alpha, we're safe. 286 // alpha, we're safe.
203 const ImageFrame* prevBuffer = &m_frameBufferCache[buffer.requiredPr eviousFrameIndex()]; 287 const ImageFrame* previousBuffer = &m_frameBufferCache[buffer.requir edPreviousFrameIndex()];
scroggo_chromium 2016/01/06 21:50:41 Why the name change? This name change distracts f
aleksandar.stojiljkovic 2016/01/18 13:58:50 Done.
204 ASSERT(prevBuffer->disposalMethod() != ImageFrame::DisposeOverwriteP revious); 288 ASSERT(previousBuffer->disposalMethod() != ImageFrame::DisposeOverwr itePrevious);
205 289
206 // Now, if we're at a DisposeNotSpecified or DisposeKeep frame, then 290 // Now, if we're at a DisposeNotSpecified or DisposeKeep frame, then
207 // we can say we have no alpha if that frame had no alpha. But 291 // we can say we have no alpha if that frame had no alpha. But
208 // since in initFrameBuffer() we already copied that frame's alpha 292 // since in initFrameBuffer() we already copied that frame's alpha
209 // state into the current frame's, we need do nothing at all here. 293 // state into the current frame's, we need do nothing at all here.
210 // 294 //
211 // The only remaining case is a DisposeOverwriteBgcolor frame. If 295 // The only remaining case is a DisposeOverwriteBgcolor frame. If
212 // it had no alpha, and its rect is contained in the current frame's 296 // it had no alpha, and its rect is contained in the current frame's
213 // rect, we know the current frame has no alpha. 297 // rect, we know the current frame has no alpha.
214 if ((prevBuffer->disposalMethod() == ImageFrame::DisposeOverwriteBgc olor) && !prevBuffer->hasAlpha() && buffer.originalFrameRect().contains(prevBuff er->originalFrameRect())) 298 if ((previousBuffer->disposalMethod() == ImageFrame::DisposeOverwrit eBgcolor) && !previousBuffer->hasAlpha() && buffer.originalFrameRect().contains( previousBuffer->originalFrameRect()))
215 buffer.setHasAlpha(false); 299 buffer.setHasAlpha(false);
216 } 300 }
217 } 301 }
218 302
219 return true; 303 return true;
220 } 304 }
221 305
222 size_t GIFImageDecoder::clearCacheExceptFrame(size_t clearExceptFrame) 306 size_t GIFImageDecoder::clearCacheExceptFrame(size_t clearExceptFrame)
223 { 307 {
224 // We need to preserve frames such that: 308 // We need to preserve frames such that:
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
286 if (m_frameBufferCache[*i].status() != ImageFrame::FrameComplete) 370 if (m_frameBufferCache[*i].status() != ImageFrame::FrameComplete)
287 break; 371 break;
288 } 372 }
289 373
290 // It is also a fatal error if all data is received and we have decoded all 374 // It is also a fatal error if all data is received and we have decoded all
291 // frames available but the file is truncated. 375 // frames available but the file is truncated.
292 if (index >= m_frameBufferCache.size() - 1 && isAllDataReceived() && m_reade r && !m_reader->parseCompleted()) 376 if (index >= m_frameBufferCache.size() - 1 && isAllDataReceived() && m_reade r && !m_reader->parseCompleted())
293 setFailed(); 377 setFailed();
294 } 378 }
295 379
380 void GIFImageDecoder::setupHaveDecodedRowCallbacks(bool isIndex8)
381 {
382 phaveDecodedRow = isIndex8
383 ? (&GIFImageDecoder::haveDecodedRowIndex8)
384 : (&GIFImageDecoder::haveDecodedRowN32);
385 }
386
296 void GIFImageDecoder::parse(GIFParseQuery query) 387 void GIFImageDecoder::parse(GIFParseQuery query)
297 { 388 {
298 if (failed()) 389 if (failed())
299 return; 390 return;
300 391
301 if (!m_reader) { 392 if (!m_reader) {
302 m_reader = adoptPtr(new GIFImageReader(this)); 393 m_reader = adoptPtr(new GIFImageReader(this));
303 m_reader->setData(m_data); 394 m_reader->setData(m_data);
304 } 395 }
305 396
306 if (!m_reader->parse(query)) 397 if (!m_reader->parse(query))
307 setFailed(); 398 setFailed();
308 } 399 }
309 400
310 bool GIFImageDecoder::initFrameBuffer(size_t frameIndex) 401 bool GIFImageDecoder::initFrameBufferFromPreviousN32(ImageFrame* const buffer, c onst ImageFrame& previousBuffer)
scroggo_chromium 2016/01/06 21:50:41 I was a little surprised to see a const pointer. A
aleksandar.stojiljkovic 2016/01/18 13:58:50 Done. Thanks. Method renamed to initFrameBufferFro
311 { 402 {
403 // Preserve the last frame as the starting state for this frame.
404 if (!buffer->copyBitmapData(previousBuffer, ImageFrame::N32))
405 return false;
406
407 if (previousBuffer.disposalMethod() == ImageFrame::DisposeOverwriteBgcolor) {
408 // We want to clear the previous frame to transparent, without
409 // affecting pixels in the image outside of the frame.
410 const IntRect& prevRect = previousBuffer.originalFrameRect();
411 ASSERT(!prevRect.contains(IntRect(IntPoint(), size())));
412 buffer->zeroFillFrameRect(prevRect);
413 }
414 return true;
415 }
416
417 bool GIFImageDecoder::initFrameBufferN32(size_t frameIndex)
418 {
419 setupHaveDecodedRowCallbacks(false);
420
312 // Initialize the frame rect in our buffer. 421 // Initialize the frame rect in our buffer.
313 ImageFrame* const buffer = &m_frameBufferCache[frameIndex]; 422 ImageFrame* const buffer = &m_frameBufferCache[frameIndex];
314 423
315 size_t requiredPreviousFrameIndex = buffer->requiredPreviousFrameIndex(); 424 size_t requiredPreviousFrameIndex = buffer->requiredPreviousFrameIndex();
316 if (requiredPreviousFrameIndex == kNotFound) { 425 if (requiredPreviousFrameIndex == kNotFound) {
317 // This frame doesn't rely on any previous data. 426 // This frame doesn't rely on any previous data.
318 if (!buffer->setSize(size().width(), size().height())) 427 if (!buffer->setSize(size().width(), size().height(), getBackgroundColor (frameIndex)))
319 return setFailed(); 428 return setFailed();
320 } else { 429 } else {
321 const ImageFrame* prevBuffer = &m_frameBufferCache[requiredPreviousFrame Index]; 430 const ImageFrame* previousBuffer = &m_frameBufferCache[requiredPreviousF rameIndex];
scroggo_chromium 2016/01/06 21:50:41 Again, I think the name change is distracting.
aleksandar.stojiljkovic 2016/01/18 13:58:50 Done.
322 ASSERT(prevBuffer->status() == ImageFrame::FrameComplete); 431 ASSERT(previousBuffer->status() == ImageFrame::FrameComplete);
323 432 if (!initFrameBufferFromPreviousN32(buffer, *previousBuffer))
324 // Preserve the last frame as the starting state for this frame.
325 if (!buffer->copyBitmapData(*prevBuffer))
326 return setFailed(); 433 return setFailed();
327
328 if (prevBuffer->disposalMethod() == ImageFrame::DisposeOverwriteBgcolor) {
329 // We want to clear the previous frame to transparent, without
330 // affecting pixels in the image outside of the frame.
331 const IntRect& prevRect = prevBuffer->originalFrameRect();
332 ASSERT(!prevRect.contains(IntRect(IntPoint(), size())));
333 buffer->zeroFillFrameRect(prevRect);
334 }
335 } 434 }
336 435
337 // Update our status to be partially complete. 436 // Update our status to be partially complete.
338 buffer->setStatus(ImageFrame::FramePartial); 437 buffer->setStatus(ImageFrame::FramePartial);
339 438
439 // Reset the alpha pixel tracker for this frame.
440 m_currentBufferSawAlpha = false;
441 return true;
442 }
443
444 bool GIFImageDecoder::isIndex8Applicable(const GIFFrameContext& frame, size_t re quiredPreviousFrameIndex) const
445 {
446 const GIFColorMap& colorMap = getColorMap(frame, *m_reader);
447 const bool useGlobalColorMap = frame.localColorMap().isDefined();
448
449 if (requiredPreviousFrameIndex == kNotFound) {
450 if (colorMap.getTableSize() == 256 && frame.transparentPixel() >= colorM ap.getTableSize()
451 && (useGlobalColorMap && m_reader->backgroundIndex() >= colorMap.get TableSize())
scroggo_chromium 2016/01/06 21:50:40 Do you need the parentheses here?
aleksandar.stojiljkovic 2016/01/18 13:58:49 Done.
452 && (!isAllDataReceived() || !frame.frameRect().contains(IntRect(IntP oint(), size())))) {
453 // Background is filled with transparency or with background color ( opaque gifs).
454 // Return false when there is a need for transparency and no space i n colormap for it;
455 // when colormap is opaque and full (256 entries) and in the same ti me:
scroggo_chromium 2016/01/06 21:50:40 nit: at* the same time
aleksandar.stojiljkovic 2016/01/18 13:58:50 Done.
456 // - image is not fully received, so missing part is presented as tr ansparent.
457 // - all frames are not fullscreen, i.e. they are decoded to sub rec tangle of image rectangle.
scroggo_chromium 2016/01/06 21:50:40 This says "all frames", but this only checks a sin
aleksandar.stojiljkovic 2016/01/18 13:58:50 Done. Yes, the check is per frame, not for all - f
458 return false;
459 }
460 return true;
461 }
462
463 // If frame is sharing colormap with required previous frame, it is possible to keep using Index8.
scroggo_chromium 2016/01/06 21:50:40 nit: If frame shares* ...
aleksandar.stojiljkovic 2016/01/18 13:58:50 Done.
464 const GIFFrameContext& previousFrame(*(m_reader->frameContext(requiredPrevio usFrameIndex)));
465 const GIFColorMap& previousColorMap = getColorMap(previousFrame, *m_reader);
466 return ((colorMap.getPosition() == previousColorMap.getPosition()) && (frame .transparentPixel() == previousFrame.transparentPixel()));
467 }
468
469 // Helper method for recomputing if frame decoding has no dependency to previous frames.
scroggo_chromium 2016/01/06 21:50:40 I find this sentence a little awkward. How about:
aleksandar.stojiljkovic 2016/01/18 13:58:50 Done, "to see " removed.
470 void GIFImageDecoder::updateRequiredPreviousFrame(ImageFrame* buffer, const GIFF rameContext& frameContext)
471 {
472 ASSERT(m_colorMode == ImageFrame::Index8);
473 if (buffer->requiredPreviousFrameIndex() == kNotFound)
474 return;
475
476 bool isFullScreen = frameContext.frameRect().contains(IntRect(IntPoint(), si ze()));
477
478 // If transparent pixel is outside color table, there is no transparent pixe l.
479 // e.g. Color table in some of the GIFs is having 40 elements and transparen tPixel set to 0xFF.
scroggo_chromium 2016/01/06 21:50:41 nit: "is having" -> "has"
aleksandar.stojiljkovic 2016/01/18 13:58:50 Done.
480 bool opaque = frameContext.transparentPixel() >= getColorMap(frameContext, * m_reader).getTableSize();
scroggo_chromium 2016/01/06 21:50:40 nit: If isFullScreen is false, we do not need to c
aleksandar.stojiljkovic 2016/01/18 13:58:50 Done.
481
482 // If fullscreen and opaque, the frame is independent of previous frame.
483 if (isFullScreen && opaque)
484 buffer->setRequiredPreviousFrameIndex(kNotFound);
485 }
486
487 // Returns transparent index or background index.
488 unsigned char GIFImageDecoder::getBackgroundIndex(const GIFFrameContext& frameCo ntext) const
489 {
490 const size_t background = frameContext.transparentPixel();
491 const GIFColorMap& colorMap = getColorMap(frameContext, *m_reader);
492 if (background < colorMap.getTableSize())
493 return background;
494
495 if (!frameContext.localColorMap().isDefined() && m_reader->backgroundIndex() < colorMap.getTableSize())
496 return m_reader->backgroundIndex();
497 return 0xFF;
scroggo_chromium 2016/01/06 21:50:40 This still concerns me. Used as an index, 0xFF wil
aleksandar.stojiljkovic 2016/01/18 13:58:50 Done. There is check in isIndex8Applicable is not
498 }
499
500 SkColor GIFImageDecoder::getBackgroundColor(size_t frameIndex) const
501 {
502 const GIFFrameContext* frameContext = m_reader->frameContext(frameIndex);
503 unsigned char backgroundIndex = getBackgroundIndex(*frameContext);
504 if (frameContext->transparentPixel() == backgroundIndex)
505 return 0;
scroggo_chromium 2016/01/06 21:50:40 SK_ColorTRANSPARENT ? Or do we use 0 elsewhere?
aleksandar.stojiljkovic 2016/01/18 13:58:50 Changed here to SK_ColorTRANSPARENT. 0 is/was in
506 const GIFColorMap::Table& table = getColorMap(*frameContext, *m_reader).tabl e();
507 return (backgroundIndex < table.size() ? table[backgroundIndex]: 0);
508 }
509
510 bool GIFImageDecoder::initFrameBuffer(size_t frameIndex)
511 {
512 ImageFrame* buffer = &m_frameBufferCache[frameIndex];
513 if (buffer->status() != ImageFrame::FrameEmpty) {
514 // If it is partial, decode to the same bitmap.
515 setupHaveDecodedRowCallbacks(buffer->getSkBitmap().colorType() == kIndex _8_SkColorType);
scroggo_chromium 2016/01/06 21:50:40 If it is partial, shouldn't we already be using th
aleksandar.stojiljkovic 2016/01/18 13:58:49 e.g. if partial frame is N32, and previous require
516 return true;
517 }
518
519 if (m_colorMode == ImageFrame::N32) {
520 return initFrameBufferN32(frameIndex);
521 }
522 const GIFFrameContext* frameContext = m_reader->frameContext(frameIndex);
523 updateRequiredPreviousFrame(buffer, *frameContext);
524 const size_t requiredPreviousFrameIndex = buffer->requiredPreviousFrameIndex ();
525
526 bool useIndex8 = true;
527 if (requiredPreviousFrameIndex == kNotFound) {
528 if (isIndex8Applicable(*frameContext, requiredPreviousFrameIndex)) {
529 const unsigned char backgroundIndex = getBackgroundIndex(*frameConte xt);
530 if (!buffer->setSizeIndex8(size().width(), size().height(), getColor Map(*frameContext, *m_reader).table(),
531 backgroundIndex, backgroundIndex == frameContext->transparentPix el()))
532 return setFailed();
scroggo_chromium 2016/01/06 21:50:40 I don't see this addressed in the style guide, but
aleksandar.stojiljkovic 2016/01/18 13:58:49 Not sure if I did the right thing, let's see if it
533 } else {
534 return initFrameBufferN32(frameIndex);
535 }
536 } else {
537 const ImageFrame* previousBuffer = &m_frameBufferCache[requiredPreviousF rameIndex];
538 ASSERT(previousBuffer->status() == ImageFrame::FrameComplete);
539
540 // Copy the required completed frame as the starting state for this fram e.
541 useIndex8 = previousBuffer->getSkBitmap().colorType() == kIndex_8_SkColo rType;
542 if (useIndex8) {
543 if (isIndex8Applicable(*frameContext, requiredPreviousFrameIndex)) {
544 if (!buffer->copyBitmapData(*previousBuffer))
545 return setFailed();
546 ASSERT(buffer->getSkBitmap().colorType() == kIndex_8_SkColorType );
547 } else {
548 useIndex8 = false;
549 }
550 }
551 // If needed to decode to N32, frame starts as copy of required frame (I ndex8/N32) to N32.
552 if (!useIndex8 && !initFrameBufferFromPreviousN32(buffer, *previousBuffe r))
553 return setFailed();
554 }
555 setupHaveDecodedRowCallbacks(useIndex8);
556
557 // Update our status to be partially complete.
558 buffer->setStatus(ImageFrame::FramePartial);
559
340 // Reset the alpha pixel tracker for this frame. 560 // Reset the alpha pixel tracker for this frame.
341 m_currentBufferSawAlpha = false; 561 m_currentBufferSawAlpha = false;
342 return true; 562 return true;
343 } 563 }
344 564
565 bool GIFImageDecoder::canDecodeTo(size_t index, ImageFrame::ColorType outputType )
566 {
567 if ((index >= frameCount()) || (m_colorMode == ImageFrame::N32) || failed())
scroggo_chromium 2016/01/06 21:50:40 if outputType == N32, shouldn't we always return t
aleksandar.stojiljkovic 2016/01/18 13:58:50 No, the recent changes made this deterministic ear
568 return (outputType == m_colorMode);
569
570 // Go from one to previous until calculating if Index8 is supported.
571 size_t frameToDecode = index;
572 ImageFrame::ColorType calculatedOutput = ImageFrame::Index8;
573 while (frameToDecode != kNotFound) {
574 ImageFrame* buffer = &m_frameBufferCache[frameToDecode];
575 if (buffer->status() == ImageFrame::FrameComplete) {
576 // In this case, color type from complete frame is kept.
577 calculatedOutput = static_cast<ImageFrame::ColorType>(buffer->getSkB itmap().colorType());
578 break;
579 }
580 const GIFFrameContext* frameContext = m_reader->frameContext(frameToDeco de);
581 updateRequiredPreviousFrame(buffer, *frameContext);
582 if (!isIndex8Applicable(*frameContext, buffer->requiredPreviousFrameInde x())) {
583 calculatedOutput = ImageFrame::N32;
584 break;
585 }
586 frameToDecode = buffer->requiredPreviousFrameIndex();
587 }
588 return (calculatedOutput == outputType);
589 }
590
345 } // namespace blink 591 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698