static void test_dimensions(skiatest::Reporter* r, const char path[]) { // Create the codec from the resource file SkAutoTDelete<SkStream> stream(resource(path)); if (!stream) { SkDebugf("Missing resource '%s'\n", path); return; } SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream.detach())); if (!codec) { ERRORF(r, "Unable to create codec '%s'", path); return; } // Check that the decode is successful for a variety of scales for (float scale = -0.05f; scale < 2.0f; scale += 0.05f) { // Scale the output dimensions SkISize scaledDims = codec->getScaledDimensions(scale); SkImageInfo scaledInfo = codec->getInfo().makeWH(scaledDims.width(), scaledDims.height()); // Set up for the decode size_t rowBytes = scaledDims.width() * sizeof(SkPMColor); size_t totalBytes = scaledInfo.getSafeSize(rowBytes); SkAutoTMalloc<SkPMColor> pixels(totalBytes); SkImageGenerator::Result result = codec->getPixels(scaledInfo, pixels.get(), rowBytes, NULL, NULL, NULL); REPORTER_ASSERT(r, SkImageGenerator::kSuccess == result); } }
TEST_F(DeferredImageDecoderTest, frameOpacity) { std::unique_ptr<DeferredImageDecoder> decoder = DeferredImageDecoder::create( m_data, true, ImageDecoder::AlphaPremultiplied, ColorBehavior::transformToTargetForTesting()); SkImageInfo pixInfo = SkImageInfo::MakeN32Premul(1, 1); size_t rowBytes = pixInfo.minRowBytes(); size_t size = pixInfo.getSafeSize(rowBytes); Vector<char> storage(size); SkPixmap pixmap(pixInfo, storage.data(), rowBytes); // Before decoding, the frame is not known to be opaque. sk_sp<SkImage> frame = decoder->createFrameAtIndex(0); ASSERT_TRUE(frame); EXPECT_FALSE(frame->isOpaque()); // Force a lazy decode by reading pixels. EXPECT_TRUE(frame->readPixels(pixmap, 0, 0)); // After decoding, the frame is known to be opaque. frame = decoder->createFrameAtIndex(0); ASSERT_TRUE(frame); EXPECT_TRUE(frame->isOpaque()); // Re-generating the opaque-marked frame should not fail. EXPECT_TRUE(frame->readPixels(pixmap, 0, 0)); }
bool SkImageGenerator::tryGenerateBitmap(SkBitmap* bitmap, const SkImageInfo* infoPtr) { const SkImageInfo info = infoPtr ? *infoPtr : this->getInfo(); const size_t rowBytes = info.minRowBytes(); const size_t pixelSize = info.getSafeSize(rowBytes); if (0 == pixelSize) { return false; } SkAutoFree pixelStorage(sk_malloc_flags(pixelSize, 0)); void* pixels = pixelStorage.get(); if (!pixels) { return false; } SkPMColor ctStorage[256]; int ctCount = 0; if (!this->getPixels(info, pixels, rowBytes, ctStorage, &ctCount)) { return false; } SkAutoTUnref<SkColorTable> ctable; if (ctCount > 0) { SkASSERT(kIndex_8_SkColorType == info.colorType()); ctable.reset(new SkColorTable(ctStorage, ctCount)); } else { SkASSERT(kIndex_8_SkColorType != info.colorType()); } return bitmap->installPixels(info, pixelStorage.detach(), rowBytes, ctable, release_malloc_proc, nullptr); }
static void test_dimensions(skiatest::Reporter* r, const char path[]) { // Create the codec from the resource file SkAutoTDelete<SkStream> stream(resource(path)); if (!stream) { SkDebugf("Missing resource '%s'\n", path); return; } SkAutoTDelete<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(stream.detach())); if (!codec) { ERRORF(r, "Unable to create codec '%s'", path); return; } // Check that the decode is successful for a variety of scales for (int sampleSize = 1; sampleSize < 32; sampleSize++) { // Scale the output dimensions SkISize scaledDims = codec->getSampledDimensions(sampleSize); SkImageInfo scaledInfo = codec->getInfo() .makeWH(scaledDims.width(), scaledDims.height()) .makeColorType(kN32_SkColorType); // Set up for the decode size_t rowBytes = scaledDims.width() * sizeof(SkPMColor); size_t totalBytes = scaledInfo.getSafeSize(rowBytes); SkAutoTMalloc<SkPMColor> pixels(totalBytes); SkAndroidCodec::AndroidOptions options; options.fSampleSize = sampleSize; SkCodec::Result result = codec->getAndroidPixels(scaledInfo, pixels.get(), rowBytes, &options); REPORTER_ASSERT(r, SkCodec::kSuccess == result); } }
TEST_F(DeferredImageDecoderTest, frameOpacity) { std::unique_ptr<ImageDecoder> actualDecoder = ImageDecoder::create(*m_data, ImageDecoder::AlphaPremultiplied, ImageDecoder::GammaAndColorProfileApplied); std::unique_ptr<DeferredImageDecoder> decoder = DeferredImageDecoder::createForTesting(std::move(actualDecoder)); decoder->setData(*m_data, true); SkImageInfo pixInfo = SkImageInfo::MakeN32Premul(1, 1); size_t rowBytes = pixInfo.minRowBytes(); size_t size = pixInfo.getSafeSize(rowBytes); SkAutoMalloc storage(size); SkPixmap pixmap(pixInfo, storage.get(), rowBytes); // Before decoding, the frame is not known to be opaque. RefPtr<SkImage> frame = decoder->createFrameAtIndex(0); ASSERT_TRUE(frame); EXPECT_FALSE(frame->isOpaque()); // Force a lazy decode by reading pixels. EXPECT_TRUE(frame->readPixels(pixmap, 0, 0)); // After decoding, the frame is known to be opaque. frame = decoder->createFrameAtIndex(0); ASSERT_TRUE(frame); EXPECT_TRUE(frame->isOpaque()); // Re-generating the opaque-marked frame should not fail. EXPECT_TRUE(frame->readPixels(pixmap, 0, 0)); }
size_t SkAutoPixmapStorage::AllocSize(const SkImageInfo& info, size_t* rowBytes) { size_t rb = info.minRowBytes(); if (rowBytes) { *rowBytes = rb; } return info.getSafeSize(rb); }
static void check_fill(skiatest::Reporter* r, const SkImageInfo& imageInfo, uint32_t startRow, uint32_t endRow, size_t rowBytes, uint32_t offset, uint32_t colorOrIndex) { // Calculate the total size of the image in bytes. Use the smallest possible size. // The offset value tells us to adjust the pointer from the memory we allocate in order // to test on different memory alignments. If offset is nonzero, we need to increase the // size of the memory we allocate in order to make sure that we have enough. We are // still allocating the smallest possible size. const size_t totalBytes = imageInfo.getSafeSize(rowBytes) + offset; // Create fake image data where every byte has a value of 0 SkAutoTDeleteArray<uint8_t> storage(new uint8_t[totalBytes]); memset(storage.get(), 0, totalBytes); // Adjust the pointer in order to test on different memory alignments uint8_t* imageData = storage.get() + offset; uint8_t* imageStart = imageData + rowBytes * startRow; const SkImageInfo fillInfo = imageInfo.makeWH(imageInfo.width(), endRow - startRow + 1); SkSampler::Fill(fillInfo, imageStart, rowBytes, colorOrIndex, SkCodec::kNo_ZeroInitialized); // Ensure that the pixels are filled properly // The bots should catch any memory corruption uint8_t* indexPtr = imageData + startRow * rowBytes; uint8_t* grayPtr = indexPtr; uint32_t* colorPtr = (uint32_t*) indexPtr; uint16_t* color565Ptr = (uint16_t*) indexPtr; for (uint32_t y = startRow; y <= endRow; y++) { for (int32_t x = 0; x < imageInfo.width(); x++) { switch (imageInfo.colorType()) { case kIndex_8_SkColorType: REPORTER_ASSERT(r, kFillIndex == indexPtr[x]); break; case kN32_SkColorType: REPORTER_ASSERT(r, kFillColor == colorPtr[x]); break; case kGray_8_SkColorType: REPORTER_ASSERT(r, kFillGray == grayPtr[x]); break; case kRGB_565_SkColorType: REPORTER_ASSERT(r, kFill565 == color565Ptr[x]); break; default: REPORTER_ASSERT(r, false); break; } } indexPtr += rowBytes; colorPtr = (uint32_t*) indexPtr; } }
ImagePattern::ImagePattern(PassRefPtr<Image> image, RepeatMode repeatMode) : Pattern(repeatMode) , m_tileImage(image->imageForCurrentFrame()) { if (m_tileImage) { // TODO(fmalita): mechanism to extract the actual SkImageInfo from an SkImage? const SkImageInfo info = SkImageInfo::MakeN32Premul(m_tileImage->width(), m_tileImage->height()); adjustExternalMemoryAllocated(info.getSafeSize(info.minRowBytes())); } }
DEF_TEST(ImageDataRef, reporter) { SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1); size_t rowBytes = info.minRowBytes(); size_t size = info.getSafeSize(rowBytes); SkData* data = SkData::NewUninitialized(size); REPORTER_ASSERT(reporter, data->unique()); SkImage* image = SkImage::NewRasterData(info, data, rowBytes); REPORTER_ASSERT(reporter, !data->unique()); image->unref(); REPORTER_ASSERT(reporter, data->unique()); data->unref(); }
// TODO(ccameron): ImagePattern should not draw to a globally set color space. // https://crbug.com/672306 ImagePattern::ImagePattern(PassRefPtr<Image> image, RepeatMode repeatMode) : Pattern(repeatMode), m_tileImage(image->imageForCurrentFrame( ColorBehavior::transformToGlobalTarget())) { m_previousLocalMatrix.setIdentity(); if (m_tileImage) { // TODO(fmalita): mechanism to extract the actual SkImageInfo from an // SkImage? const SkImageInfo info = SkImageInfo::MakeN32Premul( m_tileImage->width() + (isRepeatX() ? 0 : 2), m_tileImage->height() + (isRepeatY() ? 0 : 2)); adjustExternalMemoryAllocated(info.getSafeSize(info.minRowBytes())); } }
sk_sp<SkImage> SkImage::makeNonTextureImage() const { if (!this->isTextureBacked()) { return sk_ref_sp(const_cast<SkImage*>(this)); } SkImageInfo info = as_IB(this)->onImageInfo(); size_t rowBytes = info.minRowBytes(); size_t size = info.getSafeSize(rowBytes); auto data = SkData::MakeUninitialized(size); if (!data) { return nullptr; } SkPixmap pm(info, data->writable_data(), rowBytes); if (!this->readPixels(pm, 0, 0, kDisallow_CachingHint)) { return nullptr; } return MakeRasterData(info, data, rowBytes); }
sk_sp<SkPixelRef> SkMallocPixelRef::MakeWithData(const SkImageInfo& info, size_t rowBytes, sk_sp<SkColorTable> ctable, sk_sp<SkData> data) { SkASSERT(data != nullptr); if (!is_valid(info, ctable.get())) { return nullptr; } if ((rowBytes < info.minRowBytes()) || (data->size() < info.getSafeSize(rowBytes))) { return nullptr; } // must get this address before we call release void* pixels = const_cast<void*>(data->data()); SkPixelRef* pr = new SkMallocPixelRef(info, pixels, rowBytes, std::move(ctable), sk_data_releaseproc, data.release()); pr->setImmutable(); // since we were created with (immutable) data return sk_sp<SkPixelRef>(pr); }
static void test_newraster(skiatest::Reporter* reporter) { SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10); const size_t minRowBytes = info.minRowBytes(); const size_t size = info.getSafeSize(minRowBytes); SkAutoMalloc storage(size); SkPMColor* baseAddr = static_cast<SkPMColor*>(storage.get()); sk_bzero(baseAddr, size); SkCanvas* canvas = SkCanvas::NewRasterDirect(info, baseAddr, minRowBytes); REPORTER_ASSERT(reporter, canvas); SkImageInfo info2; size_t rowBytes; const SkPMColor* addr = (const SkPMColor*)canvas->peekPixels(&info2, &rowBytes); REPORTER_ASSERT(reporter, addr); REPORTER_ASSERT(reporter, info == info2); REPORTER_ASSERT(reporter, minRowBytes == rowBytes); for (int y = 0; y < info.height(); ++y) { for (int x = 0; x < info.width(); ++x) { REPORTER_ASSERT(reporter, 0 == addr[x]); } addr = (const SkPMColor*)((const char*)addr + rowBytes); } SkDELETE(canvas); // now try a deliberately bad info info = info.makeWH(-1, info.height()); REPORTER_ASSERT(reporter, NULL == SkCanvas::NewRasterDirect(info, baseAddr, minRowBytes)); // too big info = info.makeWH(1 << 30, 1 << 30); REPORTER_ASSERT(reporter, NULL == SkCanvas::NewRasterDirect(info, baseAddr, minRowBytes)); // not a valid pixel type info = SkImageInfo::Make(10, 10, kUnknown_SkColorType, info.alphaType()); REPORTER_ASSERT(reporter, NULL == SkCanvas::NewRasterDirect(info, baseAddr, minRowBytes)); // We should succeed with a zero-sized valid info info = SkImageInfo::MakeN32Premul(0, 0); canvas = SkCanvas::NewRasterDirect(info, baseAddr, minRowBytes); REPORTER_ASSERT(reporter, canvas); SkDELETE(canvas); }
static void check_fill(skiatest::Reporter* r, const SkImageInfo& imageInfo, uint32_t startRow, uint32_t endRow, size_t rowBytes, uint32_t offset, uint32_t colorOrIndex, SkPMColor* colorTable) { // Calculate the total size of the image in bytes. Use the smallest possible size. // The offset value tells us to adjust the pointer from the memory we allocate in order // to test on different memory alignments. If offset is nonzero, we need to increase the // size of the memory we allocate in order to make sure that we have enough. We are // still allocating the smallest possible size. const size_t totalBytes = imageInfo.getSafeSize(rowBytes) + offset; // Create fake image data where every byte has a value of 0 SkAutoTDeleteArray<uint8_t> storage(SkNEW_ARRAY(uint8_t, totalBytes)); memset(storage.get(), 0, totalBytes); // Adjust the pointer in order to test on different memory alignments uint8_t* imageData = storage.get() + offset; uint8_t* imageStart = imageData + rowBytes * startRow; // Fill image with the fill value starting at the indicated row SkSwizzler::Fill(imageStart, imageInfo, rowBytes, endRow - startRow + 1, colorOrIndex, colorTable); // Ensure that the pixels are filled properly // The bots should catch any memory corruption uint8_t* indexPtr = imageData + startRow * rowBytes; uint32_t* colorPtr = (uint32_t*) indexPtr; for (uint32_t y = startRow; y <= endRow; y++) { for (int32_t x = 0; x < imageInfo.width(); x++) { if (kIndex_8_SkColorType == imageInfo.colorType()) { REPORTER_ASSERT(r, kFillIndex == indexPtr[x]); } else { REPORTER_ASSERT(r, kFillColor == colorPtr[x]); } } indexPtr += rowBytes; colorPtr = (uint32_t*) indexPtr; } }
static void test_newraster(skiatest::Reporter* reporter) { SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10); const size_t minRowBytes = info.minRowBytes(); const size_t size = info.getSafeSize(minRowBytes); SkAutoTMalloc<SkPMColor> storage(size); SkPMColor* baseAddr = storage.get(); sk_bzero(baseAddr, size); std::unique_ptr<SkCanvas> canvas = SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes); REPORTER_ASSERT(reporter, canvas); SkPixmap pmap; const SkPMColor* addr = canvas->peekPixels(&pmap) ? pmap.addr32() : nullptr; REPORTER_ASSERT(reporter, addr); REPORTER_ASSERT(reporter, info == pmap.info()); REPORTER_ASSERT(reporter, minRowBytes == pmap.rowBytes()); for (int y = 0; y < info.height(); ++y) { for (int x = 0; x < info.width(); ++x) { REPORTER_ASSERT(reporter, 0 == addr[x]); } addr = (const SkPMColor*)((const char*)addr + pmap.rowBytes()); } // now try a deliberately bad info info = info.makeWH(-1, info.height()); REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes)); // too big info = info.makeWH(1 << 30, 1 << 30); REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes)); // not a valid pixel type info = SkImageInfo::Make(10, 10, kUnknown_SkColorType, info.alphaType()); REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes)); // We should succeed with a zero-sized valid info info = SkImageInfo::MakeN32Premul(0, 0); canvas = SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes); REPORTER_ASSERT(reporter, canvas); }
sk_sp<SkPixelRef> SkMallocPixelRef::MakeUsing(void*(*alloc)(size_t), const SkImageInfo& info, size_t requestedRowBytes, sk_sp<SkColorTable> ctable) { if (!is_valid(info, ctable.get())) { return nullptr; } // only want to permit 31bits of rowBytes int64_t minRB = (int64_t)info.minRowBytes64(); if (minRB < 0 || !sk_64_isS32(minRB)) { return nullptr; // allocation will be too large } if (requestedRowBytes > 0 && (int32_t)requestedRowBytes < minRB) { return nullptr; // cannot meet requested rowbytes } int32_t rowBytes; if (requestedRowBytes) { rowBytes = SkToS32(requestedRowBytes); } else { rowBytes = minRB; } int64_t bigSize = (int64_t)info.height() * rowBytes; if (!sk_64_isS32(bigSize)) { return nullptr; } size_t size = sk_64_asS32(bigSize); SkASSERT(size >= info.getSafeSize(rowBytes)); void* addr = alloc(size); if (nullptr == addr) { return nullptr; } return sk_sp<SkPixelRef>(new SkMallocPixelRef(info, addr, rowBytes, std::move(ctable), sk_free_releaseproc, nullptr)); }
bool SkImageGenerator::tryGenerateBitmap(SkBitmap* bitmap, const SkImageInfo* infoPtr, SkBitmap::Allocator* allocator) { SkImageInfo info = infoPtr ? *infoPtr : this->getInfo(); if (0 == info.getSafeSize(info.minRowBytes())) { return false; } if (!bitmap->setInfo(info)) { return reset_and_return_false(bitmap); } SkPMColor ctStorage[256]; memset(ctStorage, 0xFF, sizeof(ctStorage)); // init with opaque-white for the moment SkAutoTUnref<SkColorTable> ctable(new SkColorTable(ctStorage, 256)); if (!bitmap->tryAllocPixels(allocator, ctable)) { // SkResourceCache's custom allcator can'thandle ctables, so it may fail on // kIndex_8_SkColorTable. // skbug.com/4355 #if 1 // ignroe the allocator, and see if we can succeed without it if (!bitmap->tryAllocPixels(nullptr, ctable)) { return reset_and_return_false(bitmap); } #else // this is the up-scale technique, not fully debugged, but we keep it here at the moment // to remind ourselves that this might be better than ignoring the allocator. info = SkImageInfo::MakeN32(info.width(), info.height(), info.alphaType()); if (!bitmap->setInfo(info)) { return reset_and_return_false(bitmap); } // we pass nullptr for the ctable arg, since we are now explicitly N32 if (!bitmap->tryAllocPixels(allocator, nullptr)) { return reset_and_return_false(bitmap); } #endif } bitmap->lockPixels(); if (!bitmap->getPixels()) { return reset_and_return_false(bitmap); } int ctCount = 0; if (!this->getPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), ctStorage, &ctCount)) { return reset_and_return_false(bitmap); } if (ctCount > 0) { SkASSERT(kIndex_8_SkColorType == bitmap->colorType()); // we and bitmap should be owners SkASSERT(!ctable->unique()); // Now we need to overwrite the ctable we built earlier, with the correct colors. // This does mean that we may have made the table too big, but that cannot be avoided // until we can change SkImageGenerator's API to return us the ctable *before* we have to // allocate space for all the pixels. ctable->dangerous_overwriteColors(ctStorage, ctCount); } else { SkASSERT(kIndex_8_SkColorType != bitmap->colorType()); // we should be the only owner SkASSERT(ctable->unique()); } return true; }
SkCodec* SkIcoCodec::NewFromStream(SkStream* stream, Result* result) { // Ensure that we do not leak the input stream std::unique_ptr<SkStream> inputStream(stream); // Header size constants static const uint32_t kIcoDirectoryBytes = 6; static const uint32_t kIcoDirEntryBytes = 16; // Read the directory header std::unique_ptr<uint8_t[]> dirBuffer(new uint8_t[kIcoDirectoryBytes]); if (inputStream.get()->read(dirBuffer.get(), kIcoDirectoryBytes) != kIcoDirectoryBytes) { SkCodecPrintf("Error: unable to read ico directory header.\n"); *result = kIncompleteInput; return nullptr; } // Process the directory header const uint16_t numImages = get_short(dirBuffer.get(), 4); if (0 == numImages) { SkCodecPrintf("Error: No images embedded in ico.\n"); *result = kInvalidInput; return nullptr; } // This structure is used to represent the vital information about entries // in the directory header. We will obtain this information for each // directory entry. struct Entry { uint32_t offset; uint32_t size; }; SkAutoFree dirEntryBuffer(sk_malloc_flags(sizeof(Entry) * numImages, SK_MALLOC_TEMP)); if (!dirEntryBuffer) { SkCodecPrintf("Error: OOM allocating ICO directory for %i images.\n", numImages); *result = kInternalError; return nullptr; } auto* directoryEntries = reinterpret_cast<Entry*>(dirEntryBuffer.get()); // Iterate over directory entries for (uint32_t i = 0; i < numImages; i++) { uint8_t entryBuffer[kIcoDirEntryBytes]; if (inputStream->read(entryBuffer, kIcoDirEntryBytes) != kIcoDirEntryBytes) { SkCodecPrintf("Error: Dir entries truncated in ico.\n"); *result = kIncompleteInput; return nullptr; } // The directory entry contains information such as width, height, // bits per pixel, and number of colors in the color palette. We will // ignore these fields since they are repeated in the header of the // embedded image. In the event of an inconsistency, we would always // defer to the value in the embedded header anyway. // Specifies the size of the embedded image, including the header uint32_t size = get_int(entryBuffer, 8); // Specifies the offset of the embedded image from the start of file. // It does not indicate the start of the pixel data, but rather the // start of the embedded image header. uint32_t offset = get_int(entryBuffer, 12); // Save the vital fields directoryEntries[i].offset = offset; directoryEntries[i].size = size; } // Default Result, if no valid embedded codecs are found. *result = kInvalidInput; // It is "customary" that the embedded images will be stored in order of // increasing offset. However, the specification does not indicate that // they must be stored in this order, so we will not trust that this is the // case. Here we sort the embedded images by increasing offset. struct EntryLessThan { bool operator() (Entry a, Entry b) const { return a.offset < b.offset; } }; EntryLessThan lessThan; SkTQSort(directoryEntries, &directoryEntries[numImages - 1], lessThan); // Now will construct a candidate codec for each of the embedded images uint32_t bytesRead = kIcoDirectoryBytes + numImages * kIcoDirEntryBytes; std::unique_ptr<SkTArray<std::unique_ptr<SkCodec>, true>> codecs( new (SkTArray<std::unique_ptr<SkCodec>, true>)(numImages)); for (uint32_t i = 0; i < numImages; i++) { uint32_t offset = directoryEntries[i].offset; uint32_t size = directoryEntries[i].size; // Ensure that the offset is valid if (offset < bytesRead) { SkCodecPrintf("Warning: invalid ico offset.\n"); continue; } // If we cannot skip, assume we have reached the end of the stream and // stop trying to make codecs if (inputStream.get()->skip(offset - bytesRead) != offset - bytesRead) { SkCodecPrintf("Warning: could not skip to ico offset.\n"); break; } bytesRead = offset; // Create a new stream for the embedded codec SkAutoFree buffer(sk_malloc_flags(size, 0)); if (!buffer) { SkCodecPrintf("Warning: OOM trying to create embedded stream.\n"); break; } if (inputStream->read(buffer.get(), size) != size) { SkCodecPrintf("Warning: could not create embedded stream.\n"); *result = kIncompleteInput; break; } sk_sp<SkData> data(SkData::MakeFromMalloc(buffer.release(), size)); std::unique_ptr<SkMemoryStream> embeddedStream(new SkMemoryStream(data)); bytesRead += size; // Check if the embedded codec is bmp or png and create the codec SkCodec* codec = nullptr; Result dummyResult; if (SkPngCodec::IsPng((const char*) data->bytes(), data->size())) { codec = SkPngCodec::NewFromStream(embeddedStream.release(), &dummyResult); } else { codec = SkBmpCodec::NewFromIco(embeddedStream.release(), &dummyResult); } // Save a valid codec if (nullptr != codec) { codecs->push_back().reset(codec); } } // Recognize if there are no valid codecs if (0 == codecs->count()) { SkCodecPrintf("Error: could not find any valid embedded ico codecs.\n"); return nullptr; } // Use the largest codec as a "suggestion" for image info size_t maxSize = 0; int maxIndex = 0; for (int i = 0; i < codecs->count(); i++) { SkImageInfo info = codecs->operator[](i)->getInfo(); size_t size = info.getSafeSize(info.minRowBytes()); if (size > maxSize) { maxSize = size; maxIndex = i; } } int width = codecs->operator[](maxIndex)->getInfo().width(); int height = codecs->operator[](maxIndex)->getInfo().height(); SkEncodedInfo info = codecs->operator[](maxIndex)->getEncodedInfo(); SkColorSpace* colorSpace = codecs->operator[](maxIndex)->getInfo().colorSpace(); *result = kSuccess; // The original stream is no longer needed, because the embedded codecs own their // own streams. return new SkIcoCodec(width, height, info, codecs.release(), sk_ref_sp(colorSpace)); }
bool SkBitmapRegionCodec::decodeRegion(SkBitmap* bitmap, SkBRDAllocator* allocator, const SkIRect& desiredSubset, int sampleSize, SkColorType prefColorType, bool requireUnpremul) { // Fix the input sampleSize if necessary. if (sampleSize < 1) { sampleSize = 1; } // The size of the output bitmap is determined by the size of the // requested subset, not by the size of the intersection of the subset // and the image dimensions. // If inputX is negative, we will need to place decoded pixels into the // output bitmap starting at a left offset. Call this outX. // If outX is non-zero, subsetX must be zero. // If inputY is negative, we will need to place decoded pixels into the // output bitmap starting at a top offset. Call this outY. // If outY is non-zero, subsetY must be zero. int outX; int outY; SkIRect subset = desiredSubset; SubsetType type = adjust_subset_rect(fCodec->getInfo().dimensions(), &subset, &outX, &outY); if (SubsetType::kOutside_SubsetType == type) { return false; } // Ask the codec for a scaled subset if (!fCodec->getSupportedSubset(&subset)) { SkCodecPrintf("Error: Could not get subset.\n"); return false; } SkISize scaledSize = fCodec->getSampledSubsetDimensions(sampleSize, subset); // Create the image info for the decode SkColorType dstColorType = fCodec->computeOutputColorType(prefColorType); SkAlphaType dstAlphaType = fCodec->computeOutputAlphaType(requireUnpremul); SkImageInfo decodeInfo = fCodec->getInfo().makeWH(scaledSize.width(), scaledSize.height()) .makeColorType(dstColorType) .makeAlphaType(dstAlphaType); // Construct a color table for the decode if necessary SkAutoTUnref<SkColorTable> colorTable(nullptr); int maxColors = 256; SkPMColor colors[256]; if (kIndex_8_SkColorType == dstColorType) { colorTable.reset(new SkColorTable(colors, maxColors)); } // Initialize the destination bitmap int scaledOutX = 0; int scaledOutY = 0; int scaledOutWidth = scaledSize.width(); int scaledOutHeight = scaledSize.height(); if (SubsetType::kPartiallyInside_SubsetType == type) { scaledOutX = outX / sampleSize; scaledOutY = outY / sampleSize; // We need to be safe here because getSupportedSubset() may have modified the subset. const int extraX = SkTMax(0, desiredSubset.width() - outX - subset.width()); const int extraY = SkTMax(0, desiredSubset.height() - outY - subset.height()); const int scaledExtraX = extraX / sampleSize; const int scaledExtraY = extraY / sampleSize; scaledOutWidth += scaledOutX + scaledExtraX; scaledOutHeight += scaledOutY + scaledExtraY; } SkImageInfo outInfo = decodeInfo.makeWH(scaledOutWidth, scaledOutHeight); if (kGray_8_SkColorType == dstColorType) { // The legacy implementations of BitmapFactory and BitmapRegionDecoder // used kAlpha8 for grayscale images (before kGray8 existed). While // the codec recognizes kGray8, we need to decode into a kAlpha8 // bitmap in order to avoid a behavior change. outInfo = outInfo.makeColorType(kAlpha_8_SkColorType).makeAlphaType(kPremul_SkAlphaType); } bitmap->setInfo(outInfo); if (!bitmap->tryAllocPixels(allocator, colorTable.get())) { SkCodecPrintf("Error: Could not allocate pixels.\n"); return false; } // Zero the bitmap if the region is not completely within the image. // TODO (msarett): Can we make this faster by implementing it to only // zero parts of the image that we won't overwrite with // pixels? SkCodec::ZeroInitialized zeroInit = allocator ? allocator->zeroInit() : SkCodec::kNo_ZeroInitialized; if (SubsetType::kPartiallyInside_SubsetType == type && SkCodec::kNo_ZeroInitialized == zeroInit) { void* pixels = bitmap->getPixels(); size_t bytes = outInfo.getSafeSize(bitmap->rowBytes()); memset(pixels, 0, bytes); } // Decode into the destination bitmap SkAndroidCodec::AndroidOptions options; options.fSampleSize = sampleSize; options.fSubset = ⊂ options.fColorPtr = colors; options.fColorCount = &maxColors; options.fZeroInitialized = zeroInit; void* dst = bitmap->getAddr(scaledOutX, scaledOutY); SkCodec::Result result = fCodec->getAndroidPixels(decodeInfo, dst, bitmap->rowBytes(), &options); if (SkCodec::kSuccess != result && SkCodec::kIncompleteInput != result) { SkCodecPrintf("Error: Could not get pixels.\n"); return false; } // Intialize the color table if (kIndex_8_SkColorType == dstColorType) { colorTable->dangerous_overwriteColors(colors, maxColors); } return true; }