18cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn/*
28cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn**
38cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn** Copyright 2006, The Android Open Source Project
48cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn**
58cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn** Licensed under the Apache License, Version 2.0 (the "License");
68cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn** you may not use this file except in compliance with the License.
78cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn** You may obtain a copy of the License at
88cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn**
98cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn**     http://www.apache.org/licenses/LICENSE-2.0
108cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn**
118cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn** Unless required by applicable law or agreed to in writing, software
128cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn** distributed under the License is distributed on an "AS IS" BASIS,
138cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
148cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn** See the License for the specific language governing permissions and
158cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn** limitations under the License.
168cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn*/
178cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn
188cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn#define LOG_TAG "9patch"
198cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn#define LOG_NDEBUG 1
208cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn
21b13b9bdad2baf6ad1ec2e56b6b7598fa20f55fc4Mathias Agopian#include <androidfw/ResourceTypes.h>
228cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn#include <utils/Log.h>
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
24e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy#include <Caches.h>
25e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy
2611ea33471e1a14a8594f0b2cd012d86340dd3bd8Dianne Hackborn#include "SkCanvas.h"
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "SkRegion.h"
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "GraphicsJNI.h"
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "JNIHelp.h"
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
32e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guyextern void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds, const SkBitmap& bitmap,
33e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy        const android::Res_png_9patch& chunk, const SkPaint* paint, SkRegion** outRegion);
3469a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectusing namespace android;
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
37e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy/**
38e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy * IMPORTANT NOTE: 9patch chunks can be manipuated either as an array of bytes
39e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy * or as a Res_png_9patch instance. It is important to note that the size of the
40e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy * array required to hold a 9patch chunk is greater than sizeof(Res_png_9patch).
41e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy * The code below manipulates chunks as Res_png_9patch* types to draw and as
42e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy * int8_t* to allocate and free the backing storage.
43e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy */
44e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectclass SkNinePatchGlue {
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic:
47e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy    static jboolean isNinePatchChunk(JNIEnv* env, jobject, jbyteArray obj) {
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (NULL == obj) {
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (env->GetArrayLength(obj) < (int)sizeof(Res_png_9patch)) {
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        const jbyte* array = env->GetByteArrayElements(obj, 0);
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (array != NULL) {
56e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy            const Res_png_9patch* chunk = reinterpret_cast<const Res_png_9patch*>(array);
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int8_t wasDeserialized = chunk->wasDeserialized;
58e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy            env->ReleaseByteArrayElements(obj, const_cast<jbyte*>(array), JNI_ABORT);
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return wasDeserialized != -1;
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
64e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy    static int8_t* validateNinePatchChunk(JNIEnv* env, jobject, jint, jbyteArray obj) {
65e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy        size_t chunkSize = env->GetArrayLength(obj);
66e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy        if (chunkSize < (int) (sizeof(Res_png_9patch))) {
6769a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes            jniThrowRuntimeException(env, "Array too small for chunk.");
68e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy            return NULL;
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
71e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy        int8_t* storage = new int8_t[chunkSize];
72e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy        // This call copies the content of the jbyteArray
73e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy        env->GetByteArrayRegion(obj, 0, chunkSize, reinterpret_cast<jbyte*>(storage));
74e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy        // Deserialize in place, return the array we just allocated
75e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy        return (int8_t*) Res_png_9patch::deserialize(storage);
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
78e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy    static void finalize(JNIEnv* env, jobject, int8_t* patch) {
79e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy#ifdef USE_OPENGL_RENDERER
80e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy        if (android::uirenderer::Caches::hasInstance()) {
81e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy            Res_png_9patch* p = (Res_png_9patch*) patch;
82e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy            android::uirenderer::Caches::getInstance().resourceCache.destructor(p);
83e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy            return;
84e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy        }
85e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy#endif // USE_OPENGL_RENDERER
86e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy        delete[] patch;
87e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy    }
88e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy
89e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy    static void draw(JNIEnv* env, SkCanvas* canvas, SkRect& bounds, const SkBitmap* bitmap,
90e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy            Res_png_9patch* chunk, const SkPaint* paint, jint destDensity, jint srcDensity) {
91e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy        if (destDensity == srcDensity || destDensity == 0 || srcDensity == 0) {
92e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy            ALOGV("Drawing unscaled 9-patch: (%g,%g)-(%g,%g)",
93e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy                    SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop),
94e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy                    SkScalarToFloat(bounds.fRight), SkScalarToFloat(bounds.fBottom));
95e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy            NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL);
96e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy        } else {
97e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy            canvas->save();
98e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy
99e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy            SkScalar scale = SkFloatToScalar(destDensity / (float)srcDensity);
100e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy            canvas->translate(bounds.fLeft, bounds.fTop);
101e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy            canvas->scale(scale, scale);
102e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy
103e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy            bounds.fRight = SkScalarDiv(bounds.fRight-bounds.fLeft, scale);
104e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy            bounds.fBottom = SkScalarDiv(bounds.fBottom-bounds.fTop, scale);
105e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy            bounds.fLeft = bounds.fTop = 0;
106e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy
107e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy            ALOGV("Drawing scaled 9-patch: (%g,%g)-(%g,%g) srcDensity=%d destDensity=%d",
108e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy                    SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop),
109e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy                    SkScalarToFloat(bounds.fRight), SkScalarToFloat(bounds.fBottom),
110e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy                    srcDensity, destDensity);
111e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy
112e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy            NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL);
113e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy
114e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy            canvas->restore();
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11669a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes    }
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static void drawF(JNIEnv* env, jobject, SkCanvas* canvas, jobject boundsRectF,
119e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy            const SkBitmap* bitmap, Res_png_9patch* chunk, const SkPaint* paint,
120e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy            jint destDensity, jint srcDensity) {
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SkASSERT(canvas);
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SkASSERT(boundsRectF);
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SkASSERT(bitmap);
124e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy        SkASSERT(chunk);
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // paint is optional
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
127e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy        SkRect bounds;
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        GraphicsJNI::jrectf_to_rect(env, boundsRectF, &bounds);
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
130e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy        draw(env, canvas, bounds, bitmap, chunk, paint, destDensity, srcDensity);
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13269a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static void drawI(JNIEnv* env, jobject, SkCanvas* canvas, jobject boundsRect,
134e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy            const SkBitmap* bitmap, Res_png_9patch* chunk, const SkPaint* paint,
135e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy            jint destDensity, jint srcDensity) {
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SkASSERT(canvas);
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SkASSERT(boundsRect);
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SkASSERT(bitmap);
139e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy        SkASSERT(chunk);
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // paint is optional
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
142e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy        SkRect bounds;
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds);
144e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy        draw(env, canvas, bounds, bitmap, chunk, paint, destDensity, srcDensity);
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14669a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes
147e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy    static jint getTransparentRegion(JNIEnv* env, jobject, const SkBitmap* bitmap,
148e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy            Res_png_9patch* chunk, jobject boundsRect) {
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SkASSERT(bitmap);
150e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy        SkASSERT(chunk);
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SkASSERT(boundsRect);
15269a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes
153e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy        SkRect bounds;
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds);
155e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy
156e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy        SkRegion* region = NULL;
157e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy        NinePatch_Draw(NULL, bounds, *bitmap, *chunk, NULL, &region);
158e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy
159e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy        return (jint) region;
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project};
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/////////////////////////////////////////////////////////////////////////////////////////
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include <android_runtime/AndroidRuntime.h>
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectstatic JNINativeMethod gNinePatchMethods[] = {
169e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy    { "isNinePatchChunk", "([B)Z",                        (void*) SkNinePatchGlue::isNinePatchChunk },
170e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy    { "validateNinePatchChunk", "(I[B)I",                 (void*) SkNinePatchGlue::validateNinePatchChunk },
171e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy    { "nativeFinalize", "(I)V",                           (void*) SkNinePatchGlue::finalize },
172e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy    { "nativeDraw", "(ILandroid/graphics/RectF;IIIII)V",  (void*) SkNinePatchGlue::drawF },
173e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy    { "nativeDraw", "(ILandroid/graphics/Rect;IIIII)V",   (void*) SkNinePatchGlue::drawI },
174e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy    { "nativeGetTransparentRegion", "(IILandroid/graphics/Rect;)I",
175e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy                                                          (void*) SkNinePatchGlue::getTransparentRegion }
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project};
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
178e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guyint register_android_graphics_NinePatch(JNIEnv* env) {
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    return android::AndroidRuntime::registerNativeMethods(env,
180e3b0a0117a2ab4118f868a731b238fe8f2430276Romain Guy            "android/graphics/NinePatch", gNinePatchMethods, SK_ARRAY_COUNT(gNinePatchMethods));
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
182