1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <log/log.h>
18 
19 #include "android/graphics/bitmap.h"
20 #include "TypeCast.h"
21 #include "GraphicsJNI.h"
22 
23 #include <GraphicsJNI.h>
24 #include <hwui/Bitmap.h>
25 #include <SkBitmap.h>
26 #include <SkColorSpace.h>
27 #include <SkImageInfo.h>
28 #include <SkRefCnt.h>
29 #include <SkStream.h>
30 #include <utils/Color.h>
31 
32 using namespace android;
33 
ABitmap_acquireBitmapFromJava(JNIEnv * env,jobject bitmapObj)34 ABitmap* ABitmap_acquireBitmapFromJava(JNIEnv* env, jobject bitmapObj) {
35     Bitmap* bitmap = GraphicsJNI::getNativeBitmap(env, bitmapObj);
36     if (bitmap) {
37         bitmap->ref();
38         return TypeCast::toABitmap(bitmap);
39     }
40     return nullptr;
41 }
42 
ABitmap_acquireRef(ABitmap * bitmap)43 void ABitmap_acquireRef(ABitmap* bitmap) {
44     SkSafeRef(TypeCast::toBitmap(bitmap));
45 }
46 
ABitmap_releaseRef(ABitmap * bitmap)47 void ABitmap_releaseRef(ABitmap* bitmap) {
48     SkSafeUnref(TypeCast::toBitmap(bitmap));
49 }
50 
getFormat(const SkImageInfo & info)51 static AndroidBitmapFormat getFormat(const SkImageInfo& info) {
52     switch (info.colorType()) {
53         case kN32_SkColorType:
54             return ANDROID_BITMAP_FORMAT_RGBA_8888;
55         case kRGB_565_SkColorType:
56             return ANDROID_BITMAP_FORMAT_RGB_565;
57         case kARGB_4444_SkColorType:
58             return ANDROID_BITMAP_FORMAT_RGBA_4444;
59         case kAlpha_8_SkColorType:
60             return ANDROID_BITMAP_FORMAT_A_8;
61         case kRGBA_F16_SkColorType:
62             return ANDROID_BITMAP_FORMAT_RGBA_F16;
63         case kRGBA_1010102_SkColorType:
64             return ANDROID_BITMAP_FORMAT_RGBA_1010102;
65         default:
66             return ANDROID_BITMAP_FORMAT_NONE;
67     }
68 }
69 
getColorType(AndroidBitmapFormat format)70 static SkColorType getColorType(AndroidBitmapFormat format) {
71     switch (format) {
72         case ANDROID_BITMAP_FORMAT_RGBA_8888:
73             return kN32_SkColorType;
74         case ANDROID_BITMAP_FORMAT_RGB_565:
75             return kRGB_565_SkColorType;
76         case ANDROID_BITMAP_FORMAT_RGBA_4444:
77             return kARGB_4444_SkColorType;
78         case ANDROID_BITMAP_FORMAT_A_8:
79             return kAlpha_8_SkColorType;
80         case ANDROID_BITMAP_FORMAT_RGBA_F16:
81             return kRGBA_F16_SkColorType;
82         case ANDROID_BITMAP_FORMAT_RGBA_1010102:
83             return kRGBA_1010102_SkColorType;
84         default:
85             return kUnknown_SkColorType;
86     }
87 }
88 
getAlphaFlags(const SkImageInfo & info)89 static uint32_t getAlphaFlags(const SkImageInfo& info) {
90     switch (info.alphaType()) {
91         case kUnknown_SkAlphaType:
92             LOG_ALWAYS_FATAL("Bitmap has no alpha type");
93             break;
94         case kOpaque_SkAlphaType:
95             return ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE;
96         case kPremul_SkAlphaType:
97             return ANDROID_BITMAP_FLAGS_ALPHA_PREMUL;
98         case kUnpremul_SkAlphaType:
99             return ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL;
100     }
101 }
102 
getInfoFlags(const SkImageInfo & info,bool isHardware)103 static uint32_t getInfoFlags(const SkImageInfo& info, bool isHardware) {
104     uint32_t flags = getAlphaFlags(info);
105     if (isHardware) {
106         flags |= ANDROID_BITMAP_FLAGS_IS_HARDWARE;
107     }
108     return flags;
109 }
110 
ABitmap_copy(ABitmap * srcBitmapHandle,AndroidBitmapFormat dstFormat)111 ABitmap* ABitmap_copy(ABitmap* srcBitmapHandle, AndroidBitmapFormat dstFormat) {
112     SkColorType dstColorType = getColorType(dstFormat);
113     if (srcBitmapHandle && dstColorType != kUnknown_SkColorType) {
114         SkBitmap srcBitmap;
115         TypeCast::toBitmap(srcBitmapHandle)->getSkBitmap(&srcBitmap);
116 
117         sk_sp<Bitmap> dstBitmap =
118                 Bitmap::allocateHeapBitmap(srcBitmap.info().makeColorType(dstColorType));
119         if (dstBitmap && srcBitmap.readPixels(dstBitmap->info(), dstBitmap->pixels(),
120                                               dstBitmap->rowBytes(), 0, 0)) {
121             return TypeCast::toABitmap(dstBitmap.release());
122         }
123     }
124     return nullptr;
125 }
126 
getInfo(const SkImageInfo & imageInfo,uint32_t rowBytes,bool isHardware)127 static AndroidBitmapInfo getInfo(const SkImageInfo& imageInfo, uint32_t rowBytes, bool isHardware) {
128     AndroidBitmapInfo info;
129     info.width = imageInfo.width();
130     info.height = imageInfo.height();
131     info.stride = rowBytes;
132     info.format = getFormat(imageInfo);
133     info.flags = getInfoFlags(imageInfo, isHardware);
134     return info;
135 }
136 
ABitmap_getInfo(ABitmap * bitmapHandle)137 AndroidBitmapInfo ABitmap_getInfo(ABitmap* bitmapHandle) {
138     Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle);
139     return getInfo(bitmap->info(), bitmap->rowBytes(), bitmap->isHardware());
140 }
141 
ABitmap_getDataSpace(ABitmap * bitmapHandle)142 ADataSpace ABitmap_getDataSpace(ABitmap* bitmapHandle) {
143     Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle);
144     const SkImageInfo& info = bitmap->info();
145     return (ADataSpace)uirenderer::ColorSpaceToADataSpace(info.colorSpace(), info.colorType());
146 }
147 
ABitmap_getInfoFromJava(JNIEnv * env,jobject bitmapObj)148 AndroidBitmapInfo ABitmap_getInfoFromJava(JNIEnv* env, jobject bitmapObj) {
149     uint32_t rowBytes = 0;
150     bool isHardware = false;
151     SkImageInfo imageInfo = GraphicsJNI::getBitmapInfo(env, bitmapObj, &rowBytes, &isHardware);
152     return getInfo(imageInfo, rowBytes, isHardware);
153 }
154 
ABitmap_getPixels(ABitmap * bitmapHandle)155 void* ABitmap_getPixels(ABitmap* bitmapHandle) {
156     Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle);
157     if (bitmap->isHardware()) {
158         return nullptr;
159     }
160     return bitmap->pixels();
161 }
162 
ABitmapConfig_getFormatFromConfig(JNIEnv * env,jobject bitmapConfigObj)163 AndroidBitmapFormat ABitmapConfig_getFormatFromConfig(JNIEnv* env, jobject bitmapConfigObj) {
164     return GraphicsJNI::getFormatFromConfig(env, bitmapConfigObj);
165 }
166 
ABitmapConfig_getConfigFromFormat(JNIEnv * env,AndroidBitmapFormat format)167 jobject ABitmapConfig_getConfigFromFormat(JNIEnv* env, AndroidBitmapFormat format) {
168     return GraphicsJNI::getConfigFromFormat(env, format);
169 }
170 
ABitmap_notifyPixelsChanged(ABitmap * bitmapHandle)171 void ABitmap_notifyPixelsChanged(ABitmap* bitmapHandle) {
172     Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle);
173     if (!bitmap->isImmutable()) {
174         bitmap->notifyPixelsChanged();
175     }
176 }
177 
178 namespace {
getAlphaType(const AndroidBitmapInfo * info)179 SkAlphaType getAlphaType(const AndroidBitmapInfo* info) {
180     switch (info->flags & ANDROID_BITMAP_FLAGS_ALPHA_MASK) {
181         case ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE:
182             return kOpaque_SkAlphaType;
183         case ANDROID_BITMAP_FLAGS_ALPHA_PREMUL:
184             return kPremul_SkAlphaType;
185         case ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL:
186             return kUnpremul_SkAlphaType;
187         default:
188             return kUnknown_SkAlphaType;
189     }
190 }
191 
192 class CompressWriter : public SkWStream {
193 public:
CompressWriter(void * userContext,AndroidBitmap_CompressWriteFunc fn)194     CompressWriter(void* userContext, AndroidBitmap_CompressWriteFunc fn)
195           : mUserContext(userContext), mFn(fn), mBytesWritten(0) {}
196 
write(const void * buffer,size_t size)197     bool write(const void* buffer, size_t size) override {
198         if (mFn(mUserContext, buffer, size)) {
199             mBytesWritten += size;
200             return true;
201         }
202         return false;
203     }
204 
bytesWritten() const205     size_t bytesWritten() const override { return mBytesWritten; }
206 
207 private:
208     void* mUserContext;
209     AndroidBitmap_CompressWriteFunc mFn;
210     size_t mBytesWritten;
211 };
212 
213 } // anonymous namespace
214 
ABitmap_compress(const AndroidBitmapInfo * info,ADataSpace dataSpace,const void * pixels,AndroidBitmapCompressFormat inFormat,int32_t quality,void * userContext,AndroidBitmap_CompressWriteFunc fn)215 int ABitmap_compress(const AndroidBitmapInfo* info, ADataSpace dataSpace, const void* pixels,
216                      AndroidBitmapCompressFormat inFormat, int32_t quality, void* userContext,
217                      AndroidBitmap_CompressWriteFunc fn) {
218     Bitmap::JavaCompressFormat format;
219     switch (inFormat) {
220         case ANDROID_BITMAP_COMPRESS_FORMAT_JPEG:
221             format = Bitmap::JavaCompressFormat::Jpeg;
222             break;
223         case ANDROID_BITMAP_COMPRESS_FORMAT_PNG:
224             format = Bitmap::JavaCompressFormat::Png;
225             break;
226         case ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSY:
227             format = Bitmap::JavaCompressFormat::WebpLossy;
228             break;
229         case ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSLESS:
230             format = Bitmap::JavaCompressFormat::WebpLossless;
231             break;
232         default:
233             // kWEBP_JavaEncodeFormat is a valid parameter for Bitmap::compress,
234             // for the deprecated Bitmap.CompressFormat.WEBP, but it should not
235             // be provided via the NDK. Other integers are likewise invalid.
236             return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
237     }
238 
239     SkColorType colorType;
240     switch (info->format) {
241         case ANDROID_BITMAP_FORMAT_RGBA_8888:
242             colorType = kN32_SkColorType;
243             break;
244         case ANDROID_BITMAP_FORMAT_RGB_565:
245             colorType = kRGB_565_SkColorType;
246             break;
247         case ANDROID_BITMAP_FORMAT_A_8:
248             // FIXME b/146637821: Should this encode as grayscale? We should
249             // make the same decision as for encoding an android.graphics.Bitmap.
250             // Note that encoding kAlpha_8 as WebP or JPEG will fail. Encoding
251             // it to PNG encodes as GRAY+ALPHA with a secret handshake that we
252             // only care about the alpha. I'm not sure whether Android decoding
253             // APIs respect that handshake.
254             colorType = kAlpha_8_SkColorType;
255             break;
256         case ANDROID_BITMAP_FORMAT_RGBA_F16:
257             colorType = kRGBA_F16_SkColorType;
258             break;
259         case ANDROID_BITMAP_FORMAT_RGBA_1010102:
260             colorType = kRGBA_1010102_SkColorType;
261             break;
262         default:
263             return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
264     }
265 
266     auto alphaType = getAlphaType(info);
267     if (alphaType == kUnknown_SkAlphaType) {
268         return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
269     }
270 
271     sk_sp<SkColorSpace> cs;
272     if (info->format == ANDROID_BITMAP_FORMAT_A_8) {
273         // FIXME: A Java Bitmap with ALPHA_8 never has a ColorSpace. So should
274         // we force that here (as I'm doing now) or should we treat anything
275         // besides ADATASPACE_UNKNOWN as an error?
276         cs = nullptr;
277     } else {
278         cs = uirenderer::DataSpaceToColorSpace((android_dataspace) dataSpace);
279         // DataSpaceToColorSpace treats UNKNOWN as SRGB, but compress forces the
280         // client to specify SRGB if that is what they want.
281         if (!cs || dataSpace == ADATASPACE_UNKNOWN) {
282             return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
283         }
284     }
285 
286     {
287         size_t size;
288         if (!Bitmap::computeAllocationSize(info->stride, info->height, &size)) {
289             return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
290         }
291     }
292 
293     auto imageInfo =
294             SkImageInfo::Make(info->width, info->height, colorType, alphaType, std::move(cs));
295     SkBitmap bitmap;
296     // We are not going to modify the pixels, but installPixels expects them to
297     // not be const, since for all it knows we might want to draw to the SkBitmap.
298     if (!bitmap.installPixels(imageInfo, const_cast<void*>(pixels), info->stride)) {
299         return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
300     }
301 
302     CompressWriter stream(userContext, fn);
303     return Bitmap::compress(bitmap, format, quality, &stream) ? ANDROID_BITMAP_RESULT_SUCCESS
304                                                               : ANDROID_BITMAP_RESULT_JNI_EXCEPTION;
305 }
306 
ABitmap_getHardwareBuffer(ABitmap * bitmapHandle)307 AHardwareBuffer* ABitmap_getHardwareBuffer(ABitmap* bitmapHandle) {
308     Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle);
309     AHardwareBuffer* buffer = bitmap->hardwareBuffer();
310     if (buffer) {
311         AHardwareBuffer_acquire(buffer);
312     }
313     return buffer;
314 }
315