NinePatch.cpp revision 5c3d927e17e98e8fd4a9f3c86f7f4def0bcfa816
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 <Caches.h> 25 26#include "SkCanvas.h" 27#include "SkRegion.h" 28#include "GraphicsJNI.h" 29 30#include "JNIHelp.h" 31 32extern void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds, const SkBitmap& bitmap, 33 const android::Res_png_9patch& chunk, const SkPaint* paint, SkRegion** outRegion); 34 35using namespace android; 36 37/** 38 * IMPORTANT NOTE: 9patch chunks can be manipuated either as an array of bytes 39 * or as a Res_png_9patch instance. It is important to note that the size of the 40 * array required to hold a 9patch chunk is greater than sizeof(Res_png_9patch). 41 * The code below manipulates chunks as Res_png_9patch* types to draw and as 42 * int8_t* to allocate and free the backing storage. 43 */ 44 45class SkNinePatchGlue { 46public: 47 static jboolean isNinePatchChunk(JNIEnv* env, jobject, jbyteArray obj) { 48 if (NULL == obj) { 49 return JNI_FALSE; 50 } 51 if (env->GetArrayLength(obj) < (int)sizeof(Res_png_9patch)) { 52 return JNI_FALSE; 53 } 54 const jbyte* array = env->GetByteArrayElements(obj, 0); 55 if (array != NULL) { 56 const Res_png_9patch* chunk = reinterpret_cast<const Res_png_9patch*>(array); 57 int8_t wasDeserialized = chunk->wasDeserialized; 58 env->ReleaseByteArrayElements(obj, const_cast<jbyte*>(array), JNI_ABORT); 59 return (wasDeserialized != -1) ? JNI_TRUE : JNI_FALSE; 60 } 61 return JNI_FALSE; 62 } 63 64 static jlong validateNinePatchChunk(JNIEnv* env, jobject, jlong, jbyteArray obj) { 65 size_t chunkSize = env->GetArrayLength(obj); 66 if (chunkSize < (int) (sizeof(Res_png_9patch))) { 67 jniThrowRuntimeException(env, "Array too small for chunk."); 68 return NULL; 69 } 70 71 int8_t* storage = new int8_t[chunkSize]; 72 // This call copies the content of the jbyteArray 73 env->GetByteArrayRegion(obj, 0, chunkSize, reinterpret_cast<jbyte*>(storage)); 74 // Deserialize in place, return the array we just allocated 75 return reinterpret_cast<jlong>(Res_png_9patch::deserialize(storage)); 76 } 77 78 static void finalize(JNIEnv* env, jobject, jlong patchHandle) { 79 int8_t* patch = reinterpret_cast<int8_t*>(patchHandle); 80#ifdef USE_OPENGL_RENDERER 81 if (android::uirenderer::Caches::hasInstance()) { 82 Res_png_9patch* p = (Res_png_9patch*) patch; 83 android::uirenderer::Caches::getInstance().resourceCache.destructor(p); 84 return; 85 } 86#endif // USE_OPENGL_RENDERER 87 delete[] patch; 88 } 89 90 static void draw(JNIEnv* env, SkCanvas* canvas, SkRect& bounds, const SkBitmap* bitmap, 91 Res_png_9patch* chunk, const SkPaint* paint, jint destDensity, jint srcDensity) { 92 if (destDensity == srcDensity || destDensity == 0 || srcDensity == 0) { 93 ALOGV("Drawing unscaled 9-patch: (%g,%g)-(%g,%g)", 94 SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop), 95 SkScalarToFloat(bounds.fRight), SkScalarToFloat(bounds.fBottom)); 96 NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL); 97 } else { 98 canvas->save(); 99 100 SkScalar scale = destDensity / (float)srcDensity; 101 canvas->translate(bounds.fLeft, bounds.fTop); 102 canvas->scale(scale, scale); 103 104 bounds.fRight = SkScalarDiv(bounds.fRight-bounds.fLeft, scale); 105 bounds.fBottom = SkScalarDiv(bounds.fBottom-bounds.fTop, scale); 106 bounds.fLeft = bounds.fTop = 0; 107 108 ALOGV("Drawing scaled 9-patch: (%g,%g)-(%g,%g) srcDensity=%d destDensity=%d", 109 SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop), 110 SkScalarToFloat(bounds.fRight), SkScalarToFloat(bounds.fBottom), 111 srcDensity, destDensity); 112 113 NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL); 114 115 canvas->restore(); 116 } 117 } 118 119 static void drawF(JNIEnv* env, jobject, jlong canvasHandle, jobject boundsRectF, 120 jlong bitmapHandle, jlong chunkHandle, jlong paintHandle, 121 jint destDensity, jint srcDensity) { 122 SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle); 123 const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); 124 Res_png_9patch* chunk = reinterpret_cast<Res_png_9patch*>(chunkHandle); 125 const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); 126 SkASSERT(canvas); 127 SkASSERT(boundsRectF); 128 SkASSERT(bitmap); 129 SkASSERT(chunk); 130 // paint is optional 131 132 SkRect bounds; 133 GraphicsJNI::jrectf_to_rect(env, boundsRectF, &bounds); 134 135 draw(env, canvas, bounds, bitmap, chunk, paint, destDensity, srcDensity); 136 } 137 138 static void drawI(JNIEnv* env, jobject, jlong canvasHandle, jobject boundsRect, 139 jlong bitmapHandle, jlong chunkHandle, jlong paintHandle, 140 jint destDensity, jint srcDensity) { 141 SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle); 142 const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); 143 Res_png_9patch* chunk = reinterpret_cast<Res_png_9patch*>(chunkHandle); 144 const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); 145 SkASSERT(canvas); 146 SkASSERT(boundsRect); 147 SkASSERT(bitmap); 148 SkASSERT(chunk); 149 // paint is optional 150 151 SkRect bounds; 152 GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds); 153 draw(env, canvas, bounds, bitmap, chunk, paint, destDensity, srcDensity); 154 } 155 156 static jlong getTransparentRegion(JNIEnv* env, jobject, jlong bitmapHandle, 157 jlong chunkHandle, jobject boundsRect) { 158 const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); 159 Res_png_9patch* chunk = reinterpret_cast<Res_png_9patch*>(chunkHandle); 160 SkASSERT(bitmap); 161 SkASSERT(chunk); 162 SkASSERT(boundsRect); 163 164 SkRect bounds; 165 GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds); 166 167 SkRegion* region = NULL; 168 NinePatch_Draw(NULL, bounds, *bitmap, *chunk, NULL, ®ion); 169 170 return reinterpret_cast<jlong>(region); 171 } 172 173}; 174 175///////////////////////////////////////////////////////////////////////////////////////// 176 177#include <android_runtime/AndroidRuntime.h> 178 179static JNINativeMethod gNinePatchMethods[] = { 180 { "isNinePatchChunk", "([B)Z", (void*) SkNinePatchGlue::isNinePatchChunk }, 181 { "validateNinePatchChunk", "(J[B)J", (void*) SkNinePatchGlue::validateNinePatchChunk }, 182 { "nativeFinalize", "(J)V", (void*) SkNinePatchGlue::finalize }, 183 { "nativeDraw", "(JLandroid/graphics/RectF;JJJII)V", (void*) SkNinePatchGlue::drawF }, 184 { "nativeDraw", "(JLandroid/graphics/Rect;JJJII)V", (void*) SkNinePatchGlue::drawI }, 185 { "nativeGetTransparentRegion", "(JJLandroid/graphics/Rect;)J", 186 (void*) SkNinePatchGlue::getTransparentRegion } 187}; 188 189int register_android_graphics_NinePatch(JNIEnv* env) { 190 return android::AndroidRuntime::registerNativeMethods(env, 191 "android/graphics/NinePatch", gNinePatchMethods, SK_ARRAY_COUNT(gNinePatchMethods)); 192} 193