blob: 7b686e7cb5af613abd466213071169e495054c83 [file] [log] [blame]
Ruben Brunkf967a542014-04-28 16:31:11 -07001/*
2 * Copyright 2014 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_NDEBUG 0
18#define LOG_TAG "DngCreator_JNI"
19
20#include <system/camera_metadata.h>
21#include <camera/CameraMetadata.h>
22#include <img_utils/DngUtils.h>
23#include <img_utils/TagDefinitions.h>
24#include <img_utils/TiffIfd.h>
25#include <img_utils/TiffWriter.h>
26#include <img_utils/Output.h>
27
28#include <utils/Log.h>
29#include <utils/Errors.h>
30#include <utils/StrongPointer.h>
31#include <utils/RefBase.h>
32#include <cutils/properties.h>
33
34#include "android_runtime/AndroidRuntime.h"
35#include "android_runtime/android_hardware_camera2_CameraMetadata.h"
36
37#include <jni.h>
38#include <JNIHelp.h>
39
40using namespace android;
41using namespace img_utils;
42
43#define BAIL_IF_INVALID(expr, jnienv, tagId) \
44 if ((expr) != OK) { \
45 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
46 "Invalid metadata for tag %x", tagId); \
47 return; \
48 }
49
50#define BAIL_IF_EMPTY(entry, jnienv, tagId) \
51 if (entry.count == 0) { \
52 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
53 "Missing metadata fields for tag %x", tagId); \
54 return; \
55 }
56
Ruben Brunkb6079002014-05-22 12:33:54 -070057#define ANDROID_DNGCREATOR_CTX_JNI_ID "mNativeContext"
Ruben Brunkf967a542014-04-28 16:31:11 -070058
59static struct {
60 jfieldID mNativeContext;
61} gDngCreatorClassInfo;
62
63static struct {
64 jmethodID mWriteMethod;
65} gOutputStreamClassInfo;
66
67enum {
68 BITS_PER_SAMPLE = 16,
69 BYTES_PER_SAMPLE = 2,
70 TIFF_IFD_0 = 0
71};
72
73// ----------------------------------------------------------------------------
74
75// This class is not intended to be used across JNI calls.
76class JniOutputStream : public Output, public LightRefBase<JniOutputStream> {
77public:
78 JniOutputStream(JNIEnv* env, jobject outStream);
79
80 virtual ~JniOutputStream();
81
82 status_t open();
83 status_t write(const uint8_t* buf, size_t offset, size_t count);
84 status_t close();
85private:
86 enum {
87 BYTE_ARRAY_LENGTH = 1024
88 };
89 jobject mOutputStream;
90 JNIEnv* mEnv;
91 jbyteArray mByteArray;
92};
93
94JniOutputStream::JniOutputStream(JNIEnv* env, jobject outStream) : mOutputStream(outStream),
95 mEnv(env) {
96 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
97 if (mByteArray == NULL) {
98 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
99 }
100}
101
102JniOutputStream::~JniOutputStream() {
103 mEnv->DeleteLocalRef(mByteArray);
104}
105
106status_t JniOutputStream::open() {
107 // Do nothing
108 return OK;
109}
110
111status_t JniOutputStream::write(const uint8_t* buf, size_t offset, size_t count) {
112 while(count > 0) {
113 size_t len = BYTE_ARRAY_LENGTH;
114 len = (count > len) ? len : count;
115 mEnv->SetByteArrayRegion(mByteArray, 0, len, reinterpret_cast<const jbyte*>(buf + offset));
116
117 if (mEnv->ExceptionCheck()) {
118 return BAD_VALUE;
119 }
120
121 mEnv->CallVoidMethod(mOutputStream, gOutputStreamClassInfo.mWriteMethod, mByteArray,
122 0, len);
123
124 if (mEnv->ExceptionCheck()) {
125 return BAD_VALUE;
126 }
127
128 count -= len;
129 offset += len;
130 }
131 return OK;
132}
133
134status_t JniOutputStream::close() {
135 // Do nothing
136 return OK;
137}
138
139// ----------------------------------------------------------------------------
140
141extern "C" {
142
143static TiffWriter* DngCreator_getCreator(JNIEnv* env, jobject thiz) {
144 ALOGV("%s:", __FUNCTION__);
145 return reinterpret_cast<TiffWriter*>(env->GetLongField(thiz,
146 gDngCreatorClassInfo.mNativeContext));
147}
148
149static void DngCreator_setCreator(JNIEnv* env, jobject thiz, sp<TiffWriter> writer) {
150 ALOGV("%s:", __FUNCTION__);
151 TiffWriter* current = DngCreator_getCreator(env, thiz);
152 if (writer != NULL) {
153 writer->incStrong((void*) DngCreator_setCreator);
154 }
155 if (current) {
156 current->decStrong((void*) DngCreator_setCreator);
157 }
158 env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext,
159 reinterpret_cast<jlong>(writer.get()));
160}
161
162static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
163 ALOGV("%s:", __FUNCTION__);
164
165 gDngCreatorClassInfo.mNativeContext = env->GetFieldID(clazz,
Ruben Brunkb6079002014-05-22 12:33:54 -0700166 ANDROID_DNGCREATOR_CTX_JNI_ID, "J");
Ruben Brunkf967a542014-04-28 16:31:11 -0700167 LOG_ALWAYS_FATAL_IF(gDngCreatorClassInfo.mNativeContext == NULL,
Ruben Brunkb6079002014-05-22 12:33:54 -0700168 "can't find android/hardware/camera2/DngCreator.%s",
169 ANDROID_DNGCREATOR_CTX_JNI_ID);
Ruben Brunkf967a542014-04-28 16:31:11 -0700170
171 jclass outputStreamClazz = env->FindClass("java/io/OutputStream");
172 LOG_ALWAYS_FATAL_IF(outputStreamClazz == NULL, "Can't find java/io/OutputStream class");
173 gOutputStreamClassInfo.mWriteMethod = env->GetMethodID(outputStreamClazz, "write", "([BII)V");
174 LOG_ALWAYS_FATAL_IF(gOutputStreamClassInfo.mWriteMethod == NULL, "Can't find write method");
175}
176
177static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
178 jobject resultsPtr) {
179 ALOGV("%s:", __FUNCTION__);
180 CameraMetadata characteristics;
181 CameraMetadata results;
182 if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) {
183 jniThrowException(env, "java/lang/AssertionError",
184 "No native metadata defined for camera characteristics.");
185 return;
186 }
187 if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) {
188 jniThrowException(env, "java/lang/AssertionError",
189 "No native metadata defined for capture results.");
190 return;
191 }
192
193 sp<TiffWriter> writer = new TiffWriter();
194
195 writer->addIfd(TIFF_IFD_0);
196
197 status_t err = OK;
198
199 const uint32_t samplesPerPixel = 1;
200 const uint32_t bitsPerSample = BITS_PER_SAMPLE;
201 const uint32_t bitsPerByte = BITS_PER_SAMPLE / BYTES_PER_SAMPLE;
202 uint32_t imageWidth = 0;
203 uint32_t imageHeight = 0;
204
205 OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
206
207 // TODO: Greensplit.
208 // TODO: UniqueCameraModel
209 // TODO: Add remaining non-essential tags
210 {
211 // Set orientation
212 uint16_t orientation = 1; // Normal
213 BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env,
214 TAG_ORIENTATION);
215 }
216
217 {
218 // Set subfiletype
219 uint32_t subfileType = 0; // Main image
220 BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env,
221 TAG_NEWSUBFILETYPE);
222 }
223
224 {
225 // Set bits per sample
226 uint16_t bits = static_cast<uint16_t>(bitsPerSample);
227 BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
228 TAG_BITSPERSAMPLE);
229 }
230
231 {
232 // Set compression
233 uint16_t compression = 1; // None
234 BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env,
235 TAG_COMPRESSION);
236 }
237
238 {
239 // Set dimensions
240 camera_metadata_entry entry =
241 characteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
242 BAIL_IF_EMPTY(entry, env, TAG_IMAGEWIDTH);
243 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
244 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
245 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &width, TIFF_IFD_0), env,
246 TAG_IMAGEWIDTH);
247 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &height, TIFF_IFD_0), env,
248 TAG_IMAGELENGTH);
249 imageWidth = width;
250 imageHeight = height;
251 }
252
253 {
254 // Set photometric interpretation
255 uint16_t interpretation = 32803;
256 BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation,
257 TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION);
258 }
259
260 {
261 // Set blacklevel tags
262 camera_metadata_entry entry =
263 characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
264 BAIL_IF_EMPTY(entry, env, TAG_BLACKLEVEL);
265 const uint32_t* blackLevel = reinterpret_cast<const uint32_t*>(entry.data.i32);
266 BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVEL, entry.count, blackLevel, TIFF_IFD_0), env,
267 TAG_BLACKLEVEL);
268
269 uint16_t repeatDim[2] = {2, 2};
270 BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim, TIFF_IFD_0), env,
271 TAG_BLACKLEVELREPEATDIM);
272 }
273
274 {
275 // Set samples per pixel
276 uint16_t samples = static_cast<uint16_t>(samplesPerPixel);
277 BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
278 env, TAG_SAMPLESPERPIXEL);
279 }
280
281 {
282 // Set planar configuration
283 uint16_t config = 1; // Chunky
284 BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0),
285 env, TAG_PLANARCONFIGURATION);
286 }
287
288 {
289 // Set CFA pattern dimensions
290 uint16_t repeatDim[2] = {2, 2};
291 BAIL_IF_INVALID(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim, TIFF_IFD_0),
292 env, TAG_CFAREPEATPATTERNDIM);
293 }
294
295 {
296 // Set CFA pattern
297 camera_metadata_entry entry =
298 characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
299 BAIL_IF_EMPTY(entry, env, TAG_CFAPATTERN);
300 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
301 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
302 entry.data.u8[0]);
303 switch(cfa) {
304 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
305 uint8_t cfa[4] = {0, 1, 1, 2};
306 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
307 env, TAG_CFAPATTERN);
308 opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
309 break;
310 }
311 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
312 uint8_t cfa[4] = {1, 0, 2, 1};
313 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
314 env, TAG_CFAPATTERN);
315 opcodeCfaLayout = OpcodeListBuilder::CFA_GRBG;
316 break;
317 }
318 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
319 uint8_t cfa[4] = {1, 2, 0, 1};
320 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
321 env, TAG_CFAPATTERN);
322 opcodeCfaLayout = OpcodeListBuilder::CFA_GBRG;
323 break;
324 }
325 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
326 uint8_t cfa[4] = {2, 1, 1, 0};
327 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
328 env, TAG_CFAPATTERN);
329 opcodeCfaLayout = OpcodeListBuilder::CFA_BGGR;
330 break;
331 }
332 default: {
333 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
334 "Invalid metadata for tag %d", TAG_CFAPATTERN);
335 return;
336 }
337 }
338 }
339
340 {
341 // Set CFA plane color
342 uint8_t cfaPlaneColor[3] = {0, 1, 2};
343 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor, TIFF_IFD_0),
344 env, TAG_CFAPLANECOLOR);
345 }
346
347 {
348 // Set CFA layout
349 uint16_t cfaLayout = 1;
350 BAIL_IF_INVALID(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
351 env, TAG_CFALAYOUT);
352 }
353
354 {
355 // Set DNG version information
356 uint8_t version[4] = {1, 4, 0, 0};
357 BAIL_IF_INVALID(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
358 env, TAG_DNGVERSION);
359
360 uint8_t backwardVersion[4] = {1, 1, 0, 0};
361 BAIL_IF_INVALID(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion, TIFF_IFD_0),
362 env, TAG_DNGBACKWARDVERSION);
363 }
364
365 {
366 // Set whitelevel
367 camera_metadata_entry entry =
368 characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
369 BAIL_IF_EMPTY(entry, env, TAG_WHITELEVEL);
370 uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]);
371 BAIL_IF_INVALID(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0), env,
372 TAG_WHITELEVEL);
373 }
374
375 {
376 // Set default scale
377 uint32_t defaultScale[4] = {1, 1, 1, 1};
378 BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale, TIFF_IFD_0),
379 env, TAG_DEFAULTSCALE);
380 }
381
382 bool singleIlluminant = false;
383 {
384 // Set calibration illuminants
385 camera_metadata_entry entry1 =
386 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
387 BAIL_IF_EMPTY(entry1, env, TAG_CALIBRATIONILLUMINANT1);
388 camera_metadata_entry entry2 =
389 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
390 if (entry2.count == 0) {
391 singleIlluminant = true;
392 }
393 uint16_t ref1 = entry1.data.u8[0];
394
395 BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1,
396 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1);
397
398 if (!singleIlluminant) {
399 uint16_t ref2 = entry2.data.u8[0];
400 BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
401 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2);
402 }
403 }
404
405 {
406 // Set color transforms
407 camera_metadata_entry entry1 =
408 characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
409 BAIL_IF_EMPTY(entry1, env, TAG_COLORMATRIX1);
410
411 int32_t colorTransform1[entry1.count * 2];
412
413 size_t ctr = 0;
414 for(size_t i = 0; i < entry1.count; ++i) {
415 colorTransform1[ctr++] = entry1.data.r[i].numerator;
416 colorTransform1[ctr++] = entry1.data.r[i].denominator;
417 }
418
419 BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX1, entry1.count, colorTransform1, TIFF_IFD_0),
420 env, TAG_COLORMATRIX1);
421
422 if (!singleIlluminant) {
423 camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
424 BAIL_IF_EMPTY(entry2, env, TAG_COLORMATRIX2);
425 int32_t colorTransform2[entry2.count * 2];
426
427 ctr = 0;
428 for(size_t i = 0; i < entry2.count; ++i) {
429 colorTransform2[ctr++] = entry2.data.r[i].numerator;
430 colorTransform2[ctr++] = entry2.data.r[i].denominator;
431 }
432
433 BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX2, entry2.count, colorTransform2, TIFF_IFD_0),
434 env, TAG_COLORMATRIX2);
435 }
436 }
437
438 {
439 // Set calibration transforms
440 camera_metadata_entry entry1 =
441 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
442 BAIL_IF_EMPTY(entry1, env, TAG_CAMERACALIBRATION1);
443
444 int32_t calibrationTransform1[entry1.count * 2];
445
446 size_t ctr = 0;
447 for(size_t i = 0; i < entry1.count; ++i) {
448 calibrationTransform1[ctr++] = entry1.data.r[i].numerator;
449 calibrationTransform1[ctr++] = entry1.data.r[i].denominator;
450 }
451
452 BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count, calibrationTransform1,
453 TIFF_IFD_0), env, TAG_CAMERACALIBRATION1);
454
455 if (!singleIlluminant) {
456 camera_metadata_entry entry2 =
457 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
458 BAIL_IF_EMPTY(entry2, env, TAG_CAMERACALIBRATION2);
459 int32_t calibrationTransform2[entry2.count * 2];
460
461 ctr = 0;
462 for(size_t i = 0; i < entry2.count; ++i) {
463 calibrationTransform2[ctr++] = entry2.data.r[i].numerator;
464 calibrationTransform2[ctr++] = entry2.data.r[i].denominator;
465 }
466
467 BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count, calibrationTransform1,
468 TIFF_IFD_0), env, TAG_CAMERACALIBRATION2);
469 }
470 }
471
472 {
473 // Set forward transforms
474 camera_metadata_entry entry1 =
475 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
476 BAIL_IF_EMPTY(entry1, env, TAG_FORWARDMATRIX1);
477
478 int32_t forwardTransform1[entry1.count * 2];
479
480 size_t ctr = 0;
481 for(size_t i = 0; i < entry1.count; ++i) {
482 forwardTransform1[ctr++] = entry1.data.r[i].numerator;
483 forwardTransform1[ctr++] = entry1.data.r[i].denominator;
484 }
485
486 BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count, forwardTransform1,
487 TIFF_IFD_0), env, TAG_FORWARDMATRIX1);
488
489 if (!singleIlluminant) {
490 camera_metadata_entry entry2 =
491 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
492 BAIL_IF_EMPTY(entry2, env, TAG_FORWARDMATRIX2);
493 int32_t forwardTransform2[entry2.count * 2];
494
495 ctr = 0;
496 for(size_t i = 0; i < entry2.count; ++i) {
497 forwardTransform2[ctr++] = entry2.data.r[i].numerator;
498 forwardTransform2[ctr++] = entry2.data.r[i].denominator;
499 }
500
501 BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count, forwardTransform2,
502 TIFF_IFD_0), env, TAG_FORWARDMATRIX2);
503 }
504 }
505
506 {
507 // Set camera neutral
508 camera_metadata_entry entry =
509 results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
510 BAIL_IF_EMPTY(entry, env, TAG_ASSHOTNEUTRAL);
511 uint32_t cameraNeutral[entry.count * 2];
512
513 size_t ctr = 0;
514 for(size_t i = 0; i < entry.count; ++i) {
515 cameraNeutral[ctr++] =
516 static_cast<uint32_t>(entry.data.r[i].numerator);
517 cameraNeutral[ctr++] =
518 static_cast<uint32_t>(entry.data.r[i].denominator);
519 }
520
521 BAIL_IF_INVALID(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral,
522 TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL);
523 }
524
525 {
526 // Setup data strips
527 // TODO: Switch to tiled implementation.
528 uint32_t offset = 0;
529 BAIL_IF_INVALID(writer->addEntry(TAG_STRIPOFFSETS, 1, &offset, TIFF_IFD_0), env,
530 TAG_STRIPOFFSETS);
531
532 BAIL_IF_INVALID(writer->addEntry(TAG_ROWSPERSTRIP, 1, &imageHeight, TIFF_IFD_0), env,
533 TAG_ROWSPERSTRIP);
534
535 uint32_t byteCount = imageWidth * imageHeight * bitsPerSample * samplesPerPixel /
536 bitsPerByte;
537 BAIL_IF_INVALID(writer->addEntry(TAG_STRIPBYTECOUNTS, 1, &byteCount, TIFF_IFD_0), env,
538 TAG_STRIPBYTECOUNTS);
539 }
540
541 {
542 // Setup default crop + crop origin tags
543 uint32_t margin = 8; // Default margin recommended by Adobe for interpolation.
544 uint32_t dimensionLimit = 128; // Smallest image dimension crop margin from.
545 if (imageWidth >= dimensionLimit && imageHeight >= dimensionLimit) {
546 uint32_t defaultCropOrigin[] = {margin, margin};
547 uint32_t defaultCropSize[] = {imageWidth - margin, imageHeight - margin};
548 BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPORIGIN, 2, defaultCropOrigin,
549 TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN);
550 BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize,
551 TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE);
552 }
553 }
554
555 {
556 // Setup unique camera model tag
557 char model[PROPERTY_VALUE_MAX];
558 property_get("ro.product.model", model, "");
559
560 char manufacturer[PROPERTY_VALUE_MAX];
561 property_get("ro.product.manufacturer", manufacturer, "");
562
563 char brand[PROPERTY_VALUE_MAX];
564 property_get("ro.product.brand", brand, "");
565
566 String8 cameraModel(model);
567 cameraModel += "-";
568 cameraModel += manufacturer;
569 cameraModel += "-";
570 cameraModel += brand;
571
572 BAIL_IF_INVALID(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
573 reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
574 TAG_UNIQUECAMERAMODEL);
575 }
576
577 {
578 // Setup opcode List 2
579 camera_metadata_entry entry1 =
580 characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
581 BAIL_IF_EMPTY(entry1, env, TAG_OPCODELIST2);
582 uint32_t lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
583 uint32_t lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
584
585 camera_metadata_entry entry2 =
586 results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
587 BAIL_IF_EMPTY(entry2, env, TAG_OPCODELIST2);
588 if (entry2.count == lsmWidth * lsmHeight * 4) {
589
590 OpcodeListBuilder builder;
591 status_t err = builder.addGainMapsForMetadata(lsmWidth,
592 lsmHeight,
593 0,
594 0,
595 imageHeight,
596 imageWidth,
597 opcodeCfaLayout,
598 entry2.data.f);
599 if (err == OK) {
600 size_t listSize = builder.getSize();
601 uint8_t opcodeListBuf[listSize];
602 err = builder.buildOpList(opcodeListBuf);
603 if (err == OK) {
604 BAIL_IF_INVALID(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf,
605 TIFF_IFD_0), env, TAG_OPCODELIST2);
606 } else {
607 ALOGE("%s: Could not build Lens shading map opcode.", __FUNCTION__);
608 jniThrowRuntimeException(env, "failed to construct lens shading map opcode.");
609 }
610 } else {
611 ALOGE("%s: Could not add Lens shading map.", __FUNCTION__);
612 jniThrowRuntimeException(env, "failed to add lens shading map.");
613 }
614 } else {
615 ALOGW("%s: Lens shading map not present in results, skipping...", __FUNCTION__);
616 }
617 }
618
619 DngCreator_setCreator(env, thiz, writer);
620}
621
622static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
623 ALOGV("%s:", __FUNCTION__);
624 DngCreator_setCreator(env, thiz, NULL);
625}
626
627static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz) {
628 ALOGV("%s:", __FUNCTION__);
629 jniThrowRuntimeException(env, "nativeSetOrientation is not implemented");
630}
631
632static void DngCreator_nativeSetThumbnailBitmap(JNIEnv* env, jobject thiz, jobject bitmap) {
633 ALOGV("%s:", __FUNCTION__);
634 jniThrowRuntimeException(env, "nativeSetThumbnailBitmap is not implemented");
635}
636
637static void DngCreator_nativeSetThumbnailImage(JNIEnv* env, jobject thiz, jint width, jint height,
638 jobject yBuffer, jint yRowStride, jint yPixStride, jobject uBuffer, jint uRowStride,
639 jint uPixStride, jobject vBuffer, jint vRowStride, jint vPixStride) {
640 ALOGV("%s:", __FUNCTION__);
641 jniThrowRuntimeException(env, "nativeSetThumbnailImage is not implemented");
642}
643
644static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
645 jint height, jobject inBuffer, jint rowStride, jint pixStride) {
646 ALOGV("%s:", __FUNCTION__);
647
648 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
649 if(env->ExceptionCheck()) {
650 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
651 return;
652 }
653
654 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
655 if (pixelBytes == NULL) {
656 ALOGE("%s: Could not get native byte buffer", __FUNCTION__);
657 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid bytebuffer");
658 return;
659 }
660
661 TiffWriter* writer = DngCreator_getCreator(env, thiz);
662 if (writer == NULL) {
663 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
664 jniThrowException(env, "java/lang/AssertionError",
665 "Write called with uninitialized DngCreator");
666 return;
667 }
668 // TODO: handle lens shading map, etc. conversions for other raw buffer sizes.
669 uint32_t metadataWidth = *(writer->getEntry(TAG_IMAGEWIDTH, TIFF_IFD_0)->getData<uint32_t>());
670 uint32_t metadataHeight = *(writer->getEntry(TAG_IMAGELENGTH, TIFF_IFD_0)->getData<uint32_t>());
671 if (metadataWidth != width) {
672 jniThrowExceptionFmt(env, "java/lang/IllegalStateException", \
673 "Metadata width %d doesn't match image width %d", metadataWidth, width);
674 return;
675 }
676
677 if (metadataHeight != height) {
678 jniThrowExceptionFmt(env, "java/lang/IllegalStateException", \
679 "Metadata height %d doesn't match image height %d", metadataHeight, height);
680 return;
681 }
682
683 uint32_t stripOffset = writer->getTotalSize();
684
685 BAIL_IF_INVALID(writer->addEntry(TAG_STRIPOFFSETS, 1, &stripOffset, TIFF_IFD_0), env,
686 TAG_STRIPOFFSETS);
687
688 if (writer->write(out.get()) != OK) {
689 if (!env->ExceptionCheck()) {
690 jniThrowException(env, "java/io/IOException", "Failed to write metadata");
691 }
692 return;
693 }
694
695 size_t fullSize = rowStride * height;
696 jlong capacity = env->GetDirectBufferCapacity(inBuffer);
697 if (capacity < 0 || fullSize > capacity) {
698 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
699 "Invalid size %d for Image, size given in metadata is %d at current stride",
700 capacity, fullSize);
701 return;
702 }
703
704 if (pixStride == BYTES_PER_SAMPLE && rowStride == width * BYTES_PER_SAMPLE) {
705 if (out->write(pixelBytes, 0, fullSize) != OK || env->ExceptionCheck()) {
706 if (!env->ExceptionCheck()) {
707 jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
708 }
709 return;
710 }
711 } else if (pixStride == BYTES_PER_SAMPLE) {
712 for (size_t i = 0; i < height; ++i) {
713 if (out->write(pixelBytes, i * rowStride, pixStride * width) != OK ||
714 env->ExceptionCheck()) {
715 if (!env->ExceptionCheck()) {
716 jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
717 }
718 return;
719 }
720 }
721 } else {
722 for (size_t i = 0; i < height; ++i) {
723 for (size_t j = 0; j < width; ++j) {
724 if (out->write(pixelBytes, i * rowStride + j * pixStride,
725 BYTES_PER_SAMPLE) != OK || !env->ExceptionCheck()) {
726 if (env->ExceptionCheck()) {
727 jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
728 }
729 return;
730 }
731 }
732 }
733 }
734
735}
736
737static void DngCreator_nativeWriteByteBuffer(JNIEnv* env, jobject thiz, jobject outStream,
738 jobject rawBuffer, jlong offset) {
739 ALOGV("%s:", __FUNCTION__);
740 jniThrowRuntimeException(env, "nativeWriteByteBuffer is not implemented.");
741}
742
743static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
744 jobject inStream, jlong offset) {
745 ALOGV("%s:", __FUNCTION__);
746 jniThrowRuntimeException(env, "nativeWriteInputStream is not implemented.");
747}
748
749} /*extern "C" */
750
751static JNINativeMethod gDngCreatorMethods[] = {
752 {"nativeClassInit", "()V", (void*) DngCreator_nativeClassInit},
753 {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
754 "Landroid/hardware/camera2/impl/CameraMetadataNative;)V", (void*) DngCreator_init},
755 {"nativeDestroy", "()V", (void*) DngCreator_destroy},
756 {"nativeSetOrientation", "(I)V", (void*) DngCreator_nativeSetOrientation},
757 {"nativeSetThumbnailBitmap","(Landroid/graphics/Bitmap;)V",
758 (void*) DngCreator_nativeSetThumbnailBitmap},
759 {"nativeSetThumbnailImage",
760 "(IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;II)V",
761 (void*) DngCreator_nativeSetThumbnailImage},
762 {"nativeWriteImage", "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;II)V",
763 (void*) DngCreator_nativeWriteImage},
764 {"nativeWriteByteBuffer", "(Ljava/io/OutputStream;Ljava/nio/ByteBuffer;J)V",
765 (void*) DngCreator_nativeWriteByteBuffer},
766 {"nativeWriteInputStream", "(Ljava/io/OutputStream;Ljava/io/InputStream;J)V",
767 (void*) DngCreator_nativeWriteInputStream},
768};
769
Ruben Brunkb6079002014-05-22 12:33:54 -0700770int register_android_hardware_camera2_DngCreator(JNIEnv *env) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700771 return AndroidRuntime::registerNativeMethods(env,
Ruben Brunkb6079002014-05-22 12:33:54 -0700772 "android/hardware/camera2/DngCreator", gDngCreatorMethods,
773 NELEM(gDngCreatorMethods));
Ruben Brunkf967a542014-04-28 16:31:11 -0700774}