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 15 matching lines...) Expand all Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |