xref: /aosp_15_r20/frameworks/base/libs/hwui/jni/BitmapRegionDecoder.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker  * Copyright (C) 2010 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker  *
4*d57664e9SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*d57664e9SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*d57664e9SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*d57664e9SAndroid Build Coastguard Worker  *
8*d57664e9SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*d57664e9SAndroid Build Coastguard Worker  *
10*d57664e9SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*d57664e9SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*d57664e9SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d57664e9SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*d57664e9SAndroid Build Coastguard Worker  * limitations under the License.
15*d57664e9SAndroid Build Coastguard Worker  */
16*d57664e9SAndroid Build Coastguard Worker 
17*d57664e9SAndroid Build Coastguard Worker #include "BitmapRegionDecoder.h"
18*d57664e9SAndroid Build Coastguard Worker 
19*d57664e9SAndroid Build Coastguard Worker #include <HardwareBitmapUploader.h>
20*d57664e9SAndroid Build Coastguard Worker #include <androidfw/Asset.h>
21*d57664e9SAndroid Build Coastguard Worker #include <sys/stat.h>
22*d57664e9SAndroid Build Coastguard Worker #include <utils/StatsUtils.h>
23*d57664e9SAndroid Build Coastguard Worker 
24*d57664e9SAndroid Build Coastguard Worker #include <memory>
25*d57664e9SAndroid Build Coastguard Worker 
26*d57664e9SAndroid Build Coastguard Worker #include "BitmapFactory.h"
27*d57664e9SAndroid Build Coastguard Worker #include "CreateJavaOutputStreamAdaptor.h"
28*d57664e9SAndroid Build Coastguard Worker #include "Gainmap.h"
29*d57664e9SAndroid Build Coastguard Worker #include "GraphicsJNI.h"
30*d57664e9SAndroid Build Coastguard Worker #include "SkBitmap.h"
31*d57664e9SAndroid Build Coastguard Worker #include "SkCodec.h"
32*d57664e9SAndroid Build Coastguard Worker #include "SkColorSpace.h"
33*d57664e9SAndroid Build Coastguard Worker #include "SkData.h"
34*d57664e9SAndroid Build Coastguard Worker #include "SkGainmapInfo.h"
35*d57664e9SAndroid Build Coastguard Worker #include "SkStream.h"
36*d57664e9SAndroid Build Coastguard Worker #include "SkStreamPriv.h"
37*d57664e9SAndroid Build Coastguard Worker #include "Utils.h"
38*d57664e9SAndroid Build Coastguard Worker 
39*d57664e9SAndroid Build Coastguard Worker using namespace android;
40*d57664e9SAndroid Build Coastguard Worker 
41*d57664e9SAndroid Build Coastguard Worker namespace android {
42*d57664e9SAndroid Build Coastguard Worker class BitmapRegionDecoderWrapper {
43*d57664e9SAndroid Build Coastguard Worker public:
Make(sk_sp<SkData> data)44*d57664e9SAndroid Build Coastguard Worker     static std::unique_ptr<BitmapRegionDecoderWrapper> Make(sk_sp<SkData> data) {
45*d57664e9SAndroid Build Coastguard Worker         std::unique_ptr<skia::BitmapRegionDecoder> mainImageBRD =
46*d57664e9SAndroid Build Coastguard Worker                 skia::BitmapRegionDecoder::Make(std::move(data));
47*d57664e9SAndroid Build Coastguard Worker         if (!mainImageBRD) {
48*d57664e9SAndroid Build Coastguard Worker             return nullptr;
49*d57664e9SAndroid Build Coastguard Worker         }
50*d57664e9SAndroid Build Coastguard Worker 
51*d57664e9SAndroid Build Coastguard Worker         SkGainmapInfo gainmapInfo;
52*d57664e9SAndroid Build Coastguard Worker         std::unique_ptr<SkAndroidCodec> gainmapCodec;
53*d57664e9SAndroid Build Coastguard Worker         std::unique_ptr<skia::BitmapRegionDecoder> gainmapBRD = nullptr;
54*d57664e9SAndroid Build Coastguard Worker         if (!mainImageBRD->getGainmapBitmapRegionDecoder(&gainmapInfo, &gainmapBRD)) {
55*d57664e9SAndroid Build Coastguard Worker             gainmapBRD = nullptr;
56*d57664e9SAndroid Build Coastguard Worker         }
57*d57664e9SAndroid Build Coastguard Worker 
58*d57664e9SAndroid Build Coastguard Worker         return std::unique_ptr<BitmapRegionDecoderWrapper>(new BitmapRegionDecoderWrapper(
59*d57664e9SAndroid Build Coastguard Worker                 std::move(mainImageBRD), std::move(gainmapBRD), gainmapInfo));
60*d57664e9SAndroid Build Coastguard Worker     }
61*d57664e9SAndroid Build Coastguard Worker 
getEncodedFormat()62*d57664e9SAndroid Build Coastguard Worker     SkEncodedImageFormat getEncodedFormat() { return mMainImageBRD->getEncodedFormat(); }
63*d57664e9SAndroid Build Coastguard Worker 
computeOutputColorType(SkColorType requestedColorType)64*d57664e9SAndroid Build Coastguard Worker     SkColorType computeOutputColorType(SkColorType requestedColorType) {
65*d57664e9SAndroid Build Coastguard Worker         return mMainImageBRD->computeOutputColorType(requestedColorType);
66*d57664e9SAndroid Build Coastguard Worker     }
67*d57664e9SAndroid Build Coastguard Worker 
computeOutputColorSpace(SkColorType outputColorType,sk_sp<SkColorSpace> prefColorSpace=nullptr)68*d57664e9SAndroid Build Coastguard Worker     sk_sp<SkColorSpace> computeOutputColorSpace(SkColorType outputColorType,
69*d57664e9SAndroid Build Coastguard Worker                                                 sk_sp<SkColorSpace> prefColorSpace = nullptr) {
70*d57664e9SAndroid Build Coastguard Worker         return mMainImageBRD->computeOutputColorSpace(outputColorType, prefColorSpace);
71*d57664e9SAndroid Build Coastguard Worker     }
72*d57664e9SAndroid Build Coastguard Worker 
decodeRegion(SkBitmap * bitmap,skia::BRDAllocator * allocator,const SkIRect & desiredSubset,int sampleSize,SkColorType colorType,bool requireUnpremul,sk_sp<SkColorSpace> prefColorSpace)73*d57664e9SAndroid Build Coastguard Worker     bool decodeRegion(SkBitmap* bitmap, skia::BRDAllocator* allocator, const SkIRect& desiredSubset,
74*d57664e9SAndroid Build Coastguard Worker                       int sampleSize, SkColorType colorType, bool requireUnpremul,
75*d57664e9SAndroid Build Coastguard Worker                       sk_sp<SkColorSpace> prefColorSpace) {
76*d57664e9SAndroid Build Coastguard Worker         return mMainImageBRD->decodeRegion(bitmap, allocator, desiredSubset, sampleSize, colorType,
77*d57664e9SAndroid Build Coastguard Worker                                            requireUnpremul, prefColorSpace);
78*d57664e9SAndroid Build Coastguard Worker     }
79*d57664e9SAndroid Build Coastguard Worker 
80*d57664e9SAndroid Build Coastguard Worker     // Decodes the gainmap region. If decoding succeeded, returns true and
81*d57664e9SAndroid Build Coastguard Worker     // populate outGainmap with the decoded gainmap. Otherwise, returns false.
82*d57664e9SAndroid Build Coastguard Worker     //
83*d57664e9SAndroid Build Coastguard Worker     // Note that the desiredSubset is the logical region within the source
84*d57664e9SAndroid Build Coastguard Worker     // gainmap that we want to decode. This is used for scaling into the final
85*d57664e9SAndroid Build Coastguard Worker     // bitmap, since we do not want to include portions of the gainmap outside
86*d57664e9SAndroid Build Coastguard Worker     // of this region. desiredSubset is also _not_ guaranteed to be
87*d57664e9SAndroid Build Coastguard Worker     // pixel-aligned, so it's not possible to simply resize the resulting
88*d57664e9SAndroid Build Coastguard Worker     // bitmap to accomplish this.
decodeGainmapRegion(sp<uirenderer::Gainmap> * outGainmap,SkISize bitmapDimensions,const SkRect & desiredSubset,int sampleSize,bool requireUnpremul)89*d57664e9SAndroid Build Coastguard Worker     bool decodeGainmapRegion(sp<uirenderer::Gainmap>* outGainmap, SkISize bitmapDimensions,
90*d57664e9SAndroid Build Coastguard Worker                              const SkRect& desiredSubset, int sampleSize, bool requireUnpremul) {
91*d57664e9SAndroid Build Coastguard Worker         SkColorType decodeColorType = mGainmapBRD->computeOutputColorType(kN32_SkColorType);
92*d57664e9SAndroid Build Coastguard Worker         sk_sp<SkColorSpace> decodeColorSpace =
93*d57664e9SAndroid Build Coastguard Worker                 mGainmapBRD->computeOutputColorSpace(decodeColorType, nullptr);
94*d57664e9SAndroid Build Coastguard Worker         SkBitmap bm;
95*d57664e9SAndroid Build Coastguard Worker         // Because we must match the dimensions of the base bitmap, we always use a
96*d57664e9SAndroid Build Coastguard Worker         // recycling allocator even though we are allocating a new bitmap. This is to ensure
97*d57664e9SAndroid Build Coastguard Worker         // that if a recycled bitmap was used for the base image that we match the relative
98*d57664e9SAndroid Build Coastguard Worker         // dimensions of that base image. The behavior of BRD here is:
99*d57664e9SAndroid Build Coastguard Worker         // if inBitmap is specified -> output dimensions are always equal to the inBitmap's
100*d57664e9SAndroid Build Coastguard Worker         // if no bitmap is reused   -> output dimensions are the intersect of the desiredSubset &
101*d57664e9SAndroid Build Coastguard Worker         //                           the image bounds
102*d57664e9SAndroid Build Coastguard Worker         // The handling of the above conditionals are baked into the desiredSubset, so we
103*d57664e9SAndroid Build Coastguard Worker         // simply need to ensure that the resulting bitmap is the exact same width/height as
104*d57664e9SAndroid Build Coastguard Worker         // the specified desiredSubset regardless of the intersection to the image bounds.
105*d57664e9SAndroid Build Coastguard Worker         // kPremul_SkAlphaType is used just as a placeholder as it doesn't change the underlying
106*d57664e9SAndroid Build Coastguard Worker         // allocation type. RecyclingClippingPixelAllocator will populate this with the
107*d57664e9SAndroid Build Coastguard Worker         // actual alpha type in either allocPixelRef() or copyIfNecessary()
108*d57664e9SAndroid Build Coastguard Worker         sk_sp<Bitmap> nativeBitmap = Bitmap::allocateHeapBitmap(SkImageInfo::Make(
109*d57664e9SAndroid Build Coastguard Worker                 bitmapDimensions, decodeColorType, kPremul_SkAlphaType, decodeColorSpace));
110*d57664e9SAndroid Build Coastguard Worker         if (!nativeBitmap) {
111*d57664e9SAndroid Build Coastguard Worker             ALOGE("OOM allocating Bitmap for Gainmap");
112*d57664e9SAndroid Build Coastguard Worker             return false;
113*d57664e9SAndroid Build Coastguard Worker         }
114*d57664e9SAndroid Build Coastguard Worker 
115*d57664e9SAndroid Build Coastguard Worker         sampleSize = std::max(sampleSize, 1);
116*d57664e9SAndroid Build Coastguard Worker 
117*d57664e9SAndroid Build Coastguard Worker         // Map the desired subset to the space of the decoded gainmap. The
118*d57664e9SAndroid Build Coastguard Worker         // subset is repositioned relative to the resulting bitmap, and then
119*d57664e9SAndroid Build Coastguard Worker         // scaled to respect the sampleSize.
120*d57664e9SAndroid Build Coastguard Worker         // This assumes that the subset will not be modified by the decoder, which is true
121*d57664e9SAndroid Build Coastguard Worker         // for existing gainmap formats.
122*d57664e9SAndroid Build Coastguard Worker         SkRect logicalSubset = desiredSubset.makeOffset(-std::floorf(desiredSubset.left()),
123*d57664e9SAndroid Build Coastguard Worker                                                         -std::floorf(desiredSubset.top()));
124*d57664e9SAndroid Build Coastguard Worker         logicalSubset = scale(logicalSubset, 1.0f / sampleSize);
125*d57664e9SAndroid Build Coastguard Worker 
126*d57664e9SAndroid Build Coastguard Worker         // Round out the subset so that we decode a slightly larger region, in
127*d57664e9SAndroid Build Coastguard Worker         // case the subset has fractional components. When we round, we need to
128*d57664e9SAndroid Build Coastguard Worker         // round the downsampled subset to avoid possibly rounding down by accident.
129*d57664e9SAndroid Build Coastguard Worker         // Consider this concrete example if we round the desired subset directly:
130*d57664e9SAndroid Build Coastguard Worker         //
131*d57664e9SAndroid Build Coastguard Worker         // * We are decoding a 18x18 corner of an image
132*d57664e9SAndroid Build Coastguard Worker         //
133*d57664e9SAndroid Build Coastguard Worker         // * Gainmap is 1/4 resolution, which is logically a 4.5x4.5 gainmap
134*d57664e9SAndroid Build Coastguard Worker         // that we would want
135*d57664e9SAndroid Build Coastguard Worker         //
136*d57664e9SAndroid Build Coastguard Worker         // * The app wants to downsample by a factor of 2x
137*d57664e9SAndroid Build Coastguard Worker         //
138*d57664e9SAndroid Build Coastguard Worker         // * The desired gainmap dimensions are computed to be 3x3 to fit the
139*d57664e9SAndroid Build Coastguard Worker         // downsampled gainmap, since we need to fill a 2.25x2.25 region that's
140*d57664e9SAndroid Build Coastguard Worker         // later upscaled to 3x3
141*d57664e9SAndroid Build Coastguard Worker         //
142*d57664e9SAndroid Build Coastguard Worker         // * But, if we round out the desired gainmap region _first_, then we
143*d57664e9SAndroid Build Coastguard Worker         // request to decode a 5x5 region, downsampled by 2, which actually
144*d57664e9SAndroid Build Coastguard Worker         // decodes a 2x2 region since skia rounds down internally. But then we transfer
145*d57664e9SAndroid Build Coastguard Worker         // the result to a 3x3 bitmap using a clipping allocator, which leaves an inset.
146*d57664e9SAndroid Build Coastguard Worker         // Not only did we get a smaller region than we expected (so, our desired subset is
147*d57664e9SAndroid Build Coastguard Worker         // not valid), but because the API allows for decoding regions using a recycled
148*d57664e9SAndroid Build Coastguard Worker         // bitmap, we can't really safely fill in the inset since then we might
149*d57664e9SAndroid Build Coastguard Worker         // extend the gainmap beyond intended the image bounds. Oops.
150*d57664e9SAndroid Build Coastguard Worker         //
151*d57664e9SAndroid Build Coastguard Worker         // * If we instead round out as if we downsampled, then we downsample
152*d57664e9SAndroid Build Coastguard Worker         // the desired region to 2.25x2.25, round out to 3x3, then upsample back
153*d57664e9SAndroid Build Coastguard Worker         // into the source gainmap space to get 6x6. Then we decode a 6x6 region
154*d57664e9SAndroid Build Coastguard Worker         // downsampled into a 3x3 region, and everything's now correct.
155*d57664e9SAndroid Build Coastguard Worker         //
156*d57664e9SAndroid Build Coastguard Worker         // Note that we don't always run into this problem, because
157*d57664e9SAndroid Build Coastguard Worker         // decoders actually round *up* for subsampling when decoding a subset
158*d57664e9SAndroid Build Coastguard Worker         // that matches the dimensions of the image. E.g., if the original image
159*d57664e9SAndroid Build Coastguard Worker         // size in the above example was a 20x20 image, so that the gainmap was
160*d57664e9SAndroid Build Coastguard Worker         // 5x5, then we still manage to downsample into a 3x3 bitmap even with
161*d57664e9SAndroid Build Coastguard Worker         // the "wrong" math. but that's what we wanted!
162*d57664e9SAndroid Build Coastguard Worker         //
163*d57664e9SAndroid Build Coastguard Worker         // Note also that if we overshoot the gainmap bounds with the requested
164*d57664e9SAndroid Build Coastguard Worker         // subset it isn't a problem either, since now the decoded bitmap is too
165*d57664e9SAndroid Build Coastguard Worker         // large, rather than too small, so now we can use the desired subset to
166*d57664e9SAndroid Build Coastguard Worker         // avoid sampling "invalid" colors.
167*d57664e9SAndroid Build Coastguard Worker         SkRect scaledSubset = scale(desiredSubset, 1.0f / sampleSize);
168*d57664e9SAndroid Build Coastguard Worker         SkIRect roundedSubset = scale(scaledSubset.roundOut(), static_cast<float>(sampleSize));
169*d57664e9SAndroid Build Coastguard Worker 
170*d57664e9SAndroid Build Coastguard Worker         RecyclingClippingPixelAllocator allocator(nativeBitmap.get(), false, logicalSubset);
171*d57664e9SAndroid Build Coastguard Worker         if (!mGainmapBRD->decodeRegion(&bm, &allocator, roundedSubset, sampleSize, decodeColorType,
172*d57664e9SAndroid Build Coastguard Worker                                        requireUnpremul, decodeColorSpace)) {
173*d57664e9SAndroid Build Coastguard Worker             ALOGE("Error decoding Gainmap region");
174*d57664e9SAndroid Build Coastguard Worker             return false;
175*d57664e9SAndroid Build Coastguard Worker         }
176*d57664e9SAndroid Build Coastguard Worker         allocator.copyIfNecessary();
177*d57664e9SAndroid Build Coastguard Worker         auto gainmap = sp<uirenderer::Gainmap>::make();
178*d57664e9SAndroid Build Coastguard Worker         if (!gainmap) {
179*d57664e9SAndroid Build Coastguard Worker             ALOGE("OOM allocating Gainmap");
180*d57664e9SAndroid Build Coastguard Worker             return false;
181*d57664e9SAndroid Build Coastguard Worker         }
182*d57664e9SAndroid Build Coastguard Worker         gainmap->info = mGainmapInfo;
183*d57664e9SAndroid Build Coastguard Worker         gainmap->bitmap = std::move(nativeBitmap);
184*d57664e9SAndroid Build Coastguard Worker         *outGainmap = std::move(gainmap);
185*d57664e9SAndroid Build Coastguard Worker         return true;
186*d57664e9SAndroid Build Coastguard Worker     }
187*d57664e9SAndroid Build Coastguard Worker 
188*d57664e9SAndroid Build Coastguard Worker     struct Projection {
189*d57664e9SAndroid Build Coastguard Worker         SkRect srcRect;
190*d57664e9SAndroid Build Coastguard Worker         SkISize destSize;
191*d57664e9SAndroid Build Coastguard Worker     };
calculateGainmapRegion(const SkIRect & mainImageRegion,SkISize dimensions)192*d57664e9SAndroid Build Coastguard Worker     Projection calculateGainmapRegion(const SkIRect& mainImageRegion, SkISize dimensions) {
193*d57664e9SAndroid Build Coastguard Worker         const float scaleX = ((float)mGainmapBRD->width()) / mMainImageBRD->width();
194*d57664e9SAndroid Build Coastguard Worker         const float scaleY = ((float)mGainmapBRD->height()) / mMainImageBRD->height();
195*d57664e9SAndroid Build Coastguard Worker 
196*d57664e9SAndroid Build Coastguard Worker         if (uirenderer::Properties::resampleGainmapRegions()) {
197*d57664e9SAndroid Build Coastguard Worker             const auto srcRect = SkRect::MakeLTRB(
198*d57664e9SAndroid Build Coastguard Worker                     mainImageRegion.left() * scaleX, mainImageRegion.top() * scaleY,
199*d57664e9SAndroid Build Coastguard Worker                     mainImageRegion.right() * scaleX, mainImageRegion.bottom() * scaleY);
200*d57664e9SAndroid Build Coastguard Worker             // Request a slightly larger destination size so that the gainmap
201*d57664e9SAndroid Build Coastguard Worker             // subset we want fits entirely in this size.
202*d57664e9SAndroid Build Coastguard Worker             const auto destSize = SkISize::Make(std::ceil(dimensions.width() * scaleX),
203*d57664e9SAndroid Build Coastguard Worker                                                 std::ceil(dimensions.height() * scaleY));
204*d57664e9SAndroid Build Coastguard Worker             return Projection{.srcRect = srcRect, .destSize = destSize};
205*d57664e9SAndroid Build Coastguard Worker         } else {
206*d57664e9SAndroid Build Coastguard Worker             const auto srcRect = SkRect::Make(SkIRect::MakeLTRB(
207*d57664e9SAndroid Build Coastguard Worker                     mainImageRegion.left() * scaleX, mainImageRegion.top() * scaleY,
208*d57664e9SAndroid Build Coastguard Worker                     mainImageRegion.right() * scaleX, mainImageRegion.bottom() * scaleY));
209*d57664e9SAndroid Build Coastguard Worker             const auto destSize =
210*d57664e9SAndroid Build Coastguard Worker                     SkISize::Make(dimensions.width() * scaleX, dimensions.height() * scaleY);
211*d57664e9SAndroid Build Coastguard Worker             return Projection{.srcRect = srcRect, .destSize = destSize};
212*d57664e9SAndroid Build Coastguard Worker         }
213*d57664e9SAndroid Build Coastguard Worker     }
214*d57664e9SAndroid Build Coastguard Worker 
hasGainmap()215*d57664e9SAndroid Build Coastguard Worker     bool hasGainmap() { return mGainmapBRD != nullptr; }
216*d57664e9SAndroid Build Coastguard Worker 
width() const217*d57664e9SAndroid Build Coastguard Worker     int width() const { return mMainImageBRD->width(); }
height() const218*d57664e9SAndroid Build Coastguard Worker     int height() const { return mMainImageBRD->height(); }
219*d57664e9SAndroid Build Coastguard Worker 
220*d57664e9SAndroid Build Coastguard Worker private:
BitmapRegionDecoderWrapper(std::unique_ptr<skia::BitmapRegionDecoder> mainImageBRD,std::unique_ptr<skia::BitmapRegionDecoder> gainmapBRD,SkGainmapInfo info)221*d57664e9SAndroid Build Coastguard Worker     BitmapRegionDecoderWrapper(std::unique_ptr<skia::BitmapRegionDecoder> mainImageBRD,
222*d57664e9SAndroid Build Coastguard Worker                                std::unique_ptr<skia::BitmapRegionDecoder> gainmapBRD,
223*d57664e9SAndroid Build Coastguard Worker                                SkGainmapInfo info)
224*d57664e9SAndroid Build Coastguard Worker             : mMainImageBRD(std::move(mainImageBRD))
225*d57664e9SAndroid Build Coastguard Worker             , mGainmapBRD(std::move(gainmapBRD))
226*d57664e9SAndroid Build Coastguard Worker             , mGainmapInfo(info) {}
227*d57664e9SAndroid Build Coastguard Worker 
scale(SkRect rect,float scale) const228*d57664e9SAndroid Build Coastguard Worker     SkRect scale(SkRect rect, float scale) const {
229*d57664e9SAndroid Build Coastguard Worker         rect.fLeft *= scale;
230*d57664e9SAndroid Build Coastguard Worker         rect.fTop *= scale;
231*d57664e9SAndroid Build Coastguard Worker         rect.fRight *= scale;
232*d57664e9SAndroid Build Coastguard Worker         rect.fBottom *= scale;
233*d57664e9SAndroid Build Coastguard Worker         return rect;
234*d57664e9SAndroid Build Coastguard Worker     }
235*d57664e9SAndroid Build Coastguard Worker 
scale(SkIRect rect,float scale) const236*d57664e9SAndroid Build Coastguard Worker     SkIRect scale(SkIRect rect, float scale) const {
237*d57664e9SAndroid Build Coastguard Worker         rect.fLeft *= scale;
238*d57664e9SAndroid Build Coastguard Worker         rect.fTop *= scale;
239*d57664e9SAndroid Build Coastguard Worker         rect.fRight *= scale;
240*d57664e9SAndroid Build Coastguard Worker         rect.fBottom *= scale;
241*d57664e9SAndroid Build Coastguard Worker         return rect;
242*d57664e9SAndroid Build Coastguard Worker     }
243*d57664e9SAndroid Build Coastguard Worker 
244*d57664e9SAndroid Build Coastguard Worker     std::unique_ptr<skia::BitmapRegionDecoder> mMainImageBRD;
245*d57664e9SAndroid Build Coastguard Worker     std::unique_ptr<skia::BitmapRegionDecoder> mGainmapBRD;
246*d57664e9SAndroid Build Coastguard Worker     SkGainmapInfo mGainmapInfo;
247*d57664e9SAndroid Build Coastguard Worker };
248*d57664e9SAndroid Build Coastguard Worker }  // namespace android
249*d57664e9SAndroid Build Coastguard Worker 
createBitmapRegionDecoder(JNIEnv * env,sk_sp<SkData> data)250*d57664e9SAndroid Build Coastguard Worker static jobject createBitmapRegionDecoder(JNIEnv* env, sk_sp<SkData> data) {
251*d57664e9SAndroid Build Coastguard Worker     auto brd = android::BitmapRegionDecoderWrapper::Make(std::move(data));
252*d57664e9SAndroid Build Coastguard Worker     if (!brd) {
253*d57664e9SAndroid Build Coastguard Worker         doThrowIOE(env, "Image format not supported");
254*d57664e9SAndroid Build Coastguard Worker         return nullObjectReturn("CreateBitmapRegionDecoder returned null");
255*d57664e9SAndroid Build Coastguard Worker     }
256*d57664e9SAndroid Build Coastguard Worker 
257*d57664e9SAndroid Build Coastguard Worker     return GraphicsJNI::createBitmapRegionDecoder(env, brd.release());
258*d57664e9SAndroid Build Coastguard Worker }
259*d57664e9SAndroid Build Coastguard Worker 
nativeNewInstanceFromByteArray(JNIEnv * env,jobject,jbyteArray byteArray,jint offset,jint length)260*d57664e9SAndroid Build Coastguard Worker static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
261*d57664e9SAndroid Build Coastguard Worker                                               jint offset, jint length) {
262*d57664e9SAndroid Build Coastguard Worker     AutoJavaByteArray ar(env, byteArray);
263*d57664e9SAndroid Build Coastguard Worker     return createBitmapRegionDecoder(env, SkData::MakeWithCopy(ar.ptr() + offset, length));
264*d57664e9SAndroid Build Coastguard Worker }
265*d57664e9SAndroid Build Coastguard Worker 
nativeNewInstanceFromFileDescriptor(JNIEnv * env,jobject clazz,jobject fileDescriptor)266*d57664e9SAndroid Build Coastguard Worker static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz,
267*d57664e9SAndroid Build Coastguard Worker                                                    jobject fileDescriptor) {
268*d57664e9SAndroid Build Coastguard Worker     NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
269*d57664e9SAndroid Build Coastguard Worker 
270*d57664e9SAndroid Build Coastguard Worker     jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
271*d57664e9SAndroid Build Coastguard Worker 
272*d57664e9SAndroid Build Coastguard Worker     struct stat fdStat;
273*d57664e9SAndroid Build Coastguard Worker     if (fstat(descriptor, &fdStat) == -1) {
274*d57664e9SAndroid Build Coastguard Worker         doThrowIOE(env, "broken file descriptor");
275*d57664e9SAndroid Build Coastguard Worker         return nullObjectReturn("fstat return -1");
276*d57664e9SAndroid Build Coastguard Worker     }
277*d57664e9SAndroid Build Coastguard Worker 
278*d57664e9SAndroid Build Coastguard Worker     return createBitmapRegionDecoder(env, SkData::MakeFromFD(descriptor));
279*d57664e9SAndroid Build Coastguard Worker }
280*d57664e9SAndroid Build Coastguard Worker 
nativeNewInstanceFromStream(JNIEnv * env,jobject clazz,jobject is,jbyteArray storage)281*d57664e9SAndroid Build Coastguard Worker static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz, jobject is, // InputStream
282*d57664e9SAndroid Build Coastguard Worker                                            jbyteArray storage) { // byte[]
283*d57664e9SAndroid Build Coastguard Worker     jobject brd = nullptr;
284*d57664e9SAndroid Build Coastguard Worker     sk_sp<SkData> data = CopyJavaInputStream(env, is, storage);
285*d57664e9SAndroid Build Coastguard Worker 
286*d57664e9SAndroid Build Coastguard Worker     if (data) {
287*d57664e9SAndroid Build Coastguard Worker         brd = createBitmapRegionDecoder(env, std::move(data));
288*d57664e9SAndroid Build Coastguard Worker     }
289*d57664e9SAndroid Build Coastguard Worker     return brd;
290*d57664e9SAndroid Build Coastguard Worker }
291*d57664e9SAndroid Build Coastguard Worker 
nativeNewInstanceFromAsset(JNIEnv * env,jobject clazz,jlong native_asset)292*d57664e9SAndroid Build Coastguard Worker static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz, jlong native_asset) {
293*d57664e9SAndroid Build Coastguard Worker     Asset* asset = reinterpret_cast<Asset*>(native_asset);
294*d57664e9SAndroid Build Coastguard Worker     sk_sp<SkData> data = CopyAssetToData(asset);
295*d57664e9SAndroid Build Coastguard Worker     if (!data) {
296*d57664e9SAndroid Build Coastguard Worker         return nullptr;
297*d57664e9SAndroid Build Coastguard Worker     }
298*d57664e9SAndroid Build Coastguard Worker 
299*d57664e9SAndroid Build Coastguard Worker     return createBitmapRegionDecoder(env, data);
300*d57664e9SAndroid Build Coastguard Worker }
301*d57664e9SAndroid Build Coastguard Worker 
302*d57664e9SAndroid Build Coastguard Worker /*
303*d57664e9SAndroid Build Coastguard Worker  * nine patch not supported
304*d57664e9SAndroid Build Coastguard Worker  * purgeable not supported
305*d57664e9SAndroid Build Coastguard Worker  * reportSizeToVM not supported
306*d57664e9SAndroid Build Coastguard Worker  */
nativeDecodeRegion(JNIEnv * env,jobject,jlong brdHandle,jint inputX,jint inputY,jint inputWidth,jint inputHeight,jobject options,jlong inBitmapHandle,jlong colorSpaceHandle)307*d57664e9SAndroid Build Coastguard Worker static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint inputX,
308*d57664e9SAndroid Build Coastguard Worker         jint inputY, jint inputWidth, jint inputHeight, jobject options, jlong inBitmapHandle,
309*d57664e9SAndroid Build Coastguard Worker         jlong colorSpaceHandle) {
310*d57664e9SAndroid Build Coastguard Worker 
311*d57664e9SAndroid Build Coastguard Worker     // Set default options.
312*d57664e9SAndroid Build Coastguard Worker     int sampleSize = 1;
313*d57664e9SAndroid Build Coastguard Worker     SkColorType colorType = kN32_SkColorType;
314*d57664e9SAndroid Build Coastguard Worker     bool requireUnpremul = false;
315*d57664e9SAndroid Build Coastguard Worker     jobject javaBitmap = nullptr;
316*d57664e9SAndroid Build Coastguard Worker     bool isHardware = false;
317*d57664e9SAndroid Build Coastguard Worker     sk_sp<SkColorSpace> colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
318*d57664e9SAndroid Build Coastguard Worker     // Update the default options with any options supplied by the client.
319*d57664e9SAndroid Build Coastguard Worker     if (NULL != options) {
320*d57664e9SAndroid Build Coastguard Worker         sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
321*d57664e9SAndroid Build Coastguard Worker         jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
322*d57664e9SAndroid Build Coastguard Worker         colorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
323*d57664e9SAndroid Build Coastguard Worker         isHardware = GraphicsJNI::isHardwareConfig(env, jconfig);
324*d57664e9SAndroid Build Coastguard Worker         requireUnpremul = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
325*d57664e9SAndroid Build Coastguard Worker         javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
326*d57664e9SAndroid Build Coastguard Worker         // The Java options of ditherMode and preferQualityOverSpeed are deprecated.  We will
327*d57664e9SAndroid Build Coastguard Worker         // ignore the values of these fields.
328*d57664e9SAndroid Build Coastguard Worker 
329*d57664e9SAndroid Build Coastguard Worker         // Initialize these fields to indicate a failure.  If the decode succeeds, we
330*d57664e9SAndroid Build Coastguard Worker         // will update them later on.
331*d57664e9SAndroid Build Coastguard Worker         env->SetIntField(options, gOptions_widthFieldID, -1);
332*d57664e9SAndroid Build Coastguard Worker         env->SetIntField(options, gOptions_heightFieldID, -1);
333*d57664e9SAndroid Build Coastguard Worker         env->SetObjectField(options, gOptions_mimeFieldID, 0);
334*d57664e9SAndroid Build Coastguard Worker         env->SetObjectField(options, gOptions_outConfigFieldID, 0);
335*d57664e9SAndroid Build Coastguard Worker         env->SetObjectField(options, gOptions_outColorSpaceFieldID, 0);
336*d57664e9SAndroid Build Coastguard Worker     }
337*d57664e9SAndroid Build Coastguard Worker 
338*d57664e9SAndroid Build Coastguard Worker     // Recycle a bitmap if possible.
339*d57664e9SAndroid Build Coastguard Worker     android::Bitmap* recycledBitmap = nullptr;
340*d57664e9SAndroid Build Coastguard Worker     if (javaBitmap) {
341*d57664e9SAndroid Build Coastguard Worker         recycledBitmap = &bitmap::toBitmap(inBitmapHandle);
342*d57664e9SAndroid Build Coastguard Worker         if (recycledBitmap->isImmutable()) {
343*d57664e9SAndroid Build Coastguard Worker             ALOGW("Warning: Reusing an immutable bitmap as an image decoder target.");
344*d57664e9SAndroid Build Coastguard Worker         }
345*d57664e9SAndroid Build Coastguard Worker     }
346*d57664e9SAndroid Build Coastguard Worker 
347*d57664e9SAndroid Build Coastguard Worker     auto* brd = reinterpret_cast<BitmapRegionDecoderWrapper*>(brdHandle);
348*d57664e9SAndroid Build Coastguard Worker     SkColorType decodeColorType = brd->computeOutputColorType(colorType);
349*d57664e9SAndroid Build Coastguard Worker 
350*d57664e9SAndroid Build Coastguard Worker     if (isHardware) {
351*d57664e9SAndroid Build Coastguard Worker         if (decodeColorType == kRGBA_F16_SkColorType &&
352*d57664e9SAndroid Build Coastguard Worker             !uirenderer::HardwareBitmapUploader::hasFP16Support()) {
353*d57664e9SAndroid Build Coastguard Worker             decodeColorType = kN32_SkColorType;
354*d57664e9SAndroid Build Coastguard Worker         }
355*d57664e9SAndroid Build Coastguard Worker         if (decodeColorType == kRGBA_1010102_SkColorType &&
356*d57664e9SAndroid Build Coastguard Worker             !uirenderer::HardwareBitmapUploader::has1010102Support()) {
357*d57664e9SAndroid Build Coastguard Worker             decodeColorType = kN32_SkColorType;
358*d57664e9SAndroid Build Coastguard Worker         }
359*d57664e9SAndroid Build Coastguard Worker     }
360*d57664e9SAndroid Build Coastguard Worker 
361*d57664e9SAndroid Build Coastguard Worker     // Set up the pixel allocator
362*d57664e9SAndroid Build Coastguard Worker     skia::BRDAllocator* allocator = nullptr;
363*d57664e9SAndroid Build Coastguard Worker     RecyclingClippingPixelAllocator recycleAlloc(recycledBitmap);
364*d57664e9SAndroid Build Coastguard Worker     HeapAllocator heapAlloc;
365*d57664e9SAndroid Build Coastguard Worker     if (javaBitmap) {
366*d57664e9SAndroid Build Coastguard Worker         allocator = &recycleAlloc;
367*d57664e9SAndroid Build Coastguard Worker         // We are required to match the color type of the recycled bitmap.
368*d57664e9SAndroid Build Coastguard Worker         decodeColorType = recycledBitmap->info().colorType();
369*d57664e9SAndroid Build Coastguard Worker     } else {
370*d57664e9SAndroid Build Coastguard Worker         allocator = &heapAlloc;
371*d57664e9SAndroid Build Coastguard Worker     }
372*d57664e9SAndroid Build Coastguard Worker 
373*d57664e9SAndroid Build Coastguard Worker     sk_sp<SkColorSpace> decodeColorSpace = brd->computeOutputColorSpace(
374*d57664e9SAndroid Build Coastguard Worker             decodeColorType, colorSpace);
375*d57664e9SAndroid Build Coastguard Worker 
376*d57664e9SAndroid Build Coastguard Worker     // Decode the region.
377*d57664e9SAndroid Build Coastguard Worker     const SkIRect subset = SkIRect::MakeXYWH(inputX, inputY, inputWidth, inputHeight);
378*d57664e9SAndroid Build Coastguard Worker     SkBitmap bitmap;
379*d57664e9SAndroid Build Coastguard Worker     if (!brd->decodeRegion(&bitmap, allocator, subset, sampleSize,
380*d57664e9SAndroid Build Coastguard Worker             decodeColorType, requireUnpremul, decodeColorSpace)) {
381*d57664e9SAndroid Build Coastguard Worker         return nullObjectReturn("Failed to decode region.");
382*d57664e9SAndroid Build Coastguard Worker     }
383*d57664e9SAndroid Build Coastguard Worker 
384*d57664e9SAndroid Build Coastguard Worker     // If the client provided options, indicate that the decode was successful.
385*d57664e9SAndroid Build Coastguard Worker     if (NULL != options) {
386*d57664e9SAndroid Build Coastguard Worker         env->SetIntField(options, gOptions_widthFieldID, bitmap.width());
387*d57664e9SAndroid Build Coastguard Worker         env->SetIntField(options, gOptions_heightFieldID, bitmap.height());
388*d57664e9SAndroid Build Coastguard Worker 
389*d57664e9SAndroid Build Coastguard Worker         env->SetObjectField(options, gOptions_mimeFieldID,
390*d57664e9SAndroid Build Coastguard Worker                 getMimeTypeAsJavaString(env, brd->getEncodedFormat()));
391*d57664e9SAndroid Build Coastguard Worker         if (env->ExceptionCheck()) {
392*d57664e9SAndroid Build Coastguard Worker             return nullObjectReturn("OOM in encodedFormatToString()");
393*d57664e9SAndroid Build Coastguard Worker         }
394*d57664e9SAndroid Build Coastguard Worker 
395*d57664e9SAndroid Build Coastguard Worker         jint configID = GraphicsJNI::colorTypeToLegacyBitmapConfig(decodeColorType);
396*d57664e9SAndroid Build Coastguard Worker         if (isHardware) {
397*d57664e9SAndroid Build Coastguard Worker             configID = GraphicsJNI::kHardware_LegacyBitmapConfig;
398*d57664e9SAndroid Build Coastguard Worker         }
399*d57664e9SAndroid Build Coastguard Worker         jobject config = env->CallStaticObjectMethod(gBitmapConfig_class,
400*d57664e9SAndroid Build Coastguard Worker                 gBitmapConfig_nativeToConfigMethodID, configID);
401*d57664e9SAndroid Build Coastguard Worker         env->SetObjectField(options, gOptions_outConfigFieldID, config);
402*d57664e9SAndroid Build Coastguard Worker 
403*d57664e9SAndroid Build Coastguard Worker         env->SetObjectField(options, gOptions_outColorSpaceFieldID,
404*d57664e9SAndroid Build Coastguard Worker                 GraphicsJNI::getColorSpace(env, decodeColorSpace.get(), decodeColorType));
405*d57664e9SAndroid Build Coastguard Worker     }
406*d57664e9SAndroid Build Coastguard Worker 
407*d57664e9SAndroid Build Coastguard Worker     if (javaBitmap) {
408*d57664e9SAndroid Build Coastguard Worker         recycleAlloc.copyIfNecessary();
409*d57664e9SAndroid Build Coastguard Worker     }
410*d57664e9SAndroid Build Coastguard Worker 
411*d57664e9SAndroid Build Coastguard Worker     sp<uirenderer::Gainmap> gainmap;
412*d57664e9SAndroid Build Coastguard Worker     bool hasGainmap = brd->hasGainmap();
413*d57664e9SAndroid Build Coastguard Worker     if (hasGainmap) {
414*d57664e9SAndroid Build Coastguard Worker         SkISize gainmapDims = SkISize::Make(bitmap.width(), bitmap.height());
415*d57664e9SAndroid Build Coastguard Worker         if (javaBitmap) {
416*d57664e9SAndroid Build Coastguard Worker             // If we are recycling we must match the inBitmap's relative dimensions
417*d57664e9SAndroid Build Coastguard Worker             gainmapDims.fWidth = recycledBitmap->width();
418*d57664e9SAndroid Build Coastguard Worker             gainmapDims.fHeight = recycledBitmap->height();
419*d57664e9SAndroid Build Coastguard Worker         }
420*d57664e9SAndroid Build Coastguard Worker         BitmapRegionDecoderWrapper::Projection gainmapProjection =
421*d57664e9SAndroid Build Coastguard Worker                 brd->calculateGainmapRegion(subset, gainmapDims);
422*d57664e9SAndroid Build Coastguard Worker         if (!brd->decodeGainmapRegion(&gainmap, gainmapProjection.destSize,
423*d57664e9SAndroid Build Coastguard Worker                                       gainmapProjection.srcRect, sampleSize, requireUnpremul)) {
424*d57664e9SAndroid Build Coastguard Worker             // If there is an error decoding Gainmap - we don't fail. We just don't provide Gainmap
425*d57664e9SAndroid Build Coastguard Worker             hasGainmap = false;
426*d57664e9SAndroid Build Coastguard Worker         }
427*d57664e9SAndroid Build Coastguard Worker     }
428*d57664e9SAndroid Build Coastguard Worker 
429*d57664e9SAndroid Build Coastguard Worker     // If we may have reused a bitmap, we need to indicate that the pixels have changed.
430*d57664e9SAndroid Build Coastguard Worker     if (javaBitmap) {
431*d57664e9SAndroid Build Coastguard Worker         if (hasGainmap) {
432*d57664e9SAndroid Build Coastguard Worker             recycledBitmap->setGainmap(std::move(gainmap));
433*d57664e9SAndroid Build Coastguard Worker         }
434*d57664e9SAndroid Build Coastguard Worker         bitmap::reinitBitmap(env, javaBitmap, recycledBitmap->info(), !requireUnpremul);
435*d57664e9SAndroid Build Coastguard Worker         uirenderer::logBitmapDecode(*recycledBitmap);
436*d57664e9SAndroid Build Coastguard Worker         return javaBitmap;
437*d57664e9SAndroid Build Coastguard Worker     }
438*d57664e9SAndroid Build Coastguard Worker 
439*d57664e9SAndroid Build Coastguard Worker     int bitmapCreateFlags = 0;
440*d57664e9SAndroid Build Coastguard Worker     if (!requireUnpremul) {
441*d57664e9SAndroid Build Coastguard Worker         bitmapCreateFlags |= android::bitmap::kBitmapCreateFlag_Premultiplied;
442*d57664e9SAndroid Build Coastguard Worker     }
443*d57664e9SAndroid Build Coastguard Worker 
444*d57664e9SAndroid Build Coastguard Worker     if (isHardware) {
445*d57664e9SAndroid Build Coastguard Worker         sk_sp<Bitmap> hardwareBitmap = Bitmap::allocateHardwareBitmap(bitmap);
446*d57664e9SAndroid Build Coastguard Worker         if (hasGainmap) {
447*d57664e9SAndroid Build Coastguard Worker             auto gm = uirenderer::Gainmap::allocateHardwareGainmap(gainmap);
448*d57664e9SAndroid Build Coastguard Worker             if (gm) {
449*d57664e9SAndroid Build Coastguard Worker                 hardwareBitmap->setGainmap(std::move(gm));
450*d57664e9SAndroid Build Coastguard Worker             }
451*d57664e9SAndroid Build Coastguard Worker         }
452*d57664e9SAndroid Build Coastguard Worker         uirenderer::logBitmapDecode(*hardwareBitmap);
453*d57664e9SAndroid Build Coastguard Worker         return bitmap::createBitmap(env, hardwareBitmap.release(), bitmapCreateFlags);
454*d57664e9SAndroid Build Coastguard Worker     }
455*d57664e9SAndroid Build Coastguard Worker     Bitmap* heapBitmap = heapAlloc.getStorageObjAndReset();
456*d57664e9SAndroid Build Coastguard Worker     if (hasGainmap && heapBitmap != nullptr) {
457*d57664e9SAndroid Build Coastguard Worker         heapBitmap->setGainmap(std::move(gainmap));
458*d57664e9SAndroid Build Coastguard Worker     }
459*d57664e9SAndroid Build Coastguard Worker     uirenderer::logBitmapDecode(*heapBitmap);
460*d57664e9SAndroid Build Coastguard Worker     return android::bitmap::createBitmap(env, heapBitmap, bitmapCreateFlags);
461*d57664e9SAndroid Build Coastguard Worker }
462*d57664e9SAndroid Build Coastguard Worker 
nativeGetHeight(JNIEnv * env,jobject,jlong brdHandle)463*d57664e9SAndroid Build Coastguard Worker static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) {
464*d57664e9SAndroid Build Coastguard Worker     auto* brd = reinterpret_cast<BitmapRegionDecoderWrapper*>(brdHandle);
465*d57664e9SAndroid Build Coastguard Worker     return static_cast<jint>(brd->height());
466*d57664e9SAndroid Build Coastguard Worker }
467*d57664e9SAndroid Build Coastguard Worker 
nativeGetWidth(JNIEnv * env,jobject,jlong brdHandle)468*d57664e9SAndroid Build Coastguard Worker static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) {
469*d57664e9SAndroid Build Coastguard Worker     auto* brd = reinterpret_cast<BitmapRegionDecoderWrapper*>(brdHandle);
470*d57664e9SAndroid Build Coastguard Worker     return static_cast<jint>(brd->width());
471*d57664e9SAndroid Build Coastguard Worker }
472*d57664e9SAndroid Build Coastguard Worker 
nativeClean(JNIEnv * env,jobject,jlong brdHandle)473*d57664e9SAndroid Build Coastguard Worker static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) {
474*d57664e9SAndroid Build Coastguard Worker     auto* brd = reinterpret_cast<BitmapRegionDecoderWrapper*>(brdHandle);
475*d57664e9SAndroid Build Coastguard Worker     delete brd;
476*d57664e9SAndroid Build Coastguard Worker }
477*d57664e9SAndroid Build Coastguard Worker 
478*d57664e9SAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////
479*d57664e9SAndroid Build Coastguard Worker 
480*d57664e9SAndroid Build Coastguard Worker static const JNINativeMethod gBitmapRegionDecoderMethods[] = {
481*d57664e9SAndroid Build Coastguard Worker     {   "nativeDecodeRegion",
482*d57664e9SAndroid Build Coastguard Worker         "(JIIIILandroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;",
483*d57664e9SAndroid Build Coastguard Worker         (void*)nativeDecodeRegion},
484*d57664e9SAndroid Build Coastguard Worker 
485*d57664e9SAndroid Build Coastguard Worker     {   "nativeGetHeight", "(J)I", (void*)nativeGetHeight},
486*d57664e9SAndroid Build Coastguard Worker 
487*d57664e9SAndroid Build Coastguard Worker     {   "nativeGetWidth", "(J)I", (void*)nativeGetWidth},
488*d57664e9SAndroid Build Coastguard Worker 
489*d57664e9SAndroid Build Coastguard Worker     {   "nativeClean", "(J)V", (void*)nativeClean},
490*d57664e9SAndroid Build Coastguard Worker 
491*d57664e9SAndroid Build Coastguard Worker     {   "nativeNewInstance",
492*d57664e9SAndroid Build Coastguard Worker         "([BII)Landroid/graphics/BitmapRegionDecoder;",
493*d57664e9SAndroid Build Coastguard Worker         (void*)nativeNewInstanceFromByteArray
494*d57664e9SAndroid Build Coastguard Worker     },
495*d57664e9SAndroid Build Coastguard Worker 
496*d57664e9SAndroid Build Coastguard Worker     {   "nativeNewInstance",
497*d57664e9SAndroid Build Coastguard Worker         "(Ljava/io/InputStream;[B)Landroid/graphics/BitmapRegionDecoder;",
498*d57664e9SAndroid Build Coastguard Worker         (void*)nativeNewInstanceFromStream
499*d57664e9SAndroid Build Coastguard Worker     },
500*d57664e9SAndroid Build Coastguard Worker 
501*d57664e9SAndroid Build Coastguard Worker     {   "nativeNewInstance",
502*d57664e9SAndroid Build Coastguard Worker         "(Ljava/io/FileDescriptor;)Landroid/graphics/BitmapRegionDecoder;",
503*d57664e9SAndroid Build Coastguard Worker         (void*)nativeNewInstanceFromFileDescriptor
504*d57664e9SAndroid Build Coastguard Worker     },
505*d57664e9SAndroid Build Coastguard Worker 
506*d57664e9SAndroid Build Coastguard Worker     {   "nativeNewInstance",
507*d57664e9SAndroid Build Coastguard Worker         "(J)Landroid/graphics/BitmapRegionDecoder;",
508*d57664e9SAndroid Build Coastguard Worker         (void*)nativeNewInstanceFromAsset
509*d57664e9SAndroid Build Coastguard Worker     },
510*d57664e9SAndroid Build Coastguard Worker };
511*d57664e9SAndroid Build Coastguard Worker 
register_android_graphics_BitmapRegionDecoder(JNIEnv * env)512*d57664e9SAndroid Build Coastguard Worker int register_android_graphics_BitmapRegionDecoder(JNIEnv* env)
513*d57664e9SAndroid Build Coastguard Worker {
514*d57664e9SAndroid Build Coastguard Worker     return android::RegisterMethodsOrDie(env, "android/graphics/BitmapRegionDecoder",
515*d57664e9SAndroid Build Coastguard Worker             gBitmapRegionDecoderMethods, NELEM(gBitmapRegionDecoderMethods));
516*d57664e9SAndroid Build Coastguard Worker }
517