/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "SkRegion.h" #include "SkPath.h" #include "GraphicsJNI.h" #ifdef __ANDROID__ // Layoutlib does not support parcel #include #include #include #endif namespace android { static jfieldID gRegion_nativeInstanceFieldID; static inline jboolean boolTojboolean(bool value) { return value ? JNI_TRUE : JNI_FALSE; } static inline SkRegion* GetSkRegion(JNIEnv* env, jobject regionObject) { jlong regionHandle = env->GetLongField(regionObject, gRegion_nativeInstanceFieldID); SkRegion* region = reinterpret_cast(regionHandle); SkASSERT(region != NULL); return region; } static jlong Region_constructor(JNIEnv* env, jobject) { return reinterpret_cast(new SkRegion); } static void Region_destructor(JNIEnv* env, jobject, jlong regionHandle) { SkRegion* region = reinterpret_cast(regionHandle); SkASSERT(region); delete region; } static void Region_setRegion(JNIEnv* env, jobject, jlong dstHandle, jlong srcHandle) { SkRegion* dst = reinterpret_cast(dstHandle); const SkRegion* src = reinterpret_cast(srcHandle); SkASSERT(dst && src); *dst = *src; } static jboolean Region_setRect(JNIEnv* env, jobject, jlong dstHandle, jint left, jint top, jint right, jint bottom) { SkRegion* dst = reinterpret_cast(dstHandle); bool result = dst->setRect({left, top, right, bottom}); return boolTojboolean(result); } static jboolean Region_setPath(JNIEnv* env, jobject, jlong dstHandle, jlong pathHandle, jlong clipHandle) { SkRegion* dst = reinterpret_cast(dstHandle); const SkPath* path = reinterpret_cast(pathHandle); const SkRegion* clip = reinterpret_cast(clipHandle); SkASSERT(dst && path && clip); bool result = dst->setPath(*path, *clip); return boolTojboolean(result); } static jboolean Region_getBounds(JNIEnv* env, jobject, jlong regionHandle, jobject rectBounds) { SkRegion* region = reinterpret_cast(regionHandle); GraphicsJNI::irect_to_jrect(region->getBounds(), env, rectBounds); bool result = !region->isEmpty(); return boolTojboolean(result); } static jboolean Region_getBoundaryPath(JNIEnv* env, jobject, jlong regionHandle, jlong pathHandle) { const SkRegion* region = reinterpret_cast(regionHandle); SkPath* path = reinterpret_cast(pathHandle); bool result = region->getBoundaryPath(path); return boolTojboolean(result); } static jboolean Region_op0(JNIEnv* env, jobject, jlong dstHandle, jint left, jint top, jint right, jint bottom, jint op) { SkRegion* dst = reinterpret_cast(dstHandle); bool result = dst->op({left, top, right, bottom}, (SkRegion::Op)op); return boolTojboolean(result); } static jboolean Region_op1(JNIEnv* env, jobject, jlong dstHandle, jobject rectObject, jlong regionHandle, jint op) { SkRegion* dst = reinterpret_cast(dstHandle); const SkRegion* region = reinterpret_cast(regionHandle); SkIRect ir; GraphicsJNI::jrect_to_irect(env, rectObject, &ir); bool result = dst->op(ir, *region, (SkRegion::Op)op); return boolTojboolean(result); } static jboolean Region_op2(JNIEnv* env, jobject, jlong dstHandle, jlong region1Handle, jlong region2Handle, jint op) { SkRegion* dst = reinterpret_cast(dstHandle); const SkRegion* region1 = reinterpret_cast(region1Handle); const SkRegion* region2 = reinterpret_cast(region2Handle); bool result = dst->op(*region1, *region2, (SkRegion::Op)op); return boolTojboolean(result); } //////////////////////////////////// These are methods, not static static jboolean Region_isEmpty(JNIEnv* env, jobject region) { bool result = GetSkRegion(env, region)->isEmpty(); return boolTojboolean(result); } static jboolean Region_isRect(JNIEnv* env, jobject region) { bool result = GetSkRegion(env, region)->isRect(); return boolTojboolean(result); } static jboolean Region_isComplex(JNIEnv* env, jobject region) { bool result = GetSkRegion(env, region)->isComplex(); return boolTojboolean(result); } static jboolean Region_contains(JNIEnv* env, jobject region, jint x, jint y) { bool result = GetSkRegion(env, region)->contains(x, y); return boolTojboolean(result); } static jboolean Region_quickContains(JNIEnv* env, jobject region, jint left, jint top, jint right, jint bottom) { bool result = GetSkRegion(env, region)->quickContains({left, top, right, bottom}); return boolTojboolean(result); } static jboolean Region_quickRejectIIII(JNIEnv* env, jobject region, jint left, jint top, jint right, jint bottom) { SkIRect ir; ir.setLTRB(left, top, right, bottom); bool result = GetSkRegion(env, region)->quickReject(ir); return boolTojboolean(result); } static jboolean Region_quickRejectRgn(JNIEnv* env, jobject region, jobject other) { bool result = GetSkRegion(env, region)->quickReject(*GetSkRegion(env, other)); return boolTojboolean(result); } static void Region_translate(JNIEnv* env, jobject region, jint x, jint y, jobject dst) { SkRegion* rgn = GetSkRegion(env, region); if (dst) rgn->translate(x, y, GetSkRegion(env, dst)); else rgn->translate(x, y); } // Scale the rectangle by given scale and set the reuslt to the dst. static void scale_rect(SkIRect* dst, const SkIRect& src, float scale) { dst->fLeft = (int)::roundf(src.fLeft * scale); dst->fTop = (int)::roundf(src.fTop * scale); dst->fRight = (int)::roundf(src.fRight * scale); dst->fBottom = (int)::roundf(src.fBottom * scale); } // Scale the region by given scale and set the reuslt to the dst. // dest and src can be the same region instance. static void scale_rgn(SkRegion* dst, const SkRegion& src, float scale) { SkRegion tmp; SkRegion::Iterator iter(src); for (; !iter.done(); iter.next()) { SkIRect r; scale_rect(&r, iter.rect(), scale); tmp.op(r, SkRegion::kUnion_Op); } dst->swap(tmp); } static void Region_scale(JNIEnv* env, jobject region, jfloat scale, jobject dst) { SkRegion* rgn = GetSkRegion(env, region); if (dst) scale_rgn(GetSkRegion(env, dst), *rgn, scale); else scale_rgn(rgn, *rgn, scale); } static jstring Region_toString(JNIEnv* env, jobject clazz, jlong regionHandle) { SkRegion* region = reinterpret_cast(regionHandle); char* str = region->toString(); if (str == NULL) { return NULL; } jstring result = env->NewStringUTF(str); free(str); return result; } //////////////////////////////////////////////////////////////////////////////////////////////////////////// static jlong Region_createFromParcel(JNIEnv* env, jobject clazz, jobject parcel) { #ifdef __ANDROID__ // Layoutlib does not support parcel if (parcel == nullptr) { return 0; } std::vector rects; AParcel* p = AParcel_fromJavaParcel(env, parcel); ndk::AParcel_readVector(p, &rects); AParcel_delete(p); if ((rects.size() % 4) != 0) { return 0; } SkRegion* region = new SkRegion; for (size_t x = 0; x + 4 <= rects.size(); x += 4) { region->op({rects[x], rects[x+1], rects[x+2], rects[x+3]}, SkRegion::kUnion_Op); } return reinterpret_cast(region); #else return 0; #endif } static jboolean Region_writeToParcel(JNIEnv* env, jobject clazz, jlong regionHandle, jobject parcel) { #ifdef __ANDROID__ // Layoutlib does not support parcel const SkRegion* region = reinterpret_cast(regionHandle); if (parcel == nullptr) { return JNI_FALSE; } std::vector rects; SkRegion::Iterator it(*region); while (!it.done()) { const SkIRect& r = it.rect(); rects.push_back(r.fLeft); rects.push_back(r.fTop); rects.push_back(r.fRight); rects.push_back(r.fBottom); it.next(); } AParcel* p = AParcel_fromJavaParcel(env, parcel); ndk::AParcel_writeVector(p, rects); AParcel_delete(p); return JNI_TRUE; #else return JNI_FALSE; #endif } //////////////////////////////////////////////////////////////////////////////////////////////////////////// static jboolean Region_equals(JNIEnv* env, jobject clazz, jlong r1Handle, jlong r2Handle) { const SkRegion *r1 = reinterpret_cast(r1Handle); const SkRegion *r2 = reinterpret_cast(r2Handle); return boolTojboolean(*r1 == *r2); } //////////////////////////////////////////////////////////////////////////////////////////////////////////// struct RgnIterPair { SkRegion fRgn; // a copy of the caller's region SkRegion::Iterator fIter; // an iterator acting upon the copy (fRgn) explicit RgnIterPair(const SkRegion& rgn) : fRgn(rgn) { // have our iterator reference our copy (fRgn), so we know it will be // unchanged for the lifetime of the iterator fIter.reset(fRgn); } }; static jlong RegionIter_constructor(JNIEnv* env, jobject, jlong regionHandle) { const SkRegion* region = reinterpret_cast(regionHandle); SkASSERT(region); return reinterpret_cast(new RgnIterPair(*region)); } static void RegionIter_destructor(JNIEnv* env, jobject, jlong pairHandle) { RgnIterPair* pair = reinterpret_cast(pairHandle); SkASSERT(pair); delete pair; } static jboolean RegionIter_next(JNIEnv* env, jobject, jlong pairHandle, jobject rectObject) { RgnIterPair* pair = reinterpret_cast(pairHandle); // the caller has checked that rectObject is not nul SkASSERT(pair); SkASSERT(rectObject); if (!pair->fIter.done()) { GraphicsJNI::irect_to_jrect(pair->fIter.rect(), env, rectObject); pair->fIter.next(); return JNI_TRUE; } return JNI_FALSE; } //////////////////////////////////////////////////////////////////////////////////////////////////////////// static const JNINativeMethod gRegionIterMethods[] = { { "nativeConstructor", "(J)J", (void*)RegionIter_constructor }, { "nativeDestructor", "(J)V", (void*)RegionIter_destructor }, { "nativeNext", "(JLandroid/graphics/Rect;)Z", (void*)RegionIter_next } }; static const JNINativeMethod gRegionMethods[] = { // these are static methods { "nativeConstructor", "()J", (void*)Region_constructor }, { "nativeDestructor", "(J)V", (void*)Region_destructor }, { "nativeSetRegion", "(JJ)V", (void*)Region_setRegion }, { "nativeSetRect", "(JIIII)Z", (void*)Region_setRect }, { "nativeSetPath", "(JJJ)Z", (void*)Region_setPath }, { "nativeGetBounds", "(JLandroid/graphics/Rect;)Z", (void*)Region_getBounds }, { "nativeGetBoundaryPath", "(JJ)Z", (void*)Region_getBoundaryPath }, { "nativeOp", "(JIIIII)Z", (void*)Region_op0 }, { "nativeOp", "(JLandroid/graphics/Rect;JI)Z", (void*)Region_op1 }, { "nativeOp", "(JJJI)Z", (void*)Region_op2 }, // these are methods that take the java region object { "isEmpty", "()Z", (void*)Region_isEmpty }, { "isRect", "()Z", (void*)Region_isRect }, { "isComplex", "()Z", (void*)Region_isComplex }, { "contains", "(II)Z", (void*)Region_contains }, { "quickContains", "(IIII)Z", (void*)Region_quickContains }, { "quickReject", "(IIII)Z", (void*)Region_quickRejectIIII }, { "quickReject", "(Landroid/graphics/Region;)Z", (void*)Region_quickRejectRgn }, { "scale", "(FLandroid/graphics/Region;)V", (void*)Region_scale }, { "translate", "(IILandroid/graphics/Region;)V", (void*)Region_translate }, { "nativeToString", "(J)Ljava/lang/String;", (void*)Region_toString }, // parceling methods { "nativeCreateFromParcel", "(Landroid/os/Parcel;)J", (void*)Region_createFromParcel }, { "nativeWriteToParcel", "(JLandroid/os/Parcel;)Z", (void*)Region_writeToParcel }, { "nativeEquals", "(JJ)Z", (void*)Region_equals }, }; int register_android_graphics_Region(JNIEnv* env) { jclass clazz = FindClassOrDie(env, "android/graphics/Region"); gRegion_nativeInstanceFieldID = GetFieldIDOrDie(env, clazz, "mNativeRegion", "J"); RegisterMethodsOrDie(env, "android/graphics/Region", gRegionMethods, NELEM(gRegionMethods)); return RegisterMethodsOrDie(env, "android/graphics/RegionIterator", gRegionIterMethods, NELEM(gRegionIterMethods)); } } // namespace android