blob: 33100bfe7310d4c471ff583adbc78ab851744f0a [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
Ruben Brunkb8df8e02014-06-02 22:59:45 -070034#include <string.h>
35
Ruben Brunkf967a542014-04-28 16:31:11 -070036#include "android_runtime/AndroidRuntime.h"
37#include "android_runtime/android_hardware_camera2_CameraMetadata.h"
38
39#include <jni.h>
40#include <JNIHelp.h>
41
42using namespace android;
43using namespace img_utils;
44
45#define BAIL_IF_INVALID(expr, jnienv, tagId) \
46 if ((expr) != OK) { \
47 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
48 "Invalid metadata for tag %x", tagId); \
49 return; \
50 }
51
52#define BAIL_IF_EMPTY(entry, jnienv, tagId) \
53 if (entry.count == 0) { \
54 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
55 "Missing metadata fields for tag %x", tagId); \
56 return; \
57 }
58
Ruben Brunkb6079002014-05-22 12:33:54 -070059#define ANDROID_DNGCREATOR_CTX_JNI_ID "mNativeContext"
Ruben Brunkf967a542014-04-28 16:31:11 -070060
61static struct {
62 jfieldID mNativeContext;
63} gDngCreatorClassInfo;
64
65static struct {
66 jmethodID mWriteMethod;
67} gOutputStreamClassInfo;
68
69enum {
70 BITS_PER_SAMPLE = 16,
71 BYTES_PER_SAMPLE = 2,
72 TIFF_IFD_0 = 0
73};
74
75// ----------------------------------------------------------------------------
76
77// This class is not intended to be used across JNI calls.
78class JniOutputStream : public Output, public LightRefBase<JniOutputStream> {
79public:
80 JniOutputStream(JNIEnv* env, jobject outStream);
81
82 virtual ~JniOutputStream();
83
84 status_t open();
85 status_t write(const uint8_t* buf, size_t offset, size_t count);
86 status_t close();
87private:
88 enum {
89 BYTE_ARRAY_LENGTH = 1024
90 };
91 jobject mOutputStream;
92 JNIEnv* mEnv;
93 jbyteArray mByteArray;
94};
95
96JniOutputStream::JniOutputStream(JNIEnv* env, jobject outStream) : mOutputStream(outStream),
97 mEnv(env) {
98 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
99 if (mByteArray == NULL) {
100 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
101 }
102}
103
104JniOutputStream::~JniOutputStream() {
105 mEnv->DeleteLocalRef(mByteArray);
106}
107
108status_t JniOutputStream::open() {
109 // Do nothing
110 return OK;
111}
112
113status_t JniOutputStream::write(const uint8_t* buf, size_t offset, size_t count) {
114 while(count > 0) {
115 size_t len = BYTE_ARRAY_LENGTH;
116 len = (count > len) ? len : count;
117 mEnv->SetByteArrayRegion(mByteArray, 0, len, reinterpret_cast<const jbyte*>(buf + offset));
118
119 if (mEnv->ExceptionCheck()) {
120 return BAD_VALUE;
121 }
122
123 mEnv->CallVoidMethod(mOutputStream, gOutputStreamClassInfo.mWriteMethod, mByteArray,
124 0, len);
125
126 if (mEnv->ExceptionCheck()) {
127 return BAD_VALUE;
128 }
129
130 count -= len;
131 offset += len;
132 }
133 return OK;
134}
135
136status_t JniOutputStream::close() {
137 // Do nothing
138 return OK;
139}
140
141// ----------------------------------------------------------------------------
142
143extern "C" {
144
145static TiffWriter* DngCreator_getCreator(JNIEnv* env, jobject thiz) {
146 ALOGV("%s:", __FUNCTION__);
147 return reinterpret_cast<TiffWriter*>(env->GetLongField(thiz,
148 gDngCreatorClassInfo.mNativeContext));
149}
150
151static void DngCreator_setCreator(JNIEnv* env, jobject thiz, sp<TiffWriter> writer) {
152 ALOGV("%s:", __FUNCTION__);
153 TiffWriter* current = DngCreator_getCreator(env, thiz);
154 if (writer != NULL) {
155 writer->incStrong((void*) DngCreator_setCreator);
156 }
157 if (current) {
158 current->decStrong((void*) DngCreator_setCreator);
159 }
160 env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext,
161 reinterpret_cast<jlong>(writer.get()));
162}
163
164static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
165 ALOGV("%s:", __FUNCTION__);
166
167 gDngCreatorClassInfo.mNativeContext = env->GetFieldID(clazz,
Ruben Brunkb6079002014-05-22 12:33:54 -0700168 ANDROID_DNGCREATOR_CTX_JNI_ID, "J");
Ruben Brunkf967a542014-04-28 16:31:11 -0700169 LOG_ALWAYS_FATAL_IF(gDngCreatorClassInfo.mNativeContext == NULL,
Ruben Brunkb6079002014-05-22 12:33:54 -0700170 "can't find android/hardware/camera2/DngCreator.%s",
171 ANDROID_DNGCREATOR_CTX_JNI_ID);
Ruben Brunkf967a542014-04-28 16:31:11 -0700172
173 jclass outputStreamClazz = env->FindClass("java/io/OutputStream");
174 LOG_ALWAYS_FATAL_IF(outputStreamClazz == NULL, "Can't find java/io/OutputStream class");
175 gOutputStreamClassInfo.mWriteMethod = env->GetMethodID(outputStreamClazz, "write", "([BII)V");
176 LOG_ALWAYS_FATAL_IF(gOutputStreamClassInfo.mWriteMethod == NULL, "Can't find write method");
177}
178
179static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700180 jobject resultsPtr, jstring formattedCaptureTime) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700181 ALOGV("%s:", __FUNCTION__);
182 CameraMetadata characteristics;
183 CameraMetadata results;
184 if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) {
185 jniThrowException(env, "java/lang/AssertionError",
186 "No native metadata defined for camera characteristics.");
187 return;
188 }
189 if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) {
190 jniThrowException(env, "java/lang/AssertionError",
191 "No native metadata defined for capture results.");
192 return;
193 }
194
195 sp<TiffWriter> writer = new TiffWriter();
196
197 writer->addIfd(TIFF_IFD_0);
198
199 status_t err = OK;
200
201 const uint32_t samplesPerPixel = 1;
202 const uint32_t bitsPerSample = BITS_PER_SAMPLE;
203 const uint32_t bitsPerByte = BITS_PER_SAMPLE / BYTES_PER_SAMPLE;
204 uint32_t imageWidth = 0;
205 uint32_t imageHeight = 0;
206
207 OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
208
209 // TODO: Greensplit.
Ruben Brunkf967a542014-04-28 16:31:11 -0700210 // TODO: Add remaining non-essential tags
211 {
212 // Set orientation
213 uint16_t orientation = 1; // Normal
214 BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env,
215 TAG_ORIENTATION);
216 }
217
218 {
219 // Set subfiletype
220 uint32_t subfileType = 0; // Main image
221 BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env,
222 TAG_NEWSUBFILETYPE);
223 }
224
225 {
226 // Set bits per sample
227 uint16_t bits = static_cast<uint16_t>(bitsPerSample);
228 BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
229 TAG_BITSPERSAMPLE);
230 }
231
232 {
233 // Set compression
234 uint16_t compression = 1; // None
235 BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env,
236 TAG_COMPRESSION);
237 }
238
239 {
240 // Set dimensions
241 camera_metadata_entry entry =
242 characteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
243 BAIL_IF_EMPTY(entry, env, TAG_IMAGEWIDTH);
244 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
245 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
246 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &width, TIFF_IFD_0), env,
247 TAG_IMAGEWIDTH);
248 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &height, TIFF_IFD_0), env,
249 TAG_IMAGELENGTH);
250 imageWidth = width;
251 imageHeight = height;
252 }
253
254 {
255 // Set photometric interpretation
256 uint16_t interpretation = 32803;
257 BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation,
258 TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION);
259 }
260
261 {
262 // Set blacklevel tags
263 camera_metadata_entry entry =
264 characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
265 BAIL_IF_EMPTY(entry, env, TAG_BLACKLEVEL);
266 const uint32_t* blackLevel = reinterpret_cast<const uint32_t*>(entry.data.i32);
267 BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVEL, entry.count, blackLevel, TIFF_IFD_0), env,
268 TAG_BLACKLEVEL);
269
270 uint16_t repeatDim[2] = {2, 2};
271 BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim, TIFF_IFD_0), env,
272 TAG_BLACKLEVELREPEATDIM);
273 }
274
275 {
276 // Set samples per pixel
277 uint16_t samples = static_cast<uint16_t>(samplesPerPixel);
278 BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
279 env, TAG_SAMPLESPERPIXEL);
280 }
281
282 {
283 // Set planar configuration
284 uint16_t config = 1; // Chunky
285 BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0),
286 env, TAG_PLANARCONFIGURATION);
287 }
288
289 {
290 // Set CFA pattern dimensions
291 uint16_t repeatDim[2] = {2, 2};
292 BAIL_IF_INVALID(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim, TIFF_IFD_0),
293 env, TAG_CFAREPEATPATTERNDIM);
294 }
295
296 {
297 // Set CFA pattern
298 camera_metadata_entry entry =
299 characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
300 BAIL_IF_EMPTY(entry, env, TAG_CFAPATTERN);
301 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
302 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
303 entry.data.u8[0]);
304 switch(cfa) {
305 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
306 uint8_t cfa[4] = {0, 1, 1, 2};
307 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
308 env, TAG_CFAPATTERN);
309 opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
310 break;
311 }
312 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
313 uint8_t cfa[4] = {1, 0, 2, 1};
314 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
315 env, TAG_CFAPATTERN);
316 opcodeCfaLayout = OpcodeListBuilder::CFA_GRBG;
317 break;
318 }
319 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
320 uint8_t cfa[4] = {1, 2, 0, 1};
321 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
322 env, TAG_CFAPATTERN);
323 opcodeCfaLayout = OpcodeListBuilder::CFA_GBRG;
324 break;
325 }
326 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
327 uint8_t cfa[4] = {2, 1, 1, 0};
328 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
329 env, TAG_CFAPATTERN);
330 opcodeCfaLayout = OpcodeListBuilder::CFA_BGGR;
331 break;
332 }
333 default: {
334 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
335 "Invalid metadata for tag %d", TAG_CFAPATTERN);
336 return;
337 }
338 }
339 }
340
341 {
342 // Set CFA plane color
343 uint8_t cfaPlaneColor[3] = {0, 1, 2};
344 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor, TIFF_IFD_0),
345 env, TAG_CFAPLANECOLOR);
346 }
347
348 {
349 // Set CFA layout
350 uint16_t cfaLayout = 1;
351 BAIL_IF_INVALID(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
352 env, TAG_CFALAYOUT);
353 }
354
355 {
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700356 // image description
357 uint8_t imageDescription = '\0'; // empty
358 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEDESCRIPTION, 1, &imageDescription, TIFF_IFD_0),
359 env, TAG_IMAGEDESCRIPTION);
360 }
361
362 {
363 // make
364 char manufacturer[PROPERTY_VALUE_MAX];
365
366 // Use "" to represent unknown make as suggested in TIFF/EP spec.
367 property_get("ro.product.manufacturer", manufacturer, "");
368 uint32_t count = static_cast<uint32_t>(strlen(manufacturer)) + 1;
369
370 BAIL_IF_INVALID(writer->addEntry(TAG_MAKE, count, reinterpret_cast<uint8_t*>(manufacturer),
371 TIFF_IFD_0), env, TAG_MAKE);
372 }
373
374 {
375 // model
376 char model[PROPERTY_VALUE_MAX];
377
378 // Use "" to represent unknown model as suggested in TIFF/EP spec.
379 property_get("ro.product.model", model, "");
380 uint32_t count = static_cast<uint32_t>(strlen(model)) + 1;
381
382 BAIL_IF_INVALID(writer->addEntry(TAG_MODEL, count, reinterpret_cast<uint8_t*>(model),
383 TIFF_IFD_0), env, TAG_MODEL);
384 }
385
386 {
387 // x resolution
388 uint32_t xres[] = { 72, 1 }; // default 72 ppi
389 BAIL_IF_INVALID(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
390 env, TAG_XRESOLUTION);
391
392 // y resolution
393 uint32_t yres[] = { 72, 1 }; // default 72 ppi
394 BAIL_IF_INVALID(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
395 env, TAG_YRESOLUTION);
396
397 uint16_t unit = 2; // inches
398 BAIL_IF_INVALID(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
399 env, TAG_RESOLUTIONUNIT);
400 }
401
402 {
403 // software
404 char software[PROPERTY_VALUE_MAX];
405 property_get("ro.build.fingerprint", software, "");
406 uint32_t count = static_cast<uint32_t>(strlen(software)) + 1;
407 BAIL_IF_INVALID(writer->addEntry(TAG_SOFTWARE, count, reinterpret_cast<uint8_t*>(software),
408 TIFF_IFD_0), env, TAG_SOFTWARE);
409 }
410
411 {
412 // datetime
413 const size_t DATETIME_COUNT = 20;
414 const char* captureTime = env->GetStringUTFChars(formattedCaptureTime, NULL);
415
416 size_t len = strlen(captureTime) + 1;
417 if (len != DATETIME_COUNT) {
418 jniThrowException(env, "java/lang/IllegalArgumentException",
419 "Timestamp string length is not required 20 characters");
420 return;
421 }
422
423 BAIL_IF_INVALID(writer->addEntry(TAG_DATETIME, DATETIME_COUNT,
424 reinterpret_cast<const uint8_t*>(captureTime), TIFF_IFD_0), env, TAG_DATETIMEORIGINAL);
425
426 // datetime original
427 BAIL_IF_INVALID(writer->addEntry(TAG_DATETIMEORIGINAL, DATETIME_COUNT,
428 reinterpret_cast<const uint8_t*>(captureTime), TIFF_IFD_0), env, TAG_DATETIMEORIGINAL);
429 env->ReleaseStringUTFChars(formattedCaptureTime, captureTime);
430 }
431
432 {
433 // TIFF/EP standard id
434 uint8_t standardId[] = { 1, 0, 0, 0 };
435 BAIL_IF_INVALID(writer->addEntry(TAG_TIFFEPSTANDARDID, 4, standardId,
436 TIFF_IFD_0), env, TAG_TIFFEPSTANDARDID);
437 }
438
439 {
440 // copyright
441 uint8_t copyright = '\0'; // empty
442 BAIL_IF_INVALID(writer->addEntry(TAG_COPYRIGHT, 1, &copyright,
443 TIFF_IFD_0), env, TAG_COPYRIGHT);
444 }
445
446 {
447 // exposure time
448 camera_metadata_entry entry =
449 results.find(ANDROID_SENSOR_EXPOSURE_TIME);
450 BAIL_IF_EMPTY(entry, env, TAG_EXPOSURETIME);
451
452 int64_t exposureTime = *(entry.data.i64);
453
454 if (exposureTime < 0) {
455 // Should be unreachable
456 jniThrowException(env, "java/lang/IllegalArgumentException",
457 "Negative exposure time in metadata");
458 return;
459 }
460
461 // Ensure exposure time doesn't overflow (for exposures > 4s)
462 uint32_t denominator = 1000000000;
463 while (exposureTime > UINT32_MAX) {
464 exposureTime >>= 1;
465 denominator >>= 1;
466 if (denominator == 0) {
467 // Should be unreachable
468 jniThrowException(env, "java/lang/IllegalArgumentException",
469 "Exposure time too long");
470 return;
471 }
472 }
473
474 uint32_t exposure[] = { static_cast<uint32_t>(exposureTime), denominator };
475 BAIL_IF_INVALID(writer->addEntry(TAG_EXPOSURETIME, 1, exposure,
476 TIFF_IFD_0), env, TAG_EXPOSURETIME);
477
478 }
479
480 {
481 // ISO speed ratings
482 camera_metadata_entry entry =
483 results.find(ANDROID_SENSOR_SENSITIVITY);
484 BAIL_IF_EMPTY(entry, env, TAG_ISOSPEEDRATINGS);
485
486 int32_t tempIso = *(entry.data.i32);
487 if (tempIso < 0) {
488 jniThrowException(env, "java/lang/IllegalArgumentException",
489 "Negative ISO value");
490 return;
491 }
492
493 if (tempIso > UINT16_MAX) {
494 ALOGW("%s: ISO value overflows UINT16_MAX, clamping to max", __FUNCTION__);
495 tempIso = UINT16_MAX;
496 }
497
498 uint16_t iso = static_cast<uint16_t>(tempIso);
499 BAIL_IF_INVALID(writer->addEntry(TAG_ISOSPEEDRATINGS, 1, &iso,
500 TIFF_IFD_0), env, TAG_ISOSPEEDRATINGS);
501 }
502
503 {
504 // focal length
505 camera_metadata_entry entry =
506 results.find(ANDROID_LENS_FOCAL_LENGTH);
507 BAIL_IF_EMPTY(entry, env, TAG_FOCALLENGTH);
508
509 uint32_t focalLength[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
510 BAIL_IF_INVALID(writer->addEntry(TAG_FOCALLENGTH, 1, focalLength,
511 TIFF_IFD_0), env, TAG_FOCALLENGTH);
512 }
513
514 {
515 // f number
516 camera_metadata_entry entry =
517 results.find(ANDROID_LENS_APERTURE);
518 BAIL_IF_EMPTY(entry, env, TAG_FNUMBER);
519
520 uint32_t fnum[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
521 BAIL_IF_INVALID(writer->addEntry(TAG_FNUMBER, 1, fnum,
522 TIFF_IFD_0), env, TAG_FNUMBER);
523 }
524
525 {
Ruben Brunkf967a542014-04-28 16:31:11 -0700526 // Set DNG version information
527 uint8_t version[4] = {1, 4, 0, 0};
528 BAIL_IF_INVALID(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
529 env, TAG_DNGVERSION);
530
531 uint8_t backwardVersion[4] = {1, 1, 0, 0};
532 BAIL_IF_INVALID(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion, TIFF_IFD_0),
533 env, TAG_DNGBACKWARDVERSION);
534 }
535
536 {
537 // Set whitelevel
538 camera_metadata_entry entry =
539 characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
540 BAIL_IF_EMPTY(entry, env, TAG_WHITELEVEL);
541 uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]);
542 BAIL_IF_INVALID(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0), env,
543 TAG_WHITELEVEL);
544 }
545
546 {
547 // Set default scale
548 uint32_t defaultScale[4] = {1, 1, 1, 1};
549 BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale, TIFF_IFD_0),
550 env, TAG_DEFAULTSCALE);
551 }
552
553 bool singleIlluminant = false;
554 {
555 // Set calibration illuminants
556 camera_metadata_entry entry1 =
557 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
558 BAIL_IF_EMPTY(entry1, env, TAG_CALIBRATIONILLUMINANT1);
559 camera_metadata_entry entry2 =
560 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
561 if (entry2.count == 0) {
562 singleIlluminant = true;
563 }
564 uint16_t ref1 = entry1.data.u8[0];
565
566 BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1,
567 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1);
568
569 if (!singleIlluminant) {
570 uint16_t ref2 = entry2.data.u8[0];
571 BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
572 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2);
573 }
574 }
575
576 {
577 // Set color transforms
578 camera_metadata_entry entry1 =
579 characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
580 BAIL_IF_EMPTY(entry1, env, TAG_COLORMATRIX1);
581
582 int32_t colorTransform1[entry1.count * 2];
583
584 size_t ctr = 0;
585 for(size_t i = 0; i < entry1.count; ++i) {
586 colorTransform1[ctr++] = entry1.data.r[i].numerator;
587 colorTransform1[ctr++] = entry1.data.r[i].denominator;
588 }
589
590 BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX1, entry1.count, colorTransform1, TIFF_IFD_0),
591 env, TAG_COLORMATRIX1);
592
593 if (!singleIlluminant) {
594 camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
595 BAIL_IF_EMPTY(entry2, env, TAG_COLORMATRIX2);
596 int32_t colorTransform2[entry2.count * 2];
597
598 ctr = 0;
599 for(size_t i = 0; i < entry2.count; ++i) {
600 colorTransform2[ctr++] = entry2.data.r[i].numerator;
601 colorTransform2[ctr++] = entry2.data.r[i].denominator;
602 }
603
604 BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX2, entry2.count, colorTransform2, TIFF_IFD_0),
605 env, TAG_COLORMATRIX2);
606 }
607 }
608
609 {
610 // Set calibration transforms
611 camera_metadata_entry entry1 =
612 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
613 BAIL_IF_EMPTY(entry1, env, TAG_CAMERACALIBRATION1);
614
615 int32_t calibrationTransform1[entry1.count * 2];
616
617 size_t ctr = 0;
618 for(size_t i = 0; i < entry1.count; ++i) {
619 calibrationTransform1[ctr++] = entry1.data.r[i].numerator;
620 calibrationTransform1[ctr++] = entry1.data.r[i].denominator;
621 }
622
623 BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count, calibrationTransform1,
624 TIFF_IFD_0), env, TAG_CAMERACALIBRATION1);
625
626 if (!singleIlluminant) {
627 camera_metadata_entry entry2 =
628 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
629 BAIL_IF_EMPTY(entry2, env, TAG_CAMERACALIBRATION2);
630 int32_t calibrationTransform2[entry2.count * 2];
631
632 ctr = 0;
633 for(size_t i = 0; i < entry2.count; ++i) {
634 calibrationTransform2[ctr++] = entry2.data.r[i].numerator;
635 calibrationTransform2[ctr++] = entry2.data.r[i].denominator;
636 }
637
638 BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count, calibrationTransform1,
639 TIFF_IFD_0), env, TAG_CAMERACALIBRATION2);
640 }
641 }
642
643 {
644 // Set forward transforms
645 camera_metadata_entry entry1 =
646 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
647 BAIL_IF_EMPTY(entry1, env, TAG_FORWARDMATRIX1);
648
649 int32_t forwardTransform1[entry1.count * 2];
650
651 size_t ctr = 0;
652 for(size_t i = 0; i < entry1.count; ++i) {
653 forwardTransform1[ctr++] = entry1.data.r[i].numerator;
654 forwardTransform1[ctr++] = entry1.data.r[i].denominator;
655 }
656
657 BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count, forwardTransform1,
658 TIFF_IFD_0), env, TAG_FORWARDMATRIX1);
659
660 if (!singleIlluminant) {
661 camera_metadata_entry entry2 =
662 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
663 BAIL_IF_EMPTY(entry2, env, TAG_FORWARDMATRIX2);
664 int32_t forwardTransform2[entry2.count * 2];
665
666 ctr = 0;
667 for(size_t i = 0; i < entry2.count; ++i) {
668 forwardTransform2[ctr++] = entry2.data.r[i].numerator;
669 forwardTransform2[ctr++] = entry2.data.r[i].denominator;
670 }
671
672 BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count, forwardTransform2,
673 TIFF_IFD_0), env, TAG_FORWARDMATRIX2);
674 }
675 }
676
677 {
678 // Set camera neutral
679 camera_metadata_entry entry =
680 results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
681 BAIL_IF_EMPTY(entry, env, TAG_ASSHOTNEUTRAL);
682 uint32_t cameraNeutral[entry.count * 2];
683
684 size_t ctr = 0;
685 for(size_t i = 0; i < entry.count; ++i) {
686 cameraNeutral[ctr++] =
687 static_cast<uint32_t>(entry.data.r[i].numerator);
688 cameraNeutral[ctr++] =
689 static_cast<uint32_t>(entry.data.r[i].denominator);
690 }
691
692 BAIL_IF_INVALID(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral,
693 TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL);
694 }
695
696 {
697 // Setup data strips
698 // TODO: Switch to tiled implementation.
699 uint32_t offset = 0;
700 BAIL_IF_INVALID(writer->addEntry(TAG_STRIPOFFSETS, 1, &offset, TIFF_IFD_0), env,
701 TAG_STRIPOFFSETS);
702
703 BAIL_IF_INVALID(writer->addEntry(TAG_ROWSPERSTRIP, 1, &imageHeight, TIFF_IFD_0), env,
704 TAG_ROWSPERSTRIP);
705
706 uint32_t byteCount = imageWidth * imageHeight * bitsPerSample * samplesPerPixel /
707 bitsPerByte;
708 BAIL_IF_INVALID(writer->addEntry(TAG_STRIPBYTECOUNTS, 1, &byteCount, TIFF_IFD_0), env,
709 TAG_STRIPBYTECOUNTS);
710 }
711
712 {
713 // Setup default crop + crop origin tags
714 uint32_t margin = 8; // Default margin recommended by Adobe for interpolation.
715 uint32_t dimensionLimit = 128; // Smallest image dimension crop margin from.
716 if (imageWidth >= dimensionLimit && imageHeight >= dimensionLimit) {
717 uint32_t defaultCropOrigin[] = {margin, margin};
718 uint32_t defaultCropSize[] = {imageWidth - margin, imageHeight - margin};
719 BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPORIGIN, 2, defaultCropOrigin,
720 TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN);
721 BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize,
722 TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE);
723 }
724 }
725
726 {
727 // Setup unique camera model tag
728 char model[PROPERTY_VALUE_MAX];
729 property_get("ro.product.model", model, "");
730
731 char manufacturer[PROPERTY_VALUE_MAX];
732 property_get("ro.product.manufacturer", manufacturer, "");
733
734 char brand[PROPERTY_VALUE_MAX];
735 property_get("ro.product.brand", brand, "");
736
737 String8 cameraModel(model);
738 cameraModel += "-";
739 cameraModel += manufacturer;
740 cameraModel += "-";
741 cameraModel += brand;
742
743 BAIL_IF_INVALID(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
744 reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
745 TAG_UNIQUECAMERAMODEL);
746 }
747
748 {
749 // Setup opcode List 2
750 camera_metadata_entry entry1 =
751 characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
752 BAIL_IF_EMPTY(entry1, env, TAG_OPCODELIST2);
753 uint32_t lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
754 uint32_t lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
755
756 camera_metadata_entry entry2 =
757 results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
758 BAIL_IF_EMPTY(entry2, env, TAG_OPCODELIST2);
759 if (entry2.count == lsmWidth * lsmHeight * 4) {
760
761 OpcodeListBuilder builder;
762 status_t err = builder.addGainMapsForMetadata(lsmWidth,
763 lsmHeight,
764 0,
765 0,
766 imageHeight,
767 imageWidth,
768 opcodeCfaLayout,
769 entry2.data.f);
770 if (err == OK) {
771 size_t listSize = builder.getSize();
772 uint8_t opcodeListBuf[listSize];
773 err = builder.buildOpList(opcodeListBuf);
774 if (err == OK) {
775 BAIL_IF_INVALID(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf,
776 TIFF_IFD_0), env, TAG_OPCODELIST2);
777 } else {
778 ALOGE("%s: Could not build Lens shading map opcode.", __FUNCTION__);
779 jniThrowRuntimeException(env, "failed to construct lens shading map opcode.");
780 }
781 } else {
782 ALOGE("%s: Could not add Lens shading map.", __FUNCTION__);
783 jniThrowRuntimeException(env, "failed to add lens shading map.");
784 }
785 } else {
786 ALOGW("%s: Lens shading map not present in results, skipping...", __FUNCTION__);
787 }
788 }
789
790 DngCreator_setCreator(env, thiz, writer);
791}
792
793static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
794 ALOGV("%s:", __FUNCTION__);
795 DngCreator_setCreator(env, thiz, NULL);
796}
797
798static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz) {
799 ALOGV("%s:", __FUNCTION__);
800 jniThrowRuntimeException(env, "nativeSetOrientation is not implemented");
801}
802
803static void DngCreator_nativeSetThumbnailBitmap(JNIEnv* env, jobject thiz, jobject bitmap) {
804 ALOGV("%s:", __FUNCTION__);
805 jniThrowRuntimeException(env, "nativeSetThumbnailBitmap is not implemented");
806}
807
808static void DngCreator_nativeSetThumbnailImage(JNIEnv* env, jobject thiz, jint width, jint height,
809 jobject yBuffer, jint yRowStride, jint yPixStride, jobject uBuffer, jint uRowStride,
810 jint uPixStride, jobject vBuffer, jint vRowStride, jint vPixStride) {
811 ALOGV("%s:", __FUNCTION__);
812 jniThrowRuntimeException(env, "nativeSetThumbnailImage is not implemented");
813}
814
815static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
816 jint height, jobject inBuffer, jint rowStride, jint pixStride) {
817 ALOGV("%s:", __FUNCTION__);
818
819 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
820 if(env->ExceptionCheck()) {
821 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
822 return;
823 }
824
825 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
826 if (pixelBytes == NULL) {
827 ALOGE("%s: Could not get native byte buffer", __FUNCTION__);
828 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid bytebuffer");
829 return;
830 }
831
832 TiffWriter* writer = DngCreator_getCreator(env, thiz);
833 if (writer == NULL) {
834 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
835 jniThrowException(env, "java/lang/AssertionError",
836 "Write called with uninitialized DngCreator");
837 return;
838 }
839 // TODO: handle lens shading map, etc. conversions for other raw buffer sizes.
840 uint32_t metadataWidth = *(writer->getEntry(TAG_IMAGEWIDTH, TIFF_IFD_0)->getData<uint32_t>());
841 uint32_t metadataHeight = *(writer->getEntry(TAG_IMAGELENGTH, TIFF_IFD_0)->getData<uint32_t>());
842 if (metadataWidth != width) {
843 jniThrowExceptionFmt(env, "java/lang/IllegalStateException", \
844 "Metadata width %d doesn't match image width %d", metadataWidth, width);
845 return;
846 }
847
848 if (metadataHeight != height) {
849 jniThrowExceptionFmt(env, "java/lang/IllegalStateException", \
850 "Metadata height %d doesn't match image height %d", metadataHeight, height);
851 return;
852 }
853
854 uint32_t stripOffset = writer->getTotalSize();
855
856 BAIL_IF_INVALID(writer->addEntry(TAG_STRIPOFFSETS, 1, &stripOffset, TIFF_IFD_0), env,
857 TAG_STRIPOFFSETS);
858
859 if (writer->write(out.get()) != OK) {
860 if (!env->ExceptionCheck()) {
861 jniThrowException(env, "java/io/IOException", "Failed to write metadata");
862 }
863 return;
864 }
865
866 size_t fullSize = rowStride * height;
867 jlong capacity = env->GetDirectBufferCapacity(inBuffer);
868 if (capacity < 0 || fullSize > capacity) {
869 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
870 "Invalid size %d for Image, size given in metadata is %d at current stride",
871 capacity, fullSize);
872 return;
873 }
874
875 if (pixStride == BYTES_PER_SAMPLE && rowStride == width * BYTES_PER_SAMPLE) {
876 if (out->write(pixelBytes, 0, fullSize) != OK || env->ExceptionCheck()) {
877 if (!env->ExceptionCheck()) {
878 jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
879 }
880 return;
881 }
882 } else if (pixStride == BYTES_PER_SAMPLE) {
883 for (size_t i = 0; i < height; ++i) {
884 if (out->write(pixelBytes, i * rowStride, pixStride * width) != OK ||
885 env->ExceptionCheck()) {
886 if (!env->ExceptionCheck()) {
887 jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
888 }
889 return;
890 }
891 }
892 } else {
893 for (size_t i = 0; i < height; ++i) {
894 for (size_t j = 0; j < width; ++j) {
895 if (out->write(pixelBytes, i * rowStride + j * pixStride,
896 BYTES_PER_SAMPLE) != OK || !env->ExceptionCheck()) {
897 if (env->ExceptionCheck()) {
898 jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
899 }
900 return;
901 }
902 }
903 }
904 }
905
906}
907
908static void DngCreator_nativeWriteByteBuffer(JNIEnv* env, jobject thiz, jobject outStream,
909 jobject rawBuffer, jlong offset) {
910 ALOGV("%s:", __FUNCTION__);
911 jniThrowRuntimeException(env, "nativeWriteByteBuffer is not implemented.");
912}
913
914static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
915 jobject inStream, jlong offset) {
916 ALOGV("%s:", __FUNCTION__);
917 jniThrowRuntimeException(env, "nativeWriteInputStream is not implemented.");
918}
919
920} /*extern "C" */
921
922static JNINativeMethod gDngCreatorMethods[] = {
923 {"nativeClassInit", "()V", (void*) DngCreator_nativeClassInit},
924 {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700925 "Landroid/hardware/camera2/impl/CameraMetadataNative;Ljava/lang/String;)V",
926 (void*) DngCreator_init},
Ruben Brunkf967a542014-04-28 16:31:11 -0700927 {"nativeDestroy", "()V", (void*) DngCreator_destroy},
928 {"nativeSetOrientation", "(I)V", (void*) DngCreator_nativeSetOrientation},
929 {"nativeSetThumbnailBitmap","(Landroid/graphics/Bitmap;)V",
930 (void*) DngCreator_nativeSetThumbnailBitmap},
931 {"nativeSetThumbnailImage",
932 "(IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;II)V",
933 (void*) DngCreator_nativeSetThumbnailImage},
934 {"nativeWriteImage", "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;II)V",
935 (void*) DngCreator_nativeWriteImage},
936 {"nativeWriteByteBuffer", "(Ljava/io/OutputStream;Ljava/nio/ByteBuffer;J)V",
937 (void*) DngCreator_nativeWriteByteBuffer},
938 {"nativeWriteInputStream", "(Ljava/io/OutputStream;Ljava/io/InputStream;J)V",
939 (void*) DngCreator_nativeWriteInputStream},
940};
941
Ruben Brunkb6079002014-05-22 12:33:54 -0700942int register_android_hardware_camera2_DngCreator(JNIEnv *env) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700943 return AndroidRuntime::registerNativeMethods(env,
Ruben Brunkb6079002014-05-22 12:33:54 -0700944 "android/hardware/camera2/DngCreator", gDngCreatorMethods,
945 NELEM(gDngCreatorMethods));
Ruben Brunkf967a542014-04-28 16:31:11 -0700946}