blob: 93259e70abbdb193b024636508500599d7e407e0 [file] [log] [blame]
Ficus Kirkpatrick1a9c27c2010-03-05 17:05:08 -08001#define LOG_TAG "GraphicsJNI"
2
Riley Andrews39d7f302014-11-13 17:43:25 -08003#include <unistd.h>
4#include <sys/mman.h>
5
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006#include "jni.h"
Elliott Hughes8451b252011-04-07 19:17:57 -07007#include "JNIHelp.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008#include "GraphicsJNI.h"
Patrick Dubroye4ac2d62010-12-01 11:23:13 -08009
Derek Sollenberger8872b382014-06-23 14:13:53 -040010#include "Canvas.h"
Patrick Dubroye4ac2d62010-12-01 11:23:13 -080011#include "SkCanvas.h"
12#include "SkDevice.h"
Leon Scroggins46cb9bd2014-03-06 15:36:39 -050013#include "SkMath.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080014#include "SkRegion.h"
15#include <android_runtime/AndroidRuntime.h>
Riley Andrews39d7f302014-11-13 17:43:25 -080016#include <cutils/ashmem.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080017
Derek Sollenberger3d4eed72014-12-04 15:20:29 -050018#include <Caches.h>
19#include <TextureCache.h>
20
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021void doThrowNPE(JNIEnv* env) {
Elliott Hughes69a017b2011-04-08 14:10:28 -070022 jniThrowNullPointerException(env, NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023}
24
25void doThrowAIOOBE(JNIEnv* env) {
Elliott Hughes8451b252011-04-07 19:17:57 -070026 jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027}
28
29void doThrowRE(JNIEnv* env, const char* msg) {
Elliott Hughes69a017b2011-04-08 14:10:28 -070030 jniThrowRuntimeException(env, msg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031}
32
33void doThrowIAE(JNIEnv* env, const char* msg) {
Elliott Hughes8451b252011-04-07 19:17:57 -070034 jniThrowException(env, "java/lang/IllegalArgumentException", msg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035}
36
37void doThrowISE(JNIEnv* env, const char* msg) {
Elliott Hughes8451b252011-04-07 19:17:57 -070038 jniThrowException(env, "java/lang/IllegalStateException", msg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039}
40
41void doThrowOOME(JNIEnv* env, const char* msg) {
Elliott Hughes8451b252011-04-07 19:17:57 -070042 jniThrowException(env, "java/lang/OutOfMemoryError", msg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043}
44
Joseph Wenf1f48bc2010-07-19 16:59:51 +080045void doThrowIOE(JNIEnv* env, const char* msg) {
Elliott Hughes8451b252011-04-07 19:17:57 -070046 jniThrowException(env, "java/io/IOException", msg);
Joseph Wenf1f48bc2010-07-19 16:59:51 +080047}
48
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049bool GraphicsJNI::hasException(JNIEnv *env) {
50 if (env->ExceptionCheck() != 0) {
Steve Block3762c312012-01-06 19:20:56 +000051 ALOGE("*** Uncaught exception returned from Java call!\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052 env->ExceptionDescribe();
53 return true;
54 }
55 return false;
56}
57
58///////////////////////////////////////////////////////////////////////////////
59
60AutoJavaFloatArray::AutoJavaFloatArray(JNIEnv* env, jfloatArray array,
Mike Reedc04851f2009-10-28 15:09:45 -040061 int minLength, JNIAccess access)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062: fEnv(env), fArray(array), fPtr(NULL), fLen(0) {
63 SkASSERT(env);
64 if (array) {
65 fLen = env->GetArrayLength(array);
66 if (fLen < minLength) {
67 sk_throw();
68 }
69 fPtr = env->GetFloatArrayElements(array, NULL);
70 }
Mike Reedc04851f2009-10-28 15:09:45 -040071 fReleaseMode = (access == kRO_JNIAccess) ? JNI_ABORT : 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072}
73
74AutoJavaFloatArray::~AutoJavaFloatArray() {
75 if (fPtr) {
Mike Reedc04851f2009-10-28 15:09:45 -040076 fEnv->ReleaseFloatArrayElements(fArray, fPtr, fReleaseMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077 }
78}
79
80AutoJavaIntArray::AutoJavaIntArray(JNIEnv* env, jintArray array,
81 int minLength)
82: fEnv(env), fArray(array), fPtr(NULL), fLen(0) {
83 SkASSERT(env);
84 if (array) {
85 fLen = env->GetArrayLength(array);
86 if (fLen < minLength) {
87 sk_throw();
88 }
89 fPtr = env->GetIntArrayElements(array, NULL);
90 }
91}
92
93AutoJavaIntArray::~AutoJavaIntArray() {
94 if (fPtr) {
95 fEnv->ReleaseIntArrayElements(fArray, fPtr, 0);
96 }
97}
98
99AutoJavaShortArray::AutoJavaShortArray(JNIEnv* env, jshortArray array,
Mike Reedc04851f2009-10-28 15:09:45 -0400100 int minLength, JNIAccess access)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101: fEnv(env), fArray(array), fPtr(NULL), fLen(0) {
102 SkASSERT(env);
103 if (array) {
104 fLen = env->GetArrayLength(array);
105 if (fLen < minLength) {
106 sk_throw();
107 }
108 fPtr = env->GetShortArrayElements(array, NULL);
109 }
Mike Reedc04851f2009-10-28 15:09:45 -0400110 fReleaseMode = (access == kRO_JNIAccess) ? JNI_ABORT : 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111}
112
113AutoJavaShortArray::~AutoJavaShortArray() {
114 if (fPtr) {
Mike Reedc04851f2009-10-28 15:09:45 -0400115 fEnv->ReleaseShortArrayElements(fArray, fPtr, fReleaseMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 }
117}
118
119AutoJavaByteArray::AutoJavaByteArray(JNIEnv* env, jbyteArray array,
120 int minLength)
121: fEnv(env), fArray(array), fPtr(NULL), fLen(0) {
122 SkASSERT(env);
123 if (array) {
124 fLen = env->GetArrayLength(array);
125 if (fLen < minLength) {
126 sk_throw();
127 }
128 fPtr = env->GetByteArrayElements(array, NULL);
129 }
130}
131
132AutoJavaByteArray::~AutoJavaByteArray() {
133 if (fPtr) {
134 fEnv->ReleaseByteArrayElements(fArray, fPtr, 0);
135 }
136}
137
138///////////////////////////////////////////////////////////////////////////////
139
140static jclass gRect_class;
141static jfieldID gRect_leftFieldID;
142static jfieldID gRect_topFieldID;
143static jfieldID gRect_rightFieldID;
144static jfieldID gRect_bottomFieldID;
145
146static jclass gRectF_class;
147static jfieldID gRectF_leftFieldID;
148static jfieldID gRectF_topFieldID;
149static jfieldID gRectF_rightFieldID;
150static jfieldID gRectF_bottomFieldID;
151
152static jclass gPoint_class;
153static jfieldID gPoint_xFieldID;
154static jfieldID gPoint_yFieldID;
155
156static jclass gPointF_class;
157static jfieldID gPointF_xFieldID;
158static jfieldID gPointF_yFieldID;
159
160static jclass gBitmap_class;
John Reckf29ed282015-04-07 07:32:03 -0700161static jfieldID gBitmap_nativePtr;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162static jmethodID gBitmap_constructorMethodID;
Chris Craik9f583612013-05-20 18:13:47 -0700163static jmethodID gBitmap_reinitMethodID;
164static jmethodID gBitmap_getAllocationByteCountMethodID;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165
166static jclass gBitmapConfig_class;
167static jfieldID gBitmapConfig_nativeInstanceID;
168
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800169static jclass gBitmapRegionDecoder_class;
170static jmethodID gBitmapRegionDecoder_constructorMethodID;
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800171
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172static jclass gCanvas_class;
173static jfieldID gCanvas_nativeInstanceID;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175static jclass gPicture_class;
176static jfieldID gPicture_nativeInstanceID;
177
178static jclass gRegion_class;
179static jfieldID gRegion_nativeInstanceID;
180static jmethodID gRegion_constructorMethodID;
181
Mathieu Chartier7384b422013-10-17 18:16:42 -0700182static jclass gByte_class;
183static jobject gVMRuntime;
184static jclass gVMRuntime_class;
185static jmethodID gVMRuntime_newNonMovableArray;
186static jmethodID gVMRuntime_addressOf;
187
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188///////////////////////////////////////////////////////////////////////////////
189
190void GraphicsJNI::get_jrect(JNIEnv* env, jobject obj, int* L, int* T, int* R, int* B)
191{
192 SkASSERT(env->IsInstanceOf(obj, gRect_class));
193
194 *L = env->GetIntField(obj, gRect_leftFieldID);
195 *T = env->GetIntField(obj, gRect_topFieldID);
196 *R = env->GetIntField(obj, gRect_rightFieldID);
197 *B = env->GetIntField(obj, gRect_bottomFieldID);
198}
199
200void GraphicsJNI::set_jrect(JNIEnv* env, jobject obj, int L, int T, int R, int B)
201{
202 SkASSERT(env->IsInstanceOf(obj, gRect_class));
203
204 env->SetIntField(obj, gRect_leftFieldID, L);
205 env->SetIntField(obj, gRect_topFieldID, T);
206 env->SetIntField(obj, gRect_rightFieldID, R);
207 env->SetIntField(obj, gRect_bottomFieldID, B);
208}
209
210SkIRect* GraphicsJNI::jrect_to_irect(JNIEnv* env, jobject obj, SkIRect* ir)
211{
212 SkASSERT(env->IsInstanceOf(obj, gRect_class));
213
214 ir->set(env->GetIntField(obj, gRect_leftFieldID),
215 env->GetIntField(obj, gRect_topFieldID),
216 env->GetIntField(obj, gRect_rightFieldID),
217 env->GetIntField(obj, gRect_bottomFieldID));
218 return ir;
219}
220
221void GraphicsJNI::irect_to_jrect(const SkIRect& ir, JNIEnv* env, jobject obj)
222{
223 SkASSERT(env->IsInstanceOf(obj, gRect_class));
224
225 env->SetIntField(obj, gRect_leftFieldID, ir.fLeft);
226 env->SetIntField(obj, gRect_topFieldID, ir.fTop);
227 env->SetIntField(obj, gRect_rightFieldID, ir.fRight);
228 env->SetIntField(obj, gRect_bottomFieldID, ir.fBottom);
229}
230
231SkRect* GraphicsJNI::jrectf_to_rect(JNIEnv* env, jobject obj, SkRect* r)
232{
233 SkASSERT(env->IsInstanceOf(obj, gRectF_class));
Elliott Hughes8451b252011-04-07 19:17:57 -0700234
Leon Scroggins III2e0103e2014-04-04 17:05:24 -0400235 r->set(env->GetFloatField(obj, gRectF_leftFieldID),
236 env->GetFloatField(obj, gRectF_topFieldID),
237 env->GetFloatField(obj, gRectF_rightFieldID),
238 env->GetFloatField(obj, gRectF_bottomFieldID));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800239 return r;
240}
241
242SkRect* GraphicsJNI::jrect_to_rect(JNIEnv* env, jobject obj, SkRect* r)
243{
244 SkASSERT(env->IsInstanceOf(obj, gRect_class));
Elliott Hughes8451b252011-04-07 19:17:57 -0700245
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800246 r->set(SkIntToScalar(env->GetIntField(obj, gRect_leftFieldID)),
247 SkIntToScalar(env->GetIntField(obj, gRect_topFieldID)),
248 SkIntToScalar(env->GetIntField(obj, gRect_rightFieldID)),
249 SkIntToScalar(env->GetIntField(obj, gRect_bottomFieldID)));
250 return r;
251}
252
253void GraphicsJNI::rect_to_jrectf(const SkRect& r, JNIEnv* env, jobject obj)
254{
255 SkASSERT(env->IsInstanceOf(obj, gRectF_class));
256
257 env->SetFloatField(obj, gRectF_leftFieldID, SkScalarToFloat(r.fLeft));
258 env->SetFloatField(obj, gRectF_topFieldID, SkScalarToFloat(r.fTop));
259 env->SetFloatField(obj, gRectF_rightFieldID, SkScalarToFloat(r.fRight));
260 env->SetFloatField(obj, gRectF_bottomFieldID, SkScalarToFloat(r.fBottom));
261}
262
263SkIPoint* GraphicsJNI::jpoint_to_ipoint(JNIEnv* env, jobject obj, SkIPoint* point)
264{
265 SkASSERT(env->IsInstanceOf(obj, gPoint_class));
Elliott Hughes8451b252011-04-07 19:17:57 -0700266
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800267 point->set(env->GetIntField(obj, gPoint_xFieldID),
268 env->GetIntField(obj, gPoint_yFieldID));
269 return point;
270}
271
272void GraphicsJNI::ipoint_to_jpoint(const SkIPoint& ir, JNIEnv* env, jobject obj)
273{
274 SkASSERT(env->IsInstanceOf(obj, gPoint_class));
275
276 env->SetIntField(obj, gPoint_xFieldID, ir.fX);
277 env->SetIntField(obj, gPoint_yFieldID, ir.fY);
278}
279
280SkPoint* GraphicsJNI::jpointf_to_point(JNIEnv* env, jobject obj, SkPoint* point)
281{
282 SkASSERT(env->IsInstanceOf(obj, gPointF_class));
Elliott Hughes8451b252011-04-07 19:17:57 -0700283
Leon Scroggins III2e0103e2014-04-04 17:05:24 -0400284 point->set(env->GetIntField(obj, gPointF_xFieldID),
285 env->GetIntField(obj, gPointF_yFieldID));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 return point;
287}
288
289void GraphicsJNI::point_to_jpointf(const SkPoint& r, JNIEnv* env, jobject obj)
290{
291 SkASSERT(env->IsInstanceOf(obj, gPointF_class));
292
293 env->SetFloatField(obj, gPointF_xFieldID, SkScalarToFloat(r.fX));
294 env->SetFloatField(obj, gPointF_yFieldID, SkScalarToFloat(r.fY));
295}
296
Mike Reed1103b322014-07-08 12:36:44 -0400297// This enum must keep these int values, to match the int values
298// in the java Bitmap.Config enum.
299enum LegacyBitmapConfig {
300 kNo_LegacyBitmapConfig = 0,
301 kA8_LegacyBitmapConfig = 1,
302 kIndex8_LegacyBitmapConfig = 2,
303 kRGB_565_LegacyBitmapConfig = 3,
304 kARGB_4444_LegacyBitmapConfig = 4,
305 kARGB_8888_LegacyBitmapConfig = 5,
306
307 kLastEnum_LegacyBitmapConfig = kARGB_8888_LegacyBitmapConfig
308};
309
310jint GraphicsJNI::colorTypeToLegacyBitmapConfig(SkColorType colorType) {
311 switch (colorType) {
312 case kN32_SkColorType:
313 return kARGB_8888_LegacyBitmapConfig;
314 case kARGB_4444_SkColorType:
315 return kARGB_4444_LegacyBitmapConfig;
316 case kRGB_565_SkColorType:
317 return kRGB_565_LegacyBitmapConfig;
318 case kIndex_8_SkColorType:
319 return kIndex8_LegacyBitmapConfig;
320 case kAlpha_8_SkColorType:
321 return kA8_LegacyBitmapConfig;
322 case kUnknown_SkColorType:
323 default:
324 break;
325 }
326 return kNo_LegacyBitmapConfig;
327}
328
329SkColorType GraphicsJNI::legacyBitmapConfigToColorType(jint legacyConfig) {
330 const uint8_t gConfig2ColorType[] = {
331 kUnknown_SkColorType,
332 kAlpha_8_SkColorType,
333 kIndex_8_SkColorType,
334 kRGB_565_SkColorType,
335 kARGB_4444_SkColorType,
336 kN32_SkColorType
337 };
338
339 if (legacyConfig < 0 || legacyConfig > kLastEnum_LegacyBitmapConfig) {
340 legacyConfig = kNo_LegacyBitmapConfig;
341 }
342 return static_cast<SkColorType>(gConfig2ColorType[legacyConfig]);
343}
344
John Reckf29ed282015-04-07 07:32:03 -0700345android::Bitmap* GraphicsJNI::getBitmap(JNIEnv* env, jobject bitmap) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800346 SkASSERT(env);
347 SkASSERT(bitmap);
348 SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class));
John Reckf29ed282015-04-07 07:32:03 -0700349 jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr);
350 android::Bitmap* b = reinterpret_cast<android::Bitmap*>(bitmapHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351 SkASSERT(b);
352 return b;
353}
354
John Recked207b92015-04-10 13:52:57 -0700355void GraphicsJNI::getSkBitmap(JNIEnv* env, jobject bitmap, SkBitmap* outBitmap) {
John Reckf29ed282015-04-07 07:32:03 -0700356 getBitmap(env, bitmap)->getSkBitmap(outBitmap);
John Recked207b92015-04-10 13:52:57 -0700357}
358
John Reckae2e8b42015-05-06 14:55:05 -0700359SkPixelRef* GraphicsJNI::refSkPixelRef(JNIEnv* env, jobject bitmap) {
360 return getBitmap(env, bitmap)->refPixelRef();
John Recked207b92015-04-10 13:52:57 -0700361}
362
Mike Reed42a1d082014-07-07 18:06:18 -0400363SkColorType GraphicsJNI::getNativeBitmapColorType(JNIEnv* env, jobject jconfig) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 SkASSERT(env);
365 if (NULL == jconfig) {
Mike Reed42a1d082014-07-07 18:06:18 -0400366 return kUnknown_SkColorType;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367 }
368 SkASSERT(env->IsInstanceOf(jconfig, gBitmapConfig_class));
369 int c = env->GetIntField(jconfig, gBitmapConfig_nativeInstanceID);
Mike Reed1103b322014-07-08 12:36:44 -0400370 return legacyBitmapConfigToColorType(c);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800371}
372
John Reckc1b33d62015-04-22 09:04:45 -0700373android::Canvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 SkASSERT(env);
375 SkASSERT(canvas);
376 SkASSERT(env->IsInstanceOf(canvas, gCanvas_class));
Ashok Bhat36bef0b2014-01-20 20:08:01 +0000377 jlong canvasHandle = env->GetLongField(canvas, gCanvas_nativeInstanceID);
Bo Liude92f4c2014-11-24 10:53:52 -0800378 if (!canvasHandle) {
379 return NULL;
380 }
John Reckc1b33d62015-04-22 09:04:45 -0700381 return reinterpret_cast<android::Canvas*>(canvasHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800382}
383
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region)
385{
386 SkASSERT(env);
387 SkASSERT(region);
388 SkASSERT(env->IsInstanceOf(region, gRegion_class));
Ashok Bhat36bef0b2014-01-20 20:08:01 +0000389 jlong regionHandle = env->GetLongField(region, gRegion_nativeInstanceID);
390 SkRegion* r = reinterpret_cast<SkRegion*>(regionHandle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800391 SkASSERT(r);
392 return r;
393}
394
395///////////////////////////////////////////////////////////////////////////////////////////
396
Leon Scroggins III8790be62013-12-03 16:26:51 -0500397// Assert that bitmap's SkAlphaType is consistent with isPremultiplied.
John Reckf29ed282015-04-07 07:32:03 -0700398static void assert_premultiplied(const SkImageInfo& info, bool isPremultiplied) {
Leon Scroggins III8790be62013-12-03 16:26:51 -0500399 // kOpaque_SkAlphaType and kIgnore_SkAlphaType mean that isPremultiplied is
400 // irrelevant. This just tests to ensure that the SkAlphaType is not
401 // opposite of isPremultiplied.
402 if (isPremultiplied) {
John Reckf29ed282015-04-07 07:32:03 -0700403 SkASSERT(info.alphaType() != kUnpremul_SkAlphaType);
Leon Scroggins III8790be62013-12-03 16:26:51 -0500404 } else {
John Reckf29ed282015-04-07 07:32:03 -0700405 SkASSERT(info.alphaType() != kPremul_SkAlphaType);
Leon Scroggins III8790be62013-12-03 16:26:51 -0500406 }
407}
408
John Reckf29ed282015-04-07 07:32:03 -0700409jobject GraphicsJNI::createBitmap(JNIEnv* env, android::Bitmap* bitmap,
410 int bitmapCreateFlags, jbyteArray ninePatchChunk, jobject ninePatchInsets,
411 int density) {
Chris Craik1abf5d62013-08-16 12:47:03 -0700412 bool isMutable = bitmapCreateFlags & kBitmapCreateFlag_Mutable;
413 bool isPremultiplied = bitmapCreateFlags & kBitmapCreateFlag_Premultiplied;
Leon Scroggins III8790be62013-12-03 16:26:51 -0500414 // The caller needs to have already set the alpha type properly, so the
415 // native SkBitmap stays in sync with the Java Bitmap.
John Reckf29ed282015-04-07 07:32:03 -0700416 assert_premultiplied(bitmap->info(), isPremultiplied);
Leon Scroggins III8790be62013-12-03 16:26:51 -0500417
Elliott Hughescf6f7a02011-04-12 17:50:45 -0700418 jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,
John Reckf29ed282015-04-07 07:32:03 -0700419 reinterpret_cast<jlong>(bitmap), bitmap->javaByteArray(),
Chris Craik1abf5d62013-08-16 12:47:03 -0700420 bitmap->width(), bitmap->height(), density, isMutable, isPremultiplied,
Chris Craik47cd8e92014-07-08 17:13:08 -0700421 ninePatchChunk, ninePatchInsets);
Elliott Hughescf6f7a02011-04-12 17:50:45 -0700422 hasException(env); // For the side effect of logging.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 return obj;
424}
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800425
John Reckf29ed282015-04-07 07:32:03 -0700426void GraphicsJNI::reinitBitmap(JNIEnv* env, jobject javaBitmap, const SkImageInfo& info,
Chris Craik1abf5d62013-08-16 12:47:03 -0700427 bool isPremultiplied)
Chris Craik9f583612013-05-20 18:13:47 -0700428{
Leon Scroggins III8790be62013-12-03 16:26:51 -0500429 // The caller needs to have already set the alpha type properly, so the
430 // native SkBitmap stays in sync with the Java Bitmap.
John Reckf29ed282015-04-07 07:32:03 -0700431 assert_premultiplied(info, isPremultiplied);
Leon Scroggins III8790be62013-12-03 16:26:51 -0500432
Chris Craik1abf5d62013-08-16 12:47:03 -0700433 env->CallVoidMethod(javaBitmap, gBitmap_reinitMethodID,
John Reckf29ed282015-04-07 07:32:03 -0700434 info.width(), info.height(), isPremultiplied);
Chris Craik9f583612013-05-20 18:13:47 -0700435}
436
437int GraphicsJNI::getBitmapAllocationByteCount(JNIEnv* env, jobject javaBitmap)
438{
439 return env->CallIntMethod(javaBitmap, gBitmap_getAllocationByteCountMethodID);
440}
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800441
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800442jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap)
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800443{
444 SkASSERT(bitmap != NULL);
445
Elliott Hughescf6f7a02011-04-12 17:50:45 -0700446 jobject obj = env->NewObject(gBitmapRegionDecoder_class,
447 gBitmapRegionDecoder_constructorMethodID,
Ashok Bhatb091d472014-01-08 14:32:49 +0000448 reinterpret_cast<jlong>(bitmap));
Elliott Hughescf6f7a02011-04-12 17:50:45 -0700449 hasException(env); // For the side effect of logging.
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800450 return obj;
451}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800452
453jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region)
454{
455 SkASSERT(region != NULL);
Elliott Hughescf6f7a02011-04-12 17:50:45 -0700456 jobject obj = env->NewObject(gRegion_class, gRegion_constructorMethodID,
Ashok Bhat36bef0b2014-01-20 20:08:01 +0000457 reinterpret_cast<jlong>(region), 0);
Elliott Hughescf6f7a02011-04-12 17:50:45 -0700458 hasException(env); // For the side effect of logging.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800459 return obj;
460}
461
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462static JNIEnv* vm2env(JavaVM* vm)
463{
464 JNIEnv* env = NULL;
465 if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK || NULL == env)
466 {
467 SkDebugf("------- [%p] vm->GetEnv() failed\n", vm);
468 sk_throw();
469 }
470 return env;
471}
472
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800473///////////////////////////////////////////////////////////////////////////////
474
Derek Sollenbergerd37095b2015-01-29 11:22:55 -0500475static bool computeAllocationSize(const SkBitmap& bitmap, size_t* size) {
476 int32_t rowBytes32 = SkToS32(bitmap.rowBytes());
477 int64_t bigSize = (int64_t)bitmap.height() * rowBytes32;
478 if (rowBytes32 < 0 || !sk_64_isS32(bigSize)) {
479 return false; // allocation will be too large
480 }
Derek Sollenberger3d4eed72014-12-04 15:20:29 -0500481
Derek Sollenbergerd37095b2015-01-29 11:22:55 -0500482 *size = sk_64_asS32(bigSize);
483 return true;
Derek Sollenberger3d4eed72014-12-04 15:20:29 -0500484}
485
John Reckf29ed282015-04-07 07:32:03 -0700486android::Bitmap* GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800487 SkColorTable* ctable) {
Leon Scroggins46cb9bd2014-03-06 15:36:39 -0500488 const SkImageInfo& info = bitmap->info();
489 if (info.fColorType == kUnknown_SkColorType) {
490 doThrowIAE(env, "unknown bitmap configuration");
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800491 return NULL;
492 }
Derek Sollenbergerb644a3b2014-01-17 15:45:10 -0500493
Derek Sollenbergerd37095b2015-01-29 11:22:55 -0500494 size_t size;
495 if (!computeAllocationSize(*bitmap, &size)) {
Derek Sollenberger3d4eed72014-12-04 15:20:29 -0500496 return NULL;
497 }
498
Derek Sollenbergerd37095b2015-01-29 11:22:55 -0500499 // we must respect the rowBytes value already set on the bitmap instead of
500 // attempting to compute our own.
501 const size_t rowBytes = bitmap->rowBytes();
502
Mathieu Chartier7384b422013-10-17 18:16:42 -0700503 jbyteArray arrayObj = (jbyteArray) env->CallObjectMethod(gVMRuntime,
504 gVMRuntime_newNonMovableArray,
505 gByte_class, size);
Mathieu Chartiera1a19d22013-12-04 17:32:43 -0800506 if (env->ExceptionCheck() != 0) {
507 return NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800508 }
Mathieu Chartiera1a19d22013-12-04 17:32:43 -0800509 SkASSERT(arrayObj);
510 jbyte* addr = (jbyte*) env->CallLongMethod(gVMRuntime, gVMRuntime_addressOf, arrayObj);
511 if (env->ExceptionCheck() != 0) {
512 return NULL;
513 }
514 SkASSERT(addr);
John Reckf29ed282015-04-07 07:32:03 -0700515 android::Bitmap* wrapper = new android::Bitmap(env, arrayObj, (void*) addr,
516 info, rowBytes, ctable);
517 wrapper->getSkBitmap(bitmap);
Mathieu Chartiera1a19d22013-12-04 17:32:43 -0800518 // since we're already allocated, we lockPixels right away
519 // HeapAllocator behaves this way too
520 bitmap->lockPixels();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800521
John Reckf29ed282015-04-07 07:32:03 -0700522 return wrapper;
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800523}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524
Derek Sollenberger3d4eed72014-12-04 15:20:29 -0500525struct AndroidPixelRefContext {
526 int32_t stableID;
527};
528
529static void allocatePixelsReleaseProc(void* ptr, void* ctx) {
530 AndroidPixelRefContext* context = (AndroidPixelRefContext*)ctx;
531 if (android::uirenderer::Caches::hasInstance()) {
532 android::uirenderer::Caches::getInstance().textureCache.releaseTexture(context->stableID);
533 }
534
535 sk_free(ptr);
536 delete context;
537}
538
539bool GraphicsJNI::allocatePixels(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ctable) {
540 const SkImageInfo& info = bitmap->info();
541 if (info.fColorType == kUnknown_SkColorType) {
542 doThrowIAE(env, "unknown bitmap configuration");
543 return NULL;
544 }
545
Derek Sollenbergerd37095b2015-01-29 11:22:55 -0500546 size_t size;
547 if (!computeAllocationSize(*bitmap, &size)) {
Derek Sollenberger3d4eed72014-12-04 15:20:29 -0500548 return false;
549 }
550
Derek Sollenbergerd37095b2015-01-29 11:22:55 -0500551 // we must respect the rowBytes value already set on the bitmap instead of
552 // attempting to compute our own.
553 const size_t rowBytes = bitmap->rowBytes();
554
Derek Sollenberger3d4eed72014-12-04 15:20:29 -0500555 void* addr = sk_malloc_flags(size, 0);
556 if (NULL == addr) {
557 return false;
558 }
559
560 AndroidPixelRefContext* context = new AndroidPixelRefContext;
561 SkMallocPixelRef* pr = SkMallocPixelRef::NewWithProc(info, rowBytes, ctable, addr,
562 &allocatePixelsReleaseProc, context);
563 if (!pr) {
564 delete context;
565 return false;
566 }
567
568 // set the stableID in the context so that it can be used later in
569 // allocatePixelsReleaseProc to remove the texture from the cache.
570 context->stableID = pr->getStableID();
571
572 bitmap->setPixelRef(pr)->unref();
573 // since we're already allocated, we can lockPixels right away
574 bitmap->lockPixels();
575
576 return true;
577}
578
Riley Andrews39d7f302014-11-13 17:43:25 -0800579android::Bitmap* GraphicsJNI::allocateAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
580 SkColorTable* ctable) {
581 int fd;
582
583 const SkImageInfo& info = bitmap->info();
584 if (info.fColorType == kUnknown_SkColorType) {
585 doThrowIAE(env, "unknown bitmap configuration");
586 return nullptr;
587 }
588
589 size_t size;
590 if (!computeAllocationSize(*bitmap, &size)) {
591 return nullptr;
592 }
593
594 // we must respect the rowBytes value already set on the bitmap instead of
595 // attempting to compute our own.
596 const size_t rowBytes = bitmap->rowBytes();
597
598 // Create new ashmem region with read/write priv
599 fd = ashmem_create_region("bitmap", size);
600 if (fd < 0) {
601 return nullptr;
602 }
603
604 void* addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
605 if (addr == MAP_FAILED) {
606 close(fd);
607 return nullptr;
608 }
609
610 if (ashmem_set_prot_region(fd, PROT_READ) < 0) {
611 munmap(addr, size);
612 close(fd);
613 return nullptr;
614 }
615
616 android::Bitmap* wrapper = new android::Bitmap(addr, fd, info, rowBytes, ctable);
617 wrapper->getSkBitmap(bitmap);
618 // since we're already allocated, we lockPixels right away
619 // HeapAllocator behaves this way too
620 bitmap->lockPixels();
621
622 return wrapper;
623}
624
625android::Bitmap* GraphicsJNI::mapAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
Jeff Browna316c5d2015-06-05 15:14:06 -0700626 SkColorTable* ctable, int fd, void* addr, bool readOnly) {
Riley Andrews39d7f302014-11-13 17:43:25 -0800627 const SkImageInfo& info = bitmap->info();
628 if (info.fColorType == kUnknown_SkColorType) {
629 doThrowIAE(env, "unknown bitmap configuration");
630 return nullptr;
631 }
632
Jeff Browna316c5d2015-06-05 15:14:06 -0700633 if (!addr) {
634 // Map existing ashmem region if not already mapped.
635 int flags = readOnly ? (PROT_READ) : (PROT_READ | PROT_WRITE);
636 addr = mmap(NULL, ashmem_get_size_region(fd), flags, MAP_SHARED, fd, 0);
637 if (addr == MAP_FAILED) {
638 return nullptr;
639 }
Riley Andrews39d7f302014-11-13 17:43:25 -0800640 }
641
642 // we must respect the rowBytes value already set on the bitmap instead of
643 // attempting to compute our own.
644 const size_t rowBytes = bitmap->rowBytes();
645
646 android::Bitmap* wrapper = new android::Bitmap(addr, fd, info, rowBytes, ctable);
647 wrapper->getSkBitmap(bitmap);
Jeff Browna316c5d2015-06-05 15:14:06 -0700648 if (readOnly) {
649 bitmap->pixelRef()->setImmutable();
650 }
Riley Andrews39d7f302014-11-13 17:43:25 -0800651 // since we're already allocated, we lockPixels right away
652 // HeapAllocator behaves this way too
653 bitmap->lockPixels();
654
655 return wrapper;
656}
657
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658///////////////////////////////////////////////////////////////////////////////
659
John Reckf29ed282015-04-07 07:32:03 -0700660JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env) {
661 LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJavaVM) != JNI_OK,
662 "env->GetJavaVM failed");
663}
664
665JavaPixelAllocator::~JavaPixelAllocator() {
666 if (mStorage) {
667 mStorage->detachFromJava();
Wei-Ta Chen291303b2010-08-18 15:40:29 +0800668 }
669}
Elliott Hughes8451b252011-04-07 19:17:57 -0700670
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
John Reckf29ed282015-04-07 07:32:03 -0700672 JNIEnv* env = vm2env(mJavaVM);
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800673
John Reckf29ed282015-04-07 07:32:03 -0700674 mStorage = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);
675 return mStorage != nullptr;
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800676}
677
678////////////////////////////////////////////////////////////////////////////////
679
Riley Andrews721ae5f2015-05-11 16:08:22 -0700680AshmemPixelAllocator::AshmemPixelAllocator(JNIEnv *env) {
681 LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJavaVM) != JNI_OK,
682 "env->GetJavaVM failed");
683}
684
685AshmemPixelAllocator::~AshmemPixelAllocator() {
686 if (mStorage) {
687 mStorage->detachFromJava();
688 }
689}
690
691bool AshmemPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
692 JNIEnv* env = vm2env(mJavaVM);
693 mStorage = GraphicsJNI::allocateAshmemPixelRef(env, bitmap, ctable);
694 return mStorage != nullptr;
695}
696
697////////////////////////////////////////////////////////////////////////////////
698
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699static jclass make_globalref(JNIEnv* env, const char classname[])
700{
701 jclass c = env->FindClass(classname);
702 SkASSERT(c);
Mathieu Chartier6ecb7a92013-10-18 11:04:11 -0700703 return (jclass) env->NewGlobalRef(c);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800704}
705
706static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
707 const char fieldname[], const char type[])
708{
709 jfieldID id = env->GetFieldID(clazz, fieldname, type);
710 SkASSERT(id);
711 return id;
712}
713
714int register_android_graphics_Graphics(JNIEnv* env)
715{
716 jmethodID m;
717 jclass c;
718
719 gRect_class = make_globalref(env, "android/graphics/Rect");
720 gRect_leftFieldID = getFieldIDCheck(env, gRect_class, "left", "I");
721 gRect_topFieldID = getFieldIDCheck(env, gRect_class, "top", "I");
722 gRect_rightFieldID = getFieldIDCheck(env, gRect_class, "right", "I");
723 gRect_bottomFieldID = getFieldIDCheck(env, gRect_class, "bottom", "I");
724
725 gRectF_class = make_globalref(env, "android/graphics/RectF");
726 gRectF_leftFieldID = getFieldIDCheck(env, gRectF_class, "left", "F");
727 gRectF_topFieldID = getFieldIDCheck(env, gRectF_class, "top", "F");
728 gRectF_rightFieldID = getFieldIDCheck(env, gRectF_class, "right", "F");
729 gRectF_bottomFieldID = getFieldIDCheck(env, gRectF_class, "bottom", "F");
730
731 gPoint_class = make_globalref(env, "android/graphics/Point");
732 gPoint_xFieldID = getFieldIDCheck(env, gPoint_class, "x", "I");
733 gPoint_yFieldID = getFieldIDCheck(env, gPoint_class, "y", "I");
734
735 gPointF_class = make_globalref(env, "android/graphics/PointF");
736 gPointF_xFieldID = getFieldIDCheck(env, gPointF_class, "x", "F");
737 gPointF_yFieldID = getFieldIDCheck(env, gPointF_class, "y", "F");
738
739 gBitmap_class = make_globalref(env, "android/graphics/Bitmap");
John Reckf29ed282015-04-07 07:32:03 -0700740 gBitmap_nativePtr = getFieldIDCheck(env, gBitmap_class, "mNativePtr", "J");
Chris Craik47cd8e92014-07-08 17:13:08 -0700741 gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", "(J[BIIIZZ[BLandroid/graphics/NinePatch$InsetStruct;)V");
Chris Craik1abf5d62013-08-16 12:47:03 -0700742 gBitmap_reinitMethodID = env->GetMethodID(gBitmap_class, "reinit", "(IIZ)V");
Chris Craik9f583612013-05-20 18:13:47 -0700743 gBitmap_getAllocationByteCountMethodID = env->GetMethodID(gBitmap_class, "getAllocationByteCount", "()I");
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800744 gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder");
Ashok Bhatb091d472014-01-08 14:32:49 +0000745 gBitmapRegionDecoder_constructorMethodID = env->GetMethodID(gBitmapRegionDecoder_class, "<init>", "(J)V");
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800746
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800747 gBitmapConfig_class = make_globalref(env, "android/graphics/Bitmap$Config");
748 gBitmapConfig_nativeInstanceID = getFieldIDCheck(env, gBitmapConfig_class,
Elliott Hughes8451b252011-04-07 19:17:57 -0700749 "nativeInt", "I");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750
751 gCanvas_class = make_globalref(env, "android/graphics/Canvas");
Florin Malita5c3d9272014-05-08 10:35:36 -0400752 gCanvas_nativeInstanceID = getFieldIDCheck(env, gCanvas_class, "mNativeCanvasWrapper", "J");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800753
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800754 gPicture_class = make_globalref(env, "android/graphics/Picture");
Ashok Bhat36bef0b2014-01-20 20:08:01 +0000755 gPicture_nativeInstanceID = getFieldIDCheck(env, gPicture_class, "mNativePicture", "J");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800756
757 gRegion_class = make_globalref(env, "android/graphics/Region");
Ashok Bhat36bef0b2014-01-20 20:08:01 +0000758 gRegion_nativeInstanceID = getFieldIDCheck(env, gRegion_class, "mNativeRegion", "J");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800759 gRegion_constructorMethodID = env->GetMethodID(gRegion_class, "<init>",
Ashok Bhat36bef0b2014-01-20 20:08:01 +0000760 "(JI)V");
Elliott Hughes8451b252011-04-07 19:17:57 -0700761
Mathieu Chartier7384b422013-10-17 18:16:42 -0700762 c = env->FindClass("java/lang/Byte");
Mathieu Chartier6ecb7a92013-10-18 11:04:11 -0700763 gByte_class = (jclass) env->NewGlobalRef(
Mathieu Chartier7384b422013-10-17 18:16:42 -0700764 env->GetStaticObjectField(c, env->GetStaticFieldID(c, "TYPE", "Ljava/lang/Class;")));
765
766 gVMRuntime_class = make_globalref(env, "dalvik/system/VMRuntime");
767 m = env->GetStaticMethodID(gVMRuntime_class, "getRuntime", "()Ldalvik/system/VMRuntime;");
768 gVMRuntime = env->NewGlobalRef(env->CallStaticObjectMethod(gVMRuntime_class, m));
769 gVMRuntime_newNonMovableArray = env->GetMethodID(gVMRuntime_class, "newNonMovableArray",
770 "(Ljava/lang/Class;I)Ljava/lang/Object;");
771 gVMRuntime_addressOf = env->GetMethodID(gVMRuntime_class, "addressOf", "(Ljava/lang/Object;)J");
772
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800773 return 0;
774}