blob: 860d89680e2f592335e2d5e7bdf74863f47a8a7e [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
57#define ANDROID_MEDIA_DNGCREATOR_CTX_JNI_ID "mNativeContext"
58
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,
166 ANDROID_MEDIA_DNGCREATOR_CTX_JNI_ID, "J");
167 LOG_ALWAYS_FATAL_IF(gDngCreatorClassInfo.mNativeContext == NULL,
168 "can't find android/media/DngCreator.%s", ANDROID_MEDIA_DNGCREATOR_CTX_JNI_ID);
169
170 jclass outputStreamClazz = env->FindClass("java/io/OutputStream");
171 LOG_ALWAYS_FATAL_IF(outputStreamClazz == NULL, "Can't find java/io/OutputStream class");
172 gOutputStreamClassInfo.mWriteMethod = env->GetMethodID(outputStreamClazz, "write", "([BII)V");
173 LOG_ALWAYS_FATAL_IF(gOutputStreamClassInfo.mWriteMethod == NULL, "Can't find write method");
174}
175
176static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
177 jobject resultsPtr) {
178 ALOGV("%s:", __FUNCTION__);
179 CameraMetadata characteristics;
180 CameraMetadata results;
181 if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) {
182 jniThrowException(env, "java/lang/AssertionError",
183 "No native metadata defined for camera characteristics.");
184 return;
185 }
186 if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) {
187 jniThrowException(env, "java/lang/AssertionError",
188 "No native metadata defined for capture results.");
189 return;
190 }
191
192 sp<TiffWriter> writer = new TiffWriter();
193
194 writer->addIfd(TIFF_IFD_0);
195
196 status_t err = OK;
197
198 const uint32_t samplesPerPixel = 1;
199 const uint32_t bitsPerSample = BITS_PER_SAMPLE;
200 const uint32_t bitsPerByte = BITS_PER_SAMPLE / BYTES_PER_SAMPLE;
201 uint32_t imageWidth = 0;
202 uint32_t imageHeight = 0;
203
204 OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
205
206 // TODO: Greensplit.
207 // TODO: UniqueCameraModel
208 // TODO: Add remaining non-essential tags
209 {
210 // Set orientation
211 uint16_t orientation = 1; // Normal
212 BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env,
213 TAG_ORIENTATION);
214 }
215
216 {
217 // Set subfiletype
218 uint32_t subfileType = 0; // Main image
219 BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env,
220 TAG_NEWSUBFILETYPE);
221 }
222
223 {
224 // Set bits per sample
225 uint16_t bits = static_cast<uint16_t>(bitsPerSample);
226 BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
227 TAG_BITSPERSAMPLE);
228 }
229
230 {
231 // Set compression
232 uint16_t compression = 1; // None
233 BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env,
234 TAG_COMPRESSION);
235 }
236
237 {
238 // Set dimensions
239 camera_metadata_entry entry =
240 characteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
241 BAIL_IF_EMPTY(entry, env, TAG_IMAGEWIDTH);
242 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
243 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
244 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &width, TIFF_IFD_0), env,
245 TAG_IMAGEWIDTH);
246 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &height, TIFF_IFD_0), env,
247 TAG_IMAGELENGTH);
248 imageWidth = width;
249 imageHeight = height;
250 }
251
252 {
253 // Set photometric interpretation
254 uint16_t interpretation = 32803;
255 BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation,
256 TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION);
257 }
258
259 {
260 // Set blacklevel tags
261 camera_metadata_entry entry =
262 characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
263 BAIL_IF_EMPTY(entry, env, TAG_BLACKLEVEL);
264 const uint32_t* blackLevel = reinterpret_cast<const uint32_t*>(entry.data.i32);
265 BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVEL, entry.count, blackLevel, TIFF_IFD_0), env,
266 TAG_BLACKLEVEL);
267
268 uint16_t repeatDim[2] = {2, 2};
269 BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim, TIFF_IFD_0), env,
270 TAG_BLACKLEVELREPEATDIM);
271 }
272
273 {
274 // Set samples per pixel
275 uint16_t samples = static_cast<uint16_t>(samplesPerPixel);
276 BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
277 env, TAG_SAMPLESPERPIXEL);
278 }
279
280 {
281 // Set planar configuration
282 uint16_t config = 1; // Chunky
283 BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0),
284 env, TAG_PLANARCONFIGURATION);
285 }
286
287 {
288 // Set CFA pattern dimensions
289 uint16_t repeatDim[2] = {2, 2};
290 BAIL_IF_INVALID(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim, TIFF_IFD_0),
291 env, TAG_CFAREPEATPATTERNDIM);
292 }
293
294 {
295 // Set CFA pattern
296 camera_metadata_entry entry =
297 characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
298 BAIL_IF_EMPTY(entry, env, TAG_CFAPATTERN);
299 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
300 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
301 entry.data.u8[0]);
302 switch(cfa) {
303 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
304 uint8_t cfa[4] = {0, 1, 1, 2};
305 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
306 env, TAG_CFAPATTERN);
307 opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
308 break;
309 }
310 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
311 uint8_t cfa[4] = {1, 0, 2, 1};
312 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
313 env, TAG_CFAPATTERN);
314 opcodeCfaLayout = OpcodeListBuilder::CFA_GRBG;
315 break;
316 }
317 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
318 uint8_t cfa[4] = {1, 2, 0, 1};
319 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
320 env, TAG_CFAPATTERN);
321 opcodeCfaLayout = OpcodeListBuilder::CFA_GBRG;
322 break;
323 }
324 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
325 uint8_t cfa[4] = {2, 1, 1, 0};
326 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
327 env, TAG_CFAPATTERN);
328 opcodeCfaLayout = OpcodeListBuilder::CFA_BGGR;
329 break;
330 }
331 default: {
332 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
333 "Invalid metadata for tag %d", TAG_CFAPATTERN);
334 return;
335 }
336 }
337 }
338
339 {
340 // Set CFA plane color
341 uint8_t cfaPlaneColor[3] = {0, 1, 2};
342 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor, TIFF_IFD_0),
343 env, TAG_CFAPLANECOLOR);
344 }
345
346 {
347 // Set CFA layout
348 uint16_t cfaLayout = 1;
349 BAIL_IF_INVALID(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
350 env, TAG_CFALAYOUT);
351 }
352
353 {
354 // Set DNG version information
355 uint8_t version[4] = {1, 4, 0, 0};
356 BAIL_IF_INVALID(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
357 env, TAG_DNGVERSION);
358
359 uint8_t backwardVersion[4] = {1, 1, 0, 0};
360 BAIL_IF_INVALID(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion, TIFF_IFD_0),
361 env, TAG_DNGBACKWARDVERSION);
362 }
363
364 {
365 // Set whitelevel
366 camera_metadata_entry entry =
367 characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
368 BAIL_IF_EMPTY(entry, env, TAG_WHITELEVEL);
369 uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]);
370 BAIL_IF_INVALID(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0), env,
371 TAG_WHITELEVEL);
372 }
373
374 {
375 // Set default scale
376 uint32_t defaultScale[4] = {1, 1, 1, 1};
377 BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale, TIFF_IFD_0),
378 env, TAG_DEFAULTSCALE);
379 }
380
381 bool singleIlluminant = false;
382 {
383 // Set calibration illuminants
384 camera_metadata_entry entry1 =
385 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
386 BAIL_IF_EMPTY(entry1, env, TAG_CALIBRATIONILLUMINANT1);
387 camera_metadata_entry entry2 =
388 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
389 if (entry2.count == 0) {
390 singleIlluminant = true;
391 }
392 uint16_t ref1 = entry1.data.u8[0];
393
394 BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1,
395 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1);
396
397 if (!singleIlluminant) {
398 uint16_t ref2 = entry2.data.u8[0];
399 BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
400 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2);
401 }
402 }
403
404 {
405 // Set color transforms
406 camera_metadata_entry entry1 =
407 characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
408 BAIL_IF_EMPTY(entry1, env, TAG_COLORMATRIX1);
409
410 int32_t colorTransform1[entry1.count * 2];
411
412 size_t ctr = 0;
413 for(size_t i = 0; i < entry1.count; ++i) {
414 colorTransform1[ctr++] = entry1.data.r[i].numerator;
415 colorTransform1[ctr++] = entry1.data.r[i].denominator;
416 }
417
418 BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX1, entry1.count, colorTransform1, TIFF_IFD_0),
419 env, TAG_COLORMATRIX1);
420
421 if (!singleIlluminant) {
422 camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
423 BAIL_IF_EMPTY(entry2, env, TAG_COLORMATRIX2);
424 int32_t colorTransform2[entry2.count * 2];
425
426 ctr = 0;
427 for(size_t i = 0; i < entry2.count; ++i) {
428 colorTransform2[ctr++] = entry2.data.r[i].numerator;
429 colorTransform2[ctr++] = entry2.data.r[i].denominator;
430 }
431
432 BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX2, entry2.count, colorTransform2, TIFF_IFD_0),
433 env, TAG_COLORMATRIX2);
434 }
435 }
436
437 {
438 // Set calibration transforms
439 camera_metadata_entry entry1 =
440 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
441 BAIL_IF_EMPTY(entry1, env, TAG_CAMERACALIBRATION1);
442
443 int32_t calibrationTransform1[entry1.count * 2];
444
445 size_t ctr = 0;
446 for(size_t i = 0; i < entry1.count; ++i) {
447 calibrationTransform1[ctr++] = entry1.data.r[i].numerator;
448 calibrationTransform1[ctr++] = entry1.data.r[i].denominator;
449 }
450
451 BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count, calibrationTransform1,
452 TIFF_IFD_0), env, TAG_CAMERACALIBRATION1);
453
454 if (!singleIlluminant) {
455 camera_metadata_entry entry2 =
456 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
457 BAIL_IF_EMPTY(entry2, env, TAG_CAMERACALIBRATION2);
458 int32_t calibrationTransform2[entry2.count * 2];
459
460 ctr = 0;
461 for(size_t i = 0; i < entry2.count; ++i) {
462 calibrationTransform2[ctr++] = entry2.data.r[i].numerator;
463 calibrationTransform2[ctr++] = entry2.data.r[i].denominator;
464 }
465
466 BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count, calibrationTransform1,
467 TIFF_IFD_0), env, TAG_CAMERACALIBRATION2);
468 }
469 }
470
471 {
472 // Set forward transforms
473 camera_metadata_entry entry1 =
474 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
475 BAIL_IF_EMPTY(entry1, env, TAG_FORWARDMATRIX1);
476
477 int32_t forwardTransform1[entry1.count * 2];
478
479 size_t ctr = 0;
480 for(size_t i = 0; i < entry1.count; ++i) {
481 forwardTransform1[ctr++] = entry1.data.r[i].numerator;
482 forwardTransform1[ctr++] = entry1.data.r[i].denominator;
483 }
484
485 BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count, forwardTransform1,
486 TIFF_IFD_0), env, TAG_FORWARDMATRIX1);
487
488 if (!singleIlluminant) {
489 camera_metadata_entry entry2 =
490 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
491 BAIL_IF_EMPTY(entry2, env, TAG_FORWARDMATRIX2);
492 int32_t forwardTransform2[entry2.count * 2];
493
494 ctr = 0;
495 for(size_t i = 0; i < entry2.count; ++i) {
496 forwardTransform2[ctr++] = entry2.data.r[i].numerator;
497 forwardTransform2[ctr++] = entry2.data.r[i].denominator;
498 }
499
500 BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count, forwardTransform2,
501 TIFF_IFD_0), env, TAG_FORWARDMATRIX2);
502 }
503 }
504
505 {
506 // Set camera neutral
507 camera_metadata_entry entry =
508 results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
509 BAIL_IF_EMPTY(entry, env, TAG_ASSHOTNEUTRAL);
510 uint32_t cameraNeutral[entry.count * 2];
511
512 size_t ctr = 0;
513 for(size_t i = 0; i < entry.count; ++i) {
514 cameraNeutral[ctr++] =
515 static_cast<uint32_t>(entry.data.r[i].numerator);
516 cameraNeutral[ctr++] =
517 static_cast<uint32_t>(entry.data.r[i].denominator);
518 }
519
520 BAIL_IF_INVALID(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral,
521 TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL);
522 }
523
524 {
525 // Setup data strips
526 // TODO: Switch to tiled implementation.
527 uint32_t offset = 0;
528 BAIL_IF_INVALID(writer->addEntry(TAG_STRIPOFFSETS, 1, &offset, TIFF_IFD_0), env,
529 TAG_STRIPOFFSETS);
530
531 BAIL_IF_INVALID(writer->addEntry(TAG_ROWSPERSTRIP, 1, &imageHeight, TIFF_IFD_0), env,
532 TAG_ROWSPERSTRIP);
533
534 uint32_t byteCount = imageWidth * imageHeight * bitsPerSample * samplesPerPixel /
535 bitsPerByte;
536 BAIL_IF_INVALID(writer->addEntry(TAG_STRIPBYTECOUNTS, 1, &byteCount, TIFF_IFD_0), env,
537 TAG_STRIPBYTECOUNTS);
538 }
539
540 {
541 // Setup default crop + crop origin tags
542 uint32_t margin = 8; // Default margin recommended by Adobe for interpolation.
543 uint32_t dimensionLimit = 128; // Smallest image dimension crop margin from.
544 if (imageWidth >= dimensionLimit && imageHeight >= dimensionLimit) {
545 uint32_t defaultCropOrigin[] = {margin, margin};
546 uint32_t defaultCropSize[] = {imageWidth - margin, imageHeight - margin};
547 BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPORIGIN, 2, defaultCropOrigin,
548 TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN);
549 BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize,
550 TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE);
551 }
552 }
553
554 {
555 // Setup unique camera model tag
556 char model[PROPERTY_VALUE_MAX];
557 property_get("ro.product.model", model, "");
558
559 char manufacturer[PROPERTY_VALUE_MAX];
560 property_get("ro.product.manufacturer", manufacturer, "");
561
562 char brand[PROPERTY_VALUE_MAX];
563 property_get("ro.product.brand", brand, "");
564
565 String8 cameraModel(model);
566 cameraModel += "-";
567 cameraModel += manufacturer;
568 cameraModel += "-";
569 cameraModel += brand;
570
571 BAIL_IF_INVALID(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
572 reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
573 TAG_UNIQUECAMERAMODEL);
574 }
575
576 {
577 // Setup opcode List 2
578 camera_metadata_entry entry1 =
579 characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
580 BAIL_IF_EMPTY(entry1, env, TAG_OPCODELIST2);
581 uint32_t lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
582 uint32_t lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
583
584 camera_metadata_entry entry2 =
585 results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
586 BAIL_IF_EMPTY(entry2, env, TAG_OPCODELIST2);
587 if (entry2.count == lsmWidth * lsmHeight * 4) {
588
589 OpcodeListBuilder builder;
590 status_t err = builder.addGainMapsForMetadata(lsmWidth,
591 lsmHeight,
592 0,
593 0,
594 imageHeight,
595 imageWidth,
596 opcodeCfaLayout,
597 entry2.data.f);
598 if (err == OK) {
599 size_t listSize = builder.getSize();
600 uint8_t opcodeListBuf[listSize];
601 err = builder.buildOpList(opcodeListBuf);
602 if (err == OK) {
603 BAIL_IF_INVALID(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf,
604 TIFF_IFD_0), env, TAG_OPCODELIST2);
605 } else {
606 ALOGE("%s: Could not build Lens shading map opcode.", __FUNCTION__);
607 jniThrowRuntimeException(env, "failed to construct lens shading map opcode.");
608 }
609 } else {
610 ALOGE("%s: Could not add Lens shading map.", __FUNCTION__);
611 jniThrowRuntimeException(env, "failed to add lens shading map.");
612 }
613 } else {
614 ALOGW("%s: Lens shading map not present in results, skipping...", __FUNCTION__);
615 }
616 }
617
618 DngCreator_setCreator(env, thiz, writer);
619}
620
621static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
622 ALOGV("%s:", __FUNCTION__);
623 DngCreator_setCreator(env, thiz, NULL);
624}
625
626static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz) {
627 ALOGV("%s:", __FUNCTION__);
628 jniThrowRuntimeException(env, "nativeSetOrientation is not implemented");
629}
630
631static void DngCreator_nativeSetThumbnailBitmap(JNIEnv* env, jobject thiz, jobject bitmap) {
632 ALOGV("%s:", __FUNCTION__);
633 jniThrowRuntimeException(env, "nativeSetThumbnailBitmap is not implemented");
634}
635
636static void DngCreator_nativeSetThumbnailImage(JNIEnv* env, jobject thiz, jint width, jint height,
637 jobject yBuffer, jint yRowStride, jint yPixStride, jobject uBuffer, jint uRowStride,
638 jint uPixStride, jobject vBuffer, jint vRowStride, jint vPixStride) {
639 ALOGV("%s:", __FUNCTION__);
640 jniThrowRuntimeException(env, "nativeSetThumbnailImage is not implemented");
641}
642
643static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
644 jint height, jobject inBuffer, jint rowStride, jint pixStride) {
645 ALOGV("%s:", __FUNCTION__);
646
647 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
648 if(env->ExceptionCheck()) {
649 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
650 return;
651 }
652
653 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
654 if (pixelBytes == NULL) {
655 ALOGE("%s: Could not get native byte buffer", __FUNCTION__);
656 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid bytebuffer");
657 return;
658 }
659
660 TiffWriter* writer = DngCreator_getCreator(env, thiz);
661 if (writer == NULL) {
662 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
663 jniThrowException(env, "java/lang/AssertionError",
664 "Write called with uninitialized DngCreator");
665 return;
666 }
667 // TODO: handle lens shading map, etc. conversions for other raw buffer sizes.
668 uint32_t metadataWidth = *(writer->getEntry(TAG_IMAGEWIDTH, TIFF_IFD_0)->getData<uint32_t>());
669 uint32_t metadataHeight = *(writer->getEntry(TAG_IMAGELENGTH, TIFF_IFD_0)->getData<uint32_t>());
670 if (metadataWidth != width) {
671 jniThrowExceptionFmt(env, "java/lang/IllegalStateException", \
672 "Metadata width %d doesn't match image width %d", metadataWidth, width);
673 return;
674 }
675
676 if (metadataHeight != height) {
677 jniThrowExceptionFmt(env, "java/lang/IllegalStateException", \
678 "Metadata height %d doesn't match image height %d", metadataHeight, height);
679 return;
680 }
681
682 uint32_t stripOffset = writer->getTotalSize();
683
684 BAIL_IF_INVALID(writer->addEntry(TAG_STRIPOFFSETS, 1, &stripOffset, TIFF_IFD_0), env,
685 TAG_STRIPOFFSETS);
686
687 if (writer->write(out.get()) != OK) {
688 if (!env->ExceptionCheck()) {
689 jniThrowException(env, "java/io/IOException", "Failed to write metadata");
690 }
691 return;
692 }
693
694 size_t fullSize = rowStride * height;
695 jlong capacity = env->GetDirectBufferCapacity(inBuffer);
696 if (capacity < 0 || fullSize > capacity) {
697 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
698 "Invalid size %d for Image, size given in metadata is %d at current stride",
699 capacity, fullSize);
700 return;
701 }
702
703 if (pixStride == BYTES_PER_SAMPLE && rowStride == width * BYTES_PER_SAMPLE) {
704 if (out->write(pixelBytes, 0, fullSize) != OK || env->ExceptionCheck()) {
705 if (!env->ExceptionCheck()) {
706 jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
707 }
708 return;
709 }
710 } else if (pixStride == BYTES_PER_SAMPLE) {
711 for (size_t i = 0; i < height; ++i) {
712 if (out->write(pixelBytes, i * rowStride, pixStride * width) != OK ||
713 env->ExceptionCheck()) {
714 if (!env->ExceptionCheck()) {
715 jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
716 }
717 return;
718 }
719 }
720 } else {
721 for (size_t i = 0; i < height; ++i) {
722 for (size_t j = 0; j < width; ++j) {
723 if (out->write(pixelBytes, i * rowStride + j * pixStride,
724 BYTES_PER_SAMPLE) != OK || !env->ExceptionCheck()) {
725 if (env->ExceptionCheck()) {
726 jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
727 }
728 return;
729 }
730 }
731 }
732 }
733
734}
735
736static void DngCreator_nativeWriteByteBuffer(JNIEnv* env, jobject thiz, jobject outStream,
737 jobject rawBuffer, jlong offset) {
738 ALOGV("%s:", __FUNCTION__);
739 jniThrowRuntimeException(env, "nativeWriteByteBuffer is not implemented.");
740}
741
742static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
743 jobject inStream, jlong offset) {
744 ALOGV("%s:", __FUNCTION__);
745 jniThrowRuntimeException(env, "nativeWriteInputStream is not implemented.");
746}
747
748} /*extern "C" */
749
750static JNINativeMethod gDngCreatorMethods[] = {
751 {"nativeClassInit", "()V", (void*) DngCreator_nativeClassInit},
752 {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
753 "Landroid/hardware/camera2/impl/CameraMetadataNative;)V", (void*) DngCreator_init},
754 {"nativeDestroy", "()V", (void*) DngCreator_destroy},
755 {"nativeSetOrientation", "(I)V", (void*) DngCreator_nativeSetOrientation},
756 {"nativeSetThumbnailBitmap","(Landroid/graphics/Bitmap;)V",
757 (void*) DngCreator_nativeSetThumbnailBitmap},
758 {"nativeSetThumbnailImage",
759 "(IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;II)V",
760 (void*) DngCreator_nativeSetThumbnailImage},
761 {"nativeWriteImage", "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;II)V",
762 (void*) DngCreator_nativeWriteImage},
763 {"nativeWriteByteBuffer", "(Ljava/io/OutputStream;Ljava/nio/ByteBuffer;J)V",
764 (void*) DngCreator_nativeWriteByteBuffer},
765 {"nativeWriteInputStream", "(Ljava/io/OutputStream;Ljava/io/InputStream;J)V",
766 (void*) DngCreator_nativeWriteInputStream},
767};
768
769int register_android_media_DngCreator(JNIEnv *env) {
770 return AndroidRuntime::registerNativeMethods(env,
771 "android/media/DngCreator", gDngCreatorMethods, NELEM(gDngCreatorMethods));
772}