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