NinePatch.cpp revision b13b9bdad2baf6ad1ec2e56b6b7598fa20f55fc4
1/*
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9**     http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#define LOG_TAG "9patch"
19#define LOG_NDEBUG 1
20
21#include <androidfw/ResourceTypes.h>
22#include <utils/Log.h>
23
24#include "SkCanvas.h"
25#include "SkRegion.h"
26#include "GraphicsJNI.h"
27
28#include "JNIHelp.h"
29
30extern void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds,
31                const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
32                           const SkPaint* paint, SkRegion** outRegion);
33
34using namespace android;
35
36class SkNinePatchGlue {
37public:
38    static jboolean isNinePatchChunk(JNIEnv* env, jobject, jbyteArray obj)
39    {
40        if (NULL == obj) {
41            return false;
42        }
43        if (env->GetArrayLength(obj) < (int)sizeof(Res_png_9patch)) {
44            return false;
45        }
46        const jbyte* array = env->GetByteArrayElements(obj, 0);
47        if (array != NULL) {
48            const Res_png_9patch* chunk =
49                                reinterpret_cast<const Res_png_9patch*>(array);
50            int8_t wasDeserialized = chunk->wasDeserialized;
51            env->ReleaseByteArrayElements(obj, const_cast<jbyte*>(array),
52                                          JNI_ABORT);
53            return wasDeserialized != -1;
54        }
55        return false;
56    }
57
58    static void validateNinePatchChunk(JNIEnv* env, jobject, jint, jbyteArray obj)
59    {
60        if (env->GetArrayLength(obj) < (int) (sizeof(Res_png_9patch))) {
61            jniThrowRuntimeException(env, "Array too small for chunk.");
62            return;
63        }
64
65        // XXX Also check that dimensions are correct.
66    }
67
68    static void draw(JNIEnv* env, SkCanvas* canvas, SkRect& bounds,
69                      const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint,
70                      jint destDensity, jint srcDensity)
71    {
72        size_t chunkSize = env->GetArrayLength(chunkObj);
73        void* storage = alloca(chunkSize);
74        env->GetByteArrayRegion(chunkObj, 0, chunkSize,
75                                reinterpret_cast<jbyte*>(storage));
76        if (!env->ExceptionCheck()) {
77            // need to deserialize the chunk
78            Res_png_9patch* chunk = static_cast<Res_png_9patch*>(storage);
79            assert(chunkSize == chunk->serializedSize());
80            // this relies on deserialization being done in place
81            Res_png_9patch::deserialize(chunk);
82
83            if (destDensity == srcDensity || destDensity == 0
84                    || srcDensity == 0) {
85                ALOGV("Drawing unscaled 9-patch: (%g,%g)-(%g,%g)",
86                        SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop),
87                        SkScalarToFloat(bounds.fRight), SkScalarToFloat(bounds.fBottom));
88                NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL);
89            } else {
90                canvas->save();
91
92                SkScalar scale = SkFloatToScalar(destDensity / (float)srcDensity);
93                canvas->translate(bounds.fLeft, bounds.fTop);
94                canvas->scale(scale, scale);
95
96                bounds.fRight = SkScalarDiv(bounds.fRight-bounds.fLeft, scale);
97                bounds.fBottom = SkScalarDiv(bounds.fBottom-bounds.fTop, scale);
98                bounds.fLeft = bounds.fTop = 0;
99
100                ALOGV("Drawing scaled 9-patch: (%g,%g)-(%g,%g) srcDensity=%d destDensity=%d",
101                        SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop),
102                        SkScalarToFloat(bounds.fRight), SkScalarToFloat(bounds.fBottom),
103                        srcDensity, destDensity);
104
105                NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL);
106
107                canvas->restore();
108            }
109        }
110    }
111
112    static void drawF(JNIEnv* env, jobject, SkCanvas* canvas, jobject boundsRectF,
113                      const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint,
114                      jint destDensity, jint srcDensity)
115    {
116        SkASSERT(canvas);
117        SkASSERT(boundsRectF);
118        SkASSERT(bitmap);
119        SkASSERT(chunkObj);
120        // paint is optional
121
122        SkRect      bounds;
123        GraphicsJNI::jrectf_to_rect(env, boundsRectF, &bounds);
124
125        draw(env, canvas, bounds, bitmap, chunkObj, paint, destDensity, srcDensity);
126    }
127
128    static void drawI(JNIEnv* env, jobject, SkCanvas* canvas, jobject boundsRect,
129                      const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint,
130                      jint destDensity, jint srcDensity)
131    {
132        SkASSERT(canvas);
133        SkASSERT(boundsRect);
134        SkASSERT(bitmap);
135        SkASSERT(chunkObj);
136        // paint is optional
137
138        SkRect      bounds;
139        GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds);
140        draw(env, canvas, bounds, bitmap, chunkObj, paint, destDensity, srcDensity);
141    }
142
143    static jint getTransparentRegion(JNIEnv* env, jobject,
144                    const SkBitmap* bitmap, jbyteArray chunkObj,
145                    jobject boundsRect)
146    {
147        SkASSERT(bitmap);
148        SkASSERT(chunkObj);
149        SkASSERT(boundsRect);
150
151        SkRect      bounds;
152        GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds);
153        size_t chunkSize = env->GetArrayLength(chunkObj);
154        void* storage = alloca(chunkSize);
155        env->GetByteArrayRegion(chunkObj, 0, chunkSize,
156                                reinterpret_cast<jbyte*>(storage));
157        if (!env->ExceptionCheck()) {
158            // need to deserialize the chunk
159            Res_png_9patch* chunk = static_cast<Res_png_9patch*>(storage);
160            assert(chunkSize == chunk->serializedSize());
161            // this relies on deserialization being done in place
162            Res_png_9patch::deserialize(chunk);
163            SkRegion* region = NULL;
164            NinePatch_Draw(NULL, bounds, *bitmap, *chunk, NULL, &region);
165            return (jint)region;
166        }
167        return 0;
168    }
169
170};
171
172/////////////////////////////////////////////////////////////////////////////////////////
173
174#include <android_runtime/AndroidRuntime.h>
175
176static JNINativeMethod gNinePatchMethods[] = {
177    { "isNinePatchChunk", "([B)Z",                      (void*)SkNinePatchGlue::isNinePatchChunk   },
178    { "validateNinePatchChunk", "(I[B)V",               (void*)SkNinePatchGlue::validateNinePatchChunk   },
179    { "nativeDraw", "(ILandroid/graphics/RectF;I[BIII)V", (void*)SkNinePatchGlue::drawF   },
180    { "nativeDraw", "(ILandroid/graphics/Rect;I[BIII)V",  (void*)SkNinePatchGlue::drawI   },
181    { "nativeGetTransparentRegion", "(I[BLandroid/graphics/Rect;)I",
182                                                        (void*)SkNinePatchGlue::getTransparentRegion   }
183};
184
185int register_android_graphics_NinePatch(JNIEnv* env)
186{
187    return android::AndroidRuntime::registerNativeMethods(env,
188                                                       "android/graphics/NinePatch",
189                                                       gNinePatchMethods,
190                                                       SK_ARRAY_COUNT(gNinePatchMethods));
191}
192