static void check(skiatest::Reporter* r, const char path[], SkISize size, bool supportsScanlineDecoding, bool supportsSubsetDecoding) { 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 decode '%s'", path); return; } // This test is used primarily to verify rewinding works properly. Using kN32 allows // us to test this without the added overhead of creating different bitmaps depending // on the color type (ex: building a color table for kIndex8). DM is where we test // decodes to all possible destination color types. SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType); REPORTER_ASSERT(r, info.dimensions() == size); SkBitmap bm; bm.allocPixels(info); SkAutoLockPixels autoLockPixels(bm); SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes(), NULL, NULL, NULL); REPORTER_ASSERT(r, result == SkCodec::kSuccess); SkMD5::Digest digest; md5(bm, &digest); bm.eraseColor(SK_ColorYELLOW); result = codec->getPixels(info, bm.getPixels(), bm.rowBytes(), NULL, NULL, NULL); REPORTER_ASSERT(r, result == SkCodec::kSuccess); // verify that re-decoding gives the same result. compare_to_good_digest(r, digest, bm); stream.reset(resource(path)); SkAutoTDelete<SkScanlineDecoder> scanlineDecoder( SkScanlineDecoder::NewFromStream(stream.detach())); if (supportsScanlineDecoding) { bm.eraseColor(SK_ColorYELLOW); REPORTER_ASSERT(r, scanlineDecoder); REPORTER_ASSERT(r, scanlineDecoder->start(info) == SkCodec::kSuccess); for (int y = 0; y < info.height(); y++) { result = scanlineDecoder->getScanlines(bm.getAddr(0, y), 1, 0); REPORTER_ASSERT(r, result == SkCodec::kSuccess); } // verify that scanline decoding gives the same result. compare_to_good_digest(r, digest, bm); } else { REPORTER_ASSERT(r, !scanlineDecoder); } // The rest of this function tests decoding subsets, and will decode an arbitrary number of // random subsets. // Do not attempt to decode subsets of an image of only once pixel, since there is no // meaningful subset. if (size.width() * size.height() == 1) { return; } SkRandom rand; SkIRect subset; SkCodec::Options opts; opts.fSubset = ⊂ for (int i = 0; i < 5; i++) { subset = generate_random_subset(&rand, size.width(), size.height()); SkASSERT(!subset.isEmpty()); const bool supported = codec->getValidSubset(&subset); REPORTER_ASSERT(r, supported == supportsSubsetDecoding); SkImageInfo subsetInfo = info.makeWH(subset.width(), subset.height()); SkBitmap bm; bm.allocPixels(subsetInfo); const SkCodec::Result result = codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes(), &opts, NULL, NULL); if (supportsSubsetDecoding) { REPORTER_ASSERT(r, result == SkCodec::kSuccess); // Webp is the only codec that supports subsets, and it will have modified the subset // to have even left/top. REPORTER_ASSERT(r, SkIsAlign2(subset.fLeft) && SkIsAlign2(subset.fTop)); } else { // No subsets will work. REPORTER_ASSERT(r, result == SkCodec::kUnimplemented); } } }
DEF_TEST(Serialization, reporter) { // Test matrix serialization { SkMatrix matrix = SkMatrix::I(); TestObjectSerialization(&matrix, reporter); } // Test path serialization { SkPath path; TestObjectSerialization(&path, reporter); } // Test region serialization { SkRegion region; TestObjectSerialization(®ion, reporter); } // Test xfermode serialization { TestXfermodeSerialization(reporter); } // Test color filter serialization { TestColorFilterSerialization(reporter); } // Test string serialization { SkString string("string"); TestObjectSerializationNoAlign<SkString, false>(&string, reporter); TestObjectSerializationNoAlign<SkString, true>(&string, reporter); } // Test rrect serialization { // SkRRect does not initialize anything. // An uninitialized SkRRect can be serialized, // but will branch on uninitialized data when deserialized. SkRRect rrect; SkRect rect = SkRect::MakeXYWH(1, 2, 20, 30); SkVector corners[4] = { {1, 2}, {2, 3}, {3,4}, {4,5} }; rrect.setRectRadii(rect, corners); TestAlignment(&rrect, reporter); } // Test readByteArray { unsigned char data[kArraySize] = { 1, 2, 3 }; TestArraySerialization(data, reporter); } // Test readColorArray { SkColor data[kArraySize] = { SK_ColorBLACK, SK_ColorWHITE, SK_ColorRED }; TestArraySerialization(data, reporter); } // Test readIntArray { int32_t data[kArraySize] = { 1, 2, 4, 8 }; TestArraySerialization(data, reporter); } // Test readPointArray { SkPoint data[kArraySize] = { {6, 7}, {42, 128} }; TestArraySerialization(data, reporter); } // Test readScalarArray { SkScalar data[kArraySize] = { SK_Scalar1, SK_ScalarHalf, SK_ScalarMax }; TestArraySerialization(data, reporter); } // Test invalid deserializations { SkImageInfo info = SkImageInfo::MakeN32Premul(kBitmapSize, kBitmapSize); SkBitmap validBitmap; validBitmap.setInfo(info); // Create a bitmap with a really large height SkBitmap invalidBitmap; invalidBitmap.setInfo(info.makeWH(info.width(), 1000000000)); // The deserialization should succeed, and the rendering shouldn't crash, // even when the device fails to initialize, due to its size TestBitmapSerialization(validBitmap, invalidBitmap, true, reporter); } // Test simple SkPicture serialization { SkPictureRecorder recorder; draw_something(recorder.beginRecording(SkIntToScalar(kBitmapSize), SkIntToScalar(kBitmapSize), nullptr, 0)); SkAutoTUnref<SkPicture> pict(recorder.endRecording()); // Serialize picture SkWriteBuffer writer(SkWriteBuffer::kValidation_Flag); pict->flatten(writer); size_t size = writer.bytesWritten(); SkAutoTMalloc<unsigned char> data(size); writer.writeToMemory(static_cast<void*>(data.get())); // Deserialize picture SkValidatingReadBuffer reader(static_cast<void*>(data.get()), size); SkAutoTUnref<SkPicture> readPict( SkPicture::CreateFromBuffer(reader)); REPORTER_ASSERT(reporter, readPict.get()); } TestPictureTypefaceSerialization(reporter); }
GrPixelConfig SkImageInfo2GrPixelConfig(const SkImageInfo& info, const GrCaps& caps) { return SkImageInfo2GrPixelConfig(info.colorType(), info.colorSpace(), caps); }
SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, const Options* options, SkPMColor ctable[], int* ctableCount) { if (kUnknown_SkColorType == info.colorType()) { return kInvalidConversion; } if (nullptr == pixels) { return kInvalidParameters; } if (rowBytes < info.minRowBytes()) { return kInvalidParameters; } if (kIndex_8_SkColorType == info.colorType()) { if (nullptr == ctable || nullptr == ctableCount) { return kInvalidParameters; } } else { if (ctableCount) { *ctableCount = 0; } ctableCount = nullptr; ctable = nullptr; } if (!this->rewindIfNeeded()) { return kCouldNotRewind; } // Default options. Options optsStorage; if (nullptr == options) { options = &optsStorage; } else if (options->fSubset) { SkIRect subset(*options->fSubset); if (!this->onGetValidSubset(&subset) || subset != *options->fSubset) { // FIXME: How to differentiate between not supporting subset at all // and not supporting this particular subset? return kUnimplemented; } } // FIXME: Support subsets somehow? Note that this works for SkWebpCodec // because it supports arbitrary scaling/subset combinations. if (!this->dimensionsSupported(info.dimensions())) { return kInvalidScale; } // On an incomplete decode, the subclass will specify the number of scanlines that it decoded // successfully. int rowsDecoded = 0; const Result result = this->onGetPixels(info, pixels, rowBytes, *options, ctable, ctableCount, &rowsDecoded); if ((kIncompleteInput == result || kSuccess == result) && ctableCount) { SkASSERT(*ctableCount >= 0 && *ctableCount <= 256); } // A return value of kIncompleteInput indicates a truncated image stream. // In this case, we will fill any uninitialized memory with a default value. // Some subclasses will take care of filling any uninitialized memory on // their own. They indicate that all of the memory has been filled by // setting rowsDecoded equal to the height. if (kIncompleteInput == result && rowsDecoded != info.height()) { this->fillIncompleteImage(info, pixels, rowBytes, options->fZeroInitialized, info.height(), rowsDecoded); } return result; }
static SkImageInfo validate_info(const SkImageInfo& info) { SkAlphaType newAlphaType = info.alphaType(); SkAssertResult(SkColorTypeValidateAlphaType(info.colorType(), info.alphaType(), &newAlphaType)); return info.makeAlphaType(newAlphaType); }
size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& proxy, const DeferredTextureImageUsageParams params[], int paramCnt, void* buffer, SkColorSpace* dstColorSpace) const { // Extract relevant min/max values from the params array. int lowestPreScaleMipLevel = params[0].fPreScaleMipLevel; SkFilterQuality highestFilterQuality = params[0].fQuality; bool useMipMaps = should_use_mip_maps(params[0]); for (int i = 1; i < paramCnt; ++i) { if (lowestPreScaleMipLevel > params[i].fPreScaleMipLevel) lowestPreScaleMipLevel = params[i].fPreScaleMipLevel; if (highestFilterQuality < params[i].fQuality) highestFilterQuality = params[i].fQuality; useMipMaps |= should_use_mip_maps(params[i]); } const bool fillMode = SkToBool(buffer); if (fillMode && !SkIsAlign8(reinterpret_cast<intptr_t>(buffer))) { return 0; } // Calculate scaling parameters. bool isScaled = lowestPreScaleMipLevel != 0; SkISize scaledSize; if (isScaled) { // SkMipMap::ComputeLevelSize takes an index into an SkMipMap. SkMipMaps don't contain the // base level, so to get an SkMipMap index we must subtract one from the GL MipMap level. scaledSize = SkMipMap::ComputeLevelSize(this->width(), this->height(), lowestPreScaleMipLevel - 1); } else { scaledSize = SkISize::Make(this->width(), this->height()); } // We never want to scale at higher than SW medium quality, as SW medium matches GPU high. SkFilterQuality scaleFilterQuality = highestFilterQuality; if (scaleFilterQuality > kMedium_SkFilterQuality) { scaleFilterQuality = kMedium_SkFilterQuality; } const int maxTextureSize = proxy.fCaps->maxTextureSize(); if (scaledSize.width() > maxTextureSize || scaledSize.height() > maxTextureSize) { return 0; } SkAutoPixmapStorage pixmap; SkImageInfo info; size_t pixelSize = 0; if (!isScaled && this->peekPixels(&pixmap) && !pixmap.ctable()) { info = pixmap.info(); pixelSize = SkAlign8(pixmap.getSafeSize()); } else { // Here we're just using presence of data to know whether there is a codec behind the image. // In the future we will access the cacherator and get the exact data that we want to (e.g. // yuv planes) upload. sk_sp<SkData> data(this->refEncoded()); if (!data && !this->peekPixels(nullptr)) { return 0; } if (SkImageCacherator* cacher = as_IB(this)->peekCacherator()) { // Generator backed image. Tweak info to trigger correct kind of decode. SkImageCacherator::CachedFormat cacheFormat = cacher->chooseCacheFormat( dstColorSpace, proxy.fCaps.get()); info = cacher->buildCacheInfo(cacheFormat).makeWH(scaledSize.width(), scaledSize.height()); } else { info = as_IB(this)->onImageInfo().makeWH(scaledSize.width(), scaledSize.height()); } if (kIndex_8_SkColorType == info.colorType()) { // Force Index8 to be N32 instead. Index8 is unsupported in Ganesh. info = info.makeColorType(kN32_SkColorType); } pixelSize = SkAlign8(SkAutoPixmapStorage::AllocSize(info, nullptr)); if (fillMode) { pixmap.alloc(info); if (isScaled) { if (!this->scalePixels(pixmap, scaleFilterQuality, SkImage::kDisallow_CachingHint)) { return 0; } } else { if (!this->readPixels(pixmap, 0, 0, SkImage::kDisallow_CachingHint)) { return 0; } } SkASSERT(!pixmap.ctable()); } } int mipMapLevelCount = 1; if (useMipMaps) { // SkMipMap only deals with the mipmap levels it generates, which does // not include the base level. // That means it generates and holds levels 1-x instead of 0-x. // So the total mipmap level count is 1 more than what // SkMipMap::ComputeLevelCount returns. mipMapLevelCount = SkMipMap::ComputeLevelCount(scaledSize.width(), scaledSize.height()) + 1; // We already initialized pixelSize to the size of the base level. // SkMipMap will generate the extra mipmap levels. Their sizes need to // be added to the total. // Index 0 here does not refer to the base mipmap level -- it is // SkMipMap's first generated mipmap level (level 1). for (int currentMipMapLevelIndex = mipMapLevelCount - 2; currentMipMapLevelIndex >= 0; currentMipMapLevelIndex--) { SkISize mipSize = SkMipMap::ComputeLevelSize(scaledSize.width(), scaledSize.height(), currentMipMapLevelIndex); SkImageInfo mipInfo = info.makeWH(mipSize.fWidth, mipSize.fHeight); pixelSize += SkAlign8(SkAutoPixmapStorage::AllocSize(mipInfo, nullptr)); } } size_t size = 0; size_t dtiSize = SkAlign8(sizeof(DeferredTextureImage)); size += dtiSize; size += (mipMapLevelCount - 1) * sizeof(MipMapLevelData); // We subtract 1 because DeferredTextureImage already includes the base // level in its size size_t pixelOffset = size; size += pixelSize; size_t colorSpaceOffset = 0; size_t colorSpaceSize = 0; SkColorSpaceTransferFn fn; if (info.colorSpace()) { colorSpaceOffset = size; colorSpaceSize = info.colorSpace()->writeToMemory(nullptr); size += colorSpaceSize; } else if (this->colorSpace() && this->colorSpace()->isNumericalTransferFn(&fn)) { // In legacy mode, preserve the color space tag on the SkImage. This is only // supported if the color space has a parametric transfer function. SkASSERT(!dstColorSpace); colorSpaceOffset = size; colorSpaceSize = this->colorSpace()->writeToMemory(nullptr); size += colorSpaceSize; } if (!fillMode) { return size; } char* bufferAsCharPtr = reinterpret_cast<char*>(buffer); char* pixelsAsCharPtr = bufferAsCharPtr + pixelOffset; void* pixels = pixelsAsCharPtr; memcpy(reinterpret_cast<void*>(SkAlign8(reinterpret_cast<uintptr_t>(pixelsAsCharPtr))), pixmap.addr(), pixmap.getSafeSize()); // If the context has sRGB support, and we're intending to render to a surface with an attached // color space, and the image has an sRGB-like color space attached, then use our gamma (sRGB) // aware mip-mapping. SkDestinationSurfaceColorMode colorMode = SkDestinationSurfaceColorMode::kLegacy; if (proxy.fCaps->srgbSupport() && SkToBool(dstColorSpace) && info.colorSpace() && info.colorSpace()->gammaCloseToSRGB()) { colorMode = SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware; } SkASSERT(info == pixmap.info()); size_t rowBytes = pixmap.rowBytes(); static_assert(std::is_standard_layout<DeferredTextureImage>::value, "offsetof, which we use below, requires the type have standard layout"); auto dtiBufferFiller = DTIBufferFiller{bufferAsCharPtr}; FILL_MEMBER(dtiBufferFiller, fColorMode, &colorMode); FILL_MEMBER(dtiBufferFiller, fContextUniqueID, &proxy.fContextUniqueID); int width = info.width(); FILL_MEMBER(dtiBufferFiller, fWidth, &width); int height = info.height(); FILL_MEMBER(dtiBufferFiller, fHeight, &height); SkColorType colorType = info.colorType(); FILL_MEMBER(dtiBufferFiller, fColorType, &colorType); SkAlphaType alphaType = info.alphaType(); FILL_MEMBER(dtiBufferFiller, fAlphaType, &alphaType); FILL_MEMBER(dtiBufferFiller, fMipMapLevelCount, &mipMapLevelCount); memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData[0].fPixelData), &pixels, sizeof(pixels)); memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData[0].fRowBytes), &rowBytes, sizeof(rowBytes)); if (colorSpaceSize) { void* colorSpace = bufferAsCharPtr + colorSpaceOffset; FILL_MEMBER(dtiBufferFiller, fColorSpace, &colorSpace); FILL_MEMBER(dtiBufferFiller, fColorSpaceSize, &colorSpaceSize); if (info.colorSpace()) { info.colorSpace()->writeToMemory(bufferAsCharPtr + colorSpaceOffset); } else { SkASSERT(this->colorSpace() && this->colorSpace()->isNumericalTransferFn(&fn)); SkASSERT(!dstColorSpace); this->colorSpace()->writeToMemory(bufferAsCharPtr + colorSpaceOffset); } } else { memset(bufferAsCharPtr + offsetof(DeferredTextureImage, fColorSpace), 0, sizeof(DeferredTextureImage::fColorSpace)); memset(bufferAsCharPtr + offsetof(DeferredTextureImage, fColorSpaceSize), 0, sizeof(DeferredTextureImage::fColorSpaceSize)); } // Fill in the mipmap levels if they exist char* mipLevelPtr = pixelsAsCharPtr + SkAlign8(pixmap.getSafeSize()); if (useMipMaps) { static_assert(std::is_standard_layout<MipMapLevelData>::value, "offsetof, which we use below, requires the type have a standard layout"); std::unique_ptr<SkMipMap> mipmaps(SkMipMap::Build(pixmap, colorMode, nullptr)); // SkMipMap holds only the mipmap levels it generates. // A programmer can use the data they provided to SkMipMap::Build as level 0. // So the SkMipMap provides levels 1-x but it stores them in its own // range 0-(x-1). for (int generatedMipLevelIndex = 0; generatedMipLevelIndex < mipMapLevelCount - 1; generatedMipLevelIndex++) { SkMipMap::Level mipLevel; mipmaps->getLevel(generatedMipLevelIndex, &mipLevel); // Make sure the mipmap data is after the start of the buffer SkASSERT(mipLevelPtr > bufferAsCharPtr); // Make sure the mipmap data starts before the end of the buffer SkASSERT(mipLevelPtr < bufferAsCharPtr + pixelOffset + pixelSize); // Make sure the mipmap data ends before the end of the buffer SkASSERT(mipLevelPtr + mipLevel.fPixmap.getSafeSize() <= bufferAsCharPtr + pixelOffset + pixelSize); // getSafeSize includes rowbyte padding except for the last row, // right? memcpy(mipLevelPtr, mipLevel.fPixmap.addr(), mipLevel.fPixmap.getSafeSize()); memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData) + sizeof(MipMapLevelData) * (generatedMipLevelIndex + 1) + offsetof(MipMapLevelData, fPixelData), &mipLevelPtr, sizeof(void*)); size_t rowBytes = mipLevel.fPixmap.rowBytes(); memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData) + sizeof(MipMapLevelData) * (generatedMipLevelIndex + 1) + offsetof(MipMapLevelData, fRowBytes), &rowBytes, sizeof(rowBytes)); mipLevelPtr += SkAlign8(mipLevel.fPixmap.getSafeSize()); } } return size; }
/* * Performs the jpeg decode */ SkCodec::Result SkJpegCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& options, SkPMColor*, int*, int* rowsDecoded) { if (options.fSubset) { // Subsets are not supported. return kUnimplemented; } // Get a pointer to the decompress info since we will use it quite frequently jpeg_decompress_struct* dinfo = fDecoderMgr->dinfo(); // Set the jump location for libjpeg errors if (setjmp(fDecoderMgr->getJmpBuf())) { return fDecoderMgr->returnFailure("setjmp", kInvalidInput); } // Check if we can decode to the requested destination and set the output color space if (!this->setOutputColorSpace(dstInfo)) { return fDecoderMgr->returnFailure("conversion_possible", kInvalidConversion); } // Now, given valid output dimensions, we can start the decompress if (!jpeg_start_decompress(dinfo)) { return fDecoderMgr->returnFailure("startDecompress", kInvalidInput); } // The recommended output buffer height should always be 1 in high quality modes. // If it's not, we want to know because it means our strategy is not optimal. SkASSERT(1 == dinfo->rec_outbuf_height); J_COLOR_SPACE colorSpace = dinfo->out_color_space; if (JCS_CMYK == colorSpace || JCS_RGB == colorSpace) { this->initializeSwizzler(dstInfo, options); } // Perform the decode a single row at a time uint32_t dstHeight = dstInfo.height(); JSAMPLE* dstRow; if (fSwizzler) { // write data to storage row, then sample using swizzler dstRow = fSrcRow; } else { // write data directly to dst dstRow = (JSAMPLE*) dst; } for (uint32_t y = 0; y < dstHeight; y++) { // Read rows of the image uint32_t lines = jpeg_read_scanlines(dinfo, &dstRow, 1); sk_msan_mark_initialized(dstRow, dstRow + dstRowBytes, "skbug.com/4550"); // If we cannot read enough rows, assume the input is incomplete if (lines != 1) { *rowsDecoded = y; return fDecoderMgr->returnFailure("Incomplete image data", kIncompleteInput); } if (fSwizzler) { // use swizzler to sample row fSwizzler->swizzle(dst, dstRow); dst = SkTAddOffset<JSAMPLE>(dst, dstRowBytes); } else { dstRow = SkTAddOffset<JSAMPLE>(dstRow, dstRowBytes); } } return kSuccess; }
static transform_scanline_proc choose_proc(const SkImageInfo& info) { switch (info.colorType()) { case kRGBA_8888_SkColorType: switch (info.alphaType()) { case kOpaque_SkAlphaType: return transform_scanline_RGBX; case kUnpremul_SkAlphaType: return transform_scanline_memcpy; case kPremul_SkAlphaType: return transform_scanline_rgbA; default: SkASSERT(false); return nullptr; } case kBGRA_8888_SkColorType: switch (info.alphaType()) { case kOpaque_SkAlphaType: return transform_scanline_BGRX; case kUnpremul_SkAlphaType: return transform_scanline_BGRA; case kPremul_SkAlphaType: return transform_scanline_bgrA; default: SkASSERT(false); return nullptr; } case kRGB_565_SkColorType: return transform_scanline_565; case kRGB_888x_SkColorType: return transform_scanline_888x; case kARGB_4444_SkColorType: switch (info.alphaType()) { case kOpaque_SkAlphaType: return transform_scanline_444; case kPremul_SkAlphaType: // 4444 is assumed to be legacy premul. return transform_scanline_4444; default: SkASSERT(false); return nullptr; } case kGray_8_SkColorType: return transform_scanline_memcpy; case kRGBA_F16_SkColorType: switch (info.alphaType()) { case kOpaque_SkAlphaType: case kUnpremul_SkAlphaType: return transform_scanline_F16; case kPremul_SkAlphaType: return transform_scanline_F16_premul; default: SkASSERT(false); return nullptr; } case kRGBA_F32_SkColorType: switch (info.alphaType()) { case kOpaque_SkAlphaType: case kUnpremul_SkAlphaType: return transform_scanline_F32; case kPremul_SkAlphaType: return transform_scanline_F32_premul; default: SkASSERT(false); return nullptr; } case kRGBA_1010102_SkColorType: switch (info.alphaType()) { case kOpaque_SkAlphaType: case kUnpremul_SkAlphaType: return transform_scanline_1010102; case kPremul_SkAlphaType: return transform_scanline_1010102_premul; default: SkASSERT(false); return nullptr; } case kRGB_101010x_SkColorType: return transform_scanline_101010x; case kAlpha_8_SkColorType: return transform_scanline_A8_to_GrayAlpha; default: SkASSERT(false); return nullptr; } }
void SubsetTranslateBench::onDraw(const int n, SkCanvas* canvas) { // When the color type is kIndex8, we will need to store the color table. If it is // used, it will be initialized by the codec. int colorCount; SkPMColor colors[256]; if (fUseCodec) { for (int count = 0; count < n; count++) { SkAutoTDelete<SkScanlineDecoder> scanlineDecoder( SkScanlineDecoder::NewFromStream(fStream->duplicate())); const SkImageInfo info = scanlineDecoder->getInfo().makeColorType(fColorType); SkAutoTDeleteArray<uint8_t> row(new uint8_t[info.minRowBytes()]); scanlineDecoder->start(info, NULL, colors, &colorCount); SkBitmap bitmap; // Note that we use the same bitmap for all of the subsets. // It might be larger than necessary for the end subsets. SkImageInfo subsetInfo = info.makeWH(fSubsetWidth, fSubsetHeight); alloc_pixels(&bitmap, subsetInfo, colors, colorCount); for (int x = 0; x < info.width(); x += fSubsetWidth) { for (int y = 0; y < info.height(); y += fSubsetHeight) { scanlineDecoder->skipScanlines(y); const uint32_t currSubsetWidth = x + (int) fSubsetWidth > info.width() ? info.width() - x : fSubsetWidth; const uint32_t currSubsetHeight = y + (int) fSubsetHeight > info.height() ? info.height() - y : fSubsetHeight; const uint32_t bpp = info.bytesPerPixel(); for (uint32_t y = 0; y < currSubsetHeight; y++) { scanlineDecoder->getScanlines(row.get(), 1, 0); memcpy(bitmap.getAddr(0, y), row.get() + x * bpp, currSubsetWidth * bpp); } } } } } else { // We create a color table here to satisfy allocPixels() when the output // type is kIndex8. It's okay that this is uninitialized since we never // use it. SkColorTable* colorTable = new SkColorTable(colors, 0); for (int count = 0; count < n; count++) { int width, height; SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(fStream)); decoder->buildTileIndex(fStream->duplicate(), &width, &height); SkBitmap bitmap; // Note that we use the same bitmap for all of the subsets. // It might be larger than necessary for the end subsets. // If we do not include this step, decodeSubset() would allocate space // for the pixels automatically, but this would not allow us to reuse the // same bitmap as the other subsets. We want to reuse the same bitmap // because it gives a more fair comparison with SkCodec and is a common // use case of BitmapRegionDecoder. bitmap.allocPixels(SkImageInfo::Make(fSubsetWidth, fSubsetHeight, fColorType, kOpaque_SkAlphaType), NULL, colorTable); for (int x = 0; x < width; x += fSubsetWidth) { for (int y = 0; y < height; y += fSubsetHeight) { const uint32_t currSubsetWidth = x + (int) fSubsetWidth > width ? width - x : fSubsetWidth; const uint32_t currSubsetHeight = y + (int) fSubsetHeight > height ? height - y : fSubsetHeight; SkIRect rect = SkIRect::MakeXYWH(x, y, currSubsetWidth, currSubsetHeight); decoder->decodeSubset(&bitmap, rect, fColorType); } } } } }
/* * Checks if the conversion between the input image and the requested output * image has been implemented * Sets the output color space */ bool SkJpegCodec::setOutputColorSpace(const SkImageInfo& dst) { if (kUnknown_SkAlphaType == dst.alphaType()) { return false; } if (kOpaque_SkAlphaType != dst.alphaType()) { SkCodecPrintf("Warning: an opaque image should be decoded as opaque " "- it is being decoded as non-opaque, which will draw slower\n"); } // Check if we will decode to CMYK because a conversion to RGBA is not supported J_COLOR_SPACE colorSpace = fDecoderMgr->dinfo()->jpeg_color_space; bool isCMYK = JCS_CMYK == colorSpace || JCS_YCCK == colorSpace; // Check for valid color types and set the output color space switch (dst.colorType()) { case kRGBA_8888_SkColorType: if (isCMYK) { fDecoderMgr->dinfo()->out_color_space = JCS_CMYK; } else { #ifdef LIBJPEG_TURBO_VERSION fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA; #else fDecoderMgr->dinfo()->out_color_space = JCS_RGB; #endif } return true; case kBGRA_8888_SkColorType: if (isCMYK) { fDecoderMgr->dinfo()->out_color_space = JCS_CMYK; } else { #ifdef LIBJPEG_TURBO_VERSION fDecoderMgr->dinfo()->out_color_space = JCS_EXT_BGRA; #else fDecoderMgr->dinfo()->out_color_space = JCS_RGB; #endif } return true; case kRGB_565_SkColorType: if (isCMYK) { fDecoderMgr->dinfo()->out_color_space = JCS_CMYK; } else { #ifdef TURBO_HAS_565 fDecoderMgr->dinfo()->dither_mode = JDITHER_NONE; fDecoderMgr->dinfo()->out_color_space = JCS_RGB565; #else fDecoderMgr->dinfo()->out_color_space = JCS_RGB; #endif } return true; case kGray_8_SkColorType: if (isCMYK) { return false; } else { // We will enable decodes to gray even if the image is color because this is // much faster than decoding to color and then converting fDecoderMgr->dinfo()->out_color_space = JCS_GRAYSCALE; } return true; default: return false; } }
DEF_TEST(Serialization, reporter) { // Test matrix serialization { SkMatrix matrix = SkMatrix::I(); TestObjectSerialization(&matrix, reporter); } // Test path serialization { SkPath path; TestObjectSerialization(&path, reporter); } // Test region serialization { SkRegion region; TestObjectSerialization(®ion, reporter); } // Test xfermode serialization { TestXfermodeSerialization(reporter); } // Test color filter serialization { TestColorFilterSerialization(reporter); } // Test string serialization { SkString string("string"); TestObjectSerializationNoAlign<SkString, false>(&string, reporter); TestObjectSerializationNoAlign<SkString, true>(&string, reporter); } // Test rrect serialization { // SkRRect does not initialize anything. // An uninitialized SkRRect can be serialized, // but will branch on uninitialized data when deserialized. SkRRect rrect; SkRect rect = SkRect::MakeXYWH(1, 2, 20, 30); SkVector corners[4] = { {1, 2}, {2, 3}, {3,4}, {4,5} }; rrect.setRectRadii(rect, corners); TestAlignment(&rrect, reporter); } // Test readByteArray { unsigned char data[kArraySize] = { 1, 2, 3 }; TestArraySerialization(data, reporter); } // Test readColorArray { SkColor data[kArraySize] = { SK_ColorBLACK, SK_ColorWHITE, SK_ColorRED }; TestArraySerialization(data, reporter); } // Test readIntArray { int32_t data[kArraySize] = { 1, 2, 4, 8 }; TestArraySerialization(data, reporter); } // Test readPointArray { SkPoint data[kArraySize] = { {6, 7}, {42, 128} }; TestArraySerialization(data, reporter); } // Test readScalarArray { SkScalar data[kArraySize] = { SK_Scalar1, SK_ScalarHalf, SK_ScalarMax }; TestArraySerialization(data, reporter); } // Test invalid deserializations { SkImageInfo info = SkImageInfo::MakeN32Premul(kBitmapSize, kBitmapSize); SkBitmap validBitmap; validBitmap.setInfo(info); // Create a bitmap with a really large height SkBitmap invalidBitmap; invalidBitmap.setInfo(info.makeWH(info.width(), 1000000000)); // The deserialization should succeed, and the rendering shouldn't crash, // even when the device fails to initialize, due to its size TestBitmapSerialization(validBitmap, invalidBitmap, true, reporter); } // Test simple SkPicture serialization { SkPictureRecorder recorder; draw_something(recorder.beginRecording(SkIntToScalar(kBitmapSize), SkIntToScalar(kBitmapSize), nullptr, 0)); sk_sp<SkPicture> pict(recorder.finishRecordingAsPicture()); // Serialize picture SkBinaryWriteBuffer writer; pict->flatten(writer); size_t size = writer.bytesWritten(); SkAutoTMalloc<unsigned char> data(size); writer.writeToMemory(static_cast<void*>(data.get())); // Deserialize picture SkValidatingReadBuffer reader(static_cast<void*>(data.get()), size); sk_sp<SkPicture> readPict(SkPicture::MakeFromBuffer(reader)); REPORTER_ASSERT(reporter, readPict.get()); } TestPictureTypefaceSerialization(reporter); // Test SkLightingShader/NormalMapSource serialization { const int kTexSize = 2; SkLights::Builder builder; builder.add(SkLights::Light(SkColor3f::Make(1.0f, 1.0f, 1.0f), SkVector3::Make(1.0f, 0.0f, 0.0f))); builder.add(SkLights::Light(SkColor3f::Make(0.2f, 0.2f, 0.2f))); sk_sp<SkLights> fLights = builder.finish(); SkBitmap diffuse = sk_tool_utils::create_checkerboard_bitmap( kTexSize, kTexSize, sk_tool_utils::color_to_565(0x0), sk_tool_utils::color_to_565(0xFF804020), 8); SkRect bitmapBounds = SkRect::MakeIWH(diffuse.width(), diffuse.height()); SkMatrix matrix; SkRect r = SkRect::MakeWH(SkIntToScalar(kTexSize), SkIntToScalar(kTexSize)); matrix.setRectToRect(bitmapBounds, r, SkMatrix::kFill_ScaleToFit); SkMatrix ctm; ctm.setRotate(45); SkBitmap normals; normals.allocN32Pixels(kTexSize, kTexSize); sk_tool_utils::create_frustum_normal_map(&normals, SkIRect::MakeWH(kTexSize, kTexSize)); sk_sp<SkShader> normalMap = SkMakeBitmapShader(normals, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &matrix, nullptr); sk_sp<SkNormalSource> normalSource = SkNormalSource::MakeFromNormalMap(std::move(normalMap), ctm); sk_sp<SkShader> lightingShader = SkLightingShader::Make(diffuse, fLights, &matrix, std::move(normalSource)); SkAutoTUnref<SkShader>(TestFlattenableSerialization(lightingShader.get(), true, reporter)); // TODO test equality? } }
bool make_ringed_bitmap(GrContext*, TestPixels* result, int width, int height, SkColorType ct, SkAlphaType at, PIXEL_TYPE outerRingColor, PIXEL_TYPE innerRingColor, PIXEL_TYPE checkColor1, PIXEL_TYPE checkColor2) { SkASSERT(0 == width % 2 && 0 == height % 2); SkASSERT(width >= 6 && height >= 6); result->fType = TestPixels::kBitmap; SkImageInfo info = SkImageInfo::Make(width, height, ct, at); size_t rowBytes = SkAlign4(info.minRowBytes()); result->fBitmap.allocPixels(info, rowBytes); PIXEL_TYPE* scanline = (PIXEL_TYPE*)result->fBitmap.getAddr(0, 0); for (int x = 0; x < width; ++x) { scanline[x] = outerRingColor; } scanline = (PIXEL_TYPE*)result->fBitmap.getAddr(0, 1); scanline[0] = outerRingColor; for (int x = 1; x < width - 1; ++x) { scanline[x] = innerRingColor; } scanline[width - 1] = outerRingColor; for (int y = 2; y < height / 2; ++y) { scanline = (PIXEL_TYPE*)result->fBitmap.getAddr(0, y); scanline[0] = outerRingColor; scanline[1] = innerRingColor; for (int x = 2; x < width / 2; ++x) { scanline[x] = checkColor1; } for (int x = width / 2; x < width - 2; ++x) { scanline[x] = checkColor2; } scanline[width - 2] = innerRingColor; scanline[width - 1] = outerRingColor; } for (int y = height / 2; y < height - 2; ++y) { scanline = (PIXEL_TYPE*)result->fBitmap.getAddr(0, y); scanline[0] = outerRingColor; scanline[1] = innerRingColor; for (int x = 2; x < width / 2; ++x) { scanline[x] = checkColor2; } for (int x = width / 2; x < width - 2; ++x) { scanline[x] = checkColor1; } scanline[width - 2] = innerRingColor; scanline[width - 1] = outerRingColor; } scanline = (PIXEL_TYPE*)result->fBitmap.getAddr(0, height - 2); scanline[0] = outerRingColor; for (int x = 1; x < width - 1; ++x) { scanline[x] = innerRingColor; } scanline[width - 1] = outerRingColor; scanline = (PIXEL_TYPE*)result->fBitmap.getAddr(0, height - 1); for (int x = 0; x < width; ++x) { scanline[x] = outerRingColor; } result->fBitmap.setImmutable(); result->fRect.set(2, 2, width - 2, height - 2); return true; }
bool SkPngEncoderMgr::setHeader(const SkImageInfo& srcInfo, const SkPngEncoder::Options& options) { if (setjmp(png_jmpbuf(fPngPtr))) { return false; } int pngColorType; png_color_8 sigBit; int bitDepth = 8; switch (srcInfo.colorType()) { case kRGBA_F16_SkColorType: case kRGBA_F32_SkColorType: sigBit.red = 16; sigBit.green = 16; sigBit.blue = 16; sigBit.alpha = 16; bitDepth = 16; pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA; fPngBytesPerPixel = 8; break; case kGray_8_SkColorType: sigBit.gray = 8; pngColorType = PNG_COLOR_TYPE_GRAY; fPngBytesPerPixel = 1; SkASSERT(srcInfo.isOpaque()); break; case kRGBA_8888_SkColorType: case kBGRA_8888_SkColorType: sigBit.red = 8; sigBit.green = 8; sigBit.blue = 8; sigBit.alpha = 8; pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA; fPngBytesPerPixel = srcInfo.isOpaque() ? 3 : 4; break; case kRGB_888x_SkColorType: sigBit.red = 8; sigBit.green = 8; sigBit.blue = 8; pngColorType = PNG_COLOR_TYPE_RGB; fPngBytesPerPixel = 3; SkASSERT(srcInfo.isOpaque()); break; case kARGB_4444_SkColorType: if (kUnpremul_SkAlphaType == srcInfo.alphaType()) { return false; } sigBit.red = 4; sigBit.green = 4; sigBit.blue = 4; sigBit.alpha = 4; pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA; fPngBytesPerPixel = srcInfo.isOpaque() ? 3 : 4; break; case kRGB_565_SkColorType: sigBit.red = 5; sigBit.green = 6; sigBit.blue = 5; pngColorType = PNG_COLOR_TYPE_RGB; fPngBytesPerPixel = 3; SkASSERT(srcInfo.isOpaque()); break; case kAlpha_8_SkColorType: // store as gray+alpha, but ignore gray sigBit.gray = kGraySigBit_GrayAlphaIsJustAlpha; sigBit.alpha = 8; pngColorType = PNG_COLOR_TYPE_GRAY_ALPHA; fPngBytesPerPixel = 2; break; case kRGBA_1010102_SkColorType: bitDepth = 16; sigBit.red = 10; sigBit.green = 10; sigBit.blue = 10; sigBit.alpha = 2; pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA; fPngBytesPerPixel = 8; break; case kRGB_101010x_SkColorType: bitDepth = 16; sigBit.red = 10; sigBit.green = 10; sigBit.blue = 10; pngColorType = PNG_COLOR_TYPE_RGB; fPngBytesPerPixel = 6; break; default: return false; } png_set_IHDR(fPngPtr, fInfoPtr, srcInfo.width(), srcInfo.height(), bitDepth, pngColorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_set_sBIT(fPngPtr, fInfoPtr, &sigBit); int filters = (int)options.fFilterFlags & (int)SkPngEncoder::FilterFlag::kAll; SkASSERT(filters == (int)options.fFilterFlags); png_set_filter(fPngPtr, PNG_FILTER_TYPE_BASE, filters); int zlibLevel = SkTMin(SkTMax(0, options.fZLibLevel), 9); SkASSERT(zlibLevel == options.fZLibLevel); png_set_compression_level(fPngPtr, zlibLevel); // Set comments in tEXt chunk const sk_sp<SkDataTable>& comments = options.fComments; if (comments != nullptr) { std::vector<png_text> png_texts(comments->count()); std::vector<SkString> clippedKeys; for (int i = 0; i < comments->count() / 2; ++i) { const char* keyword; const char* originalKeyword = comments->atStr(2 * i); const char* text = comments->atStr(2 * i + 1); if (strlen(originalKeyword) <= PNG_KEYWORD_MAX_LENGTH) { keyword = originalKeyword; } else { SkDEBUGFAILF("PNG tEXt keyword should be no longer than %d.", PNG_KEYWORD_MAX_LENGTH); clippedKeys.emplace_back(originalKeyword, PNG_KEYWORD_MAX_LENGTH); keyword = clippedKeys.back().c_str(); } // It seems safe to convert png_const_charp to png_charp for key/text, // and we don't have to provide text_length and other fields as we're providing // 0-terminated c_str with PNG_TEXT_COMPRESSION_NONE (no compression, no itxt). png_texts[i].compression = PNG_TEXT_COMPRESSION_NONE; png_texts[i].key = (png_charp)keyword; png_texts[i].text = (png_charp)text; } png_set_text(fPngPtr, fInfoPtr, png_texts.data(), png_texts.size()); } return true; }
sk_sp<SkSurface> SkSurface_Compute::onNewSurface(const SkImageInfo& info) { return sk_make_sp<SkSurface_Compute>(compute,info.width(),info.height()); }
/* * Checks if the conversion between the input image and the requested output * image has been implemented * Sets the output color space */ bool SkJpegCodec::setOutputColorSpace(const SkImageInfo& dstInfo, bool needsColorXform) { if (kUnknown_SkAlphaType == dstInfo.alphaType()) { return false; } if (kOpaque_SkAlphaType != dstInfo.alphaType()) { SkCodecPrintf("Warning: an opaque image should be decoded as opaque " "- it is being decoded as non-opaque, which will draw slower\n"); } // Check if we will decode to CMYK. libjpeg-turbo does not convert CMYK to RGBA, so // we must do it ourselves. J_COLOR_SPACE encodedColorType = fDecoderMgr->dinfo()->jpeg_color_space; bool isCMYK = (JCS_CMYK == encodedColorType || JCS_YCCK == encodedColorType); // Check for valid color types and set the output color space switch (dstInfo.colorType()) { case kRGBA_8888_SkColorType: if (isCMYK) { fDecoderMgr->dinfo()->out_color_space = JCS_CMYK; } else { fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA; } return true; case kBGRA_8888_SkColorType: if (isCMYK) { fDecoderMgr->dinfo()->out_color_space = JCS_CMYK; } else if (needsColorXform) { // Our color transformation code requires RGBA order inputs, but it'll swizzle // to BGRA for us. fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA; } else { fDecoderMgr->dinfo()->out_color_space = JCS_EXT_BGRA; } return true; case kRGB_565_SkColorType: if (needsColorXform) { return false; } if (isCMYK) { fDecoderMgr->dinfo()->out_color_space = JCS_CMYK; } else { #ifdef TURBO_HAS_565 fDecoderMgr->dinfo()->dither_mode = JDITHER_NONE; fDecoderMgr->dinfo()->out_color_space = JCS_RGB565; #else fDecoderMgr->dinfo()->out_color_space = JCS_RGB; #endif } return true; case kGray_8_SkColorType: if (needsColorXform || JCS_GRAYSCALE != encodedColorType) { return false; } fDecoderMgr->dinfo()->out_color_space = JCS_GRAYSCALE; return true; case kRGBA_F16_SkColorType: SkASSERT(needsColorXform); if (isCMYK) { fDecoderMgr->dinfo()->out_color_space = JCS_CMYK; } else { fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA; } return true; default: return false; } }
// Reads the header and initializes the output fields, if not NULL. // // @param stream Input data. Will be read to get enough information to properly // setup the codec. // @param chunkReader SkPngChunkReader, for reading unknown chunks. May be NULL. // If not NULL, png_ptr will hold an *unowned* pointer to it. The caller is // expected to continue to own it for the lifetime of the png_ptr. // @param outCodec Optional output variable. If non-NULL, will be set to a new // SkPngCodec on success. // @param png_ptrp Optional output variable. If non-NULL, will be set to a new // png_structp on success. // @param info_ptrp Optional output variable. If non-NULL, will be set to a new // png_infop on success; // @return true on success, in which case the caller is responsible for calling // png_destroy_read_struct(png_ptrp, info_ptrp). // If it returns false, the passed in fields (except stream) are unchanged. static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader, SkCodec** outCodec, png_structp* png_ptrp, png_infop* info_ptrp) { // The image is known to be a PNG. Decode enough to know the SkImageInfo. png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, sk_error_fn, sk_warning_fn); if (!png_ptr) { return false; } AutoCleanPng autoClean(png_ptr); png_infop info_ptr = png_create_info_struct(png_ptr); if (info_ptr == nullptr) { return false; } autoClean.setInfoPtr(info_ptr); // FIXME: Could we use the return value of setjmp to specify the type of // error? if (setjmp(png_jmpbuf(png_ptr))) { return false; } png_set_read_fn(png_ptr, static_cast<void*>(stream), sk_read_fn); #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED // Hookup our chunkReader so we can see any user-chunks the caller may be interested in. // This needs to be installed before we read the png header. Android may store ninepatch // chunks in the header. if (chunkReader) { png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0); png_set_read_user_chunk_fn(png_ptr, (png_voidp) chunkReader, sk_read_user_chunk); } #endif // The call to png_read_info() gives us all of the information from the // PNG file before the first IDAT (image data chunk). png_read_info(png_ptr, info_ptr); png_uint_32 origWidth, origHeight; int bitDepth, encodedColorType; png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth, &encodedColorType, nullptr, nullptr, nullptr); // Tell libpng to strip 16 bit/color files down to 8 bits/color. // TODO: Should we handle this in SkSwizzler? Could this also benefit // RAW decodes? if (bitDepth == 16) { SkASSERT(PNG_COLOR_TYPE_PALETTE != encodedColorType); png_set_strip_16(png_ptr); } // Now determine the default colorType and alphaType and set the required transforms. // Often, we depend on SkSwizzler to perform any transforms that we need. However, we // still depend on libpng for many of the rare and PNG-specific cases. SkEncodedInfo::Color color; SkEncodedInfo::Alpha alpha; switch (encodedColorType) { case PNG_COLOR_TYPE_PALETTE: // Extract multiple pixels with bit depths of 1, 2, and 4 from a single // byte into separate bytes (useful for paletted and grayscale images). if (bitDepth < 8) { // TODO: Should we use SkSwizzler here? png_set_packing(png_ptr); } color = SkEncodedInfo::kPalette_Color; // Set the alpha depending on if a transparency chunk exists. alpha = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? SkEncodedInfo::kUnpremul_Alpha : SkEncodedInfo::kOpaque_Alpha; break; case PNG_COLOR_TYPE_RGB: if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { // Convert to RGBA if transparency chunk exists. png_set_tRNS_to_alpha(png_ptr); color = SkEncodedInfo::kRGBA_Color; alpha = SkEncodedInfo::kBinary_Alpha; } else { color = SkEncodedInfo::kRGB_Color; alpha = SkEncodedInfo::kOpaque_Alpha; } break; case PNG_COLOR_TYPE_GRAY: // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel. if (bitDepth < 8) { // TODO: Should we use SkSwizzler here? png_set_expand_gray_1_2_4_to_8(png_ptr); } if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(png_ptr); color = SkEncodedInfo::kGrayAlpha_Color; alpha = SkEncodedInfo::kBinary_Alpha; } else { color = SkEncodedInfo::kGray_Color; alpha = SkEncodedInfo::kOpaque_Alpha; } break; case PNG_COLOR_TYPE_GRAY_ALPHA: color = SkEncodedInfo::kGrayAlpha_Color; alpha = SkEncodedInfo::kUnpremul_Alpha; break; case PNG_COLOR_TYPE_RGBA: color = SkEncodedInfo::kRGBA_Color; alpha = SkEncodedInfo::kUnpremul_Alpha; break; default: // All the color types have been covered above. SkASSERT(false); color = SkEncodedInfo::kRGBA_Color; alpha = SkEncodedInfo::kUnpremul_Alpha; } int numberPasses = png_set_interlace_handling(png_ptr); autoClean.release(); if (png_ptrp) { *png_ptrp = png_ptr; } if (info_ptrp) { *info_ptrp = info_ptr; } if (outCodec) { sk_sp<SkColorSpace> colorSpace = read_color_space(png_ptr, info_ptr); if (!colorSpace) { // Treat unmarked pngs as sRGB. colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); } SkEncodedInfo encodedInfo = SkEncodedInfo::Make(color, alpha, 8); SkImageInfo imageInfo = encodedInfo.makeImageInfo(origWidth, origHeight, colorSpace); if (SkEncodedInfo::kOpaque_Alpha == alpha) { png_color_8p sigBits; if (png_get_sBIT(png_ptr, info_ptr, &sigBits)) { if (5 == sigBits->red && 6 == sigBits->green && 5 == sigBits->blue) { // Recommend a decode to 565 if the sBIT indicates 565. imageInfo = imageInfo.makeColorType(kRGB_565_SkColorType); } } } if (1 == numberPasses) { *outCodec = new SkPngNormalCodec(encodedInfo, imageInfo, stream, chunkReader, png_ptr, info_ptr, bitDepth); } else { *outCodec = new SkPngInterlacedCodec(encodedInfo, imageInfo, stream, chunkReader, png_ptr, info_ptr, bitDepth, numberPasses); } } return true; }
SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst, size_t rowBytes, const Options& options, SkPMColor ctable[], int* ctableCount) { if (options.fSubset) { // Subsets are not supported. return kUnimplemented; } Result result = fScanlineDecoder->start(requestedInfo, &options, ctable, ctableCount); if (kSuccess == result) { // native decode supported return fScanlineDecoder->getScanlines(dst, requestedInfo.height(), rowBytes); } if (kInvalidScale != result) { // no scaling requested return result; } // scaling requested int sampleX; int sampleY; if (!scaling_supported(requestedInfo, fScanlineDecoder->getInfo(), &sampleX, &sampleY)) { return kInvalidScale; } // set first sample pixel in y direction int Y0 = sampleY >> 1; int dstHeight = requestedInfo.height(); int srcHeight = fScanlineDecoder->getInfo().height(); SkImageInfo info = requestedInfo; // use original height as scanlineDecoder does not support y sampling natively info = info.makeWH(requestedInfo.width(), srcHeight); // update scanlineDecoder with new info result = fScanlineDecoder->start(info, &options, ctable, ctableCount); if (kSuccess != result) { return result; } const bool requiresPostYSampling = fScanlineDecoder->requiresPostYSampling(); if (requiresPostYSampling) { SkAutoMalloc storage(srcHeight * rowBytes); uint8_t* storagePtr = static_cast<uint8_t*>(storage.get()); result = fScanlineDecoder->getScanlines(storagePtr, srcHeight, rowBytes); if (kSuccess != result) { return result; } storagePtr += Y0 * rowBytes; for (int y = 0; y < dstHeight; y++) { memcpy(dst, storagePtr, rowBytes); storagePtr += sampleY * rowBytes; dst = SkTAddOffset<void>(dst, rowBytes); } } else { // does not require post y sampling result = fScanlineDecoder->skipScanlines(Y0); if (kSuccess != result) { return result; } for (int y = 0; y < dstHeight; y++) { result = fScanlineDecoder->getScanlines(dst, 1, rowBytes); if (kSuccess != result) { return result; } if (y < dstHeight - 1) { result = fScanlineDecoder->skipScanlines(sampleY - 1); if (kSuccess != result) { return result; } } dst = SkTAddOffset<void>(dst, rowBytes); } } return kSuccess; }