|  | /* | 
|  | * Copyright 2015 Google Inc. | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #include "client_utils/android/BitmapRegionDecoder.h" | 
|  | #include "client_utils/android/BitmapRegionDecoderPriv.h" | 
|  | #include "include/codec/SkAndroidCodec.h" | 
|  | #include "include/codec/SkEncodedImageFormat.h" | 
|  | #include "src/codec/SkCodecPriv.h" | 
|  |  | 
|  | namespace android { | 
|  | namespace skia { | 
|  |  | 
|  | std::unique_ptr<BitmapRegionDecoder> BitmapRegionDecoder::Make(sk_sp<SkData> data) { | 
|  | auto codec = SkAndroidCodec::MakeFromData(std::move(data)); | 
|  | if (nullptr == codec) { | 
|  | SkCodecPrintf("Error: Failed to create codec.\n"); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | switch (codec->getEncodedFormat()) { | 
|  | case SkEncodedImageFormat::kJPEG: | 
|  | case SkEncodedImageFormat::kPNG: | 
|  | case SkEncodedImageFormat::kWEBP: | 
|  | case SkEncodedImageFormat::kHEIF: | 
|  | break; | 
|  | default: | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | return std::unique_ptr<BitmapRegionDecoder>(new BitmapRegionDecoder(std::move(codec))); | 
|  | } | 
|  |  | 
|  | BitmapRegionDecoder::BitmapRegionDecoder(std::unique_ptr<SkAndroidCodec> codec) | 
|  | : fCodec(std::move(codec)) | 
|  | {} | 
|  |  | 
|  | int BitmapRegionDecoder::width() const { | 
|  | return fCodec->getInfo().width(); | 
|  | } | 
|  |  | 
|  | int BitmapRegionDecoder::height() const { | 
|  | return fCodec->getInfo().height(); | 
|  | } | 
|  |  | 
|  | bool BitmapRegionDecoder::decodeRegion(SkBitmap* bitmap, BRDAllocator* allocator, | 
|  | const SkIRect& desiredSubset, int sampleSize, SkColorType dstColorType, | 
|  | bool requireUnpremul, sk_sp<SkColorSpace> dstColorSpace) { | 
|  |  | 
|  | // 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 | 
|  | SkAlphaType dstAlphaType = fCodec->computeOutputAlphaType(requireUnpremul); | 
|  | SkImageInfo decodeInfo = | 
|  | SkImageInfo::Make(scaledSize, dstColorType, dstAlphaType, std::move(dstColorSpace)); | 
|  |  | 
|  | // 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 = std::max(0, desiredSubset.width() - outX - subset.width()); | 
|  | const int extraY = std::max(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)) { | 
|  | 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.computeByteSize(bitmap->rowBytes()); | 
|  | memset(pixels, 0, bytes); | 
|  | } | 
|  |  | 
|  | // Decode into the destination bitmap | 
|  | SkAndroidCodec::AndroidOptions options; | 
|  | options.fSampleSize = sampleSize; | 
|  | options.fSubset = ⊂ | 
|  | options.fZeroInitialized = zeroInit; | 
|  | void* dst = bitmap->getAddr(scaledOutX, scaledOutY); | 
|  |  | 
|  | SkCodec::Result result = fCodec->getAndroidPixels(decodeInfo, dst, bitmap->rowBytes(), | 
|  | &options); | 
|  | switch (result) { | 
|  | case SkCodec::kSuccess: | 
|  | case SkCodec::kIncompleteInput: | 
|  | case SkCodec::kErrorInInput: | 
|  | return true; | 
|  | default: | 
|  | SkCodecPrintf("Error: Could not get pixels with message \"%s\".\n", | 
|  | SkCodec::ResultToString(result)); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | } // namespace skia | 
|  | } // namespace android |