1 /*
2  * Copyright (C) 2011 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 "SkRegion.h"
18 #include "SkPath.h"
19 #include "GraphicsJNI.h"
20 
21 #ifdef __ANDROID__ // Layoutlib does not support parcel
22 #include <android/binder_parcel.h>
23 #include <android/binder_parcel_jni.h>
24 #include <android/binder_parcel_utils.h>
25 #endif
26 
27 namespace android {
28 
29 static jfieldID gRegion_nativeInstanceFieldID;
30 
boolTojboolean(bool value)31 static inline jboolean boolTojboolean(bool value) {
32     return value ? JNI_TRUE : JNI_FALSE;
33 }
34 
GetSkRegion(JNIEnv * env,jobject regionObject)35 static inline SkRegion* GetSkRegion(JNIEnv* env, jobject regionObject) {
36     jlong regionHandle = env->GetLongField(regionObject, gRegion_nativeInstanceFieldID);
37     SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
38     SkASSERT(region != NULL);
39     return region;
40 }
41 
Region_constructor(JNIEnv * env,jobject)42 static jlong Region_constructor(JNIEnv* env, jobject) {
43     return reinterpret_cast<jlong>(new SkRegion);
44 }
45 
Region_destructor(JNIEnv * env,jobject,jlong regionHandle)46 static void Region_destructor(JNIEnv* env, jobject, jlong regionHandle) {
47     SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
48     SkASSERT(region);
49     delete region;
50 }
51 
Region_setRegion(JNIEnv * env,jobject,jlong dstHandle,jlong srcHandle)52 static void Region_setRegion(JNIEnv* env, jobject, jlong dstHandle, jlong srcHandle) {
53     SkRegion* dst = reinterpret_cast<SkRegion*>(dstHandle);
54     const SkRegion* src = reinterpret_cast<SkRegion*>(srcHandle);
55     SkASSERT(dst && src);
56     *dst = *src;
57 }
58 
Region_setRect(JNIEnv * env,jobject,jlong dstHandle,jint left,jint top,jint right,jint bottom)59 static jboolean Region_setRect(JNIEnv* env, jobject, jlong dstHandle, jint left, jint top, jint right, jint bottom) {
60     SkRegion* dst = reinterpret_cast<SkRegion*>(dstHandle);
61     bool result = dst->setRect({left, top, right, bottom});
62     return boolTojboolean(result);
63 }
64 
Region_setPath(JNIEnv * env,jobject,jlong dstHandle,jlong pathHandle,jlong clipHandle)65 static jboolean Region_setPath(JNIEnv* env, jobject, jlong dstHandle,
66                                jlong pathHandle, jlong clipHandle) {
67     SkRegion*       dst  = reinterpret_cast<SkRegion*>(dstHandle);
68     const SkPath*   path = reinterpret_cast<SkPath*>(pathHandle);
69     const SkRegion* clip = reinterpret_cast<SkRegion*>(clipHandle);
70     SkASSERT(dst && path && clip);
71     bool result = dst->setPath(*path, *clip);
72     return boolTojboolean(result);
73 
74 }
75 
Region_getBounds(JNIEnv * env,jobject,jlong regionHandle,jobject rectBounds)76 static jboolean Region_getBounds(JNIEnv* env, jobject, jlong regionHandle, jobject rectBounds) {
77     SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
78     GraphicsJNI::irect_to_jrect(region->getBounds(), env, rectBounds);
79     bool result = !region->isEmpty();
80     return boolTojboolean(result);
81 }
82 
Region_getBoundaryPath(JNIEnv * env,jobject,jlong regionHandle,jlong pathHandle)83 static jboolean Region_getBoundaryPath(JNIEnv* env, jobject, jlong regionHandle, jlong pathHandle) {
84     const SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
85     SkPath*   path = reinterpret_cast<SkPath*>(pathHandle);
86     bool result = region->getBoundaryPath(path);
87     return boolTojboolean(result);
88 }
89 
Region_op0(JNIEnv * env,jobject,jlong dstHandle,jint left,jint top,jint right,jint bottom,jint op)90 static jboolean Region_op0(JNIEnv* env, jobject, jlong dstHandle, jint left, jint top, jint right, jint bottom, jint op) {
91     SkRegion* dst = reinterpret_cast<SkRegion*>(dstHandle);
92     bool result = dst->op({left, top, right, bottom}, (SkRegion::Op)op);
93     return boolTojboolean(result);
94 }
95 
Region_op1(JNIEnv * env,jobject,jlong dstHandle,jobject rectObject,jlong regionHandle,jint op)96 static jboolean Region_op1(JNIEnv* env, jobject, jlong dstHandle, jobject rectObject, jlong regionHandle, jint op) {
97     SkRegion* dst = reinterpret_cast<SkRegion*>(dstHandle);
98     const SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
99     SkIRect    ir;
100     GraphicsJNI::jrect_to_irect(env, rectObject, &ir);
101     bool result = dst->op(ir, *region, (SkRegion::Op)op);
102     return boolTojboolean(result);
103 }
104 
Region_op2(JNIEnv * env,jobject,jlong dstHandle,jlong region1Handle,jlong region2Handle,jint op)105 static jboolean Region_op2(JNIEnv* env, jobject, jlong dstHandle, jlong region1Handle, jlong region2Handle, jint op) {
106     SkRegion* dst = reinterpret_cast<SkRegion*>(dstHandle);
107     const SkRegion* region1 = reinterpret_cast<SkRegion*>(region1Handle);
108     const SkRegion* region2 = reinterpret_cast<SkRegion*>(region2Handle);
109     bool result = dst->op(*region1, *region2, (SkRegion::Op)op);
110     return boolTojboolean(result);
111 }
112 
113 ////////////////////////////////////  These are methods, not static
114 
Region_isEmpty(JNIEnv * env,jobject region)115 static jboolean Region_isEmpty(JNIEnv* env, jobject region) {
116     bool result = GetSkRegion(env, region)->isEmpty();
117     return boolTojboolean(result);
118 }
119 
Region_isRect(JNIEnv * env,jobject region)120 static jboolean Region_isRect(JNIEnv* env, jobject region) {
121     bool result = GetSkRegion(env, region)->isRect();
122     return boolTojboolean(result);
123 }
124 
Region_isComplex(JNIEnv * env,jobject region)125 static jboolean Region_isComplex(JNIEnv* env, jobject region) {
126     bool result = GetSkRegion(env, region)->isComplex();
127     return boolTojboolean(result);
128 }
129 
Region_contains(JNIEnv * env,jobject region,jint x,jint y)130 static jboolean Region_contains(JNIEnv* env, jobject region, jint x, jint y) {
131     bool result = GetSkRegion(env, region)->contains(x, y);
132     return boolTojboolean(result);
133 }
134 
Region_quickContains(JNIEnv * env,jobject region,jint left,jint top,jint right,jint bottom)135 static jboolean Region_quickContains(JNIEnv* env, jobject region, jint left, jint top, jint right, jint bottom) {
136     bool result = GetSkRegion(env, region)->quickContains({left, top, right, bottom});
137     return boolTojboolean(result);
138 }
139 
Region_quickRejectIIII(JNIEnv * env,jobject region,jint left,jint top,jint right,jint bottom)140 static jboolean Region_quickRejectIIII(JNIEnv* env, jobject region, jint left, jint top, jint right, jint bottom) {
141     SkIRect ir;
142     ir.setLTRB(left, top, right, bottom);
143     bool result = GetSkRegion(env, region)->quickReject(ir);
144     return boolTojboolean(result);
145 }
146 
Region_quickRejectRgn(JNIEnv * env,jobject region,jobject other)147 static jboolean Region_quickRejectRgn(JNIEnv* env, jobject region, jobject other) {
148     bool result = GetSkRegion(env, region)->quickReject(*GetSkRegion(env, other));
149     return boolTojboolean(result);
150 }
151 
Region_translate(JNIEnv * env,jobject region,jint x,jint y,jobject dst)152 static void Region_translate(JNIEnv* env, jobject region, jint x, jint y, jobject dst) {
153     SkRegion* rgn = GetSkRegion(env, region);
154     if (dst)
155         rgn->translate(x, y, GetSkRegion(env, dst));
156     else
157         rgn->translate(x, y);
158 }
159 
160 // Scale the rectangle by given scale and set the reuslt to the dst.
scale_rect(SkIRect * dst,const SkIRect & src,float scale)161 static void scale_rect(SkIRect* dst, const SkIRect& src, float scale) {
162    dst->fLeft = (int)::roundf(src.fLeft * scale);
163    dst->fTop = (int)::roundf(src.fTop * scale);
164    dst->fRight = (int)::roundf(src.fRight * scale);
165    dst->fBottom = (int)::roundf(src.fBottom * scale);
166 }
167 
168 // Scale the region by given scale and set the reuslt to the dst.
169 // dest and src can be the same region instance.
scale_rgn(SkRegion * dst,const SkRegion & src,float scale)170 static void scale_rgn(SkRegion* dst, const SkRegion& src, float scale) {
171    SkRegion tmp;
172    SkRegion::Iterator iter(src);
173 
174    for (; !iter.done(); iter.next()) {
175        SkIRect r;
176        scale_rect(&r, iter.rect(), scale);
177        tmp.op(r, SkRegion::kUnion_Op);
178    }
179    dst->swap(tmp);
180 }
181 
Region_scale(JNIEnv * env,jobject region,jfloat scale,jobject dst)182 static void Region_scale(JNIEnv* env, jobject region, jfloat scale, jobject dst) {
183     SkRegion* rgn = GetSkRegion(env, region);
184     if (dst)
185         scale_rgn(GetSkRegion(env, dst), *rgn, scale);
186     else
187         scale_rgn(rgn, *rgn, scale);
188 }
189 
Region_toString(JNIEnv * env,jobject clazz,jlong regionHandle)190 static jstring Region_toString(JNIEnv* env, jobject clazz, jlong regionHandle) {
191     SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
192     char* str = region->toString();
193     if (str == NULL) {
194         return NULL;
195     }
196     jstring result = env->NewStringUTF(str);
197     free(str);
198     return result;
199 }
200 
201 ////////////////////////////////////////////////////////////////////////////////////////////////////////////
202 
Region_createFromParcel(JNIEnv * env,jobject clazz,jobject parcel)203 static jlong Region_createFromParcel(JNIEnv* env, jobject clazz, jobject parcel)
204 {
205 #ifdef __ANDROID__ // Layoutlib does not support parcel
206     if (parcel == nullptr) {
207         return 0;
208     }
209 
210     std::vector<int32_t> rects;
211 
212     AParcel* p = AParcel_fromJavaParcel(env, parcel);
213     ndk::AParcel_readVector(p, &rects);
214     AParcel_delete(p);
215 
216     if ((rects.size() % 4) != 0) {
217         return 0;
218     }
219 
220     SkRegion* region = new SkRegion;
221     for (size_t x = 0; x + 4 <= rects.size(); x += 4) {
222         region->op({rects[x], rects[x+1], rects[x+2], rects[x+3]}, SkRegion::kUnion_Op);
223     }
224 
225     return reinterpret_cast<jlong>(region);
226 #else
227     return 0;
228 #endif
229 }
230 
Region_writeToParcel(JNIEnv * env,jobject clazz,jlong regionHandle,jobject parcel)231 static jboolean Region_writeToParcel(JNIEnv* env, jobject clazz, jlong regionHandle, jobject parcel)
232 {
233 #ifdef __ANDROID__ // Layoutlib does not support parcel
234     const SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
235     if (parcel == nullptr) {
236         return JNI_FALSE;
237     }
238 
239     std::vector<int32_t> rects;
240     SkRegion::Iterator it(*region);
241     while (!it.done()) {
242         const SkIRect& r = it.rect();
243         rects.push_back(r.fLeft);
244         rects.push_back(r.fTop);
245         rects.push_back(r.fRight);
246         rects.push_back(r.fBottom);
247         it.next();
248     }
249 
250     AParcel* p = AParcel_fromJavaParcel(env, parcel);
251     ndk::AParcel_writeVector(p, rects);
252     AParcel_delete(p);
253 
254     return JNI_TRUE;
255 #else
256     return JNI_FALSE;
257 #endif
258 }
259 
260 ////////////////////////////////////////////////////////////////////////////////////////////////////////////
261 
Region_equals(JNIEnv * env,jobject clazz,jlong r1Handle,jlong r2Handle)262 static jboolean Region_equals(JNIEnv* env, jobject clazz, jlong r1Handle, jlong r2Handle)
263 {
264     const SkRegion *r1 = reinterpret_cast<SkRegion*>(r1Handle);
265     const SkRegion *r2 = reinterpret_cast<SkRegion*>(r2Handle);
266     return boolTojboolean(*r1 == *r2);
267 }
268 
269 ////////////////////////////////////////////////////////////////////////////////////////////////////////////
270 
271 struct RgnIterPair {
272     SkRegion            fRgn;   // a copy of the caller's region
273     SkRegion::Iterator  fIter;  // an iterator acting upon the copy (fRgn)
274 
RgnIterPairandroid::RgnIterPair275     explicit RgnIterPair(const SkRegion& rgn) : fRgn(rgn) {
276         // have our iterator reference our copy (fRgn), so we know it will be
277         // unchanged for the lifetime of the iterator
278         fIter.reset(fRgn);
279     }
280 };
281 
RegionIter_constructor(JNIEnv * env,jobject,jlong regionHandle)282 static jlong RegionIter_constructor(JNIEnv* env, jobject, jlong regionHandle)
283 {
284     const SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
285     SkASSERT(region);
286     return reinterpret_cast<jlong>(new RgnIterPair(*region));
287 }
288 
RegionIter_destructor(JNIEnv * env,jobject,jlong pairHandle)289 static void RegionIter_destructor(JNIEnv* env, jobject, jlong pairHandle)
290 {
291     RgnIterPair* pair = reinterpret_cast<RgnIterPair*>(pairHandle);
292     SkASSERT(pair);
293     delete pair;
294 }
295 
RegionIter_next(JNIEnv * env,jobject,jlong pairHandle,jobject rectObject)296 static jboolean RegionIter_next(JNIEnv* env, jobject, jlong pairHandle, jobject rectObject)
297 {
298     RgnIterPair* pair = reinterpret_cast<RgnIterPair*>(pairHandle);
299     // the caller has checked that rectObject is not nul
300     SkASSERT(pair);
301     SkASSERT(rectObject);
302 
303     if (!pair->fIter.done()) {
304         GraphicsJNI::irect_to_jrect(pair->fIter.rect(), env, rectObject);
305         pair->fIter.next();
306         return JNI_TRUE;
307     }
308     return JNI_FALSE;
309 }
310 
311 ////////////////////////////////////////////////////////////////////////////////////////////////////////////
312 
313 static const JNINativeMethod gRegionIterMethods[] = {
314     { "nativeConstructor",  "(J)J",                         (void*)RegionIter_constructor   },
315     { "nativeDestructor",   "(J)V",                         (void*)RegionIter_destructor    },
316     { "nativeNext",         "(JLandroid/graphics/Rect;)Z",  (void*)RegionIter_next          }
317 };
318 
319 static const JNINativeMethod gRegionMethods[] = {
320     // these are static methods
321     { "nativeConstructor",      "()J",                              (void*)Region_constructor       },
322     { "nativeDestructor",       "(J)V",                             (void*)Region_destructor        },
323     { "nativeSetRegion",        "(JJ)V",                            (void*)Region_setRegion         },
324     { "nativeSetRect",          "(JIIII)Z",                         (void*)Region_setRect           },
325     { "nativeSetPath",          "(JJJ)Z",                           (void*)Region_setPath           },
326     { "nativeGetBounds",        "(JLandroid/graphics/Rect;)Z",      (void*)Region_getBounds         },
327     { "nativeGetBoundaryPath",  "(JJ)Z",                            (void*)Region_getBoundaryPath   },
328     { "nativeOp",               "(JIIIII)Z",                        (void*)Region_op0               },
329     { "nativeOp",               "(JLandroid/graphics/Rect;JI)Z",    (void*)Region_op1               },
330     { "nativeOp",               "(JJJI)Z",                          (void*)Region_op2               },
331     // these are methods that take the java region object
332     { "isEmpty",                "()Z",                              (void*)Region_isEmpty           },
333     { "isRect",                 "()Z",                              (void*)Region_isRect            },
334     { "isComplex",              "()Z",                              (void*)Region_isComplex         },
335     { "contains",               "(II)Z",                            (void*)Region_contains          },
336     { "quickContains",          "(IIII)Z",                          (void*)Region_quickContains     },
337     { "quickReject",            "(IIII)Z",                          (void*)Region_quickRejectIIII   },
338     { "quickReject",            "(Landroid/graphics/Region;)Z",     (void*)Region_quickRejectRgn    },
339     { "scale",                  "(FLandroid/graphics/Region;)V",    (void*)Region_scale             },
340     { "translate",              "(IILandroid/graphics/Region;)V",   (void*)Region_translate         },
341     { "nativeToString",         "(J)Ljava/lang/String;",            (void*)Region_toString          },
342     // parceling methods
343     { "nativeCreateFromParcel", "(Landroid/os/Parcel;)J",           (void*)Region_createFromParcel  },
344     { "nativeWriteToParcel",    "(JLandroid/os/Parcel;)Z",          (void*)Region_writeToParcel     },
345     { "nativeEquals",           "(JJ)Z",                            (void*)Region_equals            },
346 };
347 
register_android_graphics_Region(JNIEnv * env)348 int register_android_graphics_Region(JNIEnv* env)
349 {
350     jclass clazz = FindClassOrDie(env, "android/graphics/Region");
351 
352     gRegion_nativeInstanceFieldID = GetFieldIDOrDie(env, clazz, "mNativeRegion", "J");
353 
354     RegisterMethodsOrDie(env, "android/graphics/Region", gRegionMethods, NELEM(gRegionMethods));
355     return RegisterMethodsOrDie(env, "android/graphics/RegionIterator", gRegionIterMethods,
356                                 NELEM(gRegionIterMethods));
357 }
358 
359 } // namespace android
360