1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2015 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "client_utils/android/BitmapRegionDecoder.h"
9cb93a386Sopenharmony_ci#include "client_utils/android/BitmapRegionDecoderPriv.h"
10cb93a386Sopenharmony_ci#include "include/codec/SkAndroidCodec.h"
11cb93a386Sopenharmony_ci#include "src/codec/SkCodecPriv.h"
12cb93a386Sopenharmony_ci
13cb93a386Sopenharmony_cinamespace android {
14cb93a386Sopenharmony_cinamespace skia {
15cb93a386Sopenharmony_ci
16cb93a386Sopenharmony_cistd::unique_ptr<BitmapRegionDecoder> BitmapRegionDecoder::Make(sk_sp<SkData> data) {
17cb93a386Sopenharmony_ci    auto codec = SkAndroidCodec::MakeFromData(std::move(data));
18cb93a386Sopenharmony_ci    if (nullptr == codec) {
19cb93a386Sopenharmony_ci        SkCodecPrintf("Error: Failed to create codec.\n");
20cb93a386Sopenharmony_ci        return nullptr;
21cb93a386Sopenharmony_ci    }
22cb93a386Sopenharmony_ci
23cb93a386Sopenharmony_ci    switch (codec->getEncodedFormat()) {
24cb93a386Sopenharmony_ci        case SkEncodedImageFormat::kJPEG:
25cb93a386Sopenharmony_ci        case SkEncodedImageFormat::kPNG:
26cb93a386Sopenharmony_ci        case SkEncodedImageFormat::kWEBP:
27cb93a386Sopenharmony_ci        case SkEncodedImageFormat::kHEIF:
28cb93a386Sopenharmony_ci            break;
29cb93a386Sopenharmony_ci        default:
30cb93a386Sopenharmony_ci            return nullptr;
31cb93a386Sopenharmony_ci    }
32cb93a386Sopenharmony_ci
33cb93a386Sopenharmony_ci    return std::unique_ptr<BitmapRegionDecoder>(new BitmapRegionDecoder(std::move(codec)));
34cb93a386Sopenharmony_ci}
35cb93a386Sopenharmony_ci
36cb93a386Sopenharmony_ciBitmapRegionDecoder::BitmapRegionDecoder(std::unique_ptr<SkAndroidCodec> codec)
37cb93a386Sopenharmony_ci    : fCodec(std::move(codec))
38cb93a386Sopenharmony_ci{}
39cb93a386Sopenharmony_ci
40cb93a386Sopenharmony_ciint BitmapRegionDecoder::width() const {
41cb93a386Sopenharmony_ci    return fCodec->getInfo().width();
42cb93a386Sopenharmony_ci}
43cb93a386Sopenharmony_ci
44cb93a386Sopenharmony_ciint BitmapRegionDecoder::height() const {
45cb93a386Sopenharmony_ci    return fCodec->getInfo().height();
46cb93a386Sopenharmony_ci}
47cb93a386Sopenharmony_ci
48cb93a386Sopenharmony_cibool BitmapRegionDecoder::decodeRegion(SkBitmap* bitmap, BRDAllocator* allocator,
49cb93a386Sopenharmony_ci        const SkIRect& desiredSubset, int sampleSize, SkColorType dstColorType,
50cb93a386Sopenharmony_ci        bool requireUnpremul, sk_sp<SkColorSpace> dstColorSpace) {
51cb93a386Sopenharmony_ci
52cb93a386Sopenharmony_ci    // Fix the input sampleSize if necessary.
53cb93a386Sopenharmony_ci    if (sampleSize < 1) {
54cb93a386Sopenharmony_ci        sampleSize = 1;
55cb93a386Sopenharmony_ci    }
56cb93a386Sopenharmony_ci
57cb93a386Sopenharmony_ci    // The size of the output bitmap is determined by the size of the
58cb93a386Sopenharmony_ci    // requested subset, not by the size of the intersection of the subset
59cb93a386Sopenharmony_ci    // and the image dimensions.
60cb93a386Sopenharmony_ci    // If inputX is negative, we will need to place decoded pixels into the
61cb93a386Sopenharmony_ci    // output bitmap starting at a left offset.  Call this outX.
62cb93a386Sopenharmony_ci    // If outX is non-zero, subsetX must be zero.
63cb93a386Sopenharmony_ci    // If inputY is negative, we will need to place decoded pixels into the
64cb93a386Sopenharmony_ci    // output bitmap starting at a top offset.  Call this outY.
65cb93a386Sopenharmony_ci    // If outY is non-zero, subsetY must be zero.
66cb93a386Sopenharmony_ci    int outX;
67cb93a386Sopenharmony_ci    int outY;
68cb93a386Sopenharmony_ci    SkIRect subset = desiredSubset;
69cb93a386Sopenharmony_ci    SubsetType type = adjust_subset_rect(fCodec->getInfo().dimensions(), &subset, &outX, &outY);
70cb93a386Sopenharmony_ci    if (SubsetType::kOutside_SubsetType == type) {
71cb93a386Sopenharmony_ci        return false;
72cb93a386Sopenharmony_ci    }
73cb93a386Sopenharmony_ci
74cb93a386Sopenharmony_ci    // Ask the codec for a scaled subset
75cb93a386Sopenharmony_ci    if (!fCodec->getSupportedSubset(&subset)) {
76cb93a386Sopenharmony_ci        SkCodecPrintf("Error: Could not get subset.\n");
77cb93a386Sopenharmony_ci        return false;
78cb93a386Sopenharmony_ci    }
79cb93a386Sopenharmony_ci    SkISize scaledSize = fCodec->getSampledSubsetDimensions(sampleSize, subset);
80cb93a386Sopenharmony_ci
81cb93a386Sopenharmony_ci    // Create the image info for the decode
82cb93a386Sopenharmony_ci    SkAlphaType dstAlphaType = fCodec->computeOutputAlphaType(requireUnpremul);
83cb93a386Sopenharmony_ci    SkImageInfo decodeInfo =
84cb93a386Sopenharmony_ci            SkImageInfo::Make(scaledSize, dstColorType, dstAlphaType, dstColorSpace);
85cb93a386Sopenharmony_ci
86cb93a386Sopenharmony_ci    // Initialize the destination bitmap
87cb93a386Sopenharmony_ci    int scaledOutX = 0;
88cb93a386Sopenharmony_ci    int scaledOutY = 0;
89cb93a386Sopenharmony_ci    int scaledOutWidth = scaledSize.width();
90cb93a386Sopenharmony_ci    int scaledOutHeight = scaledSize.height();
91cb93a386Sopenharmony_ci    if (SubsetType::kPartiallyInside_SubsetType == type) {
92cb93a386Sopenharmony_ci        scaledOutX = outX / sampleSize;
93cb93a386Sopenharmony_ci        scaledOutY = outY / sampleSize;
94cb93a386Sopenharmony_ci        // We need to be safe here because getSupportedSubset() may have modified the subset.
95cb93a386Sopenharmony_ci        const int extraX = std::max(0, desiredSubset.width() - outX - subset.width());
96cb93a386Sopenharmony_ci        const int extraY = std::max(0, desiredSubset.height() - outY - subset.height());
97cb93a386Sopenharmony_ci        const int scaledExtraX = extraX / sampleSize;
98cb93a386Sopenharmony_ci        const int scaledExtraY = extraY / sampleSize;
99cb93a386Sopenharmony_ci        scaledOutWidth += scaledOutX + scaledExtraX;
100cb93a386Sopenharmony_ci        scaledOutHeight += scaledOutY + scaledExtraY;
101cb93a386Sopenharmony_ci    }
102cb93a386Sopenharmony_ci    SkImageInfo outInfo = decodeInfo.makeWH(scaledOutWidth, scaledOutHeight);
103cb93a386Sopenharmony_ci    if (kGray_8_SkColorType == dstColorType) {
104cb93a386Sopenharmony_ci        // The legacy implementations of BitmapFactory and BitmapRegionDecoder
105cb93a386Sopenharmony_ci        // used kAlpha8 for grayscale images (before kGray8 existed).  While
106cb93a386Sopenharmony_ci        // the codec recognizes kGray8, we need to decode into a kAlpha8
107cb93a386Sopenharmony_ci        // bitmap in order to avoid a behavior change.
108cb93a386Sopenharmony_ci        outInfo = outInfo.makeColorType(kAlpha_8_SkColorType).makeAlphaType(kPremul_SkAlphaType);
109cb93a386Sopenharmony_ci    }
110cb93a386Sopenharmony_ci    bitmap->setInfo(outInfo);
111cb93a386Sopenharmony_ci    if (!bitmap->tryAllocPixels(allocator)) {
112cb93a386Sopenharmony_ci        SkCodecPrintf("Error: Could not allocate pixels.\n");
113cb93a386Sopenharmony_ci        return false;
114cb93a386Sopenharmony_ci    }
115cb93a386Sopenharmony_ci
116cb93a386Sopenharmony_ci    // Zero the bitmap if the region is not completely within the image.
117cb93a386Sopenharmony_ci    // TODO (msarett): Can we make this faster by implementing it to only
118cb93a386Sopenharmony_ci    //                 zero parts of the image that we won't overwrite with
119cb93a386Sopenharmony_ci    //                 pixels?
120cb93a386Sopenharmony_ci    SkCodec::ZeroInitialized zeroInit = allocator ? allocator->zeroInit() :
121cb93a386Sopenharmony_ci            SkCodec::kNo_ZeroInitialized;
122cb93a386Sopenharmony_ci    if (SubsetType::kPartiallyInside_SubsetType == type &&
123cb93a386Sopenharmony_ci            SkCodec::kNo_ZeroInitialized == zeroInit) {
124cb93a386Sopenharmony_ci        void* pixels = bitmap->getPixels();
125cb93a386Sopenharmony_ci        size_t bytes = outInfo.computeByteSize(bitmap->rowBytes());
126cb93a386Sopenharmony_ci        memset(pixels, 0, bytes);
127cb93a386Sopenharmony_ci    }
128cb93a386Sopenharmony_ci
129cb93a386Sopenharmony_ci    // Decode into the destination bitmap
130cb93a386Sopenharmony_ci    SkAndroidCodec::AndroidOptions options;
131cb93a386Sopenharmony_ci    options.fSampleSize = sampleSize;
132cb93a386Sopenharmony_ci    options.fSubset = &subset;
133cb93a386Sopenharmony_ci    options.fZeroInitialized = zeroInit;
134cb93a386Sopenharmony_ci    void* dst = bitmap->getAddr(scaledOutX, scaledOutY);
135cb93a386Sopenharmony_ci
136cb93a386Sopenharmony_ci    SkCodec::Result result = fCodec->getAndroidPixels(decodeInfo, dst, bitmap->rowBytes(),
137cb93a386Sopenharmony_ci            &options);
138cb93a386Sopenharmony_ci    switch (result) {
139cb93a386Sopenharmony_ci        case SkCodec::kSuccess:
140cb93a386Sopenharmony_ci        case SkCodec::kIncompleteInput:
141cb93a386Sopenharmony_ci        case SkCodec::kErrorInInput:
142cb93a386Sopenharmony_ci            return true;
143cb93a386Sopenharmony_ci        default:
144cb93a386Sopenharmony_ci            SkCodecPrintf("Error: Could not get pixels with message \"%s\".\n",
145cb93a386Sopenharmony_ci                          SkCodec::ResultToString(result));
146cb93a386Sopenharmony_ci            return false;
147cb93a386Sopenharmony_ci    }
148cb93a386Sopenharmony_ci}
149cb93a386Sopenharmony_ci
150cb93a386Sopenharmony_ci} // namespace skia
151cb93a386Sopenharmony_ci} // namespace android
152