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 "GraphicBuffer.h"
24#include "GraphicsJNI.h"
25
26#include <android_runtime/AndroidRuntime.h>
27
28#include <binder/Parcel.h>
29
30#include <ui/GraphicBuffer.h>
31#include <ui/PixelFormat.h>
32
33#include <hwui/Bitmap.h>
34
35#include <SkCanvas.h>
36#include <SkBitmap.h>
37
38#include <private/gui/ComposerService.h>
39
40#include "core_jni_helpers.h"
41
42namespace android {
43
44// ----------------------------------------------------------------------------
45// Defines
46// ----------------------------------------------------------------------------
47
48// Debug
49static const bool kDebugGraphicBuffer = false;
50
51#define LOCK_CANVAS_USAGE (GraphicBuffer::USAGE_SW_READ_OFTEN | GraphicBuffer::USAGE_SW_WRITE_OFTEN)
52
53// ----------------------------------------------------------------------------
54// JNI Helpers
55// ----------------------------------------------------------------------------
56
57static struct {
58    jfieldID mNativeObject;
59    jclass mClass;
60    jmethodID mConstructorMethodID;
61} gGraphicBufferClassInfo;
62
63static struct {
64    jmethodID set;
65    jfieldID left;
66    jfieldID top;
67    jfieldID right;
68    jfieldID bottom;
69} gRectClassInfo;
70
71#define GET_INT(object, field) \
72    env->GetIntField(object, field)
73
74#define SET_INT(object, field, value) \
75    env->SetIntField(object, field, value)
76
77#define GET_LONG(object, field) \
78    env->GetLongField(object, field)
79
80#define SET_LONG(object, field, value) \
81    env->SetLongField(object, field, value)
82
83#define INVOKEV(object, method, ...) \
84    env->CallVoidMethod(object, method, __VA_ARGS__)
85
86// ----------------------------------------------------------------------------
87// Types
88// ----------------------------------------------------------------------------
89
90class GraphicBufferWrapper {
91public:
92    explicit GraphicBufferWrapper(const sp<GraphicBuffer>& buffer): buffer(buffer) {
93    }
94
95    sp<GraphicBuffer> buffer;
96};
97
98// ----------------------------------------------------------------------------
99// GraphicBuffer lifecycle
100// ----------------------------------------------------------------------------
101
102static jlong android_graphics_GraphicBuffer_wrap(JNIEnv* env, jobject clazz,
103        jlong unwrapped) {
104    sp<GraphicBuffer> b(reinterpret_cast<GraphicBuffer*>(unwrapped));
105    GraphicBufferWrapper* wrapper = new GraphicBufferWrapper(b);
106    return reinterpret_cast<jlong>(wrapper);
107}
108
109static jlong android_graphics_GraphicBuffer_create(JNIEnv* env, jobject clazz,
110        jint width, jint height, jint format, jint usage) {
111
112    sp<GraphicBuffer> buffer = new GraphicBuffer(
113            uint32_t(width), uint32_t(height), PixelFormat(format), uint32_t(usage),
114            std::string("android_graphics_GraphicBuffer_create pid [") +
115                    std::to_string(getpid()) +"]");
116
117    status_t error = buffer->initCheck();
118    if (error < 0) {
119        ALOGW_IF(kDebugGraphicBuffer, "createGraphicBuffer() failed in GraphicBuffer.create()");
120        return NULL;
121    }
122
123    GraphicBufferWrapper* wrapper = new GraphicBufferWrapper(buffer);
124    return reinterpret_cast<jlong>(wrapper);
125}
126
127static void android_graphics_GraphicBuffer_destroy(JNIEnv* env, jobject clazz,
128        jlong wrapperHandle) {
129    GraphicBufferWrapper* wrapper =
130                reinterpret_cast<GraphicBufferWrapper*>(wrapperHandle);
131    delete wrapper;
132}
133
134// ----------------------------------------------------------------------------
135// Canvas management
136// ----------------------------------------------------------------------------
137
138static inline SkColorType convertPixelFormat(int32_t format) {
139    switch (format) {
140        case PIXEL_FORMAT_RGBA_8888:
141            return kN32_SkColorType;
142        case PIXEL_FORMAT_RGBX_8888:
143            return kN32_SkColorType;
144        case PIXEL_FORMAT_RGBA_FP16:
145            return kRGBA_F16_SkColorType;
146        case PIXEL_FORMAT_RGB_565:
147            return kRGB_565_SkColorType;
148        default:
149            return kUnknown_SkColorType;
150    }
151}
152
153static jboolean android_graphics_GraphicBuffer_lockCanvas(JNIEnv* env, jobject,
154        jlong wrapperHandle, jobject canvas, jobject dirtyRect) {
155
156    GraphicBufferWrapper* wrapper =
157                reinterpret_cast<GraphicBufferWrapper*>(wrapperHandle);
158    if (!wrapper) {
159        return JNI_FALSE;
160    }
161
162    sp<GraphicBuffer> buffer(wrapper->buffer);
163
164    Rect rect(Rect::EMPTY_RECT);
165    if (dirtyRect) {
166        rect.left = GET_INT(dirtyRect, gRectClassInfo.left);
167        rect.top = GET_INT(dirtyRect, gRectClassInfo.top);
168        rect.right = GET_INT(dirtyRect, gRectClassInfo.right);
169        rect.bottom = GET_INT(dirtyRect, gRectClassInfo.bottom);
170    } else {
171        rect.set(Rect(buffer->getWidth(), buffer->getHeight()));
172    }
173
174    void* bits = NULL;
175    status_t status = buffer->lock(LOCK_CANVAS_USAGE, rect, &bits);
176
177    if (status) return JNI_FALSE;
178    if (!bits) {
179        buffer->unlock();
180        return JNI_FALSE;
181    }
182
183    ssize_t bytesCount = buffer->getStride() * bytesPerPixel(buffer->getPixelFormat());
184
185    SkBitmap bitmap;
186    bitmap.setInfo(SkImageInfo::Make(buffer->getWidth(), buffer->getHeight(),
187                                     convertPixelFormat(buffer->getPixelFormat()),
188                                     kPremul_SkAlphaType,
189                                     GraphicsJNI::defaultColorSpace()),
190                   bytesCount);
191
192    if (buffer->getWidth() > 0 && buffer->getHeight() > 0) {
193        bitmap.setPixels(bits);
194    } else {
195        bitmap.setPixels(NULL);
196    }
197
198    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
199    nativeCanvas->setBitmap(bitmap);
200    nativeCanvas->clipRect(rect.left, rect.top, rect.right, rect.bottom,
201            SkClipOp::kIntersect);
202
203    if (dirtyRect) {
204        INVOKEV(dirtyRect, gRectClassInfo.set,
205                int(rect.left), int(rect.top), int(rect.right), int(rect.bottom));
206    }
207
208    return JNI_TRUE;
209}
210
211static jboolean android_graphics_GraphicBuffer_unlockCanvasAndPost(JNIEnv* env, jobject,
212        jlong wrapperHandle, jobject canvas) {
213
214    GraphicBufferWrapper* wrapper =
215                reinterpret_cast<GraphicBufferWrapper*>(wrapperHandle);
216    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
217    nativeCanvas->setBitmap(SkBitmap());
218
219    if (wrapper) {
220        status_t status = wrapper->buffer->unlock();
221        return status == 0 ? JNI_TRUE : JNI_FALSE;
222    }
223
224    return JNI_FALSE;
225}
226
227// ----------------------------------------------------------------------------
228// Serialization
229// ----------------------------------------------------------------------------
230
231static void android_graphics_GraphicBuffer_write(JNIEnv* env, jobject clazz,
232        jlong wrapperHandle, jobject dest) {
233    GraphicBufferWrapper* wrapper =
234                reinterpret_cast<GraphicBufferWrapper*>(wrapperHandle);
235    Parcel* parcel = parcelForJavaObject(env, dest);
236    if (parcel) {
237        parcel->write(*wrapper->buffer);
238    }
239}
240
241static jlong android_graphics_GraphicBuffer_read(JNIEnv* env, jobject clazz,
242        jobject in) {
243
244    Parcel* parcel = parcelForJavaObject(env, in);
245    if (parcel) {
246        sp<GraphicBuffer> buffer = new GraphicBuffer();
247        parcel->read(*buffer);
248        return reinterpret_cast<jlong>(new GraphicBufferWrapper(buffer));
249    }
250
251    return NULL;
252}
253
254// ----------------------------------------------------------------------------
255// External helpers
256// ----------------------------------------------------------------------------
257
258sp<GraphicBuffer> graphicBufferForJavaObject(JNIEnv* env, jobject obj) {
259    if (obj) {
260        jlong nativeObject = env->GetLongField(obj, gGraphicBufferClassInfo.mNativeObject);
261        GraphicBufferWrapper* wrapper = (GraphicBufferWrapper*) nativeObject;
262        if (wrapper != NULL) {
263            sp<GraphicBuffer> buffer(wrapper->buffer);
264            return buffer;
265        }
266    }
267    return NULL;
268}
269
270jobject createJavaGraphicBuffer(JNIEnv* env, const sp<GraphicBuffer>& buffer) {
271    GraphicBufferWrapper* wrapper = new GraphicBufferWrapper(buffer);
272    jobject obj = env->NewObject(gGraphicBufferClassInfo.mClass,
273            gGraphicBufferClassInfo.mConstructorMethodID, buffer->getWidth(), buffer->getHeight(),
274            buffer->getPixelFormat(), buffer->getUsage(), reinterpret_cast<jlong>(wrapper));
275    return obj;
276}
277
278};
279
280using namespace android;
281// ----------------------------------------------------------------------------
282// JNI Glue
283// ----------------------------------------------------------------------------
284
285const char* const kClassPathName = "android/graphics/GraphicBuffer";
286
287static const JNINativeMethod gMethods[] = {
288    { "nCreateGraphicBuffer",  "(IIII)J", (void*) android_graphics_GraphicBuffer_create },
289    { "nDestroyGraphicBuffer", "(J)V",    (void*) android_graphics_GraphicBuffer_destroy },
290
291    { "nWriteGraphicBufferToParcel",  "(JLandroid/os/Parcel;)V",
292            (void*) android_graphics_GraphicBuffer_write },
293    { "nReadGraphicBufferFromParcel", "(Landroid/os/Parcel;)J",
294            (void*) android_graphics_GraphicBuffer_read },
295
296    { "nLockCanvas", "(JLandroid/graphics/Canvas;Landroid/graphics/Rect;)Z",
297            (void*) android_graphics_GraphicBuffer_lockCanvas },
298    { "nUnlockCanvasAndPost", "(JLandroid/graphics/Canvas;)Z",
299            (void*) android_graphics_GraphicBuffer_unlockCanvasAndPost },
300    { "nWrapGraphicBuffer", "(J)J",
301            (void*) android_graphics_GraphicBuffer_wrap }
302};
303
304int register_android_graphics_GraphicBuffer(JNIEnv* env) {
305    gGraphicBufferClassInfo.mClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, kClassPathName));
306    gGraphicBufferClassInfo.mNativeObject = GetFieldIDOrDie(env, gGraphicBufferClassInfo.mClass,
307            "mNativeObject", "J");
308    gGraphicBufferClassInfo.mConstructorMethodID = env->GetMethodID(gGraphicBufferClassInfo.mClass,
309            "<init>", "(IIIIJ)V");
310
311    jclass clazz = FindClassOrDie(env, "android/graphics/Rect");
312    gRectClassInfo.set = GetMethodIDOrDie(env, clazz, "set", "(IIII)V");
313    gRectClassInfo.left = GetFieldIDOrDie(env, clazz, "left", "I");
314    gRectClassInfo.top = GetFieldIDOrDie(env, clazz, "top", "I");
315    gRectClassInfo.right = GetFieldIDOrDie(env, clazz, "right", "I");
316    gRectClassInfo.bottom = GetFieldIDOrDie(env, clazz, "bottom", "I");
317
318    return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
319}
320