Chromium Code Reviews| 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 |