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 |