Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "GraphicBuffer"
     18 
     19 #include "jni.h"
     20 #include "JNIHelp.h"
     21 
     22 #include "android_os_Parcel.h"
     23 #include "android_view_GraphicBuffer.h"
     24 
     25 #include <android_runtime/AndroidRuntime.h>
     26 
     27 #include <binder/Parcel.h>
     28 
     29 #include <ui/GraphicBuffer.h>
     30 #include <ui/PixelFormat.h>
     31 
     32 #include <gui/IGraphicBufferAlloc.h>
     33 #include <gui/ISurfaceComposer.h>
     34 
     35 #include <SkCanvas.h>
     36 #include <SkBitmap.h>
     37 
     38 #include <private/gui/ComposerService.h>
     39 
     40 namespace android {
     41 
     42 // ----------------------------------------------------------------------------
     43 // Defines
     44 // ----------------------------------------------------------------------------
     45 
     46 // Debug
     47 #define DEBUG_GRAPHIC_BUFFER 0
     48 
     49 // Debug
     50 #if DEBUG_GRAPHIC_BUFFER
     51     #define GB_LOGD(...) ALOGD(__VA_ARGS__)
     52     #define GB_LOGW(...) ALOGW(__VA_ARGS__)
     53 #else
     54     #define GB_LOGD(...)
     55     #define GB_LOGW(...)
     56 #endif
     57 
     58 #define LOCK_CANVAS_USAGE GraphicBuffer::USAGE_SW_READ_OFTEN | GraphicBuffer::USAGE_SW_WRITE_OFTEN
     59 
     60 // ----------------------------------------------------------------------------
     61 // JNI Helpers
     62 // ----------------------------------------------------------------------------
     63 
     64 static struct {
     65     jfieldID mNativeObject;
     66 } gGraphicBufferClassInfo;
     67 
     68 static struct {
     69     jmethodID set;
     70     jfieldID left;
     71     jfieldID top;
     72     jfieldID right;
     73     jfieldID bottom;
     74 } gRectClassInfo;
     75 
     76 static struct {
     77     jfieldID mFinalizer;
     78     jfieldID mNativeCanvas;
     79     jfieldID mSurfaceFormat;
     80 } gCanvasClassInfo;
     81 
     82 static struct {
     83     jfieldID mNativeCanvas;
     84 } gCanvasFinalizerClassInfo;
     85 
     86 #define GET_INT(object, field) \
     87     env->GetIntField(object, field)
     88 
     89 #define SET_INT(object, field, value) \
     90     env->SetIntField(object, field, value)
     91 
     92 #define INVOKEV(object, method, ...) \
     93     env->CallVoidMethod(object, method, __VA_ARGS__)
     94 
     95 // ----------------------------------------------------------------------------
     96 // Types
     97 // ----------------------------------------------------------------------------
     98 
     99 class GraphicBufferWrapper {
    100 public:
    101     GraphicBufferWrapper(const sp<GraphicBuffer>& buffer): buffer(buffer) {
    102     }
    103 
    104     sp<GraphicBuffer> buffer;
    105 };
    106 
    107 // ----------------------------------------------------------------------------
    108 // GraphicBuffer lifecycle
    109 // ----------------------------------------------------------------------------
    110 
    111 static GraphicBufferWrapper* android_view_GraphiceBuffer_create(JNIEnv* env, jobject clazz,
    112         jint width, jint height, jint format, jint usage) {
    113 
    114     sp<ISurfaceComposer> composer(ComposerService::getComposerService());
    115     sp<IGraphicBufferAlloc> alloc(composer->createGraphicBufferAlloc());
    116     if (alloc == NULL) {
    117         GB_LOGW("createGraphicBufferAlloc() failed in GraphicBuffer.create()");
    118         return NULL;
    119     }
    120 
    121     status_t error;
    122     sp<GraphicBuffer> buffer(alloc->createGraphicBuffer(width, height, format, usage, &error));
    123     if (buffer == NULL) {
    124         GB_LOGW("createGraphicBuffer() failed in GraphicBuffer.create()");
    125         return NULL;
    126     }
    127 
    128     return new GraphicBufferWrapper(buffer);
    129 }
    130 
    131 static void android_view_GraphiceBuffer_destroy(JNIEnv* env, jobject clazz,
    132         GraphicBufferWrapper* wrapper) {
    133     delete wrapper;
    134 }
    135 
    136 // ----------------------------------------------------------------------------
    137 // Canvas management
    138 // ----------------------------------------------------------------------------
    139 
    140 static inline void swapCanvasPtr(JNIEnv* env, jobject canvasObj, SkCanvas* newCanvas) {
    141     jobject canvasFinalizerObj = env->GetObjectField(canvasObj, gCanvasClassInfo.mFinalizer);
    142     SkCanvas* previousCanvas = reinterpret_cast<SkCanvas*>(
    143             GET_INT(canvasObj, gCanvasClassInfo.mNativeCanvas));
    144     SET_INT(canvasObj, gCanvasClassInfo.mNativeCanvas, (int) newCanvas);
    145     SET_INT(canvasFinalizerObj, gCanvasFinalizerClassInfo.mNativeCanvas, (int) newCanvas);
    146     SkSafeUnref(previousCanvas);
    147 }
    148 
    149 static inline SkBitmap::Config convertPixelFormat(int32_t format) {
    150     switch (format) {
    151         case PIXEL_FORMAT_RGBA_8888:
    152             return SkBitmap::kARGB_8888_Config;
    153         case PIXEL_FORMAT_RGBX_8888:
    154             return SkBitmap::kARGB_8888_Config;
    155         case PIXEL_FORMAT_RGB_565:
    156             return SkBitmap::kRGB_565_Config;
    157         default:
    158             return SkBitmap::kNo_Config;
    159     }
    160 }
    161 
    162 static jboolean android_view_GraphicBuffer_lockCanvas(JNIEnv* env, jobject,
    163         GraphicBufferWrapper* wrapper, jobject canvas, jobject dirtyRect) {
    164 
    165     if (!wrapper) {
    166         return false;
    167     }
    168 
    169     sp<GraphicBuffer> buffer(wrapper->buffer);
    170 
    171     Rect rect;
    172     if (dirtyRect) {
    173         rect.left = GET_INT(dirtyRect, gRectClassInfo.left);
    174         rect.top = GET_INT(dirtyRect, gRectClassInfo.top);
    175         rect.right = GET_INT(dirtyRect, gRectClassInfo.right);
    176         rect.bottom = GET_INT(dirtyRect, gRectClassInfo.bottom);
    177     } else {
    178         rect.set(Rect(buffer->getWidth(), buffer->getHeight()));
    179     }
    180 
    181     void* bits = NULL;
    182     status_t status = buffer->lock(LOCK_CANVAS_USAGE, rect, &bits);
    183 
    184     if (status) return false;
    185     if (!bits) {
    186         buffer->unlock();
    187         return false;
    188     }
    189 
    190     ssize_t bytesCount = buffer->getStride() * bytesPerPixel(buffer->getPixelFormat());
    191 
    192     SkBitmap bitmap;
    193     bitmap.setConfig(convertPixelFormat(buffer->getPixelFormat()),
    194             buffer->getWidth(), buffer->getHeight(), bytesCount);
    195 
    196     if (buffer->getWidth() > 0 && buffer->getHeight() > 0) {
    197         bitmap.setPixels(bits);
    198     } else {
    199         bitmap.setPixels(NULL);
    200     }
    201 
    202     SET_INT(canvas, gCanvasClassInfo.mSurfaceFormat, buffer->getPixelFormat());
    203 
    204     SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap));
    205     swapCanvasPtr(env, canvas, nativeCanvas);
    206 
    207     SkRect clipRect;
    208     clipRect.set(rect.left, rect.top, rect.right, rect.bottom);
    209     nativeCanvas->clipRect(clipRect);
    210 
    211     if (dirtyRect) {
    212         INVOKEV(dirtyRect, gRectClassInfo.set,
    213                 int(rect.left), int(rect.top), int(rect.right), int(rect.bottom));
    214     }
    215 
    216     return true;
    217 }
    218 
    219 static jboolean android_view_GraphicBuffer_unlockCanvasAndPost(JNIEnv* env, jobject,
    220         GraphicBufferWrapper* wrapper, jobject canvas) {
    221 
    222     SkCanvas* nativeCanvas = SkNEW(SkCanvas);
    223     swapCanvasPtr(env, canvas, nativeCanvas);
    224 
    225     if (wrapper) {
    226         status_t status = wrapper->buffer->unlock();
    227         return status == 0;
    228     }
    229 
    230     return false;
    231 }
    232 
    233 // ----------------------------------------------------------------------------
    234 // Serialization
    235 // ----------------------------------------------------------------------------
    236 
    237 static void android_view_GraphiceBuffer_write(JNIEnv* env, jobject clazz,
    238         GraphicBufferWrapper* wrapper, jobject dest) {
    239     Parcel* parcel = parcelForJavaObject(env, dest);
    240     if (parcel) {
    241         parcel->write(*wrapper->buffer);
    242     }
    243 }
    244 
    245 static GraphicBufferWrapper* android_view_GraphiceBuffer_read(JNIEnv* env, jobject clazz,
    246         jobject in) {
    247 
    248     Parcel* parcel = parcelForJavaObject(env, in);
    249     if (parcel) {
    250         sp<GraphicBuffer> buffer = new GraphicBuffer();
    251         parcel->read(*buffer);
    252         return new GraphicBufferWrapper(buffer);
    253     }
    254 
    255     return NULL;
    256 }
    257 
    258 // ----------------------------------------------------------------------------
    259 // External helpers
    260 // ----------------------------------------------------------------------------
    261 
    262 sp<GraphicBuffer> graphicBufferForJavaObject(JNIEnv* env, jobject obj) {
    263     if (obj) {
    264         jint nativeObject = env->GetIntField(obj, gGraphicBufferClassInfo.mNativeObject);
    265         GraphicBufferWrapper* wrapper = (GraphicBufferWrapper*) nativeObject;
    266         if (wrapper != NULL) {
    267             sp<GraphicBuffer> buffer(wrapper->buffer);
    268             return buffer;
    269         }
    270     }
    271     return NULL;
    272 }
    273 
    274 // ----------------------------------------------------------------------------
    275 // JNI Glue
    276 // ----------------------------------------------------------------------------
    277 
    278 #define FIND_CLASS(var, className) \
    279         var = env->FindClass(className); \
    280         LOG_FATAL_IF(! var, "Unable to find class " className);
    281 
    282 #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
    283         var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
    284         LOG_FATAL_IF(! var, "Unable to find field " fieldName);
    285 
    286 #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
    287         var = env->GetMethodID(clazz, methodName, methodDescriptor); \
    288         LOG_FATAL_IF(!var, "Unable to find method " methodName);
    289 
    290 const char* const kClassPathName = "android/view/GraphicBuffer";
    291 
    292 static JNINativeMethod gMethods[] = {
    293     { "nCreateGraphicBuffer",  "(IIII)I", (void*) android_view_GraphiceBuffer_create },
    294     { "nDestroyGraphicBuffer", "(I)V",    (void*) android_view_GraphiceBuffer_destroy },
    295 
    296     { "nWriteGraphicBufferToParcel",  "(ILandroid/os/Parcel;)V",
    297             (void*) android_view_GraphiceBuffer_write },
    298     { "nReadGraphicBufferFromParcel", "(Landroid/os/Parcel;)I",
    299             (void*) android_view_GraphiceBuffer_read },
    300 
    301     { "nLockCanvas", "(ILandroid/graphics/Canvas;Landroid/graphics/Rect;)Z",
    302             (void*) android_view_GraphicBuffer_lockCanvas },
    303     { "nUnlockCanvasAndPost", "(ILandroid/graphics/Canvas;)Z",
    304             (void*) android_view_GraphicBuffer_unlockCanvasAndPost },
    305 };
    306 
    307 int register_android_view_GraphicBuffer(JNIEnv* env) {
    308     jclass clazz;
    309     FIND_CLASS(clazz, "android/view/GraphicBuffer");
    310     GET_FIELD_ID(gGraphicBufferClassInfo.mNativeObject, clazz, "mNativeObject", "I");
    311 
    312     FIND_CLASS(clazz, "android/graphics/Rect");
    313     GET_METHOD_ID(gRectClassInfo.set, clazz, "set", "(IIII)V");
    314     GET_FIELD_ID(gRectClassInfo.left, clazz, "left", "I");
    315     GET_FIELD_ID(gRectClassInfo.top, clazz, "top", "I");
    316     GET_FIELD_ID(gRectClassInfo.right, clazz, "right", "I");
    317     GET_FIELD_ID(gRectClassInfo.bottom, clazz, "bottom", "I");
    318 
    319     FIND_CLASS(clazz, "android/graphics/Canvas");
    320     GET_FIELD_ID(gCanvasClassInfo.mFinalizer, clazz, "mFinalizer",
    321             "Landroid/graphics/Canvas$CanvasFinalizer;");
    322     GET_FIELD_ID(gCanvasClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "I");
    323     GET_FIELD_ID(gCanvasClassInfo.mSurfaceFormat, clazz, "mSurfaceFormat", "I");
    324 
    325     FIND_CLASS(clazz, "android/graphics/Canvas$CanvasFinalizer");
    326     GET_FIELD_ID(gCanvasFinalizerClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "I");
    327 
    328     return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
    329 }
    330 
    331 };
    332