TEST(GIFImageDecoderTest, resumePartialDecodeAfterClearFrameBufferCache)
{
    RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources/animated-10color.gif");
    ASSERT_TRUE(fullData.get());
    Vector<unsigned> baselineHashes;
    createDecodingBaseline(fullData.get(), &baselineHashes);
    size_t frameCount = baselineHashes.size();

    OwnPtr<GIFImageDecoder> decoder = createDecoder();

    // Let frame 0 be partially decoded.
    size_t partialSize = 1;
    do {
        RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), partialSize);
        decoder->setData(data.get(), false);
        ++partialSize;
    } while (!decoder->frameCount() || decoder->frameBufferAtIndex(0)->status() == ImageFrame::FrameEmpty);

    // Skip to the last frame and clear.
    decoder->setData(fullData.get(), true);
    EXPECT_EQ(frameCount, decoder->frameCount());
    ImageFrame* lastFrame = decoder->frameBufferAtIndex(frameCount - 1);
    EXPECT_EQ(baselineHashes[frameCount - 1], hashSkBitmap(lastFrame->getSkBitmap()));
    decoder->clearCacheExceptFrame(kNotFound);

    // Resume decoding of the first frame.
    ImageFrame* firstFrame = decoder->frameBufferAtIndex(0);
    EXPECT_EQ(ImageFrame::FrameComplete, firstFrame->status());
    EXPECT_EQ(baselineHashes[0], hashSkBitmap(firstFrame->getSkBitmap()));
}
TEST(GIFImageDecoderTest, progressiveDecode)
{
    RefPtr<SharedBuffer> fullData = readFile("/Source/web/tests/data/radient.gif");
    ASSERT_TRUE(fullData.get());
    const size_t fullLength = fullData->size();

    OwnPtr<GIFImageDecoder> decoder;
    ImageFrame* frame;

    Vector<unsigned> truncatedHashes;
    Vector<unsigned> progressiveHashes;

    // Compute hashes when the file is truncated.
    const size_t increment = 1;
    for (size_t i = 1; i <= fullLength; i += increment) {
        decoder = createDecoder();
        RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), i);
        decoder->setData(data.get(), i == fullLength);
        frame = decoder->frameBufferAtIndex(0);
        if (!frame) {
            truncatedHashes.append(0);
            continue;
        }
        truncatedHashes.append(hashSkBitmap(frame->getSkBitmap()));
    }

    // Compute hashes when the file is progressively decoded.
    decoder = createDecoder();
    EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount());
    for (size_t i = 1; i <= fullLength; i += increment) {
        RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), i);
        decoder->setData(data.get(), i == fullLength);
        frame = decoder->frameBufferAtIndex(0);
        if (!frame) {
            progressiveHashes.append(0);
            continue;
        }
        progressiveHashes.append(hashSkBitmap(frame->getSkBitmap()));
    }
    EXPECT_EQ(cAnimationNone, decoder->repetitionCount());

    bool match = true;
    for (size_t i = 0; i < truncatedHashes.size(); ++i) {
        if (truncatedHashes[i] != progressiveHashes[i]) {
            match = false;
            break;
        }
    }
    EXPECT_TRUE(match);
}
void downsample(size_t maxDecodedBytes, unsigned* outputWidth, unsigned* outputHeight, const char* imageFilePath)
{
    RefPtr<SharedBuffer> data = readFile(imageFilePath);
    ASSERT_TRUE(data.get());

    OwnPtr<JPEGImageDecoder> decoder = createDecoder(maxDecodedBytes);
    decoder->setData(data.get(), true);

    ImageFrame* frame = decoder->frameBufferAtIndex(0);
    ASSERT_TRUE(frame);
    *outputWidth = frame->getSkBitmap().width();
    *outputHeight = frame->getSkBitmap().height();
    EXPECT_EQ(IntSize(*outputWidth, *outputHeight), decoder->decodedSize());
}
TEST(AnimatedWebPTests, uniqueGenerationIDs)
{
    OwnPtr<WEBPImageDecoder> decoder = createDecoder();

    RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/webp-animated.webp");
    ASSERT_TRUE(data.get());
    decoder->setData(data.get(), true);

    ImageFrame* frame = decoder->frameBufferAtIndex(0);
    uint32_t generationID0 = frame->getSkBitmap().getGenerationID();
    frame = decoder->frameBufferAtIndex(1);
    uint32_t generationID1 = frame->getSkBitmap().getGenerationID();

    EXPECT_TRUE(generationID0 != generationID1);
}
TEST(AnimatedWebPTests, progressiveDecode)
{
    RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources/webp-animated.webp");
    ASSERT_TRUE(fullData.get());
    const size_t fullLength = fullData->size();

    OwnPtr<WEBPImageDecoder>  decoder;
    ImageFrame* frame;

    Vector<unsigned> truncatedHashes;
    Vector<unsigned> progressiveHashes;

    // Compute hashes when the file is truncated.
    const size_t increment = 1;
    for (size_t i = 1; i <= fullLength; i += increment) {
        decoder = createDecoder();
        RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), i);
        decoder->setData(data.get(), i == fullLength);
        frame = decoder->frameBufferAtIndex(0);
        if (!frame) {
            truncatedHashes.append(0);
            continue;
        }
        truncatedHashes.append(hashSkBitmap(frame->getSkBitmap()));
    }

    // Compute hashes when the file is progressively decoded.
    decoder = createDecoder();
    for (size_t i = 1; i <= fullLength; i += increment) {
        RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), i);
        decoder->setData(data.get(), i == fullLength);
        frame = decoder->frameBufferAtIndex(0);
        if (!frame) {
            progressiveHashes.append(0);
            continue;
        }
        progressiveHashes.append(hashSkBitmap(frame->getSkBitmap()));
    }

    bool match = true;
    for (size_t i = 0; i < truncatedHashes.size(); ++i) {
        if (truncatedHashes[i] != progressiveHashes[i]) {
            match = false;
            break;
        }
    }
    EXPECT_TRUE(match);
}
Beispiel #6
0
const ScaledImageFragment* ImageFrameGenerator::tryToDecodeAndScale(const SkISize& scaledSize)
{
    RefPtr<SharedBuffer> data;
    bool allDataReceived = false;
    {
        MutexLocker lock(m_dataMutex);

        // FIXME: We should do a shallow copy instead. Now we're restricted by the API of SharedBuffer.
        data = m_data->copy();
        allDataReceived = m_allDataReceived;
    }

    OwnPtr<ImageDecoder> decoder(adoptPtr(ImageDecoder::create(*data.get(), ImageSource::AlphaPremultiplied, ImageSource::GammaAndColorProfileApplied)));
    if (!decoder && m_imageDecoderFactory)
        decoder = m_imageDecoderFactory->create();
    if (!decoder)
        return 0;

    decoder->setData(data.get(), allDataReceived);
    ImageFrame* frame = decoder->frameBufferAtIndex(0);
    if (!frame || frame->status() == ImageFrame::FrameEmpty)
        return 0;

    bool isComplete = frame->status() == ImageFrame::FrameComplete;
    SkBitmap fullSizeBitmap = frame->getSkBitmap();
    ASSERT(fullSizeBitmap.width() == m_fullSize.width() && fullSizeBitmap.height() == m_fullSize.height());

    const ScaledImageFragment* fullSizeImage = ImageDecodingStore::instance()->insertAndLockCache(
        this, ScaledImageFragment::create(m_fullSize, fullSizeBitmap, isComplete));

    if (m_fullSize == scaledSize)
        return fullSizeImage;
    return tryToScale(fullSizeImage, scaledSize);
}
TEST(BMPImageDecoderTest, parseAndDecode)
{
    const char* bmpFile = "/LayoutTests/fast/images/resources/lenna.bmp"; // 256x256
    RefPtr<SharedBuffer> data = readFile(bmpFile);
    ASSERT_TRUE(data.get());

    OwnPtr<ImageDecoder> decoder = createDecoder();
    decoder->setData(data.get(), true);

    ImageFrame* frame = decoder->frameBufferAtIndex(0);
    ASSERT_TRUE(frame);
    EXPECT_EQ(ImageFrame::FrameComplete, frame->status());
    EXPECT_EQ(256, frame->getSkBitmap().width());
    EXPECT_EQ(256, frame->getSkBitmap().height());
    EXPECT_FALSE(decoder->failed());
}
bool ImageFrameGenerator::decode(size_t index, ImageDecoder** decoder, SkBitmap* bitmap)
{
    TRACE_EVENT2("blink", "ImageFrameGenerator::decode", "width", m_fullSize.width(), "height", m_fullSize.height());

    ASSERT(decoder);
    SharedBuffer* data = 0;
    bool allDataReceived = false;
    bool newDecoder = false;
    m_data.data(&data, &allDataReceived);

    // Try to create an ImageDecoder if we are not given one.
    if (!*decoder) {
        newDecoder = true;
        if (m_imageDecoderFactory)
            *decoder = m_imageDecoderFactory->create().leakPtr();

        if (!*decoder)
            *decoder = ImageDecoder::create(*data, ImageSource::AlphaPremultiplied, ImageSource::GammaAndColorProfileApplied).leakPtr();

        if (!*decoder)
            return false;
    }

    if (!m_isMultiFrame && newDecoder && allDataReceived) {
        // If we're using an external memory allocator that means we're decoding
        // directly into the output memory and we can save one memcpy.
        ASSERT(m_externalAllocator.get());
        (*decoder)->setMemoryAllocator(m_externalAllocator.get());
    }
    (*decoder)->setData(data, allDataReceived);

    ImageFrame* frame = (*decoder)->frameBufferAtIndex(index);
    // For multi-frame image decoders, we need to know how many frames are
    // in that image in order to release the decoder when all frames are
    // decoded. frameCount() is reliable only if all data is received and set in
    // decoder, particularly with GIF.
    if (allDataReceived)
        m_frameCount = (*decoder)->frameCount();

    (*decoder)->setData(0, false); // Unref SharedBuffer from ImageDecoder.
    (*decoder)->clearCacheExceptFrame(index);
    (*decoder)->setMemoryAllocator(0);

    if (!frame || frame->status() == ImageFrame::FrameEmpty)
        return false;

    // A cache object is considered complete if we can decode a complete frame.
    // Or we have received all data. The image might not be fully decoded in
    // the latter case.
    const bool isDecodeComplete = frame->status() == ImageFrame::FrameComplete || allDataReceived;
    SkBitmap fullSizeBitmap = frame->getSkBitmap();
    if (!fullSizeBitmap.isNull())
    {
        ASSERT(fullSizeBitmap.width() == m_fullSize.width() && fullSizeBitmap.height() == m_fullSize.height());
        setHasAlpha(index, !fullSizeBitmap.isOpaque());
    }
    *bitmap = fullSizeBitmap;
    return isDecodeComplete;
}
TEST(GIFImageDecoderTest, badTerminator)
{
    RefPtr<SharedBuffer> referenceData = readFile("/Source/web/tests/data/radient.gif");
    RefPtr<SharedBuffer> testData = readFile("/Source/web/tests/data/radient-bad-terminator.gif");
    ASSERT_TRUE(referenceData.get());
    ASSERT_TRUE(testData.get());

    OwnPtr<GIFImageDecoder> referenceDecoder(createDecoder());
    referenceDecoder->setData(referenceData.get(), true);
    EXPECT_EQ(1u, referenceDecoder->frameCount());
    ImageFrame* referenceFrame = referenceDecoder->frameBufferAtIndex(0);
    ASSERT(referenceFrame);

    OwnPtr<GIFImageDecoder> testDecoder(createDecoder());
    testDecoder->setData(testData.get(), true);
    EXPECT_EQ(1u, testDecoder->frameCount());
    ImageFrame* testFrame = testDecoder->frameBufferAtIndex(0);
    ASSERT(testFrame);

    EXPECT_EQ(hashSkBitmap(referenceFrame->getSkBitmap()), hashSkBitmap(testFrame->getSkBitmap()));
}
TEST(GIFImageDecoderTest, decodeTwoFrames)
{
    OwnPtr<GIFImageDecoder> decoder = createDecoder();

    RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/animated.gif");
    ASSERT_TRUE(data.get());
    decoder->setData(data.get(), true);
    EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount());

    ImageFrame* frame = decoder->frameBufferAtIndex(0);
    uint32_t generationID0 = frame->getSkBitmap().getGenerationID();
    EXPECT_EQ(ImageFrame::FrameComplete, frame->status());
    EXPECT_EQ(16, frame->getSkBitmap().width());
    EXPECT_EQ(16, frame->getSkBitmap().height());

    frame = decoder->frameBufferAtIndex(1);
    uint32_t generationID1 = frame->getSkBitmap().getGenerationID();
    EXPECT_EQ(ImageFrame::FrameComplete, frame->status());
    EXPECT_EQ(16, frame->getSkBitmap().width());
    EXPECT_EQ(16, frame->getSkBitmap().height());
    EXPECT_TRUE(generationID0 != generationID1);

    EXPECT_EQ(2u, decoder->frameCount());
    EXPECT_EQ(cAnimationLoopInfinite, decoder->repetitionCount());
}
static bool decodeBitmap(const void* data, size_t length, SkBitmap* result)
{
    RefPtr<SharedBuffer> buffer = SharedBuffer::create(static_cast<const char*>(data), length);
    OwnPtr<ImageDecoder> imageDecoder = ImageDecoder::create(*buffer, ImageDecoder::AlphaPremultiplied, ImageDecoder::GammaAndColorProfileIgnored);
    if (!imageDecoder)
        return false;
    imageDecoder->setData(buffer.get(), true);
    ImageFrame* frame = imageDecoder->frameBufferAtIndex(0);
    if (!frame)
        return true;
    *result = frame->getSkBitmap();
    return true;
}
TEST_F(DeferredImageDecoderTest, singleFrameImageLoading)
{
    m_status = ImageFrame::FramePartial;
    m_lazyDecoder->setData(*m_data, false);
    EXPECT_FALSE(m_lazyDecoder->frameIsCompleteAtIndex(0));
    ImageFrame* frame = m_lazyDecoder->frameBufferAtIndex(0);
    unsigned firstId = frame->getSkBitmap().getGenerationID();
    EXPECT_EQ(ImageFrame::FramePartial, frame->status());
    EXPECT_TRUE(m_actualDecoder);

    m_status = ImageFrame::FrameComplete;
    m_data->append(" ", 1);
    m_lazyDecoder->setData(*m_data, true);
    EXPECT_FALSE(m_actualDecoder);
    EXPECT_TRUE(m_lazyDecoder->frameIsCompleteAtIndex(0));
    frame = m_lazyDecoder->frameBufferAtIndex(0);
    unsigned secondId = frame->getSkBitmap().getGenerationID();
    EXPECT_EQ(ImageFrame::FrameComplete, frame->status());
    EXPECT_FALSE(m_frameBufferRequestCount);
    EXPECT_NE(firstId, secondId);

    EXPECT_EQ(secondId, m_lazyDecoder->frameBufferAtIndex(0)->getSkBitmap().getGenerationID());
}
TEST(GIFImageDecoderTest, parseAndDecode)
{
    OwnPtr<GIFImageDecoder> decoder = createDecoder();

    RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/animated.gif");
    ASSERT_TRUE(data.get());
    decoder->setData(data.get(), true);
    EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount());

    // This call will parse the entire file.
    EXPECT_EQ(2u, decoder->frameCount());

    ImageFrame* frame = decoder->frameBufferAtIndex(0);
    EXPECT_EQ(ImageFrame::FrameComplete, frame->status());
    EXPECT_EQ(16, frame->getSkBitmap().width());
    EXPECT_EQ(16, frame->getSkBitmap().height());

    frame = decoder->frameBufferAtIndex(1);
    EXPECT_EQ(ImageFrame::FrameComplete, frame->status());
    EXPECT_EQ(16, frame->getSkBitmap().width());
    EXPECT_EQ(16, frame->getSkBitmap().height());
    EXPECT_EQ(cAnimationLoopInfinite, decoder->repetitionCount());
}
Beispiel #14
0
PassOwnPtr<ScaledImageFragment> ImageFrameGenerator::decode(ImageDecoder** decoder)
{
    TRACE_EVENT2("webkit", "ImageFrameGenerator::decode", "width", m_fullSize.width(), "height", m_fullSize.height());

    ASSERT(decoder);
    SharedBuffer* data = 0;
    bool allDataReceived = false;
    m_data.data(&data, &allDataReceived);

    // Try to create an ImageDecoder if we are not given one.
    if (!*decoder) {
        *decoder = ImageDecoder::create(*data, ImageSource::AlphaPremultiplied, ImageSource::GammaAndColorProfileApplied).leakPtr();

        if (!*decoder && m_imageDecoderFactory)
            *decoder = m_imageDecoderFactory->create().leakPtr();

        if (!*decoder)
            return nullptr;
    }

    // TODO: this is very ugly. We need to refactor the way how we can pass a
    // memory allocator to image decoders.
    (*decoder)->setMemoryAllocator(&m_allocator);
    (*decoder)->setData(data, allDataReceived);
    // If this call returns a newly allocated DiscardablePixelRef, then
    // ImageFrame::m_bitmap and the contained DiscardablePixelRef are locked.
    // They will be unlocked when ImageDecoder is destroyed since ImageDecoder
    // owns the ImageFrame. Partially decoded SkBitmap is thus inserted into the
    // ImageDecodingStore while locked.
    ImageFrame* frame = (*decoder)->frameBufferAtIndex(0);
    (*decoder)->setData(0, false); // Unref SharedBuffer from ImageDecoder.

    if (!frame || frame->status() == ImageFrame::FrameEmpty)
        return nullptr;

    bool isComplete = frame->status() == ImageFrame::FrameComplete;
    SkBitmap fullSizeBitmap = frame->getSkBitmap();
    {
        MutexLocker lock(m_alphaMutex);
        m_hasAlpha = !fullSizeBitmap.isOpaque();
    }
    ASSERT(fullSizeBitmap.width() == m_fullSize.width() && fullSizeBitmap.height() == m_fullSize.height());

    return ScaledImageFragment::create(m_fullSize, fullSizeBitmap, isComplete);
}
void CanvasImageDecoder::OnDataComplete() {
  OwnPtr<ImageDecoder> decoder =
      ImageDecoder::create(*buffer_.get(), ImageSource::AlphaPremultiplied,
                           ImageSource::GammaAndColorProfileIgnored);
  // decoder can be null if the buffer we was empty and we couldn't even guess
  // what type of image to decode.
  if (!decoder) {
    callback_->handleEvent(nullptr);
    return;
  }
  decoder->setData(buffer_.get(), true);
  if (decoder->failed() || decoder->frameCount() == 0) {
    callback_->handleEvent(nullptr);
    return;
  }

  RefPtr<CanvasImage> resultImage = CanvasImage::create();
  ImageFrame* imageFrame = decoder->frameBufferAtIndex(0);
  RefPtr<SkImage> skImage = adoptRef(SkImage::NewFromBitmap(imageFrame->getSkBitmap()));
  resultImage->setImage(skImage.release());
  callback_->handleEvent(resultImage.get());
}
PassOwnPtr<ScaledImageFragment> ImageFrameGenerator::decode(size_t index, ImageDecoder** decoder)
{
    TRACE_EVENT2("webkit", "ImageFrameGenerator::decode", "width", m_fullSize.width(), "height", m_fullSize.height());

    ASSERT(decoder);
    SharedBuffer* data = 0;
    bool allDataReceived = false;
    m_data.data(&data, &allDataReceived);

    // Try to create an ImageDecoder if we are not given one.
    if (!*decoder) {
        if (m_imageDecoderFactory)
            *decoder = m_imageDecoderFactory->create().leakPtr();

        if (!*decoder)
            *decoder = ImageDecoder::create(*data, ImageSource::AlphaPremultiplied, ImageSource::GammaAndColorProfileApplied).leakPtr();

        if (!*decoder)
            return nullptr;
    }

    // TODO: this is very ugly. We need to refactor the way how we can pass a
    // memory allocator to image decoders.
    if (!m_isMultiFrame)
        (*decoder)->setMemoryAllocator(&m_allocator);
    (*decoder)->setData(data, allDataReceived);
    // If this call returns a newly allocated DiscardablePixelRef, then
    // ImageFrame::m_bitmap and the contained DiscardablePixelRef are locked.
    // They will be unlocked when ImageDecoder is destroyed since ImageDecoder
    // owns the ImageFrame. Partially decoded SkBitmap is thus inserted into the
    // ImageDecodingStore while locked.
    ImageFrame* frame = (*decoder)->frameBufferAtIndex(index);
    (*decoder)->setData(0, false); // Unref SharedBuffer from ImageDecoder.
    (*decoder)->clearCacheExceptFrame(index);

    if (!frame || frame->status() == ImageFrame::FrameEmpty)
        return nullptr;

    const bool isComplete = frame->status() == ImageFrame::FrameComplete;
    SkBitmap fullSizeBitmap = frame->getSkBitmap();
    {
        MutexLocker lock(m_alphaMutex);
        if (index >= m_hasAlpha.size()) {
            const size_t oldSize = m_hasAlpha.size();
            m_hasAlpha.resize(index + 1);
            for (size_t i = oldSize; i < m_hasAlpha.size(); ++i)
                m_hasAlpha[i] = true;
        }
        m_hasAlpha[index] = !fullSizeBitmap.isOpaque();
    }
    ASSERT(fullSizeBitmap.width() == m_fullSize.width() && fullSizeBitmap.height() == m_fullSize.height());

    if (isComplete)
        return ScaledImageFragment::createComplete(m_fullSize, index, fullSizeBitmap);

    // If the image is partial we need to return a copy. This is to avoid future
    // decode operations writing to the same bitmap.
    SkBitmap copyBitmap;
    fullSizeBitmap.copyTo(&copyBitmap, fullSizeBitmap.config(), &m_allocator);
    return ScaledImageFragment::createPartial(m_fullSize, index, nextGenerationId(), copyBitmap);
}