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