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 <utils/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 jniThrowException(env, "java/lang/RuntimeException", 62 "Array too small for chunk."); 63 return; 64 } 65 66 // XXX Also check that dimensions are correct. 67 } 68 69 static void draw(JNIEnv* env, SkCanvas* canvas, SkRect& bounds, 70 const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint, 71 jint destDensity, jint srcDensity) 72 { 73 size_t chunkSize = env->GetArrayLength(chunkObj); 74 void* storage = alloca(chunkSize); 75 env->GetByteArrayRegion(chunkObj, 0, chunkSize, 76 reinterpret_cast<jbyte*>(storage)); 77 if (!env->ExceptionCheck()) { 78 // need to deserialize the chunk 79 Res_png_9patch* chunk = static_cast<Res_png_9patch*>(storage); 80 assert(chunkSize == chunk->serializedSize()); 81 // this relies on deserialization being done in place 82 Res_png_9patch::deserialize(chunk); 83 84 if (destDensity == srcDensity || destDensity == 0 85 || srcDensity == 0) { 86 LOGV("Drawing unscaled 9-patch: (%g,%g)-(%g,%g)", 87 SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop), 88 SkScalarToFloat(bounds.fRight), SkScalarToFloat(bounds.fBottom)); 89 NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL); 90 } else { 91 canvas->save(); 92 93 SkScalar scale = SkFloatToScalar(destDensity / (float)srcDensity); 94 canvas->translate(bounds.fLeft, bounds.fTop); 95 canvas->scale(scale, scale); 96 97 bounds.fRight = SkScalarDiv(bounds.fRight-bounds.fLeft, scale); 98 bounds.fBottom = SkScalarDiv(bounds.fBottom-bounds.fTop, scale); 99 bounds.fLeft = bounds.fTop = 0; 100 101 LOGV("Drawing scaled 9-patch: (%g,%g)-(%g,%g) srcDensity=%d destDensity=%d", 102 SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop), 103 SkScalarToFloat(bounds.fRight), SkScalarToFloat(bounds.fBottom), 104 srcDensity, destDensity); 105 106 NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL); 107 108 canvas->restore(); 109 } 110 } 111 } 112 113 static void drawF(JNIEnv* env, jobject, SkCanvas* canvas, jobject boundsRectF, 114 const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint, 115 jint destDensity, jint srcDensity) 116 { 117 SkASSERT(canvas); 118 SkASSERT(boundsRectF); 119 SkASSERT(bitmap); 120 SkASSERT(chunkObj); 121 // paint is optional 122 123 SkRect bounds; 124 GraphicsJNI::jrectf_to_rect(env, boundsRectF, &bounds); 125 126 draw(env, canvas, bounds, bitmap, chunkObj, paint, destDensity, srcDensity); 127 } 128 129 static void drawI(JNIEnv* env, jobject, SkCanvas* canvas, jobject boundsRect, 130 const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint, 131 jint destDensity, jint srcDensity) 132 { 133 SkASSERT(canvas); 134 SkASSERT(boundsRect); 135 SkASSERT(bitmap); 136 SkASSERT(chunkObj); 137 // paint is optional 138 139 SkRect bounds; 140 GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds); 141 draw(env, canvas, bounds, bitmap, chunkObj, paint, destDensity, srcDensity); 142 } 143 144 static jint getTransparentRegion(JNIEnv* env, jobject, 145 const SkBitmap* bitmap, jbyteArray chunkObj, 146 jobject boundsRect) 147 { 148 SkASSERT(bitmap); 149 SkASSERT(chunkObj); 150 SkASSERT(boundsRect); 151 152 SkRect bounds; 153 GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds); 154 size_t chunkSize = env->GetArrayLength(chunkObj); 155 void* storage = alloca(chunkSize); 156 env->GetByteArrayRegion(chunkObj, 0, chunkSize, 157 reinterpret_cast<jbyte*>(storage)); 158 if (!env->ExceptionCheck()) { 159 // need to deserialize the chunk 160 Res_png_9patch* chunk = static_cast<Res_png_9patch*>(storage); 161 assert(chunkSize == chunk->serializedSize()); 162 // this relies on deserialization being done in place 163 Res_png_9patch::deserialize(chunk); 164 SkRegion* region = NULL; 165 NinePatch_Draw(NULL, bounds, *bitmap, *chunk, NULL, ®ion); 166 return (jint)region; 167 } 168 return 0; 169 } 170 171}; 172 173///////////////////////////////////////////////////////////////////////////////////////// 174 175#include <android_runtime/AndroidRuntime.h> 176 177static JNINativeMethod gNinePatchMethods[] = { 178 { "isNinePatchChunk", "([B)Z", (void*)SkNinePatchGlue::isNinePatchChunk }, 179 { "validateNinePatchChunk", "(I[B)V", (void*)SkNinePatchGlue::validateNinePatchChunk }, 180 { "nativeDraw", "(ILandroid/graphics/RectF;I[BIII)V", (void*)SkNinePatchGlue::drawF }, 181 { "nativeDraw", "(ILandroid/graphics/Rect;I[BIII)V", (void*)SkNinePatchGlue::drawI }, 182 { "nativeGetTransparentRegion", "(I[BLandroid/graphics/Rect;)I", 183 (void*)SkNinePatchGlue::getTransparentRegion } 184}; 185 186int register_android_graphics_NinePatch(JNIEnv* env); 187int register_android_graphics_NinePatch(JNIEnv* env) 188{ 189 return android::AndroidRuntime::registerNativeMethods(env, 190 "android/graphics/NinePatch", 191 gNinePatchMethods, 192 SK_ARRAY_COUNT(gNinePatchMethods)); 193} 194