NinePatch.cpp revision 11ea33471e1a14a8594f0b2cd012d86340dd3bd8
1#include <utils/ResourceTypes.h>
2
3#include "SkCanvas.h"
4#include "SkRegion.h"
5#include "GraphicsJNI.h"
6
7#include "JNIHelp.h"
8
9extern void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds,
10                const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
11                           const SkPaint* paint, SkRegion** outRegion);
12
13using namespace android;
14
15class SkNinePatchGlue {
16public:
17    static jboolean isNinePatchChunk(JNIEnv* env, jobject, jbyteArray obj)
18    {
19        if (NULL == obj) {
20            return false;
21        }
22        if (env->GetArrayLength(obj) < (int)sizeof(Res_png_9patch)) {
23            return false;
24        }
25        const jbyte* array = env->GetByteArrayElements(obj, 0);
26        if (array != NULL) {
27            const Res_png_9patch* chunk =
28                                reinterpret_cast<const Res_png_9patch*>(array);
29            int8_t wasDeserialized = chunk->wasDeserialized;
30            env->ReleaseByteArrayElements(obj, const_cast<jbyte*>(array),
31                                          JNI_ABORT);
32            return wasDeserialized != -1;
33        }
34        return false;
35    }
36
37    static void validateNinePatchChunk(JNIEnv* env, jobject, jint, jbyteArray obj)
38    {
39        if (env->GetArrayLength(obj) < (int) (sizeof(Res_png_9patch))) {
40            jniThrowException(env, "java/lang/RuntimeException",
41                              "Array too small for chunk.");
42            return;
43        }
44
45        // XXX Also check that dimensions are correct.
46    }
47
48    static void draw(JNIEnv* env, SkCanvas* canvas, SkRect& bounds,
49                      const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint,
50                      jint destDensity, jint srcDensity)
51    {
52        size_t chunkSize = env->GetArrayLength(chunkObj);
53        void* storage = alloca(chunkSize);
54        env->GetByteArrayRegion(chunkObj, 0, chunkSize,
55                                reinterpret_cast<jbyte*>(storage));
56        if (!env->ExceptionCheck()) {
57            // need to deserialize the chunk
58            Res_png_9patch* chunk = static_cast<Res_png_9patch*>(storage);
59            assert(chunkSize == chunk->serializedSize());
60            // this relies on deserialization being done in place
61            Res_png_9patch::deserialize(chunk);
62
63            if (destDensity == srcDensity || destDensity == 0
64                    || srcDensity == 0) {
65                NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL);
66            } else {
67                canvas->save();
68
69                SkScalar scale = SkFloatToScalar(destDensity / (float)srcDensity);
70                canvas->translate(bounds.fLeft, bounds.fTop);
71                canvas->scale(scale, scale);
72
73                bounds.fRight = SkScalarDiv(bounds.fRight-bounds.fLeft, scale);
74                bounds.fBottom = SkScalarDiv(bounds.fBottom-bounds.fTop, scale);
75                bounds.fLeft = bounds.fTop = 0;
76
77                NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL);
78
79                canvas->restore();
80            }
81        }
82    }
83
84    static void drawF(JNIEnv* env, jobject, SkCanvas* canvas, jobject boundsRectF,
85                      const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint,
86                      jint destDensity, jint srcDensity)
87    {
88        SkASSERT(canvas);
89        SkASSERT(boundsRectF);
90        SkASSERT(bitmap);
91        SkASSERT(chunkObj);
92        // paint is optional
93
94        SkRect      bounds;
95        GraphicsJNI::jrectf_to_rect(env, boundsRectF, &bounds);
96
97        draw(env, canvas, bounds, bitmap, chunkObj, paint, destDensity, srcDensity);
98    }
99
100    static void drawI(JNIEnv* env, jobject, SkCanvas* canvas, jobject boundsRect,
101                      const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint,
102                      jint destDensity, jint srcDensity)
103    {
104        SkASSERT(canvas);
105        SkASSERT(boundsRect);
106        SkASSERT(bitmap);
107        SkASSERT(chunkObj);
108        // paint is optional
109
110        SkRect      bounds;
111        GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds);
112        draw(env, canvas, bounds, bitmap, chunkObj, paint, destDensity, srcDensity);
113    }
114
115    static jint getTransparentRegion(JNIEnv* env, jobject,
116                    const SkBitmap* bitmap, jbyteArray chunkObj,
117                    jobject boundsRect)
118    {
119        SkASSERT(bitmap);
120        SkASSERT(chunkObj);
121        SkASSERT(boundsRect);
122
123        SkRect      bounds;
124        GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds);
125        size_t chunkSize = env->GetArrayLength(chunkObj);
126        void* storage = alloca(chunkSize);
127        env->GetByteArrayRegion(chunkObj, 0, chunkSize,
128                                reinterpret_cast<jbyte*>(storage));
129        if (!env->ExceptionCheck()) {
130            // need to deserialize the chunk
131            Res_png_9patch* chunk = static_cast<Res_png_9patch*>(storage);
132            assert(chunkSize == chunk->serializedSize());
133            // this relies on deserialization being done in place
134            Res_png_9patch::deserialize(chunk);
135            SkRegion* region = NULL;
136            NinePatch_Draw(NULL, bounds, *bitmap, *chunk, NULL, &region);
137            return (jint)region;
138        }
139        return 0;
140    }
141
142};
143
144/////////////////////////////////////////////////////////////////////////////////////////
145
146#include <android_runtime/AndroidRuntime.h>
147
148static JNINativeMethod gNinePatchMethods[] = {
149    { "isNinePatchChunk", "([B)Z",                      (void*)SkNinePatchGlue::isNinePatchChunk   },
150    { "validateNinePatchChunk", "(I[B)V",               (void*)SkNinePatchGlue::validateNinePatchChunk   },
151    { "nativeDraw", "(ILandroid/graphics/RectF;I[BIII)V", (void*)SkNinePatchGlue::drawF   },
152    { "nativeDraw", "(ILandroid/graphics/Rect;I[BIII)V",  (void*)SkNinePatchGlue::drawI   },
153    { "nativeGetTransparentRegion", "(I[BLandroid/graphics/Rect;)I",
154                                                        (void*)SkNinePatchGlue::getTransparentRegion   }
155};
156
157int register_android_graphics_NinePatch(JNIEnv* env);
158int register_android_graphics_NinePatch(JNIEnv* env)
159{
160    return android::AndroidRuntime::registerNativeMethods(env,
161                                                       "android/graphics/NinePatch",
162                                                       gNinePatchMethods,
163                                                       SK_ARRAY_COUNT(gNinePatchMethods));
164}
165