android_hardware_camera2_DngCreator.cpp revision b6079005ed0631c3972ff427f56e12523ec214a7
1/*
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_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_DNGCREATOR_CTX_JNI_ID, "J");
167    LOG_ALWAYS_FATAL_IF(gDngCreatorClassInfo.mNativeContext == NULL,
168            "can't find android/hardware/camera2/DngCreator.%s",
169            ANDROID_DNGCREATOR_CTX_JNI_ID);
170
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
770int register_android_hardware_camera2_DngCreator(JNIEnv *env) {
771    return AndroidRuntime::registerNativeMethods(env,
772                   "android/hardware/camera2/DngCreator", gDngCreatorMethods,
773                   NELEM(gDngCreatorMethods));
774}
775