NinePatch.cpp revision e3b0a0117a2ab4118f868a731b238fe8f2430276
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 false; 50 } 51 if (env->GetArrayLength(obj) < (int)sizeof(Res_png_9patch)) { 52 return 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; 60 } 61 return false; 62 } 63 64 static int8_t* validateNinePatchChunk(JNIEnv* env, jobject, jint, 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 (int8_t*) Res_png_9patch::deserialize(storage); 76 } 77 78 static void finalize(JNIEnv* env, jobject, int8_t* patch) { 79#ifdef USE_OPENGL_RENDERER 80 if (android::uirenderer::Caches::hasInstance()) { 81 Res_png_9patch* p = (Res_png_9patch*) patch; 82 android::uirenderer::Caches::getInstance().resourceCache.destructor(p); 83 return; 84 } 85#endif // USE_OPENGL_RENDERER 86 delete[] patch; 87 } 88 89 static void draw(JNIEnv* env, SkCanvas* canvas, SkRect& bounds, const SkBitmap* bitmap, 90 Res_png_9patch* chunk, const SkPaint* paint, jint destDensity, jint srcDensity) { 91 if (destDensity == srcDensity || destDensity == 0 || srcDensity == 0) { 92 ALOGV("Drawing unscaled 9-patch: (%g,%g)-(%g,%g)", 93 SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop), 94 SkScalarToFloat(bounds.fRight), SkScalarToFloat(bounds.fBottom)); 95 NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL); 96 } else { 97 canvas->save(); 98 99 SkScalar scale = SkFloatToScalar(destDensity / (float)srcDensity); 100 canvas->translate(bounds.fLeft, bounds.fTop); 101 canvas->scale(scale, scale); 102 103 bounds.fRight = SkScalarDiv(bounds.fRight-bounds.fLeft, scale); 104 bounds.fBottom = SkScalarDiv(bounds.fBottom-bounds.fTop, scale); 105 bounds.fLeft = bounds.fTop = 0; 106 107 ALOGV("Drawing scaled 9-patch: (%g,%g)-(%g,%g) srcDensity=%d destDensity=%d", 108 SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop), 109 SkScalarToFloat(bounds.fRight), SkScalarToFloat(bounds.fBottom), 110 srcDensity, destDensity); 111 112 NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL); 113 114 canvas->restore(); 115 } 116 } 117 118 static void drawF(JNIEnv* env, jobject, SkCanvas* canvas, jobject boundsRectF, 119 const SkBitmap* bitmap, Res_png_9patch* chunk, const SkPaint* paint, 120 jint destDensity, jint srcDensity) { 121 SkASSERT(canvas); 122 SkASSERT(boundsRectF); 123 SkASSERT(bitmap); 124 SkASSERT(chunk); 125 // paint is optional 126 127 SkRect bounds; 128 GraphicsJNI::jrectf_to_rect(env, boundsRectF, &bounds); 129 130 draw(env, canvas, bounds, bitmap, chunk, paint, destDensity, srcDensity); 131 } 132 133 static void drawI(JNIEnv* env, jobject, SkCanvas* canvas, jobject boundsRect, 134 const SkBitmap* bitmap, Res_png_9patch* chunk, const SkPaint* paint, 135 jint destDensity, jint srcDensity) { 136 SkASSERT(canvas); 137 SkASSERT(boundsRect); 138 SkASSERT(bitmap); 139 SkASSERT(chunk); 140 // paint is optional 141 142 SkRect bounds; 143 GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds); 144 draw(env, canvas, bounds, bitmap, chunk, paint, destDensity, srcDensity); 145 } 146 147 static jint getTransparentRegion(JNIEnv* env, jobject, const SkBitmap* bitmap, 148 Res_png_9patch* chunk, jobject boundsRect) { 149 SkASSERT(bitmap); 150 SkASSERT(chunk); 151 SkASSERT(boundsRect); 152 153 SkRect bounds; 154 GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds); 155 156 SkRegion* region = NULL; 157 NinePatch_Draw(NULL, bounds, *bitmap, *chunk, NULL, ®ion); 158 159 return (jint) region; 160 } 161 162}; 163 164///////////////////////////////////////////////////////////////////////////////////////// 165 166#include <android_runtime/AndroidRuntime.h> 167 168static JNINativeMethod gNinePatchMethods[] = { 169 { "isNinePatchChunk", "([B)Z", (void*) SkNinePatchGlue::isNinePatchChunk }, 170 { "validateNinePatchChunk", "(I[B)I", (void*) SkNinePatchGlue::validateNinePatchChunk }, 171 { "nativeFinalize", "(I)V", (void*) SkNinePatchGlue::finalize }, 172 { "nativeDraw", "(ILandroid/graphics/RectF;IIIII)V", (void*) SkNinePatchGlue::drawF }, 173 { "nativeDraw", "(ILandroid/graphics/Rect;IIIII)V", (void*) SkNinePatchGlue::drawI }, 174 { "nativeGetTransparentRegion", "(IILandroid/graphics/Rect;)I", 175 (void*) SkNinePatchGlue::getTransparentRegion } 176}; 177 178int register_android_graphics_NinePatch(JNIEnv* env) { 179 return android::AndroidRuntime::registerNativeMethods(env, 180 "android/graphics/NinePatch", gNinePatchMethods, SK_ARRAY_COUNT(gNinePatchMethods)); 181} 182