1/*
2 * Copyright (C) 2011 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#include "android/bitmap.h"
18
19#include "jni/jni_native_frame.h"
20#include "jni/jni_native_buffer.h"
21#include "jni/jni_util.h"
22
23#include "native/base/logging.h"
24#include "native/core/gl_frame.h"
25#include "native/core/native_frame.h"
26
27using android::filterfw::NativeFrame;
28using android::filterfw::GLFrame;
29
30typedef union {
31    uint32_t value;
32    uint8_t  rgba[4];
33} Pixel;
34
35jboolean Java_android_filterfw_core_NativeFrame_nativeAllocate(JNIEnv* env,
36                                                               jobject thiz,
37                                                               jint size) {
38  return ToJBool(WrapObjectInJava(new NativeFrame(size), env, thiz, true));
39}
40
41jboolean Java_android_filterfw_core_NativeFrame_nativeDeallocate(JNIEnv* env, jobject thiz) {
42  return ToJBool(DeleteNativeObject<NativeFrame>(env, thiz));
43}
44
45jint Java_android_filterfw_core_NativeFrame_nativeIntSize(JNIEnv*, jclass) {
46  return sizeof(jint);
47}
48
49jint Java_android_filterfw_core_NativeFrame_nativeFloatSize(JNIEnv*, jclass) {
50  return sizeof(jfloat);
51}
52
53jboolean Java_android_filterfw_core_NativeFrame_setNativeData(JNIEnv* env,
54                                                              jobject thiz,
55                                                              jbyteArray data,
56                                                              jint offset,
57                                                              jint length) {
58  NativeFrame* frame = ConvertFromJava<NativeFrame>(env, thiz);
59  if (frame && data) {
60    jbyte* bytes = env->GetByteArrayElements(data, NULL);
61    if (bytes) {
62      const bool success = frame->WriteData(reinterpret_cast<const uint8_t*>(bytes + offset),
63                                            0,
64                                            length);
65      env->ReleaseByteArrayElements(data, bytes, JNI_ABORT);
66      return ToJBool(success);
67    }
68  }
69  return JNI_FALSE;
70}
71
72jbyteArray Java_android_filterfw_core_NativeFrame_getNativeData(JNIEnv* env,
73                                                                jobject thiz,
74                                                                jint size) {
75  NativeFrame* frame = ConvertFromJava<NativeFrame>(env, thiz);
76  if (frame) {
77    const uint8_t* data = frame->Data();
78    if (!data || size > frame->Size())
79      return NULL;
80    jbyteArray result = env->NewByteArray(size);
81    env->SetByteArrayRegion(result, 0, size, reinterpret_cast<const jbyte*>(data));
82    return result;
83  }
84  return NULL;
85}
86
87jboolean Java_android_filterfw_core_NativeFrame_getNativeBuffer(JNIEnv* env,
88                                                                jobject thiz,
89                                                                jobject buffer) {
90  NativeFrame* frame = ConvertFromJava<NativeFrame>(env, thiz);
91  if (frame) {
92    char* data = reinterpret_cast<char*>(frame->MutableData());
93    return ToJBool(AttachDataToJBuffer(env, buffer, data, frame->Size()));
94  }
95  return JNI_FALSE;
96}
97
98jboolean Java_android_filterfw_core_NativeFrame_setNativeInts(JNIEnv* env,
99                                                              jobject thiz,
100                                                              jintArray ints) {
101  NativeFrame* frame = ConvertFromJava<NativeFrame>(env, thiz);
102  if (frame && ints) {
103    jint* int_ptr = env->GetIntArrayElements(ints, NULL);
104    const int length = env->GetArrayLength(ints);
105    if (int_ptr) {
106      const bool success = frame->WriteData(reinterpret_cast<const uint8_t*>(int_ptr),
107                                            0,
108                                            length * sizeof(jint));
109      env->ReleaseIntArrayElements(ints, int_ptr, JNI_ABORT);
110      return ToJBool(success);
111    }
112  }
113  return JNI_FALSE;
114}
115
116jintArray Java_android_filterfw_core_NativeFrame_getNativeInts(JNIEnv* env,
117                                                               jobject thiz,
118                                                               jint size) {
119  NativeFrame* frame = ConvertFromJava<NativeFrame>(env, thiz);
120  if (frame) {
121    const uint8_t* data = frame->Data();
122    if (!data || size > frame->Size() || (size % sizeof(jint)) != 0)
123      return NULL;
124    const int count = size / sizeof(jint);
125    jintArray result = env->NewIntArray(count);
126    env->SetIntArrayRegion(result, 0, count, reinterpret_cast<const jint*>(data));
127    return result;
128  }
129  return NULL;
130}
131
132jboolean Java_android_filterfw_core_NativeFrame_setNativeFloats(JNIEnv* env,
133                                                                jobject thiz,
134                                                                jfloatArray floats) {
135  NativeFrame* frame = ConvertFromJava<NativeFrame>(env, thiz);
136  if (frame && floats) {
137    jfloat* float_ptr = env->GetFloatArrayElements(floats, NULL);
138    const int length = env->GetArrayLength(floats);
139    if (float_ptr) {
140      const bool success = frame->WriteData(reinterpret_cast<const uint8_t*>(float_ptr),
141                                            0,
142                                            length * sizeof(jfloat));
143      env->ReleaseFloatArrayElements(floats, float_ptr, JNI_ABORT);
144      return ToJBool(success);
145    }
146  }
147  return JNI_FALSE;
148}
149
150jfloatArray Java_android_filterfw_core_NativeFrame_getNativeFloats(JNIEnv* env,
151                                                                   jobject thiz,
152                                                                   jint size) {
153  NativeFrame* frame = ConvertFromJava<NativeFrame>(env, thiz);
154  if (frame) {
155    const uint8_t* data = frame->Data();
156    if (!data || size > frame->Size() || (size % sizeof(jfloat)) != 0)
157      return NULL;
158    const int count = size / sizeof(jfloat);
159    jfloatArray result = env->NewFloatArray(count);
160    env->SetFloatArrayRegion(result, 0, count, reinterpret_cast<const jfloat*>(data));
161    return result;
162  }
163  return NULL;
164}
165
166jboolean Java_android_filterfw_core_NativeFrame_setNativeBitmap(JNIEnv* env,
167                                                                jobject thiz,
168                                                                jobject bitmap,
169                                                                jint size,
170                                                                jint bytes_per_sample) {
171  NativeFrame* frame = ConvertFromJava<NativeFrame>(env, thiz);
172  if (frame && bitmap) {
173    // Make sure frame size matches bitmap size
174    if ((size / 4) != (frame->Size() / bytes_per_sample)) {
175      ALOGE("Size mismatch in native setBitmap()!");
176      return JNI_FALSE;
177    }
178
179    Pixel* src_ptr;
180    const int result = AndroidBitmap_lockPixels(env, bitmap, reinterpret_cast<void**>(&src_ptr));
181    if (result == ANDROID_BITMAP_RESUT_SUCCESS) {
182      // Create destination pointers
183      uint8_t* dst_ptr = reinterpret_cast<uint8_t*>(frame->MutableData());
184      const uint8_t* end_ptr = dst_ptr + frame->Size();
185      switch (bytes_per_sample) {
186        case 1: { // RGBA -> GRAY
187          while (dst_ptr < end_ptr) {
188            const Pixel pixel = *(src_ptr++);
189            *(dst_ptr++) = (pixel.rgba[0] + pixel.rgba[1] + pixel.rgba[2]) / 3;
190          }
191          break;
192        }
193        case 3: { // RGBA -> RGB
194          while (dst_ptr < end_ptr) {
195            const Pixel pixel = *(src_ptr++);
196            *(dst_ptr++) = pixel.rgba[0];
197            *(dst_ptr++) = pixel.rgba[1];
198            *(dst_ptr++) = pixel.rgba[2];
199          }
200          break;
201        }
202        case 4: { // RGBA -> RGBA
203          memcpy(dst_ptr, src_ptr, frame->Size());
204          break;
205        }
206        default:
207          ALOGE("Unsupported bytes-per-pixel %d in setBitmap!", bytes_per_sample);
208          break;
209      }
210      return (AndroidBitmap_unlockPixels(env, bitmap) == ANDROID_BITMAP_RESUT_SUCCESS);
211    }
212  }
213  return JNI_FALSE;
214}
215
216jboolean Java_android_filterfw_core_NativeFrame_getNativeBitmap(JNIEnv* env,
217                                                                jobject thiz,
218                                                                jobject bitmap,
219                                                                jint size,
220                                                                jint bytes_per_sample) {
221  NativeFrame* frame = ConvertFromJava<NativeFrame>(env, thiz);
222  if (frame && bitmap) {
223    Pixel* dst_ptr;
224    const int result = AndroidBitmap_lockPixels(env, bitmap, reinterpret_cast<void**>(&dst_ptr));
225    if (result == ANDROID_BITMAP_RESUT_SUCCESS) {
226      // Make sure frame size matches bitmap size
227      if ((size / 4) != (frame->Size() / bytes_per_sample)) {
228        ALOGE("Size mismatch in native getBitmap()!");
229        return JNI_FALSE;
230      }
231
232      const uint8_t* src_ptr = frame->Data();
233      const uint8_t* end_ptr = src_ptr + frame->Size();
234      switch (bytes_per_sample) {
235        case 1: { // GRAY -> RGBA
236          while (src_ptr < end_ptr) {
237            const uint8_t value = *(src_ptr++);
238            dst_ptr->rgba[0] = dst_ptr->rgba[1] = dst_ptr->rgba[2] = value;
239            dst_ptr->rgba[3] = 255;
240            ++dst_ptr;
241          }
242          break;
243        }
244        case 3: { // RGB -> RGBA
245          while (src_ptr < end_ptr) {
246            dst_ptr->rgba[0] = *(src_ptr++);
247            dst_ptr->rgba[1] = *(src_ptr++);
248            dst_ptr->rgba[2] = *(src_ptr++);
249            dst_ptr->rgba[3] = 255;
250            ++dst_ptr;
251          }
252          break;
253        }
254        case 4: { // RGBA -> RGBA
255          memcpy(dst_ptr, src_ptr, frame->Size());
256          break;
257        }
258        default:
259          ALOGE("Unsupported bytes-per-pixel %d in getBitmap!", bytes_per_sample);
260          break;
261      }
262      return (AndroidBitmap_unlockPixels(env, bitmap) == ANDROID_BITMAP_RESUT_SUCCESS);
263    }
264  }
265  return JNI_FALSE;
266}
267
268jint Java_android_filterfw_core_NativeFrame_getNativeCapacity(JNIEnv* env, jobject thiz) {
269  NativeFrame* frame = ConvertFromJava<NativeFrame>(env, thiz);
270  return frame ? frame->Capacity() : -1;
271}
272
273jboolean Java_android_filterfw_core_NativeFrame_nativeCopyFromNative(JNIEnv* env,
274                                                                     jobject thiz,
275                                                                     jobject frame) {
276  NativeFrame* this_frame = ConvertFromJava<NativeFrame>(env, thiz);
277  NativeFrame* other_frame = ConvertFromJava<NativeFrame>(env, frame);
278  if (this_frame && other_frame) {
279    return ToJBool(this_frame->WriteData(other_frame->Data(), 0, other_frame->Size()));
280  }
281  return JNI_FALSE;
282}
283
284jboolean Java_android_filterfw_core_NativeFrame_nativeCopyFromGL(JNIEnv* env,
285                                                                 jobject thiz,
286                                                                 jobject frame) {
287  NativeFrame* this_frame = ConvertFromJava<NativeFrame>(env, thiz);
288  GLFrame* other_frame = ConvertFromJava<GLFrame>(env, frame);
289  if (this_frame && other_frame) {
290    return ToJBool(other_frame->CopyDataTo(this_frame->MutableData(), this_frame->Size()));
291  }
292  return JNI_FALSE;
293}
294