OLD | NEW |
---|---|
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 10 matching lines...) Expand all Loading... | |
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
24 */ | 24 */ |
25 | 25 |
26 #include "platform/image-decoders/gif/GIFImageDecoder.h" | 26 #include "platform/image-decoders/gif/GIFImageDecoder.h" |
27 | 27 |
28 #include <limits> | 28 #include <limits> |
29 #include "platform/image-decoders/gif/GIFImageReader.h" | 29 #include "platform/image-decoders/gif/GIFImageReader.h" |
30 #include "wtf/NotFound.h" | 30 #include "wtf/NotFound.h" |
31 #include "wtf/PassOwnPtr.h" | |
scroggo_chromium
2016/04/29 19:48:14
Unrelated change?
| |
32 | 31 |
33 namespace blink { | 32 namespace blink { |
34 | 33 |
35 GIFImageDecoder::GIFImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOp tion colorOptions, size_t maxDecodedBytes) | 34 GIFImageDecoder::GIFImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOp tion colorOptions, size_t maxDecodedBytes, ImageFrame::ColorType colorType) |
36 : ImageDecoder(alphaOption, colorOptions, maxDecodedBytes) | 35 : ImageDecoder(alphaOption, colorOptions, maxDecodedBytes) |
37 , m_repetitionCount(cAnimationLoopOnce) | 36 , m_repetitionCount(cAnimationLoopOnce) |
37 , m_requestedColorMode(colorType) | |
38 { | 38 { |
39 } | 39 } |
40 | 40 |
41 GIFImageDecoder::~GIFImageDecoder() | 41 GIFImageDecoder::~GIFImageDecoder() |
42 { | 42 { |
43 } | 43 } |
44 | 44 |
45 void GIFImageDecoder::onSetData(SharedBuffer* data) | 45 void GIFImageDecoder::onSetData(SharedBuffer* data) |
46 { | 46 { |
47 if (m_reader) | 47 if (m_reader) |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
94 m_reader->frameContext(index)->isHeaderDefined()) ? | 94 m_reader->frameContext(index)->isHeaderDefined()) ? |
95 m_reader->frameContext(index)->delayTime() : 0; | 95 m_reader->frameContext(index)->delayTime() : 0; |
96 } | 96 } |
97 | 97 |
98 bool GIFImageDecoder::setFailed() | 98 bool GIFImageDecoder::setFailed() |
99 { | 99 { |
100 m_reader.clear(); | 100 m_reader.clear(); |
101 return ImageDecoder::setFailed(); | 101 return ImageDecoder::setFailed(); |
102 } | 102 } |
103 | 103 |
104 bool GIFImageDecoder::haveDecodedRow(size_t frameIndex, GIFRow::const_iterator r owBegin, size_t width, size_t rowNumber, unsigned repeatCount, bool writeTranspa rentPixels) | 104 static inline const GIFColorMap& getColorMap(const GIFFrameContext& frameContext , const GIFImageReader& reader) |
105 { | 105 { |
106 const GIFFrameContext* frameContext = m_reader->frameContext(frameIndex); | 106 return (frameContext.localColorMap().isDefined() ? frameContext.localColorMa p() : reader.globalColorMap()); |
107 } | |
108 | |
109 void GIFImageDecoder::haveDecodedRow(const GIFFrameContext& frameContext, GIFRow ::const_iterator rowBegin, size_t rowNumber, unsigned repeatCount, bool writeTra nsparentPixels) | |
110 { | |
107 // The pixel data and coordinates supplied to us are relative to the frame's | 111 // The pixel data and coordinates supplied to us are relative to the frame's |
108 // origin within the entire image size, i.e. | 112 // origin within the entire image size, i.e. |
109 // (frameContext->xOffset, frameContext->yOffset). There is no guarantee | 113 // (frameContext->xOffset, frameContext->yOffset). There is no guarantee |
110 // that width == (size().width() - frameContext->xOffset), so | 114 // that width == (size().width() - frameContext->xOffset), so |
111 // we must ensure we don't run off the end of either the source data or the | 115 // we must ensure we don't run off the end of either the source data or the |
112 // row's X-coordinates. | 116 // row's X-coordinates. |
113 const int xBegin = frameContext->xOffset(); | 117 const size_t width = frameContext.width(); |
114 const int yBegin = frameContext->yOffset() + rowNumber; | 118 const int xBegin = frameContext.xOffset(); |
115 const int xEnd = std::min(static_cast<int>(frameContext->xOffset() + width), size().width()); | 119 const int yBegin = frameContext.yOffset() + rowNumber; |
116 const int yEnd = std::min(static_cast<int>(frameContext->yOffset() + rowNumb er + repeatCount), size().height()); | 120 const int xEnd = std::min(static_cast<int>(xBegin + width), size().width()); |
121 const int yEnd = std::min(static_cast<int>(yBegin + repeatCount), size().hei ght()); | |
117 if (!width || (xBegin < 0) || (yBegin < 0) || (xEnd <= xBegin) || (yEnd <= y Begin)) | 122 if (!width || (xBegin < 0) || (yBegin < 0) || (xEnd <= xBegin) || (yEnd <= y Begin)) |
118 return true; | 123 return; |
119 | 124 |
120 const GIFColorMap::Table& colorTable = frameContext->localColorMap().isDefin ed() ? frameContext->localColorMap().table() : m_reader->globalColorMap().table( ); | 125 const GIFColorTable& colorTable = getColorMap(frameContext, *m_reader).table (); |
121 | 126 |
122 if (colorTable.isEmpty()) | 127 if (colorTable.isEmpty()) |
123 return true; | 128 return; |
124 | 129 |
125 GIFColorMap::Table::const_iterator colorTableIter = colorTable.begin(); | 130 ImageFrame& buffer = m_frameBufferCache[frameContext.frameId()]; |
scroggo_chromium
2016/04/29 19:48:15
As I understand it, we will now have always initia
| |
131 GIFRow::const_iterator rowEnd = rowBegin + (xEnd - xBegin); | |
126 | 132 |
127 // Initialize the frame if necessary. | 133 (this->*m_writeRowFunction)(frameContext, buffer, colorTable, rowBegin, rowE nd, xBegin, yBegin, writeTransparentPixels); |
128 ImageFrame& buffer = m_frameBufferCache[frameIndex]; | |
129 if ((buffer.status() == ImageFrame::FrameEmpty) && !initFrameBuffer(frameInd ex)) | |
130 return false; | |
131 | 134 |
132 const size_t transparentPixel = frameContext->transparentPixel(); | 135 // Tell the frame to copy the row data if need be. |
133 GIFRow::const_iterator rowEnd = rowBegin + (xEnd - xBegin); | 136 if (repeatCount > 1) |
137 (*m_copyRowNTimesFunction)(buffer, xBegin, xEnd, yBegin, yEnd); | |
scroggo_chromium
2016/04/29 19:48:14
I found it weird that this method does not take N
| |
138 | |
139 buffer.setPixelsChanged(true); | |
140 } | |
141 | |
142 | |
143 void GIFImageDecoder::writeRowN32(const GIFFrameContext& frameContext, ImageFram e& buffer, const GIFColorTable& colorTable, GIFRow::const_iterator rowBegin, GIF Row::const_iterator rowEnd, int xBegin, int yBegin, bool writeTransparentPixels) | |
144 { | |
145 ASSERT(m_frameBufferCache[frameContext.frameId()].bitmap().colorType() == kN 32_SkColorType); | |
146 | |
147 GIFColorTable::const_iterator colorTableIter = colorTable.begin(); | |
148 const size_t transparentPixel = frameContext.transparentPixel(); | |
134 ImageFrame::PixelData* currentAddress = buffer.getAddr(xBegin, yBegin); | 149 ImageFrame::PixelData* currentAddress = buffer.getAddr(xBegin, yBegin); |
135 | 150 |
136 // We may or may not need to write transparent pixels to the buffer. | 151 // We may or may not need to write transparent pixels to the buffer. |
137 // If we're compositing against a previous image, it's wrong, and if | 152 // If we're compositing against a previous image, it's wrong, and if |
138 // we're writing atop a cleared, fully transparent buffer, it's | 153 // we're writing atop a cleared, fully transparent buffer, it's |
139 // unnecessary; but if we're decoding an interlaced gif and | 154 // unnecessary; but if we're decoding an interlaced gif and |
140 // displaying it "Haeberli"-style, we must write these for passes | 155 // displaying it "Haeberli"-style, we must write these for passes |
141 // beyond the first, or the initial passes will "show through" the | 156 // beyond the first, or the initial passes will "show through" the |
142 // later ones. | 157 // later ones. |
143 // | 158 // |
(...skipping 13 matching lines...) Expand all Loading... | |
157 } | 172 } |
158 } else { | 173 } else { |
159 for (; rowBegin != rowEnd; ++rowBegin, ++currentAddress) { | 174 for (; rowBegin != rowEnd; ++rowBegin, ++currentAddress) { |
160 const size_t sourceValue = *rowBegin; | 175 const size_t sourceValue = *rowBegin; |
161 if ((sourceValue != transparentPixel) && (sourceValue < colorTable.s ize())) | 176 if ((sourceValue != transparentPixel) && (sourceValue < colorTable.s ize())) |
162 *currentAddress = colorTableIter[sourceValue]; | 177 *currentAddress = colorTableIter[sourceValue]; |
163 else | 178 else |
164 m_currentBufferSawAlpha = true; | 179 m_currentBufferSawAlpha = true; |
165 } | 180 } |
166 } | 181 } |
182 } | |
167 | 183 |
168 // Tell the frame to copy the row data if need be. | 184 void GIFImageDecoder::writeRowIndex8(const GIFFrameContext& frameContext, ImageF rame& buffer, const GIFColorTable& colorTable, GIFRow::const_iterator rowBegin, GIFRow::const_iterator rowEnd, int xBegin, int yBegin, bool writeTransparentPixe ls) |
169 if (repeatCount > 1) | 185 { |
170 buffer.copyRowNTimes(xBegin, xEnd, yBegin, yEnd); | 186 ASSERT(m_frameBufferCache[frameContext.frameId()].bitmap().colorType() == kI ndex_8_SkColorType); |
171 | 187 |
172 buffer.setPixelsChanged(true); | 188 const size_t transparentPixel = frameContext.transparentPixel(); |
173 return true; | 189 ImageFrame::PixelData8* currentAddress = buffer.getAddr8(xBegin, yBegin); |
190 | |
191 bool opaque = true; | |
192 // writeTransparentPixels is writing without check, but calculates if there | |
scroggo_chromium
2016/04/29 19:48:15
What does this mean? It sure looks like if (writeT
| |
193 // is transparency (m_currentBufferSawAlpha). If transparency was found in | |
194 // previous rows, no need to calculate here. | |
scroggo_chromium
2016/04/29 19:48:15
Would writeRowN32 benefit from this optimization?
| |
195 writeTransparentPixels = writeTransparentPixels || buffer.requiredPreviousFr ameIndex() == kNotFound; | |
196 if (transparentPixel < colorTable.size() && !(writeTransparentPixels && m_cu rrentBufferSawAlpha)) { | |
197 if (writeTransparentPixels) { | |
198 while (rowBegin != rowEnd) { | |
199 opaque = opaque && (*rowBegin ^ transparentPixel); | |
200 *currentAddress++ = *rowBegin++; | |
201 } | |
202 } else { | |
203 for (; rowBegin != rowEnd; ++rowBegin, ++currentAddress) { | |
204 size_t index = *rowBegin; | |
205 if (index == transparentPixel) { | |
scroggo_chromium
2016/04/29 19:48:15
I personally prefer braces here, but the style gui
| |
206 opaque = false; | |
207 } else { | |
208 *currentAddress = index; | |
209 } | |
210 } | |
211 } | |
212 } else { | |
213 // No transparency to deal with. | |
scroggo_chromium
2016/04/29 19:48:15
I think this comment is misleading. We're not keep
| |
214 if (rowEnd - rowBegin == size().width()) { | |
215 size_t* destination = (size_t*)currentAddress; | |
216 const size_t* source = (const size_t*)rowBegin; | |
217 const size_t count = (rowEnd - rowBegin + sizeof(size_t) - 1) / size of(size_t); | |
218 const size_t* sourceEnd = source + count; | |
219 while (source != sourceEnd) | |
scroggo_chromium
2016/04/29 19:48:14
Why is this not a memcpy?
| |
220 *destination++ = *source++; | |
221 } else { | |
222 while (rowBegin != rowEnd) | |
scroggo_chromium
2016/04/29 19:48:15
Can this be a memcpy?
| |
223 *currentAddress++ = *rowBegin++; | |
224 } | |
225 } | |
226 | |
227 if (!opaque) | |
228 m_currentBufferSawAlpha = true; | |
229 } | |
230 | |
231 // Copies the pixel data at [(xBegin, yBegin), (xEnd, yEnd)) to the | |
scroggo_chromium
2016/04/29 19:48:15
Don't these actually copy the data from [(xBegin,
| |
232 // same X-coordinates on each subsequent row up to but not including | |
233 // yEnd. | |
234 static void copyRowNTimesFunctionN32(ImageFrame& buffer, int xBegin, int xEnd, i nt yBegin, int yEnd) | |
235 { | |
236 const int rowBytes = (xEnd - xBegin) * sizeof(ImageFrame::PixelData); | |
scroggo_chromium
2016/04/29 19:48:15
I'm not sure "rowBytes" is the correct name here.
| |
237 const ImageFrame::PixelData* const startAddr = buffer.getAddr(xBegin, yBegin ); | |
238 for (int destY = yBegin + 1; destY < yEnd; ++destY) | |
239 memcpy(buffer.getAddr(xBegin, destY), startAddr, rowBytes); | |
240 } | |
241 | |
242 static void copyRowNTimesFunctionIndex8(ImageFrame& buffer, int xBegin, int xEnd , int yBegin, int yEnd) | |
243 { | |
244 const int rowBytes = (xEnd - xBegin) * sizeof(ImageFrame::PixelData8); | |
245 const ImageFrame::PixelData8* const startAddr = buffer.getAddr8(xBegin, yBeg in); | |
246 for (int destY = yBegin + 1; destY < yEnd; ++destY) | |
247 memcpy(buffer.getAddr8(xBegin, destY), startAddr, rowBytes); | |
174 } | 248 } |
175 | 249 |
176 bool GIFImageDecoder::parseCompleted() const | 250 bool GIFImageDecoder::parseCompleted() const |
177 { | 251 { |
178 return m_reader && m_reader->parseCompleted(); | 252 return m_reader && m_reader->parseCompleted(); |
179 } | 253 } |
180 | 254 |
181 bool GIFImageDecoder::frameComplete(size_t frameIndex) | 255 bool GIFImageDecoder::frameComplete(size_t frameIndex) |
182 { | 256 { |
183 // Initialize the frame if necessary. Some GIFs insert do-nothing frames, | 257 // Initialize the frame if necessary. Some GIFs insert do-nothing frames, |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
285 if (m_frameBufferCache[*i].status() != ImageFrame::FrameComplete) | 359 if (m_frameBufferCache[*i].status() != ImageFrame::FrameComplete) |
286 break; | 360 break; |
287 } | 361 } |
288 | 362 |
289 // It is also a fatal error if all data is received and we have decoded all | 363 // It is also a fatal error if all data is received and we have decoded all |
290 // frames available but the file is truncated. | 364 // frames available but the file is truncated. |
291 if (index >= m_frameBufferCache.size() - 1 && isAllDataReceived() && m_reade r && !m_reader->parseCompleted()) | 365 if (index >= m_frameBufferCache.size() - 1 && isAllDataReceived() && m_reade r && !m_reader->parseCompleted()) |
292 setFailed(); | 366 setFailed(); |
293 } | 367 } |
294 | 368 |
369 void GIFImageDecoder::setupHaveDecodedRowCallbacks(bool isIndex8) | |
370 { | |
371 if (isIndex8) { | |
372 m_writeRowFunction = &GIFImageDecoder::writeRowIndex8; | |
373 m_copyRowNTimesFunction = ©RowNTimesFunctionIndex8; | |
374 } else { | |
375 m_writeRowFunction = &GIFImageDecoder::writeRowN32; | |
376 m_copyRowNTimesFunction = ©RowNTimesFunctionN32; | |
377 } | |
378 } | |
379 | |
295 void GIFImageDecoder::parse(GIFParseQuery query) | 380 void GIFImageDecoder::parse(GIFParseQuery query) |
296 { | 381 { |
297 if (failed()) | 382 if (failed()) |
298 return; | 383 return; |
299 | 384 |
300 if (!m_reader) { | 385 if (!m_reader) { |
301 m_reader = adoptPtr(new GIFImageReader(this)); | 386 m_reader = adoptPtr(new GIFImageReader(this)); |
302 m_reader->setData(m_data); | 387 m_reader->setData(m_data); |
303 } | 388 } |
304 | 389 |
305 if (!m_reader->parse(query)) | 390 if (!m_reader->parse(query)) |
306 setFailed(); | 391 setFailed(); |
307 } | 392 } |
308 | 393 |
394 bool GIFImageDecoder::initFrameBufferFromPrevious(ImageFrame* buffer, const Imag eFrame& previousBuffer, ImageFrame::ColorType targetType, size_t transparentInde x) | |
395 { | |
396 // Preserve the last frame as the starting state for this frame. | |
397 if (!buffer->copyBitmapData(previousBuffer, targetType)) | |
398 return false; | |
399 | |
400 if (previousBuffer.disposalMethod() == ImageFrame::DisposeOverwriteBgcolor) { | |
401 // We want to clear the previous frame to transparent, without | |
402 // affecting pixels in the image outside of the frame. | |
403 const IntRect& prevRect = previousBuffer.originalFrameRect(); | |
404 ASSERT(!prevRect.contains(IntRect(IntPoint(), size()))); | |
405 if (targetType == ImageFrame::Index8) | |
406 buffer->zeroFillFrameRectIndex8(prevRect, (transparentIndex > 255) ? 255 : transparentIndex); | |
scroggo_chromium
2016/04/29 19:48:14
I think I've brought this up before - this looks l
| |
407 else | |
408 buffer->zeroFillFrameRect(prevRect); | |
409 } | |
410 return true; | |
411 } | |
412 | |
413 bool GIFImageDecoder::initFrameBufferN32(size_t frameIndex) | |
414 { | |
415 setupHaveDecodedRowCallbacks(false); | |
416 | |
417 // Initialize the frame rect in our buffer. | |
418 ImageFrame* buffer = &m_frameBufferCache[frameIndex]; | |
419 | |
420 size_t requiredPreviousFrameIndex = buffer->requiredPreviousFrameIndex(); | |
421 if (requiredPreviousFrameIndex == kNotFound) { | |
422 // This frame doesn't rely on any previous data. | |
scroggo_chromium
2016/04/29 19:48:15
If it does not rely on previous data, can we not s
| |
423 if (!buffer->setSize(size().width(), size().height(), getBackgroundColor (frameIndex))) | |
424 return setFailed(); | |
425 } else { | |
426 const ImageFrame* prevBuffer = &m_frameBufferCache[requiredPreviousFrame Index]; | |
427 ASSERT(prevBuffer->status() == ImageFrame::FrameComplete); | |
428 if (!initFrameBufferFromPrevious(buffer, *prevBuffer, ImageFrame::N32)) | |
429 return setFailed(); | |
430 } | |
431 | |
432 // Update our status to be partially complete. | |
433 buffer->setStatus(ImageFrame::FramePartial); | |
434 | |
435 // Reset the alpha pixel tracker for this frame. | |
436 m_currentBufferSawAlpha = false; | |
437 return true; | |
438 } | |
439 | |
440 bool GIFImageDecoder::isIndex8Applicable(const GIFFrameContext& frame, size_t re quiredPreviousFrameIndex) const | |
441 { | |
442 const GIFColorMap& colorMap = getColorMap(frame, *m_reader); | |
443 const bool useGlobalColorMap = !frame.localColorMap().isDefined(); | |
444 const bool transparencySupported = colorMap.getTableSize() < 256 || frame.tr ansparentPixel() >= colorMap.getTableSize(); | |
445 | |
446 if (requiredPreviousFrameIndex == kNotFound) { | |
447 if (!transparencySupported | |
scroggo_chromium
2016/04/29 19:48:14
Why not switch this from:
if (condition) {
retu
| |
448 && useGlobalColorMap && m_reader->backgroundIndex() >= colorMap.getT ableSize() | |
449 && (!isAllDataReceived() || !frame.frameRect().contains(IntRect(IntP oint(), size())))) { | |
450 // Background is filled with transparency or with background color - | |
451 // background color is used for opaque gifs. | |
452 // Return false when there is a need for transparency and no space | |
453 // in colormap for it; when colormap is opaque and full (it has 256 | |
454 // entries) and at the same time | |
455 // - image is not fully received, so missing part would need to be | |
456 // presented as transparent, or | |
457 // - the frame is not fullscreen, i.e. it would be decoded to a sub | |
458 // rectangle of the image rectangle. | |
459 return false; | |
460 } | |
461 return true; | |
462 } | |
463 | |
464 const GIFFrameContext& previousFrame(*(m_reader->frameContext(requiredPrevio usFrameIndex))); | |
465 | |
466 // Skip Index8 for DisposeOverwriteBgcolor disposal method if there is no tr ansparency. | |
467 if (previousFrame.disposalMethod() == ImageFrame::DisposeOverwriteBgcolor && !transparencySupported) | |
468 return false; | |
469 | |
470 // If frame shares colormap with required previous frame, it is possible to keep using Index8. | |
471 const GIFColorMap& previousColorMap = getColorMap(previousFrame, *m_reader); | |
472 return ((colorMap.getPosition() == previousColorMap.getPosition()) && (frame .transparentPixel() == previousFrame.transparentPixel())); | |
473 } | |
474 | |
475 // Helper method to check if a previous frame dependency can be removed. | |
476 void GIFImageDecoder::updateRequiredPreviousFrame(ImageFrame* buffer, const GIFF rameContext& frameContext) | |
477 { | |
478 ASSERT(m_requestedColorMode == ImageFrame::Index8); | |
479 if (buffer->requiredPreviousFrameIndex() == kNotFound) | |
480 return; | |
481 | |
482 bool isFullScreen = frameContext.frameRect().contains(IntRect(IntPoint(), si ze())); | |
483 | |
484 // If fullscreen and opaque, the frame is independent of previous frame. | |
scroggo_chromium
2016/04/29 19:48:15
This comment seems a little out of place to me. Ho
| |
485 if (!isFullScreen) | |
486 return; | |
487 | |
488 // If transparent pixel is outside color table, there is no transparent | |
489 // pixel, e.g. color table in some of the GIFs has 40 elements and | |
490 // transparentPixel value is 0xFF. | |
491 if (frameContext.transparentPixel() >= getColorMap(frameContext, *m_reader). getTableSize()) | |
492 buffer->setRequiredPreviousFrameIndex(kNotFound); | |
493 } | |
494 | |
495 // Returns transparent index or background index. | |
496 unsigned GIFImageDecoder::getBackgroundIndex(const GIFFrameContext& frameContext ) const | |
497 { | |
498 const size_t background = frameContext.transparentPixel(); | |
499 const GIFColorMap& colorMap = getColorMap(frameContext, *m_reader); | |
500 if (background < colorMap.getTableSize()) | |
501 return background; | |
502 | |
503 if (!frameContext.localColorMap().isDefined() && m_reader->backgroundIndex() < colorMap.getTableSize()) | |
504 return m_reader->backgroundIndex(); | |
505 return -1; | |
scroggo_chromium
2016/04/29 19:48:15
Again, I think it's weird to return -1 as an unsig
| |
506 } | |
507 | |
508 SkColor GIFImageDecoder::getBackgroundColor(size_t frameIndex) const | |
509 { | |
510 const GIFFrameContext* frameContext = m_reader->frameContext(frameIndex); | |
511 unsigned backgroundIndex = getBackgroundIndex(*frameContext); | |
512 if (frameContext->transparentPixel() == backgroundIndex) | |
513 return SK_ColorTRANSPARENT; | |
514 const GIFColorTable& table = getColorMap(*frameContext, *m_reader).table(); | |
515 return (backgroundIndex < table.size() ? table[backgroundIndex]: SK_ColorTRA NSPARENT); | |
scroggo_chromium
2016/04/29 19:48:15
How does this fall back to transparent? It seems l
| |
516 } | |
517 | |
309 bool GIFImageDecoder::initFrameBuffer(size_t frameIndex) | 518 bool GIFImageDecoder::initFrameBuffer(size_t frameIndex) |
310 { | 519 { |
311 // Initialize the frame rect in our buffer. | 520 ImageFrame* buffer = &m_frameBufferCache[frameIndex]; |
312 ImageFrame* const buffer = &m_frameBufferCache[frameIndex]; | 521 if (buffer->status() != ImageFrame::FrameEmpty) { |
522 // If it is partial, decode to the same bitmap. | |
523 setupHaveDecodedRowCallbacks(buffer->getSkBitmap().colorType() == kIndex _8_SkColorType); | |
524 return true; | |
525 } | |
313 | 526 |
314 size_t requiredPreviousFrameIndex = buffer->requiredPreviousFrameIndex(); | 527 if (m_requestedColorMode == ImageFrame::N32) { |
528 return initFrameBufferN32(frameIndex); | |
529 } | |
530 const GIFFrameContext* frameContext = m_reader->frameContext(frameIndex); | |
531 updateRequiredPreviousFrame(buffer, *frameContext); | |
532 const size_t requiredPreviousFrameIndex = buffer->requiredPreviousFrameIndex (); | |
533 | |
534 bool useIndex8 = true; | |
315 if (requiredPreviousFrameIndex == kNotFound) { | 535 if (requiredPreviousFrameIndex == kNotFound) { |
316 // This frame doesn't rely on any previous data. | 536 if (isIndex8Applicable(*frameContext, requiredPreviousFrameIndex)) { |
317 if (!buffer->setSize(size().width(), size().height())) | 537 const unsigned backgroundIndex = getBackgroundIndex(*frameContext); |
318 return setFailed(); | 538 if (!buffer->setSizeIndex8( |
539 size().width(), size().height(), | |
540 getColorMap(*frameContext, *m_reader).table(), backgroundIndex, | |
541 backgroundIndex == frameContext->transparentPixel())) | |
542 return setFailed(); | |
543 } else { | |
544 return initFrameBufferN32(frameIndex); | |
545 } | |
319 } else { | 546 } else { |
320 const ImageFrame* prevBuffer = &m_frameBufferCache[requiredPreviousFrame Index]; | 547 const ImageFrame* prevBuffer = &m_frameBufferCache[requiredPreviousFrame Index]; |
321 ASSERT(prevBuffer->status() == ImageFrame::FrameComplete); | 548 ASSERT(prevBuffer->status() == ImageFrame::FrameComplete); |
322 | 549 |
323 // Preserve the last frame as the starting state for this frame. | 550 // Copy the required completed frame as the starting state for this fram e. |
324 if (!buffer->copyBitmapData(*prevBuffer)) | 551 useIndex8 = (prevBuffer->getSkBitmap().colorType() == kIndex_8_SkColorTy pe) && isIndex8Applicable(*frameContext, requiredPreviousFrameIndex); |
552 if (!initFrameBufferFromPrevious(buffer, *prevBuffer, useIndex8 ? ImageF rame::Index8 : ImageFrame::N32, frameContext->transparentPixel())) | |
325 return setFailed(); | 553 return setFailed(); |
326 | 554 ASSERT(useIndex8 == (buffer->getSkBitmap().colorType() == kIndex_8_SkCol orType)); |
327 if (prevBuffer->disposalMethod() == ImageFrame::DisposeOverwriteBgcolor) { | |
328 // We want to clear the previous frame to transparent, without | |
329 // affecting pixels in the image outside of the frame. | |
330 const IntRect& prevRect = prevBuffer->originalFrameRect(); | |
331 ASSERT(!prevRect.contains(IntRect(IntPoint(), size()))); | |
332 buffer->zeroFillFrameRect(prevRect); | |
333 } | |
334 } | 555 } |
556 setupHaveDecodedRowCallbacks(useIndex8); | |
335 | 557 |
336 // Update our status to be partially complete. | 558 // Update our status to be partially complete. |
337 buffer->setStatus(ImageFrame::FramePartial); | 559 buffer->setStatus(ImageFrame::FramePartial); |
338 | 560 |
339 // Reset the alpha pixel tracker for this frame. | 561 // Reset the alpha pixel tracker for this frame. |
340 m_currentBufferSawAlpha = false; | 562 m_currentBufferSawAlpha = false; |
341 return true; | 563 return true; |
342 } | 564 } |
343 | 565 |
566 bool GIFImageDecoder::canDecodeTo(size_t index, ImageFrame::ColorType outputType ) | |
567 { | |
568 if ((index >= frameCount()) || (m_requestedColorMode == ImageFrame::N32) || failed()) | |
scroggo_chromium
2016/04/29 19:48:15
if (failed()), should this just return false? Othe
| |
569 return (outputType == m_requestedColorMode); | |
570 | |
571 // Go from one to previous to calculate if Index8 is supported. | |
572 size_t frameToDecode = index; | |
573 ImageFrame::ColorType calculatedOutput = ImageFrame::Index8; | |
574 while (frameToDecode != kNotFound) { | |
575 ImageFrame* buffer = &m_frameBufferCache[frameToDecode]; | |
576 if (buffer->status() == ImageFrame::FrameComplete) { | |
577 // In this case, color type from complete frame is kept. | |
578 calculatedOutput = static_cast<ImageFrame::ColorType>(buffer->getSkB itmap().colorType()); | |
579 break; | |
580 } | |
581 const GIFFrameContext* frameContext = m_reader->frameContext(frameToDeco de); | |
582 updateRequiredPreviousFrame(buffer, *frameContext); | |
583 if (!isIndex8Applicable(*frameContext, buffer->requiredPreviousFrameInde x())) { | |
584 calculatedOutput = ImageFrame::N32; | |
585 break; | |
586 } | |
587 frameToDecode = buffer->requiredPreviousFrameIndex(); | |
588 } | |
589 return (calculatedOutput == outputType); | |
590 } | |
591 | |
344 } // namespace blink | 592 } // namespace blink |
OLD | NEW |