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 <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             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, &region);
    166             return (jint)region;
    167         }
    168         return 0;
    169     }
    170 
    171 };
    172 
    173 /////////////////////////////////////////////////////////////////////////////////////////
    174 
    175 #include <android_runtime/AndroidRuntime.h>
    176 
    177 static 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 
    186 int register_android_graphics_NinePatch(JNIEnv* env);
    187 int 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