//#define LOG_NDEBUG 0 #define LOG_TAG "DngCreator_JNI" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // #include "core_jni_helpers.h" // #include "android_runtime/AndroidRuntime.h" // #include "android_runtime/android_hardware_camera2_CameraMetadata.h" #include // #include using namespace android; using namespace img_utils; // using android::base::GetProperty; /** * Max width or height dimension for thumbnails. */ // max pixel dimension for TIFF/EP #define MAX_THUMBNAIL_DIMENSION 256 // bytes per sample #define DEFAULT_PIXEL_STRIDE 2 // byts per pixel #define BYTES_PER_RGB_PIX 3 #define GPS_LAT_REF_NORTH "N" #define GPS_LAT_REF_SOUTH "S" #define GPS_LONG_REF_EAST "E" #define GPS_LONG_REF_WEST "W" #define GPS_DATE_FORMAT_STR "yyyy:MM:dd" #define TIFF_DATETIME_FORMAT "yyyy:MM:dd kk:mm:ss" class ByteVectorOutput : public Output { public: ByteVectorOutput(std::vector& buf); virtual ~ByteVectorOutput(); virtual status_t open(); virtual status_t write(const uint8_t* buf, size_t offset, size_t count); virtual status_t close(); protected: std::vector& m_buf; }; class ByteVectorInput : public Input { public: ByteVectorInput(const std::vector& buf); virtual ~ByteVectorInput(); /** * Open this Input. * * Returns OK on success, or a negative error code. */ status_t open(); /** * Read bytes into the given buffer. At most, the number of bytes given in the * count argument will be read. Bytes will be written into the given buffer starting * at the index given in the offset argument. * * Returns the number of bytes read, or NOT_ENOUGH_DATA if at the end of the file. If an * error has occurred, this will return a negative error code other than NOT_ENOUGH_DATA. */ ssize_t read(uint8_t* buf, size_t offset, size_t count); /** * Skips bytes in the input. * * Returns the number of bytes skipped, or NOT_ENOUGH_DATA if at the end of the file. If an * error has occurred, this will return a negative error code other than NOT_ENOUGH_DATA. */ ssize_t skip(size_t count); /** * Close the Input. It is not valid to call open on a previously closed Input. * * Returns OK on success, or a negative error code. */ status_t close(); protected: const std::vector& m_buf; size_t m_offset; }; class ByteBufferInput : public Input { public: ByteBufferInput(const uint8_t* buf, size_t len); virtual ~ByteBufferInput(); /** * Open this Input. * * Returns OK on success, or a negative error code. */ status_t open(); /** * Read bytes into the given buffer. At most, the number of bytes given in the * count argument will be read. Bytes will be written into the given buffer starting * at the index given in the offset argument. * * Returns the number of bytes read, or NOT_ENOUGH_DATA if at the end of the file. If an * error has occurred, this will return a negative error code other than NOT_ENOUGH_DATA. */ ssize_t read(uint8_t* buf, size_t offset, size_t count); /** * Skips bytes in the input. * * Returns the number of bytes skipped, or NOT_ENOUGH_DATA if at the end of the file. If an * error has occurred, this will return a negative error code other than NOT_ENOUGH_DATA. */ ssize_t skip(size_t count); /** * Close the Input. It is not valid to call open on a previously closed Input. * * Returns OK on success, or a negative error code. */ status_t close(); protected: const uint8_t* m_buf; size_t m_len; size_t m_offset; }; struct SIZE { int width; int height; }; #define BAIL_IF_INVALID_RET_BOOL(expr, jnienv, tagId, writer) \ if ((expr) != OK) { \ return false; \ } #define BAIL_IF_INVALID_RET_NULL_SP(expr, jnienv, tagId, writer) \ if ((expr) != OK) { \ return nullptr; \ } #define BAIL_IF_INVALID_R(expr, jnienv, tagId, writer) \ if ((expr) != OK) { \ return -1; \ } #define BAIL_IF_EMPTY_RET_NULL_SP(entry, jnienv, tagId, writer) \ if ((entry).count == 0) { \ return nullptr; \ } #define BAIL_IF_EXPR_RET_NULL_SP(expr, jnienv, tagId, writer) \ if (expr) { \ return nullptr; \ } #define ANDROID_DNGCREATOR_CTX_JNI_ID "mNativeContext" enum { BITS_PER_SAMPLE = 16, BYTES_PER_SAMPLE = 2, BYTES_PER_RGB_PIXEL = 3, BITS_PER_RGB_SAMPLE = 8, BYTES_PER_RGB_SAMPLE = 1, SAMPLES_PER_RGB_PIXEL = 3, SAMPLES_PER_RAW_PIXEL = 1, TIFF_IFD_0 = 0, TIFF_IFD_SUB1 = 1, TIFF_IFD_GPSINFO = 2, }; /** * POD container class for GPS tag data. */ class GpsData { public: enum { GPS_VALUE_LENGTH = 6, GPS_REF_LENGTH = 2, GPS_DATE_LENGTH = 11, }; uint32_t mLatitude[GPS_VALUE_LENGTH]; uint32_t mLongitude[GPS_VALUE_LENGTH]; uint32_t mTimestamp[GPS_VALUE_LENGTH]; uint8_t mLatitudeRef[GPS_REF_LENGTH]; uint8_t mLongitudeRef[GPS_REF_LENGTH]; uint8_t mDate[GPS_DATE_LENGTH]; }; // ---------------------------------------------------------------------------- /** * Container class for the persistent native context. */ class NativeContext : public LightRefBase { public: enum { DATETIME_COUNT = 20, }; NativeContext(ACameraMetadata* characteristics, ACameraMetadata* result); virtual ~NativeContext(); TiffWriter* getWriter(); ACameraMetadata* getCharacteristics() const; ACameraMetadata* getResult() const; uint32_t getThumbnailWidth() const; uint32_t getThumbnailHeight() const; const uint8_t* getThumbnail() const; bool hasThumbnail() const; bool setThumbnail(const std::vector& buffer, uint32_t width, uint32_t height); void setOrientation(uint16_t orientation); uint16_t getOrientation() const; void setDescription(const std::string& desc); std::string getDescription() const; bool hasDescription() const; void setGpsData(const GpsData& data); GpsData getGpsData() const; bool hasGpsData() const; void setCaptureTime(const std::string& formattedCaptureTime); std::string getCaptureTime() const; bool hasCaptureTime() const; protected: std::vector mCurrentThumbnail; TiffWriter mWriter; ACameraMetadata* mCharacteristics; ACameraMetadata* mResult; uint32_t mThumbnailWidth; uint32_t mThumbnailHeight; uint16_t mOrientation; bool mThumbnailSet; bool mGpsSet; bool mDescriptionSet; bool mCaptureTimeSet; std::string mDescription; GpsData mGpsData; std::string mFormattedCaptureTime; }; class DngCreator : public NativeContext { public: DngCreator(ACameraMetadata* characteristics, ACameraMetadata* result); #if 0 void setLocation(Location location); #endif void writeInputStream(std::vector& dngOutput, SIZE size, const std::vector& pixels, long offset); void writeByteBuffer(std::vector& dngOutput, SIZE size, const std::vector& pixels, long offset); #if 0 void writeImage(OutputStream& dngOutput, AImage& pixels); #endif void close(); // private static final DateFormat sExifGPSDateStamp = new SimpleDateFormat(GPS_DATE_FORMAT_STR); // private static final DateFormat sDateTimeStampFormat = new SimpleDateFormat(TIFF_DATETIME_FORMAT); #if 0 static { sDateTimeStampFormat.setTimeZone(TimeZone.getDefault()); sExifGPSDateStamp.setTimeZone(TimeZone.getTimeZone("UTC")); } #endif /** * Offset, rowStride, and pixelStride are given in bytes. Height and width are given in pixels. */ void writeByteBuffer(int width, int height, const std::vector& pixels, std::vector& dngOutput, int pixelStride, int rowStride, long offset); /** * Generate a direct RGB {@link ByteBuffer} from a {@link Bitmap}. */ /** * Convert coordinate to EXIF GPS tag format. */ void toExifLatLong(double value, int data[6]); void init(ACameraMetadata* characteristics, ACameraMetadata* result, const std::string& captureTime); sp setup(uint32_t imageWidth, uint32_t imageHeight); void destroy(); void setGpsTags(const std::vector& latTag, const std::string& latRef, const std::vector& longTag, const std::string& longRef, const std::string& dateTag, const std::vector& timeTag); void writeImage(std::vector& out, uint32_t width, uint32_t height, const std::vector& rawBuffer, int rowStride, int pixStride, uint64_t offset, bool isDirect); void writeInputStream(std::vector& out, const std::vector& rawStream, uint32_t width, uint32_t height, long offset); void writeInputBuffer(std::vector& out, const uint8_t* rawBuffer, size_t bufferLen, uint32_t width, uint32_t height, long offset); };