1f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk/*
2f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk * Copyright 2014 The Android Open Source Project
3f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk *
4f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk * Licensed under the Apache License, Version 2.0 (the "License");
5f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk * you may not use this file except in compliance with the License.
6f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk * You may obtain a copy of the License at
7f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk *
8f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk *      http://www.apache.org/licenses/LICENSE-2.0
9f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk *
10f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk * Unless required by applicable law or agreed to in writing, software
11f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk * distributed under the License is distributed on an "AS IS" BASIS,
12f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk * See the License for the specific language governing permissions and
14f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk * limitations under the License.
15f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk */
16f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
178c35d5be63ab1811679875f12b19b050ac163b18Eino-Ville Talvala//#define LOG_NDEBUG 0
18f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk#define LOG_TAG "DngCreator_JNI"
1946d8444631b4b1253a76bfcc78a29d26014d022fDan Albert#include <inttypes.h>
2046d8444631b4b1253a76bfcc78a29d26014d022fDan Albert#include <string.h>
21b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk#include <algorithm>
22b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk#include <memory>
239ce22a095685a581823316457217a318fb40c754Ruben Brunk#include <vector>
247ca1371987736e8b3c51818afcbd245ca6286bc0Emilian Peev#include <cmath>
2546d8444631b4b1253a76bfcc78a29d26014d022fDan Albert
2646d8444631b4b1253a76bfcc78a29d26014d022fDan Albert#include <utils/Log.h>
2746d8444631b4b1253a76bfcc78a29d26014d022fDan Albert#include <utils/Errors.h>
2846d8444631b4b1253a76bfcc78a29d26014d022fDan Albert#include <utils/StrongPointer.h>
2946d8444631b4b1253a76bfcc78a29d26014d022fDan Albert#include <utils/RefBase.h>
3046d8444631b4b1253a76bfcc78a29d26014d022fDan Albert#include <utils/Vector.h>
312079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk#include <utils/String8.h>
3246d8444631b4b1253a76bfcc78a29d26014d022fDan Albert#include <cutils/properties.h>
33f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk#include <system/camera_metadata.h>
34f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk#include <camera/CameraMetadata.h>
35f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk#include <img_utils/DngUtils.h>
36f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk#include <img_utils/TagDefinitions.h>
37f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk#include <img_utils/TiffIfd.h>
38f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk#include <img_utils/TiffWriter.h>
39f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk#include <img_utils/Output.h>
4047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk#include <img_utils/Input.h>
4147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk#include <img_utils/StripSource.h>
42f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
43ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe#include "core_jni_helpers.h"
44b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
45f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk#include "android_runtime/AndroidRuntime.h"
46f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk#include "android_runtime/android_hardware_camera2_CameraMetadata.h"
47f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
48f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk#include <jni.h>
492279b2534272282a5b5152723235da397e49195cSteven Moreland#include <nativehelper/JNIHelp.h>
50f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
51f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunkusing namespace android;
52f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunkusing namespace img_utils;
53f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
542079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk#define BAIL_IF_INVALID_RET_BOOL(expr, jnienv, tagId, writer) \
55f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    if ((expr) != OK) { \
56f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
5747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
582079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        return false; \
59f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
60f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
612079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
622079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk#define BAIL_IF_INVALID_RET_NULL_SP(expr, jnienv, tagId, writer) \
632079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    if ((expr) != OK) { \
642079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
652079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
662079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        return nullptr; \
672079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    }
682079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
692079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
70b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk#define BAIL_IF_INVALID_R(expr, jnienv, tagId, writer) \
71b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    if ((expr) != OK) { \
72b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk        jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
73b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk                "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
74b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk        return -1; \
75b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    }
76b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk
772079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk#define BAIL_IF_EMPTY_RET_NULL_SP(entry, jnienv, tagId, writer) \
783c22e00fb9e1574bee2ce96c57cca3a62d8aaa05Chih-Hung Hsieh    if ((entry).count == 0) { \
79f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
8047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                "Missing metadata fields for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
812079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        return nullptr; \
82f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
83f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
848069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala#define BAIL_IF_EXPR_RET_NULL_SP(expr, jnienv, tagId, writer) \
858069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala    if (expr) { \
868069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala        jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
878069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala                "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
888069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala        return nullptr; \
898069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala    }
908069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala
912079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
92b6079005ed0631c3972ff427f56e12523ec214a7Ruben Brunk#define ANDROID_DNGCREATOR_CTX_JNI_ID     "mNativeContext"
93f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
94f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunkstatic struct {
95f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    jfieldID mNativeContext;
96f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk} gDngCreatorClassInfo;
97f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
98f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunkstatic struct {
99f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    jmethodID mWriteMethod;
100f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk} gOutputStreamClassInfo;
101f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
10247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkstatic struct {
10347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    jmethodID mReadMethod;
10447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    jmethodID mSkipMethod;
10547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk} gInputStreamClassInfo;
10647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
10747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkstatic struct {
10847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    jmethodID mGetMethod;
10947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk} gInputByteBufferClassInfo;
11047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
111f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunkenum {
112f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    BITS_PER_SAMPLE = 16,
113f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    BYTES_PER_SAMPLE = 2,
11447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    BYTES_PER_RGB_PIXEL = 3,
11547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    BITS_PER_RGB_SAMPLE = 8,
11647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    BYTES_PER_RGB_SAMPLE = 1,
11747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    SAMPLES_PER_RGB_PIXEL = 3,
11847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    SAMPLES_PER_RAW_PIXEL = 1,
11947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    TIFF_IFD_0 = 0,
12047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    TIFF_IFD_SUB1 = 1,
12147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    TIFF_IFD_GPSINFO = 2,
122f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk};
123f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1242079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
1252079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk/**
1262079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk * POD container class for GPS tag data.
1272079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk */
1282079612e5851d73f4672ae3729c883a58adc4dddRuben Brunkclass GpsData {
1292079612e5851d73f4672ae3729c883a58adc4dddRuben Brunkpublic:
1302079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    enum {
1312079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        GPS_VALUE_LENGTH = 6,
1322079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        GPS_REF_LENGTH = 2,
1332079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        GPS_DATE_LENGTH = 11,
1342079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    };
1352079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
1362079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    uint32_t mLatitude[GPS_VALUE_LENGTH];
1372079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    uint32_t mLongitude[GPS_VALUE_LENGTH];
1382079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    uint32_t mTimestamp[GPS_VALUE_LENGTH];
1392079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    uint8_t mLatitudeRef[GPS_REF_LENGTH];
1402079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    uint8_t mLongitudeRef[GPS_REF_LENGTH];
1412079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    uint8_t mDate[GPS_DATE_LENGTH];
1422079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk};
1432079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
144f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk// ----------------------------------------------------------------------------
145f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
14647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk/**
14747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk * Container class for the persistent native context.
14847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk */
14947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
15047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkclass NativeContext : public LightRefBase<NativeContext> {
15147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkpublic:
1522079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    enum {
1532079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        DATETIME_COUNT = 20,
1542079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    };
1552079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
156b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    NativeContext(const CameraMetadata& characteristics, const CameraMetadata& result);
15747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    virtual ~NativeContext();
15847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
15947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    TiffWriter* getWriter();
16047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
161b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    std::shared_ptr<const CameraMetadata> getCharacteristics() const;
162b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    std::shared_ptr<const CameraMetadata> getResult() const;
163b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk
1642079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    uint32_t getThumbnailWidth() const;
1652079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    uint32_t getThumbnailHeight() const;
1662079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    const uint8_t* getThumbnail() const;
1672079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    bool hasThumbnail() const;
16847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
16947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    bool setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height);
17047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
1712079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    void setOrientation(uint16_t orientation);
1722079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    uint16_t getOrientation() const;
1732079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
1742079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    void setDescription(const String8& desc);
1752079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    String8 getDescription() const;
1762079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    bool hasDescription() const;
1772079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
1782079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    void setGpsData(const GpsData& data);
1792079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    GpsData getGpsData() const;
1802079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    bool hasGpsData() const;
1812079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
1822079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    void setCaptureTime(const String8& formattedCaptureTime);
1832079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    String8 getCaptureTime() const;
1842079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    bool hasCaptureTime() const;
1852079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
18647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkprivate:
18747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    Vector<uint8_t> mCurrentThumbnail;
18847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    TiffWriter mWriter;
189b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    std::shared_ptr<CameraMetadata> mCharacteristics;
190b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    std::shared_ptr<CameraMetadata> mResult;
19147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t mThumbnailWidth;
19247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t mThumbnailHeight;
1932079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    uint16_t mOrientation;
1942079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    bool mThumbnailSet;
1952079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    bool mGpsSet;
1962079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    bool mDescriptionSet;
1972079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    bool mCaptureTimeSet;
1982079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    String8 mDescription;
1992079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    GpsData mGpsData;
2002079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    String8 mFormattedCaptureTime;
20147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk};
20247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
203b8f4c6ab1e99a44a51af26dc522819bb833825abRuben BrunkNativeContext::NativeContext(const CameraMetadata& characteristics, const CameraMetadata& result) :
204b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk        mCharacteristics(std::make_shared<CameraMetadata>(characteristics)),
205b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk        mResult(std::make_shared<CameraMetadata>(result)), mThumbnailWidth(0),
2068069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala        mThumbnailHeight(0), mOrientation(TAG_ORIENTATION_UNKNOWN), mThumbnailSet(false),
2078069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala        mGpsSet(false), mDescriptionSet(false), mCaptureTimeSet(false) {}
20847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
20947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben BrunkNativeContext::~NativeContext() {}
21047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
21147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben BrunkTiffWriter* NativeContext::getWriter() {
21247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    return &mWriter;
21347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk}
21447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
215b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunkstd::shared_ptr<const CameraMetadata> NativeContext::getCharacteristics() const {
216b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    return mCharacteristics;
217b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk}
218b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk
219b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunkstd::shared_ptr<const CameraMetadata> NativeContext::getResult() const {
220b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    return mResult;
221b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk}
222b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk
2232079612e5851d73f4672ae3729c883a58adc4dddRuben Brunkuint32_t NativeContext::getThumbnailWidth() const {
22447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    return mThumbnailWidth;
22547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk}
22647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
2272079612e5851d73f4672ae3729c883a58adc4dddRuben Brunkuint32_t NativeContext::getThumbnailHeight() const {
22847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    return mThumbnailHeight;
22947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk}
23047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
2312079612e5851d73f4672ae3729c883a58adc4dddRuben Brunkconst uint8_t* NativeContext::getThumbnail() const {
23247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    return mCurrentThumbnail.array();
23347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk}
23447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
2352079612e5851d73f4672ae3729c883a58adc4dddRuben Brunkbool NativeContext::hasThumbnail() const {
2362079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    return mThumbnailSet;
2372079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk}
2382079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
23947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkbool NativeContext::setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height) {
24047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    mThumbnailWidth = width;
24147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    mThumbnailHeight = height;
24247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
24347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    size_t size = BYTES_PER_RGB_PIXEL * width * height;
24447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    if (mCurrentThumbnail.resize(size) < 0) {
24547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        ALOGE("%s: Could not resize thumbnail buffer.", __FUNCTION__);
24647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        return false;
24747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
24847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
24947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint8_t* thumb = mCurrentThumbnail.editArray();
25047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    memcpy(thumb, buffer, size);
2512079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    mThumbnailSet = true;
25247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    return true;
25347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk}
25447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
2552079612e5851d73f4672ae3729c883a58adc4dddRuben Brunkvoid NativeContext::setOrientation(uint16_t orientation) {
2562079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    mOrientation = orientation;
2572079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk}
2582079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
2592079612e5851d73f4672ae3729c883a58adc4dddRuben Brunkuint16_t NativeContext::getOrientation() const {
2602079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    return mOrientation;
2612079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk}
2622079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
2632079612e5851d73f4672ae3729c883a58adc4dddRuben Brunkvoid NativeContext::setDescription(const String8& desc) {
2642079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    mDescription = desc;
2652079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    mDescriptionSet = true;
2662079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk}
2672079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
2682079612e5851d73f4672ae3729c883a58adc4dddRuben BrunkString8 NativeContext::getDescription() const {
2692079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    return mDescription;
2702079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk}
2712079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
2722079612e5851d73f4672ae3729c883a58adc4dddRuben Brunkbool NativeContext::hasDescription() const {
2732079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    return mDescriptionSet;
2742079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk}
2752079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
2762079612e5851d73f4672ae3729c883a58adc4dddRuben Brunkvoid NativeContext::setGpsData(const GpsData& data) {
2772079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    mGpsData = data;
2782079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    mGpsSet = true;
2792079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk}
2802079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
2812079612e5851d73f4672ae3729c883a58adc4dddRuben BrunkGpsData NativeContext::getGpsData() const {
2822079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    return mGpsData;
2832079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk}
2842079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
2852079612e5851d73f4672ae3729c883a58adc4dddRuben Brunkbool NativeContext::hasGpsData() const {
2862079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    return mGpsSet;
2872079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk}
2882079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
2892079612e5851d73f4672ae3729c883a58adc4dddRuben Brunkvoid NativeContext::setCaptureTime(const String8& formattedCaptureTime) {
2902079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    mFormattedCaptureTime = formattedCaptureTime;
2912079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    mCaptureTimeSet = true;
2922079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk}
2932079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
2942079612e5851d73f4672ae3729c883a58adc4dddRuben BrunkString8 NativeContext::getCaptureTime() const {
2952079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    return mFormattedCaptureTime;
2962079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk}
2972079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
2982079612e5851d73f4672ae3729c883a58adc4dddRuben Brunkbool NativeContext::hasCaptureTime() const {
2992079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    return mCaptureTimeSet;
3002079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk}
3012079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
30247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk// End of NativeContext
30347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk// ----------------------------------------------------------------------------
30447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
30547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk/**
30647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk * Wrapper class for a Java OutputStream.
30747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk *
30847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk * This class is not intended to be used across JNI calls.
30947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk */
310f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunkclass JniOutputStream : public Output, public LightRefBase<JniOutputStream> {
311f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunkpublic:
312f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    JniOutputStream(JNIEnv* env, jobject outStream);
313f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
314f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    virtual ~JniOutputStream();
315f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
316f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    status_t open();
31747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
318f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    status_t write(const uint8_t* buf, size_t offset, size_t count);
31947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
320f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    status_t close();
321f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunkprivate:
322f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    enum {
32347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        BYTE_ARRAY_LENGTH = 4096
324f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    };
325f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    jobject mOutputStream;
326f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    JNIEnv* mEnv;
327f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    jbyteArray mByteArray;
328f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk};
329f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
330f967a5486a78db244624fde4c105aa5e6fa914b9Ruben BrunkJniOutputStream::JniOutputStream(JNIEnv* env, jobject outStream) : mOutputStream(outStream),
331f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        mEnv(env) {
332f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
3332079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    if (mByteArray == nullptr) {
334f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
335f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
336f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk}
337f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
338f967a5486a78db244624fde4c105aa5e6fa914b9Ruben BrunkJniOutputStream::~JniOutputStream() {
339f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    mEnv->DeleteLocalRef(mByteArray);
340f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk}
341f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
342f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunkstatus_t JniOutputStream::open() {
343f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    // Do nothing
344f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    return OK;
345f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk}
346f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
347f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunkstatus_t JniOutputStream::write(const uint8_t* buf, size_t offset, size_t count) {
348f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    while(count > 0) {
349f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        size_t len = BYTE_ARRAY_LENGTH;
350f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        len = (count > len) ? len : count;
351f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        mEnv->SetByteArrayRegion(mByteArray, 0, len, reinterpret_cast<const jbyte*>(buf + offset));
352f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
353f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        if (mEnv->ExceptionCheck()) {
354f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            return BAD_VALUE;
355f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        }
356f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
357f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        mEnv->CallVoidMethod(mOutputStream, gOutputStreamClassInfo.mWriteMethod, mByteArray,
358f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk                0, len);
359f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
360f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        if (mEnv->ExceptionCheck()) {
361f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            return BAD_VALUE;
362f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        }
363f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
364f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        count -= len;
365f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        offset += len;
366f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
367f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    return OK;
368f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk}
369f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
370f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunkstatus_t JniOutputStream::close() {
371f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    // Do nothing
372f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    return OK;
373f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk}
374f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
37547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk// End of JniOutputStream
376f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk// ----------------------------------------------------------------------------
377f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
37847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk/**
37947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk * Wrapper class for a Java InputStream.
38047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk *
38147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk * This class is not intended to be used across JNI calls.
38247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk */
38347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkclass JniInputStream : public Input, public LightRefBase<JniInputStream> {
38447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkpublic:
38547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    JniInputStream(JNIEnv* env, jobject inStream);
38647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
38747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    status_t open();
38847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
38947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    status_t close();
39047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
39147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    ssize_t read(uint8_t* buf, size_t offset, size_t count);
39247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
39347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    ssize_t skip(size_t count);
39447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
39547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    virtual ~JniInputStream();
39647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkprivate:
39747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    enum {
39847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        BYTE_ARRAY_LENGTH = 4096
39947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    };
40047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    jobject mInStream;
40147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    JNIEnv* mEnv;
40247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    jbyteArray mByteArray;
40347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
40447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk};
40547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
40647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben BrunkJniInputStream::JniInputStream(JNIEnv* env, jobject inStream) : mInStream(inStream), mEnv(env) {
40747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
4082079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    if (mByteArray == nullptr) {
40947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
41047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
41147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk}
41247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
41347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben BrunkJniInputStream::~JniInputStream() {
41447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    mEnv->DeleteLocalRef(mByteArray);
41547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk}
41647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
41747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkssize_t JniInputStream::read(uint8_t* buf, size_t offset, size_t count) {
41847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
41947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    jint realCount = BYTE_ARRAY_LENGTH;
42047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    if (count < BYTE_ARRAY_LENGTH) {
42147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        realCount = count;
42247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
42347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    jint actual = mEnv->CallIntMethod(mInStream, gInputStreamClassInfo.mReadMethod, mByteArray, 0,
42447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            realCount);
42547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
42647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    if (actual < 0) {
42747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        return NOT_ENOUGH_DATA;
42847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
42947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
43047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    if (mEnv->ExceptionCheck()) {
43147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        return BAD_VALUE;
43247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
43347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
43447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    mEnv->GetByteArrayRegion(mByteArray, 0, actual, reinterpret_cast<jbyte*>(buf + offset));
43547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    if (mEnv->ExceptionCheck()) {
43647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        return BAD_VALUE;
43747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
43847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    return actual;
43947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk}
44047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
44147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkssize_t JniInputStream::skip(size_t count) {
44247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    jlong actual = mEnv->CallLongMethod(mInStream, gInputStreamClassInfo.mSkipMethod,
44347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            static_cast<jlong>(count));
44447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
44547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    if (mEnv->ExceptionCheck()) {
44647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        return BAD_VALUE;
44747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
44847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    if (actual < 0) {
44947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        return NOT_ENOUGH_DATA;
45047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
45147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    return actual;
45247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk}
45347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
45447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkstatus_t JniInputStream::open() {
45547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    // Do nothing
45647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    return OK;
45747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk}
45847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
45947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkstatus_t JniInputStream::close() {
46047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    // Do nothing
46147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    return OK;
46247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk}
46347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
46447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk// End of JniInputStream
46547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk// ----------------------------------------------------------------------------
46647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
46747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk/**
46847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk * Wrapper class for a non-direct Java ByteBuffer.
46947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk *
47047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk * This class is not intended to be used across JNI calls.
47147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk */
47247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkclass JniInputByteBuffer : public Input, public LightRefBase<JniInputByteBuffer> {
47347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkpublic:
47447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    JniInputByteBuffer(JNIEnv* env, jobject inBuf);
47547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
47647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    status_t open();
47747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
47847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    status_t close();
47947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
48047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    ssize_t read(uint8_t* buf, size_t offset, size_t count);
48147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
48247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    virtual ~JniInputByteBuffer();
48347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkprivate:
48447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    enum {
48547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        BYTE_ARRAY_LENGTH = 4096
48647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    };
48747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    jobject mInBuf;
48847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    JNIEnv* mEnv;
48947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    jbyteArray mByteArray;
49047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk};
49147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
49247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben BrunkJniInputByteBuffer::JniInputByteBuffer(JNIEnv* env, jobject inBuf) : mInBuf(inBuf), mEnv(env) {
49347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
4942079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    if (mByteArray == nullptr) {
49547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
49647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
49747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk}
49847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
49947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben BrunkJniInputByteBuffer::~JniInputByteBuffer() {
50047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    mEnv->DeleteLocalRef(mByteArray);
50147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk}
50247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
50347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkssize_t JniInputByteBuffer::read(uint8_t* buf, size_t offset, size_t count) {
50447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    jint realCount = BYTE_ARRAY_LENGTH;
50547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    if (count < BYTE_ARRAY_LENGTH) {
50647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        realCount = count;
50747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
50847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
5095f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk    jobject chainingBuf = mEnv->CallObjectMethod(mInBuf, gInputByteBufferClassInfo.mGetMethod,
5105f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk            mByteArray, 0, realCount);
511a3fdec8dac09b178d642c07a538c42faf84c2aaaRuben Brunk    mEnv->DeleteLocalRef(chainingBuf);
51247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
51347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    if (mEnv->ExceptionCheck()) {
514a3fdec8dac09b178d642c07a538c42faf84c2aaaRuben Brunk        ALOGE("%s: Exception while reading from input into byte buffer.", __FUNCTION__);
51547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        return BAD_VALUE;
51647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
51747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
51847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    mEnv->GetByteArrayRegion(mByteArray, 0, realCount, reinterpret_cast<jbyte*>(buf + offset));
51947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    if (mEnv->ExceptionCheck()) {
520a3fdec8dac09b178d642c07a538c42faf84c2aaaRuben Brunk        ALOGE("%s: Exception while reading from byte buffer.", __FUNCTION__);
52147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        return BAD_VALUE;
52247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
52347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    return realCount;
52447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk}
52547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
52647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkstatus_t JniInputByteBuffer::open() {
52747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    // Do nothing
52847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    return OK;
52947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk}
53047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
53147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkstatus_t JniInputByteBuffer::close() {
53247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    // Do nothing
53347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    return OK;
53447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk}
53547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
53647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk// End of JniInputByteBuffer
53747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk// ----------------------------------------------------------------------------
53847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
53947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk/**
54047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk * StripSource subclass for Input types.
54147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk *
54247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk * This class is not intended to be used across JNI calls.
54347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk */
54447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
54547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkclass InputStripSource : public StripSource, public LightRefBase<InputStripSource> {
54647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkpublic:
54747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width, uint32_t height,
54847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            uint32_t pixStride, uint32_t rowStride, uint64_t offset, uint32_t bytesPerSample,
54947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            uint32_t samplesPerPixel);
55047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
55147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    virtual ~InputStripSource();
55247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
55347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    virtual status_t writeToStream(Output& stream, uint32_t count);
55447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
55547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    virtual uint32_t getIfd() const;
55647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkprotected:
55747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t mIfd;
55847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    Input* mInput;
55947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t mWidth;
56047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t mHeight;
56147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t mPixStride;
56247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t mRowStride;
56347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint64_t mOffset;
56447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    JNIEnv* mEnv;
56547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t mBytesPerSample;
56647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t mSamplesPerPixel;
56747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk};
56847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
56947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben BrunkInputStripSource::InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width,
57047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
57147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd), mInput(&input),
57247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        mWidth(width), mHeight(height), mPixStride(pixStride), mRowStride(rowStride),
57347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
57447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        mSamplesPerPixel(samplesPerPixel) {}
57547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
57647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben BrunkInputStripSource::~InputStripSource() {}
57747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
57847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkstatus_t InputStripSource::writeToStream(Output& stream, uint32_t count) {
5793e1902504979b9b456a14dffa6507ee2d9ea3d6aRuben Brunk    uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
58047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    jlong offset = mOffset;
58147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
58247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    if (fullSize != count) {
58347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
58447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                fullSize);
58547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
58647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        return BAD_VALUE;
58747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
58847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
58947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    // Skip offset
59047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    while (offset > 0) {
59147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        ssize_t skipped = mInput->skip(offset);
59247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        if (skipped <= 0) {
59347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            if (skipped == NOT_ENOUGH_DATA || skipped == 0) {
59447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                jniThrowExceptionFmt(mEnv, "java/io/IOException",
59547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                        "Early EOF encountered in skip, not enough pixel data for image of size %u",
59647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                        fullSize);
59747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                skipped = NOT_ENOUGH_DATA;
59847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            } else {
59947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                if (!mEnv->ExceptionCheck()) {
60047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                    jniThrowException(mEnv, "java/io/IOException",
60147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                            "Error encountered while skip bytes in input stream.");
60247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                }
60347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            }
60447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
60547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            return skipped;
60647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        }
60747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        offset -= skipped;
60847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
60947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
61047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    Vector<uint8_t> row;
61147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    if (row.resize(mRowStride) < 0) {
61247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        jniThrowException(mEnv, "java/lang/OutOfMemoryError", "Could not allocate row vector.");
61347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        return BAD_VALUE;
61447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
61547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
61647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint8_t* rowBytes = row.editArray();
61747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
61847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    for (uint32_t i = 0; i < mHeight; ++i) {
61947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        size_t rowFillAmt = 0;
620a3fdec8dac09b178d642c07a538c42faf84c2aaaRuben Brunk        size_t rowSize = mRowStride;
62147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
62247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        while (rowFillAmt < mRowStride) {
62347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            ssize_t bytesRead = mInput->read(rowBytes, rowFillAmt, rowSize);
62447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            if (bytesRead <= 0) {
62547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                if (bytesRead == NOT_ENOUGH_DATA || bytesRead == 0) {
626a3fdec8dac09b178d642c07a538c42faf84c2aaaRuben Brunk                    ALOGE("%s: Early EOF on row %" PRIu32 ", received bytesRead %zd",
627a3fdec8dac09b178d642c07a538c42faf84c2aaaRuben Brunk                            __FUNCTION__, i, bytesRead);
62847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                    jniThrowExceptionFmt(mEnv, "java/io/IOException",
629a3fdec8dac09b178d642c07a538c42faf84c2aaaRuben Brunk                            "Early EOF encountered, not enough pixel data for image of size %"
630a3fdec8dac09b178d642c07a538c42faf84c2aaaRuben Brunk                            PRIu32, fullSize);
63147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                    bytesRead = NOT_ENOUGH_DATA;
63247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                } else {
63347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                    if (!mEnv->ExceptionCheck()) {
63447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                        jniThrowException(mEnv, "java/io/IOException",
63547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                                "Error encountered while reading");
63647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                    }
63747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                }
63847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                return bytesRead;
63947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            }
64047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            rowFillAmt += bytesRead;
64147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            rowSize -= bytesRead;
64247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        }
64347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
64447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
64547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            ALOGV("%s: Using stream per-row write for strip.", __FUNCTION__);
64647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
64747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            if (stream.write(rowBytes, 0, mBytesPerSample * mSamplesPerPixel * mWidth) != OK ||
64847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                    mEnv->ExceptionCheck()) {
64947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                if (!mEnv->ExceptionCheck()) {
65047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                    jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
65147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                }
65247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                return BAD_VALUE;
65347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            }
65447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        } else {
65547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            ALOGV("%s: Using stream per-pixel write for strip.", __FUNCTION__);
65647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            jniThrowException(mEnv, "java/lang/IllegalStateException",
65747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                    "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
65847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            return BAD_VALUE;
65947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
66047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            // TODO: Add support for non-contiguous pixels if needed.
66147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        }
66247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
66347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    return OK;
66447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk}
66547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
66647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkuint32_t InputStripSource::getIfd() const {
66747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    return mIfd;
66847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk}
66947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
67047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk// End of InputStripSource
67147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk// ----------------------------------------------------------------------------
67247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
67347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk/**
67447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk * StripSource subclass for direct buffer types.
67547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk *
67647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk * This class is not intended to be used across JNI calls.
67747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk */
67847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
67947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkclass DirectStripSource : public StripSource, public LightRefBase<DirectStripSource> {
68047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkpublic:
68147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd, uint32_t width,
68247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
68347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            uint32_t bytesPerSample, uint32_t samplesPerPixel);
68447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
68547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    virtual ~DirectStripSource();
68647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
68747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    virtual status_t writeToStream(Output& stream, uint32_t count);
68847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
68947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    virtual uint32_t getIfd() const;
69047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkprotected:
69147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t mIfd;
69247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    const uint8_t* mPixelBytes;
69347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t mWidth;
69447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t mHeight;
69547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t mPixStride;
69647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t mRowStride;
69747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint16_t mOffset;
69847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    JNIEnv* mEnv;
69947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t mBytesPerSample;
70047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t mSamplesPerPixel;
70147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk};
70247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
70347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben BrunkDirectStripSource::DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd,
70447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            uint32_t width, uint32_t height, uint32_t pixStride, uint32_t rowStride,
70547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            uint64_t offset, uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd),
70647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            mPixelBytes(pixelBytes), mWidth(width), mHeight(height), mPixStride(pixStride),
70747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            mRowStride(rowStride), mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
70847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            mSamplesPerPixel(samplesPerPixel) {}
70947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
71047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben BrunkDirectStripSource::~DirectStripSource() {}
71147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
71247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkstatus_t DirectStripSource::writeToStream(Output& stream, uint32_t count) {
7133e1902504979b9b456a14dffa6507ee2d9ea3d6aRuben Brunk    uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
71447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
71547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    if (fullSize != count) {
71647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
71747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                fullSize);
71847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
71947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        return BAD_VALUE;
72047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
72147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
7222079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
72347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    if (mPixStride == mBytesPerSample * mSamplesPerPixel
72447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            && mRowStride == mWidth * mBytesPerSample * mSamplesPerPixel) {
72547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        ALOGV("%s: Using direct single-pass write for strip.", __FUNCTION__);
72647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
72747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        if (stream.write(mPixelBytes, mOffset, fullSize) != OK || mEnv->ExceptionCheck()) {
72847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            if (!mEnv->ExceptionCheck()) {
72947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
73047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            }
73147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            return BAD_VALUE;
73247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        }
73347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    } else if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
73447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        ALOGV("%s: Using direct per-row write for strip.", __FUNCTION__);
73547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
73647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        for (size_t i = 0; i < mHeight; ++i) {
73747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            if (stream.write(mPixelBytes, mOffset + i * mRowStride, mPixStride * mWidth) != OK ||
73847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                        mEnv->ExceptionCheck()) {
73947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                if (!mEnv->ExceptionCheck()) {
74047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                    jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
74147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                }
74247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                return BAD_VALUE;
74347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            }
74447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        }
74547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    } else {
74647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        ALOGV("%s: Using direct per-pixel write for strip.", __FUNCTION__);
74747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
74847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        jniThrowException(mEnv, "java/lang/IllegalStateException",
74947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
75047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        return BAD_VALUE;
75147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
75247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        // TODO: Add support for non-contiguous pixels if needed.
75347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
75447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    return OK;
75547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
75647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk}
75747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
75847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkuint32_t DirectStripSource::getIfd() const {
75947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    return mIfd;
76047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk}
76147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
76247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk// End of DirectStripSource
76347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk// ----------------------------------------------------------------------------
76447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
765b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk/**
766a4ff47ced1ebed01722b77bc417d5a4eb51d0af9Ruben Brunk * Calculate the default crop relative to the "active area" of the image sensor (this active area
767a4ff47ced1ebed01722b77bc417d5a4eb51d0af9Ruben Brunk * will always be the pre-correction active area rectangle), and set this.
768b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk */
769b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunkstatic status_t calculateAndSetCrop(JNIEnv* env, const CameraMetadata& characteristics,
770a4ff47ced1ebed01722b77bc417d5a4eb51d0af9Ruben Brunk        sp<TiffWriter> writer) {
771b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk
772b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    camera_metadata_ro_entry entry =
7732079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
774b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
775b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
776b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk
7772079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    const uint32_t margin = 8; // Default margin recommended by Adobe for interpolation.
7782079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
779a4ff47ced1ebed01722b77bc417d5a4eb51d0af9Ruben Brunk    if (width < margin * 2 || height < margin * 2) {
780a4ff47ced1ebed01722b77bc417d5a4eb51d0af9Ruben Brunk        ALOGE("%s: Cannot calculate default crop for image, pre-correction active area is too"
781a4ff47ced1ebed01722b77bc417d5a4eb51d0af9Ruben Brunk                "small: h=%" PRIu32 ", w=%" PRIu32, __FUNCTION__, height, width);
782a4ff47ced1ebed01722b77bc417d5a4eb51d0af9Ruben Brunk        jniThrowException(env, "java/lang/IllegalStateException",
783a4ff47ced1ebed01722b77bc417d5a4eb51d0af9Ruben Brunk                "Pre-correction active area is too small.");
784a4ff47ced1ebed01722b77bc417d5a4eb51d0af9Ruben Brunk        return BAD_VALUE;
7852079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    }
7862079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
787a4ff47ced1ebed01722b77bc417d5a4eb51d0af9Ruben Brunk    uint32_t defaultCropOrigin[] = {margin, margin};
788a4ff47ced1ebed01722b77bc417d5a4eb51d0af9Ruben Brunk    uint32_t defaultCropSize[] = {width - defaultCropOrigin[0] - margin,
789a4ff47ced1ebed01722b77bc417d5a4eb51d0af9Ruben Brunk                                  height - defaultCropOrigin[1] - margin};
790a4ff47ced1ebed01722b77bc417d5a4eb51d0af9Ruben Brunk
791b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    BAIL_IF_INVALID_R(writer->addEntry(TAG_DEFAULTCROPORIGIN, 2, defaultCropOrigin,
792b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk            TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN, writer);
793b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    BAIL_IF_INVALID_R(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize,
794b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk            TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE, writer);
795b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk
796b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    return OK;
797b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk}
79847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
7992079612e5851d73f4672ae3729c883a58adc4dddRuben Brunkstatic bool validateDngHeader(JNIEnv* env, sp<TiffWriter> writer,
800b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk        const CameraMetadata& characteristics, jint width, jint height) {
801b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    if (width <= 0) {
802b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
803b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk                        "Image width %d is invalid", width);
804b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk        return false;
805b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    }
80647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
807b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    if (height <= 0) {
80847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
809b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk                        "Image height %d is invalid", height);
81047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        return false;
81147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
81247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
813b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    camera_metadata_ro_entry preCorrectionEntry =
814b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk            characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
815b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    camera_metadata_ro_entry pixelArrayEntry =
816b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk            characteristics.find(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE);
817b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk
818b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    int pWidth = static_cast<int>(pixelArrayEntry.data.i32[0]);
819b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    int pHeight = static_cast<int>(pixelArrayEntry.data.i32[1]);
820b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    int cWidth = static_cast<int>(preCorrectionEntry.data.i32[2]);
821b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    int cHeight = static_cast<int>(preCorrectionEntry.data.i32[3]);
822b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk
823b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    bool matchesPixelArray = (pWidth == width && pHeight == height);
824b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    bool matchesPreCorrectionArray = (cWidth == width && cHeight == height);
825b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk
8262079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    if (!(matchesPixelArray || matchesPreCorrectionArray)) {
82747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
828b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk                        "Image dimensions (w=%d,h=%d) are invalid, must match either the pixel "
829b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk                        "array size (w=%d, h=%d) or the pre-correction array size (w=%d, h=%d)",
830b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk                        width, height, pWidth, pHeight, cWidth, cHeight);
83147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        return false;
83247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
83347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
83447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    return true;
83547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk}
83647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
8372079612e5851d73f4672ae3729c883a58adc4dddRuben Brunkstatic status_t moveEntries(sp<TiffWriter> writer, uint32_t ifdFrom, uint32_t ifdTo,
83847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        const Vector<uint16_t>& entries) {
83947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    for (size_t i = 0; i < entries.size(); ++i) {
84047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        uint16_t tagId = entries[i];
84147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        sp<TiffEntry> entry = writer->getEntry(tagId, ifdFrom);
8422079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        if (entry.get() == nullptr) {
84347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            ALOGE("%s: moveEntries failed, entry %u not found in IFD %u", __FUNCTION__, tagId,
84447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                    ifdFrom);
84547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            return BAD_VALUE;
84647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        }
84747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        if (writer->addEntry(entry, ifdTo) != OK) {
84847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            ALOGE("%s: moveEntries failed, could not add entry %u to IFD %u", __FUNCTION__, tagId,
84947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                    ifdFrom);
85047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            return BAD_VALUE;
85147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        }
85247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        writer->removeEntry(tagId, ifdFrom);
85347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
85447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    return OK;
85547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk}
85647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
857d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk/**
858d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk * Write CFA pattern for given CFA enum into cfaOut.  cfaOut must have length >= 4.
859d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk * Returns OK on success, or a negative error code if the CFA enum was invalid.
860d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk */
861d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunkstatic status_t convertCFA(uint8_t cfaEnum, /*out*/uint8_t* cfaOut) {
862d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk    camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
863d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
864d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            cfaEnum);
865d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk    switch(cfa) {
866d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
867d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            cfaOut[0] = 0;
868d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            cfaOut[1] = 1;
869d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            cfaOut[2] = 1;
870d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            cfaOut[3] = 2;
871d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            break;
872d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        }
873d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
874d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            cfaOut[0] = 1;
875d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            cfaOut[1] = 0;
876d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            cfaOut[2] = 2;
877d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            cfaOut[3] = 1;
878d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            break;
879d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        }
880d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
881d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            cfaOut[0] = 1;
882d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            cfaOut[1] = 2;
883d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            cfaOut[2] = 0;
884d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            cfaOut[3] = 1;
885d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            break;
886d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        }
887d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
888d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            cfaOut[0] = 2;
889d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            cfaOut[1] = 1;
890d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            cfaOut[2] = 1;
891d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            cfaOut[3] = 0;
892d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            break;
893d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        }
894d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        default: {
895d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            return BAD_VALUE;
896d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        }
897d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk    }
898d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk    return OK;
899d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk}
900d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk
901d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk/**
902d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk * Convert the CFA layout enum to an OpcodeListBuilder::CfaLayout enum, defaults to
903d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk * RGGB for an unknown enum.
904d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk */
905d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunkstatic OpcodeListBuilder::CfaLayout convertCFAEnumToOpcodeLayout(uint8_t cfaEnum) {
906d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk    camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
907d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
908d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            cfaEnum);
909d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk    switch(cfa) {
910d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
911d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            return OpcodeListBuilder::CFA_RGGB;
912d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        }
913d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
914d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            return OpcodeListBuilder::CFA_GRBG;
915d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        }
916d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
917d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            return OpcodeListBuilder::CFA_GBRG;
918d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        }
919d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
920d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            return OpcodeListBuilder::CFA_BGGR;
921d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        }
922d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        default: {
923d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            return OpcodeListBuilder::CFA_RGGB;
924d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        }
925d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk    }
926d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk}
927d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk
928d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk/**
929d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk * For each color plane, find the corresponding noise profile coefficients given in the
930d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk * per-channel noise profile.  If multiple channels in the CFA correspond to a color in the color
931d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk * plane, this method takes the pair of noise profile coefficients with the higher S coefficient.
932d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk *
933d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk * perChannelNoiseProfile - numChannels * 2 noise profile coefficients.
934d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk * cfa - numChannels color channels corresponding to each of the per-channel noise profile
935d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk *       coefficients.
936d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk * numChannels - the number of noise profile coefficient pairs and color channels given in
937d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk *       the perChannelNoiseProfile and cfa arguments, respectively.
938d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk * planeColors - the color planes in the noise profile output.
939d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk * numPlanes - the number of planes in planeColors and pairs of coefficients in noiseProfile.
940d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk * noiseProfile - 2 * numPlanes doubles containing numPlanes pairs of noise profile coefficients.
941d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk *
942d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk * returns OK, or a negative error code on failure.
943d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk */
944d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunkstatic status_t generateNoiseProfile(const double* perChannelNoiseProfile, uint8_t* cfa,
945d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        size_t numChannels, const uint8_t* planeColors, size_t numPlanes,
946d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        /*out*/double* noiseProfile) {
947d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk
948d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk    for (size_t p = 0; p < numPlanes; ++p) {
949d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        size_t S = p * 2;
950d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        size_t O = p * 2 + 1;
951d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk
952d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        noiseProfile[S] = 0;
953d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        noiseProfile[O] = 0;
954d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        bool uninitialized = true;
955d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        for (size_t c = 0; c < numChannels; ++c) {
956d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            if (cfa[c] == planeColors[p] && perChannelNoiseProfile[c * 2] > noiseProfile[S]) {
957d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk                noiseProfile[S] = perChannelNoiseProfile[c * 2];
958d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk                noiseProfile[O] = perChannelNoiseProfile[c * 2 + 1];
959d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk                uninitialized = false;
960d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            }
961d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        }
962d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        if (uninitialized) {
96346d8444631b4b1253a76bfcc78a29d26014d022fDan Albert            ALOGE("%s: No valid NoiseProfile coefficients for color plane %zu",
96446d8444631b4b1253a76bfcc78a29d26014d022fDan Albert                  __FUNCTION__, p);
965d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            return BAD_VALUE;
966d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        }
967d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk    }
968d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk    return OK;
969d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk}
970d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk
97147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk// ----------------------------------------------------------------------------
972f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunkextern "C" {
973f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
97447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkstatic NativeContext* DngCreator_getNativeContext(JNIEnv* env, jobject thiz) {
975f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    ALOGV("%s:", __FUNCTION__);
97647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    return reinterpret_cast<NativeContext*>(env->GetLongField(thiz,
977f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            gDngCreatorClassInfo.mNativeContext));
978f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk}
979f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
98047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkstatic void DngCreator_setNativeContext(JNIEnv* env, jobject thiz, sp<NativeContext> context) {
981f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    ALOGV("%s:", __FUNCTION__);
98247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    NativeContext* current = DngCreator_getNativeContext(env, thiz);
98347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
9842079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    if (context != nullptr) {
98547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        context->incStrong((void*) DngCreator_setNativeContext);
986f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
98747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
988f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    if (current) {
98947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        current->decStrong((void*) DngCreator_setNativeContext);
990f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
99147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
992f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext,
99347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            reinterpret_cast<jlong>(context.get()));
99447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk}
99547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
996f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunkstatic void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
997f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    ALOGV("%s:", __FUNCTION__);
998f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
999ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe    gDngCreatorClassInfo.mNativeContext = GetFieldIDOrDie(env,
1000ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe            clazz, ANDROID_DNGCREATOR_CTX_JNI_ID, "J");
1001ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe
1002ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe    jclass outputStreamClazz = FindClassOrDie(env, "java/io/OutputStream");
1003ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe    gOutputStreamClassInfo.mWriteMethod = GetMethodIDOrDie(env,
1004ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe            outputStreamClazz, "write", "([BII)V");
1005ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe
1006ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe    jclass inputStreamClazz = FindClassOrDie(env, "java/io/InputStream");
1007ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe    gInputStreamClassInfo.mReadMethod = GetMethodIDOrDie(env, inputStreamClazz, "read", "([BII)I");
1008ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe    gInputStreamClassInfo.mSkipMethod = GetMethodIDOrDie(env, inputStreamClazz, "skip", "(J)J");
1009ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe
1010ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe    jclass inputBufferClazz = FindClassOrDie(env, "java/nio/ByteBuffer");
1011ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe    gInputByteBufferClassInfo.mGetMethod = GetMethodIDOrDie(env,
1012ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe            inputBufferClazz, "get", "([BII)Ljava/nio/ByteBuffer;");
1013f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk}
1014f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1015f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunkstatic void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
1016b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        jobject resultsPtr, jstring formattedCaptureTime) {
1017f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    ALOGV("%s:", __FUNCTION__);
1018f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    CameraMetadata characteristics;
1019f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    CameraMetadata results;
1020f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) {
1021f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk         jniThrowException(env, "java/lang/AssertionError",
1022f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk                "No native metadata defined for camera characteristics.");
1023f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk         return;
1024f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1025f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) {
1026f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        jniThrowException(env, "java/lang/AssertionError",
1027f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk                "No native metadata defined for capture results.");
1028f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        return;
1029f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1030f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1031b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    sp<NativeContext> nativeContext = new NativeContext(characteristics, results);
10322079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
10332079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    const char* captureTime = env->GetStringUTFChars(formattedCaptureTime, nullptr);
10342079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
10352079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    size_t len = strlen(captureTime) + 1;
10362079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    if (len != NativeContext::DATETIME_COUNT) {
10372079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        jniThrowException(env, "java/lang/IllegalArgumentException",
10382079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                "Formatted capture time string length is not required 20 characters");
10392079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        return;
10402079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    }
10412079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
10422079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    nativeContext->setCaptureTime(String8(captureTime));
10432079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
10442079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    DngCreator_setNativeContext(env, thiz, nativeContext);
10452079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk}
10462079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
10472079612e5851d73f4672ae3729c883a58adc4dddRuben Brunkstatic sp<TiffWriter> DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t imageWidth,
10482079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        uint32_t imageHeight) {
10492079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
10502079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    NativeContext* nativeContext = DngCreator_getNativeContext(env, thiz);
10512079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
10522079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    if (nativeContext == nullptr) {
10532079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        jniThrowException(env, "java/lang/AssertionError",
10542079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                "No native context, must call init before other operations.");
10552079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        return nullptr;
10562079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    }
10572079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
10582079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    CameraMetadata characteristics = *(nativeContext->getCharacteristics());
10592079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    CameraMetadata results = *(nativeContext->getResult());
10602079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
10612079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    sp<TiffWriter> writer = new TiffWriter();
10622079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
10632079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    uint32_t preWidth = 0;
10642079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    uint32_t preHeight = 0;
10652079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    {
10662079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        // Check dimensions
10672079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        camera_metadata_entry entry =
10682079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
10692079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_IMAGEWIDTH, writer);
10702079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        preWidth = static_cast<uint32_t>(entry.data.i32[2]);
10712079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        preHeight = static_cast<uint32_t>(entry.data.i32[3]);
10722079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
10732079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        camera_metadata_entry pixelArrayEntry =
10742079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                characteristics.find(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE);
10752079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        uint32_t pixWidth = static_cast<uint32_t>(pixelArrayEntry.data.i32[0]);
10762079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        uint32_t pixHeight = static_cast<uint32_t>(pixelArrayEntry.data.i32[1]);
10772079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
10782079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        if (!((imageWidth == preWidth && imageHeight == preHeight) ||
10792079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            (imageWidth == pixWidth && imageHeight == pixHeight))) {
10802079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            jniThrowException(env, "java/lang/AssertionError",
10812079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    "Height and width of imate buffer did not match height and width of"
10822079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    "either the preCorrectionActiveArraySize or the pixelArraySize.");
10832079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            return nullptr;
10842079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        }
10852079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    }
10862079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
10872079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
1088f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1089f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    writer->addIfd(TIFF_IFD_0);
1090f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1091f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    status_t err = OK;
1092f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1093f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    const uint32_t samplesPerPixel = 1;
1094f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    const uint32_t bitsPerSample = BITS_PER_SAMPLE;
1095f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1096f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
1097d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk    uint8_t cfaPlaneColor[3] = {0, 1, 2};
1098d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk    uint8_t cfaEnum = -1;
1099f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1100f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    // TODO: Greensplit.
1101f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    // TODO: Add remaining non-essential tags
110247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
110347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    // Setup main image tags
110447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
1105f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {
1106f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        // Set orientation
11078069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala        uint16_t orientation = TAG_ORIENTATION_NORMAL;
11082079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0),
11092079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                env, TAG_ORIENTATION, writer);
1110f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1111f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1112f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {
1113f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        // Set subfiletype
1114f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        uint32_t subfileType = 0; // Main image
11152079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType,
11162079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                TIFF_IFD_0), env, TAG_NEWSUBFILETYPE, writer);
1117f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1118f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1119f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {
1120f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        // Set bits per sample
1121f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        uint16_t bits = static_cast<uint16_t>(bitsPerSample);
11222079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
112347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                TAG_BITSPERSAMPLE, writer);
1124f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1125f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1126f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {
1127f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        // Set compression
1128f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        uint16_t compression = 1; // None
11292079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COMPRESSION, 1, &compression,
11302079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                TIFF_IFD_0), env, TAG_COMPRESSION, writer);
1131f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1132f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1133f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {
1134f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        // Set dimensions
11352079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEWIDTH, 1, &imageWidth, TIFF_IFD_0),
11362079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                env, TAG_IMAGEWIDTH, writer);
11372079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGELENGTH, 1, &imageHeight, TIFF_IFD_0),
11382079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                env, TAG_IMAGELENGTH, writer);
1139f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1140f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1141f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {
1142f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        // Set photometric interpretation
114347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        uint16_t interpretation = 32803; // CFA
11442079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1,
11452079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                &interpretation, TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
1146f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1147f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1148f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {
11498069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala        // Set blacklevel tags, using dynamic black level if available
1150f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        camera_metadata_entry entry =
11518069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala                results.find(ANDROID_SENSOR_DYNAMIC_BLACK_LEVEL);
11528069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala        uint32_t blackLevelRational[8] = {0};
11538069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala        if (entry.count != 0) {
11548069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala            BAIL_IF_EXPR_RET_NULL_SP(entry.count != 4, env, TAG_BLACKLEVEL, writer);
11558069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala            for (size_t i = 0; i < entry.count; i++) {
11568069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala                blackLevelRational[i * 2] = static_cast<uint32_t>(entry.data.f[i] * 100);
11578069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala                blackLevelRational[i * 2 + 1] = 100;
11588069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala            }
11598069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala        } else {
11608069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala            // Fall back to static black level which is guaranteed
11618069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala            entry = characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
11628069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala            BAIL_IF_EXPR_RET_NULL_SP(entry.count != 4, env, TAG_BLACKLEVEL, writer);
11638069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala            for (size_t i = 0; i < entry.count; i++) {
11648069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala                blackLevelRational[i * 2] = static_cast<uint32_t>(entry.data.i32[i]);
11658069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala                blackLevelRational[i * 2 + 1] = 1;
11668069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala            }
11678069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala
11688069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala        }
11698069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVEL, 4, blackLevelRational,
11702079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                TIFF_IFD_0), env, TAG_BLACKLEVEL, writer);
1171f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1172f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        uint16_t repeatDim[2] = {2, 2};
11732079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim,
11742079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                TIFF_IFD_0), env, TAG_BLACKLEVELREPEATDIM, writer);
1175f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1176f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1177f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {
1178f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        // Set samples per pixel
1179f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        uint16_t samples = static_cast<uint16_t>(samplesPerPixel);
11802079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
118147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                env, TAG_SAMPLESPERPIXEL, writer);
1182f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1183f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1184f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {
1185f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        // Set planar configuration
1186f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        uint16_t config = 1; // Chunky
11872079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config,
11882079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                TIFF_IFD_0), env, TAG_PLANARCONFIGURATION, writer);
1189f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1190f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1191f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {
1192f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        // Set CFA pattern dimensions
1193f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        uint16_t repeatDim[2] = {2, 2};
11942079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim,
11952079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                TIFF_IFD_0), env, TAG_CFAREPEATPATTERNDIM, writer);
1196f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1197f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1198f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {
1199f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        // Set CFA pattern
1200f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        camera_metadata_entry entry =
1201f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk                        characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
12022079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_CFAPATTERN, writer);
1203d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk
1204d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        const int cfaLength = 4;
1205d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        cfaEnum = entry.data.u8[0];
1206d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        uint8_t cfa[cfaLength];
1207d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        if ((err = convertCFA(cfaEnum, /*out*/cfa)) != OK) {
1208d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
1209d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk                        "Invalid metadata for tag %d", TAG_CFAPATTERN);
1210f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        }
1211d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk
12122079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAPATTERN, cfaLength, cfa, TIFF_IFD_0),
12132079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                env, TAG_CFAPATTERN, writer);
1214d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk
1215d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        opcodeCfaLayout = convertCFAEnumToOpcodeLayout(cfaEnum);
1216f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1217f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1218f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {
1219f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        // Set CFA plane color
12202079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor,
12212079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                TIFF_IFD_0), env, TAG_CFAPLANECOLOR, writer);
1222f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1223f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1224f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {
1225f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        // Set CFA layout
1226f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        uint16_t cfaLayout = 1;
12272079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
122847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                env, TAG_CFALAYOUT, writer);
1229f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1230f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1231f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {
1232b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        // image description
1233b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        uint8_t imageDescription = '\0'; // empty
12342079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEDESCRIPTION, 1, &imageDescription,
12352079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                TIFF_IFD_0), env, TAG_IMAGEDESCRIPTION, writer);
1236b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk    }
1237b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1238b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk    {
1239b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        // make
1240b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        char manufacturer[PROPERTY_VALUE_MAX];
1241b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1242b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        // Use "" to represent unknown make as suggested in TIFF/EP spec.
1243b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        property_get("ro.product.manufacturer", manufacturer, "");
1244b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        uint32_t count = static_cast<uint32_t>(strlen(manufacturer)) + 1;
1245b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
12462079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_MAKE, count,
12472079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                reinterpret_cast<uint8_t*>(manufacturer), TIFF_IFD_0), env, TAG_MAKE, writer);
1248b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk    }
1249b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1250b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk    {
1251b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        // model
1252b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        char model[PROPERTY_VALUE_MAX];
1253b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1254b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        // Use "" to represent unknown model as suggested in TIFF/EP spec.
1255b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        property_get("ro.product.model", model, "");
1256b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        uint32_t count = static_cast<uint32_t>(strlen(model)) + 1;
1257b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
12582079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_MODEL, count,
12592079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                reinterpret_cast<uint8_t*>(model), TIFF_IFD_0), env, TAG_MODEL, writer);
1260b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk    }
1261b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1262b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk    {
1263b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        // x resolution
1264b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        uint32_t xres[] = { 72, 1 }; // default 72 ppi
12652079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
126647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                env, TAG_XRESOLUTION, writer);
1267b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1268b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        // y resolution
1269b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        uint32_t yres[] = { 72, 1 }; // default 72 ppi
12702079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
127147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                env, TAG_YRESOLUTION, writer);
1272b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1273b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        uint16_t unit = 2; // inches
12742079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
127547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                env, TAG_RESOLUTIONUNIT, writer);
1276b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk    }
1277b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1278b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk    {
1279b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        // software
1280b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        char software[PROPERTY_VALUE_MAX];
1281b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        property_get("ro.build.fingerprint", software, "");
1282b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        uint32_t count = static_cast<uint32_t>(strlen(software)) + 1;
12832079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SOFTWARE, count,
12842079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                reinterpret_cast<uint8_t*>(software), TIFF_IFD_0), env, TAG_SOFTWARE, writer);
1285b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk    }
1286b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
12872079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    if (nativeContext->hasCaptureTime()) {
1288b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        // datetime
12892079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        String8 captureTime = nativeContext->getCaptureTime();
1290b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
12912079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        if (writer->addEntry(TAG_DATETIME, NativeContext::DATETIME_COUNT,
12922079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                reinterpret_cast<const uint8_t*>(captureTime.string()), TIFF_IFD_0) != OK) {
129347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
129447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                    "Invalid metadata for tag %x", TAG_DATETIME);
12952079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            return nullptr;
129647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        }
1297b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1298b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        // datetime original
12992079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        if (writer->addEntry(TAG_DATETIMEORIGINAL, NativeContext::DATETIME_COUNT,
13002079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                reinterpret_cast<const uint8_t*>(captureTime.string()), TIFF_IFD_0) != OK) {
130147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
130247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                    "Invalid metadata for tag %x", TAG_DATETIMEORIGINAL);
13032079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            return nullptr;
130447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        }
1305b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk    }
1306b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1307b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk    {
1308b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        // TIFF/EP standard id
1309b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        uint8_t standardId[] = { 1, 0, 0, 0 };
13102079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_TIFFEPSTANDARDID, 4, standardId,
131147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                TIFF_IFD_0), env, TAG_TIFFEPSTANDARDID, writer);
1312b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk    }
1313b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1314b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk    {
1315b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        // copyright
1316b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        uint8_t copyright = '\0'; // empty
13172079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COPYRIGHT, 1, &copyright,
131847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                TIFF_IFD_0), env, TAG_COPYRIGHT, writer);
1319b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk    }
1320b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1321b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk    {
1322b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        // exposure time
1323b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        camera_metadata_entry entry =
1324b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk            results.find(ANDROID_SENSOR_EXPOSURE_TIME);
13252079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_EXPOSURETIME, writer);
1326b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1327b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        int64_t exposureTime = *(entry.data.i64);
1328b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1329b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        if (exposureTime < 0) {
1330b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk            // Should be unreachable
1331b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk            jniThrowException(env, "java/lang/IllegalArgumentException",
1332b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk                    "Negative exposure time in metadata");
13332079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            return nullptr;
1334b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        }
1335b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1336b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        // Ensure exposure time doesn't overflow (for exposures > 4s)
1337b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        uint32_t denominator = 1000000000;
1338b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        while (exposureTime > UINT32_MAX) {
1339b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk            exposureTime >>= 1;
1340b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk            denominator >>= 1;
1341b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk            if (denominator == 0) {
1342b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk                // Should be unreachable
1343b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk                jniThrowException(env, "java/lang/IllegalArgumentException",
1344b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk                        "Exposure time too long");
13452079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                return nullptr;
1346b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk            }
1347b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        }
1348b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1349b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        uint32_t exposure[] = { static_cast<uint32_t>(exposureTime), denominator };
13502079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_EXPOSURETIME, 1, exposure,
135147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                TIFF_IFD_0), env, TAG_EXPOSURETIME, writer);
1352b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1353b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk    }
1354b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1355b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk    {
1356b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        // ISO speed ratings
1357b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        camera_metadata_entry entry =
1358b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk            results.find(ANDROID_SENSOR_SENSITIVITY);
13592079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ISOSPEEDRATINGS, writer);
1360b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1361b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        int32_t tempIso = *(entry.data.i32);
1362b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        if (tempIso < 0) {
1363b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk            jniThrowException(env, "java/lang/IllegalArgumentException",
1364b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk                                    "Negative ISO value");
13652079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            return nullptr;
1366b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        }
1367b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1368b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        if (tempIso > UINT16_MAX) {
1369b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk            ALOGW("%s: ISO value overflows UINT16_MAX, clamping to max", __FUNCTION__);
1370b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk            tempIso = UINT16_MAX;
1371b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        }
1372b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1373b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        uint16_t iso = static_cast<uint16_t>(tempIso);
13742079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ISOSPEEDRATINGS, 1, &iso,
137547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                TIFF_IFD_0), env, TAG_ISOSPEEDRATINGS, writer);
1376b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk    }
1377b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1378b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk    {
13797ca1371987736e8b3c51818afcbd245ca6286bc0Emilian Peev        // Baseline exposure
13807ca1371987736e8b3c51818afcbd245ca6286bc0Emilian Peev        camera_metadata_entry entry =
13817ca1371987736e8b3c51818afcbd245ca6286bc0Emilian Peev                results.find(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST);
13827ca1371987736e8b3c51818afcbd245ca6286bc0Emilian Peev        BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_BASELINEEXPOSURE, writer);
13837ca1371987736e8b3c51818afcbd245ca6286bc0Emilian Peev
13847ca1371987736e8b3c51818afcbd245ca6286bc0Emilian Peev        // post RAW gain should be boostValue / 100
13857ca1371987736e8b3c51818afcbd245ca6286bc0Emilian Peev        double postRAWGain = static_cast<double> (entry.data.i32[0]) / 100.f;
13867ca1371987736e8b3c51818afcbd245ca6286bc0Emilian Peev        // Baseline exposure should be in EV units so log2(gain) =
13877ca1371987736e8b3c51818afcbd245ca6286bc0Emilian Peev        // log10(gain)/log10(2)
13887ca1371987736e8b3c51818afcbd245ca6286bc0Emilian Peev        double baselineExposure = std::log(postRAWGain) / std::log(2.0f);
13897ca1371987736e8b3c51818afcbd245ca6286bc0Emilian Peev        int32_t baseExposureSRat[] = { static_cast<int32_t> (baselineExposure * 100),
13907ca1371987736e8b3c51818afcbd245ca6286bc0Emilian Peev                100 };
13917ca1371987736e8b3c51818afcbd245ca6286bc0Emilian Peev        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BASELINEEXPOSURE, 1,
13927ca1371987736e8b3c51818afcbd245ca6286bc0Emilian Peev                baseExposureSRat, TIFF_IFD_0), env, TAG_BASELINEEXPOSURE, writer);
13937ca1371987736e8b3c51818afcbd245ca6286bc0Emilian Peev    }
13947ca1371987736e8b3c51818afcbd245ca6286bc0Emilian Peev
13957ca1371987736e8b3c51818afcbd245ca6286bc0Emilian Peev    {
1396b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        // focal length
1397b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        camera_metadata_entry entry =
1398b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk            results.find(ANDROID_LENS_FOCAL_LENGTH);
13992079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_FOCALLENGTH, writer);
1400b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1401b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        uint32_t focalLength[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
14022079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FOCALLENGTH, 1, focalLength,
140347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                TIFF_IFD_0), env, TAG_FOCALLENGTH, writer);
1404b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk    }
1405b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1406b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk    {
1407b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        // f number
1408b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        camera_metadata_entry entry =
1409b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk            results.find(ANDROID_LENS_APERTURE);
14102079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_FNUMBER, writer);
1411b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1412b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk        uint32_t fnum[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
14132079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FNUMBER, 1, fnum,
141447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                TIFF_IFD_0), env, TAG_FNUMBER, writer);
1415b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk    }
1416b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk
1417b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk    {
1418f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        // Set DNG version information
1419f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        uint8_t version[4] = {1, 4, 0, 0};
14202079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
142147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                env, TAG_DNGVERSION, writer);
1422f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1423f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        uint8_t backwardVersion[4] = {1, 1, 0, 0};
14242079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion,
14252079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                TIFF_IFD_0), env, TAG_DNGBACKWARDVERSION, writer);
1426f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1427f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1428f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {
1429f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        // Set whitelevel
1430f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        camera_metadata_entry entry =
1431f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk                characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
14322079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_WHITELEVEL, writer);
1433f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]);
14342079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0),
14352079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                env, TAG_WHITELEVEL, writer);
1436f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1437f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1438f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {
1439f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        // Set default scale
1440f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        uint32_t defaultScale[4] = {1, 1, 1, 1};
14412079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale,
14422079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                TIFF_IFD_0), env, TAG_DEFAULTSCALE, writer);
1443f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1444f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1445f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    bool singleIlluminant = false;
1446f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {
1447f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        // Set calibration illuminants
1448f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        camera_metadata_entry entry1 =
1449f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
14502079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_CALIBRATIONILLUMINANT1, writer);
1451f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        camera_metadata_entry entry2 =
1452f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
1453f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        if (entry2.count == 0) {
1454f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            singleIlluminant = true;
1455f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        }
1456f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        uint16_t ref1 = entry1.data.u8[0];
1457f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
14582079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1,
145947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1, writer);
1460f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1461f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        if (!singleIlluminant) {
1462f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            uint16_t ref2 = entry2.data.u8[0];
14632079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
146447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                    TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2, writer);
1465f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        }
1466f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1467f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1468f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {
1469f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        // Set color transforms
1470f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        camera_metadata_entry entry1 =
1471f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
14722079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_COLORMATRIX1, writer);
1473f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1474f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        int32_t colorTransform1[entry1.count * 2];
1475f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1476f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        size_t ctr = 0;
1477f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        for(size_t i = 0; i < entry1.count; ++i) {
1478f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            colorTransform1[ctr++] = entry1.data.r[i].numerator;
1479f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            colorTransform1[ctr++] = entry1.data.r[i].denominator;
1480f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        }
1481f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
14822079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COLORMATRIX1, entry1.count,
14832079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                colorTransform1, TIFF_IFD_0), env, TAG_COLORMATRIX1, writer);
1484f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1485f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        if (!singleIlluminant) {
1486f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
14872079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_COLORMATRIX2, writer);
1488f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            int32_t colorTransform2[entry2.count * 2];
1489f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1490f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            ctr = 0;
1491f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            for(size_t i = 0; i < entry2.count; ++i) {
1492f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk                colorTransform2[ctr++] = entry2.data.r[i].numerator;
1493f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk                colorTransform2[ctr++] = entry2.data.r[i].denominator;
1494f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            }
1495f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
14962079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COLORMATRIX2, entry2.count,
14972079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    colorTransform2, TIFF_IFD_0), env, TAG_COLORMATRIX2, writer);
1498f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        }
1499f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1500f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1501f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {
1502f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        // Set calibration transforms
1503f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        camera_metadata_entry entry1 =
1504f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
15052079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_CAMERACALIBRATION1, writer);
1506f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1507f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        int32_t calibrationTransform1[entry1.count * 2];
1508f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1509f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        size_t ctr = 0;
1510f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        for(size_t i = 0; i < entry1.count; ++i) {
1511f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            calibrationTransform1[ctr++] = entry1.data.r[i].numerator;
1512f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            calibrationTransform1[ctr++] = entry1.data.r[i].denominator;
1513f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        }
1514f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
15152079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count,
1516b1971dc8a69b8cee91208b7d3017c52b36e55721Ruben Brunk                calibrationTransform1, TIFF_IFD_0), env, TAG_CAMERACALIBRATION1, writer);
1517f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1518f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        if (!singleIlluminant) {
1519f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            camera_metadata_entry entry2 =
1520f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk                characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
15212079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_CAMERACALIBRATION2, writer);
1522f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            int32_t calibrationTransform2[entry2.count * 2];
1523f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1524f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            ctr = 0;
1525f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            for(size_t i = 0; i < entry2.count; ++i) {
1526f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk                calibrationTransform2[ctr++] = entry2.data.r[i].numerator;
1527f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk                calibrationTransform2[ctr++] = entry2.data.r[i].denominator;
1528f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            }
1529f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
15302079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count,
15312377cd319f8a77f147f3d70a7ddc75fa9e9fe87cAndreas Gampe                    calibrationTransform2, TIFF_IFD_0),  env, TAG_CAMERACALIBRATION2, writer);
1532f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        }
1533f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1534f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1535f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {
1536f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        // Set forward transforms
1537f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        camera_metadata_entry entry1 =
1538f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
15392079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_FORWARDMATRIX1, writer);
1540f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1541f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        int32_t forwardTransform1[entry1.count * 2];
1542f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1543f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        size_t ctr = 0;
1544f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        for(size_t i = 0; i < entry1.count; ++i) {
1545f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            forwardTransform1[ctr++] = entry1.data.r[i].numerator;
1546f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            forwardTransform1[ctr++] = entry1.data.r[i].denominator;
1547f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        }
1548f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
15492079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count,
15502079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                forwardTransform1, TIFF_IFD_0), env, TAG_FORWARDMATRIX1, writer);
1551f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1552f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        if (!singleIlluminant) {
1553f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            camera_metadata_entry entry2 =
1554f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk                characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
15552079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_FORWARDMATRIX2, writer);
1556f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            int32_t forwardTransform2[entry2.count * 2];
1557f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1558f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            ctr = 0;
1559f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            for(size_t i = 0; i < entry2.count; ++i) {
1560f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk                forwardTransform2[ctr++] = entry2.data.r[i].numerator;
1561f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk                forwardTransform2[ctr++] = entry2.data.r[i].denominator;
1562f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            }
1563f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
15642079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count,
15652079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    forwardTransform2, TIFF_IFD_0),  env, TAG_FORWARDMATRIX2, writer);
1566f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        }
1567f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1568f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1569f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {
1570f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        // Set camera neutral
1571f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        camera_metadata_entry entry =
1572f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
15732079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ASSHOTNEUTRAL, writer);
1574f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        uint32_t cameraNeutral[entry.count * 2];
1575f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1576f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        size_t ctr = 0;
1577f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        for(size_t i = 0; i < entry.count; ++i) {
1578f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            cameraNeutral[ctr++] =
1579f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk                    static_cast<uint32_t>(entry.data.r[i].numerator);
1580f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            cameraNeutral[ctr++] =
1581f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk                    static_cast<uint32_t>(entry.data.r[i].denominator);
1582f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        }
1583f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
15842079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral,
158547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL, writer);
1586f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1587f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1588f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1589f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {
1590b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk        // Set dimensions
1591a4ff47ced1ebed01722b77bc417d5a4eb51d0af9Ruben Brunk        if (calculateAndSetCrop(env, characteristics, writer) != OK) {
15922079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            return nullptr;
15932079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        }
1594b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk        camera_metadata_entry entry =
1595b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk                characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
1596a4ff47ced1ebed01722b77bc417d5a4eb51d0af9Ruben Brunk        BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ACTIVEAREA, writer);
1597b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk        uint32_t xmin = static_cast<uint32_t>(entry.data.i32[0]);
1598b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk        uint32_t ymin = static_cast<uint32_t>(entry.data.i32[1]);
1599b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk        uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
1600b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk        uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
16012079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
1602a4ff47ced1ebed01722b77bc417d5a4eb51d0af9Ruben Brunk        // If we only have a buffer containing the pre-correction rectangle, ignore the offset
1603a4ff47ced1ebed01722b77bc417d5a4eb51d0af9Ruben Brunk        // relative to the pixel array.
1604a4ff47ced1ebed01722b77bc417d5a4eb51d0af9Ruben Brunk        if (imageWidth == width && imageHeight == height) {
1605a4ff47ced1ebed01722b77bc417d5a4eb51d0af9Ruben Brunk            xmin = 0;
1606a4ff47ced1ebed01722b77bc417d5a4eb51d0af9Ruben Brunk            ymin = 0;
1607a4ff47ced1ebed01722b77bc417d5a4eb51d0af9Ruben Brunk        }
1608a4ff47ced1ebed01722b77bc417d5a4eb51d0af9Ruben Brunk
16092079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        uint32_t activeArea[] = {ymin, xmin, ymin + height, xmin + width};
16102079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ACTIVEAREA, 4, activeArea, TIFF_IFD_0),
16112079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                env, TAG_ACTIVEAREA, writer);
1612f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1613f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1614f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {
1615f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        // Setup unique camera model tag
1616f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        char model[PROPERTY_VALUE_MAX];
1617f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        property_get("ro.product.model", model, "");
1618f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1619f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        char manufacturer[PROPERTY_VALUE_MAX];
1620f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        property_get("ro.product.manufacturer", manufacturer, "");
1621f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1622f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        char brand[PROPERTY_VALUE_MAX];
1623f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        property_get("ro.product.brand", brand, "");
1624f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1625f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        String8 cameraModel(model);
1626f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        cameraModel += "-";
1627f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        cameraModel += manufacturer;
1628f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        cameraModel += "-";
1629f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        cameraModel += brand;
1630f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
16312079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
1632f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk                reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
163347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                TAG_UNIQUECAMERAMODEL, writer);
1634f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1635f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
1636f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {
1637b1971dc8a69b8cee91208b7d3017c52b36e55721Ruben Brunk        // Setup sensor noise model
1638b1971dc8a69b8cee91208b7d3017c52b36e55721Ruben Brunk        camera_metadata_entry entry =
1639b1971dc8a69b8cee91208b7d3017c52b36e55721Ruben Brunk            results.find(ANDROID_SENSOR_NOISE_PROFILE);
1640b1971dc8a69b8cee91208b7d3017c52b36e55721Ruben Brunk
1641d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        const status_t numPlaneColors = 3;
1642d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        const status_t numCfaChannels = 4;
1643d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk
1644d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        uint8_t cfaOut[numCfaChannels];
1645d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        if ((err = convertCFA(cfaEnum, /*out*/cfaOut)) != OK) {
1646d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            jniThrowException(env, "java/lang/IllegalArgumentException",
1647d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk                    "Invalid CFA from camera characteristics");
16482079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            return nullptr;
1649d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        }
1650d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk
1651d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk        double noiseProfile[numPlaneColors * 2];
1652d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk
1653b1971dc8a69b8cee91208b7d3017c52b36e55721Ruben Brunk        if (entry.count > 0) {
1654d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            if (entry.count != numCfaChannels * 2) {
165546d8444631b4b1253a76bfcc78a29d26014d022fDan Albert                ALOGW("%s: Invalid entry count %zu for noise profile returned "
165646d8444631b4b1253a76bfcc78a29d26014d022fDan Albert                      "in characteristics, no noise profile tag written...",
165746d8444631b4b1253a76bfcc78a29d26014d022fDan Albert                      __FUNCTION__, entry.count);
1658d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            } else {
1659d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk                if ((err = generateNoiseProfile(entry.data.d, cfaOut, numCfaChannels,
1660d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk                        cfaPlaneColor, numPlaneColors, /*out*/ noiseProfile)) == OK) {
1661d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk
16622079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NOISEPROFILE,
16632079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                            numPlaneColors * 2, noiseProfile, TIFF_IFD_0), env, TAG_NOISEPROFILE,
16642079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                            writer);
1665d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk                } else {
1666d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk                    ALOGW("%s: Error converting coefficients for noise profile, no noise profile"
1667d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk                            " tag written...", __FUNCTION__);
1668d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk                }
1669d70132c373eee01b4e0a35250bd4246672e7a02eRuben Brunk            }
1670b1971dc8a69b8cee91208b7d3017c52b36e55721Ruben Brunk        } else {
1671b1971dc8a69b8cee91208b7d3017c52b36e55721Ruben Brunk            ALOGW("%s: No noise profile found in result metadata.  Image quality may be reduced.",
1672b1971dc8a69b8cee91208b7d3017c52b36e55721Ruben Brunk                    __FUNCTION__);
1673b1971dc8a69b8cee91208b7d3017c52b36e55721Ruben Brunk        }
1674b1971dc8a69b8cee91208b7d3017c52b36e55721Ruben Brunk    }
1675b1971dc8a69b8cee91208b7d3017c52b36e55721Ruben Brunk
1676b1971dc8a69b8cee91208b7d3017c52b36e55721Ruben Brunk    {
16775f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk        // Set up opcode List 2
16785f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk        OpcodeListBuilder builder;
16795f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk        status_t err = OK;
16805f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk
16815f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk        // Set up lens shading map
1682f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        camera_metadata_entry entry1 =
1683f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk                characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
168447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
168547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        uint32_t lsmWidth = 0;
168647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        uint32_t lsmHeight = 0;
168747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
168847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        if (entry1.count != 0) {
168947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
169047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
169147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        }
1692f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
16939ce22a095685a581823316457217a318fb40c754Ruben Brunk        camera_metadata_entry entry2 = results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
169447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
16952079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        camera_metadata_entry entry =
16962079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
16972079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_IMAGEWIDTH, writer);
16982079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        uint32_t xmin = static_cast<uint32_t>(entry.data.i32[0]);
16992079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        uint32_t ymin = static_cast<uint32_t>(entry.data.i32[1]);
17002079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
17012079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
170247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        if (entry2.count > 0 && entry2.count == lsmWidth * lsmHeight * 4) {
1703c03443b23de0b474080168d892cd685283c3285bRuben Brunk            // GainMap rectangle is relative to the active area origin.
17045f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk            err = builder.addGainMapsForMetadata(lsmWidth,
17055f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk                                                 lsmHeight,
1706c03443b23de0b474080168d892cd685283c3285bRuben Brunk                                                 0,
1707c03443b23de0b474080168d892cd685283c3285bRuben Brunk                                                 0,
17082079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                                                 height,
17092079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                                                 width,
17105f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk                                                 opcodeCfaLayout,
17115f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk                                                 entry2.data.f);
17125f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk            if (err != OK) {
1713f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk                ALOGE("%s: Could not add Lens shading map.", __FUNCTION__);
1714f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk                jniThrowRuntimeException(env, "failed to add lens shading map.");
17152079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                return nullptr;
17165f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk            }
17175f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk        }
17185f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk
17199ce22a095685a581823316457217a318fb40c754Ruben Brunk
17209ce22a095685a581823316457217a318fb40c754Ruben Brunk        // Set up bad pixel correction list
17219ce22a095685a581823316457217a318fb40c754Ruben Brunk        camera_metadata_entry entry3 = characteristics.find(ANDROID_STATISTICS_HOT_PIXEL_MAP);
17229ce22a095685a581823316457217a318fb40c754Ruben Brunk
17239ce22a095685a581823316457217a318fb40c754Ruben Brunk        if ((entry3.count % 2) != 0) {
17249ce22a095685a581823316457217a318fb40c754Ruben Brunk            ALOGE("%s: Hot pixel map contains odd number of values, cannot map to pairs!",
17259ce22a095685a581823316457217a318fb40c754Ruben Brunk                    __FUNCTION__);
17269ce22a095685a581823316457217a318fb40c754Ruben Brunk            jniThrowRuntimeException(env, "failed to add hotpixel map.");
17279ce22a095685a581823316457217a318fb40c754Ruben Brunk            return nullptr;
17289ce22a095685a581823316457217a318fb40c754Ruben Brunk        }
17299ce22a095685a581823316457217a318fb40c754Ruben Brunk
17309ce22a095685a581823316457217a318fb40c754Ruben Brunk        // Adjust the bad pixel coordinates to be relative to the origin of the active area DNG tag
17319ce22a095685a581823316457217a318fb40c754Ruben Brunk        std::vector<uint32_t> v;
17329ce22a095685a581823316457217a318fb40c754Ruben Brunk        for (size_t i = 0; i < entry3.count; i+=2) {
17339ce22a095685a581823316457217a318fb40c754Ruben Brunk            int32_t x = entry3.data.i32[i];
17349ce22a095685a581823316457217a318fb40c754Ruben Brunk            int32_t y = entry3.data.i32[i + 1];
17359ce22a095685a581823316457217a318fb40c754Ruben Brunk            x -= static_cast<int32_t>(xmin);
17369ce22a095685a581823316457217a318fb40c754Ruben Brunk            y -= static_cast<int32_t>(ymin);
17379ce22a095685a581823316457217a318fb40c754Ruben Brunk            if (x < 0 || y < 0 || static_cast<uint32_t>(x) >= width ||
17389ce22a095685a581823316457217a318fb40c754Ruben Brunk                    static_cast<uint32_t>(y) >= width) {
17399ce22a095685a581823316457217a318fb40c754Ruben Brunk                continue;
17409ce22a095685a581823316457217a318fb40c754Ruben Brunk            }
17419ce22a095685a581823316457217a318fb40c754Ruben Brunk            v.push_back(x);
17429ce22a095685a581823316457217a318fb40c754Ruben Brunk            v.push_back(y);
17439ce22a095685a581823316457217a318fb40c754Ruben Brunk        }
17449ce22a095685a581823316457217a318fb40c754Ruben Brunk        const uint32_t* badPixels = &v[0];
17459ce22a095685a581823316457217a318fb40c754Ruben Brunk        uint32_t badPixelCount = v.size();
17469ce22a095685a581823316457217a318fb40c754Ruben Brunk
17479ce22a095685a581823316457217a318fb40c754Ruben Brunk        if (badPixelCount > 0) {
17489ce22a095685a581823316457217a318fb40c754Ruben Brunk            err = builder.addBadPixelListForMetadata(badPixels, badPixelCount, opcodeCfaLayout);
17499ce22a095685a581823316457217a318fb40c754Ruben Brunk
17509ce22a095685a581823316457217a318fb40c754Ruben Brunk            if (err != OK) {
17519ce22a095685a581823316457217a318fb40c754Ruben Brunk                ALOGE("%s: Could not add hotpixel map.", __FUNCTION__);
17529ce22a095685a581823316457217a318fb40c754Ruben Brunk                jniThrowRuntimeException(env, "failed to add hotpixel map.");
17539ce22a095685a581823316457217a318fb40c754Ruben Brunk                return nullptr;
17549ce22a095685a581823316457217a318fb40c754Ruben Brunk            }
17559ce22a095685a581823316457217a318fb40c754Ruben Brunk        }
17569ce22a095685a581823316457217a318fb40c754Ruben Brunk
17579ce22a095685a581823316457217a318fb40c754Ruben Brunk
1758fe81662cc5c0f04ecd2ef2d96b1d8890500d69a5Ruben Brunk        size_t listSize = builder.getSize();
1759fe81662cc5c0f04ecd2ef2d96b1d8890500d69a5Ruben Brunk        uint8_t opcodeListBuf[listSize];
1760fe81662cc5c0f04ecd2ef2d96b1d8890500d69a5Ruben Brunk        err = builder.buildOpList(opcodeListBuf);
1761fe81662cc5c0f04ecd2ef2d96b1d8890500d69a5Ruben Brunk        if (err == OK) {
17622079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf,
1763fe81662cc5c0f04ecd2ef2d96b1d8890500d69a5Ruben Brunk                    TIFF_IFD_0), env, TAG_OPCODELIST2, writer);
1764fe81662cc5c0f04ecd2ef2d96b1d8890500d69a5Ruben Brunk        } else {
1765fe81662cc5c0f04ecd2ef2d96b1d8890500d69a5Ruben Brunk            ALOGE("%s: Could not build list of opcodes for distortion correction and lens shading"
1766fe81662cc5c0f04ecd2ef2d96b1d8890500d69a5Ruben Brunk                    "map.", __FUNCTION__);
1767fe81662cc5c0f04ecd2ef2d96b1d8890500d69a5Ruben Brunk            jniThrowRuntimeException(env, "failed to construct opcode list for distortion"
1768fe81662cc5c0f04ecd2ef2d96b1d8890500d69a5Ruben Brunk                    " correction and lens shading map");
17692079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            return nullptr;
1770fe81662cc5c0f04ecd2ef2d96b1d8890500d69a5Ruben Brunk        }
1771fe81662cc5c0f04ecd2ef2d96b1d8890500d69a5Ruben Brunk    }
1772fe81662cc5c0f04ecd2ef2d96b1d8890500d69a5Ruben Brunk
1773fe81662cc5c0f04ecd2ef2d96b1d8890500d69a5Ruben Brunk    {
1774fe81662cc5c0f04ecd2ef2d96b1d8890500d69a5Ruben Brunk        // Set up opcode List 3
1775fe81662cc5c0f04ecd2ef2d96b1d8890500d69a5Ruben Brunk        OpcodeListBuilder builder;
1776fe81662cc5c0f04ecd2ef2d96b1d8890500d69a5Ruben Brunk        status_t err = OK;
1777fe81662cc5c0f04ecd2ef2d96b1d8890500d69a5Ruben Brunk
17785f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk        // Set up rectilinear distortion correction
17795f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk        camera_metadata_entry entry3 =
17805f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk                results.find(ANDROID_LENS_RADIAL_DISTORTION);
17815f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk        camera_metadata_entry entry4 =
17825f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk                results.find(ANDROID_LENS_INTRINSIC_CALIBRATION);
17835f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk
17845f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk        if (entry3.count == 6 && entry4.count == 5) {
17855f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk            float cx = entry4.data.f[/*c_x*/2];
17865f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk            float cy = entry4.data.f[/*c_y*/3];
17872079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            err = builder.addWarpRectilinearForMetadata(entry3.data.f, preWidth, preHeight, cx,
17885f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk                    cy);
17895f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk            if (err != OK) {
17905f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk                ALOGE("%s: Could not add distortion correction.", __FUNCTION__);
17915f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk                jniThrowRuntimeException(env, "failed to add distortion correction.");
17922079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                return nullptr;
1793f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            }
17945f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk        }
17955f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk
17965f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk        size_t listSize = builder.getSize();
17975f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk        uint8_t opcodeListBuf[listSize];
17985f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk        err = builder.buildOpList(opcodeListBuf);
17995f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk        if (err == OK) {
18002079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_OPCODELIST3, listSize, opcodeListBuf,
1801fe81662cc5c0f04ecd2ef2d96b1d8890500d69a5Ruben Brunk                    TIFF_IFD_0), env, TAG_OPCODELIST3, writer);
1802f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        } else {
18035f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk            ALOGE("%s: Could not build list of opcodes for distortion correction and lens shading"
18045f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk                    "map.", __FUNCTION__);
18055f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk            jniThrowRuntimeException(env, "failed to construct opcode list for distortion"
18065f2368d6def80ad18ee77cdf0e451c0e9f41e322Ruben Brunk                    " correction and lens shading map");
18072079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            return nullptr;
1808f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        }
1809f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
1810f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
18112079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    {
18122079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        // Set up orientation tags.
18138c35d5be63ab1811679875f12b19b050ac163b18Eino-Ville Talvala        // Note: There's only one orientation field for the whole file, in IFD0
18148c35d5be63ab1811679875f12b19b050ac163b18Eino-Ville Talvala        // The main image and any thumbnails therefore have the same orientation.
18152079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        uint16_t orientation = nativeContext->getOrientation();
18162079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0),
18172079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                env, TAG_ORIENTATION, writer);
18182079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
18192079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    }
18202079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
18212079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    if (nativeContext->hasDescription()){
18222079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        // Set Description
18232079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        String8 description = nativeContext->getDescription();
18242079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        size_t len = description.bytes() + 1;
18252079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        if (writer->addEntry(TAG_IMAGEDESCRIPTION, len,
18262079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                reinterpret_cast<const uint8_t*>(description.string()), TIFF_IFD_0) != OK) {
18272079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
18282079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    "Invalid metadata for tag %x", TAG_IMAGEDESCRIPTION);
18292079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        }
18302079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    }
18312079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
18322079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    if (nativeContext->hasGpsData()) {
18332079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        // Set GPS tags
18342079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        GpsData gpsData = nativeContext->getGpsData();
18352079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        if (!writer->hasIfd(TIFF_IFD_GPSINFO)) {
18362079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_GPSINFO, TiffWriter::GPSINFO) != OK) {
18372079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                ALOGE("%s: Failed to add GpsInfo IFD %u to IFD %u", __FUNCTION__, TIFF_IFD_GPSINFO,
18382079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                        TIFF_IFD_0);
18392079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                jniThrowException(env, "java/lang/IllegalStateException", "Failed to add GPSINFO");
18402079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                return nullptr;
18412079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            }
18422079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        }
18432079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
18442079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        {
18452079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            uint8_t version[] = {2, 3, 0, 0};
18462079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSVERSIONID, 4, version,
18472079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    TIFF_IFD_GPSINFO), env, TAG_GPSVERSIONID, writer);
18482079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        }
18492079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
18502079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        {
18512079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLATITUDEREF,
18522079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    GpsData::GPS_REF_LENGTH, gpsData.mLatitudeRef, TIFF_IFD_GPSINFO), env,
18532079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    TAG_GPSLATITUDEREF, writer);
18542079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        }
18552079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
18562079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        {
18572079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLONGITUDEREF,
18582079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    GpsData::GPS_REF_LENGTH, gpsData.mLongitudeRef, TIFF_IFD_GPSINFO), env,
18592079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    TAG_GPSLONGITUDEREF, writer);
18602079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        }
18612079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
18622079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        {
18632079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLATITUDE, 3, gpsData.mLatitude,
18642079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDE, writer);
18652079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        }
18662079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
18672079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        {
18682079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLONGITUDE, 3, gpsData.mLongitude,
18692079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDE, writer);
18702079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        }
18712079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
18722079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        {
18732079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSTIMESTAMP, 3, gpsData.mTimestamp,
18742079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    TIFF_IFD_GPSINFO), env, TAG_GPSTIMESTAMP, writer);
18752079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        }
18762079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
18772079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        {
18782079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSDATESTAMP,
18792079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    GpsData::GPS_DATE_LENGTH, gpsData.mDate, TIFF_IFD_GPSINFO), env,
18802079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    TAG_GPSDATESTAMP, writer);
18812079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        }
18822079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    }
18832079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
18842079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
18852079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    if (nativeContext->hasThumbnail()) {
18862079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        if (!writer->hasIfd(TIFF_IFD_SUB1)) {
18872079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_SUB1) != OK) {
18882079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                ALOGE("%s: Failed to add SubIFD %u to IFD %u", __FUNCTION__, TIFF_IFD_SUB1,
18892079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                        TIFF_IFD_0);
18902079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                jniThrowException(env, "java/lang/IllegalStateException", "Failed to add SubIFD");
18912079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                return nullptr;
18922079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            }
18932079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        }
18942079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
18952079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        Vector<uint16_t> tagsToMove;
18962079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        tagsToMove.add(TAG_NEWSUBFILETYPE);
18972079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        tagsToMove.add(TAG_ACTIVEAREA);
18982079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        tagsToMove.add(TAG_BITSPERSAMPLE);
18992079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        tagsToMove.add(TAG_COMPRESSION);
19002079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        tagsToMove.add(TAG_IMAGEWIDTH);
19012079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        tagsToMove.add(TAG_IMAGELENGTH);
19022079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        tagsToMove.add(TAG_PHOTOMETRICINTERPRETATION);
19032079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        tagsToMove.add(TAG_BLACKLEVEL);
19042079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        tagsToMove.add(TAG_BLACKLEVELREPEATDIM);
19052079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        tagsToMove.add(TAG_SAMPLESPERPIXEL);
19062079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        tagsToMove.add(TAG_PLANARCONFIGURATION);
19072079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        tagsToMove.add(TAG_CFAREPEATPATTERNDIM);
19082079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        tagsToMove.add(TAG_CFAPATTERN);
19092079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        tagsToMove.add(TAG_CFAPLANECOLOR);
19102079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        tagsToMove.add(TAG_CFALAYOUT);
19112079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        tagsToMove.add(TAG_XRESOLUTION);
19122079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        tagsToMove.add(TAG_YRESOLUTION);
19132079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        tagsToMove.add(TAG_RESOLUTIONUNIT);
19142079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        tagsToMove.add(TAG_WHITELEVEL);
19152079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        tagsToMove.add(TAG_DEFAULTSCALE);
19162079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        tagsToMove.add(TAG_DEFAULTCROPORIGIN);
19172079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        tagsToMove.add(TAG_DEFAULTCROPSIZE);
19182079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        tagsToMove.add(TAG_OPCODELIST2);
19192079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        tagsToMove.add(TAG_OPCODELIST3);
19202079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
19212079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        if (moveEntries(writer, TIFF_IFD_0, TIFF_IFD_SUB1, tagsToMove) != OK) {
19222079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            jniThrowException(env, "java/lang/IllegalStateException", "Failed to move entries");
19232079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            return nullptr;
19242079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        }
19252079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
19262079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        // Setup thumbnail tags
19272079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
19282079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        {
19292079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            // Set photometric interpretation
19302079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            uint16_t interpretation = 2; // RGB
19312079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1,
19322079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    &interpretation, TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
19332079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        }
19342079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
19352079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        {
19362079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            // Set planar configuration
19372079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            uint16_t config = 1; // Chunky
19382079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config,
19392079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    TIFF_IFD_0), env, TAG_PLANARCONFIGURATION, writer);
19402079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        }
19412079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
19422079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        {
19432079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            // Set samples per pixel
19442079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            uint16_t samples = SAMPLES_PER_RGB_PIXEL;
19452079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples,
19462079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    TIFF_IFD_0), env, TAG_SAMPLESPERPIXEL, writer);
19472079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        }
19482079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
19492079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        {
19502079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            // Set bits per sample
19518069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala            uint16_t bits[SAMPLES_PER_RGB_PIXEL];
19528069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala            for (int i = 0; i < SAMPLES_PER_RGB_PIXEL; i++) bits[i] = BITS_PER_RGB_SAMPLE;
19538069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala            BAIL_IF_INVALID_RET_NULL_SP(
19548069b1f252d6a1e1c469dfc733bdd0db5d69574fEino-Ville Talvala                    writer->addEntry(TAG_BITSPERSAMPLE, SAMPLES_PER_RGB_PIXEL, bits, TIFF_IFD_0),
19552079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    env, TAG_BITSPERSAMPLE, writer);
19562079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        }
19572079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
19582079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        {
19592079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            // Set subfiletype
19602079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            uint32_t subfileType = 1; // Thumbnail image
19612079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType,
19622079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    TIFF_IFD_0), env, TAG_NEWSUBFILETYPE, writer);
19632079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        }
19642079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
19652079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        {
19662079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            // Set compression
19672079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            uint16_t compression = 1; // None
19682079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COMPRESSION, 1, &compression,
19692079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    TIFF_IFD_0), env, TAG_COMPRESSION, writer);
19702079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        }
19712079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
19722079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        {
19732079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            // Set dimensions
19742079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            uint32_t uWidth = nativeContext->getThumbnailWidth();
19752079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            uint32_t uHeight = nativeContext->getThumbnailHeight();
19762079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEWIDTH, 1, &uWidth, TIFF_IFD_0),
19772079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    env, TAG_IMAGEWIDTH, writer);
19782079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGELENGTH, 1, &uHeight, TIFF_IFD_0),
19792079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    env, TAG_IMAGELENGTH, writer);
19802079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        }
19812079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
19822079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        {
19832079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            // x resolution
19842079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            uint32_t xres[] = { 72, 1 }; // default 72 ppi
19852079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
19862079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    env, TAG_XRESOLUTION, writer);
19872079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
19882079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            // y resolution
19892079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            uint32_t yres[] = { 72, 1 }; // default 72 ppi
19902079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
19912079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    env, TAG_YRESOLUTION, writer);
19922079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
19932079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            uint16_t unit = 2; // inches
19942079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
19952079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    env, TAG_RESOLUTIONUNIT, writer);
19962079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        }
19972079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    }
19982079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
19992079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    if (writer->addStrip(TIFF_IFD_0) != OK) {
20002079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        ALOGE("%s: Could not setup thumbnail strip tags.", __FUNCTION__);
20012079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        jniThrowException(env, "java/lang/IllegalStateException",
20022079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                "Failed to setup thumbnail strip tags.");
20032079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        return nullptr;
20042079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    }
20052079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
20062079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    if (writer->hasIfd(TIFF_IFD_SUB1)) {
20072079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        if (writer->addStrip(TIFF_IFD_SUB1) != OK) {
20082079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            ALOGE("%s: Could not main image strip tags.", __FUNCTION__);
20092079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            jniThrowException(env, "java/lang/IllegalStateException",
20102079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk                    "Failed to setup main image strip tags.");
20112079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            return nullptr;
20122079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        }
20132079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    }
20142079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    return writer;
2015f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk}
2016f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
2017f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunkstatic void DngCreator_destroy(JNIEnv* env, jobject thiz) {
2018f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    ALOGV("%s:", __FUNCTION__);
20192079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    DngCreator_setNativeContext(env, thiz, nullptr);
2020f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk}
2021f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
202247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkstatic void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz, jint orient) {
2023f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    ALOGV("%s:", __FUNCTION__);
2024f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
20252079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    NativeContext* context = DngCreator_getNativeContext(env, thiz);
20262079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    if (context == nullptr) {
202747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
202847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        jniThrowException(env, "java/lang/AssertionError",
202947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                "setOrientation called with uninitialized DngCreator");
203047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        return;
203147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
203247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
203347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint16_t orientation = static_cast<uint16_t>(orient);
20342079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    context->setOrientation(orientation);
2035f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk}
2036f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
203747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkstatic void DngCreator_nativeSetDescription(JNIEnv* env, jobject thiz, jstring description) {
2038f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    ALOGV("%s:", __FUNCTION__);
203947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
20402079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    NativeContext* context = DngCreator_getNativeContext(env, thiz);
20412079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    if (context == nullptr) {
204247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
204347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        jniThrowException(env, "java/lang/AssertionError",
204447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                "setDescription called with uninitialized DngCreator");
204547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        return;
204647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
204747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
20482079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    const char* desc = env->GetStringUTFChars(description, nullptr);
20492079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    context->setDescription(String8(desc));
205047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    env->ReleaseStringUTFChars(description, desc);
2051f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk}
2052f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
20532079612e5851d73f4672ae3729c883a58adc4dddRuben Brunkstatic void DngCreator_nativeSetGpsTags(JNIEnv* env, jobject thiz, jintArray latTag,
20542079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        jstring latRef, jintArray longTag, jstring longRef, jstring dateTag, jintArray timeTag) {
2055f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    ALOGV("%s:", __FUNCTION__);
2056f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
20572079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    NativeContext* context = DngCreator_getNativeContext(env, thiz);
20582079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    if (context == nullptr) {
205947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
206047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        jniThrowException(env, "java/lang/AssertionError",
206147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                "setGpsTags called with uninitialized DngCreator");
2062f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        return;
2063f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
2064f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
20652079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    GpsData data;
206647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
206747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    jsize latLen = env->GetArrayLength(latTag);
206847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    jsize longLen = env->GetArrayLength(longTag);
206947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    jsize timeLen = env->GetArrayLength(timeTag);
20702079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    if (latLen != GpsData::GPS_VALUE_LENGTH) {
207147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        jniThrowException(env, "java/lang/IllegalArgumentException",
207247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                "invalid latitude tag length");
207347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        return;
20742079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    } else if (longLen != GpsData::GPS_VALUE_LENGTH) {
207547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        jniThrowException(env, "java/lang/IllegalArgumentException",
207647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                "invalid longitude tag length");
2077f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        return;
20782079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    } else if (timeLen != GpsData::GPS_VALUE_LENGTH) {
207947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        jniThrowException(env, "java/lang/IllegalArgumentException",
208047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                "invalid time tag length");
208147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        return;
208247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
208347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
20842079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    env->GetIntArrayRegion(latTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
20852079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            reinterpret_cast<jint*>(&data.mLatitude));
20862079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    env->GetIntArrayRegion(longTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
20872079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            reinterpret_cast<jint*>(&data.mLongitude));
20882079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    env->GetIntArrayRegion(timeTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
20892079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            reinterpret_cast<jint*>(&data.mTimestamp));
209047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
209147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
20922079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    env->GetStringUTFRegion(latRef, 0, 1, reinterpret_cast<char*>(&data.mLatitudeRef));
20932079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    data.mLatitudeRef[GpsData::GPS_REF_LENGTH - 1] = '\0';
20942079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    env->GetStringUTFRegion(longRef, 0, 1, reinterpret_cast<char*>(&data.mLongitudeRef));
20952079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    data.mLongitudeRef[GpsData::GPS_REF_LENGTH - 1] = '\0';
20962079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    env->GetStringUTFRegion(dateTag, 0, GpsData::GPS_DATE_LENGTH - 1,
20972079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk            reinterpret_cast<char*>(&data.mDate));
20982079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    data.mDate[GpsData::GPS_DATE_LENGTH - 1] = '\0';
209947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
21002079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    context->setGpsData(data);
210147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk}
210247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
210347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkstatic void DngCreator_nativeSetThumbnail(JNIEnv* env, jobject thiz, jobject buffer, jint width,
210447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        jint height) {
210547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    ALOGV("%s:", __FUNCTION__);
210647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
210747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    NativeContext* context = DngCreator_getNativeContext(env, thiz);
21082079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    if (context == nullptr) {
2109f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2110f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        jniThrowException(env, "java/lang/AssertionError",
211147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                "setThumbnail called with uninitialized DngCreator");
2112f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        return;
2113f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
211447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
211547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    size_t fullSize = width * height * BYTES_PER_RGB_PIXEL;
211647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    jlong capacity = env->GetDirectBufferCapacity(buffer);
21170f0b4919667f418b249c497f5ad3e83fdf4437e5Andreas Gampe    if (static_cast<uint64_t>(capacity) != static_cast<uint64_t>(fullSize)) {
211847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        jniThrowExceptionFmt(env, "java/lang/AssertionError",
211947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                "Invalid size %d for thumbnail, expected size was %d",
212047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                capacity, fullSize);
2121f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        return;
2122f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
2123f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
212447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
21252079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    if (pixelBytes == nullptr) {
212647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
212747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
2128f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        return;
2129f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
2130f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
213147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    if (!context->setThumbnail(pixelBytes, width, height)) {
213247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        jniThrowException(env, "java/lang/IllegalStateException",
213347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                "Failed to set thumbnail.");
2134f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        return;
2135f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
213647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk}
2137f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
213847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk// TODO: Refactor out common preamble for the two nativeWrite methods.
213947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunkstatic void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
214047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        jint height, jobject inBuffer, jint rowStride, jint pixStride, jlong offset,
214147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        jboolean isDirect) {
214247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    ALOGV("%s:", __FUNCTION__);
214346d8444631b4b1253a76bfcc78a29d26014d022fDan Albert    ALOGV("%s: nativeWriteImage called with: width=%d, height=%d, "
214446d8444631b4b1253a76bfcc78a29d26014d022fDan Albert          "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width,
214546d8444631b4b1253a76bfcc78a29d26014d022fDan Albert          height, rowStride, pixStride, offset);
214647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t rStride = static_cast<uint32_t>(rowStride);
214747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t pStride = static_cast<uint32_t>(pixStride);
214847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t uWidth = static_cast<uint32_t>(width);
214947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t uHeight = static_cast<uint32_t>(height);
215047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint64_t uOffset = static_cast<uint64_t>(offset);
215147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
215247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    sp<JniOutputStream> out = new JniOutputStream(env, outStream);
215347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    if(env->ExceptionCheck()) {
215447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
2155f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        return;
2156f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
2157f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
215847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    NativeContext* context = DngCreator_getNativeContext(env, thiz);
21592079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    if (context == nullptr) {
216047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
216147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        jniThrowException(env, "java/lang/AssertionError",
216247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                "Write called with uninitialized DngCreator");
216347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        return;
216447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
21652079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    sp<TiffWriter> writer = DngCreator_setup(env, thiz, uWidth, uHeight);
216647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
21672079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    if (writer.get() == nullptr) {
21682079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        return;
21692079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    }
21702079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
21712079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    // Validate DNG size
2172b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    if (!validateDngHeader(env, writer, *(context->getCharacteristics()), width, height)) {
217347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        return;
217447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
217547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
217647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    sp<JniInputByteBuffer> inBuf;
217747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    Vector<StripSource*> sources;
217847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    sp<DirectStripSource> thumbnailSource;
217947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t targetIfd = TIFF_IFD_0;
218047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
218147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
218247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
218347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    if (hasThumbnail) {
218447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
218547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
218647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        uint32_t thumbWidth = context->getThumbnailWidth();
218747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
218847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                thumbWidth, context->getThumbnailHeight(), bytesPerPixel,
218947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                bytesPerPixel * thumbWidth, /*offset*/0, BYTES_PER_RGB_SAMPLE,
219047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                SAMPLES_PER_RGB_PIXEL);
219147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        sources.add(thumbnailSource.get());
219247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        targetIfd = TIFF_IFD_SUB1;
219347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
219447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
219547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    if (isDirect) {
219647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        size_t fullSize = rStride * uHeight;
219747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        jlong capacity = env->GetDirectBufferCapacity(inBuffer);
219847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        if (capacity < 0 || fullSize + uOffset > static_cast<uint64_t>(capacity)) {
219947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
220047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                    "Invalid size %d for Image, size given in metadata is %d at current stride",
220147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                    capacity, fullSize);
2202f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            return;
2203f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        }
220447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
220547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
22062079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        if (pixelBytes == nullptr) {
220747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
220847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
220947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            return;
221047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        }
221147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
221247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        ALOGV("%s: Using direct-type strip source.", __FUNCTION__);
221347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        DirectStripSource stripSource(env, pixelBytes, targetIfd, uWidth, uHeight, pStride,
221447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
221547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        sources.add(&stripSource);
221647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
221747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        status_t ret = OK;
221847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
221947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
222047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            if (!env->ExceptionCheck()) {
222147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                jniThrowExceptionFmt(env, "java/io/IOException",
222247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                        "Encountered error %d while writing file.", ret);
2223f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            }
222447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            return;
2225f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        }
2226f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    } else {
222747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        inBuf = new JniInputByteBuffer(env, inBuffer);
222847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
222947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        ALOGV("%s: Using input-type strip source.", __FUNCTION__);
223047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        InputStripSource stripSource(env, *inBuf, targetIfd, uWidth, uHeight, pStride,
223147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
223247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        sources.add(&stripSource);
223347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
223447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        status_t ret = OK;
223547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
223647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
223747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            if (!env->ExceptionCheck()) {
223847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                jniThrowExceptionFmt(env, "java/io/IOException",
223947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                        "Encountered error %d while writing file.", ret);
2240f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            }
224147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            return;
2242f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk        }
2243f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    }
2244f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk}
2245f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
2246f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunkstatic void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
224747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        jobject inStream, jint width, jint height, jlong offset) {
2248f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    ALOGV("%s:", __FUNCTION__);
224947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
225047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t rowStride = width * BYTES_PER_SAMPLE;
225147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t pixStride = BYTES_PER_SAMPLE;
225247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t uWidth = static_cast<uint32_t>(width);
225347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t uHeight = static_cast<uint32_t>(height);
225447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint64_t uOffset = static_cast<uint32_t>(offset);
225547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
225646d8444631b4b1253a76bfcc78a29d26014d022fDan Albert    ALOGV("%s: nativeWriteInputStream called with: width=%d, height=%d, "
225746d8444631b4b1253a76bfcc78a29d26014d022fDan Albert          "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width,
225846d8444631b4b1253a76bfcc78a29d26014d022fDan Albert          height, rowStride, pixStride, offset);
225947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
226047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    sp<JniOutputStream> out = new JniOutputStream(env, outStream);
226146d8444631b4b1253a76bfcc78a29d26014d022fDan Albert    if (env->ExceptionCheck()) {
226247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
226347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        return;
226447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
226547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
226647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    NativeContext* context = DngCreator_getNativeContext(env, thiz);
22672079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    if (context == nullptr) {
226847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
226947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        jniThrowException(env, "java/lang/AssertionError",
227047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                "Write called with uninitialized DngCreator");
227147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        return;
227247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
22732079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    sp<TiffWriter> writer = DngCreator_setup(env, thiz, uWidth, uHeight);
22742079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk
22752079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    if (writer.get() == nullptr) {
22762079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk        return;
22772079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    }
227847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
22792079612e5851d73f4672ae3729c883a58adc4dddRuben Brunk    // Validate DNG size
2280b8f4c6ab1e99a44a51af26dc522819bb833825abRuben Brunk    if (!validateDngHeader(env, writer, *(context->getCharacteristics()), width, height)) {
228147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        return;
228247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
228347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
228447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    sp<DirectStripSource> thumbnailSource;
228547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    uint32_t targetIfd = TIFF_IFD_0;
228647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
228747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    Vector<StripSource*> sources;
228847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
228947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    if (hasThumbnail) {
229047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
229147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
229247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        uint32_t width = context->getThumbnailWidth();
229347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
229447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                width, context->getThumbnailHeight(), bytesPerPixel,
229547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                bytesPerPixel * width, /*offset*/0, BYTES_PER_RGB_SAMPLE,
229647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                SAMPLES_PER_RGB_PIXEL);
229747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        sources.add(thumbnailSource.get());
229847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        targetIfd = TIFF_IFD_SUB1;
229947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
230047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
230147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    sp<JniInputStream> in = new JniInputStream(env, inStream);
230247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
230347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    ALOGV("%s: Using input-type strip source.", __FUNCTION__);
230447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    InputStripSource stripSource(env, *in, targetIfd, uWidth, uHeight, pixStride,
230547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk             rowStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
230647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    sources.add(&stripSource);
230747e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk
230847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    status_t ret = OK;
230947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
231047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
231147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        if (!env->ExceptionCheck()) {
231247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            jniThrowExceptionFmt(env, "java/io/IOException",
231347e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk                    "Encountered error %d while writing file.", ret);
231447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        }
231547e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk        return;
231647e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    }
2317f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk}
2318f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
2319f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk} /*extern "C" */
2320f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
232176f6a86de25e1bf74717e047e55fd44b089673f3Daniel Micaystatic const JNINativeMethod gDngCreatorMethods[] = {
2322f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {"nativeClassInit",        "()V", (void*) DngCreator_nativeClassInit},
2323f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
2324b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk            "Landroid/hardware/camera2/impl/CameraMetadataNative;Ljava/lang/String;)V",
2325b8df8e07d6fc530c82d21ca3199411e2e60975b1Ruben Brunk            (void*) DngCreator_init},
2326f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {"nativeDestroy",           "()V",      (void*) DngCreator_destroy},
2327f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk    {"nativeSetOrientation",    "(I)V",     (void*) DngCreator_nativeSetOrientation},
232847e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    {"nativeSetDescription",    "(Ljava/lang/String;)V", (void*) DngCreator_nativeSetDescription},
232947e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    {"nativeSetGpsTags",    "([ILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;[I)V",
233047e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk            (void*) DngCreator_nativeSetGpsTags},
233147e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    {"nativeSetThumbnail","(Ljava/nio/ByteBuffer;II)V", (void*) DngCreator_nativeSetThumbnail},
233247e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    {"nativeWriteImage",        "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;IIJZ)V",
2333f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            (void*) DngCreator_nativeWriteImage},
233447e91f20952e5eb2290146ba6e33a694dd2e45e8Ruben Brunk    {"nativeWriteInputStream",    "(Ljava/io/OutputStream;Ljava/io/InputStream;IIJ)V",
2335f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk            (void*) DngCreator_nativeWriteInputStream},
2336f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk};
2337f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk
2338b6079005ed0631c3972ff427f56e12523ec214a7Ruben Brunkint register_android_hardware_camera2_DngCreator(JNIEnv *env) {
2339ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe    return RegisterMethodsOrDie(env,
2340ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe            "android/hardware/camera2/DngCreator", gDngCreatorMethods, NELEM(gDngCreatorMethods));
2341f967a5486a78db244624fde4c105aa5e6fa914b9Ruben Brunk}
2342