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 <hwui/Canvas.h>
     23 #include <hwui/Paint.h>
     24 #include <utils/Log.h>
     25 
     26 #include "SkCanvas.h"
     27 #include "SkLatticeIter.h"
     28 #include "SkRegion.h"
     29 #include "GraphicsJNI.h"
     30 #include "NinePatchPeeker.h"
     31 #include "NinePatchUtils.h"
     32 
     33 #include <nativehelper/JNIHelp.h>
     34 #include "core_jni_helpers.h"
     35 
     36 jclass      gInsetStruct_class;
     37 jmethodID   gInsetStruct_constructorMethodID;
     38 
     39 using namespace android;
     40 
     41 /**
     42  * IMPORTANT NOTE: 9patch chunks can be manipuated either as an array of bytes
     43  * or as a Res_png_9patch instance. It is important to note that the size of the
     44  * array required to hold a 9patch chunk is greater than sizeof(Res_png_9patch).
     45  * The code below manipulates chunks as Res_png_9patch* types to draw and as
     46  * int8_t* to allocate and free the backing storage.
     47  */
     48 
     49 class SkNinePatchGlue {
     50 public:
     51     static jboolean isNinePatchChunk(JNIEnv* env, jobject, jbyteArray obj) {
     52         if (NULL == obj) {
     53             return JNI_FALSE;
     54         }
     55         if (env->GetArrayLength(obj) < (int)sizeof(Res_png_9patch)) {
     56             return JNI_FALSE;
     57         }
     58         const jbyte* array = env->GetByteArrayElements(obj, 0);
     59         if (array != NULL) {
     60             const Res_png_9patch* chunk = reinterpret_cast<const Res_png_9patch*>(array);
     61             int8_t wasDeserialized = chunk->wasDeserialized;
     62             env->ReleaseByteArrayElements(obj, const_cast<jbyte*>(array), JNI_ABORT);
     63             return (wasDeserialized != -1) ? JNI_TRUE : JNI_FALSE;
     64         }
     65         return JNI_FALSE;
     66     }
     67 
     68     static jlong validateNinePatchChunk(JNIEnv* env, jobject, jbyteArray obj) {
     69         size_t chunkSize = env->GetArrayLength(obj);
     70         if (chunkSize < (int) (sizeof(Res_png_9patch))) {
     71             jniThrowRuntimeException(env, "Array too small for chunk.");
     72             return NULL;
     73         }
     74 
     75         int8_t* storage = new int8_t[chunkSize];
     76         // This call copies the content of the jbyteArray
     77         env->GetByteArrayRegion(obj, 0, chunkSize, reinterpret_cast<jbyte*>(storage));
     78         // Deserialize in place, return the array we just allocated
     79         return reinterpret_cast<jlong>(Res_png_9patch::deserialize(storage));
     80     }
     81 
     82     static void finalize(JNIEnv* env, jobject, jlong patchHandle) {
     83         int8_t* patch = reinterpret_cast<int8_t*>(patchHandle);
     84         delete[] patch;
     85     }
     86 
     87     static jlong getTransparentRegion(JNIEnv* env, jobject, jlong bitmapPtr,
     88             jlong chunkHandle, jobject dstRect) {
     89         Res_png_9patch* chunk = reinterpret_cast<Res_png_9patch*>(chunkHandle);
     90         SkASSERT(chunk);
     91 
     92         SkBitmap bitmap;
     93         bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
     94         SkRect dst;
     95         GraphicsJNI::jrect_to_rect(env, dstRect, &dst);
     96 
     97         SkCanvas::Lattice lattice;
     98         SkIRect src = SkIRect::MakeWH(bitmap.width(), bitmap.height());
     99         lattice.fBounds = &src;
    100         NinePatchUtils::SetLatticeDivs(&lattice, *chunk, bitmap.width(), bitmap.height());
    101         lattice.fRectTypes = nullptr;
    102         lattice.fColors = nullptr;
    103 
    104         SkRegion* region = nullptr;
    105         if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), lattice)) {
    106             SkLatticeIter iter(lattice, dst);
    107             if (iter.numRectsToDraw() == chunk->numColors) {
    108                 SkRect dummy;
    109                 SkRect iterDst;
    110                 int index = 0;
    111                 while (iter.next(&dummy, &iterDst)) {
    112                     if (0 == chunk->getColors()[index++] && !iterDst.isEmpty()) {
    113                         if (!region) {
    114                             region = new SkRegion();
    115                         }
    116 
    117                         region->op(iterDst.round(), SkRegion::kUnion_Op);
    118                     }
    119                 }
    120             }
    121         }
    122 
    123         return reinterpret_cast<jlong>(region);
    124     }
    125 
    126 };
    127 
    128 jobject NinePatchPeeker::createNinePatchInsets(JNIEnv* env, float scale) const {
    129     if (!mHasInsets) {
    130         return nullptr;
    131     }
    132 
    133     return env->NewObject(gInsetStruct_class, gInsetStruct_constructorMethodID,
    134             mOpticalInsets[0], mOpticalInsets[1],
    135             mOpticalInsets[2], mOpticalInsets[3],
    136             mOutlineInsets[0], mOutlineInsets[1],
    137             mOutlineInsets[2], mOutlineInsets[3],
    138             mOutlineRadius, mOutlineAlpha, scale);
    139 }
    140 
    141 void NinePatchPeeker::getPadding(JNIEnv* env, jobject outPadding) const {
    142     if (mPatch) {
    143         GraphicsJNI::set_jrect(env, outPadding,
    144                 mPatch->paddingLeft, mPatch->paddingTop,
    145                 mPatch->paddingRight, mPatch->paddingBottom);
    146 
    147     } else {
    148         GraphicsJNI::set_jrect(env, outPadding, -1, -1, -1, -1);
    149     }
    150 }
    151 
    152 /////////////////////////////////////////////////////////////////////////////////////////
    153 
    154 static const JNINativeMethod gNinePatchMethods[] = {
    155     { "isNinePatchChunk", "([B)Z", (void*) SkNinePatchGlue::isNinePatchChunk },
    156     { "validateNinePatchChunk", "([B)J",
    157             (void*) SkNinePatchGlue::validateNinePatchChunk },
    158     { "nativeFinalize", "(J)V", (void*) SkNinePatchGlue::finalize },
    159     { "nativeGetTransparentRegion", "(JJLandroid/graphics/Rect;)J",
    160             (void*) SkNinePatchGlue::getTransparentRegion }
    161 };
    162 
    163 int register_android_graphics_NinePatch(JNIEnv* env) {
    164     gInsetStruct_class = MakeGlobalRefOrDie(env, FindClassOrDie(env,
    165             "android/graphics/NinePatch$InsetStruct"));
    166     gInsetStruct_constructorMethodID = GetMethodIDOrDie(env, gInsetStruct_class, "<init>",
    167             "(IIIIIIIIFIF)V");
    168     return android::RegisterMethodsOrDie(env,
    169             "android/graphics/NinePatch", gNinePatchMethods, NELEM(gNinePatchMethods));
    170 }
    171