实现DngCreator
parent
6d421a6cc1
commit
0779d47b36
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,332 @@
|
||||
//#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);
|
||||
|
||||
};
|
@ -0,0 +1,60 @@
|
||||
// Copyright 2014 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
cc_library_shared {
|
||||
name: "libimg_utils",
|
||||
|
||||
srcs: [
|
||||
"src/EndianUtils.cpp",
|
||||
"src/FileInput.cpp",
|
||||
"src/FileOutput.cpp",
|
||||
"src/SortedEntryVector.cpp",
|
||||
"src/Input.cpp",
|
||||
"src/Output.cpp",
|
||||
"src/Orderable.cpp",
|
||||
"src/TiffIfd.cpp",
|
||||
"src/TiffWritable.cpp",
|
||||
"src/TiffWriter.cpp",
|
||||
"src/TiffEntry.cpp",
|
||||
"src/TiffEntryImpl.cpp",
|
||||
"src/ByteArrayOutput.cpp",
|
||||
"src/DngUtils.cpp",
|
||||
"src/StripSource.cpp",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"liblog",
|
||||
"libutils",
|
||||
"libcutils",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Werror",
|
||||
"-fvisibility=hidden",
|
||||
],
|
||||
|
||||
product_variables: {
|
||||
debuggable: {
|
||||
// Enable assert() in eng builds
|
||||
cflags: [
|
||||
"-UNDEBUG",
|
||||
"-DLOG_NDEBUG=1",
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
export_include_dirs: ["include"],
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_BYTE_ARRAY_OUTPUT_H
|
||||
#define IMG_UTILS_BYTE_ARRAY_OUTPUT_H
|
||||
|
||||
#include <img_utils/Output.h>
|
||||
|
||||
#include <utils/Errors.h>
|
||||
// #include <utils/Vector.h>
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
/**
|
||||
* Utility class that accumulates written bytes into a buffer.
|
||||
*/
|
||||
class ANDROID_API ByteArrayOutput : public Output {
|
||||
public:
|
||||
|
||||
ByteArrayOutput();
|
||||
|
||||
virtual ~ByteArrayOutput();
|
||||
|
||||
/**
|
||||
* Open this ByteArrayOutput.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t open();
|
||||
|
||||
/**
|
||||
* Write bytes from the given buffer. The number of bytes given in the count
|
||||
* argument will be written. Bytes will be written from the given buffer starting
|
||||
* at the index given in the offset argument.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t write(const uint8_t* buf, size_t offset, size_t count);
|
||||
|
||||
/**
|
||||
* Close this ByteArrayOutput.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t close();
|
||||
|
||||
/**
|
||||
* Get current size of the array of bytes written.
|
||||
*/
|
||||
virtual size_t getSize() const;
|
||||
|
||||
/**
|
||||
* Get pointer to array of bytes written. It is not valid to use this pointer if
|
||||
* open, write, or close is called after this method.
|
||||
*/
|
||||
virtual const uint8_t* getArray() const;
|
||||
|
||||
protected:
|
||||
std::vector<uint8_t> mByteArray;
|
||||
};
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_BYTE_ARRAY_OUTPUT_H*/
|
@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_DNG_UTILS_H
|
||||
#define IMG_UTILS_DNG_UTILS_H
|
||||
|
||||
#include <img_utils/ByteArrayOutput.h>
|
||||
#include <img_utils/EndianUtils.h>
|
||||
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/RefBase.h>
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
#define NELEMS(x) ((int) (sizeof(x) / sizeof((x)[0])))
|
||||
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
|
||||
|
||||
/**
|
||||
* Utility class for building values for the OpcodeList tags specified
|
||||
* in the Adobe DNG 1.4 spec.
|
||||
*/
|
||||
class ANDROID_API OpcodeListBuilder : public LightRefBase<OpcodeListBuilder> {
|
||||
public:
|
||||
// Note that the Adobe DNG 1.4 spec for Bayer phase (defined for the
|
||||
// FixBadPixelsConstant and FixBadPixelsList opcodes) is incorrect. It's
|
||||
// inconsistent with the DNG SDK (cf. dng_negative::SetBayerMosaic and
|
||||
// dng_opcode_FixBadPixelsList::IsGreen), and Adobe confirms that the
|
||||
// spec should be updated to match the SDK.
|
||||
enum CfaLayout {
|
||||
CFA_GRBG = 0,
|
||||
CFA_RGGB,
|
||||
CFA_BGGR,
|
||||
CFA_GBRG,
|
||||
CFA_NONE,
|
||||
};
|
||||
|
||||
OpcodeListBuilder();
|
||||
virtual ~OpcodeListBuilder();
|
||||
|
||||
/**
|
||||
* Get the total size of this opcode list in bytes.
|
||||
*/
|
||||
virtual size_t getSize() const;
|
||||
|
||||
/**
|
||||
* Get the number of opcodes defined in this list.
|
||||
*/
|
||||
virtual uint32_t getCount() const;
|
||||
|
||||
/**
|
||||
* Write the opcode list into the given buffer. This buffer
|
||||
* must be able to hold at least as many elements as returned
|
||||
* by calling the getSize() method.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t buildOpList(/*out*/ uint8_t* buf) const;
|
||||
|
||||
/**
|
||||
* Add GainMap opcode(s) for the given metadata parameters. The given
|
||||
* CFA layout must match the layout of the shading map passed into the
|
||||
* lensShadingMap parameter.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t addGainMapsForMetadata(uint32_t lsmWidth,
|
||||
uint32_t lsmHeight,
|
||||
uint32_t activeAreaTop,
|
||||
uint32_t activeAreaLeft,
|
||||
uint32_t activeAreaBottom,
|
||||
uint32_t activeAreaRight,
|
||||
CfaLayout cfa,
|
||||
const float* lensShadingMap);
|
||||
|
||||
/**
|
||||
* Add a GainMap opcode with the given fields. The mapGains array
|
||||
* must have mapPointsV * mapPointsH * mapPlanes elements.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t addGainMap(uint32_t top,
|
||||
uint32_t left,
|
||||
uint32_t bottom,
|
||||
uint32_t right,
|
||||
uint32_t plane,
|
||||
uint32_t planes,
|
||||
uint32_t rowPitch,
|
||||
uint32_t colPitch,
|
||||
uint32_t mapPointsV,
|
||||
uint32_t mapPointsH,
|
||||
double mapSpacingV,
|
||||
double mapSpacingH,
|
||||
double mapOriginV,
|
||||
double mapOriginH,
|
||||
uint32_t mapPlanes,
|
||||
const float* mapGains);
|
||||
|
||||
/**
|
||||
* Add WarpRectilinear opcode for the given metadata parameters.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t addWarpRectilinearForMetadata(const float* kCoeffs,
|
||||
uint32_t activeArrayWidth,
|
||||
uint32_t activeArrayHeight,
|
||||
float opticalCenterX,
|
||||
float opticalCenterY);
|
||||
|
||||
/**
|
||||
* Add a WarpRectilinear opcode.
|
||||
*
|
||||
* numPlanes - Number of planes included in this opcode.
|
||||
* opticalCenterX, opticalCenterY - Normalized x,y coordinates of the sensor optical
|
||||
* center relative to the top,left pixel of the produced images (e.g. [0.5, 0.5]
|
||||
* gives a sensor optical center in the image center.
|
||||
* kCoeffs - A list of coefficients for the polynomial equation representing the distortion
|
||||
* correction. For each plane, 6 coefficients must be included:
|
||||
* {k_r0, k_r1, k_r2, k_r3, k_t0, k_t1}. See the DNG 1.4 specification for an
|
||||
* outline of the polynomial used here.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t addWarpRectilinear(uint32_t numPlanes,
|
||||
double opticalCenterX,
|
||||
double opticalCenterY,
|
||||
const double* kCoeffs);
|
||||
|
||||
|
||||
/**
|
||||
* Add FixBadPixelsList opcode for the given metadata parameters.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t addBadPixelListForMetadata(const uint32_t* hotPixels,
|
||||
uint32_t xyPairCount,
|
||||
uint32_t colorFilterArrangement);
|
||||
|
||||
/**
|
||||
* Add FixBadPixelsList opcode.
|
||||
*
|
||||
* bayerPhase - 0=top-left of image is red, 1=top-left of image is green pixel in red row,
|
||||
* 2=top-left of image is green pixel in blue row, 3=top-left of image is
|
||||
* blue.
|
||||
* badPointCount - number of (x,y) pairs of bad pixels are given in badPointRowColPairs.
|
||||
* badRectCount - number of (top, left, bottom, right) tuples are given in
|
||||
* badRectTopLeftBottomRightTuples
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t addBadPixelList(uint32_t bayerPhase,
|
||||
uint32_t badPointCount,
|
||||
uint32_t badRectCount,
|
||||
const uint32_t* badPointRowColPairs,
|
||||
const uint32_t* badRectTopLeftBottomRightTuples);
|
||||
|
||||
// TODO: Add other Opcode methods
|
||||
protected:
|
||||
static const uint32_t FLAG_OPTIONAL = 0x1u;
|
||||
static const uint32_t FLAG_OPTIONAL_FOR_PREVIEW = 0x2u;
|
||||
|
||||
// Opcode IDs
|
||||
enum {
|
||||
WARP_RECTILINEAR_ID = 1,
|
||||
FIX_BAD_PIXELS_LIST = 5,
|
||||
GAIN_MAP_ID = 9,
|
||||
};
|
||||
|
||||
// LSM mosaic indices
|
||||
enum {
|
||||
LSM_R_IND = 0,
|
||||
LSM_GE_IND = 1,
|
||||
LSM_GO_IND = 2,
|
||||
LSM_B_IND = 3,
|
||||
};
|
||||
|
||||
uint32_t mCount;
|
||||
ByteArrayOutput mOpList;
|
||||
EndianOutput mEndianOut;
|
||||
|
||||
status_t addOpcodePreamble(uint32_t opcodeId);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Add Bayer GainMap opcode(s) for the given metadata parameters.
|
||||
* CFA layout must match the layout of the shading map passed into the
|
||||
* lensShadingMap parameter.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
status_t addBayerGainMapsForMetadata(uint32_t lsmWidth,
|
||||
uint32_t lsmHeight,
|
||||
uint32_t activeAreaWidth,
|
||||
uint32_t activeAreaHeight,
|
||||
CfaLayout cfa,
|
||||
const float* lensShadingMap);
|
||||
|
||||
/**
|
||||
* Add Bayer GainMap opcode(s) for the given metadata parameters.
|
||||
* CFA layout must match the layout of the shading map passed into the
|
||||
* lensShadingMap parameter.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
status_t addMonochromeGainMapsForMetadata(uint32_t lsmWidth,
|
||||
uint32_t lsmHeight,
|
||||
uint32_t activeAreaWidth,
|
||||
uint32_t activeAreaHeight,
|
||||
const float* lensShadingMap);
|
||||
};
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_DNG_UTILS_H*/
|
@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_ENDIAN_UTILS
|
||||
#define IMG_UTILS_ENDIAN_UTILS
|
||||
|
||||
#include <img_utils/Output.h>
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <stdint.h>
|
||||
#include <endian.h>
|
||||
#include <assert.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
/**
|
||||
* Endianness types supported.
|
||||
*/
|
||||
enum ANDROID_API Endianness {
|
||||
UNDEFINED_ENDIAN, // Default endianness will be used.
|
||||
BIG,
|
||||
LITTLE
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert from the native device endianness to big endian.
|
||||
*/
|
||||
template<typename T>
|
||||
T convertToBigEndian(T in);
|
||||
|
||||
/**
|
||||
* Convert from the native device endianness to little endian.
|
||||
*/
|
||||
template<typename T>
|
||||
T convertToLittleEndian(T in);
|
||||
|
||||
/**
|
||||
* A utility class for writing to an Output with the given endianness.
|
||||
*/
|
||||
class ANDROID_API EndianOutput : public Output {
|
||||
public:
|
||||
/**
|
||||
* Wrap the given Output. Calling write methods will result in
|
||||
* writes to this output.
|
||||
*/
|
||||
explicit EndianOutput(Output* out, Endianness end=LITTLE);
|
||||
|
||||
virtual ~EndianOutput();
|
||||
|
||||
/**
|
||||
* Call open on the wrapped output.
|
||||
*/
|
||||
virtual status_t open();
|
||||
|
||||
/**
|
||||
* Call close on the wrapped output.
|
||||
*/
|
||||
virtual status_t close();
|
||||
|
||||
/**
|
||||
* Set the endianness to use when writing.
|
||||
*/
|
||||
virtual void setEndianness(Endianness end);
|
||||
|
||||
/**
|
||||
* Get the currently configured endianness.
|
||||
*/
|
||||
virtual Endianness getEndianness() const;
|
||||
|
||||
/**
|
||||
* Get the current number of bytes written by this EndianOutput.
|
||||
*/
|
||||
virtual uint32_t getCurrentOffset() const;
|
||||
|
||||
|
||||
// TODO: switch write methods to uint32_t instead of size_t,
|
||||
// the max size of a TIFF files is bounded
|
||||
|
||||
/**
|
||||
* The following methods will write elements from given input buffer to the output.
|
||||
* Count elements in the buffer will be written with the endianness set for this
|
||||
* EndianOutput. If the given offset is greater than zero, that many elements will
|
||||
* be skipped in the buffer before writing.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t write(const uint8_t* buf, size_t offset, size_t count);
|
||||
|
||||
virtual status_t write(const int8_t* buf, size_t offset, size_t count);
|
||||
|
||||
virtual status_t write(const uint16_t* buf, size_t offset, size_t count);
|
||||
|
||||
virtual status_t write(const int16_t* buf, size_t offset, size_t count);
|
||||
|
||||
virtual status_t write(const uint32_t* buf, size_t offset, size_t count);
|
||||
|
||||
virtual status_t write(const int32_t* buf, size_t offset, size_t count);
|
||||
|
||||
virtual status_t write(const uint64_t* buf, size_t offset, size_t count);
|
||||
|
||||
virtual status_t write(const int64_t* buf, size_t offset, size_t count);
|
||||
|
||||
virtual status_t write(const float* buf, size_t offset, size_t count);
|
||||
|
||||
virtual status_t write(const double* buf, size_t offset, size_t count);
|
||||
|
||||
protected:
|
||||
template<typename T>
|
||||
inline status_t writeHelper(const T* buf, size_t offset, size_t count);
|
||||
|
||||
uint32_t mOffset;
|
||||
Output* mOutput;
|
||||
Endianness mEndian;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline status_t EndianOutput::writeHelper(const T* buf, size_t offset, size_t count) {
|
||||
assert(offset <= count);
|
||||
status_t res = OK;
|
||||
size_t size = sizeof(T);
|
||||
switch(mEndian) {
|
||||
case BIG: {
|
||||
for (size_t i = offset; i < count; ++i) {
|
||||
T tmp = convertToBigEndian<T>(buf[offset + i]);
|
||||
if ((res = mOutput->write(reinterpret_cast<uint8_t*>(&tmp), 0, size))
|
||||
!= OK) {
|
||||
return res;
|
||||
}
|
||||
mOffset += size;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LITTLE: {
|
||||
for (size_t i = offset; i < count; ++i) {
|
||||
T tmp = convertToLittleEndian<T>(buf[offset + i]);
|
||||
if ((res = mOutput->write(reinterpret_cast<uint8_t*>(&tmp), 0, size))
|
||||
!= OK) {
|
||||
return res;
|
||||
}
|
||||
mOffset += size;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline uint8_t convertToBigEndian(uint8_t in) {
|
||||
return in;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int8_t convertToBigEndian(int8_t in) {
|
||||
return in;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline uint16_t convertToBigEndian(uint16_t in) {
|
||||
return htobe16(in);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int16_t convertToBigEndian(int16_t in) {
|
||||
return htobe16(in);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline uint32_t convertToBigEndian(uint32_t in) {
|
||||
return htobe32(in);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int32_t convertToBigEndian(int32_t in) {
|
||||
return htobe32(in);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline uint64_t convertToBigEndian(uint64_t in) {
|
||||
return htobe64(in);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int64_t convertToBigEndian(int64_t in) {
|
||||
return htobe64(in);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline uint8_t convertToLittleEndian(uint8_t in) {
|
||||
return in;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int8_t convertToLittleEndian(int8_t in) {
|
||||
return in;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline uint16_t convertToLittleEndian(uint16_t in) {
|
||||
return htole16(in);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int16_t convertToLittleEndian(int16_t in) {
|
||||
return htole16(in);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline uint32_t convertToLittleEndian(uint32_t in) {
|
||||
return htole32(in);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int32_t convertToLittleEndian(int32_t in) {
|
||||
return htole32(in);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline uint64_t convertToLittleEndian(uint64_t in) {
|
||||
return htole64(in);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int64_t convertToLittleEndian(int64_t in) {
|
||||
return htole64(in);
|
||||
}
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_ENDIAN_UTILS*/
|
||||
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_FILE_INPUT_H
|
||||
#define IMG_UTILS_FILE_INPUT_H
|
||||
|
||||
#include <img_utils/Input.h>
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/String8.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
/**
|
||||
* Utility class for reading from a file.
|
||||
*/
|
||||
class ANDROID_API FileInput : public Input {
|
||||
public:
|
||||
/**
|
||||
* Create a file input for the given path.
|
||||
*/
|
||||
explicit FileInput(String8 path);
|
||||
|
||||
virtual ~FileInput();
|
||||
|
||||
/**
|
||||
* Open a file descriptor to the path given in the constructor.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t open();
|
||||
|
||||
/**
|
||||
* Read bytes from the file 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.
|
||||
*/
|
||||
virtual ssize_t read(uint8_t* buf, size_t offset, size_t count);
|
||||
|
||||
/**
|
||||
* Close the file descriptor to the path given in the constructor.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t close();
|
||||
private:
|
||||
FILE *mFp;
|
||||
String8 mPath;
|
||||
bool mOpen;
|
||||
};
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
|
||||
#endif /*IMG_UTILS_INPUT_H*/
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_FILE_OUTPUT_H
|
||||
#define IMG_UTILS_FILE_OUTPUT_H
|
||||
|
||||
#include <img_utils/Output.h>
|
||||
#include <cutils/compiler.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/String8.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
class ANDROID_API FileOutput : public Output {
|
||||
public:
|
||||
explicit FileOutput(String8 path);
|
||||
virtual ~FileOutput();
|
||||
virtual status_t open();
|
||||
virtual status_t write(const uint8_t* buf, size_t offset, size_t count);
|
||||
virtual status_t close();
|
||||
private:
|
||||
FILE *mFp;
|
||||
String8 mPath;
|
||||
bool mOpen;
|
||||
};
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_FILE_OUTPUT_H*/
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_INPUT_H
|
||||
#define IMG_UTILS_INPUT_H
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
/**
|
||||
* Utility class used as a source of bytes.
|
||||
*/
|
||||
class ANDROID_API Input {
|
||||
public:
|
||||
virtual ~Input();
|
||||
|
||||
/**
|
||||
* Open this Input.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual 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.
|
||||
*/
|
||||
virtual ssize_t read(uint8_t* buf, size_t offset, size_t count) = 0;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
virtual 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.
|
||||
*/
|
||||
virtual status_t close();
|
||||
};
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
|
||||
#endif /*IMG_UTILS_INPUT_H*/
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_ORDERABLE
|
||||
#define IMG_UTILS_ORDERABLE
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
#define COMPARE_DEF(op) \
|
||||
inline bool operator op (const Orderable& orderable) const;
|
||||
|
||||
/**
|
||||
* Subclasses of Orderable can be compared and sorted. This is
|
||||
* intended to be used to create sorted arrays of TIFF entries
|
||||
* and IFDs.
|
||||
*/
|
||||
class ANDROID_API Orderable {
|
||||
public:
|
||||
virtual ~Orderable();
|
||||
|
||||
/**
|
||||
* Comparison operatotors are based on the value returned
|
||||
* from this method.
|
||||
*/
|
||||
virtual uint32_t getComparableValue() const = 0;
|
||||
|
||||
COMPARE_DEF(>)
|
||||
COMPARE_DEF(<)
|
||||
COMPARE_DEF(>=)
|
||||
COMPARE_DEF(<=)
|
||||
COMPARE_DEF(==)
|
||||
COMPARE_DEF(!=)
|
||||
};
|
||||
|
||||
#undef COMPARE_DEF
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_ORDERABLE*/
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_OUTPUT_H
|
||||
#define IMG_UTILS_OUTPUT_H
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
/**
|
||||
* Utility class used to output bytes.
|
||||
*/
|
||||
class ANDROID_API Output {
|
||||
public:
|
||||
virtual ~Output();
|
||||
|
||||
/**
|
||||
* Open this Output.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t open();
|
||||
|
||||
/**
|
||||
* Write bytes from the given buffer. The number of bytes given in the count
|
||||
* argument will be written. Bytes will be written from the given buffer starting
|
||||
* at the index given in the offset argument.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t write(const uint8_t* buf, size_t offset, size_t count) = 0;
|
||||
|
||||
/**
|
||||
* Close this Output. It is not valid to call open on a previously closed Output.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t close();
|
||||
};
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_OUTPUT_H*/
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_PAIR_H
|
||||
#define IMG_UTILS_PAIR_H
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
/**
|
||||
* Generic pair utility class. Nothing special here.
|
||||
*/
|
||||
template<typename F, typename S>
|
||||
class ANDROID_API Pair {
|
||||
public:
|
||||
F first;
|
||||
S second;
|
||||
|
||||
Pair() {}
|
||||
|
||||
Pair(const Pair& o) : first(o.first), second(o.second) {}
|
||||
|
||||
Pair(const F& f, const S& s) : first(f), second(s) {}
|
||||
};
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_PAIR_H*/
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_SORTED_ENTRY_VECTOR_H
|
||||
#define IMG_UTILS_SORTED_ENTRY_VECTOR_H
|
||||
|
||||
#include <img_utils/TiffEntry.h>
|
||||
|
||||
#include <utils/StrongPointer.h>
|
||||
#include <utils/SortedVector.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
/**
|
||||
* Subclass of SortedVector that has been extended to
|
||||
* do comparisons/lookups based on the tag ID of the entries.
|
||||
*/
|
||||
class SortedEntryVector : public SortedVector<sp<TiffEntry> > {
|
||||
public:
|
||||
virtual ~SortedEntryVector();
|
||||
|
||||
/**
|
||||
* Returns the index of the entry with the given tag ID, or
|
||||
* -1 if none exists.
|
||||
*/
|
||||
ssize_t indexOfTag(uint16_t tag) const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Compare tag ID.
|
||||
*/
|
||||
virtual int do_compare(const void* lhs, const void* rhs) const;
|
||||
};
|
||||
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_SORTED_ENTRY_VECTOR_H*/
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_STRIP_SOURCE_H
|
||||
#define IMG_UTILS_STRIP_SOURCE_H
|
||||
|
||||
#include <img_utils/Output.h>
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <utils/Errors.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
/**
|
||||
* This class acts as a data source for strips set in a TiffIfd.
|
||||
*/
|
||||
class ANDROID_API StripSource {
|
||||
public:
|
||||
virtual ~StripSource();
|
||||
|
||||
/**
|
||||
* Write count bytes to the stream.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t writeToStream(Output& stream, uint32_t count) = 0;
|
||||
|
||||
/**
|
||||
* Return the source IFD.
|
||||
*/
|
||||
virtual uint32_t getIfd() const = 0;
|
||||
};
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_STRIP_SOURCE_H*/
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_TIFF_ENTRY
|
||||
#define IMG_UTILS_TIFF_ENTRY
|
||||
|
||||
#include <img_utils/TiffWritable.h>
|
||||
#include <img_utils/TiffHelpers.h>
|
||||
#include <img_utils/EndianUtils.h>
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
// #include <utils/String8.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
#define COMPARE_DEF(op) \
|
||||
inline bool operator op (const TiffEntry& entry) const;
|
||||
|
||||
/**
|
||||
* This class holds a single TIFF IFD entry.
|
||||
*
|
||||
* Subclasses are expected to support assignment and copying operations.
|
||||
*/
|
||||
class ANDROID_API TiffEntry : public TiffWritable {
|
||||
public:
|
||||
virtual ~TiffEntry();
|
||||
|
||||
/**
|
||||
* Write the 12-byte IFD entry to the output. The given offset will be
|
||||
* set as the tag value if the size of the tag value exceeds the max
|
||||
* size for the TIFF Value field (4 bytes), and should be word aligned.
|
||||
*
|
||||
* Returns OK on success, or a negative error code on failure.
|
||||
*/
|
||||
virtual status_t writeTagInfo(uint32_t offset, /*out*/EndianOutput* out) const = 0;
|
||||
|
||||
/**
|
||||
* Get the count set for this entry. This corresponds to the TIFF Count
|
||||
* field.
|
||||
*/
|
||||
virtual uint32_t getCount() const = 0;
|
||||
|
||||
/**
|
||||
* Get the tag id set for this entry. This corresponds to the TIFF Tag
|
||||
* field.
|
||||
*/
|
||||
virtual uint16_t getTag() const = 0;
|
||||
|
||||
/**
|
||||
* Get the type set for this entry. This corresponds to the TIFF Type
|
||||
* field.
|
||||
*/
|
||||
virtual TagType getType() const = 0;
|
||||
|
||||
/**
|
||||
* Get the defined endianness for this entry. If this is defined,
|
||||
* the tag value will be written with the given byte order.
|
||||
*/
|
||||
virtual Endianness getEndianness() const = 0;
|
||||
|
||||
/**
|
||||
* Get the value for this entry. This corresponds to the TIFF Value
|
||||
* field.
|
||||
*
|
||||
* Returns NULL if the value is NULL, or if the type used does not
|
||||
* match the type of this tag.
|
||||
*/
|
||||
template<typename T>
|
||||
const T* getData() const;
|
||||
|
||||
virtual std::string toString() const;
|
||||
|
||||
/**
|
||||
* Force the type used here to be a valid TIFF type.
|
||||
*
|
||||
* Returns NULL if the given value is NULL, or if the type given does
|
||||
* not match the type of the value given.
|
||||
*/
|
||||
template<typename T>
|
||||
static const T* forceValidType(TagType type, const T* value);
|
||||
|
||||
virtual const void* getDataHelper() const = 0;
|
||||
|
||||
COMPARE_DEF(>)
|
||||
COMPARE_DEF(<)
|
||||
|
||||
protected:
|
||||
enum {
|
||||
MAX_PRINT_STRING_LENGTH = 256
|
||||
};
|
||||
};
|
||||
|
||||
#define COMPARE(op) \
|
||||
bool TiffEntry::operator op (const TiffEntry& entry) const { \
|
||||
return getComparableValue() op entry.getComparableValue(); \
|
||||
}
|
||||
|
||||
COMPARE(>)
|
||||
COMPARE(<)
|
||||
|
||||
|
||||
template<typename T>
|
||||
const T* TiffEntry::getData() const {
|
||||
const T* value = reinterpret_cast<const T*>(getDataHelper());
|
||||
return forceValidType<T>(getType(), value);
|
||||
}
|
||||
|
||||
#undef COMPARE
|
||||
#undef COMPARE_DEF
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_TIFF_ENTRY*/
|
@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_TIFF_ENTRY_IMPL
|
||||
#define IMG_UTILS_TIFF_ENTRY_IMPL
|
||||
|
||||
#include <img_utils/TiffIfd.h>
|
||||
#include <img_utils/TiffEntry.h>
|
||||
#include <img_utils/TiffHelpers.h>
|
||||
#include <img_utils/Output.h>
|
||||
#include <img_utils/EndianUtils.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Errors.h>
|
||||
// #include <utils/Vector.h>
|
||||
#include <utils/StrongPointer.h>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
template<typename T>
|
||||
class TiffEntryImpl : public TiffEntry {
|
||||
public:
|
||||
TiffEntryImpl(uint16_t tag, TagType type, uint32_t count, Endianness end, const T* data);
|
||||
virtual ~TiffEntryImpl();
|
||||
|
||||
status_t writeData(uint32_t offset, /*out*/EndianOutput* out) const;
|
||||
status_t writeTagInfo(uint32_t offset, /*out*/EndianOutput* out) const;
|
||||
|
||||
uint32_t getCount() const;
|
||||
uint16_t getTag() const;
|
||||
TagType getType() const;
|
||||
Endianness getEndianness() const;
|
||||
size_t getSize() const;
|
||||
uint32_t getComparableValue() const;
|
||||
|
||||
protected:
|
||||
const void* getDataHelper() const;
|
||||
uint32_t getActualSize() const;
|
||||
|
||||
uint16_t mTag;
|
||||
uint16_t mType;
|
||||
uint32_t mCount;
|
||||
Endianness mEnd;
|
||||
std::vector<T> mData;
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
TiffEntryImpl<T>::TiffEntryImpl(uint16_t tag, TagType type, uint32_t count, Endianness end,
|
||||
const T* data)
|
||||
: mTag(tag), mType(static_cast<uint16_t>(type)), mCount(count), mEnd(end) {
|
||||
count = (type == RATIONAL || type == SRATIONAL) ? count * 2 : count;
|
||||
auto it = mData.insert(mData.end(), data, data + count);
|
||||
// LOG_ALWAYS_FATAL_IF(index < 0, "%s: Could not allocate vector for data.", __FUNCTION__);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
TiffEntryImpl<T>::~TiffEntryImpl() {}
|
||||
|
||||
template<typename T>
|
||||
uint32_t TiffEntryImpl<T>::getCount() const {
|
||||
return mCount;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
uint16_t TiffEntryImpl<T>::getTag() const {
|
||||
return mTag;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
TagType TiffEntryImpl<T>::getType() const {
|
||||
return static_cast<TagType>(mType);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const void* TiffEntryImpl<T>::getDataHelper() const {
|
||||
return reinterpret_cast<const void*>(&mData[0]);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
size_t TiffEntryImpl<T>::getSize() const {
|
||||
uint32_t total = getActualSize();
|
||||
WORD_ALIGN(total)
|
||||
return (total <= OFFSET_SIZE) ? 0 : total;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
uint32_t TiffEntryImpl<T>::getActualSize() const {
|
||||
uint32_t total = sizeof(T) * mCount;
|
||||
if (getType() == RATIONAL || getType() == SRATIONAL) {
|
||||
// 2 ints stored for each rational, multiply by 2
|
||||
total <<= 1;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Endianness TiffEntryImpl<T>::getEndianness() const {
|
||||
return mEnd;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
uint32_t TiffEntryImpl<T>::getComparableValue() const {
|
||||
return mTag;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
status_t TiffEntryImpl<T>::writeTagInfo(uint32_t offset, /*out*/EndianOutput* out) const {
|
||||
assert((offset % TIFF_WORD_SIZE) == 0);
|
||||
status_t ret = OK;
|
||||
BAIL_ON_FAIL(out->write(&mTag, 0, 1), ret);
|
||||
BAIL_ON_FAIL(out->write(&mType, 0, 1), ret);
|
||||
BAIL_ON_FAIL(out->write(&mCount, 0, 1), ret);
|
||||
|
||||
uint32_t dataSize = getActualSize();
|
||||
if (dataSize > OFFSET_SIZE) {
|
||||
BAIL_ON_FAIL(out->write(&offset, 0, 1), ret);
|
||||
} else {
|
||||
uint32_t count = mCount;
|
||||
if (getType() == RATIONAL || getType() == SRATIONAL) {
|
||||
/**
|
||||
* Rationals are stored as an array of ints. Each
|
||||
* rational is represented by 2 ints. To recover the
|
||||
* size of the array here, multiply the count by 2.
|
||||
*/
|
||||
count <<= 1;
|
||||
}
|
||||
BAIL_ON_FAIL(out->write(&mData[0], 0, count), ret);
|
||||
ZERO_TILL_WORD(out, dataSize, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
status_t TiffEntryImpl<T>::writeData(uint32_t /*offset*/, EndianOutput* out) const {
|
||||
status_t ret = OK;
|
||||
|
||||
// Some tags have fixed-endian value output
|
||||
Endianness tmp = UNDEFINED_ENDIAN;
|
||||
if (mEnd != UNDEFINED_ENDIAN) {
|
||||
tmp = out->getEndianness();
|
||||
out->setEndianness(mEnd);
|
||||
}
|
||||
|
||||
uint32_t count = mCount;
|
||||
if (getType() == RATIONAL || getType() == SRATIONAL) {
|
||||
/**
|
||||
* Rationals are stored as an array of ints. Each
|
||||
* rational is represented by 2 ints. To recover the
|
||||
* size of the array here, multiply the count by 2.
|
||||
*/
|
||||
count <<= 1;
|
||||
}
|
||||
|
||||
BAIL_ON_FAIL(out->write(&mData[0], 0, count), ret);
|
||||
|
||||
if (mEnd != UNDEFINED_ENDIAN) {
|
||||
out->setEndianness(tmp);
|
||||
}
|
||||
|
||||
// Write to next word alignment
|
||||
ZERO_TILL_WORD(out, sizeof(T) * count, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline status_t TiffEntryImpl<sp<TiffIfd> >::writeTagInfo(uint32_t offset,
|
||||
/*out*/EndianOutput* out) const {
|
||||
assert((offset % TIFF_WORD_SIZE) == 0);
|
||||
status_t ret = OK;
|
||||
BAIL_ON_FAIL(out->write(&mTag, 0, 1), ret);
|
||||
BAIL_ON_FAIL(out->write(&mType, 0, 1), ret);
|
||||
BAIL_ON_FAIL(out->write(&mCount, 0, 1), ret);
|
||||
|
||||
BAIL_ON_FAIL(out->write(&offset, 0, 1), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline uint32_t TiffEntryImpl<sp<TiffIfd> >::getActualSize() const {
|
||||
uint32_t total = 0;
|
||||
for (size_t i = 0; i < mData.size(); ++i) {
|
||||
total += mData[i]->getSize();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline status_t TiffEntryImpl<sp<TiffIfd> >::writeData(uint32_t offset, EndianOutput* out) const {
|
||||
status_t ret = OK;
|
||||
for (uint32_t i = 0; i < mCount; ++i) {
|
||||
BAIL_ON_FAIL(mData[i]->writeData(offset, out), ret);
|
||||
offset += mData[i]->getSize();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_TIFF_ENTRY_IMPL*/
|
||||
|
||||
|
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_TIFF_HELPERS_H
|
||||
#define IMG_UTILS_TIFF_HELPERS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
const uint8_t ZERO_WORD[] = {0, 0, 0, 0};
|
||||
|
||||
#define BAIL_ON_FAIL(x, flag) \
|
||||
if (((flag) = (x)) != OK) return flag;
|
||||
|
||||
#define BYTES_TILL_WORD(index) \
|
||||
((TIFF_WORD_SIZE - ((index) % TIFF_WORD_SIZE)) % TIFF_WORD_SIZE)
|
||||
|
||||
#define WORD_ALIGN(count) \
|
||||
count += BYTES_TILL_WORD(count);
|
||||
|
||||
#define ZERO_TILL_WORD(output, index, ret) \
|
||||
{ \
|
||||
size_t remaining = BYTES_TILL_WORD(index); \
|
||||
if (remaining > 0) { \
|
||||
BAIL_ON_FAIL((output)->write(ZERO_WORD, 0, remaining), ret); \
|
||||
} \
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic TIFF header constants.
|
||||
*/
|
||||
enum {
|
||||
BAD_OFFSET = 0,
|
||||
TIFF_WORD_SIZE = 4, // Size in bytes
|
||||
IFD_HEADER_SIZE = 2, // Size in bytes
|
||||
IFD_FOOTER_SIZE = 4, // Size in bytes
|
||||
TIFF_ENTRY_SIZE = 12, // Size in bytes
|
||||
MAX_IFD_ENTRIES = UINT16_MAX,
|
||||
FILE_HEADER_SIZE = 8, // Size in bytes
|
||||
ENDIAN_MARKER_SIZE = 2, // Size in bytes
|
||||
TIFF_MARKER_SIZE = 2, // Size in bytes
|
||||
OFFSET_MARKER_SIZE = 4, // Size in bytes
|
||||
TIFF_FILE_MARKER = 42,
|
||||
BIG_ENDIAN_MARKER = 0x4D4Du,
|
||||
LITTLE_ENDIAN_MARKER = 0x4949u
|
||||
};
|
||||
|
||||
/**
|
||||
* Constants for the TIFF tag types.
|
||||
*/
|
||||
enum TagType {
|
||||
UNKNOWN_TAGTYPE = 0,
|
||||
BYTE=1,
|
||||
ASCII,
|
||||
SHORT,
|
||||
LONG,
|
||||
RATIONAL,
|
||||
SBYTE,
|
||||
UNDEFINED,
|
||||
SSHORT,
|
||||
SLONG,
|
||||
SRATIONAL,
|
||||
FLOAT,
|
||||
DOUBLE
|
||||
};
|
||||
|
||||
/**
|
||||
* Sizes of the TIFF entry fields (in bytes).
|
||||
*/
|
||||
enum {
|
||||
TAG_SIZE = 2,
|
||||
TYPE_SIZE = 2,
|
||||
COUNT_SIZE = 4,
|
||||
OFFSET_SIZE = 4
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenience IFD id constants.
|
||||
*/
|
||||
enum {
|
||||
IFD_0 = 0,
|
||||
RAW_IFD,
|
||||
PROFILE_IFD,
|
||||
PREVIEW_IFD
|
||||
};
|
||||
|
||||
inline size_t getTypeSize(TagType type) {
|
||||
switch(type) {
|
||||
case UNDEFINED:
|
||||
case ASCII:
|
||||
case BYTE:
|
||||
case SBYTE:
|
||||
return 1;
|
||||
case SHORT:
|
||||
case SSHORT:
|
||||
return 2;
|
||||
case LONG:
|
||||
case SLONG:
|
||||
case FLOAT:
|
||||
return 4;
|
||||
case RATIONAL:
|
||||
case SRATIONAL:
|
||||
case DOUBLE:
|
||||
return 8;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline uint32_t calculateIfdSize(size_t numberOfEntries) {
|
||||
return IFD_HEADER_SIZE + IFD_FOOTER_SIZE + TIFF_ENTRY_SIZE * numberOfEntries;
|
||||
}
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_TIFF_HELPERS_H*/
|
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_TIFF_IFD_H
|
||||
#define IMG_UTILS_TIFF_IFD_H
|
||||
|
||||
#include <img_utils/TiffWritable.h>
|
||||
#include <img_utils/TiffEntry.h>
|
||||
#include <img_utils/Output.h>
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/StrongPointer.h>
|
||||
#include <stdint.h>
|
||||
#include <map>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
/**
|
||||
* This class holds a single TIFF Image File Directory (IFD) structure.
|
||||
*
|
||||
* This maps to the TIFF IFD structure that is logically composed of:
|
||||
* - A 2-byte field listing the number of entries.
|
||||
* - A list of 12-byte TIFF entries.
|
||||
* - A 4-byte offset to the next IFD.
|
||||
*/
|
||||
class ANDROID_API TiffIfd : public TiffWritable {
|
||||
public:
|
||||
explicit TiffIfd(uint32_t ifdId);
|
||||
virtual ~TiffIfd();
|
||||
|
||||
/**
|
||||
* Add a TiffEntry to this IFD or replace an existing entry with the
|
||||
* same tag ID. No validation is done.
|
||||
*
|
||||
* Returns OK on success, or a negative error code on failure.
|
||||
*/
|
||||
virtual status_t addEntry(const sp<TiffEntry>& entry);
|
||||
|
||||
/**
|
||||
* Set the pointer to the next IFD. This is used to create a linked
|
||||
* list of IFDs as defined by the TIFF 6.0 spec., and is not included
|
||||
* when calculating the size of IFD and entries for the getSize()
|
||||
* method (unlike SubIFDs).
|
||||
*/
|
||||
virtual void setNextIfd(const sp<TiffIfd>& ifd);
|
||||
|
||||
/**
|
||||
* Get the pointer to the next IFD, or NULL if none exists.
|
||||
*/
|
||||
virtual sp<TiffIfd> getNextIfd() const;
|
||||
|
||||
/**
|
||||
* Write the IFD data. This includes the IFD header, entries, footer,
|
||||
* and the corresponding values for each entry (recursively including
|
||||
* sub-IFDs). The written amount should end on a word boundary, and
|
||||
* the given offset should be word aligned.
|
||||
*
|
||||
* Returns OK on success, or a negative error code on failure.
|
||||
*/
|
||||
virtual status_t writeData(uint32_t offset, /*out*/EndianOutput* out) const;
|
||||
|
||||
/**
|
||||
* Get the size of the IFD. This includes the IFD header, entries, footer,
|
||||
* and the corresponding values for each entry (recursively including
|
||||
* any sub-IFDs).
|
||||
*/
|
||||
virtual size_t getSize() const;
|
||||
|
||||
/**
|
||||
* Get the id of this IFD.
|
||||
*/
|
||||
virtual uint32_t getId() const;
|
||||
|
||||
/**
|
||||
* Get an entry with the given tag ID.
|
||||
*
|
||||
* Returns a strong pointer to the entry if it exists, or an empty strong
|
||||
* pointer.
|
||||
*/
|
||||
virtual sp<TiffEntry> getEntry(uint16_t tag) const;
|
||||
|
||||
/**
|
||||
* Remove the entry with the given tag ID if it exists.
|
||||
*/
|
||||
virtual void removeEntry(uint16_t tag);
|
||||
|
||||
/**
|
||||
* Convenience method to validate and set strip-related image tags.
|
||||
*
|
||||
* This sets all strip related tags, but leaves offset values unitialized.
|
||||
* setStripOffsets must be called with the desired offset before writing.
|
||||
* The strip tag values are calculated from the existing tags for image
|
||||
* dimensions and pixel type set in the IFD.
|
||||
*
|
||||
* Does not handle planar image configurations (PlanarConfiguration != 1).
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t validateAndSetStripTags();
|
||||
|
||||
/**
|
||||
* Returns true if validateAndSetStripTags has been called, but not setStripOffsets.
|
||||
*/
|
||||
virtual bool uninitializedOffsets() const;
|
||||
|
||||
/**
|
||||
* Convenience method to set beginning offset for strips.
|
||||
*
|
||||
* Call this to update the strip offsets before calling writeData.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t setStripOffset(uint32_t offset);
|
||||
|
||||
/**
|
||||
* Get the total size of the strips in bytes.
|
||||
*
|
||||
* This sums the byte count at each strip offset, and returns
|
||||
* the total count of bytes stored in strips for this IFD.
|
||||
*/
|
||||
virtual uint32_t getStripSize() const;
|
||||
|
||||
/**
|
||||
* Get a formatted string representing this IFD.
|
||||
*/
|
||||
virtual std::string toString() const;
|
||||
|
||||
/**
|
||||
* Print a formatted string representing this IFD to logcat.
|
||||
*/
|
||||
void log() const;
|
||||
|
||||
/**
|
||||
* Get value used to determine sort order.
|
||||
*/
|
||||
virtual uint32_t getComparableValue() const;
|
||||
|
||||
protected:
|
||||
virtual uint32_t checkAndGetOffset(uint32_t offset) const;
|
||||
std::map<uint16_t, sp<TiffEntry> > mEntries;
|
||||
sp<TiffIfd> mNextIfd;
|
||||
uint32_t mIfdId;
|
||||
bool mStripOffsetsInitialized;
|
||||
};
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_TIFF_IFD_H*/
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_TIFF_WRITABLE
|
||||
#define IMG_UTILS_TIFF_WRITABLE
|
||||
|
||||
#include <img_utils/Orderable.h>
|
||||
#include <img_utils/EndianUtils.h>
|
||||
#include <img_utils/Output.h>
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/RefBase.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
/**
|
||||
* TiffWritable subclasses represent TIFF metadata objects that can be written
|
||||
* to an EndianOutput object. This is used for TIFF entries and IFDs.
|
||||
*/
|
||||
class ANDROID_API TiffWritable : public Orderable, public LightRefBase<TiffWritable> {
|
||||
public:
|
||||
TiffWritable();
|
||||
virtual ~TiffWritable();
|
||||
|
||||
/**
|
||||
* Write the data to the output. The given offset is used to calculate
|
||||
* the header offset for values written. The offset is defined
|
||||
* relative to the beginning of the TIFF header, and is word aligned.
|
||||
*
|
||||
* Returns OK on success, or a negative error code on failure.
|
||||
*/
|
||||
virtual status_t writeData(uint32_t offset, /*out*/EndianOutput* out) const = 0;
|
||||
|
||||
/**
|
||||
* Get the size of the data to write.
|
||||
*/
|
||||
virtual size_t getSize() const = 0;
|
||||
|
||||
};
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_TIFF_WRITABLE*/
|
@ -0,0 +1,328 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_TIFF_WRITER_H
|
||||
#define IMG_UTILS_TIFF_WRITER_H
|
||||
|
||||
#include <img_utils/EndianUtils.h>
|
||||
#include <img_utils/StripSource.h>
|
||||
#include <img_utils/TiffEntryImpl.h>
|
||||
#include <img_utils/TagDefinitions.h>
|
||||
#include <img_utils/TiffIfd.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/StrongPointer.h>
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
class TiffEntry;
|
||||
class TiffIfd;
|
||||
class Output;
|
||||
|
||||
/**
|
||||
* This class holds a collection of TIFF IFDs that can be written as a
|
||||
* complete DNG file header.
|
||||
*
|
||||
* This maps to the TIFF header structure that is logically composed of:
|
||||
* - An 8-byte file header containing an endianness indicator, the TIFF
|
||||
* file marker, and the offset to the first IFD.
|
||||
* - A list of TIFF IFD structures.
|
||||
*/
|
||||
class ANDROID_API TiffWriter : public LightRefBase<TiffWriter> {
|
||||
public:
|
||||
enum SubIfdType {
|
||||
SUBIFD = 0,
|
||||
GPSINFO
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructs a TiffWriter with the default tag mappings. This enables
|
||||
* all of the tags defined in TagDefinitions.h, and uses the following
|
||||
* mapping precedence to resolve collisions:
|
||||
* (highest precedence) TIFF/EP > DNG > EXIF 2.3 > TIFF 6.0
|
||||
*/
|
||||
TiffWriter();
|
||||
|
||||
/**
|
||||
* Constructs a TiffWriter with the given tag mappings. The mapping
|
||||
* precedence will be in the order that the definition maps are given,
|
||||
* where the lower index map gets precedence.
|
||||
*
|
||||
* This can be used with user-defined definitions, or definitions form
|
||||
* TagDefinitions.h
|
||||
*
|
||||
* The enabledDefinitions mapping object is owned by the caller, and must
|
||||
* stay alive for the lifespan of the constructed TiffWriter object.
|
||||
*/
|
||||
TiffWriter(std::map<uint16_t, const TagDefinition_t*>* enabledDefinitions,
|
||||
size_t length);
|
||||
|
||||
virtual ~TiffWriter();
|
||||
|
||||
/**
|
||||
* Write a TIFF header containing each IFD set. This will recursively
|
||||
* write all SubIFDs and tags.
|
||||
*
|
||||
* Any StripSources passed in will be written to the output as image strips
|
||||
* at the appropriate offests. The StripByteCounts, RowsPerStrip, and
|
||||
* StripOffsets tags must be set to use this. To set these tags in a
|
||||
* given IFD, use the addStrip method.
|
||||
*
|
||||
* Returns OK on success, or a negative error code on failure.
|
||||
*/
|
||||
virtual status_t write(Output* out, StripSource** sources, size_t sourcesCount,
|
||||
Endianness end = LITTLE);
|
||||
|
||||
/**
|
||||
* Write a TIFF header containing each IFD set. This will recursively
|
||||
* write all SubIFDs and tags.
|
||||
*
|
||||
* Image data for strips or tiles must be written separately at the
|
||||
* appropriate offsets. These offsets must not fall within the file
|
||||
* header written this way. The size of the header written is given
|
||||
* by the getTotalSize() method.
|
||||
*
|
||||
* Returns OK on success, or a negative error code on failure.
|
||||
*/
|
||||
virtual status_t write(Output* out, Endianness end = LITTLE);
|
||||
|
||||
/**
|
||||
* Get the total size in bytes of the TIFF header. This includes all
|
||||
* IFDs, tags, and values set for this TiffWriter.
|
||||
*/
|
||||
virtual uint32_t getTotalSize() const;
|
||||
|
||||
/**
|
||||
* Add an entry to the IFD with the given ID.
|
||||
*
|
||||
* Returns OK on success, or a negative error code on failure. Valid
|
||||
* error codes for this method are:
|
||||
* - BAD_INDEX - The given tag doesn't exist.
|
||||
* - BAD_VALUE - The given count doesn't match the required count for
|
||||
* this tag.
|
||||
* - BAD_TYPE - The type of the given data isn't compatible with the
|
||||
* type required for this tag.
|
||||
* - NAME_NOT_FOUND - No ifd exists with the given ID.
|
||||
*/
|
||||
virtual status_t addEntry(const sp<TiffEntry>& entry, uint32_t ifd);
|
||||
|
||||
/**
|
||||
* Build an entry for a known tag and add it to the IFD with the given ID.
|
||||
* This tag must be defined in one of the definition vectors this TIFF writer
|
||||
* was constructed with. The count and type are validated.
|
||||
*
|
||||
* Returns OK on success, or a negative error code on failure. Valid
|
||||
* error codes for this method are:
|
||||
* - BAD_INDEX - The given tag doesn't exist.
|
||||
* - BAD_VALUE - The given count doesn't match the required count for
|
||||
* this tag.
|
||||
* - BAD_TYPE - The type of the given data isn't compatible with the
|
||||
* type required for this tag.
|
||||
* - NAME_NOT_FOUND - No ifd exists with the given ID.
|
||||
*/
|
||||
template<typename T>
|
||||
status_t addEntry(uint16_t tag, uint32_t count, const T* data, uint32_t ifd);
|
||||
|
||||
/**
|
||||
* Build an entry for a known tag. This tag must be one of the tags
|
||||
* defined in one of the definition vectors this TIFF writer was constructed
|
||||
* with. The count and type are validated. If this succeeds, the resulting
|
||||
* entry will be placed in the outEntry pointer.
|
||||
*
|
||||
* Returns OK on success, or a negative error code on failure. Valid
|
||||
* error codes for this method are:
|
||||
* - BAD_INDEX - The given tag doesn't exist.
|
||||
* - BAD_VALUE - The given count doesn't match the required count for
|
||||
* this tag.
|
||||
* - BAD_TYPE - The type of the given data isn't compatible with the
|
||||
* type required for this tag.
|
||||
*/
|
||||
template<typename T>
|
||||
status_t buildEntry(uint16_t tag, uint32_t count, const T* data,
|
||||
/*out*/sp<TiffEntry>* outEntry) const;
|
||||
|
||||
/**
|
||||
* Convenience function to set the strip related tags for a given IFD.
|
||||
*
|
||||
* Call this before using a StripSource as an input to write.
|
||||
* The following tags must be set before calling this method:
|
||||
* - ImageWidth
|
||||
* - ImageLength
|
||||
* - SamplesPerPixel
|
||||
* - BitsPerSample
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t addStrip(uint32_t ifd);
|
||||
|
||||
/**
|
||||
* Return the TIFF entry with the given tag ID in the IFD with the given ID,
|
||||
* or an empty pointer if none exists.
|
||||
*/
|
||||
virtual sp<TiffEntry> getEntry(uint16_t tag, uint32_t ifd) const;
|
||||
|
||||
/**
|
||||
* Remove the TIFF entry with the given tag ID in the given IFD if it exists.
|
||||
*/
|
||||
virtual void removeEntry(uint16_t tag, uint32_t ifd);
|
||||
|
||||
/**
|
||||
* Create an empty IFD with the given ID and add it to the end of the
|
||||
* list of IFDs.
|
||||
*/
|
||||
virtual status_t addIfd(uint32_t ifd);
|
||||
|
||||
/**
|
||||
* Create an empty IFD with the given ID and add it as a SubIfd of the
|
||||
* parent IFD.
|
||||
*/
|
||||
virtual status_t addSubIfd(uint32_t parentIfd, uint32_t ifd, SubIfdType type = SUBIFD);
|
||||
|
||||
/**
|
||||
* Returns the default type for the given tag ID.
|
||||
*/
|
||||
virtual TagType getDefaultType(uint16_t tag) const;
|
||||
|
||||
/**
|
||||
* Returns the default count for a given tag ID, or 0 if this
|
||||
* tag normally has a variable count.
|
||||
*/
|
||||
virtual uint32_t getDefaultCount(uint16_t tag) const;
|
||||
|
||||
/**
|
||||
* Returns true if an IFD with the given ID exists.
|
||||
*/
|
||||
virtual bool hasIfd(uint32_t ifd) const;
|
||||
|
||||
/**
|
||||
* Returns true if a definition exist for the given tag ID.
|
||||
*/
|
||||
virtual bool checkIfDefined(uint16_t tag) const;
|
||||
|
||||
/**
|
||||
* Returns the name of the tag if a definition exists for the given tag
|
||||
* ID, or null if no definition exists.
|
||||
*/
|
||||
virtual const char* getTagName(uint16_t tag) const;
|
||||
|
||||
/**
|
||||
* Print the currently configured IFDs and entries to logcat.
|
||||
*/
|
||||
virtual void log() const;
|
||||
|
||||
/**
|
||||
* Build an entry. No validation is done.
|
||||
*
|
||||
* WARNING: Using this method can result in creating poorly formatted
|
||||
* TIFF files.
|
||||
*
|
||||
* Returns a TiffEntry with the given tag, type, count, endianness,
|
||||
* and data.
|
||||
*/
|
||||
template<typename T>
|
||||
static sp<TiffEntry> uncheckedBuildEntry(uint16_t tag, TagType type,
|
||||
uint32_t count, Endianness end, const T* data);
|
||||
|
||||
/**
|
||||
* Utility function to build atag-to-definition mapping from a given
|
||||
* array of tag definitions.
|
||||
*/
|
||||
#if 0
|
||||
static KeyedVector<uint16_t, const TagDefinition_t*> buildTagMap(
|
||||
const TagDefinition_t* definitions, size_t length);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
enum {
|
||||
DEFAULT_NUM_TAG_MAPS = 4,
|
||||
};
|
||||
|
||||
sp<TiffIfd> findLastIfd();
|
||||
status_t writeFileHeader(EndianOutput& out);
|
||||
const TagDefinition_t* lookupDefinition(uint16_t tag) const;
|
||||
status_t calculateOffsets();
|
||||
|
||||
sp<TiffIfd> mIfd;
|
||||
std::map<uint32_t, sp<TiffIfd> > mNamedIfds;
|
||||
std::vector<std::map<uint16_t, const TagDefinition_t*> > mTagMaps;
|
||||
size_t mNumTagMaps;
|
||||
#if 0
|
||||
static KeyedVector<uint16_t, const TagDefinition_t*> sTagMaps[];
|
||||
#endif
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
status_t TiffWriter::buildEntry(uint16_t tag, uint32_t count, const T* data,
|
||||
/*out*/sp<TiffEntry>* outEntry) const {
|
||||
const TagDefinition_t* definition = lookupDefinition(tag);
|
||||
|
||||
if (definition == NULL) {
|
||||
ALOGE("%s: No such tag exists for id %x.", __FUNCTION__, tag);
|
||||
return BAD_INDEX;
|
||||
}
|
||||
|
||||
uint32_t fixedCount = definition->fixedCount;
|
||||
if (fixedCount > 0 && fixedCount != count) {
|
||||
ALOGE("%s: Invalid count %d for tag %x (expects %d).", __FUNCTION__, count, tag,
|
||||
fixedCount);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
TagType fixedType = definition->defaultType;
|
||||
if (TiffEntry::forceValidType(fixedType, data) == NULL) {
|
||||
ALOGE("%s: Invalid type used for tag value for tag %x.", __FUNCTION__, tag);
|
||||
return BAD_TYPE;
|
||||
}
|
||||
|
||||
*outEntry = new TiffEntryImpl<T>(tag, fixedType, count,
|
||||
definition->fixedEndian, data);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
status_t TiffWriter::addEntry(uint16_t tag, uint32_t count, const T* data, uint32_t ifd) {
|
||||
sp<TiffEntry> outEntry;
|
||||
|
||||
status_t ret = buildEntry<T>(tag, count, data, &outEntry);
|
||||
if (ret != OK) {
|
||||
ALOGE("%s: Could not build entry for tag %x.", __FUNCTION__, tag);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return addEntry(outEntry, ifd);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
sp<TiffEntry> TiffWriter::uncheckedBuildEntry(uint16_t tag, TagType type, uint32_t count,
|
||||
Endianness end, const T* data) {
|
||||
TiffEntryImpl<T>* entry = new TiffEntryImpl<T>(tag, type, count, end, data);
|
||||
return sp<TiffEntry>(entry);
|
||||
}
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
|
||||
#endif /*IMG_UTILS_TIFF_WRITER_H*/
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <img_utils/ByteArrayOutput.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
ByteArrayOutput::ByteArrayOutput() {}
|
||||
|
||||
ByteArrayOutput::~ByteArrayOutput() {}
|
||||
|
||||
status_t ByteArrayOutput::open() {
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t ByteArrayOutput::write(const uint8_t* buf, size_t offset, size_t count) {
|
||||
if (mByteArray.insert(mByteArray.end(), buf + offset, buf + offset + count) == mByteArray.end()) {
|
||||
ALOGE("%s: Failed to write to ByteArrayOutput.", __FUNCTION__);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t ByteArrayOutput::close() {
|
||||
mByteArray.clear();
|
||||
return OK;
|
||||
}
|
||||
|
||||
size_t ByteArrayOutput::getSize() const {
|
||||
return mByteArray.size();
|
||||
}
|
||||
|
||||
const uint8_t* ByteArrayOutput::getArray() const {
|
||||
return &mByteArray[0];
|
||||
}
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
@ -0,0 +1,496 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <img_utils/DngUtils.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <math.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
OpcodeListBuilder::OpcodeListBuilder() : mCount(0), mOpList(), mEndianOut(&mOpList, BIG) {
|
||||
if(mEndianOut.open() != OK) {
|
||||
ALOGE("%s: Open failed.", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
OpcodeListBuilder::~OpcodeListBuilder() {
|
||||
if(mEndianOut.close() != OK) {
|
||||
ALOGE("%s: Close failed.", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
size_t OpcodeListBuilder::getSize() const {
|
||||
return mOpList.getSize() + sizeof(mCount);
|
||||
}
|
||||
|
||||
uint32_t OpcodeListBuilder::getCount() const {
|
||||
return mCount;
|
||||
}
|
||||
|
||||
status_t OpcodeListBuilder::buildOpList(uint8_t* buf) const {
|
||||
uint32_t count = convertToBigEndian(mCount);
|
||||
memcpy(buf, &count, sizeof(count));
|
||||
memcpy(buf + sizeof(count), mOpList.getArray(), mOpList.getSize());
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t OpcodeListBuilder::addGainMapsForMetadata(uint32_t lsmWidth,
|
||||
uint32_t lsmHeight,
|
||||
uint32_t activeAreaTop,
|
||||
uint32_t activeAreaLeft,
|
||||
uint32_t activeAreaBottom,
|
||||
uint32_t activeAreaRight,
|
||||
CfaLayout cfa,
|
||||
const float* lensShadingMap) {
|
||||
status_t err = OK;
|
||||
uint32_t activeAreaWidth = activeAreaRight - activeAreaLeft;
|
||||
uint32_t activeAreaHeight = activeAreaBottom - activeAreaTop;
|
||||
|
||||
switch (cfa) {
|
||||
case CFA_RGGB:
|
||||
case CFA_GRBG:
|
||||
case CFA_GBRG:
|
||||
case CFA_BGGR:
|
||||
err = addBayerGainMapsForMetadata(lsmWidth, lsmHeight, activeAreaWidth,
|
||||
activeAreaHeight, cfa, lensShadingMap);
|
||||
break;
|
||||
case CFA_NONE:
|
||||
err = addMonochromeGainMapsForMetadata(lsmWidth, lsmHeight, activeAreaWidth,
|
||||
activeAreaHeight, lensShadingMap);
|
||||
break;
|
||||
default:
|
||||
ALOGE("%s: Unknown CFA layout %d", __FUNCTION__, cfa);
|
||||
err = BAD_VALUE;
|
||||
break;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
status_t OpcodeListBuilder::addBayerGainMapsForMetadata(uint32_t lsmWidth,
|
||||
uint32_t lsmHeight,
|
||||
uint32_t activeAreaWidth,
|
||||
uint32_t activeAreaHeight,
|
||||
CfaLayout cfa,
|
||||
const float* lensShadingMap) {
|
||||
uint32_t redTop = 0;
|
||||
uint32_t redLeft = 0;
|
||||
uint32_t greenEvenTop = 0;
|
||||
uint32_t greenEvenLeft = 1;
|
||||
uint32_t greenOddTop = 1;
|
||||
uint32_t greenOddLeft = 0;
|
||||
uint32_t blueTop = 1;
|
||||
uint32_t blueLeft = 1;
|
||||
|
||||
switch(cfa) {
|
||||
case CFA_RGGB:
|
||||
redTop = 0;
|
||||
redLeft = 0;
|
||||
greenEvenTop = 0;
|
||||
greenEvenLeft = 1;
|
||||
greenOddTop = 1;
|
||||
greenOddLeft = 0;
|
||||
blueTop = 1;
|
||||
blueLeft = 1;
|
||||
break;
|
||||
case CFA_GRBG:
|
||||
redTop = 0;
|
||||
redLeft = 1;
|
||||
greenEvenTop = 0;
|
||||
greenEvenLeft = 0;
|
||||
greenOddTop = 1;
|
||||
greenOddLeft = 1;
|
||||
blueTop = 1;
|
||||
blueLeft = 0;
|
||||
break;
|
||||
case CFA_GBRG:
|
||||
redTop = 1;
|
||||
redLeft = 0;
|
||||
greenEvenTop = 0;
|
||||
greenEvenLeft = 0;
|
||||
greenOddTop = 1;
|
||||
greenOddLeft = 1;
|
||||
blueTop = 0;
|
||||
blueLeft = 1;
|
||||
break;
|
||||
case CFA_BGGR:
|
||||
redTop = 1;
|
||||
redLeft = 1;
|
||||
greenEvenTop = 0;
|
||||
greenEvenLeft = 1;
|
||||
greenOddTop = 1;
|
||||
greenOddLeft = 0;
|
||||
blueTop = 0;
|
||||
blueLeft = 0;
|
||||
break;
|
||||
default:
|
||||
ALOGE("%s: Unknown CFA layout %d", __FUNCTION__, cfa);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
std::vector<float> redMapVector(lsmWidth * lsmHeight);
|
||||
float *redMap = redMapVector.data();
|
||||
|
||||
std::vector<float> greenEvenMapVector(lsmWidth * lsmHeight);
|
||||
float *greenEvenMap = greenEvenMapVector.data();
|
||||
|
||||
std::vector<float> greenOddMapVector(lsmWidth * lsmHeight);
|
||||
float *greenOddMap = greenOddMapVector.data();
|
||||
|
||||
std::vector<float> blueMapVector(lsmWidth * lsmHeight);
|
||||
float *blueMap = blueMapVector.data();
|
||||
|
||||
double spacingV = 1.0 / std::max(1u, lsmHeight - 1);
|
||||
double spacingH = 1.0 / std::max(1u, lsmWidth - 1);
|
||||
|
||||
size_t lsmMapSize = lsmWidth * lsmHeight * 4;
|
||||
|
||||
// Split lens shading map channels into separate arrays
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < lsmMapSize; i += 4, ++j) {
|
||||
redMap[j] = lensShadingMap[i + LSM_R_IND];
|
||||
greenEvenMap[j] = lensShadingMap[i + LSM_GE_IND];
|
||||
greenOddMap[j] = lensShadingMap[i + LSM_GO_IND];
|
||||
blueMap[j] = lensShadingMap[i + LSM_B_IND];
|
||||
}
|
||||
|
||||
status_t err = addGainMap(/*top*/redTop,
|
||||
/*left*/redLeft,
|
||||
/*bottom*/activeAreaHeight,
|
||||
/*right*/activeAreaWidth,
|
||||
/*plane*/0,
|
||||
/*planes*/1,
|
||||
/*rowPitch*/2,
|
||||
/*colPitch*/2,
|
||||
/*mapPointsV*/lsmHeight,
|
||||
/*mapPointsH*/lsmWidth,
|
||||
/*mapSpacingV*/spacingV,
|
||||
/*mapSpacingH*/spacingH,
|
||||
/*mapOriginV*/0,
|
||||
/*mapOriginH*/0,
|
||||
/*mapPlanes*/1,
|
||||
/*mapGains*/redMap);
|
||||
if (err != OK) return err;
|
||||
|
||||
err = addGainMap(/*top*/greenEvenTop,
|
||||
/*left*/greenEvenLeft,
|
||||
/*bottom*/activeAreaHeight,
|
||||
/*right*/activeAreaWidth,
|
||||
/*plane*/0,
|
||||
/*planes*/1,
|
||||
/*rowPitch*/2,
|
||||
/*colPitch*/2,
|
||||
/*mapPointsV*/lsmHeight,
|
||||
/*mapPointsH*/lsmWidth,
|
||||
/*mapSpacingV*/spacingV,
|
||||
/*mapSpacingH*/spacingH,
|
||||
/*mapOriginV*/0,
|
||||
/*mapOriginH*/0,
|
||||
/*mapPlanes*/1,
|
||||
/*mapGains*/greenEvenMap);
|
||||
if (err != OK) return err;
|
||||
|
||||
err = addGainMap(/*top*/greenOddTop,
|
||||
/*left*/greenOddLeft,
|
||||
/*bottom*/activeAreaHeight,
|
||||
/*right*/activeAreaWidth,
|
||||
/*plane*/0,
|
||||
/*planes*/1,
|
||||
/*rowPitch*/2,
|
||||
/*colPitch*/2,
|
||||
/*mapPointsV*/lsmHeight,
|
||||
/*mapPointsH*/lsmWidth,
|
||||
/*mapSpacingV*/spacingV,
|
||||
/*mapSpacingH*/spacingH,
|
||||
/*mapOriginV*/0,
|
||||
/*mapOriginH*/0,
|
||||
/*mapPlanes*/1,
|
||||
/*mapGains*/greenOddMap);
|
||||
if (err != OK) return err;
|
||||
|
||||
err = addGainMap(/*top*/blueTop,
|
||||
/*left*/blueLeft,
|
||||
/*bottom*/activeAreaHeight,
|
||||
/*right*/activeAreaWidth,
|
||||
/*plane*/0,
|
||||
/*planes*/1,
|
||||
/*rowPitch*/2,
|
||||
/*colPitch*/2,
|
||||
/*mapPointsV*/lsmHeight,
|
||||
/*mapPointsH*/lsmWidth,
|
||||
/*mapSpacingV*/spacingV,
|
||||
/*mapSpacingH*/spacingH,
|
||||
/*mapOriginV*/0,
|
||||
/*mapOriginH*/0,
|
||||
/*mapPlanes*/1,
|
||||
/*mapGains*/blueMap);
|
||||
return err;
|
||||
}
|
||||
|
||||
status_t OpcodeListBuilder::addMonochromeGainMapsForMetadata(uint32_t lsmWidth,
|
||||
uint32_t lsmHeight,
|
||||
uint32_t activeAreaWidth,
|
||||
uint32_t activeAreaHeight,
|
||||
const float* lensShadingMap) {
|
||||
std::vector<float> mapVector(lsmWidth * lsmHeight);
|
||||
float *map = mapVector.data();
|
||||
|
||||
double spacingV = 1.0 / std::max(1u, lsmHeight - 1);
|
||||
double spacingH = 1.0 / std::max(1u, lsmWidth - 1);
|
||||
|
||||
size_t lsmMapSize = lsmWidth * lsmHeight * 4;
|
||||
|
||||
// Split lens shading map channels into separate arrays
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < lsmMapSize; i += 4, ++j) {
|
||||
map[j] = lensShadingMap[i];
|
||||
}
|
||||
|
||||
status_t err = addGainMap(/*top*/0,
|
||||
/*left*/0,
|
||||
/*bottom*/activeAreaHeight,
|
||||
/*right*/activeAreaWidth,
|
||||
/*plane*/0,
|
||||
/*planes*/1,
|
||||
/*rowPitch*/1,
|
||||
/*colPitch*/1,
|
||||
/*mapPointsV*/lsmHeight,
|
||||
/*mapPointsH*/lsmWidth,
|
||||
/*mapSpacingV*/spacingV,
|
||||
/*mapSpacingH*/spacingH,
|
||||
/*mapOriginV*/0,
|
||||
/*mapOriginH*/0,
|
||||
/*mapPlanes*/1,
|
||||
/*mapGains*/map);
|
||||
if (err != OK) return err;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
status_t OpcodeListBuilder::addGainMap(uint32_t top,
|
||||
uint32_t left,
|
||||
uint32_t bottom,
|
||||
uint32_t right,
|
||||
uint32_t plane,
|
||||
uint32_t planes,
|
||||
uint32_t rowPitch,
|
||||
uint32_t colPitch,
|
||||
uint32_t mapPointsV,
|
||||
uint32_t mapPointsH,
|
||||
double mapSpacingV,
|
||||
double mapSpacingH,
|
||||
double mapOriginV,
|
||||
double mapOriginH,
|
||||
uint32_t mapPlanes,
|
||||
const float* mapGains) {
|
||||
|
||||
status_t err = addOpcodePreamble(GAIN_MAP_ID);
|
||||
if (err != OK) return err;
|
||||
|
||||
// Allow this opcode to be skipped if not supported
|
||||
uint32_t flags = FLAG_OPTIONAL;
|
||||
|
||||
err = mEndianOut.write(&flags, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
const uint32_t NUMBER_INT_ARGS = 11;
|
||||
const uint32_t NUMBER_DOUBLE_ARGS = 4;
|
||||
|
||||
uint32_t totalSize = NUMBER_INT_ARGS * sizeof(uint32_t) + NUMBER_DOUBLE_ARGS * sizeof(double) +
|
||||
mapPointsV * mapPointsH * mapPlanes * sizeof(float);
|
||||
|
||||
err = mEndianOut.write(&totalSize, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
// Batch writes as much as possible
|
||||
uint32_t settings1[] = { top,
|
||||
left,
|
||||
bottom,
|
||||
right,
|
||||
plane,
|
||||
planes,
|
||||
rowPitch,
|
||||
colPitch,
|
||||
mapPointsV,
|
||||
mapPointsH };
|
||||
|
||||
err = mEndianOut.write(settings1, 0, NELEMS(settings1));
|
||||
if (err != OK) return err;
|
||||
|
||||
double settings2[] = { mapSpacingV,
|
||||
mapSpacingH,
|
||||
mapOriginV,
|
||||
mapOriginH };
|
||||
|
||||
err = mEndianOut.write(settings2, 0, NELEMS(settings2));
|
||||
if (err != OK) return err;
|
||||
|
||||
err = mEndianOut.write(&mapPlanes, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
err = mEndianOut.write(mapGains, 0, mapPointsV * mapPointsH * mapPlanes);
|
||||
if (err != OK) return err;
|
||||
|
||||
mCount++;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t OpcodeListBuilder::addWarpRectilinearForMetadata(const float* kCoeffs,
|
||||
uint32_t activeArrayWidth,
|
||||
uint32_t activeArrayHeight,
|
||||
float opticalCenterX,
|
||||
float opticalCenterY) {
|
||||
if (activeArrayWidth <= 1 || activeArrayHeight <= 1) {
|
||||
ALOGE("%s: Cannot add opcode for active array with dimensions w=%" PRIu32 ", h=%" PRIu32,
|
||||
__FUNCTION__, activeArrayWidth, activeArrayHeight);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
double normalizedOCX = opticalCenterX / static_cast<double>(activeArrayWidth);
|
||||
double normalizedOCY = opticalCenterY / static_cast<double>(activeArrayHeight);
|
||||
|
||||
normalizedOCX = CLAMP(normalizedOCX, 0, 1);
|
||||
normalizedOCY = CLAMP(normalizedOCY, 0, 1);
|
||||
|
||||
double coeffs[6] = {
|
||||
kCoeffs[0],
|
||||
kCoeffs[1],
|
||||
kCoeffs[2],
|
||||
kCoeffs[3],
|
||||
kCoeffs[4],
|
||||
kCoeffs[5]
|
||||
};
|
||||
|
||||
return addWarpRectilinear(/*numPlanes*/1,
|
||||
/*opticalCenterX*/normalizedOCX,
|
||||
/*opticalCenterY*/normalizedOCY,
|
||||
coeffs);
|
||||
}
|
||||
|
||||
status_t OpcodeListBuilder::addWarpRectilinear(uint32_t numPlanes,
|
||||
double opticalCenterX,
|
||||
double opticalCenterY,
|
||||
const double* kCoeffs) {
|
||||
|
||||
status_t err = addOpcodePreamble(WARP_RECTILINEAR_ID);
|
||||
if (err != OK) return err;
|
||||
|
||||
// Allow this opcode to be skipped if not supported
|
||||
uint32_t flags = FLAG_OPTIONAL;
|
||||
|
||||
err = mEndianOut.write(&flags, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
const uint32_t NUMBER_CENTER_ARGS = 2;
|
||||
const uint32_t NUMBER_COEFFS = numPlanes * 6;
|
||||
uint32_t totalSize = (NUMBER_CENTER_ARGS + NUMBER_COEFFS) * sizeof(double) + sizeof(uint32_t);
|
||||
|
||||
err = mEndianOut.write(&totalSize, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
err = mEndianOut.write(&numPlanes, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
err = mEndianOut.write(kCoeffs, 0, NUMBER_COEFFS);
|
||||
if (err != OK) return err;
|
||||
|
||||
err = mEndianOut.write(&opticalCenterX, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
err = mEndianOut.write(&opticalCenterY, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
mCount++;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t OpcodeListBuilder::addBadPixelListForMetadata(const uint32_t* hotPixels,
|
||||
uint32_t xyPairCount,
|
||||
uint32_t colorFilterArrangement) {
|
||||
if (colorFilterArrangement > 3) {
|
||||
ALOGE("%s: Unknown color filter arrangement %" PRIu32, __FUNCTION__,
|
||||
colorFilterArrangement);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
return addBadPixelList(colorFilterArrangement, xyPairCount, 0, hotPixels, nullptr);
|
||||
}
|
||||
|
||||
status_t OpcodeListBuilder::addBadPixelList(uint32_t bayerPhase,
|
||||
uint32_t badPointCount,
|
||||
uint32_t badRectCount,
|
||||
const uint32_t* badPointRowColPairs,
|
||||
const uint32_t* badRectTopLeftBottomRightTuples) {
|
||||
|
||||
status_t err = addOpcodePreamble(FIX_BAD_PIXELS_LIST);
|
||||
if (err != OK) return err;
|
||||
|
||||
// Allow this opcode to be skipped if not supported
|
||||
uint32_t flags = FLAG_OPTIONAL;
|
||||
|
||||
err = mEndianOut.write(&flags, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
const uint32_t NUM_NON_VARLEN_FIELDS = 3;
|
||||
const uint32_t SIZE_OF_POINT = 2;
|
||||
const uint32_t SIZE_OF_RECT = 4;
|
||||
|
||||
uint32_t totalSize = (NUM_NON_VARLEN_FIELDS + badPointCount * SIZE_OF_POINT +
|
||||
badRectCount * SIZE_OF_RECT) * sizeof(uint32_t);
|
||||
err = mEndianOut.write(&totalSize, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
err = mEndianOut.write(&bayerPhase, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
err = mEndianOut.write(&badPointCount, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
err = mEndianOut.write(&badRectCount, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
if (badPointCount > 0) {
|
||||
err = mEndianOut.write(badPointRowColPairs, 0, SIZE_OF_POINT * badPointCount);
|
||||
if (err != OK) return err;
|
||||
}
|
||||
|
||||
if (badRectCount > 0) {
|
||||
err = mEndianOut.write(badRectTopLeftBottomRightTuples, 0, SIZE_OF_RECT * badRectCount);
|
||||
if (err != OK) return err;
|
||||
}
|
||||
|
||||
mCount++;
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t OpcodeListBuilder::addOpcodePreamble(uint32_t opcodeId) {
|
||||
status_t err = mEndianOut.write(&opcodeId, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
uint8_t version[] = {1, 3, 0, 0};
|
||||
err = mEndianOut.write(version, 0, NELEMS(version));
|
||||
if (err != OK) return err;
|
||||
return OK;
|
||||
}
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <img_utils/EndianUtils.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
EndianOutput::EndianOutput(Output* out, Endianness end)
|
||||
: mOffset(0), mOutput(out), mEndian(end) {}
|
||||
|
||||
EndianOutput::~EndianOutput() {}
|
||||
|
||||
status_t EndianOutput::open() {
|
||||
mOffset = 0;
|
||||
return mOutput->open();
|
||||
}
|
||||
|
||||
status_t EndianOutput::close() {
|
||||
return mOutput->close();
|
||||
}
|
||||
|
||||
void EndianOutput::setEndianness(Endianness end) {
|
||||
mEndian = end;
|
||||
}
|
||||
|
||||
uint32_t EndianOutput::getCurrentOffset() const {
|
||||
return mOffset;
|
||||
}
|
||||
|
||||
Endianness EndianOutput::getEndianness() const {
|
||||
return mEndian;
|
||||
}
|
||||
|
||||
status_t EndianOutput::write(const uint8_t* buf, size_t offset, size_t count) {
|
||||
status_t res = OK;
|
||||
if((res = mOutput->write(buf, offset, count)) == OK) {
|
||||
mOffset += count;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
status_t EndianOutput::write(const int8_t* buf, size_t offset, size_t count) {
|
||||
return write(reinterpret_cast<const uint8_t*>(buf), offset, count);
|
||||
}
|
||||
|
||||
#define DEFINE_WRITE(_type_) \
|
||||
status_t EndianOutput::write(const _type_* buf, size_t offset, size_t count) { \
|
||||
return writeHelper<_type_>(buf, offset, count); \
|
||||
}
|
||||
|
||||
DEFINE_WRITE(uint16_t)
|
||||
DEFINE_WRITE(int16_t)
|
||||
DEFINE_WRITE(uint32_t)
|
||||
DEFINE_WRITE(int32_t)
|
||||
DEFINE_WRITE(uint64_t)
|
||||
DEFINE_WRITE(int64_t)
|
||||
|
||||
status_t EndianOutput::write(const float* buf, size_t offset, size_t count) {
|
||||
assert(sizeof(float) == sizeof(uint32_t));
|
||||
return writeHelper<uint32_t>(reinterpret_cast<const uint32_t*>(buf), offset, count);
|
||||
}
|
||||
|
||||
status_t EndianOutput::write(const double* buf, size_t offset, size_t count) {
|
||||
assert(sizeof(double) == sizeof(uint64_t));
|
||||
return writeHelper<uint64_t>(reinterpret_cast<const uint64_t*>(buf), offset, count);
|
||||
}
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <img_utils/FileInput.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
FileInput::FileInput(String8 path) : mFp(NULL), mPath(path), mOpen(false) {}
|
||||
|
||||
FileInput::~FileInput() {
|
||||
if (mOpen) {
|
||||
ALOGE("%s: FileInput destroyed without calling close!", __FUNCTION__);
|
||||
close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
status_t FileInput::open() {
|
||||
if (mOpen) {
|
||||
ALOGW("%s: Open called when file %s already open.", __FUNCTION__, mPath.string());
|
||||
return OK;
|
||||
}
|
||||
mFp = ::fopen(mPath, "rb");
|
||||
if (!mFp) {
|
||||
ALOGE("%s: Could not open file %s", __FUNCTION__, mPath.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
mOpen = true;
|
||||
return OK;
|
||||
}
|
||||
|
||||
ssize_t FileInput::read(uint8_t* buf, size_t offset, size_t count) {
|
||||
if (!mOpen) {
|
||||
ALOGE("%s: Could not read file %s, file not open.", __FUNCTION__, mPath.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
size_t bytesRead = ::fread(buf + offset, sizeof(uint8_t), count, mFp);
|
||||
int error = ::ferror(mFp);
|
||||
if (error != 0) {
|
||||
ALOGE("%s: Error %d occurred while reading file %s.", __FUNCTION__, error, mPath.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
// End of file reached
|
||||
if (::feof(mFp) != 0 && bytesRead == 0) {
|
||||
return NOT_ENOUGH_DATA;
|
||||
}
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
status_t FileInput::close() {
|
||||
if(!mOpen) {
|
||||
ALOGW("%s: Close called when file %s already close.", __FUNCTION__, mPath.string());
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t ret = OK;
|
||||
if(::fclose(mFp) != 0) {
|
||||
ALOGE("%s: Failed to close file %s.", __FUNCTION__, mPath.string());
|
||||
ret = BAD_VALUE;
|
||||
}
|
||||
mOpen = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <img_utils/FileOutput.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
FileOutput::FileOutput(String8 path) : mFp(NULL), mPath(path), mOpen(false) {}
|
||||
|
||||
FileOutput::~FileOutput() {
|
||||
if (mOpen) {
|
||||
ALOGW("%s: Destructor called with %s still open.", __FUNCTION__, mPath.string());
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
status_t FileOutput::open() {
|
||||
if (mOpen) {
|
||||
ALOGW("%s: Open called when file %s already open.", __FUNCTION__, mPath.string());
|
||||
return OK;
|
||||
}
|
||||
mFp = ::fopen(mPath, "wb");
|
||||
if (!mFp) {
|
||||
ALOGE("%s: Could not open file %s", __FUNCTION__, mPath.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
mOpen = true;
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t FileOutput::write(const uint8_t* buf, size_t offset, size_t count) {
|
||||
if (!mOpen) {
|
||||
ALOGE("%s: Could not write file %s, file not open.", __FUNCTION__, mPath.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
::fwrite(buf + offset, sizeof(uint8_t), count, mFp);
|
||||
|
||||
int error = ::ferror(mFp);
|
||||
if (error != 0) {
|
||||
ALOGE("%s: Error %d occurred while writing file %s.", __FUNCTION__, error, mPath.string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t FileOutput::close() {
|
||||
if(!mOpen) {
|
||||
ALOGW("%s: Close called when file %s already close.", __FUNCTION__, mPath.string());
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t ret = OK;
|
||||
if(::fclose(mFp) != 0) {
|
||||
ALOGE("%s: Failed to close file %s.", __FUNCTION__, mPath.string());
|
||||
ret = BAD_VALUE;
|
||||
}
|
||||
mOpen = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <img_utils/Input.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
Input::~Input() {}
|
||||
|
||||
status_t Input::open() { return OK; }
|
||||
|
||||
status_t Input::close() { return OK; }
|
||||
|
||||
ssize_t Input::skip(size_t count) {
|
||||
const size_t SKIP_BUF_SIZE = 1024;
|
||||
uint8_t skipBuf[SKIP_BUF_SIZE];
|
||||
|
||||
size_t remaining = count;
|
||||
while (remaining > 0) {
|
||||
size_t amt = (SKIP_BUF_SIZE > remaining) ? remaining : SKIP_BUF_SIZE;
|
||||
ssize_t ret = read(skipBuf, 0, amt);
|
||||
if (ret < 0) {
|
||||
if(ret == NOT_ENOUGH_DATA) {
|
||||
// End of file encountered
|
||||
if (remaining == count) {
|
||||
// Read no bytes, return EOF
|
||||
return NOT_ENOUGH_DATA;
|
||||
} else {
|
||||
// Return num bytes read
|
||||
return count - remaining;
|
||||
}
|
||||
}
|
||||
// Return error code.
|
||||
return ret;
|
||||
}
|
||||
remaining -= ret;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <img_utils/Orderable.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
#define COMPARE(op) \
|
||||
bool Orderable::operator op (const Orderable& orderable) const { \
|
||||
return getComparableValue() op orderable.getComparableValue(); \
|
||||
}
|
||||
|
||||
COMPARE(>)
|
||||
COMPARE(<)
|
||||
COMPARE(>=)
|
||||
COMPARE(<=)
|
||||
COMPARE(==)
|
||||
COMPARE(!=)
|
||||
|
||||
Orderable::~Orderable() {}
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
#include <img_utils/Output.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
Output::~Output() {}
|
||||
status_t Output::open() { return OK; }
|
||||
status_t Output::close() { return OK; }
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <img_utils/SortedEntryVector.h>
|
||||
|
||||
#include <utils/TypeHelpers.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
SortedEntryVector::~SortedEntryVector() {}
|
||||
|
||||
ssize_t SortedEntryVector::indexOfTag(uint16_t tag) const {
|
||||
// TODO: Use binary search here.
|
||||
for (size_t i = 0; i < size(); ++i) {
|
||||
if (itemAt(i)->getTag() == tag) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int SortedEntryVector::do_compare(const void* lhs, const void* rhs) const {
|
||||
const sp<TiffEntry>* lEntry = reinterpret_cast<const sp<TiffEntry>*>(lhs);
|
||||
const sp<TiffEntry>* rEntry = reinterpret_cast<const sp<TiffEntry>*>(rhs);
|
||||
return compare_type(**lEntry, **rEntry);
|
||||
}
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <img_utils/StripSource.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
StripSource::~StripSource() {}
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <img_utils/TiffIfd.h>
|
||||
#include <img_utils/TiffHelpers.h>
|
||||
#include <img_utils/TiffEntry.h>
|
||||
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/StrongPointer.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
TiffEntry::~TiffEntry() {}
|
||||
|
||||
/**
|
||||
* Specialize for each valid type, including sub-IFDs.
|
||||
*
|
||||
* Values with types other than the ones given here should not compile.
|
||||
*/
|
||||
|
||||
template<>
|
||||
const sp<TiffIfd>* TiffEntry::forceValidType<sp<TiffIfd> >(TagType type, const sp<TiffIfd>* value) {
|
||||
if (type == LONG) {
|
||||
return value;
|
||||
}
|
||||
ALOGE("%s: Value of type 'ifd' is not valid for tag with TIFF type %d.",
|
||||
__FUNCTION__, type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
template<>
|
||||
const uint8_t* TiffEntry::forceValidType<uint8_t>(TagType type, const uint8_t* value) {
|
||||
if (type == BYTE || type == ASCII || type == UNDEFINED) {
|
||||
return value;
|
||||
}
|
||||
ALOGE("%s: Value of type 'uint8_t' is not valid for tag with TIFF type %d.",
|
||||
__FUNCTION__, type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
template<>
|
||||
const int8_t* TiffEntry::forceValidType<int8_t>(TagType type, const int8_t* value) {
|
||||
if (type == SBYTE || type == ASCII || type == UNDEFINED) {
|
||||
return value;
|
||||
}
|
||||
ALOGE("%s: Value of type 'int8_t' is not valid for tag with TIFF type %d.",
|
||||
__FUNCTION__, type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
template<>
|
||||
const uint16_t* TiffEntry::forceValidType<uint16_t>(TagType type, const uint16_t* value) {
|
||||
if (type == SHORT) {
|
||||
return value;
|
||||
}
|
||||
ALOGE("%s: Value of type 'uint16_t' is not valid for tag with TIFF type %d.",
|
||||
__FUNCTION__, type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
template<>
|
||||
const int16_t* TiffEntry::forceValidType<int16_t>(TagType type, const int16_t* value) {
|
||||
if (type == SSHORT) {
|
||||
return value;
|
||||
}
|
||||
ALOGE("%s: Value of type 'int16_t' is not valid for tag with TIFF type %d.",
|
||||
__FUNCTION__, type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
template<>
|
||||
const uint32_t* TiffEntry::forceValidType<uint32_t>(TagType type, const uint32_t* value) {
|
||||
if (type == LONG || type == RATIONAL) {
|
||||
return value;
|
||||
}
|
||||
ALOGE("%s: Value of type 'uint32_t' is not valid for tag with TIFF type %d.",
|
||||
__FUNCTION__, type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
template<>
|
||||
const int32_t* TiffEntry::forceValidType<int32_t>(TagType type, const int32_t* value) {
|
||||
if (type == SLONG || type == SRATIONAL) {
|
||||
return value;
|
||||
}
|
||||
ALOGE("%s: Value of type 'int32_t' is not valid for tag with TIFF type %d.",
|
||||
__FUNCTION__, type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
template<>
|
||||
const double* TiffEntry::forceValidType<double>(TagType type, const double* value) {
|
||||
if (type == DOUBLE) {
|
||||
return value;
|
||||
}
|
||||
ALOGE("%s: Value of type 'double' is not valid for tag with TIFF type %d.",
|
||||
__FUNCTION__, type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
template<>
|
||||
const float* TiffEntry::forceValidType<float>(TagType type, const float* value) {
|
||||
if (type == FLOAT) {
|
||||
return value;
|
||||
}
|
||||
ALOGE("%s: Value of type 'float' is not valid for tag with TIFF type %d.",
|
||||
__FUNCTION__, type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::string TiffEntry::toString() const {
|
||||
std::string output;
|
||||
uint32_t count = getCount();
|
||||
char buf[256] = { 0 };
|
||||
snprintf(buf, sizeof(buf), "[id: %x, type: %d, count: %u, value: '", getTag(), getType(), count);
|
||||
output.append(buf);
|
||||
|
||||
size_t cappedCount = count;
|
||||
if (count > MAX_PRINT_STRING_LENGTH) {
|
||||
cappedCount = MAX_PRINT_STRING_LENGTH;
|
||||
}
|
||||
|
||||
TagType type = getType();
|
||||
switch (type) {
|
||||
case UNDEFINED:
|
||||
case BYTE: {
|
||||
const uint8_t* typed_data = getData<uint8_t>();
|
||||
for (size_t i = 0; i < cappedCount; ++i) {
|
||||
output.append(std::to_string(typed_data[i]));
|
||||
output.append(" ");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ASCII: {
|
||||
const char* typed_data = reinterpret_cast<const char*>(getData<uint8_t>());
|
||||
size_t len = count;
|
||||
if (count > MAX_PRINT_STRING_LENGTH) {
|
||||
len = MAX_PRINT_STRING_LENGTH;
|
||||
}
|
||||
output.append(typed_data, len);
|
||||
break;
|
||||
}
|
||||
case SHORT: {
|
||||
const uint16_t* typed_data = getData<uint16_t>();
|
||||
for (size_t i = 0; i < cappedCount; ++i) {
|
||||
output.append(std::to_string(typed_data[i]));
|
||||
output.append(" ");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LONG: {
|
||||
const uint32_t* typed_data = getData<uint32_t>();
|
||||
for (size_t i = 0; i < cappedCount; ++i) {
|
||||
output.append(std::to_string(typed_data[i]));
|
||||
output.append(" ");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RATIONAL: {
|
||||
const uint32_t* typed_data = getData<uint32_t>();
|
||||
cappedCount <<= 1;
|
||||
for (size_t i = 0; i < cappedCount; i+=2) {
|
||||
output.append(std::to_string(typed_data[i]));
|
||||
output.append("/");
|
||||
output.append(std::to_string(typed_data[i + 1]));
|
||||
output.append(" ");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SBYTE: {
|
||||
const int8_t* typed_data = getData<int8_t>();
|
||||
for (size_t i = 0; i < cappedCount; ++i) {
|
||||
output.append(std::to_string(typed_data[i]));
|
||||
output.append(" ");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SSHORT: {
|
||||
const int16_t* typed_data = getData<int16_t>();
|
||||
for (size_t i = 0; i < cappedCount; ++i) {
|
||||
output.append(std::to_string(typed_data[i]));
|
||||
output.append(" ");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SLONG: {
|
||||
const int32_t* typed_data = getData<int32_t>();
|
||||
for (size_t i = 0; i < cappedCount; ++i) {
|
||||
output.append(std::to_string(typed_data[i]));
|
||||
output.append(" ");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SRATIONAL: {
|
||||
const int32_t* typed_data = getData<int32_t>();
|
||||
cappedCount <<= 1;
|
||||
for (size_t i = 0; i < cappedCount; i+=2) {
|
||||
output.append(std::to_string(typed_data[i]));
|
||||
output.append("/");
|
||||
output.append(std::to_string(typed_data[i + 1]));
|
||||
output.append(" ");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FLOAT: {
|
||||
const float* typed_data = getData<float>();
|
||||
for (size_t i = 0; i < cappedCount; ++i) {
|
||||
output.append(std::to_string(typed_data[i]));
|
||||
output.append(" ");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DOUBLE: {
|
||||
const double* typed_data = getData<double>();
|
||||
for (size_t i = 0; i < cappedCount; ++i) {
|
||||
output.append(std::to_string(typed_data[i]));
|
||||
output.append(" ");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
output.append("unknown type ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > MAX_PRINT_STRING_LENGTH) {
|
||||
output.append("...");
|
||||
}
|
||||
output.append("']");
|
||||
return output;
|
||||
}
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <img_utils/TiffEntryImpl.h>
|
||||
|
||||
// #include <utils/Vector.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
@ -0,0 +1,386 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "TiffIfd"
|
||||
|
||||
#include <img_utils/TagDefinitions.h>
|
||||
#include <img_utils/TiffHelpers.h>
|
||||
#include <img_utils/TiffIfd.h>
|
||||
#include <img_utils/TiffWriter.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
TiffIfd::TiffIfd(uint32_t ifdId)
|
||||
: mNextIfd(), mIfdId(ifdId), mStripOffsetsInitialized(false) {}
|
||||
|
||||
TiffIfd::~TiffIfd() {}
|
||||
|
||||
status_t TiffIfd::addEntry(const sp<TiffEntry>& entry) {
|
||||
size_t size = mEntries.size();
|
||||
if (size >= MAX_IFD_ENTRIES) {
|
||||
ALOGW("%s: Failed to add entry for tag 0x%x to IFD %u, too many entries in IFD!",
|
||||
__FUNCTION__, entry->getTag(), mIfdId);
|
||||
return BAD_INDEX;
|
||||
}
|
||||
|
||||
mEntries[entry->getTag()] = entry;
|
||||
return OK;
|
||||
}
|
||||
|
||||
sp<TiffEntry> TiffIfd::getEntry(uint16_t tag) const {
|
||||
auto it = mEntries.find(tag);
|
||||
if (it == mEntries.cend()) {
|
||||
ALOGW("%s: No entry for tag 0x%x in ifd %u.", __FUNCTION__, tag, mIfdId);
|
||||
return NULL;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void TiffIfd::removeEntry(uint16_t tag) {
|
||||
std::map<uint16_t, sp<TiffEntry> >::iterator it = mEntries.find(tag);
|
||||
if (it != mEntries.end()) {
|
||||
mEntries.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TiffIfd::setNextIfd(const sp<TiffIfd>& ifd) {
|
||||
mNextIfd = ifd;
|
||||
}
|
||||
|
||||
sp<TiffIfd> TiffIfd::getNextIfd() const {
|
||||
return mNextIfd;
|
||||
}
|
||||
|
||||
uint32_t TiffIfd::checkAndGetOffset(uint32_t offset) const {
|
||||
size_t size = mEntries.size();
|
||||
|
||||
if (size > MAX_IFD_ENTRIES) {
|
||||
ALOGW("%s: Could not calculate IFD offsets, IFD %u contains too many entries.",
|
||||
__FUNCTION__, mIfdId);
|
||||
return BAD_OFFSET;
|
||||
}
|
||||
|
||||
if (size <= 0) {
|
||||
ALOGW("%s: Could not calculate IFD offsets, IFD %u contains no entries.", __FUNCTION__,
|
||||
mIfdId);
|
||||
return BAD_OFFSET;
|
||||
}
|
||||
|
||||
if (offset == BAD_OFFSET) {
|
||||
ALOGW("%s: Could not calculate IFD offsets, IFD %u had a bad initial offset.",
|
||||
__FUNCTION__, mIfdId);
|
||||
return BAD_OFFSET;
|
||||
}
|
||||
|
||||
uint32_t ifdSize = calculateIfdSize(size);
|
||||
WORD_ALIGN(ifdSize);
|
||||
return offset + ifdSize;
|
||||
}
|
||||
|
||||
status_t TiffIfd::writeData(uint32_t offset, /*out*/EndianOutput* out) const {
|
||||
assert((offset % TIFF_WORD_SIZE) == 0);
|
||||
status_t ret = OK;
|
||||
|
||||
ALOGV("%s: IFD %u written to offset %u", __FUNCTION__, mIfdId, offset );
|
||||
uint32_t valueOffset = checkAndGetOffset(offset);
|
||||
if (valueOffset == 0) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
size_t size = mEntries.size();
|
||||
|
||||
// Writer IFD header (2 bytes, number of entries).
|
||||
uint16_t header = static_cast<uint16_t>(size);
|
||||
BAIL_ON_FAIL(out->write(&header, 0, 1), ret);
|
||||
|
||||
// Write tag entries
|
||||
for (auto it = mEntries.cbegin(); it != mEntries.cend(); ++it) {
|
||||
BAIL_ON_FAIL(it->second->writeTagInfo(valueOffset, out), ret);
|
||||
valueOffset += it->second->getSize();
|
||||
}
|
||||
|
||||
// Writer IFD footer (4 bytes, offset to next IFD).
|
||||
uint32_t footer = (mNextIfd != NULL) ? offset + getSize() : 0;
|
||||
BAIL_ON_FAIL(out->write(&footer, 0, 1), ret);
|
||||
|
||||
assert(out->getCurrentOffset() == offset + calculateIfdSize(size));
|
||||
|
||||
// Write zeroes till word aligned
|
||||
ZERO_TILL_WORD(out, calculateIfdSize(size), ret);
|
||||
|
||||
// Write values for each tag entry
|
||||
for (auto it = mEntries.cbegin(); it != mEntries.cend(); ++it) {
|
||||
size_t last = out->getCurrentOffset();
|
||||
// Only write values that are too large to fit in the 12-byte TIFF entry
|
||||
if (it->second->getSize() > OFFSET_SIZE) {
|
||||
BAIL_ON_FAIL(it->second->writeData(out->getCurrentOffset(), out), ret);
|
||||
}
|
||||
size_t next = out->getCurrentOffset();
|
||||
size_t diff = (next - last);
|
||||
size_t actual = it->second->getSize();
|
||||
if (diff != actual) {
|
||||
ALOGW("Sizes do not match for tag %x. Expected %zu, received %zu",
|
||||
it->first, actual, diff);
|
||||
}
|
||||
}
|
||||
|
||||
assert(out->getCurrentOffset() == offset + getSize());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t TiffIfd::getSize() const {
|
||||
size_t size = mEntries.size();
|
||||
uint32_t total = calculateIfdSize(size);
|
||||
WORD_ALIGN(total);
|
||||
for (auto it = mEntries.cbegin(); it != mEntries.cend(); ++it) {
|
||||
total += it->second->getSize();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
uint32_t TiffIfd::getId() const {
|
||||
return mIfdId;
|
||||
}
|
||||
|
||||
uint32_t TiffIfd::getComparableValue() const {
|
||||
return mIfdId;
|
||||
}
|
||||
|
||||
status_t TiffIfd::validateAndSetStripTags() {
|
||||
sp<TiffEntry> widthEntry = getEntry(TAG_IMAGEWIDTH);
|
||||
if (widthEntry == NULL) {
|
||||
ALOGE("%s: IFD %u doesn't have a ImageWidth tag set", __FUNCTION__, mIfdId);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
sp<TiffEntry> heightEntry = getEntry(TAG_IMAGELENGTH);
|
||||
if (heightEntry == NULL) {
|
||||
ALOGE("%s: IFD %u doesn't have a ImageLength tag set", __FUNCTION__, mIfdId);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
sp<TiffEntry> samplesEntry = getEntry(TAG_SAMPLESPERPIXEL);
|
||||
if (samplesEntry == NULL) {
|
||||
ALOGE("%s: IFD %u doesn't have a SamplesPerPixel tag set", __FUNCTION__, mIfdId);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
sp<TiffEntry> bitsEntry = getEntry(TAG_BITSPERSAMPLE);
|
||||
if (bitsEntry == NULL) {
|
||||
ALOGE("%s: IFD %u doesn't have a BitsPerSample tag set", __FUNCTION__, mIfdId);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
uint32_t width = *(widthEntry->getData<uint32_t>());
|
||||
uint32_t height = *(heightEntry->getData<uint32_t>());
|
||||
uint16_t bitsPerSample = *(bitsEntry->getData<uint16_t>());
|
||||
uint16_t samplesPerPixel = *(samplesEntry->getData<uint16_t>());
|
||||
|
||||
if ((bitsPerSample % 8) != 0) {
|
||||
ALOGE("%s: BitsPerSample %d in IFD %u is not byte-aligned.", __FUNCTION__,
|
||||
bitsPerSample, mIfdId);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
uint32_t bytesPerSample = bitsPerSample / 8;
|
||||
|
||||
// Choose strip size as close to 8kb as possible without splitting rows.
|
||||
// If the row length is >8kb, each strip will only contain a single row.
|
||||
const uint32_t rowLengthBytes = bytesPerSample * samplesPerPixel * width;
|
||||
const uint32_t idealChunkSize = (1 << 13); // 8kb
|
||||
uint32_t rowsPerChunk = idealChunkSize / rowLengthBytes;
|
||||
rowsPerChunk = (rowsPerChunk == 0) ? 1 : rowsPerChunk;
|
||||
const uint32_t actualChunkSize = rowLengthBytes * rowsPerChunk;
|
||||
|
||||
const uint32_t lastChunkRows = height % rowsPerChunk;
|
||||
const uint32_t lastChunkSize = lastChunkRows * rowLengthBytes;
|
||||
|
||||
if (actualChunkSize > /*max strip size for TIFF/EP*/65536) {
|
||||
ALOGE("%s: Strip length too long.", __FUNCTION__);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
size_t numStrips = height / rowsPerChunk;
|
||||
|
||||
// Add another strip for the incomplete chunk.
|
||||
if (lastChunkRows > 0) {
|
||||
numStrips += 1;
|
||||
}
|
||||
|
||||
// Put each row in it's own strip
|
||||
uint32_t rowsPerStripVal = rowsPerChunk;
|
||||
sp<TiffEntry> rowsPerStrip = TiffWriter::uncheckedBuildEntry(TAG_ROWSPERSTRIP, LONG, 1,
|
||||
UNDEFINED_ENDIAN, &rowsPerStripVal);
|
||||
|
||||
if (rowsPerStrip == NULL) {
|
||||
ALOGE("%s: Could not build entry for RowsPerStrip tag.", __FUNCTION__);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> byteCounts;
|
||||
byteCounts.reserve(numStrips);
|
||||
|
||||
for (size_t i = 0; i < numStrips; ++i) {
|
||||
if (lastChunkRows > 0 && i == (numStrips - 1)) {
|
||||
byteCounts.push_back(lastChunkSize);
|
||||
} else {
|
||||
byteCounts.push_back(actualChunkSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Set byte counts for each strip
|
||||
sp<TiffEntry> stripByteCounts = TiffWriter::uncheckedBuildEntry(TAG_STRIPBYTECOUNTS, LONG,
|
||||
static_cast<uint32_t>(numStrips), UNDEFINED_ENDIAN, &byteCounts[0]);
|
||||
|
||||
if (stripByteCounts == NULL) {
|
||||
ALOGE("%s: Could not build entry for StripByteCounts tag.", __FUNCTION__);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> stripOffsetsVector;
|
||||
stripOffsetsVector.resize(numStrips);
|
||||
|
||||
// Set uninitialized offsets
|
||||
sp<TiffEntry> stripOffsets = TiffWriter::uncheckedBuildEntry(TAG_STRIPOFFSETS, LONG,
|
||||
static_cast<uint32_t>(numStrips), UNDEFINED_ENDIAN, &stripOffsetsVector[0]);
|
||||
|
||||
if (stripOffsets == NULL) {
|
||||
ALOGE("%s: Could not build entry for StripOffsets tag.", __FUNCTION__);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
if(addEntry(stripByteCounts) != OK) {
|
||||
ALOGE("%s: Could not add entry for StripByteCounts to IFD %u", __FUNCTION__, mIfdId);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
if(addEntry(rowsPerStrip) != OK) {
|
||||
ALOGE("%s: Could not add entry for StripByteCounts to IFD %u", __FUNCTION__, mIfdId);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
if(addEntry(stripOffsets) != OK) {
|
||||
ALOGE("%s: Could not add entry for StripByteCounts to IFD %u", __FUNCTION__, mIfdId);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mStripOffsetsInitialized = true;
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool TiffIfd::uninitializedOffsets() const {
|
||||
return mStripOffsetsInitialized;
|
||||
}
|
||||
|
||||
status_t TiffIfd::setStripOffset(uint32_t offset) {
|
||||
|
||||
// Get old offsets and bytecounts
|
||||
sp<TiffEntry> oldOffsets = getEntry(TAG_STRIPOFFSETS);
|
||||
if (oldOffsets == NULL) {
|
||||
ALOGE("%s: IFD %u does not contain StripOffsets entry.", __FUNCTION__, mIfdId);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
sp<TiffEntry> stripByteCounts = getEntry(TAG_STRIPBYTECOUNTS);
|
||||
if (stripByteCounts == NULL) {
|
||||
ALOGE("%s: IFD %u does not contain StripByteCounts entry.", __FUNCTION__, mIfdId);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
uint32_t offsetsCount = oldOffsets->getCount();
|
||||
uint32_t byteCount = stripByteCounts->getCount();
|
||||
if (offsetsCount != byteCount) {
|
||||
ALOGE("%s: StripOffsets count (%u) doesn't match StripByteCounts count (%u) in IFD %u",
|
||||
__FUNCTION__, offsetsCount, byteCount, mIfdId);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
const uint32_t* stripByteCountsArray = stripByteCounts->getData<uint32_t>();
|
||||
|
||||
size_t numStrips = offsetsCount;
|
||||
|
||||
std::vector<uint32_t> stripOffsets;
|
||||
stripOffsets.reserve(numStrips);
|
||||
|
||||
// Calculate updated byte offsets
|
||||
for (size_t i = 0; i < numStrips; ++i) {
|
||||
stripOffsets.push_back(offset);
|
||||
offset += stripByteCountsArray[i];
|
||||
}
|
||||
|
||||
sp<TiffEntry> newOffsets = TiffWriter::uncheckedBuildEntry(TAG_STRIPOFFSETS, LONG,
|
||||
static_cast<uint32_t>(numStrips), UNDEFINED_ENDIAN, &stripOffsets[0]);
|
||||
|
||||
if (newOffsets == NULL) {
|
||||
ALOGE("%s: Coult not build updated offsets entry in IFD %u", __FUNCTION__, mIfdId);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
if (addEntry(newOffsets) != OK) {
|
||||
ALOGE("%s: Failed to add updated offsets entry in IFD %u", __FUNCTION__, mIfdId);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
uint32_t TiffIfd::getStripSize() const {
|
||||
sp<TiffEntry> stripByteCounts = getEntry(TAG_STRIPBYTECOUNTS);
|
||||
if (stripByteCounts == NULL) {
|
||||
ALOGE("%s: IFD %u does not contain StripByteCounts entry.", __FUNCTION__, mIfdId);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
uint32_t count = stripByteCounts->getCount();
|
||||
const uint32_t* byteCounts = stripByteCounts->getData<uint32_t>();
|
||||
|
||||
uint32_t total = 0;
|
||||
for (size_t i = 0; i < static_cast<size_t>(count); ++i) {
|
||||
total += byteCounts[i];
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
std::string TiffIfd::toString() const {
|
||||
size_t s = mEntries.size();
|
||||
std::string output;
|
||||
char buf[1024] = { 0 };
|
||||
snprintf(buf, sizeof(buf), "[ifd: %x, num_entries: %zu, entries:\n", getId(), s);
|
||||
output.append(buf);
|
||||
for(auto it = mEntries.cbegin(); it != mEntries.cend(); ++it) {
|
||||
output.append("\t");
|
||||
output.append(it->second->toString());
|
||||
output.append("\n");
|
||||
}
|
||||
output.append(", next_ifd: %x]", ((mNextIfd != NULL) ? mNextIfd->getId() : 0));
|
||||
return output;
|
||||
}
|
||||
|
||||
void TiffIfd::log() const {
|
||||
size_t s = mEntries.size();
|
||||
ALOGI("[ifd: %x, num_entries: %zu, entries:\n", getId(), s);
|
||||
for(auto it = mEntries.cbegin(); it != mEntries.cend(); ++it) {
|
||||
ALOGI("\t%s", it->second->toString().c_str());
|
||||
}
|
||||
ALOGI(", next_ifd: %x]", ((mNextIfd != NULL) ? mNextIfd->getId() : 0));
|
||||
}
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
#include <img_utils/TiffWritable.h>
|
||||
#include <img_utils/TiffHelpers.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
TiffWritable::TiffWritable() {}
|
||||
|
||||
TiffWritable::~TiffWritable() {}
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
@ -0,0 +1,425 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "TiffWriter"
|
||||
|
||||
#include <img_utils/TiffHelpers.h>
|
||||
#include <img_utils/TiffWriter.h>
|
||||
#include <img_utils/TagDefinitions.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
#if 0
|
||||
KeyedVector<uint16_t, const TagDefinition_t*> TiffWriter::buildTagMap(
|
||||
const TagDefinition_t* definitions, size_t length) {
|
||||
KeyedVector<uint16_t, const TagDefinition_t*> map;
|
||||
for(size_t i = 0; i < length; ++i) {
|
||||
map.add(definitions[i].tagId, definitions + i);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define COMPARE(op) \
|
||||
bool Orderable::operator op (const Orderable& orderable) const { \
|
||||
return getComparableValue() op orderable.getComparableValue(); \
|
||||
}
|
||||
|
||||
#define ARRAY_SIZE(array) \
|
||||
(sizeof(array) / sizeof((array)[0]))
|
||||
|
||||
#if 0
|
||||
KeyedVector<uint16_t, const TagDefinition_t*> TiffWriter::sTagMaps[] = {
|
||||
buildTagMap(TIFF_EP_TAG_DEFINITIONS, ARRAY_SIZE(TIFF_EP_TAG_DEFINITIONS)),
|
||||
buildTagMap(DNG_TAG_DEFINITIONS, ARRAY_SIZE(DNG_TAG_DEFINITIONS)),
|
||||
buildTagMap(EXIF_2_3_TAG_DEFINITIONS, ARRAY_SIZE(EXIF_2_3_TAG_DEFINITIONS)),
|
||||
buildTagMap(TIFF_6_TAG_DEFINITIONS, ARRAY_SIZE(TIFF_6_TAG_DEFINITIONS))
|
||||
};
|
||||
#endif
|
||||
|
||||
TiffWriter::TiffWriter() : mNumTagMaps(DEFAULT_NUM_TAG_MAPS)
|
||||
{
|
||||
mTagMaps.reserve(DEFAULT_NUM_TAG_MAPS);
|
||||
// = new KeyedVector<uint16_t, const TagDefinition_t*>[DEFAULT_NUM_TAG_MAPS];
|
||||
|
||||
std::vector<std::map<uint16_t, const TagDefinition_t*> >::iterator it = mTagMaps.insert(mTagMaps.end(), std::map<uint16_t, const TagDefinition_t*>());
|
||||
for(size_t i = 0; i < ARRAY_SIZE(TIFF_EP_TAG_DEFINITIONS); ++i) {
|
||||
(*it)[TIFF_EP_TAG_DEFINITIONS[i].tagId] = TIFF_EP_TAG_DEFINITIONS + i;
|
||||
}
|
||||
it = mTagMaps.insert(mTagMaps.end(), std::map<uint16_t, const TagDefinition_t*>());
|
||||
for(size_t i = 0; i < ARRAY_SIZE(DNG_TAG_DEFINITIONS); ++i) {
|
||||
(*it)[DNG_TAG_DEFINITIONS[i].tagId] = DNG_TAG_DEFINITIONS + i;
|
||||
}
|
||||
it = mTagMaps.insert(mTagMaps.end(), std::map<uint16_t, const TagDefinition_t*>());
|
||||
for(size_t i = 0; i < ARRAY_SIZE(EXIF_2_3_TAG_DEFINITIONS); ++i) {
|
||||
(*it)[EXIF_2_3_TAG_DEFINITIONS[i].tagId] = EXIF_2_3_TAG_DEFINITIONS + i;
|
||||
}
|
||||
it = mTagMaps.insert(mTagMaps.end(), std::map<uint16_t, const TagDefinition_t*>());
|
||||
for(size_t i = 0; i < ARRAY_SIZE(TIFF_6_TAG_DEFINITIONS); ++i) {
|
||||
(*it)[TIFF_6_TAG_DEFINITIONS[i].tagId] = TIFF_6_TAG_DEFINITIONS + i;
|
||||
}
|
||||
}
|
||||
|
||||
TiffWriter::TiffWriter(std::map<uint16_t, const TagDefinition_t*>* enabledDefinitions,
|
||||
size_t length) : mNumTagMaps(length)
|
||||
{
|
||||
mTagMaps.reserve(length);
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
auto it = mTagMaps.insert(mTagMaps.end(), std::map<uint16_t, const TagDefinition_t*>());
|
||||
for(auto it2 = enabledDefinitions[i].cbegin(); it2 != enabledDefinitions[i].cend(); ++it2) {
|
||||
(*it)[it2->first] = it2->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TiffWriter::~TiffWriter() {}
|
||||
|
||||
status_t TiffWriter::write(Output* out, StripSource** sources, size_t sourcesCount,
|
||||
Endianness end) {
|
||||
status_t ret = OK;
|
||||
EndianOutput endOut(out, end);
|
||||
|
||||
if (mIfd == NULL) {
|
||||
ALOGE("%s: Tiff header is empty.", __FUNCTION__);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
uint32_t totalSize = getTotalSize();
|
||||
|
||||
std::map<uint32_t, uint32_t> offsetVector;
|
||||
|
||||
for (std::map<uint32_t, sp<TiffIfd> >::iterator it = mNamedIfds.begin(); it != mNamedIfds.end(); ++it) {
|
||||
if (it->second->uninitializedOffsets()) {
|
||||
uint32_t stripSize = it->second->getStripSize();
|
||||
if (it->second->setStripOffset(totalSize) != OK) {
|
||||
ALOGE("%s: Could not set strip offsets.", __FUNCTION__);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
totalSize += stripSize;
|
||||
WORD_ALIGN(totalSize);
|
||||
offsetVector[it->first] = totalSize;
|
||||
}
|
||||
}
|
||||
|
||||
size_t offVecSize = offsetVector.size();
|
||||
if (offVecSize != sourcesCount) {
|
||||
ALOGE("%s: Mismatch between number of IFDs with uninitialized strips (%zu) and"
|
||||
" sources (%zu).", __FUNCTION__, offVecSize, sourcesCount);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
BAIL_ON_FAIL(writeFileHeader(endOut), ret);
|
||||
|
||||
uint32_t offset = FILE_HEADER_SIZE;
|
||||
sp<TiffIfd> ifd = mIfd;
|
||||
while(ifd != NULL) {
|
||||
BAIL_ON_FAIL(ifd->writeData(offset, &endOut), ret);
|
||||
offset += ifd->getSize();
|
||||
ifd = ifd->getNextIfd();
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
log();
|
||||
#endif
|
||||
|
||||
for (auto it = offsetVector.begin(); it != offsetVector.end(); ++it) {
|
||||
uint32_t ifdKey = it->first;
|
||||
uint32_t sizeToWrite = mNamedIfds[ifdKey]->getStripSize();
|
||||
bool found = false;
|
||||
for (size_t j = 0; j < sourcesCount; ++j) {
|
||||
if (sources[j]->getIfd() == ifdKey) {
|
||||
int i = std::distance(offsetVector.begin(), it);
|
||||
if ((ret = sources[i]->writeToStream(endOut, sizeToWrite)) != OK) {
|
||||
ALOGE("%s: Could not write to stream, received %d.", __FUNCTION__, ret);
|
||||
return ret;
|
||||
}
|
||||
ZERO_TILL_WORD(&endOut, sizeToWrite, ret);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
ALOGE("%s: No stream for byte strips for IFD %u", __FUNCTION__, ifdKey);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
assert(it->second == endOut.getCurrentOffset());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
status_t TiffWriter::write(Output* out, Endianness end) {
|
||||
status_t ret = OK;
|
||||
EndianOutput endOut(out, end);
|
||||
|
||||
if (mIfd == NULL) {
|
||||
ALOGE("%s: Tiff header is empty.", __FUNCTION__);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
BAIL_ON_FAIL(writeFileHeader(endOut), ret);
|
||||
|
||||
uint32_t offset = FILE_HEADER_SIZE;
|
||||
sp<TiffIfd> ifd = mIfd;
|
||||
while(ifd != NULL) {
|
||||
BAIL_ON_FAIL(ifd->writeData(offset, &endOut), ret);
|
||||
offset += ifd->getSize();
|
||||
ifd = ifd->getNextIfd();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
const TagDefinition_t* TiffWriter::lookupDefinition(uint16_t tag) const {
|
||||
const TagDefinition_t* definition = NULL;
|
||||
for (size_t i = 0; i < mNumTagMaps; ++i) {
|
||||
auto it = mTagMaps[i].find(tag);
|
||||
if (it != mTagMaps[i].cend()) {
|
||||
definition = it->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (definition == NULL) {
|
||||
ALOGE("%s: No definition exists for tag with id %x.", __FUNCTION__, tag);
|
||||
}
|
||||
return definition;
|
||||
}
|
||||
|
||||
sp<TiffEntry> TiffWriter::getEntry(uint16_t tag, uint32_t ifd) const {
|
||||
auto it = mNamedIfds.find(ifd);
|
||||
if (it == mNamedIfds.cend()) {
|
||||
ALOGE("%s: No IFD %d set for this writer.", __FUNCTION__, ifd);
|
||||
return NULL;
|
||||
}
|
||||
return it->second->getEntry(tag);
|
||||
}
|
||||
|
||||
void TiffWriter::removeEntry(uint16_t tag, uint32_t ifd) {
|
||||
auto it = mNamedIfds.find(ifd);
|
||||
if (it != mNamedIfds.end()) {
|
||||
it->second->removeEntry(tag);
|
||||
}
|
||||
}
|
||||
|
||||
status_t TiffWriter::addEntry(const sp<TiffEntry>& entry, uint32_t ifd) {
|
||||
uint16_t tag = entry->getTag();
|
||||
|
||||
const TagDefinition_t* definition = lookupDefinition(tag);
|
||||
|
||||
if (definition == NULL) {
|
||||
ALOGE("%s: No definition exists for tag 0x%x.", __FUNCTION__, tag);
|
||||
return BAD_INDEX;
|
||||
}
|
||||
|
||||
std::map<uint32_t, sp<TiffIfd> >::iterator it = mNamedIfds.find(ifd);
|
||||
|
||||
// Add a new IFD if necessary
|
||||
if (it == mNamedIfds.end()) {
|
||||
ALOGE("%s: No IFD %u exists.", __FUNCTION__, ifd);
|
||||
return NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
sp<TiffIfd> selectedIfd = it->second;
|
||||
return selectedIfd->addEntry(entry);
|
||||
}
|
||||
|
||||
status_t TiffWriter::addStrip(uint32_t ifd) {
|
||||
std::map<uint32_t, sp<TiffIfd> >::iterator it = mNamedIfds.find(ifd);
|
||||
if (it == mNamedIfds.end()) {
|
||||
ALOGE("%s: Ifd %u doesn't exist, cannot add strip entries.", __FUNCTION__, ifd);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
sp<TiffIfd> selected = it->second;
|
||||
return selected->validateAndSetStripTags();
|
||||
}
|
||||
|
||||
status_t TiffWriter::addIfd(uint32_t ifd) {
|
||||
std::map<uint32_t, sp<TiffIfd> >::iterator it = mNamedIfds.find(ifd);
|
||||
if (it != mNamedIfds.end()) {
|
||||
ALOGE("%s: Ifd with ID 0x%x already exists.", __FUNCTION__, ifd);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
sp<TiffIfd> newIfd = new TiffIfd(ifd);
|
||||
if (mIfd == NULL) {
|
||||
mIfd = newIfd;
|
||||
} else {
|
||||
sp<TiffIfd> last = findLastIfd();
|
||||
last->setNextIfd(newIfd);
|
||||
}
|
||||
|
||||
mNamedIfds[ifd] = newIfd;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t TiffWriter::addSubIfd(uint32_t parentIfd, uint32_t ifd, SubIfdType type) {
|
||||
|
||||
std::map<uint32_t, sp<TiffIfd> >::iterator it = mNamedIfds.find(ifd);
|
||||
if (it != mNamedIfds.end()) {
|
||||
ALOGE("%s: Ifd with ID 0x%x already exists.", __FUNCTION__, ifd);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
std::map<uint32_t, sp<TiffIfd> >::iterator parentIt = mNamedIfds.find(parentIfd);
|
||||
if (parentIt == mNamedIfds.end()) {
|
||||
ALOGE("%s: Parent IFD with ID 0x%x does not exist.", __FUNCTION__, parentIfd);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
sp<TiffIfd> parent = parentIt->second;
|
||||
sp<TiffIfd> newIfd = new TiffIfd(ifd);
|
||||
|
||||
uint16_t subIfdTag;
|
||||
if (type == SUBIFD) {
|
||||
subIfdTag = TAG_SUBIFDS;
|
||||
} else if (type == GPSINFO) {
|
||||
subIfdTag = TAG_GPSINFO;
|
||||
} else {
|
||||
ALOGE("%s: Unknown SubIFD type %d.", __FUNCTION__, type);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
sp<TiffEntry> subIfds = parent->getEntry(subIfdTag);
|
||||
if (subIfds == NULL) {
|
||||
if (buildEntry(subIfdTag, 1, &newIfd, &subIfds) < 0) {
|
||||
ALOGE("%s: Failed to build SubIfd entry in IFD 0x%x.", __FUNCTION__, parentIfd);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
} else {
|
||||
if (type == GPSINFO) {
|
||||
ALOGE("%s: Cannot add GPSInfo SubIFD to IFD %u, one already exists.", __FUNCTION__,
|
||||
ifd);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
std::vector<sp<TiffIfd> > subIfdList;
|
||||
const sp<TiffIfd>* oldIfdArray = subIfds->getData<sp<TiffIfd> >();
|
||||
subIfdList.insert(subIfdList.end(), oldIfdArray, oldIfdArray + subIfds->getCount());
|
||||
#if 0
|
||||
ALOGE("%s: Failed to build SubIfd entry in IFD 0x%x.", __FUNCTION__, parentIfd);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
subIfdList.push_back(newIfd); // < 0) {
|
||||
#if 0
|
||||
ALOGE("%s: Failed to build SubIfd entry in IFD 0x%x.", __FUNCTION__, parentIfd);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t count = subIfdList.size();
|
||||
if (buildEntry(subIfdTag, count, &subIfdList[0], &subIfds) < 0) {
|
||||
ALOGE("%s: Failed to build SubIfd entry in IFD 0x%x.", __FUNCTION__, parentIfd);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (parent->addEntry(subIfds) < 0) {
|
||||
ALOGE("%s: Failed to add SubIfd entry in IFD 0x%x.", __FUNCTION__, parentIfd);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mNamedIfds[ifd] = newIfd;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
TagType TiffWriter::getDefaultType(uint16_t tag) const {
|
||||
const TagDefinition_t* definition = lookupDefinition(tag);
|
||||
if (definition == NULL) {
|
||||
ALOGE("%s: Could not find definition for tag %x", __FUNCTION__, tag);
|
||||
return UNKNOWN_TAGTYPE;
|
||||
}
|
||||
return definition->defaultType;
|
||||
}
|
||||
|
||||
uint32_t TiffWriter::getDefaultCount(uint16_t tag) const {
|
||||
const TagDefinition_t* definition = lookupDefinition(tag);
|
||||
if (definition == NULL) {
|
||||
ALOGE("%s: Could not find definition for tag %x", __FUNCTION__, tag);
|
||||
return 0;
|
||||
}
|
||||
return definition->fixedCount;
|
||||
}
|
||||
|
||||
bool TiffWriter::hasIfd(uint32_t ifd) const {
|
||||
auto it = mNamedIfds.find(ifd);
|
||||
return it != mNamedIfds.cend();
|
||||
}
|
||||
|
||||
bool TiffWriter::checkIfDefined(uint16_t tag) const {
|
||||
return lookupDefinition(tag) != NULL;
|
||||
}
|
||||
|
||||
const char* TiffWriter::getTagName(uint16_t tag) const {
|
||||
const TagDefinition_t* definition = lookupDefinition(tag);
|
||||
if (definition == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return definition->tagName;
|
||||
}
|
||||
|
||||
sp<TiffIfd> TiffWriter::findLastIfd() {
|
||||
sp<TiffIfd> ifd = mIfd;
|
||||
while(ifd != NULL) {
|
||||
sp<TiffIfd> nextIfd = ifd->getNextIfd();
|
||||
if (nextIfd == NULL) {
|
||||
break;
|
||||
}
|
||||
ifd = std::move(nextIfd);
|
||||
}
|
||||
return ifd;
|
||||
}
|
||||
|
||||
status_t TiffWriter::writeFileHeader(EndianOutput& out) {
|
||||
status_t ret = OK;
|
||||
uint16_t endMarker = (out.getEndianness() == BIG) ? BIG_ENDIAN_MARKER : LITTLE_ENDIAN_MARKER;
|
||||
BAIL_ON_FAIL(out.write(&endMarker, 0, 1), ret);
|
||||
|
||||
uint16_t tiffMarker = TIFF_FILE_MARKER;
|
||||
BAIL_ON_FAIL(out.write(&tiffMarker, 0, 1), ret);
|
||||
|
||||
uint32_t offsetMarker = FILE_HEADER_SIZE;
|
||||
BAIL_ON_FAIL(out.write(&offsetMarker, 0, 1), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t TiffWriter::getTotalSize() const {
|
||||
uint32_t totalSize = FILE_HEADER_SIZE;
|
||||
sp<TiffIfd> ifd = mIfd;
|
||||
while(ifd != NULL) {
|
||||
totalSize += ifd->getSize();
|
||||
ifd = ifd->getNextIfd();
|
||||
}
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
void TiffWriter::log() const {
|
||||
ALOGI("%s: TiffWriter:", __FUNCTION__);
|
||||
size_t length = mNamedIfds.size();
|
||||
for (auto it = mNamedIfds.begin(); it != mNamedIfds.end(); ++it) {
|
||||
it->second->log();
|
||||
}
|
||||
}
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2011, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <cutils/android_reboot.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <cutils/properties.h>
|
||||
|
||||
#define TAG "android_reboot"
|
||||
|
||||
int android_reboot(unsigned cmd, int /*flags*/, const char* arg) {
|
||||
int ret;
|
||||
const char* restart_cmd = NULL;
|
||||
char* prop_value;
|
||||
|
||||
switch (cmd) {
|
||||
case ANDROID_RB_RESTART: // deprecated
|
||||
case ANDROID_RB_RESTART2:
|
||||
restart_cmd = "reboot";
|
||||
break;
|
||||
case ANDROID_RB_POWEROFF:
|
||||
restart_cmd = "shutdown";
|
||||
break;
|
||||
case ANDROID_RB_THERMOFF:
|
||||
restart_cmd = "shutdown,thermal";
|
||||
break;
|
||||
}
|
||||
if (!restart_cmd) return -1;
|
||||
if (arg && arg[0]) {
|
||||
ret = asprintf(&prop_value, "%s,%s", restart_cmd, arg);
|
||||
} else {
|
||||
ret = asprintf(&prop_value, "%s", restart_cmd);
|
||||
}
|
||||
if (ret < 0) return -1;
|
||||
ret = property_set(ANDROID_RB_PROPERTY, prop_value);
|
||||
free(prop_value);
|
||||
return ret;
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#if defined(__slm__)
|
||||
/* Values are optimized for Silvermont */
|
||||
#define SHARED_CACHE_SIZE (1024*1024) /* Silvermont L2 Cache */
|
||||
#define DATA_CACHE_SIZE (24*1024) /* Silvermont L1 Data Cache */
|
||||
#else
|
||||
/* Values are optimized for Atom */
|
||||
#define SHARED_CACHE_SIZE (512*1024) /* Atom L2 Cache */
|
||||
#define DATA_CACHE_SIZE (24*1024) /* Atom L1 Data Cache */
|
||||
#endif
|
||||
|
||||
#define SHARED_CACHE_SIZE_HALF (SHARED_CACHE_SIZE / 2)
|
||||
#define DATA_CACHE_SIZE_HALF (DATA_CACHE_SIZE / 2)
|
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* Values are optimized for Silvermont */
|
||||
#define SHARED_CACHE_SIZE (1024*1024) /* Silvermont L2 Cache */
|
||||
#define DATA_CACHE_SIZE (24*1024) /* Silvermont L1 Data Cache */
|
||||
|
||||
#define SHARED_CACHE_SIZE_HALF (SHARED_CACHE_SIZE / 2)
|
||||
#define DATA_CACHE_SIZE_HALF (DATA_CACHE_SIZE / 2)
|
@ -0,0 +1,468 @@
|
||||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <cutils/ashmem.h>
|
||||
|
||||
/*
|
||||
* Implementation of the user-space ashmem API for devices, which have our
|
||||
* ashmem-enabled kernel. See ashmem-sim.c for the "fake" tmp-based version,
|
||||
* used by the simulator.
|
||||
*/
|
||||
#define LOG_TAG "ashmem"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/ashmem.h>
|
||||
#include <linux/memfd.h>
|
||||
#include <log/log.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
/* Will be added to UAPI once upstream change is merged */
|
||||
#define F_SEAL_FUTURE_WRITE 0x0010
|
||||
|
||||
/*
|
||||
* The minimum vendor API level at and after which it is safe to use memfd.
|
||||
* This is to facilitate deprecation of ashmem.
|
||||
*/
|
||||
#define MIN_MEMFD_VENDOR_API_LEVEL 29
|
||||
#define MIN_MEMFD_VENDOR_API_LEVEL_CHAR 'Q'
|
||||
|
||||
/* ashmem identity */
|
||||
static dev_t __ashmem_rdev;
|
||||
/*
|
||||
* If we trigger a signal handler in the middle of locked activity and the
|
||||
* signal handler calls ashmem, we could get into a deadlock state.
|
||||
*/
|
||||
static pthread_mutex_t __ashmem_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/*
|
||||
* has_memfd_support() determines if the device can use memfd. memfd support
|
||||
* has been there for long time, but certain things in it may be missing. We
|
||||
* check for needed support in it. Also we check if the VNDK version of
|
||||
* libcutils being used is new enough, if its not, then we cannot use memfd
|
||||
* since the older copies may be using ashmem so we just use ashmem. Once all
|
||||
* Android devices that are getting updates are new enough (ex, they were
|
||||
* originally shipped with Android release > P), then we can just use memfd and
|
||||
* delete all ashmem code from libcutils (while preserving the interface).
|
||||
*
|
||||
* NOTE:
|
||||
* The sys.use_memfd property is set by default to false in Android
|
||||
* to temporarily disable memfd, till vendor and apps are ready for it.
|
||||
* The main issue: either apps or vendor processes can directly make ashmem
|
||||
* IOCTLs on FDs they receive by assuming they are ashmem, without going
|
||||
* through libcutils. Such fds could have very well be originally created with
|
||||
* libcutils hence they could be memfd. Thus the IOCTLs will break.
|
||||
*
|
||||
* Set default value of sys.use_memfd property to true once the issue is
|
||||
* resolved, so that the code can then self-detect if kernel support is present
|
||||
* on the device. The property can also set to true from adb shell, for
|
||||
* debugging.
|
||||
*/
|
||||
|
||||
static bool debug_log = false; /* set to true for verbose logging and other debug */
|
||||
static bool pin_deprecation_warn = true; /* Log the pin deprecation warning only once */
|
||||
|
||||
/* Determine if vendor processes would be ok with memfd in the system:
|
||||
*
|
||||
* If VNDK is using older libcutils, don't use memfd. This is so that the
|
||||
* same shared memory mechanism is used across binder transactions between
|
||||
* vendor partition processes and system partition processes.
|
||||
*/
|
||||
static bool check_vendor_memfd_allowed() {
|
||||
std::string vndk_version = android::base::GetProperty("ro.vndk.version", "");
|
||||
|
||||
if (vndk_version == "") {
|
||||
ALOGE("memfd: ro.vndk.version not defined or invalid (%s), this is mandated since P.\n",
|
||||
vndk_version.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
/* No issues if vendor is targetting current Dessert */
|
||||
if (vndk_version == "current") {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if VNDK version is a number and act on it */
|
||||
char* p;
|
||||
long int vers = strtol(vndk_version.c_str(), &p, 10);
|
||||
if (*p == 0) {
|
||||
if (vers < MIN_MEMFD_VENDOR_API_LEVEL) {
|
||||
ALOGI("memfd: device VNDK version (%s) is < Q so using ashmem.\n",
|
||||
vndk_version.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If its not a number, assume string, but check if its a sane string */
|
||||
if (tolower(vndk_version[0]) < 'a' || tolower(vndk_version[0]) > 'z') {
|
||||
ALOGE("memfd: ro.vndk.version not defined or invalid (%s), this is mandated since P.\n",
|
||||
vndk_version.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tolower(vndk_version[0]) < tolower(MIN_MEMFD_VENDOR_API_LEVEL_CHAR)) {
|
||||
ALOGI("memfd: device is using VNDK version (%s) which is less than Q. Use ashmem only.\n",
|
||||
vndk_version.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* Determine if memfd can be supported. This is just one-time hardwork
|
||||
* which will be cached by the caller.
|
||||
*/
|
||||
static bool __has_memfd_support() {
|
||||
if (check_vendor_memfd_allowed() == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Used to turn on/off the detection at runtime, in the future this
|
||||
* property will be removed once we switch everything over to ashmem.
|
||||
* Currently it is used only for debugging to switch the system over.
|
||||
*/
|
||||
if (!android::base::GetBoolProperty("sys.use_memfd", false)) {
|
||||
if (debug_log) {
|
||||
ALOGD("sys.use_memfd=false so memfd disabled\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if kernel support exists, otherwise fall back to ashmem */
|
||||
android::base::unique_fd fd(
|
||||
syscall(__NR_memfd_create, "test_android_memfd", MFD_ALLOW_SEALING));
|
||||
if (fd == -1) {
|
||||
ALOGE("memfd_create failed: %s, no memfd support.\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fcntl(fd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE) == -1) {
|
||||
ALOGE("fcntl(F_ADD_SEALS) failed: %s, no memfd support.\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (debug_log) {
|
||||
ALOGD("memfd: device has memfd support, using it\n");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool has_memfd_support() {
|
||||
/* memfd_supported is the initial global per-process state of what is known
|
||||
* about memfd.
|
||||
*/
|
||||
static bool memfd_supported = __has_memfd_support();
|
||||
|
||||
return memfd_supported;
|
||||
}
|
||||
|
||||
static std::string get_ashmem_device_path() {
|
||||
static const std::string boot_id_path = "/proc/sys/kernel/random/boot_id";
|
||||
std::string boot_id;
|
||||
if (!android::base::ReadFileToString(boot_id_path, &boot_id)) {
|
||||
ALOGE("Failed to read %s: %s.\n", boot_id_path.c_str(), strerror(errno));
|
||||
return "";
|
||||
};
|
||||
boot_id = android::base::Trim(boot_id);
|
||||
|
||||
return "/dev/ashmem" + boot_id;
|
||||
}
|
||||
|
||||
/* logistics of getting file descriptor for ashmem */
|
||||
static int __ashmem_open_locked()
|
||||
{
|
||||
static const std::string ashmem_device_path = get_ashmem_device_path();
|
||||
|
||||
if (ashmem_device_path.empty()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fd = TEMP_FAILURE_RETRY(open(ashmem_device_path.c_str(), O_RDWR | O_CLOEXEC));
|
||||
|
||||
// fallback for APEX w/ use_vendor on Q, which would have still used /dev/ashmem
|
||||
if (fd < 0) {
|
||||
fd = TEMP_FAILURE_RETRY(open("/dev/ashmem", O_RDWR | O_CLOEXEC));
|
||||
}
|
||||
|
||||
if (fd < 0) {
|
||||
return fd;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
int ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
|
||||
if (ret < 0) {
|
||||
int save_errno = errno;
|
||||
close(fd);
|
||||
errno = save_errno;
|
||||
return ret;
|
||||
}
|
||||
if (!S_ISCHR(st.st_mode) || !st.st_rdev) {
|
||||
close(fd);
|
||||
errno = ENOTTY;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__ashmem_rdev = st.st_rdev;
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int __ashmem_open()
|
||||
{
|
||||
int fd;
|
||||
|
||||
pthread_mutex_lock(&__ashmem_lock);
|
||||
fd = __ashmem_open_locked();
|
||||
pthread_mutex_unlock(&__ashmem_lock);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* Make sure file descriptor references ashmem, negative number means false */
|
||||
static int __ashmem_is_ashmem(int fd, int fatal)
|
||||
{
|
||||
dev_t rdev;
|
||||
struct stat st;
|
||||
|
||||
if (fstat(fd, &st) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rdev = 0; /* Too much complexity to sniff __ashmem_rdev */
|
||||
if (S_ISCHR(st.st_mode) && st.st_rdev) {
|
||||
pthread_mutex_lock(&__ashmem_lock);
|
||||
rdev = __ashmem_rdev;
|
||||
if (rdev) {
|
||||
pthread_mutex_unlock(&__ashmem_lock);
|
||||
} else {
|
||||
int fd = __ashmem_open_locked();
|
||||
if (fd < 0) {
|
||||
pthread_mutex_unlock(&__ashmem_lock);
|
||||
return -1;
|
||||
}
|
||||
rdev = __ashmem_rdev;
|
||||
pthread_mutex_unlock(&__ashmem_lock);
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
if (st.st_rdev == rdev) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (fatal) {
|
||||
if (rdev) {
|
||||
LOG_ALWAYS_FATAL("illegal fd=%d mode=0%o rdev=%d:%d expected 0%o %d:%d",
|
||||
fd, st.st_mode, major(st.st_rdev), minor(st.st_rdev),
|
||||
S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IRGRP,
|
||||
major(rdev), minor(rdev));
|
||||
} else {
|
||||
LOG_ALWAYS_FATAL("illegal fd=%d mode=0%o rdev=%d:%d expected 0%o",
|
||||
fd, st.st_mode, major(st.st_rdev), minor(st.st_rdev),
|
||||
S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IRGRP);
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
errno = ENOTTY;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int __ashmem_check_failure(int fd, int result)
|
||||
{
|
||||
if (result == -1 && errno == ENOTTY) __ashmem_is_ashmem(fd, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool memfd_is_ashmem(int fd) {
|
||||
static bool fd_check_error_once = false;
|
||||
|
||||
if (__ashmem_is_ashmem(fd, 0) == 0) {
|
||||
if (!fd_check_error_once) {
|
||||
ALOGE("memfd: memfd expected but ashmem fd used - please use libcutils.\n");
|
||||
fd_check_error_once = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int ashmem_valid(int fd)
|
||||
{
|
||||
if (has_memfd_support() && !memfd_is_ashmem(fd)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return __ashmem_is_ashmem(fd, 0) >= 0;
|
||||
}
|
||||
|
||||
static int memfd_create_region(const char* name, size_t size) {
|
||||
android::base::unique_fd fd(syscall(__NR_memfd_create, name, MFD_ALLOW_SEALING));
|
||||
|
||||
if (fd == -1) {
|
||||
ALOGE("memfd_create(%s, %zd) failed: %s\n", name, size, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ftruncate(fd, size) == -1) {
|
||||
ALOGE("ftruncate(%s, %zd) failed for memfd creation: %s\n", name, size, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (debug_log) {
|
||||
ALOGE("memfd_create(%s, %zd) success. fd=%d\n", name, size, fd.get());
|
||||
}
|
||||
return fd.release();
|
||||
}
|
||||
|
||||
/*
|
||||
* ashmem_create_region - creates a new ashmem region and returns the file
|
||||
* descriptor, or <0 on error
|
||||
*
|
||||
* `name' is an optional label to give the region (visible in /proc/pid/maps)
|
||||
* `size' is the size of the region, in page-aligned bytes
|
||||
*/
|
||||
int ashmem_create_region(const char *name, size_t size)
|
||||
{
|
||||
int ret, save_errno;
|
||||
|
||||
if (has_memfd_support()) {
|
||||
return memfd_create_region(name ? name : "none", size);
|
||||
}
|
||||
|
||||
int fd = __ashmem_open();
|
||||
if (fd < 0) {
|
||||
return fd;
|
||||
}
|
||||
|
||||
if (name) {
|
||||
char buf[ASHMEM_NAME_LEN] = {0};
|
||||
|
||||
strlcpy(buf, name, sizeof(buf));
|
||||
ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_NAME, buf));
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_SIZE, size));
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
return fd;
|
||||
|
||||
error:
|
||||
save_errno = errno;
|
||||
close(fd);
|
||||
errno = save_errno;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int memfd_set_prot_region(int fd, int prot) {
|
||||
/* Only proceed if an fd needs to be write-protected */
|
||||
if (prot & PROT_WRITE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fcntl(fd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE) == -1) {
|
||||
ALOGE("memfd_set_prot_region(%d, %d): F_SEAL_FUTURE_WRITE seal failed: %s\n", fd, prot,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ashmem_set_prot_region(int fd, int prot)
|
||||
{
|
||||
if (has_memfd_support() && !memfd_is_ashmem(fd)) {
|
||||
return memfd_set_prot_region(fd, prot);
|
||||
}
|
||||
|
||||
return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_PROT_MASK, prot)));
|
||||
}
|
||||
|
||||
int ashmem_pin_region(int fd, size_t offset, size_t len)
|
||||
{
|
||||
if (!pin_deprecation_warn || debug_log) {
|
||||
ALOGE("Pinning is deprecated since Android Q. Please use trim or other methods.\n");
|
||||
pin_deprecation_warn = true;
|
||||
}
|
||||
|
||||
if (has_memfd_support() && !memfd_is_ashmem(fd)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: should LP64 reject too-large offset/len?
|
||||
ashmem_pin pin = { static_cast<uint32_t>(offset), static_cast<uint32_t>(len) };
|
||||
return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_PIN, &pin)));
|
||||
}
|
||||
|
||||
int ashmem_unpin_region(int fd, size_t offset, size_t len)
|
||||
{
|
||||
if (!pin_deprecation_warn || debug_log) {
|
||||
ALOGE("Pinning is deprecated since Android Q. Please use trim or other methods.\n");
|
||||
pin_deprecation_warn = true;
|
||||
}
|
||||
|
||||
if (has_memfd_support() && !memfd_is_ashmem(fd)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: should LP64 reject too-large offset/len?
|
||||
ashmem_pin pin = { static_cast<uint32_t>(offset), static_cast<uint32_t>(len) };
|
||||
return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_UNPIN, &pin)));
|
||||
}
|
||||
|
||||
int ashmem_get_size_region(int fd)
|
||||
{
|
||||
if (has_memfd_support() && !memfd_is_ashmem(fd)) {
|
||||
struct stat sb;
|
||||
|
||||
if (fstat(fd, &sb) == -1) {
|
||||
ALOGE("ashmem_get_size_region(%d): fstat failed: %s\n", fd, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (debug_log) {
|
||||
ALOGD("ashmem_get_size_region(%d): %d\n", fd, static_cast<int>(sb.st_size));
|
||||
}
|
||||
|
||||
return sb.st_size;
|
||||
}
|
||||
|
||||
return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL)));
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <cutils/ashmem.h>
|
||||
|
||||
/*
|
||||
* Implementation of the user-space ashmem API for the simulator, which lacks
|
||||
* an ashmem-enabled kernel. See ashmem-dev.c for the real ashmem-based version.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <utils/Compat.h>
|
||||
|
||||
static bool ashmem_validate_stat(int fd, struct stat* buf) {
|
||||
int result = fstat(fd, buf);
|
||||
if (result == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if this is an "ashmem" region.
|
||||
* TODO: This is very hacky, and can easily break.
|
||||
* We need some reliable indicator.
|
||||
*/
|
||||
if (!(buf->st_nlink == 0 && S_ISREG(buf->st_mode))) {
|
||||
errno = ENOTTY;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int ashmem_valid(int fd) {
|
||||
struct stat buf;
|
||||
return ashmem_validate_stat(fd, &buf);
|
||||
}
|
||||
|
||||
int ashmem_create_region(const char* /*ignored*/, size_t size) {
|
||||
char pattern[PATH_MAX];
|
||||
snprintf(pattern, sizeof(pattern), "/tmp/android-ashmem-%d-XXXXXXXXX", getpid());
|
||||
int fd = mkstemp(pattern);
|
||||
if (fd == -1) return -1;
|
||||
|
||||
unlink(pattern);
|
||||
|
||||
if (TEMP_FAILURE_RETRY(ftruncate(fd, size)) == -1) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int ashmem_set_prot_region(int /*fd*/, int /*prot*/) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ashmem_pin_region(int /*fd*/, size_t /*offset*/, size_t /*len*/) {
|
||||
return 0 /*ASHMEM_NOT_PURGED*/;
|
||||
}
|
||||
|
||||
int ashmem_unpin_region(int /*fd*/, size_t /*offset*/, size_t /*len*/) {
|
||||
return 0 /*ASHMEM_IS_UNPINNED*/;
|
||||
}
|
||||
|
||||
int ashmem_get_size_region(int fd)
|
||||
{
|
||||
struct stat buf;
|
||||
if (!ashmem_validate_stat(fd, &buf)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return buf.st_size;
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <private/android_filesystem_config.h>
|
||||
#include <private/canned_fs_config.h>
|
||||
#include <private/fs_config.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
const char* path;
|
||||
unsigned uid;
|
||||
unsigned gid;
|
||||
unsigned mode;
|
||||
uint64_t capabilities;
|
||||
} Path;
|
||||
|
||||
static Path* canned_data = NULL;
|
||||
static int canned_alloc = 0;
|
||||
static int canned_used = 0;
|
||||
|
||||
static int path_compare(const void* a, const void* b) {
|
||||
return strcmp(((Path*)a)->path, ((Path*)b)->path);
|
||||
}
|
||||
|
||||
int load_canned_fs_config(const char* fn) {
|
||||
char buf[PATH_MAX + 200];
|
||||
FILE* f;
|
||||
|
||||
f = fopen(fn, "r");
|
||||
if (f == NULL) {
|
||||
fprintf(stderr, "failed to open %s: %s\n", fn, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (fgets(buf, sizeof(buf), f)) {
|
||||
Path* p;
|
||||
char* token;
|
||||
char* line = buf;
|
||||
bool rootdir;
|
||||
|
||||
while (canned_used >= canned_alloc) {
|
||||
canned_alloc = (canned_alloc+1) * 2;
|
||||
canned_data = (Path*) realloc(canned_data, canned_alloc * sizeof(Path));
|
||||
}
|
||||
p = canned_data + canned_used;
|
||||
if (line[0] == '/') line++;
|
||||
rootdir = line[0] == ' ';
|
||||
p->path = strdup(rootdir ? "" : strtok(line, " "));
|
||||
p->uid = atoi(strtok(rootdir ? line : NULL, " "));
|
||||
p->gid = atoi(strtok(NULL, " "));
|
||||
p->mode = strtol(strtok(NULL, " "), NULL, 8); // mode is in octal
|
||||
p->capabilities = 0;
|
||||
|
||||
do {
|
||||
token = strtok(NULL, " ");
|
||||
if (token && strncmp(token, "capabilities=", 13) == 0) {
|
||||
p->capabilities = strtoll(token+13, NULL, 0);
|
||||
break;
|
||||
}
|
||||
} while (token);
|
||||
|
||||
canned_used++;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
qsort(canned_data, canned_used, sizeof(Path), path_compare);
|
||||
printf("loaded %d fs_config entries\n", canned_used);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const int kDebugCannedFsConfig = 0;
|
||||
|
||||
void canned_fs_config(const char* path, int dir, const char* target_out_path,
|
||||
unsigned* uid, unsigned* gid, unsigned* mode, uint64_t* capabilities) {
|
||||
Path key, *p;
|
||||
|
||||
key.path = path;
|
||||
if (path[0] == '/') key.path++; // canned paths lack the leading '/'
|
||||
p = (Path*) bsearch(&key, canned_data, canned_used, sizeof(Path), path_compare);
|
||||
if (p == NULL) {
|
||||
fprintf(stderr, "failed to find [%s] in canned fs_config\n", path);
|
||||
exit(1);
|
||||
}
|
||||
*uid = p->uid;
|
||||
*gid = p->gid;
|
||||
*mode = p->mode;
|
||||
*capabilities = p->capabilities;
|
||||
|
||||
if (kDebugCannedFsConfig) {
|
||||
// for debugging, run the built-in fs_config and compare the results.
|
||||
|
||||
unsigned c_uid, c_gid, c_mode;
|
||||
uint64_t c_capabilities;
|
||||
|
||||
fs_config(path, dir, target_out_path, &c_uid, &c_gid, &c_mode, &c_capabilities);
|
||||
|
||||
if (c_uid != *uid) printf("%s uid %d %d\n", path, *uid, c_uid);
|
||||
if (c_gid != *gid) printf("%s gid %d %d\n", path, *gid, c_gid);
|
||||
if (c_mode != *mode) printf("%s mode 0%o 0%o\n", path, *mode, c_mode);
|
||||
if (c_capabilities != *capabilities) {
|
||||
printf("%s capabilities %" PRIx64 " %" PRIx64 "\n",
|
||||
path,
|
||||
*capabilities,
|
||||
c_capabilities);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,328 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <cutils/config_utils.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cutils/misc.h>
|
||||
|
||||
cnode* config_node(const char *name, const char *value)
|
||||
{
|
||||
cnode* node = static_cast<cnode*>(calloc(sizeof(cnode), 1));
|
||||
if(node) {
|
||||
node->name = name ? name : "";
|
||||
node->value = value ? value : "";
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
cnode* config_find(cnode *root, const char *name)
|
||||
{
|
||||
cnode *node, *match = NULL;
|
||||
|
||||
/* we walk the whole list, as we need to return the last (newest) entry */
|
||||
for(node = root->first_child; node; node = node->next)
|
||||
if(!strcmp(node->name, name))
|
||||
match = node;
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
static cnode* _config_create(cnode *root, const char *name)
|
||||
{
|
||||
cnode *node;
|
||||
|
||||
node = config_node(name, NULL);
|
||||
|
||||
if(root->last_child)
|
||||
root->last_child->next = node;
|
||||
else
|
||||
root->first_child = node;
|
||||
|
||||
root->last_child = node;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
int config_bool(cnode *root, const char *name, int _default)
|
||||
{
|
||||
cnode *node;
|
||||
|
||||
node = config_find(root, name);
|
||||
if(!node)
|
||||
return _default;
|
||||
|
||||
switch(node->value[0]) {
|
||||
case 'y':
|
||||
case 'Y':
|
||||
case '1':
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const char* config_str(cnode *root, const char *name, const char *_default)
|
||||
{
|
||||
cnode *node;
|
||||
|
||||
node = config_find(root, name);
|
||||
if(!node)
|
||||
return _default;
|
||||
return node->value;
|
||||
}
|
||||
|
||||
void config_set(cnode *root, const char *name, const char *value)
|
||||
{
|
||||
cnode *node;
|
||||
|
||||
node = config_find(root, name);
|
||||
if(node)
|
||||
node->value = value;
|
||||
else {
|
||||
node = _config_create(root, name);
|
||||
node->value = value;
|
||||
}
|
||||
}
|
||||
|
||||
#define T_EOF 0
|
||||
#define T_TEXT 1
|
||||
#define T_DOT 2
|
||||
#define T_OBRACE 3
|
||||
#define T_CBRACE 4
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *data;
|
||||
char *text;
|
||||
int len;
|
||||
char next;
|
||||
} cstate;
|
||||
|
||||
static int _lex(cstate *cs, int value)
|
||||
{
|
||||
char c;
|
||||
char *s;
|
||||
char *data;
|
||||
|
||||
data = cs->data;
|
||||
|
||||
if(cs->next != 0) {
|
||||
c = cs->next;
|
||||
cs->next = 0;
|
||||
goto got_c;
|
||||
}
|
||||
|
||||
restart:
|
||||
for(;;) {
|
||||
c = *data++;
|
||||
got_c:
|
||||
if(isspace(c))
|
||||
continue;
|
||||
|
||||
switch(c) {
|
||||
case 0:
|
||||
return T_EOF;
|
||||
|
||||
case '#':
|
||||
for(;;) {
|
||||
switch(*data) {
|
||||
case 0:
|
||||
cs->data = data;
|
||||
return T_EOF;
|
||||
case '\n':
|
||||
cs->data = data + 1;
|
||||
goto restart;
|
||||
default:
|
||||
data++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case '.':
|
||||
cs->data = data;
|
||||
return T_DOT;
|
||||
|
||||
case '{':
|
||||
cs->data = data;
|
||||
return T_OBRACE;
|
||||
|
||||
case '}':
|
||||
cs->data = data;
|
||||
return T_CBRACE;
|
||||
|
||||
default:
|
||||
s = data - 1;
|
||||
|
||||
if(value) {
|
||||
for(;;) {
|
||||
if(*data == 0) {
|
||||
cs->data = data;
|
||||
break;
|
||||
}
|
||||
if(*data == '\n') {
|
||||
cs->data = data + 1;
|
||||
*data-- = 0;
|
||||
break;
|
||||
}
|
||||
data++;
|
||||
}
|
||||
|
||||
/* strip trailing whitespace */
|
||||
while(data > s){
|
||||
if(!isspace(*data)) break;
|
||||
*data-- = 0;
|
||||
}
|
||||
|
||||
goto got_text;
|
||||
} else {
|
||||
for(;;) {
|
||||
if(isspace(*data)) {
|
||||
*data = 0;
|
||||
cs->data = data + 1;
|
||||
goto got_text;
|
||||
}
|
||||
switch(*data) {
|
||||
case 0:
|
||||
cs->data = data;
|
||||
goto got_text;
|
||||
case '.':
|
||||
case '{':
|
||||
case '}':
|
||||
cs->next = *data;
|
||||
*data = 0;
|
||||
cs->data = data + 1;
|
||||
goto got_text;
|
||||
default:
|
||||
data++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
got_text:
|
||||
cs->text = s;
|
||||
return T_TEXT;
|
||||
}
|
||||
|
||||
#if 0
|
||||
char *TOKENNAMES[] = { "EOF", "TEXT", "DOT", "OBRACE", "CBRACE" };
|
||||
|
||||
static int lex(cstate *cs, int value)
|
||||
{
|
||||
int tok = _lex(cs, value);
|
||||
printf("TOKEN(%d) %s %s\n", value, TOKENNAMES[tok],
|
||||
tok == T_TEXT ? cs->text : "");
|
||||
return tok;
|
||||
}
|
||||
#else
|
||||
#define lex(cs,v) _lex(cs,v)
|
||||
#endif
|
||||
|
||||
static int parse_expr(cstate *cs, cnode *node);
|
||||
|
||||
static int parse_block(cstate *cs, cnode *node)
|
||||
{
|
||||
for(;;){
|
||||
switch(lex(cs, 0)){
|
||||
case T_TEXT:
|
||||
if(parse_expr(cs, node)) return -1;
|
||||
continue;
|
||||
|
||||
case T_CBRACE:
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_expr(cstate *cs, cnode *root)
|
||||
{
|
||||
cnode *node;
|
||||
|
||||
/* last token was T_TEXT */
|
||||
node = config_find(root, cs->text);
|
||||
if(!node || *node->value)
|
||||
node = _config_create(root, cs->text);
|
||||
|
||||
for(;;) {
|
||||
switch(lex(cs, 1)) {
|
||||
case T_DOT:
|
||||
if(lex(cs, 0) != T_TEXT)
|
||||
return -1;
|
||||
node = _config_create(node, cs->text);
|
||||
continue;
|
||||
|
||||
case T_TEXT:
|
||||
node->value = cs->text;
|
||||
return 0;
|
||||
|
||||
case T_OBRACE:
|
||||
return parse_block(cs, node);
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void config_load(cnode *root, char *data)
|
||||
{
|
||||
if(data != 0) {
|
||||
cstate cs;
|
||||
cs.data = data;
|
||||
cs.next = 0;
|
||||
|
||||
for(;;) {
|
||||
switch(lex(&cs, 0)) {
|
||||
case T_TEXT:
|
||||
if(parse_expr(&cs, root))
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void config_load_file(cnode *root, const char *fn)
|
||||
{
|
||||
char* data = static_cast<char*>(load_file(fn, nullptr));
|
||||
config_load(root, data);
|
||||
// TODO: deliberate leak :-/
|
||||
}
|
||||
|
||||
void config_free(cnode *root)
|
||||
{
|
||||
cnode *cur = root->first_child;
|
||||
|
||||
while (cur) {
|
||||
cnode *prev = cur;
|
||||
config_free(cur);
|
||||
cur = cur->next;
|
||||
free(prev);
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_CUTILS_COMPILER_H
|
||||
#define ANDROID_CUTILS_COMPILER_H
|
||||
|
||||
/*
|
||||
* helps the compiler's optimizer predicting branches
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
# define CC_LIKELY( exp ) (__builtin_expect( !!(exp), true ))
|
||||
# define CC_UNLIKELY( exp ) (__builtin_expect( !!(exp), false ))
|
||||
#else
|
||||
# define CC_LIKELY( exp ) (__builtin_expect( !!(exp), 1 ))
|
||||
# define CC_UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 ))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* exports marked symbols
|
||||
*
|
||||
* if used on a C++ class declaration, this macro must be inserted
|
||||
* after the "class" keyword. For instance:
|
||||
*
|
||||
* template <typename TYPE>
|
||||
* class ANDROID_API Singleton { }
|
||||
*/
|
||||
|
||||
#define ANDROID_API __attribute__((visibility("default")))
|
||||
|
||||
#endif // ANDROID_CUTILS_COMPILER_H
|
@ -0,0 +1,65 @@
|
||||
// DO NOT INCLUDE ANYTHING NEW IN THIS FILE.
|
||||
|
||||
// <log/log.h> has replaced this file and all changes should go there instead.
|
||||
// This path remains strictly to include that header as there are thousands of
|
||||
// references to <utils/Log.h> in the tree.
|
||||
|
||||
// #include <log/log.h>
|
||||
#include <jni.h>
|
||||
#include <android/log.h>
|
||||
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define LOG_NDEBUG 1
|
||||
#else
|
||||
#define LOG_NDEBUG 0
|
||||
#endif
|
||||
|
||||
#define LOG_TAG "MPLOG"
|
||||
#define ALOGV(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG,__VA_ARGS__)
|
||||
#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG,__VA_ARGS__)
|
||||
#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
|
||||
#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
|
||||
#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,__VA_ARGS__)
|
||||
|
||||
|
||||
#define android_printAssert(cond, tag, ...) \
|
||||
__android_log_assert(cond, tag, \
|
||||
__android_second(0, ##__VA_ARGS__, NULL) \
|
||||
__android_rest(__VA_ARGS__))
|
||||
|
||||
|
||||
#define __FAKE_USE_VA_ARGS(...) ((void)(0))
|
||||
|
||||
#ifndef LOG_ALWAYS_FATAL_IF
|
||||
#define LOG_ALWAYS_FATAL_IF(cond, ...) \
|
||||
((__predict_false(cond)) ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), \
|
||||
((void)android_printAssert(#cond, LOG_TAG, ##__VA_ARGS__))) \
|
||||
: ((void)0))
|
||||
#endif
|
||||
|
||||
#ifndef LOG_ALWAYS_FATAL
|
||||
#define LOG_ALWAYS_FATAL(...) \
|
||||
(((void)android_printAssert(NULL, LOG_TAG, ##__VA_ARGS__)))
|
||||
#endif
|
||||
|
||||
#if NDEBUG
|
||||
|
||||
#ifndef LOG_FATAL_IF
|
||||
#define LOG_FATAL_IF(cond, ...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
|
||||
#endif
|
||||
#ifndef LOG_FATAL
|
||||
#define LOG_FATAL(...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#ifndef LOG_FATAL_IF
|
||||
#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ##__VA_ARGS__)
|
||||
#endif
|
||||
#ifndef LOG_FATAL
|
||||
#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright (C) 2005 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "sharedbuffer"
|
||||
|
||||
#include "SharedBuffer.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <log/log.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
namespace android {
|
||||
|
||||
SharedBuffer* SharedBuffer::alloc(size_t size)
|
||||
{
|
||||
// Don't overflow if the combined size of the buffer / header is larger than
|
||||
// size_max.
|
||||
#if 0
|
||||
LOG_ALWAYS_FATAL_IF((size >= (SIZE_MAX - sizeof(SharedBuffer))),
|
||||
"Invalid buffer size %zu", size);
|
||||
#endif
|
||||
|
||||
SharedBuffer* sb = static_cast<SharedBuffer *>(malloc(sizeof(SharedBuffer) + size));
|
||||
if (sb) {
|
||||
// Should be std::atomic_init(&sb->mRefs, 1);
|
||||
// But that generates a warning with some compilers.
|
||||
// The following is OK on Android-supported platforms.
|
||||
sb->mRefs.store(1, std::memory_order_relaxed);
|
||||
sb->mSize = size;
|
||||
sb->mClientMetadata = 0;
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
|
||||
|
||||
void SharedBuffer::dealloc(const SharedBuffer* released)
|
||||
{
|
||||
free(const_cast<SharedBuffer*>(released));
|
||||
}
|
||||
|
||||
SharedBuffer* SharedBuffer::edit() const
|
||||
{
|
||||
if (onlyOwner()) {
|
||||
return const_cast<SharedBuffer*>(this);
|
||||
}
|
||||
SharedBuffer* sb = alloc(mSize);
|
||||
if (sb) {
|
||||
memcpy(sb->data(), data(), size());
|
||||
release();
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
|
||||
SharedBuffer* SharedBuffer::editResize(size_t newSize) const
|
||||
{
|
||||
if (onlyOwner()) {
|
||||
SharedBuffer* buf = const_cast<SharedBuffer*>(this);
|
||||
if (buf->mSize == newSize) return buf;
|
||||
// Don't overflow if the combined size of the new buffer / header is larger than
|
||||
// size_max.
|
||||
#if 0
|
||||
LOG_ALWAYS_FATAL_IF((newSize >= (SIZE_MAX - sizeof(SharedBuffer))),
|
||||
"Invalid buffer size %zu", newSize);
|
||||
#endif
|
||||
|
||||
buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize);
|
||||
if (buf != nullptr) {
|
||||
buf->mSize = newSize;
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
SharedBuffer* sb = alloc(newSize);
|
||||
if (sb) {
|
||||
const size_t mySize = mSize;
|
||||
memcpy(sb->data(), data(), newSize < mySize ? newSize : mySize);
|
||||
release();
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
|
||||
SharedBuffer* SharedBuffer::attemptEdit() const
|
||||
{
|
||||
if (onlyOwner()) {
|
||||
return const_cast<SharedBuffer*>(this);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SharedBuffer* SharedBuffer::reset(size_t new_size) const
|
||||
{
|
||||
// cheap-o-reset.
|
||||
SharedBuffer* sb = alloc(new_size);
|
||||
if (sb) {
|
||||
release();
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
|
||||
void SharedBuffer::acquire() const {
|
||||
mRefs.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
int32_t SharedBuffer::release(uint32_t flags) const
|
||||
{
|
||||
const bool useDealloc = ((flags & eKeepStorage) == 0);
|
||||
if (onlyOwner()) {
|
||||
// Since we're the only owner, our reference count goes to zero.
|
||||
mRefs.store(0, std::memory_order_relaxed);
|
||||
if (useDealloc) {
|
||||
dealloc(this);
|
||||
}
|
||||
// As the only owner, our previous reference count was 1.
|
||||
return 1;
|
||||
}
|
||||
// There's multiple owners, we need to use an atomic decrement.
|
||||
int32_t prevRefCount = mRefs.fetch_sub(1, std::memory_order_release);
|
||||
if (prevRefCount == 1) {
|
||||
// We're the last reference, we need the acquire fence.
|
||||
atomic_thread_fence(std::memory_order_acquire);
|
||||
if (useDealloc) {
|
||||
dealloc(this);
|
||||
}
|
||||
}
|
||||
return prevRefCount;
|
||||
}
|
||||
|
||||
|
||||
}; // namespace android
|
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2005 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* DEPRECATED. DO NOT USE FOR NEW CODE.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_SHARED_BUFFER_H
|
||||
#define ANDROID_SHARED_BUFFER_H
|
||||
|
||||
#include <atomic>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
namespace android {
|
||||
|
||||
class SharedBuffer
|
||||
{
|
||||
public:
|
||||
|
||||
/* flags to use with release() */
|
||||
enum {
|
||||
eKeepStorage = 0x00000001
|
||||
};
|
||||
|
||||
/*! allocate a buffer of size 'size' and acquire() it.
|
||||
* call release() to free it.
|
||||
*/
|
||||
static SharedBuffer* alloc(size_t size);
|
||||
|
||||
/*! free the memory associated with the SharedBuffer.
|
||||
* Fails if there are any users associated with this SharedBuffer.
|
||||
* In other words, the buffer must have been release by all its
|
||||
* users.
|
||||
*/
|
||||
static void dealloc(const SharedBuffer* released);
|
||||
|
||||
//! access the data for read
|
||||
inline const void* data() const;
|
||||
|
||||
//! access the data for read/write
|
||||
inline void* data();
|
||||
|
||||
//! get size of the buffer
|
||||
inline size_t size() const;
|
||||
|
||||
//! get back a SharedBuffer object from its data
|
||||
static inline SharedBuffer* bufferFromData(void* data);
|
||||
|
||||
//! get back a SharedBuffer object from its data
|
||||
static inline const SharedBuffer* bufferFromData(const void* data);
|
||||
|
||||
//! get the size of a SharedBuffer object from its data
|
||||
static inline size_t sizeFromData(const void* data);
|
||||
|
||||
//! edit the buffer (get a writtable, or non-const, version of it)
|
||||
SharedBuffer* edit() const;
|
||||
|
||||
//! edit the buffer, resizing if needed
|
||||
SharedBuffer* editResize(size_t size) const;
|
||||
|
||||
//! like edit() but fails if a copy is required
|
||||
SharedBuffer* attemptEdit() const;
|
||||
|
||||
//! resize and edit the buffer, loose it's content.
|
||||
SharedBuffer* reset(size_t size) const;
|
||||
|
||||
//! acquire/release a reference on this buffer
|
||||
void acquire() const;
|
||||
|
||||
/*! release a reference on this buffer, with the option of not
|
||||
* freeing the memory associated with it if it was the last reference
|
||||
* returns the previous reference count
|
||||
*/
|
||||
int32_t release(uint32_t flags = 0) const;
|
||||
|
||||
//! returns wether or not we're the only owner
|
||||
inline bool onlyOwner() const;
|
||||
|
||||
|
||||
private:
|
||||
inline SharedBuffer() { }
|
||||
inline ~SharedBuffer() { }
|
||||
SharedBuffer(const SharedBuffer&);
|
||||
SharedBuffer& operator = (const SharedBuffer&);
|
||||
|
||||
// Must be sized to preserve correct alignment.
|
||||
mutable std::atomic<int32_t> mRefs;
|
||||
size_t mSize;
|
||||
uint32_t mReserved;
|
||||
public:
|
||||
// mClientMetadata is reserved for client use. It is initialized to 0
|
||||
// and the clients can do whatever they want with it. Note that this is
|
||||
// placed last so that it is adjcent to the buffer allocated.
|
||||
uint32_t mClientMetadata;
|
||||
};
|
||||
|
||||
static_assert(sizeof(SharedBuffer) % 8 == 0
|
||||
&& (sizeof(size_t) > 4 || sizeof(SharedBuffer) == 16),
|
||||
"SharedBuffer has unexpected size");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const void* SharedBuffer::data() const {
|
||||
return this + 1;
|
||||
}
|
||||
|
||||
void* SharedBuffer::data() {
|
||||
return this + 1;
|
||||
}
|
||||
|
||||
size_t SharedBuffer::size() const {
|
||||
return mSize;
|
||||
}
|
||||
|
||||
SharedBuffer* SharedBuffer::bufferFromData(void* data) {
|
||||
return data ? static_cast<SharedBuffer *>(data)-1 : nullptr;
|
||||
}
|
||||
|
||||
const SharedBuffer* SharedBuffer::bufferFromData(const void* data) {
|
||||
return data ? static_cast<const SharedBuffer *>(data)-1 : nullptr;
|
||||
}
|
||||
|
||||
size_t SharedBuffer::sizeFromData(const void* data) {
|
||||
return data ? bufferFromData(data)->mSize : 0;
|
||||
}
|
||||
|
||||
bool SharedBuffer::onlyOwner() const {
|
||||
return (mRefs.load(std::memory_order_acquire) == 1);
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#endif // ANDROID_VECTOR_H
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "sp"
|
||||
|
||||
#include <log/log.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
void sp_report_race()
|
||||
{
|
||||
#if 0
|
||||
LOG_ALWAYS_FATAL("sp<> assignment detected data race");
|
||||
#endif
|
||||
}
|
||||
|
||||
void sp_report_stack_pointer()
|
||||
{
|
||||
#if 0
|
||||
LOG_ALWAYS_FATAL("sp<> constructed with stack pointer argument");
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <string>
|
||||
|
||||
namespace android {
|
||||
|
||||
/**
|
||||
* The type used to return success/failure from frameworks APIs.
|
||||
* See the anonymous enum below for valid values.
|
||||
*/
|
||||
typedef int32_t status_t;
|
||||
|
||||
/*
|
||||
* Error codes.
|
||||
* All error codes are negative values.
|
||||
*/
|
||||
|
||||
// Win32 #defines NO_ERROR as well. It has the same value, so there's no
|
||||
// real conflict, though it's a bit awkward.
|
||||
#ifdef _WIN32
|
||||
# undef NO_ERROR
|
||||
#endif
|
||||
|
||||
enum {
|
||||
OK = 0, // Preferred constant for checking success.
|
||||
NO_ERROR = OK, // Deprecated synonym for `OK`. Prefer `OK` because it doesn't conflict with Windows.
|
||||
|
||||
UNKNOWN_ERROR = (-2147483647-1), // INT32_MIN value
|
||||
|
||||
NO_MEMORY = -ENOMEM,
|
||||
INVALID_OPERATION = -ENOSYS,
|
||||
BAD_VALUE = -EINVAL,
|
||||
BAD_TYPE = (UNKNOWN_ERROR + 1),
|
||||
NAME_NOT_FOUND = -ENOENT,
|
||||
PERMISSION_DENIED = -EPERM,
|
||||
NO_INIT = -ENODEV,
|
||||
ALREADY_EXISTS = -EEXIST,
|
||||
DEAD_OBJECT = -EPIPE,
|
||||
FAILED_TRANSACTION = (UNKNOWN_ERROR + 2),
|
||||
#if !defined(_WIN32)
|
||||
BAD_INDEX = -EOVERFLOW,
|
||||
NOT_ENOUGH_DATA = -ENODATA,
|
||||
WOULD_BLOCK = -EWOULDBLOCK,
|
||||
TIMED_OUT = -ETIMEDOUT,
|
||||
UNKNOWN_TRANSACTION = -EBADMSG,
|
||||
#else
|
||||
BAD_INDEX = -E2BIG,
|
||||
NOT_ENOUGH_DATA = (UNKNOWN_ERROR + 3),
|
||||
WOULD_BLOCK = (UNKNOWN_ERROR + 4),
|
||||
TIMED_OUT = (UNKNOWN_ERROR + 5),
|
||||
UNKNOWN_TRANSACTION = (UNKNOWN_ERROR + 6),
|
||||
#endif
|
||||
FDS_NOT_ALLOWED = (UNKNOWN_ERROR + 7),
|
||||
UNEXPECTED_NULL = (UNKNOWN_ERROR + 8),
|
||||
};
|
||||
|
||||
// Human readable name of error
|
||||
std::string statusToString(status_t status);
|
||||
|
||||
// Restore define; enumeration is in "android" namespace, so the value defined
|
||||
// there won't work for Win32 code in a different namespace.
|
||||
#ifdef _WIN32
|
||||
# define NO_ERROR 0L
|
||||
#endif
|
||||
|
||||
} // namespace android
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* See documentation in RefBase.h
|
||||
*/
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class ReferenceRenamer;
|
||||
|
||||
template <class T>
|
||||
class LightRefBase
|
||||
{
|
||||
public:
|
||||
inline LightRefBase() : mCount(0) { }
|
||||
inline void incStrong(__attribute__((unused)) const void* id) const {
|
||||
mCount.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
inline void decStrong(__attribute__((unused)) const void* id) const {
|
||||
if (mCount.fetch_sub(1, std::memory_order_release) == 1) {
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
delete static_cast<const T*>(this);
|
||||
}
|
||||
}
|
||||
//! DEBUGGING ONLY: Get current strong ref count.
|
||||
inline int32_t getStrongCount() const {
|
||||
return mCount.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
protected:
|
||||
inline ~LightRefBase() { }
|
||||
|
||||
private:
|
||||
friend class ReferenceMover;
|
||||
inline static void renameRefs(size_t /*n*/, const ReferenceRenamer& /*renamer*/) { }
|
||||
inline static void renameRefId(T* /*ref*/, const void* /*old_id*/ , const void* /*new_id*/) { }
|
||||
|
||||
private:
|
||||
mutable std::atomic<int32_t> mCount;
|
||||
};
|
||||
|
||||
|
||||
// This is a wrapper around LightRefBase that simply enforces a virtual
|
||||
// destructor to eliminate the template requirement of LightRefBase
|
||||
class VirtualLightRefBase : public LightRefBase<VirtualLightRefBase> {
|
||||
public:
|
||||
virtual ~VirtualLightRefBase() = default;
|
||||
};
|
||||
|
||||
} // namespace android
|
@ -0,0 +1,9 @@
|
||||
// DO NOT INCLUDE ANYTHING NEW IN THIS FILE.
|
||||
|
||||
// <log/log.h> has replaced this file and all changes should go there instead.
|
||||
// This path remains strictly to include that header as there are thousands of
|
||||
// references to <utils/Log.h> in the tree.
|
||||
|
||||
// #include <log/log.h>
|
||||
#include <android/log.h>
|
||||
#include <AndroidHelper.h>
|
@ -0,0 +1,713 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
// SOME COMMENTS ABOUT USAGE:
|
||||
|
||||
// This provides primarily wp<> weak pointer types and RefBase, which work
|
||||
// together with sp<> from <StrongPointer.h>.
|
||||
|
||||
// sp<> (and wp<>) are a type of smart pointer that use a well defined protocol
|
||||
// to operate. As long as the object they are templated with implements that
|
||||
// protocol, these smart pointers work. In several places the platform
|
||||
// instantiates sp<> with non-RefBase objects; the two are not tied to each
|
||||
// other.
|
||||
|
||||
// RefBase is such an implementation and it supports strong pointers, weak
|
||||
// pointers and some magic features for the binder.
|
||||
|
||||
// So, when using RefBase objects, you have the ability to use strong and weak
|
||||
// pointers through sp<> and wp<>.
|
||||
|
||||
// Normally, when the last strong pointer goes away, the object is destroyed,
|
||||
// i.e. it's destructor is called. HOWEVER, parts of its associated memory is not
|
||||
// freed until the last weak pointer is released.
|
||||
|
||||
// Weak pointers are essentially "safe" pointers. They are always safe to
|
||||
// access through promote(). They may return nullptr if the object was
|
||||
// destroyed because it ran out of strong pointers. This makes them good candidates
|
||||
// for keys in a cache for instance.
|
||||
|
||||
// Weak pointers remain valid for comparison purposes even after the underlying
|
||||
// object has been destroyed. Even if object A is destroyed and its memory reused
|
||||
// for B, A remaining weak pointer to A will not compare equal to one to B.
|
||||
// This again makes them attractive for use as keys.
|
||||
|
||||
// How is this supposed / intended to be used?
|
||||
|
||||
// Our recommendation is to use strong references (sp<>) when there is an
|
||||
// ownership relation. e.g. when an object "owns" another one, use a strong
|
||||
// ref. And of course use strong refs as arguments of functions (it's extremely
|
||||
// rare that a function will take a wp<>).
|
||||
|
||||
// Typically a newly allocated object will immediately be used to initialize
|
||||
// a strong pointer, which may then be used to construct or assign to other
|
||||
// strong and weak pointers.
|
||||
|
||||
// Use weak references when there are no ownership relation. e.g. the keys in a
|
||||
// cache (you cannot use plain pointers because there is no safe way to acquire
|
||||
// a strong reference from a vanilla pointer).
|
||||
|
||||
// This implies that two objects should never (or very rarely) have sp<> on
|
||||
// each other, because they can't both own each other.
|
||||
|
||||
|
||||
// Caveats with reference counting
|
||||
|
||||
// Obviously, circular strong references are a big problem; this creates leaks
|
||||
// and it's hard to debug -- except it's in fact really easy because RefBase has
|
||||
// tons of debugging code for that. It can basically tell you exactly where the
|
||||
// leak is.
|
||||
|
||||
// Another problem has to do with destructors with side effects. You must
|
||||
// assume that the destructor of reference counted objects can be called AT ANY
|
||||
// TIME. For instance code as simple as this:
|
||||
|
||||
// void setStuff(const sp<Stuff>& stuff) {
|
||||
// std::lock_guard<std::mutex> lock(mMutex);
|
||||
// mStuff = stuff;
|
||||
// }
|
||||
|
||||
// is very dangerous. This code WILL deadlock one day or another.
|
||||
|
||||
// What isn't obvious is that ~Stuff() can be called as a result of the
|
||||
// assignment. And it gets called with the lock held. First of all, the lock is
|
||||
// protecting mStuff, not ~Stuff(). Secondly, if ~Stuff() uses its own internal
|
||||
// mutex, now you have mutex ordering issues. Even worse, if ~Stuff() is
|
||||
// virtual, now you're calling into "user" code (potentially), by that, I mean,
|
||||
// code you didn't even write.
|
||||
|
||||
// A correct way to write this code is something like:
|
||||
|
||||
// void setStuff(const sp<Stuff>& stuff) {
|
||||
// std::unique_lock<std::mutex> lock(mMutex);
|
||||
// sp<Stuff> hold = mStuff;
|
||||
// mStuff = stuff;
|
||||
// lock.unlock();
|
||||
// }
|
||||
|
||||
// More importantly, reference counted objects should do as little work as
|
||||
// possible in their destructor, or at least be mindful that their destructor
|
||||
// could be called from very weird and unintended places.
|
||||
|
||||
// Other more specific restrictions for wp<> and sp<>:
|
||||
|
||||
// Do not construct a strong pointer to "this" in an object's constructor.
|
||||
// The onFirstRef() callback would be made on an incompletely constructed
|
||||
// object.
|
||||
// Construction of a weak pointer to "this" in an object's constructor is also
|
||||
// discouraged. But the implementation was recently changed so that, in the
|
||||
// absence of extendObjectLifetime() calls, weak pointers no longer impact
|
||||
// object lifetime, and hence this no longer risks premature deallocation,
|
||||
// and hence usually works correctly.
|
||||
|
||||
// Such strong or weak pointers can be safely created in the RefBase onFirstRef()
|
||||
// callback.
|
||||
|
||||
// Use of wp::unsafe_get() for any purpose other than debugging is almost
|
||||
// always wrong. Unless you somehow know that there is a longer-lived sp<> to
|
||||
// the same object, it may well return a pointer to a deallocated object that
|
||||
// has since been reallocated for a different purpose. (And if you know there
|
||||
// is a longer-lived sp<>, why not use an sp<> directly?) A wp<> should only be
|
||||
// dereferenced by using promote().
|
||||
|
||||
// Any object inheriting from RefBase should always be destroyed as the result
|
||||
// of a reference count decrement, not via any other means. Such objects
|
||||
// should never be stack allocated, or appear directly as data members in other
|
||||
// objects. Objects inheriting from RefBase should have their strong reference
|
||||
// count incremented as soon as possible after construction. Usually this
|
||||
// will be done via construction of an sp<> to the object, but may instead
|
||||
// involve other means of calling RefBase::incStrong().
|
||||
// Explicitly deleting or otherwise destroying a RefBase object with outstanding
|
||||
// wp<> or sp<> pointers to it will result in an abort or heap corruption.
|
||||
|
||||
// It is particularly important not to mix sp<> and direct storage management
|
||||
// since the sp from raw pointer constructor is implicit. Thus if a RefBase-
|
||||
// -derived object of type T is managed without ever incrementing its strong
|
||||
// count, and accidentally passed to f(sp<T>), a strong pointer to the object
|
||||
// will be temporarily constructed and destroyed, prematurely deallocating the
|
||||
// object, and resulting in heap corruption. None of this would be easily
|
||||
// visible in the source.
|
||||
|
||||
// Extra Features:
|
||||
|
||||
// RefBase::extendObjectLifetime() can be used to prevent destruction of the
|
||||
// object while there are still weak references. This is really special purpose
|
||||
// functionality to support Binder.
|
||||
|
||||
// Wp::promote(), implemented via the attemptIncStrong() member function, is
|
||||
// used to try to convert a weak pointer back to a strong pointer. It's the
|
||||
// normal way to try to access the fields of an object referenced only through
|
||||
// a wp<>. Binder code also sometimes uses attemptIncStrong() directly.
|
||||
|
||||
// RefBase provides a number of additional callbacks for certain reference count
|
||||
// events, as well as some debugging facilities.
|
||||
|
||||
// Debugging support can be enabled by turning on DEBUG_REFS in RefBase.cpp.
|
||||
// Otherwise little checking is provided.
|
||||
|
||||
// Thread safety:
|
||||
|
||||
// Like std::shared_ptr, sp<> and wp<> allow concurrent accesses to DIFFERENT
|
||||
// sp<> and wp<> instances that happen to refer to the same underlying object.
|
||||
// They do NOT support concurrent access (where at least one access is a write)
|
||||
// to THE SAME sp<> or wp<>. In effect, their thread-safety properties are
|
||||
// exactly like those of T*, NOT atomic<T*>.
|
||||
|
||||
#ifndef ANDROID_REF_BASE_H
|
||||
#define ANDROID_REF_BASE_H
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <type_traits> // for common_type.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// LightRefBase used to be declared in this header, so we have to include it
|
||||
#include <utils/LightRefBase.h>
|
||||
|
||||
#include <utils/StrongPointer.h>
|
||||
#include <utils/TypeHelpers.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
namespace android {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#define COMPARE_WEAK(_op_) \
|
||||
template<typename U> \
|
||||
inline bool operator _op_ (const U* o) const { \
|
||||
return m_ptr _op_ o; \
|
||||
} \
|
||||
/* Needed to handle type inference for nullptr: */ \
|
||||
inline bool operator _op_ (const T* o) const { \
|
||||
return m_ptr _op_ o; \
|
||||
}
|
||||
|
||||
template<template<typename C> class comparator, typename T, typename U>
|
||||
static inline bool _wp_compare_(T* a, U* b) {
|
||||
return comparator<typename std::common_type<T*, U*>::type>()(a, b);
|
||||
}
|
||||
|
||||
// Use std::less and friends to avoid undefined behavior when ordering pointers
|
||||
// to different objects.
|
||||
#define COMPARE_WEAK_FUNCTIONAL(_op_, _compare_) \
|
||||
template<typename U> \
|
||||
inline bool operator _op_ (const U* o) const { \
|
||||
return _wp_compare_<_compare_>(m_ptr, o); \
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// RefererenceRenamer is pure abstract, there is no virtual method
|
||||
// implementation to put in a translation unit in order to silence the
|
||||
// weak vtables warning.
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wweak-vtables"
|
||||
#endif
|
||||
|
||||
class ReferenceRenamer {
|
||||
protected:
|
||||
// destructor is purposely not virtual so we avoid code overhead from
|
||||
// subclasses; we have to make it protected to guarantee that it
|
||||
// cannot be called from this base class (and to make strict compilers
|
||||
// happy).
|
||||
~ReferenceRenamer() { }
|
||||
public:
|
||||
virtual void operator()(size_t i) const = 0;
|
||||
};
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
class RefBase
|
||||
{
|
||||
public:
|
||||
void incStrong(const void* id) const;
|
||||
void decStrong(const void* id) const;
|
||||
|
||||
void forceIncStrong(const void* id) const;
|
||||
|
||||
//! DEBUGGING ONLY: Get current strong ref count.
|
||||
int32_t getStrongCount() const;
|
||||
|
||||
class weakref_type
|
||||
{
|
||||
public:
|
||||
RefBase* refBase() const;
|
||||
|
||||
void incWeak(const void* id);
|
||||
void decWeak(const void* id);
|
||||
|
||||
// acquires a strong reference if there is already one.
|
||||
bool attemptIncStrong(const void* id);
|
||||
|
||||
// acquires a weak reference if there is already one.
|
||||
// This is not always safe. see ProcessState.cpp and BpBinder.cpp
|
||||
// for proper use.
|
||||
bool attemptIncWeak(const void* id);
|
||||
|
||||
//! DEBUGGING ONLY: Get current weak ref count.
|
||||
int32_t getWeakCount() const;
|
||||
|
||||
//! DEBUGGING ONLY: Print references held on object.
|
||||
void printRefs() const;
|
||||
|
||||
//! DEBUGGING ONLY: Enable tracking for this object.
|
||||
// enable -- enable/disable tracking
|
||||
// retain -- when tracking is enable, if true, then we save a stack trace
|
||||
// for each reference and dereference; when retain == false, we
|
||||
// match up references and dereferences and keep only the
|
||||
// outstanding ones.
|
||||
|
||||
void trackMe(bool enable, bool retain);
|
||||
};
|
||||
|
||||
weakref_type* createWeak(const void* id) const;
|
||||
|
||||
weakref_type* getWeakRefs() const;
|
||||
|
||||
//! DEBUGGING ONLY: Print references held on object.
|
||||
inline void printRefs() const { getWeakRefs()->printRefs(); }
|
||||
|
||||
//! DEBUGGING ONLY: Enable tracking of object.
|
||||
inline void trackMe(bool enable, bool retain)
|
||||
{
|
||||
getWeakRefs()->trackMe(enable, retain);
|
||||
}
|
||||
|
||||
protected:
|
||||
RefBase();
|
||||
virtual ~RefBase();
|
||||
|
||||
//! Flags for extendObjectLifetime()
|
||||
enum {
|
||||
OBJECT_LIFETIME_STRONG = 0x0000,
|
||||
OBJECT_LIFETIME_WEAK = 0x0001,
|
||||
OBJECT_LIFETIME_MASK = 0x0001
|
||||
};
|
||||
|
||||
void extendObjectLifetime(int32_t mode);
|
||||
|
||||
//! Flags for onIncStrongAttempted()
|
||||
enum {
|
||||
FIRST_INC_STRONG = 0x0001
|
||||
};
|
||||
|
||||
// Invoked after creation of initial strong pointer/reference.
|
||||
virtual void onFirstRef();
|
||||
// Invoked when either the last strong reference goes away, or we need to undo
|
||||
// the effect of an unnecessary onIncStrongAttempted.
|
||||
virtual void onLastStrongRef(const void* id);
|
||||
// Only called in OBJECT_LIFETIME_WEAK case. Returns true if OK to promote to
|
||||
// strong reference. May have side effects if it returns true.
|
||||
// The first flags argument is always FIRST_INC_STRONG.
|
||||
// TODO: Remove initial flag argument.
|
||||
virtual bool onIncStrongAttempted(uint32_t flags, const void* id);
|
||||
// Invoked in the OBJECT_LIFETIME_WEAK case when the last reference of either
|
||||
// kind goes away. Unused.
|
||||
// TODO: Remove.
|
||||
virtual void onLastWeakRef(const void* id);
|
||||
|
||||
private:
|
||||
friend class weakref_type;
|
||||
class weakref_impl;
|
||||
|
||||
RefBase(const RefBase& o);
|
||||
RefBase& operator=(const RefBase& o);
|
||||
|
||||
private:
|
||||
friend class ReferenceMover;
|
||||
|
||||
static void renameRefs(size_t n, const ReferenceRenamer& renamer);
|
||||
|
||||
static void renameRefId(weakref_type* ref,
|
||||
const void* old_id, const void* new_id);
|
||||
|
||||
static void renameRefId(RefBase* ref,
|
||||
const void* old_id, const void* new_id);
|
||||
|
||||
weakref_impl* const mRefs;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
template <typename T>
|
||||
class wp
|
||||
{
|
||||
public:
|
||||
typedef typename RefBase::weakref_type weakref_type;
|
||||
|
||||
inline wp() : m_ptr(nullptr), m_refs(nullptr) { }
|
||||
|
||||
wp(T* other); // NOLINT(implicit)
|
||||
wp(const wp<T>& other);
|
||||
explicit wp(const sp<T>& other);
|
||||
template<typename U> wp(U* other); // NOLINT(implicit)
|
||||
template<typename U> wp(const sp<U>& other); // NOLINT(implicit)
|
||||
template<typename U> wp(const wp<U>& other); // NOLINT(implicit)
|
||||
|
||||
~wp();
|
||||
|
||||
// Assignment
|
||||
|
||||
wp& operator = (T* other);
|
||||
wp& operator = (const wp<T>& other);
|
||||
wp& operator = (const sp<T>& other);
|
||||
|
||||
template<typename U> wp& operator = (U* other);
|
||||
template<typename U> wp& operator = (const wp<U>& other);
|
||||
template<typename U> wp& operator = (const sp<U>& other);
|
||||
|
||||
void set_object_and_refs(T* other, weakref_type* refs);
|
||||
|
||||
// promotion to sp
|
||||
|
||||
sp<T> promote() const;
|
||||
|
||||
// Reset
|
||||
|
||||
void clear();
|
||||
|
||||
// Accessors
|
||||
|
||||
inline weakref_type* get_refs() const { return m_refs; }
|
||||
|
||||
inline T* unsafe_get() const { return m_ptr; }
|
||||
|
||||
// Operators
|
||||
|
||||
COMPARE_WEAK(==)
|
||||
COMPARE_WEAK(!=)
|
||||
COMPARE_WEAK_FUNCTIONAL(>, std::greater)
|
||||
COMPARE_WEAK_FUNCTIONAL(<, std::less)
|
||||
COMPARE_WEAK_FUNCTIONAL(<=, std::less_equal)
|
||||
COMPARE_WEAK_FUNCTIONAL(>=, std::greater_equal)
|
||||
|
||||
template<typename U>
|
||||
inline bool operator == (const wp<U>& o) const {
|
||||
return m_refs == o.m_refs; // Implies m_ptr == o.mptr; see invariants below.
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
inline bool operator == (const sp<U>& o) const {
|
||||
// Just comparing m_ptr fields is often dangerous, since wp<> may refer to an older
|
||||
// object at the same address.
|
||||
if (o == nullptr) {
|
||||
return m_ptr == nullptr;
|
||||
} else {
|
||||
return m_refs == o->getWeakRefs(); // Implies m_ptr == o.mptr.
|
||||
}
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
inline bool operator != (const sp<U>& o) const {
|
||||
return !(*this == o);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
inline bool operator > (const wp<U>& o) const {
|
||||
if (m_ptr == o.m_ptr) {
|
||||
return _wp_compare_<std::greater>(m_refs, o.m_refs);
|
||||
} else {
|
||||
return _wp_compare_<std::greater>(m_ptr, o.m_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
inline bool operator < (const wp<U>& o) const {
|
||||
if (m_ptr == o.m_ptr) {
|
||||
return _wp_compare_<std::less>(m_refs, o.m_refs);
|
||||
} else {
|
||||
return _wp_compare_<std::less>(m_ptr, o.m_ptr);
|
||||
}
|
||||
}
|
||||
template<typename U> inline bool operator != (const wp<U>& o) const { return !operator == (o); }
|
||||
template<typename U> inline bool operator <= (const wp<U>& o) const { return !operator > (o); }
|
||||
template<typename U> inline bool operator >= (const wp<U>& o) const { return !operator < (o); }
|
||||
|
||||
private:
|
||||
template<typename Y> friend class sp;
|
||||
template<typename Y> friend class wp;
|
||||
|
||||
T* m_ptr;
|
||||
weakref_type* m_refs;
|
||||
};
|
||||
|
||||
#undef COMPARE_WEAK
|
||||
#undef COMPARE_WEAK_FUNCTIONAL
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// No user serviceable parts below here.
|
||||
|
||||
// Implementation invariants:
|
||||
// Either
|
||||
// 1) m_ptr and m_refs are both null, or
|
||||
// 2) m_refs == m_ptr->mRefs, or
|
||||
// 3) *m_ptr is no longer live, and m_refs points to the weakref_type object that corresponded
|
||||
// to m_ptr while it was live. *m_refs remains live while a wp<> refers to it.
|
||||
//
|
||||
// The m_refs field in a RefBase object is allocated on construction, unique to that RefBase
|
||||
// object, and never changes. Thus if two wp's have identical m_refs fields, they are either both
|
||||
// null or point to the same object. If two wp's have identical m_ptr fields, they either both
|
||||
// point to the same live object and thus have the same m_ref fields, or at least one of the
|
||||
// objects is no longer live.
|
||||
//
|
||||
// Note that the above comparison operations go out of their way to provide an ordering consistent
|
||||
// with ordinary pointer comparison; otherwise they could ignore m_ptr, and just compare m_refs.
|
||||
|
||||
template<typename T>
|
||||
wp<T>::wp(T* other)
|
||||
: m_ptr(other)
|
||||
{
|
||||
m_refs = other ? m_refs = other->createWeak(this) : nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
wp<T>::wp(const wp<T>& other)
|
||||
: m_ptr(other.m_ptr), m_refs(other.m_refs)
|
||||
{
|
||||
if (m_ptr) m_refs->incWeak(this);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
wp<T>::wp(const sp<T>& other)
|
||||
: m_ptr(other.m_ptr)
|
||||
{
|
||||
m_refs = m_ptr ? m_ptr->createWeak(this) : nullptr;
|
||||
}
|
||||
|
||||
template<typename T> template<typename U>
|
||||
wp<T>::wp(U* other)
|
||||
: m_ptr(other)
|
||||
{
|
||||
m_refs = other ? other->createWeak(this) : nullptr;
|
||||
}
|
||||
|
||||
template<typename T> template<typename U>
|
||||
wp<T>::wp(const wp<U>& other)
|
||||
: m_ptr(other.m_ptr)
|
||||
{
|
||||
if (m_ptr) {
|
||||
m_refs = other.m_refs;
|
||||
m_refs->incWeak(this);
|
||||
} else {
|
||||
m_refs = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> template<typename U>
|
||||
wp<T>::wp(const sp<U>& other)
|
||||
: m_ptr(other.m_ptr)
|
||||
{
|
||||
m_refs = m_ptr ? m_ptr->createWeak(this) : nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
wp<T>::~wp()
|
||||
{
|
||||
if (m_ptr) m_refs->decWeak(this);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
wp<T>& wp<T>::operator = (T* other)
|
||||
{
|
||||
weakref_type* newRefs =
|
||||
other ? other->createWeak(this) : nullptr;
|
||||
if (m_ptr) m_refs->decWeak(this);
|
||||
m_ptr = other;
|
||||
m_refs = newRefs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
wp<T>& wp<T>::operator = (const wp<T>& other)
|
||||
{
|
||||
weakref_type* otherRefs(other.m_refs);
|
||||
T* otherPtr(other.m_ptr);
|
||||
if (otherPtr) otherRefs->incWeak(this);
|
||||
if (m_ptr) m_refs->decWeak(this);
|
||||
m_ptr = otherPtr;
|
||||
m_refs = otherRefs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
wp<T>& wp<T>::operator = (const sp<T>& other)
|
||||
{
|
||||
weakref_type* newRefs =
|
||||
other != nullptr ? other->createWeak(this) : nullptr;
|
||||
T* otherPtr(other.m_ptr);
|
||||
if (m_ptr) m_refs->decWeak(this);
|
||||
m_ptr = otherPtr;
|
||||
m_refs = newRefs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T> template<typename U>
|
||||
wp<T>& wp<T>::operator = (U* other)
|
||||
{
|
||||
weakref_type* newRefs =
|
||||
other ? other->createWeak(this) : 0;
|
||||
if (m_ptr) m_refs->decWeak(this);
|
||||
m_ptr = other;
|
||||
m_refs = newRefs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T> template<typename U>
|
||||
wp<T>& wp<T>::operator = (const wp<U>& other)
|
||||
{
|
||||
weakref_type* otherRefs(other.m_refs);
|
||||
U* otherPtr(other.m_ptr);
|
||||
if (otherPtr) otherRefs->incWeak(this);
|
||||
if (m_ptr) m_refs->decWeak(this);
|
||||
m_ptr = otherPtr;
|
||||
m_refs = otherRefs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T> template<typename U>
|
||||
wp<T>& wp<T>::operator = (const sp<U>& other)
|
||||
{
|
||||
weakref_type* newRefs =
|
||||
other != nullptr ? other->createWeak(this) : 0;
|
||||
U* otherPtr(other.m_ptr);
|
||||
if (m_ptr) m_refs->decWeak(this);
|
||||
m_ptr = otherPtr;
|
||||
m_refs = newRefs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void wp<T>::set_object_and_refs(T* other, weakref_type* refs)
|
||||
{
|
||||
if (other) refs->incWeak(this);
|
||||
if (m_ptr) m_refs->decWeak(this);
|
||||
m_ptr = other;
|
||||
m_refs = refs;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
sp<T> wp<T>::promote() const
|
||||
{
|
||||
sp<T> result;
|
||||
if (m_ptr && m_refs->attemptIncStrong(&result)) {
|
||||
result.set_pointer(m_ptr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void wp<T>::clear()
|
||||
{
|
||||
if (m_ptr) {
|
||||
m_refs->decWeak(this);
|
||||
m_refs = 0;
|
||||
m_ptr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// this class just serves as a namespace so TYPE::moveReferences can stay
|
||||
// private.
|
||||
class ReferenceMover {
|
||||
public:
|
||||
// it would be nice if we could make sure no extra code is generated
|
||||
// for sp<TYPE> or wp<TYPE> when TYPE is a descendant of RefBase:
|
||||
// Using a sp<RefBase> override doesn't work; it's a bit like we wanted
|
||||
// a template<typename TYPE inherits RefBase> template...
|
||||
|
||||
template<typename TYPE> static inline
|
||||
void move_references(sp<TYPE>* dest, sp<TYPE> const* src, size_t n) {
|
||||
|
||||
class Renamer : public ReferenceRenamer {
|
||||
sp<TYPE>* d_;
|
||||
sp<TYPE> const* s_;
|
||||
virtual void operator()(size_t i) const {
|
||||
// The id are known to be the sp<>'s this pointer
|
||||
TYPE::renameRefId(d_[i].get(), &s_[i], &d_[i]);
|
||||
}
|
||||
public:
|
||||
Renamer(sp<TYPE>* d, sp<TYPE> const* s) : d_(d), s_(s) { }
|
||||
virtual ~Renamer() { }
|
||||
};
|
||||
|
||||
memmove(dest, src, n*sizeof(sp<TYPE>));
|
||||
TYPE::renameRefs(n, Renamer(dest, src));
|
||||
}
|
||||
|
||||
|
||||
template<typename TYPE> static inline
|
||||
void move_references(wp<TYPE>* dest, wp<TYPE> const* src, size_t n) {
|
||||
|
||||
class Renamer : public ReferenceRenamer {
|
||||
wp<TYPE>* d_;
|
||||
wp<TYPE> const* s_;
|
||||
virtual void operator()(size_t i) const {
|
||||
// The id are known to be the wp<>'s this pointer
|
||||
TYPE::renameRefId(d_[i].get_refs(), &s_[i], &d_[i]);
|
||||
}
|
||||
public:
|
||||
Renamer(wp<TYPE>* rd, wp<TYPE> const* rs) : d_(rd), s_(rs) { }
|
||||
virtual ~Renamer() { }
|
||||
};
|
||||
|
||||
memmove(dest, src, n*sizeof(wp<TYPE>));
|
||||
TYPE::renameRefs(n, Renamer(dest, src));
|
||||
}
|
||||
};
|
||||
|
||||
// specialization for moving sp<> and wp<> types.
|
||||
// these are used by the [Sorted|Keyed]Vector<> implementations
|
||||
// sp<> and wp<> need to be handled specially, because they do not
|
||||
// have trivial copy operation in the general case (see RefBase.cpp
|
||||
// when DEBUG ops are enabled), but can be implemented very
|
||||
// efficiently in most cases.
|
||||
|
||||
template<typename TYPE> inline
|
||||
void move_forward_type(sp<TYPE>* d, sp<TYPE> const* s, size_t n) {
|
||||
ReferenceMover::move_references(d, s, n);
|
||||
}
|
||||
|
||||
template<typename TYPE> inline
|
||||
void move_backward_type(sp<TYPE>* d, sp<TYPE> const* s, size_t n) {
|
||||
ReferenceMover::move_references(d, s, n);
|
||||
}
|
||||
|
||||
template<typename TYPE> inline
|
||||
void move_forward_type(wp<TYPE>* d, wp<TYPE> const* s, size_t n) {
|
||||
ReferenceMover::move_references(d, s, n);
|
||||
}
|
||||
|
||||
template<typename TYPE> inline
|
||||
void move_backward_type(wp<TYPE>* d, wp<TYPE> const* s, size_t n) {
|
||||
ReferenceMover::move_references(d, s, n);
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#endif // ANDROID_REF_BASE_H
|
@ -0,0 +1,317 @@
|
||||
/*
|
||||
* Copyright (C) 2005 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_STRONG_POINTER_H
|
||||
#define ANDROID_STRONG_POINTER_H
|
||||
|
||||
#include <functional>
|
||||
#include <type_traits> // for common_type.
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
namespace android {
|
||||
|
||||
template<typename T> class wp;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
template<typename T>
|
||||
class sp {
|
||||
public:
|
||||
inline sp() : m_ptr(nullptr) { }
|
||||
|
||||
sp(T* other); // NOLINT(implicit)
|
||||
sp(const sp<T>& other);
|
||||
sp(sp<T>&& other) noexcept;
|
||||
template<typename U> sp(U* other); // NOLINT(implicit)
|
||||
template<typename U> sp(const sp<U>& other); // NOLINT(implicit)
|
||||
template<typename U> sp(sp<U>&& other); // NOLINT(implicit)
|
||||
|
||||
~sp();
|
||||
|
||||
// Assignment
|
||||
|
||||
sp& operator = (T* other);
|
||||
sp& operator = (const sp<T>& other);
|
||||
sp& operator=(sp<T>&& other) noexcept;
|
||||
|
||||
template<typename U> sp& operator = (const sp<U>& other);
|
||||
template<typename U> sp& operator = (sp<U>&& other);
|
||||
template<typename U> sp& operator = (U* other);
|
||||
|
||||
//! Special optimization for use by ProcessState (and nobody else).
|
||||
void force_set(T* other);
|
||||
|
||||
// Reset
|
||||
|
||||
void clear();
|
||||
|
||||
// Accessors
|
||||
|
||||
inline T& operator* () const { return *m_ptr; }
|
||||
inline T* operator-> () const { return m_ptr; }
|
||||
inline T* get() const { return m_ptr; }
|
||||
inline explicit operator bool () const { return m_ptr != nullptr; }
|
||||
|
||||
// Punt these to the wp<> implementation.
|
||||
template<typename U>
|
||||
inline bool operator == (const wp<U>& o) const {
|
||||
return o == *this;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
inline bool operator != (const wp<U>& o) const {
|
||||
return o != *this;
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename Y> friend class sp;
|
||||
template<typename Y> friend class wp;
|
||||
void set_pointer(T* ptr);
|
||||
static inline void check_not_on_stack(const void* ptr);
|
||||
T* m_ptr;
|
||||
};
|
||||
|
||||
#define COMPARE_STRONG(_op_) \
|
||||
template <typename T, typename U> \
|
||||
static inline bool operator _op_(const sp<T>& t, const sp<U>& u) { \
|
||||
return t.get() _op_ u.get(); \
|
||||
} \
|
||||
template <typename T, typename U> \
|
||||
static inline bool operator _op_(const T* t, const sp<U>& u) { \
|
||||
return t _op_ u.get(); \
|
||||
} \
|
||||
template <typename T, typename U> \
|
||||
static inline bool operator _op_(const sp<T>& t, const U* u) { \
|
||||
return t.get() _op_ u; \
|
||||
} \
|
||||
template <typename T> \
|
||||
static inline bool operator _op_(const sp<T>& t, std::nullptr_t) { \
|
||||
return t.get() _op_ nullptr; \
|
||||
} \
|
||||
template <typename T> \
|
||||
static inline bool operator _op_(std::nullptr_t, const sp<T>& t) { \
|
||||
return nullptr _op_ t.get(); \
|
||||
}
|
||||
|
||||
template <template <typename C> class comparator, typename T, typename U>
|
||||
static inline bool _sp_compare_(T* a, U* b) {
|
||||
return comparator<typename std::common_type<T*, U*>::type>()(a, b);
|
||||
}
|
||||
|
||||
#define COMPARE_STRONG_FUNCTIONAL(_op_, _compare_) \
|
||||
template <typename T, typename U> \
|
||||
static inline bool operator _op_(const sp<T>& t, const sp<U>& u) { \
|
||||
return _sp_compare_<_compare_>(t.get(), u.get()); \
|
||||
} \
|
||||
template <typename T, typename U> \
|
||||
static inline bool operator _op_(const T* t, const sp<U>& u) { \
|
||||
return _sp_compare_<_compare_>(t, u.get()); \
|
||||
} \
|
||||
template <typename T, typename U> \
|
||||
static inline bool operator _op_(const sp<T>& t, const U* u) { \
|
||||
return _sp_compare_<_compare_>(t.get(), u); \
|
||||
} \
|
||||
template <typename T> \
|
||||
static inline bool operator _op_(const sp<T>& t, std::nullptr_t) { \
|
||||
return _sp_compare_<_compare_>(t.get(), nullptr); \
|
||||
} \
|
||||
template <typename T> \
|
||||
static inline bool operator _op_(std::nullptr_t, const sp<T>& t) { \
|
||||
return _sp_compare_<_compare_>(nullptr, t.get()); \
|
||||
}
|
||||
|
||||
COMPARE_STRONG(==)
|
||||
COMPARE_STRONG(!=)
|
||||
COMPARE_STRONG_FUNCTIONAL(>, std::greater)
|
||||
COMPARE_STRONG_FUNCTIONAL(<, std::less)
|
||||
COMPARE_STRONG_FUNCTIONAL(<=, std::less_equal)
|
||||
COMPARE_STRONG_FUNCTIONAL(>=, std::greater_equal)
|
||||
|
||||
#undef COMPARE_STRONG
|
||||
#undef COMPARE_STRONG_FUNCTIONAL
|
||||
|
||||
// For code size reasons, we do not want these inlined or templated.
|
||||
void sp_report_race();
|
||||
void sp_report_stack_pointer();
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// No user serviceable parts below here.
|
||||
|
||||
// Check whether address is definitely on the calling stack. We actually check whether it is on
|
||||
// the same 4K page as the frame pointer.
|
||||
//
|
||||
// Assumptions:
|
||||
// - Pages are never smaller than 4K (MIN_PAGE_SIZE)
|
||||
// - Malloced memory never shares a page with a stack.
|
||||
//
|
||||
// It does not appear safe to broaden this check to include adjacent pages; apparently this code
|
||||
// is used in environments where there may not be a guard page below (at higher addresses than)
|
||||
// the bottom of the stack.
|
||||
//
|
||||
// TODO: Consider adding make_sp<T>() to allocate an object and wrap the resulting pointer safely
|
||||
// without checking overhead.
|
||||
template <typename T>
|
||||
void sp<T>::check_not_on_stack(const void* ptr) {
|
||||
static constexpr int MIN_PAGE_SIZE = 0x1000; // 4K. Safer than including sys/user.h.
|
||||
static constexpr uintptr_t MIN_PAGE_MASK = ~static_cast<uintptr_t>(MIN_PAGE_SIZE - 1);
|
||||
uintptr_t my_frame_address =
|
||||
reinterpret_cast<uintptr_t>(__builtin_frame_address(0 /* this frame */));
|
||||
if (((reinterpret_cast<uintptr_t>(ptr) ^ my_frame_address) & MIN_PAGE_MASK) == 0) {
|
||||
sp_report_stack_pointer();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
sp<T>::sp(T* other)
|
||||
: m_ptr(other) {
|
||||
if (other) {
|
||||
check_not_on_stack(other);
|
||||
other->incStrong(this);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
sp<T>::sp(const sp<T>& other)
|
||||
: m_ptr(other.m_ptr) {
|
||||
if (m_ptr)
|
||||
m_ptr->incStrong(this);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
sp<T>::sp(sp<T>&& other) noexcept : m_ptr(other.m_ptr) {
|
||||
other.m_ptr = nullptr;
|
||||
}
|
||||
|
||||
template<typename T> template<typename U>
|
||||
sp<T>::sp(U* other)
|
||||
: m_ptr(other) {
|
||||
if (other) {
|
||||
check_not_on_stack(other);
|
||||
(static_cast<T*>(other))->incStrong(this);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> template<typename U>
|
||||
sp<T>::sp(const sp<U>& other)
|
||||
: m_ptr(other.m_ptr) {
|
||||
if (m_ptr)
|
||||
m_ptr->incStrong(this);
|
||||
}
|
||||
|
||||
template<typename T> template<typename U>
|
||||
sp<T>::sp(sp<U>&& other)
|
||||
: m_ptr(other.m_ptr) {
|
||||
other.m_ptr = nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
sp<T>::~sp() {
|
||||
if (m_ptr)
|
||||
m_ptr->decStrong(this);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
sp<T>& sp<T>::operator =(const sp<T>& other) {
|
||||
// Force m_ptr to be read twice, to heuristically check for data races.
|
||||
T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
|
||||
T* otherPtr(other.m_ptr);
|
||||
if (otherPtr) otherPtr->incStrong(this);
|
||||
if (oldPtr) oldPtr->decStrong(this);
|
||||
if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
|
||||
m_ptr = otherPtr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
sp<T>& sp<T>::operator=(sp<T>&& other) noexcept {
|
||||
T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
|
||||
if (oldPtr) oldPtr->decStrong(this);
|
||||
if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
|
||||
m_ptr = other.m_ptr;
|
||||
other.m_ptr = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
sp<T>& sp<T>::operator =(T* other) {
|
||||
T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
|
||||
if (other) {
|
||||
check_not_on_stack(other);
|
||||
other->incStrong(this);
|
||||
}
|
||||
if (oldPtr) oldPtr->decStrong(this);
|
||||
if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
|
||||
m_ptr = other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T> template<typename U>
|
||||
sp<T>& sp<T>::operator =(const sp<U>& other) {
|
||||
T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
|
||||
T* otherPtr(other.m_ptr);
|
||||
if (otherPtr) otherPtr->incStrong(this);
|
||||
if (oldPtr) oldPtr->decStrong(this);
|
||||
if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
|
||||
m_ptr = otherPtr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T> template<typename U>
|
||||
sp<T>& sp<T>::operator =(sp<U>&& other) {
|
||||
T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
|
||||
if (m_ptr) m_ptr->decStrong(this);
|
||||
if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
|
||||
m_ptr = other.m_ptr;
|
||||
other.m_ptr = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T> template<typename U>
|
||||
sp<T>& sp<T>::operator =(U* other) {
|
||||
T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
|
||||
if (other) (static_cast<T*>(other))->incStrong(this);
|
||||
if (oldPtr) oldPtr->decStrong(this);
|
||||
if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
|
||||
m_ptr = other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void sp<T>::force_set(T* other) {
|
||||
other->forceIncStrong(this);
|
||||
m_ptr = other;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void sp<T>::clear() {
|
||||
T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
|
||||
if (oldPtr) {
|
||||
oldPtr->decStrong(this);
|
||||
if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
|
||||
m_ptr = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void sp<T>::set_pointer(T* ptr) {
|
||||
m_ptr = ptr;
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#endif // ANDROID_STRONG_POINTER_H
|
@ -0,0 +1,336 @@
|
||||
/*
|
||||
* Copyright (C) 2005 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_TYPE_HELPERS_H
|
||||
#define ANDROID_TYPE_HELPERS_H
|
||||
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
namespace android {
|
||||
|
||||
/*
|
||||
* Types traits
|
||||
*/
|
||||
|
||||
template <typename T> struct trait_trivial_ctor { enum { value = false }; };
|
||||
template <typename T> struct trait_trivial_dtor { enum { value = false }; };
|
||||
template <typename T> struct trait_trivial_copy { enum { value = false }; };
|
||||
template <typename T> struct trait_trivial_move { enum { value = false }; };
|
||||
template <typename T> struct trait_pointer { enum { value = false }; };
|
||||
template <typename T> struct trait_pointer<T*> { enum { value = true }; };
|
||||
|
||||
template <typename TYPE>
|
||||
struct traits {
|
||||
enum {
|
||||
// whether this type is a pointer
|
||||
is_pointer = trait_pointer<TYPE>::value,
|
||||
// whether this type's constructor is a no-op
|
||||
has_trivial_ctor = is_pointer || trait_trivial_ctor<TYPE>::value,
|
||||
// whether this type's destructor is a no-op
|
||||
has_trivial_dtor = is_pointer || trait_trivial_dtor<TYPE>::value,
|
||||
// whether this type type can be copy-constructed with memcpy
|
||||
has_trivial_copy = is_pointer || trait_trivial_copy<TYPE>::value,
|
||||
// whether this type can be moved with memmove
|
||||
has_trivial_move = is_pointer || trait_trivial_move<TYPE>::value
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct aggregate_traits {
|
||||
enum {
|
||||
is_pointer = false,
|
||||
has_trivial_ctor =
|
||||
traits<T>::has_trivial_ctor && traits<U>::has_trivial_ctor,
|
||||
has_trivial_dtor =
|
||||
traits<T>::has_trivial_dtor && traits<U>::has_trivial_dtor,
|
||||
has_trivial_copy =
|
||||
traits<T>::has_trivial_copy && traits<U>::has_trivial_copy,
|
||||
has_trivial_move =
|
||||
traits<T>::has_trivial_move && traits<U>::has_trivial_move
|
||||
};
|
||||
};
|
||||
|
||||
#define ANDROID_TRIVIAL_CTOR_TRAIT( T ) \
|
||||
template<> struct trait_trivial_ctor< T > { enum { value = true }; };
|
||||
|
||||
#define ANDROID_TRIVIAL_DTOR_TRAIT( T ) \
|
||||
template<> struct trait_trivial_dtor< T > { enum { value = true }; };
|
||||
|
||||
#define ANDROID_TRIVIAL_COPY_TRAIT( T ) \
|
||||
template<> struct trait_trivial_copy< T > { enum { value = true }; };
|
||||
|
||||
#define ANDROID_TRIVIAL_MOVE_TRAIT( T ) \
|
||||
template<> struct trait_trivial_move< T > { enum { value = true }; };
|
||||
|
||||
#define ANDROID_BASIC_TYPES_TRAITS( T ) \
|
||||
ANDROID_TRIVIAL_CTOR_TRAIT( T ) \
|
||||
ANDROID_TRIVIAL_DTOR_TRAIT( T ) \
|
||||
ANDROID_TRIVIAL_COPY_TRAIT( T ) \
|
||||
ANDROID_TRIVIAL_MOVE_TRAIT( T )
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* basic types traits
|
||||
*/
|
||||
|
||||
ANDROID_BASIC_TYPES_TRAITS( void )
|
||||
ANDROID_BASIC_TYPES_TRAITS( bool )
|
||||
ANDROID_BASIC_TYPES_TRAITS( char )
|
||||
ANDROID_BASIC_TYPES_TRAITS( unsigned char )
|
||||
ANDROID_BASIC_TYPES_TRAITS( short )
|
||||
ANDROID_BASIC_TYPES_TRAITS( unsigned short )
|
||||
ANDROID_BASIC_TYPES_TRAITS( int )
|
||||
ANDROID_BASIC_TYPES_TRAITS( unsigned int )
|
||||
ANDROID_BASIC_TYPES_TRAITS( long )
|
||||
ANDROID_BASIC_TYPES_TRAITS( unsigned long )
|
||||
ANDROID_BASIC_TYPES_TRAITS( long long )
|
||||
ANDROID_BASIC_TYPES_TRAITS( unsigned long long )
|
||||
ANDROID_BASIC_TYPES_TRAITS( float )
|
||||
ANDROID_BASIC_TYPES_TRAITS( double )
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
/*
|
||||
* compare and order types
|
||||
*/
|
||||
|
||||
template<typename TYPE> inline
|
||||
int strictly_order_type(const TYPE& lhs, const TYPE& rhs) {
|
||||
return (lhs < rhs) ? 1 : 0;
|
||||
}
|
||||
|
||||
template<typename TYPE> inline
|
||||
int compare_type(const TYPE& lhs, const TYPE& rhs) {
|
||||
return strictly_order_type(rhs, lhs) - strictly_order_type(lhs, rhs);
|
||||
}
|
||||
|
||||
/*
|
||||
* create, destroy, copy and move types...
|
||||
*/
|
||||
|
||||
template<typename TYPE> inline
|
||||
void construct_type(TYPE* p, size_t n) {
|
||||
if (!traits<TYPE>::has_trivial_ctor) {
|
||||
while (n > 0) {
|
||||
n--;
|
||||
new(p++) TYPE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TYPE> inline
|
||||
void destroy_type(TYPE* p, size_t n) {
|
||||
if (!traits<TYPE>::has_trivial_dtor) {
|
||||
while (n > 0) {
|
||||
n--;
|
||||
p->~TYPE();
|
||||
p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TYPE>
|
||||
typename std::enable_if<traits<TYPE>::has_trivial_copy>::type
|
||||
inline
|
||||
copy_type(TYPE* d, const TYPE* s, size_t n) {
|
||||
memcpy(d,s,n*sizeof(TYPE));
|
||||
}
|
||||
|
||||
template<typename TYPE>
|
||||
typename std::enable_if<!traits<TYPE>::has_trivial_copy>::type
|
||||
inline
|
||||
copy_type(TYPE* d, const TYPE* s, size_t n) {
|
||||
while (n > 0) {
|
||||
n--;
|
||||
new(d) TYPE(*s);
|
||||
d++, s++;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TYPE> inline
|
||||
void splat_type(TYPE* where, const TYPE* what, size_t n) {
|
||||
if (!traits<TYPE>::has_trivial_copy) {
|
||||
while (n > 0) {
|
||||
n--;
|
||||
new(where) TYPE(*what);
|
||||
where++;
|
||||
}
|
||||
} else {
|
||||
while (n > 0) {
|
||||
n--;
|
||||
*where++ = *what;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TYPE>
|
||||
struct use_trivial_move : public std::integral_constant<bool,
|
||||
(traits<TYPE>::has_trivial_dtor && traits<TYPE>::has_trivial_copy)
|
||||
|| traits<TYPE>::has_trivial_move
|
||||
> {};
|
||||
|
||||
template<typename TYPE>
|
||||
typename std::enable_if<use_trivial_move<TYPE>::value>::type
|
||||
inline
|
||||
move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) {
|
||||
memmove(d, s, n*sizeof(TYPE));
|
||||
}
|
||||
|
||||
template<typename TYPE>
|
||||
typename std::enable_if<!use_trivial_move<TYPE>::value>::type
|
||||
inline
|
||||
move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) {
|
||||
d += n;
|
||||
s += n;
|
||||
while (n > 0) {
|
||||
n--;
|
||||
--d, --s;
|
||||
if (!traits<TYPE>::has_trivial_copy) {
|
||||
new(d) TYPE(*s);
|
||||
} else {
|
||||
*d = *s;
|
||||
}
|
||||
if (!traits<TYPE>::has_trivial_dtor) {
|
||||
s->~TYPE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TYPE>
|
||||
typename std::enable_if<use_trivial_move<TYPE>::value>::type
|
||||
inline
|
||||
move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) {
|
||||
memmove(d, s, n*sizeof(TYPE));
|
||||
}
|
||||
|
||||
template<typename TYPE>
|
||||
typename std::enable_if<!use_trivial_move<TYPE>::value>::type
|
||||
inline
|
||||
move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) {
|
||||
while (n > 0) {
|
||||
n--;
|
||||
if (!traits<TYPE>::has_trivial_copy) {
|
||||
new(d) TYPE(*s);
|
||||
} else {
|
||||
*d = *s;
|
||||
}
|
||||
if (!traits<TYPE>::has_trivial_dtor) {
|
||||
s->~TYPE();
|
||||
}
|
||||
d++, s++;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* a key/value pair
|
||||
*/
|
||||
|
||||
template <typename KEY, typename VALUE>
|
||||
struct key_value_pair_t {
|
||||
typedef KEY key_t;
|
||||
typedef VALUE value_t;
|
||||
|
||||
KEY key;
|
||||
VALUE value;
|
||||
key_value_pair_t() { }
|
||||
key_value_pair_t(const key_value_pair_t& o) : key(o.key), value(o.value) { }
|
||||
key_value_pair_t& operator=(const key_value_pair_t& o) {
|
||||
key = o.key;
|
||||
value = o.value;
|
||||
return *this;
|
||||
}
|
||||
key_value_pair_t(const KEY& k, const VALUE& v) : key(k), value(v) { }
|
||||
explicit key_value_pair_t(const KEY& k) : key(k) { }
|
||||
inline bool operator < (const key_value_pair_t& o) const {
|
||||
return strictly_order_type(key, o.key);
|
||||
}
|
||||
inline const KEY& getKey() const {
|
||||
return key;
|
||||
}
|
||||
inline const VALUE& getValue() const {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename K, typename V>
|
||||
struct trait_trivial_ctor< key_value_pair_t<K, V> >
|
||||
{ enum { value = aggregate_traits<K,V>::has_trivial_ctor }; };
|
||||
template <typename K, typename V>
|
||||
struct trait_trivial_dtor< key_value_pair_t<K, V> >
|
||||
{ enum { value = aggregate_traits<K,V>::has_trivial_dtor }; };
|
||||
template <typename K, typename V>
|
||||
struct trait_trivial_copy< key_value_pair_t<K, V> >
|
||||
{ enum { value = aggregate_traits<K,V>::has_trivial_copy }; };
|
||||
template <typename K, typename V>
|
||||
struct trait_trivial_move< key_value_pair_t<K, V> >
|
||||
{ enum { value = aggregate_traits<K,V>::has_trivial_move }; };
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Hash codes.
|
||||
*/
|
||||
typedef uint32_t hash_t;
|
||||
|
||||
template <typename TKey>
|
||||
hash_t hash_type(const TKey& key);
|
||||
|
||||
/* Built-in hash code specializations */
|
||||
#define ANDROID_INT32_HASH(T) \
|
||||
template <> inline hash_t hash_type(const T& value) { return hash_t(value); }
|
||||
#define ANDROID_INT64_HASH(T) \
|
||||
template <> inline hash_t hash_type(const T& value) { \
|
||||
return hash_t((value >> 32) ^ value); }
|
||||
#define ANDROID_REINTERPRET_HASH(T, R) \
|
||||
template <> inline hash_t hash_type(const T& value) { \
|
||||
R newValue; \
|
||||
static_assert(sizeof(newValue) == sizeof(value), "size mismatch"); \
|
||||
memcpy(&newValue, &value, sizeof(newValue)); \
|
||||
return hash_type(newValue); \
|
||||
}
|
||||
|
||||
ANDROID_INT32_HASH(bool)
|
||||
ANDROID_INT32_HASH(int8_t)
|
||||
ANDROID_INT32_HASH(uint8_t)
|
||||
ANDROID_INT32_HASH(int16_t)
|
||||
ANDROID_INT32_HASH(uint16_t)
|
||||
ANDROID_INT32_HASH(int32_t)
|
||||
ANDROID_INT32_HASH(uint32_t)
|
||||
ANDROID_INT64_HASH(int64_t)
|
||||
ANDROID_INT64_HASH(uint64_t)
|
||||
ANDROID_REINTERPRET_HASH(float, uint32_t)
|
||||
ANDROID_REINTERPRET_HASH(double, uint64_t)
|
||||
|
||||
template <typename T> inline hash_t hash_type(T* const & value) {
|
||||
return hash_type(uintptr_t(value));
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#endif // ANDROID_TYPE_HELPERS_H
|
Loading…
Reference in New Issue