Index: third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp |
diff --git a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp |
index 07dd9de56970d610b5895b1f6ebbb0dc0d9b8ca7..e2a0888a8147454083127eb2805d1a1875d3196a 100644 |
--- a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp |
+++ b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp |
@@ -33,9 +33,11 @@ |
namespace blink { |
-GIFImageDecoder::GIFImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOption colorOptions, size_t maxDecodedBytes) |
+GIFImageDecoder::GIFImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOption colorOptions, size_t maxDecodedBytes, ImageFrame::ColorType colorType) |
: ImageDecoder(alphaOption, colorOptions, maxDecodedBytes) |
+ , 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
|
, m_repetitionCount(cAnimationLoopOnce) |
+ , m_colorMode(colorType) |
{ |
} |
@@ -102,35 +104,38 @@ bool GIFImageDecoder::setFailed() |
return ImageDecoder::setFailed(); |
} |
-bool GIFImageDecoder::haveDecodedRow(size_t frameIndex, GIFRow::const_iterator rowBegin, size_t width, size_t rowNumber, unsigned repeatCount, bool writeTransparentPixels) |
+static inline const GIFColorMap& getColorMap(const GIFFrameContext& frameContext, const GIFImageReader& reader) |
{ |
- const GIFFrameContext* frameContext = m_reader->frameContext(frameIndex); |
+ return (frameContext.localColorMap().isDefined() ? frameContext.localColorMap() : reader.globalColorMap()); |
+} |
+ |
+bool GIFImageDecoder::haveDecodedRowN32(const GIFFrameContext& frameContext, GIFRow::const_iterator rowBegin, size_t rowNumber, unsigned repeatCount, bool writeTransparentPixels) |
+{ |
+ ASSERT(m_frameBufferCache[frameContext.frameId()].bitmap().colorType() == kN32_SkColorType); |
+ |
// The pixel data and coordinates supplied to us are relative to the frame's |
// origin within the entire image size, i.e. |
// (frameContext->xOffset, frameContext->yOffset). There is no guarantee |
// that width == (size().width() - frameContext->xOffset), so |
// we must ensure we don't run off the end of either the source data or the |
// row's X-coordinates. |
- const int xBegin = frameContext->xOffset(); |
- const int yBegin = frameContext->yOffset() + rowNumber; |
- const int xEnd = std::min(static_cast<int>(frameContext->xOffset() + width), size().width()); |
- const int yEnd = std::min(static_cast<int>(frameContext->yOffset() + rowNumber + repeatCount), size().height()); |
+ const size_t width = frameContext.width(); |
+ const int xBegin = frameContext.xOffset(); |
+ const int yBegin = frameContext.yOffset() + rowNumber; |
+ const int xEnd = std::min(static_cast<int>(xBegin + width), size().width()); |
+ const int yEnd = std::min(static_cast<int>(yBegin + repeatCount), size().height()); |
if (!width || (xBegin < 0) || (yBegin < 0) || (xEnd <= xBegin) || (yEnd <= yBegin)) |
return true; |
- const GIFColorMap::Table& colorTable = frameContext->localColorMap().isDefined() ? frameContext->localColorMap().table() : m_reader->globalColorMap().table(); |
+ const GIFColorMap::Table& colorTable = getColorMap(frameContext, *m_reader).table(); |
if (colorTable.isEmpty()) |
return true; |
GIFColorMap::Table::const_iterator colorTableIter = colorTable.begin(); |
- // Initialize the frame if necessary. |
- ImageFrame& buffer = m_frameBufferCache[frameIndex]; |
- if ((buffer.status() == ImageFrame::FrameEmpty) && !initFrameBuffer(frameIndex)) |
- return false; |
- |
- const size_t transparentPixel = frameContext->transparentPixel(); |
+ ImageFrame& buffer = m_frameBufferCache[frameContext.frameId()]; |
+ const size_t transparentPixel = frameContext.transparentPixel(); |
GIFRow::const_iterator rowEnd = rowBegin + (xEnd - xBegin); |
ImageFrame::PixelData* currentAddress = buffer.getAddr(xBegin, yBegin); |
@@ -174,6 +179,85 @@ bool GIFImageDecoder::haveDecodedRow(size_t frameIndex, GIFRow::const_iterator r |
return true; |
} |
+bool GIFImageDecoder::haveDecodedRowIndex8(const GIFFrameContext& frameContext, GIFRow::const_iterator rowBegin, size_t rowNumber, unsigned repeatCount, bool writeTransparentPixels) |
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.
|
+{ |
+ ASSERT(m_frameBufferCache[frameContext.frameId()].bitmap().colorType() == kIndex_8_SkColorType); |
+ |
+ // The pixel data and coordinates supplied to us are relative to the frame's |
+ // origin within the entire image size, i.e. |
+ // (frameContext.xOffset, frameContext.yOffset). There is no guarantee |
+ // that width == (size().width() - frameContext.xOffset), so |
+ // we must ensure we don't run off the end of either the source data or the |
+ // row's X-coordinates. |
+ const size_t width = frameContext.width(); |
+ const int xBegin = frameContext.xOffset(); |
+ const int yBegin = frameContext.yOffset() + rowNumber; |
+ const int xEnd = std::min(static_cast<int>(frameContext.xOffset() + width), size().width()); |
+ const int yEnd = std::min(static_cast<int>(frameContext.yOffset() + rowNumber + repeatCount), size().height()); |
+ if (!width || (xBegin < 0) || (yBegin < 0) || (xEnd <= xBegin) || (yEnd <= yBegin)) |
+ return true; |
+ |
+ const GIFColorMap::Table& colorTable = getColorMap(frameContext, *m_reader).table(); |
+ |
+ if (colorTable.isEmpty()) |
+ return true; |
+ |
+ |
+ ImageFrame& buffer = m_frameBufferCache[frameContext.frameId()]; |
+ const size_t transparentPixel = frameContext.transparentPixel(); |
+ GIFRow::const_iterator rowEnd = rowBegin + (xEnd - xBegin); |
+ ImageFrame::PixelData8* currentAddress = buffer.getAddr8(xBegin, yBegin); |
+ |
+ bool opaque = true; |
+ // writeTransparentPixels is writing without check, but calculates if there is transparency |
+ // (m_currentBufferSawAlpha). If transparency was found in previous rows, no need to calculate here. |
+ writeTransparentPixels = writeTransparentPixels || buffer.requiredPreviousFrameIndex() == kNotFound; |
+ if (transparentPixel < colorTable.size() && !(writeTransparentPixels && m_currentBufferSawAlpha)) { |
+ if (writeTransparentPixels) { |
+ while (rowBegin != rowEnd) { |
+ opaque = opaque && (*rowBegin ^ transparentPixel); |
+ *currentAddress++ = *rowBegin++; |
+ } |
+ } else { |
+ for (; rowBegin != rowEnd; ++rowBegin, ++currentAddress) { |
+ size_t index = *rowBegin; |
+ if (index == transparentPixel) { |
+ opaque = false; |
+ } else { |
+ *currentAddress = index; |
+ } |
+ } |
+ } |
+ } else { |
+ // No transparency to deal with. |
+ if (xEnd - xBegin == size().width()) { |
+ size_t* destination = (size_t*)currentAddress; |
+ const size_t* source = (const size_t*)rowBegin; |
+ const size_t count = (rowEnd - rowBegin + sizeof(size_t) - 1) / sizeof(size_t); |
+ const size_t* sourceEnd = source + count; |
+ while (source != sourceEnd) |
+ *destination++ = *source++; |
+ } else { |
+ while (rowBegin != rowEnd) |
+ *currentAddress++ = *rowBegin++; |
+ } |
+ } |
+ |
+ if (!opaque) |
+ m_currentBufferSawAlpha = true; |
+ |
+ // Tell the frame to copy the row data if specified. |
+ if (repeatCount > 1) { |
+ const int rowBytes = (xEnd - xBegin) * sizeof(uint8_t); |
+ const ImageFrame::PixelData8* const startAddr = buffer.getAddr8(xBegin, yBegin); |
+ for (int destY = yBegin + 1; destY < yEnd; ++destY) |
+ memcpy(buffer.getAddr8(xBegin, destY), startAddr, rowBytes); |
+ } |
+ |
+ buffer.setPixelsChanged(true); |
+ return true; |
+} |
+ |
bool GIFImageDecoder::parseCompleted() const |
{ |
return m_reader && m_reader->parseCompleted(); |
@@ -200,8 +284,8 @@ bool GIFImageDecoder::frameComplete(size_t frameIndex) |
// outside its rect doesn't have alpha. To know whether this is |
// true, we check the start state of the frame -- if it doesn't have |
// alpha, we're safe. |
- const ImageFrame* prevBuffer = &m_frameBufferCache[buffer.requiredPreviousFrameIndex()]; |
- ASSERT(prevBuffer->disposalMethod() != ImageFrame::DisposeOverwritePrevious); |
+ const ImageFrame* previousBuffer = &m_frameBufferCache[buffer.requiredPreviousFrameIndex()]; |
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.
|
+ ASSERT(previousBuffer->disposalMethod() != ImageFrame::DisposeOverwritePrevious); |
// Now, if we're at a DisposeNotSpecified or DisposeKeep frame, then |
// we can say we have no alpha if that frame had no alpha. But |
@@ -211,7 +295,7 @@ bool GIFImageDecoder::frameComplete(size_t frameIndex) |
// The only remaining case is a DisposeOverwriteBgcolor frame. If |
// it had no alpha, and its rect is contained in the current frame's |
// rect, we know the current frame has no alpha. |
- if ((prevBuffer->disposalMethod() == ImageFrame::DisposeOverwriteBgcolor) && !prevBuffer->hasAlpha() && buffer.originalFrameRect().contains(prevBuffer->originalFrameRect())) |
+ if ((previousBuffer->disposalMethod() == ImageFrame::DisposeOverwriteBgcolor) && !previousBuffer->hasAlpha() && buffer.originalFrameRect().contains(previousBuffer->originalFrameRect())) |
buffer.setHasAlpha(false); |
} |
} |
@@ -293,6 +377,13 @@ void GIFImageDecoder::decode(size_t index) |
setFailed(); |
} |
+void GIFImageDecoder::setupHaveDecodedRowCallbacks(bool isIndex8) |
+{ |
+ phaveDecodedRow = isIndex8 |
+ ? (&GIFImageDecoder::haveDecodedRowIndex8) |
+ : (&GIFImageDecoder::haveDecodedRowN32); |
+} |
+ |
void GIFImageDecoder::parse(GIFParseQuery query) |
{ |
if (failed()) |
@@ -307,33 +398,162 @@ void GIFImageDecoder::parse(GIFParseQuery query) |
setFailed(); |
} |
-bool GIFImageDecoder::initFrameBuffer(size_t frameIndex) |
+bool GIFImageDecoder::initFrameBufferFromPreviousN32(ImageFrame* const buffer, const 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
|
{ |
+ // Preserve the last frame as the starting state for this frame. |
+ if (!buffer->copyBitmapData(previousBuffer, ImageFrame::N32)) |
+ return false; |
+ |
+ if (previousBuffer.disposalMethod() == ImageFrame::DisposeOverwriteBgcolor) { |
+ // We want to clear the previous frame to transparent, without |
+ // affecting pixels in the image outside of the frame. |
+ const IntRect& prevRect = previousBuffer.originalFrameRect(); |
+ ASSERT(!prevRect.contains(IntRect(IntPoint(), size()))); |
+ buffer->zeroFillFrameRect(prevRect); |
+ } |
+ return true; |
+} |
+ |
+bool GIFImageDecoder::initFrameBufferN32(size_t frameIndex) |
+{ |
+ setupHaveDecodedRowCallbacks(false); |
+ |
// Initialize the frame rect in our buffer. |
ImageFrame* const buffer = &m_frameBufferCache[frameIndex]; |
size_t requiredPreviousFrameIndex = buffer->requiredPreviousFrameIndex(); |
if (requiredPreviousFrameIndex == kNotFound) { |
// This frame doesn't rely on any previous data. |
- if (!buffer->setSize(size().width(), size().height())) |
+ if (!buffer->setSize(size().width(), size().height(), getBackgroundColor(frameIndex))) |
return setFailed(); |
} else { |
- const ImageFrame* prevBuffer = &m_frameBufferCache[requiredPreviousFrameIndex]; |
- ASSERT(prevBuffer->status() == ImageFrame::FrameComplete); |
- |
- // Preserve the last frame as the starting state for this frame. |
- if (!buffer->copyBitmapData(*prevBuffer)) |
+ const ImageFrame* previousBuffer = &m_frameBufferCache[requiredPreviousFrameIndex]; |
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.
|
+ ASSERT(previousBuffer->status() == ImageFrame::FrameComplete); |
+ if (!initFrameBufferFromPreviousN32(buffer, *previousBuffer)) |
return setFailed(); |
+ } |
- if (prevBuffer->disposalMethod() == ImageFrame::DisposeOverwriteBgcolor) { |
- // We want to clear the previous frame to transparent, without |
- // affecting pixels in the image outside of the frame. |
- const IntRect& prevRect = prevBuffer->originalFrameRect(); |
- ASSERT(!prevRect.contains(IntRect(IntPoint(), size()))); |
- buffer->zeroFillFrameRect(prevRect); |
+ // Update our status to be partially complete. |
+ buffer->setStatus(ImageFrame::FramePartial); |
+ |
+ // Reset the alpha pixel tracker for this frame. |
+ m_currentBufferSawAlpha = false; |
+ return true; |
+} |
+ |
+bool GIFImageDecoder::isIndex8Applicable(const GIFFrameContext& frame, size_t requiredPreviousFrameIndex) const |
+{ |
+ const GIFColorMap& colorMap = getColorMap(frame, *m_reader); |
+ const bool useGlobalColorMap = frame.localColorMap().isDefined(); |
+ |
+ if (requiredPreviousFrameIndex == kNotFound) { |
+ if (colorMap.getTableSize() == 256 && frame.transparentPixel() >= colorMap.getTableSize() |
+ && (useGlobalColorMap && m_reader->backgroundIndex() >= colorMap.getTableSize()) |
scroggo_chromium
2016/01/06 21:50:40
Do you need the parentheses here?
aleksandar.stojiljkovic
2016/01/18 13:58:49
Done.
|
+ && (!isAllDataReceived() || !frame.frameRect().contains(IntRect(IntPoint(), size())))) { |
+ // Background is filled with transparency or with background color (opaque gifs). |
+ // Return false when there is a need for transparency and no space in colormap for it; |
+ // when colormap is opaque and full (256 entries) and in the same time: |
scroggo_chromium
2016/01/06 21:50:40
nit: at* the same time
aleksandar.stojiljkovic
2016/01/18 13:58:50
Done.
|
+ // - image is not fully received, so missing part is presented as transparent. |
+ // - all frames are not fullscreen, i.e. they are decoded to sub rectangle 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
|
+ return false; |
} |
+ return true; |
} |
+ // 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.
|
+ const GIFFrameContext& previousFrame(*(m_reader->frameContext(requiredPreviousFrameIndex))); |
+ const GIFColorMap& previousColorMap = getColorMap(previousFrame, *m_reader); |
+ return ((colorMap.getPosition() == previousColorMap.getPosition()) && (frame.transparentPixel() == previousFrame.transparentPixel())); |
+} |
+ |
+// 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.
|
+void GIFImageDecoder::updateRequiredPreviousFrame(ImageFrame* buffer, const GIFFrameContext& frameContext) |
+{ |
+ ASSERT(m_colorMode == ImageFrame::Index8); |
+ if (buffer->requiredPreviousFrameIndex() == kNotFound) |
+ return; |
+ |
+ bool isFullScreen = frameContext.frameRect().contains(IntRect(IntPoint(), size())); |
+ |
+ // If transparent pixel is outside color table, there is no transparent pixel. |
+ // e.g. Color table in some of the GIFs is having 40 elements and transparentPixel set to 0xFF. |
scroggo_chromium
2016/01/06 21:50:41
nit: "is having" -> "has"
aleksandar.stojiljkovic
2016/01/18 13:58:50
Done.
|
+ 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.
|
+ |
+ // If fullscreen and opaque, the frame is independent of previous frame. |
+ if (isFullScreen && opaque) |
+ buffer->setRequiredPreviousFrameIndex(kNotFound); |
+} |
+ |
+// Returns transparent index or background index. |
+unsigned char GIFImageDecoder::getBackgroundIndex(const GIFFrameContext& frameContext) const |
+{ |
+ const size_t background = frameContext.transparentPixel(); |
+ const GIFColorMap& colorMap = getColorMap(frameContext, *m_reader); |
+ if (background < colorMap.getTableSize()) |
+ return background; |
+ |
+ if (!frameContext.localColorMap().isDefined() && m_reader->backgroundIndex() < colorMap.getTableSize()) |
+ return m_reader->backgroundIndex(); |
+ 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
|
+} |
+ |
+SkColor GIFImageDecoder::getBackgroundColor(size_t frameIndex) const |
+{ |
+ const GIFFrameContext* frameContext = m_reader->frameContext(frameIndex); |
+ unsigned char backgroundIndex = getBackgroundIndex(*frameContext); |
+ if (frameContext->transparentPixel() == backgroundIndex) |
+ 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
|
+ const GIFColorMap::Table& table = getColorMap(*frameContext, *m_reader).table(); |
+ return (backgroundIndex < table.size() ? table[backgroundIndex]: 0); |
+} |
+ |
+bool GIFImageDecoder::initFrameBuffer(size_t frameIndex) |
+{ |
+ ImageFrame* buffer = &m_frameBufferCache[frameIndex]; |
+ if (buffer->status() != ImageFrame::FrameEmpty) { |
+ // If it is partial, decode to the same bitmap. |
+ 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
|
+ return true; |
+ } |
+ |
+ if (m_colorMode == ImageFrame::N32) { |
+ return initFrameBufferN32(frameIndex); |
+ } |
+ const GIFFrameContext* frameContext = m_reader->frameContext(frameIndex); |
+ updateRequiredPreviousFrame(buffer, *frameContext); |
+ const size_t requiredPreviousFrameIndex = buffer->requiredPreviousFrameIndex(); |
+ |
+ bool useIndex8 = true; |
+ if (requiredPreviousFrameIndex == kNotFound) { |
+ if (isIndex8Applicable(*frameContext, requiredPreviousFrameIndex)) { |
+ const unsigned char backgroundIndex = getBackgroundIndex(*frameContext); |
+ if (!buffer->setSizeIndex8(size().width(), size().height(), getColorMap(*frameContext, *m_reader).table(), |
+ backgroundIndex, backgroundIndex == frameContext->transparentPixel())) |
+ 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
|
+ } else { |
+ return initFrameBufferN32(frameIndex); |
+ } |
+ } else { |
+ const ImageFrame* previousBuffer = &m_frameBufferCache[requiredPreviousFrameIndex]; |
+ ASSERT(previousBuffer->status() == ImageFrame::FrameComplete); |
+ |
+ // Copy the required completed frame as the starting state for this frame. |
+ useIndex8 = previousBuffer->getSkBitmap().colorType() == kIndex_8_SkColorType; |
+ if (useIndex8) { |
+ if (isIndex8Applicable(*frameContext, requiredPreviousFrameIndex)) { |
+ if (!buffer->copyBitmapData(*previousBuffer)) |
+ return setFailed(); |
+ ASSERT(buffer->getSkBitmap().colorType() == kIndex_8_SkColorType); |
+ } else { |
+ useIndex8 = false; |
+ } |
+ } |
+ // If needed to decode to N32, frame starts as copy of required frame (Index8/N32) to N32. |
+ if (!useIndex8 && !initFrameBufferFromPreviousN32(buffer, *previousBuffer)) |
+ return setFailed(); |
+ } |
+ setupHaveDecodedRowCallbacks(useIndex8); |
+ |
// Update our status to be partially complete. |
buffer->setStatus(ImageFrame::FramePartial); |
@@ -342,4 +562,30 @@ bool GIFImageDecoder::initFrameBuffer(size_t frameIndex) |
return true; |
} |
+bool GIFImageDecoder::canDecodeTo(size_t index, ImageFrame::ColorType outputType) |
+{ |
+ 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
|
+ return (outputType == m_colorMode); |
+ |
+ // Go from one to previous until calculating if Index8 is supported. |
+ size_t frameToDecode = index; |
+ ImageFrame::ColorType calculatedOutput = ImageFrame::Index8; |
+ while (frameToDecode != kNotFound) { |
+ ImageFrame* buffer = &m_frameBufferCache[frameToDecode]; |
+ if (buffer->status() == ImageFrame::FrameComplete) { |
+ // In this case, color type from complete frame is kept. |
+ calculatedOutput = static_cast<ImageFrame::ColorType>(buffer->getSkBitmap().colorType()); |
+ break; |
+ } |
+ const GIFFrameContext* frameContext = m_reader->frameContext(frameToDecode); |
+ updateRequiredPreviousFrame(buffer, *frameContext); |
+ if (!isIndex8Applicable(*frameContext, buffer->requiredPreviousFrameIndex())) { |
+ calculatedOutput = ImageFrame::N32; |
+ break; |
+ } |
+ frameToDecode = buffer->requiredPreviousFrameIndex(); |
+ } |
+ return (calculatedOutput == outputType); |
+} |
+ |
} // namespace blink |