You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
333 lines
9.3 KiB
C
333 lines
9.3 KiB
C
9 months ago
|
//#define LOG_NDEBUG 0
|
||
|
#define LOG_TAG "DngCreator_JNI"
|
||
|
#include <inttypes.h>
|
||
|
#include <string.h>
|
||
|
#include <algorithm>
|
||
|
#include <array>
|
||
|
#include <memory>
|
||
|
#include <vector>
|
||
|
#include <cmath>
|
||
|
#include <algorithm>
|
||
|
|
||
|
#include <camera/NdkCameraMetadata.h>
|
||
|
#include <img_utils/DngUtils.h>
|
||
|
#include <img_utils/TagDefinitions.h>
|
||
|
#include <img_utils/TiffIfd.h>
|
||
|
#include <img_utils/TiffWriter.h>
|
||
|
#include <img_utils/Output.h>
|
||
|
#include <img_utils/Input.h>
|
||
|
#include <img_utils/StripSource.h>
|
||
|
#include <sys/system_properties.h>
|
||
|
|
||
|
// #include "core_jni_helpers.h"
|
||
|
|
||
|
// #include "android_runtime/AndroidRuntime.h"
|
||
|
// #include "android_runtime/android_hardware_camera2_CameraMetadata.h"
|
||
|
|
||
|
#include <jni.h>
|
||
|
// #include <nativehelper/JNIHelp.h>
|
||
|
|
||
|
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<uint8_t>& 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<uint8_t>& m_buf;
|
||
|
};
|
||
|
|
||
|
class ByteVectorInput : public Input {
|
||
|
public:
|
||
|
ByteVectorInput(const std::vector<uint8_t>& 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<uint8_t>& 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<NativeContext> {
|
||
|
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<uint8_t>& 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<uint8_t> 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<uint8_t>& dngOutput, SIZE size, const std::vector<uint8_t>& pixels, long offset);
|
||
|
void writeByteBuffer(std::vector<uint8_t>& dngOutput, SIZE size, const std::vector<uint8_t>& 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<uint8_t>& pixels, std::vector<uint8_t>& 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<TiffWriter> setup(uint32_t imageWidth, uint32_t imageHeight);
|
||
|
void destroy();
|
||
|
void setGpsTags(const std::vector<int>& latTag, const std::string& latRef, const std::vector<int>& longTag, const std::string& longRef, const std::string& dateTag, const std::vector<int>& timeTag);
|
||
|
void writeImage(std::vector<uint8_t>& out, uint32_t width, uint32_t height, const std::vector<uint8_t>& rawBuffer, int rowStride, int pixStride, uint64_t offset, bool isDirect);
|
||
|
|
||
|
void writeInputStream(std::vector<uint8_t>& out, const std::vector<uint8_t>& rawStream, uint32_t width, uint32_t height, long offset);
|
||
|
|
||
|
void writeInputBuffer(std::vector<uint8_t>& out, const uint8_t* rawBuffer, size_t bufferLen, uint32_t width, uint32_t height, long offset);
|
||
|
|
||
|
};
|