blob: 684b1c1d357973002d521658003a77e11eda4eff [file] [log] [blame]
Dianne Hackborn8cae1242009-09-10 14:32:16 -07001/*
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
Mathias Agopianb13b9bd2012-02-17 18:27:36 -080021#include <androidfw/ResourceTypes.h>
Dianne Hackborn8cae1242009-09-10 14:32:16 -070022#include <utils/Log.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023
Dianne Hackborn11ea3342009-07-22 21:48:55 -070024#include "SkCanvas.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025#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);
Elliott Hughes69a017b2011-04-08 14:10:28 -070033
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034using 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))) {
Elliott Hughes69a017b2011-04-08 14:10:28 -070061 jniThrowRuntimeException(env, "Array too small for chunk.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062 return;
63 }
64
65 // XXX Also check that dimensions are correct.
66 }
67
68 static void draw(JNIEnv* env, SkCanvas* canvas, SkRect& bounds,
Dianne Hackborn11ea3342009-07-22 21:48:55 -070069 const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint,
70 jint destDensity, jint srcDensity)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071 {
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
Dianne Hackborn11ea3342009-07-22 21:48:55 -070081 Res_png_9patch::deserialize(chunk);
Elliott Hughes69a017b2011-04-08 14:10:28 -070082
Dianne Hackborn11ea3342009-07-22 21:48:55 -070083 if (destDensity == srcDensity || destDensity == 0
84 || srcDensity == 0) {
Steve Block71f2cf12011-10-20 11:56:00 +010085 ALOGV("Drawing unscaled 9-patch: (%g,%g)-(%g,%g)",
Dianne Hackborn8cae1242009-09-10 14:32:16 -070086 SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop),
87 SkScalarToFloat(bounds.fRight), SkScalarToFloat(bounds.fBottom));
Dianne Hackborn11ea3342009-07-22 21:48:55 -070088 NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL);
89 } else {
90 canvas->save();
Elliott Hughes69a017b2011-04-08 14:10:28 -070091
Dianne Hackborn11ea3342009-07-22 21:48:55 -070092 SkScalar scale = SkFloatToScalar(destDensity / (float)srcDensity);
93 canvas->translate(bounds.fLeft, bounds.fTop);
94 canvas->scale(scale, scale);
Elliott Hughes69a017b2011-04-08 14:10:28 -070095
Dianne Hackborn11ea3342009-07-22 21:48:55 -070096 bounds.fRight = SkScalarDiv(bounds.fRight-bounds.fLeft, scale);
97 bounds.fBottom = SkScalarDiv(bounds.fBottom-bounds.fTop, scale);
98 bounds.fLeft = bounds.fTop = 0;
Elliott Hughes69a017b2011-04-08 14:10:28 -070099
Steve Block71f2cf12011-10-20 11:56:00 +0100100 ALOGV("Drawing scaled 9-patch: (%g,%g)-(%g,%g) srcDensity=%d destDensity=%d",
Dianne Hackborn8cae1242009-09-10 14:32:16 -0700101 SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop),
102 SkScalarToFloat(bounds.fRight), SkScalarToFloat(bounds.fBottom),
103 srcDensity, destDensity);
Elliott Hughes69a017b2011-04-08 14:10:28 -0700104
Dianne Hackborn11ea3342009-07-22 21:48:55 -0700105 NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL);
Elliott Hughes69a017b2011-04-08 14:10:28 -0700106
Dianne Hackborn11ea3342009-07-22 21:48:55 -0700107 canvas->restore();
108 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 }
Elliott Hughes69a017b2011-04-08 14:10:28 -0700110 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111
112 static void drawF(JNIEnv* env, jobject, SkCanvas* canvas, jobject boundsRectF,
Dianne Hackborn11ea3342009-07-22 21:48:55 -0700113 const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint,
114 jint destDensity, jint srcDensity)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 {
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
Dianne Hackborn11ea3342009-07-22 21:48:55 -0700125 draw(env, canvas, bounds, bitmap, chunkObj, paint, destDensity, srcDensity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 }
Elliott Hughes69a017b2011-04-08 14:10:28 -0700127
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128 static void drawI(JNIEnv* env, jobject, SkCanvas* canvas, jobject boundsRect,
Dianne Hackborn11ea3342009-07-22 21:48:55 -0700129 const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint,
130 jint destDensity, jint srcDensity)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131 {
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);
Dianne Hackborn11ea3342009-07-22 21:48:55 -0700140 draw(env, canvas, bounds, bitmap, chunkObj, paint, destDensity, srcDensity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 }
Elliott Hughes69a017b2011-04-08 14:10:28 -0700142
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 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);
Elliott Hughes69a017b2011-04-08 14:10:28 -0700150
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 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, &region);
165 return (jint)region;
166 }
167 return 0;
168 }
169
170};
171
172/////////////////////////////////////////////////////////////////////////////////////////
173
174#include <android_runtime/AndroidRuntime.h>
175
176static JNINativeMethod gNinePatchMethods[] = {
177 { "isNinePatchChunk", "([B)Z", (void*)SkNinePatchGlue::isNinePatchChunk },
178 { "validateNinePatchChunk", "(I[B)V", (void*)SkNinePatchGlue::validateNinePatchChunk },
Dianne Hackborn11ea3342009-07-22 21:48:55 -0700179 { "nativeDraw", "(ILandroid/graphics/RectF;I[BIII)V", (void*)SkNinePatchGlue::drawF },
180 { "nativeDraw", "(ILandroid/graphics/Rect;I[BIII)V", (void*)SkNinePatchGlue::drawI },
Elliott Hughes69a017b2011-04-08 14:10:28 -0700181 { "nativeGetTransparentRegion", "(I[BLandroid/graphics/Rect;)I",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800182 (void*)SkNinePatchGlue::getTransparentRegion }
183};
184
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185int 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}