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 17 matching lines...) Expand all Loading... | |
| 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) |
| 37 : ImageDecoder(alphaOption, colorOptions, maxDecodedBytes) | 37 : ImageDecoder(alphaOption, colorOptions, maxDecodedBytes) |
| 38 , phaveDecodedRow(&GIFImageDecoder::haveDecodedRowIndex8) | |
| 38 , m_repetitionCount(cAnimationLoopOnce) | 39 , m_repetitionCount(cAnimationLoopOnce) |
| 40 , m_colorMode(ImageFrame::Index8) | |
| 41 , m_forceN32Decoding(false) | |
| 39 { | 42 { |
| 40 } | 43 } |
| 41 | 44 |
| 42 GIFImageDecoder::~GIFImageDecoder() | 45 GIFImageDecoder::~GIFImageDecoder() |
| 43 { | 46 { |
| 44 } | 47 } |
| 45 | 48 |
| 46 void GIFImageDecoder::onSetData(SharedBuffer* data) | 49 void GIFImageDecoder::onSetData(SharedBuffer* data) |
| 47 { | 50 { |
| 48 if (m_reader) | 51 if (m_reader) |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 95 m_reader->frameContext(index)->isHeaderDefined()) ? | 98 m_reader->frameContext(index)->isHeaderDefined()) ? |
| 96 m_reader->frameContext(index)->delayTime() : 0; | 99 m_reader->frameContext(index)->delayTime() : 0; |
| 97 } | 100 } |
| 98 | 101 |
| 99 bool GIFImageDecoder::setFailed() | 102 bool GIFImageDecoder::setFailed() |
| 100 { | 103 { |
| 101 m_reader.clear(); | 104 m_reader.clear(); |
| 102 return ImageDecoder::setFailed(); | 105 return ImageDecoder::setFailed(); |
| 103 } | 106 } |
| 104 | 107 |
| 105 bool GIFImageDecoder::haveDecodedRow(size_t frameIndex, GIFRow::const_iterator r owBegin, size_t width, size_t rowNumber, unsigned repeatCount, bool writeTranspa rentPixels) | 108 bool GIFImageDecoder::haveDecodedRowN32(size_t frameIndex, GIFRow::const_iterato r rowBegin, size_t width, size_t rowNumber, unsigned repeatCount, bool writeTran sparentPixels) |
| 106 { | 109 { |
| 110 ASSERT(m_colorMode == ImageFrame::N32 || m_forceN32Decoding); | |
|
scroggo_chromium
2015/12/03 21:47:21
It's weird to me that m_colorMode might *not* be N
aleksandar.stojiljkovic
2015/12/04 00:07:35
Yes. m_colorMode holds information about what was
aleksandar.stojiljkovic
2015/12/07 19:24:22
Done.
| |
| 107 const GIFFrameContext* frameContext = m_reader->frameContext(frameIndex); | 111 const GIFFrameContext* frameContext = m_reader->frameContext(frameIndex); |
| 108 // The pixel data and coordinates supplied to us are relative to the frame's | 112 // The pixel data and coordinates supplied to us are relative to the frame's |
| 109 // origin within the entire image size, i.e. | 113 // origin within the entire image size, i.e. |
| 110 // (frameContext->xOffset, frameContext->yOffset). There is no guarantee | 114 // (frameContext->xOffset, frameContext->yOffset). There is no guarantee |
| 111 // that width == (size().width() - frameContext->xOffset), so | 115 // 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 | 116 // we must ensure we don't run off the end of either the source data or the |
| 113 // row's X-coordinates. | 117 // row's X-coordinates. |
| 114 const int xBegin = frameContext->xOffset(); | 118 const int xBegin = frameContext->xOffset(); |
| 115 const int yBegin = frameContext->yOffset() + rowNumber; | 119 const int yBegin = frameContext->yOffset() + rowNumber; |
| 116 const int xEnd = std::min(static_cast<int>(frameContext->xOffset() + width), size().width()); | 120 const int xEnd = std::min(static_cast<int>(frameContext->xOffset() + width), size().width()); |
| 117 const int yEnd = std::min(static_cast<int>(frameContext->yOffset() + rowNumb er + repeatCount), size().height()); | 121 const int yEnd = std::min(static_cast<int>(frameContext->yOffset() + rowNumb er + repeatCount), size().height()); |
| 118 if (!width || (xBegin < 0) || (yBegin < 0) || (xEnd <= xBegin) || (yEnd <= y Begin)) | 122 if (!width || (xBegin < 0) || (yBegin < 0) || (xEnd <= xBegin) || (yEnd <= y Begin)) |
| 119 return true; | 123 return true; |
| 120 | 124 |
| 121 const GIFColorMap::Table& colorTable = frameContext->localColorMap().isDefin ed() ? frameContext->localColorMap().table() : m_reader->globalColorMap().table( ); | 125 const GIFColorMap::Table& colorTable = frameContext->localColorMap().isDefin ed() ? frameContext->localColorMap().table() : m_reader->globalColorMap().table( ); |
| 122 | 126 |
| 123 if (colorTable.isEmpty()) | 127 if (colorTable.isEmpty()) |
| 124 return true; | 128 return true; |
| 125 | 129 |
| 126 GIFColorMap::Table::const_iterator colorTableIter = colorTable.begin(); | 130 GIFColorMap::Table::const_iterator colorTableIter = colorTable.begin(); |
| 127 | 131 |
| 128 // Initialize the frame if necessary. | 132 // Initialize the frame if necessary. |
| 129 ImageFrame& buffer = m_frameBufferCache[frameIndex]; | 133 ImageFrame& buffer = m_frameBufferCache[frameIndex]; |
| 130 if ((buffer.status() == ImageFrame::FrameEmpty) && !initFrameBuffer(frameInd ex)) | 134 if (buffer.status() == ImageFrame::FrameEmpty) { |
| 131 return false; | 135 if (!initFrameBuffer(frameIndex)) |
| 136 return false; | |
| 137 // Tricky part here - decoding first row inits frame, and recognized tha t the frame doesnt require | |
|
scroggo_chromium
2015/12/03 21:47:22
This is tricky. Maybe it would be better for initF
aleksandar.stojiljkovic
2015/12/04 01:27:27
I actually plan to move init out of looping throug
aleksandar.stojiljkovic
2015/12/07 19:24:22
done (removed), init happens before outputting row
scroggo_chromium
2015/12/08 22:24:51
Yeah, I think that has to be a judgment call. Chan
| |
| 138 // forced N32 decoding (. initFrameBuffer sets proper haveDecodedRow cal lback, but for this | |
| 139 // first row, we need manually to redirect. | |
| 140 // It would be much cleaner to move initFrameBuffer before calling haveD ecodedRow, but the | |
| 141 // intention here is not to change original code path. | |
| 142 if (m_colorMode == ImageFrame::Index8 && !m_forceN32Decoding) | |
|
scroggo_chromium
2015/12/03 21:47:22
IIUC, if this is true, initFrameBuffer called init
aleksandar.stojiljkovic
2015/12/04 01:27:27
yes, for every row check if initialized then initi
aleksandar.stojiljkovic
2015/12/07 19:24:21
Done.
| |
| 143 return haveDecodedRowIndex8(frameIndex, rowBegin, width, rowNumber, repeatCount, writeTransparentPixels); | |
| 144 } | |
| 132 | 145 |
| 133 const size_t transparentPixel = frameContext->transparentPixel(); | 146 const size_t transparentPixel = frameContext->transparentPixel(); |
| 134 GIFRow::const_iterator rowEnd = rowBegin + (xEnd - xBegin); | 147 GIFRow::const_iterator rowEnd = rowBegin + (xEnd - xBegin); |
| 135 ImageFrame::PixelData* currentAddress = buffer.getAddr(xBegin, yBegin); | 148 ImageFrame::PixelData* currentAddress = buffer.getAddr(xBegin, yBegin); |
| 136 | 149 |
| 137 // We may or may not need to write transparent pixels to the buffer. | 150 // 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 | 151 // 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 | 152 // we're writing atop a cleared, fully transparent buffer, it's |
| 140 // unnecessary; but if we're decoding an interlaced gif and | 153 // unnecessary; but if we're decoding an interlaced gif and |
| 141 // displaying it "Haeberli"-style, we must write these for passes | 154 // displaying it "Haeberli"-style, we must write these for passes |
| 142 // beyond the first, or the initial passes will "show through" the | 155 // beyond the first, or the initial passes will "show through" the |
| 143 // later ones. | 156 // later ones. |
| 144 // | 157 // |
| 145 // The loops below are almost identical. One writes a transparent pixel | 158 // The loops below are almost identical. One writes a transparent pixel |
| 146 // and one doesn't based on the value of |writeTransparentPixels|. | 159 // and one doesn't based on the value of |writeTransparentPixels|. |
| 147 // The condition check is taken out of the loop to enhance performance. | 160 // The condition check is taken out of the loop to enhance performance. |
| 148 // This optimization reduces decoding time by about 15% for a 3MB image. | 161 // This optimization reduces decoding time by about 15% for a 3MB image. |
| 149 if (writeTransparentPixels) { | 162 if (writeTransparentPixels) { |
| 150 for (; rowBegin != rowEnd; ++rowBegin, ++currentAddress) { | 163 for (; rowBegin != rowEnd; ++rowBegin, ++currentAddress) { |
| 151 const size_t sourceValue = *rowBegin; | 164 const size_t sourceValue = *rowBegin; |
| 165 // FIXME - potential optimization: extend colorTable to 256 elems, w rite 0 | |
|
scroggo_chromium
2015/12/03 21:47:21
So this optimization is when the colortable is les
aleksandar.stojiljkovic
2015/12/04 01:27:27
no; i meant that for colortables of e.g. 32 elemen
aleksandar.stojiljkovic
2015/12/07 19:24:22
Done.
| |
| 166 // in colortable on index transparentPixel so there would be no need to to this | |
| 167 // branching bellow. See index8 implementation. | |
| 168 // FIXME optimization: do separate branching for when there is no tr ansparentPixel | |
| 169 // (value outside colorTable.size()) | |
| 152 if ((sourceValue != transparentPixel) && (sourceValue < colorTable.s ize())) { | 170 if ((sourceValue != transparentPixel) && (sourceValue < colorTable.s ize())) { |
| 153 *currentAddress = colorTableIter[sourceValue]; | 171 *currentAddress = colorTableIter[sourceValue]; |
| 154 } else { | 172 } else { |
| 155 *currentAddress = 0; | 173 *currentAddress = 0; |
| 156 m_currentBufferSawAlpha = true; | 174 m_currentBufferSawAlpha = true; |
| 157 } | 175 } |
| 158 } | 176 } |
| 159 } else { | 177 } else { |
| 160 for (; rowBegin != rowEnd; ++rowBegin, ++currentAddress) { | 178 for (; rowBegin != rowEnd; ++rowBegin, ++currentAddress) { |
| 161 const size_t sourceValue = *rowBegin; | 179 const size_t sourceValue = *rowBegin; |
| 162 if ((sourceValue != transparentPixel) && (sourceValue < colorTable.s ize())) | 180 if ((sourceValue != transparentPixel) && (sourceValue < colorTable.s ize())) |
| 163 *currentAddress = colorTableIter[sourceValue]; | 181 *currentAddress = colorTableIter[sourceValue]; |
| 164 else | 182 else |
| 165 m_currentBufferSawAlpha = true; | 183 m_currentBufferSawAlpha = true; |
| 166 } | 184 } |
| 167 } | 185 } |
| 168 | 186 |
| 169 // Tell the frame to copy the row data if need be. | 187 // Tell the frame to copy the row data if need be. |
| 170 if (repeatCount > 1) | 188 if (repeatCount > 1) |
| 171 buffer.copyRowNTimes(xBegin, xEnd, yBegin, yEnd); | 189 buffer.copyRowNTimes(xBegin, xEnd, yBegin, yEnd); |
| 172 | 190 |
| 173 buffer.setPixelsChanged(true); | 191 buffer.setPixelsChanged(true); |
| 174 return true; | 192 return true; |
| 175 } | 193 } |
| 176 | 194 |
| 195 bool GIFImageDecoder::haveDecodedRowIndex8(size_t frameIndex, GIFRow::const_iter ator rowBegin, size_t width, size_t rowNumber, unsigned repeatCount, bool writeT ransparentPixels) | |
|
scroggo_chromium
2015/12/03 21:47:21
It looks like this is largely the same as haveDeco
aleksandar.stojiljkovic
2015/12/04 01:27:27
Agree, duplication for purpose that reviewer easie
| |
| 196 { | |
| 197 ASSERT(m_colorMode == ImageFrame::Index8 && !m_forceN32Decoding); | |
| 198 const GIFFrameContext* frameContext = m_reader->frameContext(frameIndex); | |
| 199 // The pixel data and coordinates supplied to us are relative to the frame's | |
| 200 // origin within the entire image size, i.e. | |
| 201 // (frameContext->xOffset, frameContext->yOffset). There is no guarantee | |
| 202 // that width == (size().width() - frameContext->xOffset), so | |
| 203 // we must ensure we don't run off the end of either the source data or the | |
| 204 // row's X-coordinates. | |
| 205 const int xBegin = frameContext->xOffset(); | |
| 206 const int yBegin = frameContext->yOffset() + rowNumber; | |
| 207 const int xEnd = std::min(static_cast<int>(frameContext->xOffset() + width), size().width()); | |
| 208 const int yEnd = std::min(static_cast<int>(frameContext->yOffset() + rowNumb er + repeatCount), size().height()); | |
| 209 if (!width || (xBegin < 0) || (yBegin < 0) || (xEnd <= xBegin) || (yEnd <= y Begin)) | |
| 210 return true; | |
| 211 | |
| 212 const GIFColorMap::Table& colorTable = frameContext->localColorMap().isDefin ed() ? frameContext->localColorMap().table() : m_reader->globalColorMap().table( ); | |
| 213 | |
| 214 if (colorTable.isEmpty()) | |
| 215 return true; | |
| 216 | |
| 217 | |
| 218 // Initialize the frame if necessary. | |
| 219 ImageFrame& buffer = m_frameBufferCache[frameIndex]; | |
| 220 if (buffer.status() == ImageFrame::FrameEmpty) { | |
| 221 if (!initFrameBufferIndex8(frameIndex)) | |
| 222 return false; | |
| 223 // Tricky part here - decoding first row inits frame, and recognised tha t the frame requires | |
| 224 // N32 decoding. initFrameBuffer sets proper haveDecodedRow callback, bu t for this | |
| 225 // first row, we need manually to redirect. | |
| 226 // It would be much cleaner to move initFrameBuffer before calling haveD ecodedRow, but the | |
| 227 // intention here is not to change original code path. | |
| 228 if (m_forceN32Decoding) | |
| 229 return haveDecodedRowN32(frameIndex, rowBegin, width, rowNumber, rep eatCount, writeTransparentPixels); | |
| 230 } | |
| 231 | |
| 232 const size_t transparentPixel = frameContext->transparentPixel(); | |
| 233 GIFRow::const_iterator rowEnd = rowBegin + (xEnd - xBegin); | |
| 234 ImageFrame::PixelData8* currentAddress = buffer.getAddr8(xBegin, yBegin); | |
| 235 | |
| 236 bool opaque = true; | |
| 237 if (transparentPixel < colorTable.size()) { | |
| 238 // writeTransparentPixels is writing without check, suitable for paralle l uint32_t write later. | |
| 239 writeTransparentPixels = writeTransparentPixels || buffer.requiredPrevio usFrameIndex() == kNotFound; | |
| 240 if (writeTransparentPixels) { | |
| 241 for (; rowBegin != rowEnd;) { | |
| 242 opaque = opaque && (*rowBegin ^ transparentPixel); | |
| 243 *currentAddress++ = *rowBegin++; | |
| 244 } | |
| 245 } else { | |
| 246 for (; rowBegin != rowEnd; ++rowBegin, ++currentAddress) { | |
| 247 size_t index = *rowBegin; | |
| 248 if (index == transparentPixel) { | |
| 249 opaque = false; | |
|
scroggo_chromium
2015/12/03 21:47:22
Why do we not want to put index into currentAddres
aleksandar.stojiljkovic
2015/12/04 01:27:26
It is faster this way (at least for examples I use
| |
| 250 } else { | |
| 251 *currentAddress = index; | |
| 252 } | |
| 253 } | |
| 254 } | |
| 255 } else { | |
| 256 // No transparency to deal with. | |
| 257 for (; rowBegin != rowEnd;) | |
| 258 *currentAddress++ = *rowBegin++; | |
| 259 } | |
| 260 | |
| 261 m_currentBufferSawAlpha = !opaque; | |
| 262 | |
| 263 // Tell the frame to copy the row data if specified. | |
| 264 if (repeatCount > 1) { | |
| 265 const int rowBytes = (xEnd - xBegin) * sizeof(uint8_t); | |
| 266 const ImageFrame::PixelData8* const startAddr = buffer.getAddr8(xBegin, yBegin); | |
| 267 for (int destY = yBegin + 1; destY < yEnd; ++destY) | |
| 268 memcpy(buffer.getAddr8(xBegin, destY), startAddr, rowBytes); | |
| 269 } | |
| 270 | |
| 271 buffer.setPixelsChanged(true); | |
| 272 return true; | |
| 273 } | |
| 274 | |
| 177 bool GIFImageDecoder::parseCompleted() const | 275 bool GIFImageDecoder::parseCompleted() const |
| 178 { | 276 { |
| 179 return m_reader && m_reader->parseCompleted(); | 277 return m_reader && m_reader->parseCompleted(); |
| 180 } | 278 } |
| 181 | 279 |
| 182 bool GIFImageDecoder::frameComplete(size_t frameIndex) | 280 bool GIFImageDecoder::frameComplete(size_t frameIndex) |
| 183 { | 281 { |
| 184 // Initialize the frame if necessary. Some GIFs insert do-nothing frames, | 282 // Initialize the frame if necessary. Some GIFs insert do-nothing frames, |
| 185 // in which case we never reach haveDecodedRow() before getting here. | 283 // in which case we never reach haveDecodedRow() before getting here. |
| 284 // FIXME do nothing frames for Index8 are probably copy of previous frame. | |
|
scroggo_chromium
2015/12/03 21:47:21
What does this mean, and what is the FIXME? (i.e.
aleksandar.stojiljkovic
2015/12/04 01:27:27
All fine now, to be removed.
aleksandar.stojiljkovic
2015/12/07 19:24:22
Done.
| |
| 186 ImageFrame& buffer = m_frameBufferCache[frameIndex]; | 285 ImageFrame& buffer = m_frameBufferCache[frameIndex]; |
| 187 if ((buffer.status() == ImageFrame::FrameEmpty) && !initFrameBuffer(frameInd ex)) | 286 if ((buffer.status() == ImageFrame::FrameEmpty) && !initFrameBuffer(frameInd ex)) |
| 188 return false; // initFrameBuffer() has already called setFailed(). | 287 return false; // initFrameBuffer() has already called setFailed(). |
| 189 | 288 |
| 190 buffer.setStatus(ImageFrame::FrameComplete); | 289 buffer.setStatus(ImageFrame::FrameComplete); |
| 191 | 290 |
| 192 if (!m_currentBufferSawAlpha) { | 291 if (!m_currentBufferSawAlpha) { |
| 193 // The whole frame was non-transparent, so it's possible that the entire | 292 // The whole frame was non-transparent, so it's possible that the entire |
| 194 // resulting buffer was non-transparent, and we can setHasAlpha(false). | 293 // resulting buffer was non-transparent, and we can setHasAlpha(false). |
| 195 if (buffer.originalFrameRect().contains(IntRect(IntPoint(), size()))) { | 294 if (buffer.originalFrameRect().contains(IntRect(IntPoint(), size()))) { |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 255 void GIFImageDecoder::initializeNewFrame(size_t index) | 354 void GIFImageDecoder::initializeNewFrame(size_t index) |
| 256 { | 355 { |
| 257 ImageFrame* buffer = &m_frameBufferCache[index]; | 356 ImageFrame* buffer = &m_frameBufferCache[index]; |
| 258 const GIFFrameContext* frameContext = m_reader->frameContext(index); | 357 const GIFFrameContext* frameContext = m_reader->frameContext(index); |
| 259 buffer->setOriginalFrameRect(intersection(frameContext->frameRect(), IntRect (IntPoint(), size()))); | 358 buffer->setOriginalFrameRect(intersection(frameContext->frameRect(), IntRect (IntPoint(), size()))); |
| 260 buffer->setDuration(frameContext->delayTime()); | 359 buffer->setDuration(frameContext->delayTime()); |
| 261 buffer->setDisposalMethod(frameContext->disposalMethod()); | 360 buffer->setDisposalMethod(frameContext->disposalMethod()); |
| 262 buffer->setRequiredPreviousFrameIndex(findRequiredPreviousFrame(index, false )); | 361 buffer->setRequiredPreviousFrameIndex(findRequiredPreviousFrame(index, false )); |
| 263 } | 362 } |
| 264 | 363 |
| 265 void GIFImageDecoder::decode(size_t index) | 364 void GIFImageDecoder::decode(size_t index) |
|
scroggo_chromium
2015/12/03 21:47:21
So IIUC, decodeTo (which you added) lets the calle
aleksandar.stojiljkovic
2015/12/04 01:27:27
decodeto wraps decode - decode() is not called dir
aleksandar.stojiljkovic
2015/12/07 19:24:22
Done - removed decodeTo method. Decoding output se
| |
| 266 { | 365 { |
| 366 phaveDecodedRow = (m_colorMode == ImageFrame::Index8 && !m_forceN32Decoding) | |
| 367 ? (&GIFImageDecoder::haveDecodedRowIndex8) | |
| 368 : (&GIFImageDecoder::haveDecodedRowN32); | |
| 267 parse(GIFFrameCountQuery); | 369 parse(GIFFrameCountQuery); |
| 268 | 370 |
| 269 if (failed()) | 371 if (failed()) |
| 270 return; | 372 return; |
| 271 | 373 |
| 272 Vector<size_t> framesToDecode; | 374 Vector<size_t> framesToDecode; |
| 273 size_t frameToDecode = index; | 375 size_t frameToDecode = index; |
| 274 do { | 376 do { |
| 275 framesToDecode.append(frameToDecode); | 377 framesToDecode.append(frameToDecode); |
| 276 frameToDecode = m_frameBufferCache[frameToDecode].requiredPreviousFrameI ndex(); | 378 frameToDecode = m_frameBufferCache[frameToDecode].requiredPreviousFrameI ndex(); |
| 277 } while (frameToDecode != kNotFound && m_frameBufferCache[frameToDecode].sta tus() != ImageFrame::FrameComplete); | 379 } while (frameToDecode != kNotFound && m_frameBufferCache[frameToDecode].sta tus() != ImageFrame::FrameComplete); |
| 278 | 380 |
| 279 for (auto i = framesToDecode.rbegin(); i != framesToDecode.rend(); ++i) { | 381 for (auto i = framesToDecode.rbegin(); i != framesToDecode.rend(); ++i) { |
| 382 bool notSuitableForIndex8(m_forceN32Decoding); | |
| 280 if (!m_reader->decode(*i)) { | 383 if (!m_reader->decode(*i)) { |
|
scroggo_chromium
2015/12/03 21:47:21
So decode() may now fail for another reason (wrong
aleksandar.stojiljkovic
2015/12/04 01:27:27
it could fail for previous reasons (setFailed() ca
aleksandar.stojiljkovic
2015/12/22 15:19:55
Done. This code removed.
| |
| 281 setFailed(); | 384 // Decoder asks to switch color mode as Index8 is not applicable. Tr y to decode using N32. |
| 282 return; | 385 // This happens only when transparent pixels showing previous frames or offsets are detected |
| 386 // and table is changed. | |
| 387 if (notSuitableForIndex8 != m_forceN32Decoding) { | |
|
scroggo_chromium
2015/12/03 21:47:21
So GIFImageReader::decode modified m_forceN32Decod
aleksandar.stojiljkovic
2015/12/04 01:27:27
Acknowledged.
aleksandar.stojiljkovic
2015/12/04 01:27:27
This relates to that init inside haveDecodedRow an
aleksandar.stojiljkovic
2015/12/22 15:19:55
Done. Simplified and removed m_forceN32Decoding.
| |
| 388 ASSERT(m_colorMode == ImageFrame::Index8); | |
| 389 clearFrameBuffer(*i); | |
| 390 // N32 decoding would continue until there is non dependent Inde x8 frame detected, see initFrameBuffer(). | |
| 391 phaveDecodedRow = &GIFImageDecoder::haveDecodedRowN32; | |
| 392 if (!m_reader->decode(*i)) { | |
| 393 setFailed(); | |
| 394 return; | |
| 395 } | |
| 396 } else { | |
| 397 setFailed(); | |
| 398 return; | |
| 399 } | |
| 283 } | 400 } |
| 284 | 401 |
| 285 // We need more data to continue decoding. | 402 // We need more data to continue decoding. |
| 286 if (m_frameBufferCache[*i].status() != ImageFrame::FrameComplete) | 403 if (m_frameBufferCache[*i].status() != ImageFrame::FrameComplete) |
| 287 break; | 404 break; |
| 288 } | 405 } |
| 289 | 406 |
| 290 // It is also a fatal error if all data is received and we have decoded all | 407 // It is also a fatal error if all data is received and we have decoded all |
| 291 // frames available but the file is truncated. | 408 // frames available but the file is truncated. |
| 292 if (index >= m_frameBufferCache.size() - 1 && isAllDataReceived() && m_reade r && !m_reader->parseCompleted()) | 409 if (index >= m_frameBufferCache.size() - 1 && isAllDataReceived() && m_reade r && !m_reader->parseCompleted()) |
| 293 setFailed(); | 410 setFailed(); |
| 294 } | 411 } |
| 295 | 412 |
| 413 void GIFImageDecoder::decodeTo(size_t index, ImageFrame::ColorType outputColor) | |
| 414 { | |
| 415 ASSERT(outputColor == ImageFrame::N32 || outputColor == ImageFrame::Index8); | |
| 416 ImageFrame::ColorType c = m_colorMode; | |
| 417 m_colorMode = outputColor; | |
| 418 decode(index); | |
| 419 m_colorMode = c; | |
| 420 } | |
| 421 | |
| 422 bool GIFImageDecoder::canDecodeTo(size_t index, ImageFrame::ColorType outputType ) | |
| 423 { | |
| 424 return ((outputType == ImageFrame::N32) | |
| 425 || (outputType == ImageFrame::Index8 && !m_forceN32Decoding)); | |
| 426 } | |
| 427 | |
| 428 void GIFImageDecoder::setForceN32Decoding(bool value) | |
| 429 { | |
| 430 ASSERT(m_colorMode == ImageFrame::Index8); | |
| 431 m_forceN32Decoding = value; | |
| 432 phaveDecodedRow = (!value && m_colorMode == ImageFrame::Index8) | |
| 433 ? (&GIFImageDecoder::haveDecodedRowIndex8) | |
| 434 : (&GIFImageDecoder::haveDecodedRowN32); | |
| 435 } | |
| 436 | |
| 296 void GIFImageDecoder::parse(GIFParseQuery query) | 437 void GIFImageDecoder::parse(GIFParseQuery query) |
| 297 { | 438 { |
| 298 if (failed()) | 439 if (failed()) |
| 299 return; | 440 return; |
| 300 | 441 |
| 301 if (!m_reader) { | 442 if (!m_reader) { |
| 302 m_reader = adoptPtr(new GIFImageReader(this)); | 443 m_reader = adoptPtr(new GIFImageReader(this)); |
| 303 m_reader->setData(m_data); | 444 m_reader->setData(m_data); |
| 304 } | 445 } |
| 305 | 446 |
| 306 if (!m_reader->parse(query)) | 447 if (!m_reader->parse(query)) |
| 307 setFailed(); | 448 setFailed(); |
| 308 } | 449 } |
| 309 | 450 |
| 310 bool GIFImageDecoder::initFrameBuffer(size_t frameIndex) | 451 bool GIFImageDecoder::initFrameBuffer(size_t frameIndex) |
| 311 { | 452 { |
| 453 if (m_colorMode == ImageFrame::Index8) { | |
| 454 // Ended up here, since previous frame didn't support Index8 decoding. | |
| 455 // Try to switch back to Index( decoding. | |
| 456 return initFrameBufferIndex8(frameIndex); | |
| 457 } | |
| 312 // Initialize the frame rect in our buffer. | 458 // Initialize the frame rect in our buffer. |
| 313 ImageFrame* const buffer = &m_frameBufferCache[frameIndex]; | 459 ImageFrame* const buffer = &m_frameBufferCache[frameIndex]; |
| 314 | 460 |
| 315 size_t requiredPreviousFrameIndex = buffer->requiredPreviousFrameIndex(); | 461 size_t requiredPreviousFrameIndex = buffer->requiredPreviousFrameIndex(); |
| 316 if (requiredPreviousFrameIndex == kNotFound) { | 462 if (requiredPreviousFrameIndex == kNotFound) { |
| 317 // This frame doesn't rely on any previous data. | 463 // This frame doesn't rely on any previous data. |
| 318 if (!buffer->setSize(size().width(), size().height())) | 464 if (!buffer->setSize(size().width(), size().height())) |
| 319 return setFailed(); | 465 return setFailed(); |
| 320 } else { | 466 } else { |
| 321 const ImageFrame* prevBuffer = &m_frameBufferCache[requiredPreviousFrame Index]; | 467 const ImageFrame* prevBuffer = &m_frameBufferCache[requiredPreviousFrame Index]; |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 335 } | 481 } |
| 336 | 482 |
| 337 // Update our status to be partially complete. | 483 // Update our status to be partially complete. |
| 338 buffer->setStatus(ImageFrame::FramePartial); | 484 buffer->setStatus(ImageFrame::FramePartial); |
| 339 | 485 |
| 340 // Reset the alpha pixel tracker for this frame. | 486 // Reset the alpha pixel tracker for this frame. |
| 341 m_currentBufferSawAlpha = false; | 487 m_currentBufferSawAlpha = false; |
| 342 return true; | 488 return true; |
| 343 } | 489 } |
| 344 | 490 |
| 491 static bool framesUseSameColorTable(const GIFFrameContext *frame1, const GIFFram eContext *frame2) | |
|
scroggo_chromium
2015/12/03 21:47:22
I think these should be const references?
aleksandar.stojiljkovic
2015/12/04 01:27:27
Acknowledged.
aleksandar.stojiljkovic
2015/12/07 19:24:22
Done.
| |
| 492 { | |
| 493 if (frame1->localColorMap().isDefined() ^ frame2->localColorMap().isDefined( )) | |
| 494 return false; | |
| 495 if (!frame1->localColorMap().isDefined()) { | |
| 496 // They both use global ColorMap. | |
|
scroggo_chromium
2015/12/03 21:47:22
ASSERT(!frame2->localColorMap().isDefined())?
aleksandar.stojiljkovic
2015/12/04 01:27:27
previous ^ should be resolving it, but it is compl
aleksandar.stojiljkovic
2015/12/07 19:24:22
Done.
| |
| 497 return (frame1->transparentPixel() == frame1->transparentPixel()); | |
| 498 } | |
| 499 if (frame1->localColorMap().getPosition() == frame2->localColorMap().getPosi tion()) { | |
|
scroggo_chromium
2015/12/03 21:47:21
This block of code is tough to follow. I think som
aleksandar.stojiljkovic
2015/12/07 19:24:22
Done.
| |
| 500 if (frame1->transparentPixel() == frame2->transparentPixel() | |
| 501 || (frame1->transparentPixel() >= frame1->localColorMap().table().si ze() | |
| 502 && frame2->transparentPixel() >= frame2->localColorMap().table() .size())) | |
| 503 return true; | |
| 504 } | |
| 505 return false; | |
| 506 } | |
| 507 | |
| 508 bool GIFImageDecoder::initFrameBufferIndex8(size_t frameIndex) | |
| 509 { | |
| 510 ASSERT(m_colorMode == ImageFrame::Index8); | |
| 511 // Initialize the frame rect in our buffer. | |
| 512 ImageFrame* buffer = &m_frameBufferCache[frameIndex]; | |
| 513 | |
| 514 const GIFFrameContext* frameContext = m_reader->frameContext(frameIndex); | |
| 515 bool isFullScreen = frameContext->frameRect().contains(IntRect(IntPoint(), s ize())); | |
| 516 const GIFColorMap::Table& colorTable = frameContext->localColorMap().isDefin ed() | |
| 517 ? frameContext->localColorMap().table() : m_reader->globalColorMap().tab le(); | |
| 518 | |
| 519 // Checking frameContext->transparentPixel() != kNotFound is not precise, si nce | |
| 520 // it also needs to include colortable information - if transparent pixels i s outside colortable | |
| 521 // there is no transparent pixel. | |
| 522 // e.g. Colortable in some of the GIFs is having 40 elements andtransparentP ixel set to 0xFF. | |
| 523 // This is changed here only to affect Index8 and the intention is to keep t he N32 code unchanged. | |
|
scroggo_chromium
2015/12/03 21:47:21
Are you saying that updating required previous fra
aleksandar.stojiljkovic
2015/12/04 01:27:27
Originally didn't want to change it, tried to deli
| |
| 524 bool opaque = frameContext->transparentPixel() >= colorTable.size(); | |
| 525 | |
| 526 // The same check - if fullscreen and opaque, then previus is not required t o copy - happens in | |
| 527 // frameComplete -move it earlier so it affects also current run (not only starting from next loop). | |
| 528 if (isFullScreen && opaque) | |
| 529 buffer->setRequiredPreviousFrameIndex(kNotFound); | |
| 530 | |
| 531 size_t requiredPreviousFrameIndex = buffer->requiredPreviousFrameIndex(); | |
| 532 | |
| 533 if (requiredPreviousFrameIndex == kNotFound) { | |
| 534 // This frame doesn't rely on any previous data. | |
| 535 // Reset not suitable and table changed information as this is full new frame. | |
|
scroggo_chromium
2015/12/03 21:47:21
What does "Reset not suitable" mean?
aleksandar.stojiljkovic
2015/12/04 01:27:27
m_forceN32Decoding can be reset to false, and resu
aleksandar.stojiljkovic
2015/12/07 19:24:22
Done.
| |
| 536 // It is OK to switch back from N32 to Index8 if it was originally reque sted. | |
| 537 setForceN32Decoding(false); | |
| 538 phaveDecodedRow = &GIFImageDecoder::haveDecodedRowIndex8; | |
| 539 if (!buffer->setSizeIndex8(size().width(), size().height(), colorTable, frameContext->transparentPixel())) | |
|
aleksandar.stojiljkovic
2015/12/04 01:27:27
This call is prefilling it so that all pixels have
| |
| 540 return setFailed(); | |
| 541 } else { | |
| 542 const ImageFrame* prevBuffer = &m_frameBufferCache[requiredPreviousFrame Index]; | |
| 543 ASSERT(prevBuffer->status() == ImageFrame::FrameComplete); | |
| 544 ASSERT(!isFullScreen || !opaque); | |
| 545 | |
| 546 // Preserve the last frame as the starting state for this frame. | |
| 547 // If in Index8 mode, and colorTable is not changed nor setForceN32Decod ing set, | |
| 548 // preserve frame same way as for N32. | |
| 549 bool useN32 = true; | |
| 550 if (!m_forceN32Decoding) { | |
|
scroggo_chromium
2015/12/03 21:47:21
Wait, should we reach this method (or get this far
aleksandar.stojiljkovic
2015/12/07 19:24:22
I think it looks more logical now - initFrameBuffe
aleksandar.stojiljkovic
2015/12/22 15:19:55
Done. Simplified and removed m_forceN32Decoding.
| |
| 551 // If table is changed and in the same time, there is transparency o r following frame | |
| 552 // is subrect, cannot do Index8. Otherwise, with the same table, no problem to continue | |
| 553 // with Index8 copy of dependent frame as starting point for this fr ame. | |
| 554 if (framesUseSameColorTable(frameContext, m_reader->frameContext(req uiredPreviousFrameIndex))) { | |
| 555 useN32 = false; | |
| 556 if (!buffer->copyBitmapData(*prevBuffer)) | |
| 557 return setFailed(); | |
| 558 ASSERT(buffer->getSkBitmap().colorType() == kIndex_8_SkColorType ); | |
| 559 } else { | |
| 560 setForceN32Decoding(true); | |
| 561 useN32 = true; | |
| 562 } | |
| 563 } | |
| 564 // For unsupported Index8 and N32, next frame starts as previous frame c opy to N32. | |
| 565 if (useN32 && !buffer->copyBitmapData(*prevBuffer, ImageFrame::N32)) | |
| 566 return setFailed(); | |
| 567 | |
| 568 if (prevBuffer->disposalMethod() == ImageFrame::DisposeOverwriteBgcolor) { | |
| 569 // We want to clear the previous frame to transparent, without | |
| 570 // affecting pixels in the image outside of the frame. | |
| 571 const IntRect& prevRect = prevBuffer->originalFrameRect(); | |
| 572 ASSERT(!prevRect.contains(IntRect(IntPoint(), size()))); | |
| 573 buffer->zeroFillFrameRect(prevRect); | |
| 574 } | |
| 575 } | |
| 576 | |
| 577 // Update our status to be partially complete. | |
| 578 buffer->setStatus(ImageFrame::FramePartial); | |
| 579 | |
| 580 // Reset the alpha pixel tracker for this frame. | |
| 581 m_currentBufferSawAlpha = false; | |
| 582 return true; | |
| 583 } | |
| 345 } // namespace blink | 584 } // namespace blink |
| OLD | NEW |