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