Home | History | Annotate | Download | only in graphics
      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 "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                 ALOGV("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                 ALOGV("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, &region);
    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