xref: /aosp_15_r20/frameworks/base/libs/hwui/jni/NinePatch.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker **
3*d57664e9SAndroid Build Coastguard Worker ** Copyright 2006, The Android Open Source Project
4*d57664e9SAndroid Build Coastguard Worker **
5*d57664e9SAndroid Build Coastguard Worker ** Licensed under the Apache License, Version 2.0 (the "License");
6*d57664e9SAndroid Build Coastguard Worker ** you may not use this file except in compliance with the License.
7*d57664e9SAndroid Build Coastguard Worker ** You may obtain a copy of the License at
8*d57664e9SAndroid Build Coastguard Worker **
9*d57664e9SAndroid Build Coastguard Worker **     http://www.apache.org/licenses/LICENSE-2.0
10*d57664e9SAndroid Build Coastguard Worker **
11*d57664e9SAndroid Build Coastguard Worker ** Unless required by applicable law or agreed to in writing, software
12*d57664e9SAndroid Build Coastguard Worker ** distributed under the License is distributed on an "AS IS" BASIS,
13*d57664e9SAndroid Build Coastguard Worker ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*d57664e9SAndroid Build Coastguard Worker ** See the License for the specific language governing permissions and
15*d57664e9SAndroid Build Coastguard Worker ** limitations under the License.
16*d57664e9SAndroid Build Coastguard Worker */
17*d57664e9SAndroid Build Coastguard Worker 
18*d57664e9SAndroid Build Coastguard Worker #define LOG_NDEBUG 1
19*d57664e9SAndroid Build Coastguard Worker 
20*d57664e9SAndroid Build Coastguard Worker #include <androidfw/ResourceTypes.h>
21*d57664e9SAndroid Build Coastguard Worker #include <hwui/Canvas.h>
22*d57664e9SAndroid Build Coastguard Worker #include <hwui/Paint.h>
23*d57664e9SAndroid Build Coastguard Worker #include <utils/Log.h>
24*d57664e9SAndroid Build Coastguard Worker 
25*d57664e9SAndroid Build Coastguard Worker #include "SkBitmap.h"
26*d57664e9SAndroid Build Coastguard Worker #include "SkCanvas.h"
27*d57664e9SAndroid Build Coastguard Worker #include "SkLatticeIter.h"
28*d57664e9SAndroid Build Coastguard Worker #include "SkRect.h"
29*d57664e9SAndroid Build Coastguard Worker #include "SkRegion.h"
30*d57664e9SAndroid Build Coastguard Worker #include "GraphicsJNI.h"
31*d57664e9SAndroid Build Coastguard Worker #include "NinePatchPeeker.h"
32*d57664e9SAndroid Build Coastguard Worker #include "NinePatchUtils.h"
33*d57664e9SAndroid Build Coastguard Worker 
34*d57664e9SAndroid Build Coastguard Worker jclass      gInsetStruct_class;
35*d57664e9SAndroid Build Coastguard Worker jmethodID   gInsetStruct_constructorMethodID;
36*d57664e9SAndroid Build Coastguard Worker 
37*d57664e9SAndroid Build Coastguard Worker using namespace android;
38*d57664e9SAndroid Build Coastguard Worker 
39*d57664e9SAndroid Build Coastguard Worker /**
40*d57664e9SAndroid Build Coastguard Worker  * IMPORTANT NOTE: 9patch chunks can be manipuated either as an array of bytes
41*d57664e9SAndroid Build Coastguard Worker  * or as a Res_png_9patch instance. It is important to note that the size of the
42*d57664e9SAndroid Build Coastguard Worker  * array required to hold a 9patch chunk is greater than sizeof(Res_png_9patch).
43*d57664e9SAndroid Build Coastguard Worker  * The code below manipulates chunks as Res_png_9patch* types to draw and as
44*d57664e9SAndroid Build Coastguard Worker  * int8_t* to allocate and free the backing storage.
45*d57664e9SAndroid Build Coastguard Worker  */
46*d57664e9SAndroid Build Coastguard Worker 
47*d57664e9SAndroid Build Coastguard Worker class SkNinePatchGlue {
48*d57664e9SAndroid Build Coastguard Worker public:
isNinePatchChunk(JNIEnv * env,jobject,jbyteArray obj)49*d57664e9SAndroid Build Coastguard Worker     static jboolean isNinePatchChunk(JNIEnv* env, jobject, jbyteArray obj) {
50*d57664e9SAndroid Build Coastguard Worker         if (NULL == obj) {
51*d57664e9SAndroid Build Coastguard Worker             return JNI_FALSE;
52*d57664e9SAndroid Build Coastguard Worker         }
53*d57664e9SAndroid Build Coastguard Worker         if (env->GetArrayLength(obj) < (int)sizeof(Res_png_9patch)) {
54*d57664e9SAndroid Build Coastguard Worker             return JNI_FALSE;
55*d57664e9SAndroid Build Coastguard Worker         }
56*d57664e9SAndroid Build Coastguard Worker         const jbyte* array = env->GetByteArrayElements(obj, 0);
57*d57664e9SAndroid Build Coastguard Worker         if (array != NULL) {
58*d57664e9SAndroid Build Coastguard Worker             const Res_png_9patch* chunk = reinterpret_cast<const Res_png_9patch*>(array);
59*d57664e9SAndroid Build Coastguard Worker             int8_t wasDeserialized = chunk->wasDeserialized;
60*d57664e9SAndroid Build Coastguard Worker             env->ReleaseByteArrayElements(obj, const_cast<jbyte*>(array), JNI_ABORT);
61*d57664e9SAndroid Build Coastguard Worker             return (wasDeserialized != -1) ? JNI_TRUE : JNI_FALSE;
62*d57664e9SAndroid Build Coastguard Worker         }
63*d57664e9SAndroid Build Coastguard Worker         return JNI_FALSE;
64*d57664e9SAndroid Build Coastguard Worker     }
65*d57664e9SAndroid Build Coastguard Worker 
validateNinePatchChunk(JNIEnv * env,jobject,jbyteArray obj)66*d57664e9SAndroid Build Coastguard Worker     static jlong validateNinePatchChunk(JNIEnv* env, jobject, jbyteArray obj) {
67*d57664e9SAndroid Build Coastguard Worker         size_t chunkSize = obj != NULL ? env->GetArrayLength(obj) : 0;
68*d57664e9SAndroid Build Coastguard Worker         if (chunkSize < (int) (sizeof(Res_png_9patch))) {
69*d57664e9SAndroid Build Coastguard Worker             jniThrowRuntimeException(env, "Array too small for chunk.");
70*d57664e9SAndroid Build Coastguard Worker             return 0;
71*d57664e9SAndroid Build Coastguard Worker         }
72*d57664e9SAndroid Build Coastguard Worker 
73*d57664e9SAndroid Build Coastguard Worker         int8_t* storage = new int8_t[chunkSize];
74*d57664e9SAndroid Build Coastguard Worker         // This call copies the content of the jbyteArray
75*d57664e9SAndroid Build Coastguard Worker         env->GetByteArrayRegion(obj, 0, chunkSize, reinterpret_cast<jbyte*>(storage));
76*d57664e9SAndroid Build Coastguard Worker         // Deserialize in place, return the array we just allocated
77*d57664e9SAndroid Build Coastguard Worker         return reinterpret_cast<jlong>(Res_png_9patch::deserialize(storage));
78*d57664e9SAndroid Build Coastguard Worker     }
79*d57664e9SAndroid Build Coastguard Worker 
finalize(JNIEnv * env,jobject,jlong patchHandle)80*d57664e9SAndroid Build Coastguard Worker     static void finalize(JNIEnv* env, jobject, jlong patchHandle) {
81*d57664e9SAndroid Build Coastguard Worker         int8_t* patch = reinterpret_cast<int8_t*>(patchHandle);
82*d57664e9SAndroid Build Coastguard Worker         delete[] patch;
83*d57664e9SAndroid Build Coastguard Worker     }
84*d57664e9SAndroid Build Coastguard Worker 
getTransparentRegion(JNIEnv * env,jobject,jlong bitmapPtr,jlong chunkHandle,jobject dstRect)85*d57664e9SAndroid Build Coastguard Worker     static jlong getTransparentRegion(JNIEnv* env, jobject, jlong bitmapPtr,
86*d57664e9SAndroid Build Coastguard Worker             jlong chunkHandle, jobject dstRect) {
87*d57664e9SAndroid Build Coastguard Worker         Res_png_9patch* chunk = reinterpret_cast<Res_png_9patch*>(chunkHandle);
88*d57664e9SAndroid Build Coastguard Worker         SkASSERT(chunk);
89*d57664e9SAndroid Build Coastguard Worker 
90*d57664e9SAndroid Build Coastguard Worker         SkBitmap bitmap;
91*d57664e9SAndroid Build Coastguard Worker         bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
92*d57664e9SAndroid Build Coastguard Worker         SkRect dst;
93*d57664e9SAndroid Build Coastguard Worker         GraphicsJNI::jrect_to_rect(env, dstRect, &dst);
94*d57664e9SAndroid Build Coastguard Worker 
95*d57664e9SAndroid Build Coastguard Worker         SkCanvas::Lattice lattice;
96*d57664e9SAndroid Build Coastguard Worker         SkIRect src = SkIRect::MakeWH(bitmap.width(), bitmap.height());
97*d57664e9SAndroid Build Coastguard Worker         lattice.fBounds = &src;
98*d57664e9SAndroid Build Coastguard Worker         NinePatchUtils::SetLatticeDivs(&lattice, *chunk, bitmap.width(), bitmap.height());
99*d57664e9SAndroid Build Coastguard Worker         lattice.fRectTypes = nullptr;
100*d57664e9SAndroid Build Coastguard Worker         lattice.fColors = nullptr;
101*d57664e9SAndroid Build Coastguard Worker 
102*d57664e9SAndroid Build Coastguard Worker         SkRegion* region = nullptr;
103*d57664e9SAndroid Build Coastguard Worker         if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), lattice)) {
104*d57664e9SAndroid Build Coastguard Worker             SkLatticeIter iter(lattice, dst);
105*d57664e9SAndroid Build Coastguard Worker             if (iter.numRectsToDraw() == chunk->numColors) {
106*d57664e9SAndroid Build Coastguard Worker                 SkRect dummy;
107*d57664e9SAndroid Build Coastguard Worker                 SkRect iterDst;
108*d57664e9SAndroid Build Coastguard Worker                 int index = 0;
109*d57664e9SAndroid Build Coastguard Worker                 while (iter.next(&dummy, &iterDst)) {
110*d57664e9SAndroid Build Coastguard Worker                     if (0 == chunk->getColors()[index++] && !iterDst.isEmpty()) {
111*d57664e9SAndroid Build Coastguard Worker                         if (!region) {
112*d57664e9SAndroid Build Coastguard Worker                             region = new SkRegion();
113*d57664e9SAndroid Build Coastguard Worker                         }
114*d57664e9SAndroid Build Coastguard Worker 
115*d57664e9SAndroid Build Coastguard Worker                         region->op(iterDst.round(), SkRegion::kUnion_Op);
116*d57664e9SAndroid Build Coastguard Worker                     }
117*d57664e9SAndroid Build Coastguard Worker                 }
118*d57664e9SAndroid Build Coastguard Worker             }
119*d57664e9SAndroid Build Coastguard Worker         }
120*d57664e9SAndroid Build Coastguard Worker 
121*d57664e9SAndroid Build Coastguard Worker         return reinterpret_cast<jlong>(region);
122*d57664e9SAndroid Build Coastguard Worker     }
123*d57664e9SAndroid Build Coastguard Worker 
124*d57664e9SAndroid Build Coastguard Worker };
125*d57664e9SAndroid Build Coastguard Worker 
createNinePatchInsets(JNIEnv * env,float scale) const126*d57664e9SAndroid Build Coastguard Worker jobject NinePatchPeeker::createNinePatchInsets(JNIEnv* env, float scale) const {
127*d57664e9SAndroid Build Coastguard Worker     if (!mHasInsets) {
128*d57664e9SAndroid Build Coastguard Worker         return nullptr;
129*d57664e9SAndroid Build Coastguard Worker     }
130*d57664e9SAndroid Build Coastguard Worker 
131*d57664e9SAndroid Build Coastguard Worker     return env->NewObject(gInsetStruct_class, gInsetStruct_constructorMethodID,
132*d57664e9SAndroid Build Coastguard Worker             mOpticalInsets[0], mOpticalInsets[1],
133*d57664e9SAndroid Build Coastguard Worker             mOpticalInsets[2], mOpticalInsets[3],
134*d57664e9SAndroid Build Coastguard Worker             mOutlineInsets[0], mOutlineInsets[1],
135*d57664e9SAndroid Build Coastguard Worker             mOutlineInsets[2], mOutlineInsets[3],
136*d57664e9SAndroid Build Coastguard Worker             mOutlineRadius, mOutlineAlpha, scale);
137*d57664e9SAndroid Build Coastguard Worker }
138*d57664e9SAndroid Build Coastguard Worker 
getPadding(JNIEnv * env,jobject outPadding) const139*d57664e9SAndroid Build Coastguard Worker void NinePatchPeeker::getPadding(JNIEnv* env, jobject outPadding) const {
140*d57664e9SAndroid Build Coastguard Worker     if (mPatch) {
141*d57664e9SAndroid Build Coastguard Worker         GraphicsJNI::set_jrect(env, outPadding,
142*d57664e9SAndroid Build Coastguard Worker                 mPatch->paddingLeft, mPatch->paddingTop,
143*d57664e9SAndroid Build Coastguard Worker                 mPatch->paddingRight, mPatch->paddingBottom);
144*d57664e9SAndroid Build Coastguard Worker 
145*d57664e9SAndroid Build Coastguard Worker     } else {
146*d57664e9SAndroid Build Coastguard Worker         GraphicsJNI::set_jrect(env, outPadding, -1, -1, -1, -1);
147*d57664e9SAndroid Build Coastguard Worker     }
148*d57664e9SAndroid Build Coastguard Worker }
149*d57664e9SAndroid Build Coastguard Worker 
150*d57664e9SAndroid Build Coastguard Worker /////////////////////////////////////////////////////////////////////////////////////////
151*d57664e9SAndroid Build Coastguard Worker 
152*d57664e9SAndroid Build Coastguard Worker static const JNINativeMethod gNinePatchMethods[] = {
153*d57664e9SAndroid Build Coastguard Worker     { "isNinePatchChunk", "([B)Z", (void*) SkNinePatchGlue::isNinePatchChunk },
154*d57664e9SAndroid Build Coastguard Worker     { "validateNinePatchChunk", "([B)J",
155*d57664e9SAndroid Build Coastguard Worker             (void*) SkNinePatchGlue::validateNinePatchChunk },
156*d57664e9SAndroid Build Coastguard Worker     { "nativeFinalize", "(J)V", (void*) SkNinePatchGlue::finalize },
157*d57664e9SAndroid Build Coastguard Worker     { "nativeGetTransparentRegion", "(JJLandroid/graphics/Rect;)J",
158*d57664e9SAndroid Build Coastguard Worker             (void*) SkNinePatchGlue::getTransparentRegion }
159*d57664e9SAndroid Build Coastguard Worker };
160*d57664e9SAndroid Build Coastguard Worker 
register_android_graphics_NinePatch(JNIEnv * env)161*d57664e9SAndroid Build Coastguard Worker int register_android_graphics_NinePatch(JNIEnv* env) {
162*d57664e9SAndroid Build Coastguard Worker     gInsetStruct_class = MakeGlobalRefOrDie(env, FindClassOrDie(env,
163*d57664e9SAndroid Build Coastguard Worker             "android/graphics/NinePatch$InsetStruct"));
164*d57664e9SAndroid Build Coastguard Worker     gInsetStruct_constructorMethodID = GetMethodIDOrDie(env, gInsetStruct_class, "<init>",
165*d57664e9SAndroid Build Coastguard Worker             "(IIIIIIIIFIF)V");
166*d57664e9SAndroid Build Coastguard Worker     return android::RegisterMethodsOrDie(env,
167*d57664e9SAndroid Build Coastguard Worker             "android/graphics/NinePatch", gNinePatchMethods, NELEM(gNinePatchMethods));
168*d57664e9SAndroid Build Coastguard Worker }
169