diff --git a/cdcp.cpp b/cdcp.cpp new file mode 100644 index 0000000..801fc11 --- /dev/null +++ b/cdcp.cpp @@ -0,0 +1,12 @@ +#include "cdcp.h" + + +cDCP::cDCP(QObject* parent) : + QObject(parent) +{ +} + +void cDCPList::load(const QString& fileName) +{ + m_fileName = fileName; +} diff --git a/cdcp.h b/cdcp.h new file mode 100644 index 0000000..5329d25 --- /dev/null +++ b/cdcp.h @@ -0,0 +1,34 @@ +#ifndef CDCP_H +#define CDCP_H + + +#include +#include +#include + + +class cDCP : public QObject +{ + Q_OBJECT +public: + explicit cDCP(QObject *parent = nullptr); + +signals: + +public slots: + +private: +}; + +Q_DECLARE_METATYPE(cDCP*) + +class cDCPList : public QList +{ +public: + void load(const QString& fileName); + +private: + QString m_fileName; +}; + +#endif // CDCP_H diff --git a/cimage.cpp b/cimage.cpp index a9b9d50..b5ab7ba 100644 --- a/cimage.cpp +++ b/cimage.cpp @@ -10,6 +10,8 @@ #include #include +#include + cImage::cImage() : QImage() @@ -76,75 +78,181 @@ bool cImage::load(const QString &fileName, const char *format) return(loadRAW(fileName)); } -bool cImage::loadRAW(const QString &fileName) -{ - LibRaw rawProcessor; - libraw_processed_image_t* lpOutput; - - if(rawProcessor.open_file(fileName.toUtf8()) != LIBRAW_SUCCESS) - return(false); +#include - rawProcessor.imgdata.params.use_camera_wb = 0; - rawProcessor.imgdata.params.use_auto_wb = 0; - - if(rawProcessor.unpack() != LIBRAW_SUCCESS) - return(false); - - rawProcessor.dcraw_process(); - lpOutput = rawProcessor.dcraw_make_mem_image(); +QImage LibRawImageToQImage(const uchar *data, const int width, const int height, const int nCols, const int colorBits) +{ + int colorSize = (colorBits % 8) == 0 ? colorBits / 8 : static_cast(ceil(static_cast(colorBits) / 8.0)); + int numPixels = width * height; + int pixelSize = nCols * colorSize; + uchar* pixels = new uchar[numPixels * 3]; - const libraw_data_t& imgdata = rawProcessor.imgdata; - uchar* pixels = nullptr; + for(int i = 0; i < numPixels; i++, data += pixelSize) + { + if(nCols == 3) + { + // this ordering produces correct RGB results - don't ask why + // tested with .CR2 (Canon) + pixels[i * 3] = data[3*colorSize]; + pixels[i * 3 + 1] = data[colorSize]; + pixels[i * 3 + 2] = data[2*colorSize]; + } + else + { + pixels[i * 3] = data[0]; + pixels[i * 3 + 1] = data[0]; + pixels[i * 3 + 2] = data[0]; + } + } + // immediately create a copy since otherwise we'd have to + // 'delete[] pixels' somewhere else, ourselves + // see http://doc.qt.io/qt-5.5/qimage.html#QImage-6 + QImage out = QImage(pixels, width, height, width * 3, QImage::Format_RGB888).copy(); +// QImage out = QImage(pixels, width, height, width * 3, QImage::Format_RGB32).copy(); + delete[] pixels; + return out; +} - if(lpOutput->type == LIBRAW_IMAGE_JPEG) +void LibRawImagePerformFlip(const int flip, QImage& image) +{ + if(flip != 0) { - loadFromData(lpOutput->data, static_cast(lpOutput->data_size), "JPEG"); + QTransform rotation; + int angle = 0; + + if(flip == 3) + angle = 180; + else if(flip == 5) + angle = -90; + else if(flip == 6) + angle = 90; - if(imgdata.sizes.flip != 0) + if (angle != 0) { - QTransform rotation; - int angle = 0; - - if(imgdata.sizes.flip == 3) - angle = 180; - else if(imgdata.sizes.flip == 5) - angle = -90; - else if(imgdata.sizes.flip == 6) - angle = 90; - if(angle != 0) - { - rotation.rotate(angle); - *this = transformed(rotation); - } + rotation.rotate(angle); + image = image.transformed(rotation); } } - else - { - int numPixels = lpOutput->width * lpOutput->height; - int colorSize = lpOutput->bits / 8; - int pixelSize = lpOutput->colors * colorSize; - pixels = new uchar[numPixels * 4]; - uchar* data = lpOutput->data; +} + - for(int i = 0; i < numPixels; i++, data += pixelSize) +bool cImage::loadRAW(const QString &fileName) +{ + LibRaw RawProcessor; + QImage image; + +// RawProcessor.imgdata.params.gamm[0] = 1.00; +// RawProcessor.imgdata.params.gamm[1] = 0.00; +// RawProcessor.imgdata.params.gamm[0] = 0.45; +// RawProcessor.imgdata.params.gamm[1] = 4.50; + RawProcessor.imgdata.params.gamm[0] = 1/2.4; + RawProcessor.imgdata.params.gamm[1] = 12.92; +// RawProcessor.imgdata.params.user_qual = 0; // fastest interpolation (linear) + RawProcessor.imgdata.params.use_camera_wb = 1; + + if(LIBRAW_SUCCESS == RawProcessor.open_file(fileName.toUtf8())) + { + if(LIBRAW_SUCCESS == RawProcessor.unpack()) { - if(lpOutput->colors == 3) + if(LIBRAW_SUCCESS == RawProcessor.dcraw_process()) { - pixels[i * 4] = data[2 * colorSize]; - pixels[i * 4 + 1] = data[1 * colorSize]; - pixels[i * 4 + 2] = data[0]; - } - else - { - pixels[i * 4] = data[0]; - pixels[i * 4 + 1] = data[0]; - pixels[i * 4 + 2] = data[0]; + libraw_processed_image_t* output = RawProcessor.dcraw_make_mem_image(); + + if(LIBRAW_IMAGE_JPEG == output->type) + { + image.loadFromData(static_cast(output->data), static_cast(output->data_size), "JPEG"); + LibRawImagePerformFlip(RawProcessor.imgdata.sizes.flip, image); + } + else if(LIBRAW_IMAGE_BITMAP == output->type) + { + image = LibRawImageToQImage(static_cast(output->data), output->width, output->height, output->colors, output->bits); + } // else: could not read + LibRaw::dcraw_clear_mem(output); } + RawProcessor.recycle(); } - *this = QImage(pixels, lpOutput->width, lpOutput->height, QImage::Format_RGB32); } - - rawProcessor.recycle(); + *this = image; return(true); } + +//bool cImage::loadRAW(const QString &fileName) +//{ +// LibRaw rawProcessor; +// libraw_processed_image_t* lpOutput; + +// rawProcessor.imgdata.params.use_camera_wb = 1; +// rawProcessor.imgdata.params.use_auto_wb = 1; +// rawProcessor.imgdata.params.use_camera_matrix = 0; +//// rawProcessor.imgdata.params.gamm[0] = 1/2.22; +//// rawProcessor.imgdata.params.gamm[1] = 4.50; +// rawProcessor.imgdata.params.gamm[0] = 1/2.40; +// rawProcessor.imgdata.params.gamm[1] = 12.92; + +// if(rawProcessor.open_file(fileName.toUtf8()) != LIBRAW_SUCCESS) +// return(false); + +// if(rawProcessor.unpack() != LIBRAW_SUCCESS) +// return(false); + +// rawProcessor.raw2image(); + +// rawProcessor.dcraw_process(); +// lpOutput = rawProcessor.dcraw_make_mem_image(); + +// const libraw_data_t& imgdata = rawProcessor.imgdata; +// uchar* pixels = nullptr; + +// if(lpOutput->type == LIBRAW_IMAGE_JPEG) +// { +// loadFromData(lpOutput->data, static_cast(lpOutput->data_size), "JPEG"); + +// if(imgdata.sizes.flip != 0) +// { +// QTransform rotation; +// int angle = 0; + +// if(imgdata.sizes.flip == 3) +// angle = 180; +// else if(imgdata.sizes.flip == 5) +// angle = -90; +// else if(imgdata.sizes.flip == 6) +// angle = 90; +// if(angle != 0) +// { +// rotation.rotate(angle); +// *this = transformed(rotation); +// } +// } +// } +// else +// { +// int numPixels = lpOutput->width * lpOutput->height; +// int colorSize = lpOutput->bits / 8; +// int pixelSize = lpOutput->colors * colorSize; +// pixels = new uchar[numPixels * 4]; +// uchar* data = lpOutput->data; + +// for(int i = 0; i < numPixels; i++, data += pixelSize) +// { +// if(lpOutput->colors == 3) +// { +// pixels[i * 4] = data[2 * colorSize]; +// pixels[i * 4 + 1] = data[1 * colorSize]; +// pixels[i * 4 + 2] = data[0]; +// } +// else +// { +// pixels[i * 4] = data[0]; +// pixels[i * 4 + 1] = data[0]; +// pixels[i * 4 + 2] = data[0]; +// } +// } +// *this = QImage(pixels, lpOutput->width, lpOutput->height, QImage::Format_RGB32); +// } + +// rawProcessor.recycle(); + +// return(true); +//} diff --git a/dng/RawEnvironment.h b/dng/RawEnvironment.h new file mode 100644 index 0000000..86a9b0b --- /dev/null +++ b/dng/RawEnvironment.h @@ -0,0 +1,19 @@ +#ifndef __dng_RawEnvironment__ +#define __dng_RawEnvironment__ +// Define preprocessor constants that control platform-specific conditional +// compilation. The constants qMacOS and qWinOS must be defined on all +// platforms. Other constants, such as qLinux, only need to be defined if we're +// actually compiling for that platform. +#if defined(__linux__) +#define qMacOS 0 +#define qWinOS 0 +#define qLinux 1 +#elif defined(__APPLE__) +#define qMacOS 1 +#define qWinOS 0 +#elif defined(_WIN32) +#define qMacOS 0 +#define qWinOS 1 +#endif +#define qDNGXMPDocOps 0 +#endif // __dng_RawEnvironment__ diff --git a/dng/dng_1d_function.cpp b/dng/dng_1d_function.cpp new file mode 100644 index 0000000..07f49da --- /dev/null +++ b/dng/dng_1d_function.cpp @@ -0,0 +1,188 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_1d_function.h" + +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_1d_function::~dng_1d_function () + { + + } + +/*****************************************************************************/ + +bool dng_1d_function::IsIdentity () const + { + + return false; + + } + +/*****************************************************************************/ + +real64 dng_1d_function::EvaluateInverse (real64 y) const + { + + const uint32 kMaxIterations = 30; + const real64 kNearZero = 1.0e-10; + + real64 x0 = 0.0; + real64 y0 = Evaluate (x0); + + real64 x1 = 1.0; + real64 y1 = Evaluate (x1); + + for (uint32 iteration = 0; iteration < kMaxIterations; iteration++) + { + + if (Abs_real64 (y1 - y0) < kNearZero) + { + break; + } + + real64 x2 = Pin_real64 (0.0, + x1 + (y - y1) * (x1 - x0) / (y1 - y0), + 1.0); + + real64 y2 = Evaluate (x2); + + x0 = x1; + y0 = y1; + + x1 = x2; + y1 = y2; + + } + + return x1; + + } + +/*****************************************************************************/ + +bool dng_1d_identity::IsIdentity () const + { + + return true; + + } + +/*****************************************************************************/ + +real64 dng_1d_identity::Evaluate (real64 x) const + { + + return x; + + } + +/*****************************************************************************/ + +real64 dng_1d_identity::EvaluateInverse (real64 x) const + { + + return x; + + } + +/*****************************************************************************/ + +const dng_1d_function & dng_1d_identity::Get () + { + + static dng_1d_identity static_function; + + return static_function; + + } + +/*****************************************************************************/ + +dng_1d_concatenate::dng_1d_concatenate (const dng_1d_function &function1, + const dng_1d_function &function2) + + : fFunction1 (function1) + , fFunction2 (function2) + + { + + } + +/*****************************************************************************/ + +bool dng_1d_concatenate::IsIdentity () const + { + + return fFunction1.IsIdentity () && + fFunction2.IsIdentity (); + + } + +/*****************************************************************************/ + +real64 dng_1d_concatenate::Evaluate (real64 x) const + { + + real64 y = Pin_real64 (0.0, fFunction1.Evaluate (x), 1.0); + + return fFunction2.Evaluate (y); + + } + +/*****************************************************************************/ + +real64 dng_1d_concatenate::EvaluateInverse (real64 x) const + { + + real64 y = fFunction2.EvaluateInverse (x); + + return fFunction1.EvaluateInverse (y); + + } + +/*****************************************************************************/ + +dng_1d_inverse::dng_1d_inverse (const dng_1d_function &f) + + : fFunction (f) + + { + + } + +/*****************************************************************************/ + +bool dng_1d_inverse::IsIdentity () const + { + + return fFunction.IsIdentity (); + + } + +/*****************************************************************************/ + +real64 dng_1d_inverse::Evaluate (real64 x) const + { + + return fFunction.EvaluateInverse (x); + + } + +/*****************************************************************************/ + +real64 dng_1d_inverse::EvaluateInverse (real64 y) const + { + + return fFunction.Evaluate (y); + + } + +/*****************************************************************************/ diff --git a/dng/dng_1d_function.h b/dng/dng_1d_function.h new file mode 100644 index 0000000..600db03 --- /dev/null +++ b/dng/dng_1d_function.h @@ -0,0 +1,154 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Classes for a 1D floating-point to floating-point function abstraction. + */ + +/*****************************************************************************/ + +#ifndef __dng_1d_function__ +#define __dng_1d_function__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_types.h" + +/*****************************************************************************/ + +/// \brief A 1D floating-point function. +/// +/// The domain (input) is always from 0.0 to 1.0, while the range (output) can be an arbitrary interval. + +class dng_1d_function + { + + public: + + virtual ~dng_1d_function (); + + /// Returns true if this function is the map x -> y such that x == y for all x . That is if Evaluate(x) == x for all x. + + virtual bool IsIdentity () const; + + /// Return the mapping for value x. + /// This method must be implemented by a derived class of dng_1d_function and the derived class determines the + /// lookup method and function used. + /// \param x A value between 0.0 and 1.0 (inclusive). + /// \retval Mapped value for x + + virtual real64 Evaluate (real64 x) const = 0; + + /// Return the reverse mapped value for y. + /// This method can be implemented by derived classes. The default implementation uses Newton's method to solve + /// for x such that Evaluate(x) == y. + /// \param y A value to reverse map. Should be within the range of the function implemented by this dng_1d_function . + /// \retval A value x such that Evaluate(x) == y (to very close approximation). + + virtual real64 EvaluateInverse (real64 y) const; + + }; + +/*****************************************************************************/ + +/// An identity (x -> y such that x == y for all x) mapping function. + +class dng_1d_identity: public dng_1d_function + { + + public: + /// Always returns true for this class. + + virtual bool IsIdentity () const; + + /// Always returns x for this class. + + virtual real64 Evaluate (real64 x) const; + + /// Always returns y for this class. + + virtual real64 EvaluateInverse (real64 y) const; + + /// This class is a singleton, and is entirely threadsafe. Use this method to get an instance of the class. + + static const dng_1d_function & Get (); + + }; + +/*****************************************************************************/ + +/// A dng_1d_function that represents the composition (curry) of two other dng_1d_functions. + +class dng_1d_concatenate: public dng_1d_function + { + + protected: + + const dng_1d_function &fFunction1; + + const dng_1d_function &fFunction2; + + public: + + /// Create a dng_1d_function which computes y = function2.Evaluate(function1.Evaluate(x)). + /// Compose function1 and function2 to compute y = function2.Evaluate(function1.Evaluate(x)). The range of function1.Evaluate must be a subset of 0.0 to 1.0 inclusive, + /// otherwise the result of function1(x) will be pinned (clipped) to 0.0 if <0.0 and to 1.0 if > 1.0 . + /// \param function1 Inner function of composition. + /// \param function2 Outer function of composition. + + dng_1d_concatenate (const dng_1d_function &function1, + const dng_1d_function &function2); + + /// Only true if both function1 and function2 have IsIdentity equal to true. + + virtual bool IsIdentity () const; + + /// Return the composed mapping for value x. + /// \param x A value between 0.0 and 1.0 (inclusive). + /// \retval function2.Evaluate(function1.Evaluate(x)). + + virtual real64 Evaluate (real64 x) const; + + /// Return the reverse mapped value for y. + /// Be careful using this method with compositions where the inner function does not have a range 0.0 to 1.0 . (Or better yet, do not use such functions.) + /// \param y A value to reverse map. Should be within the range of function2.Evaluate. + /// \retval A value x such that function2.Evaluate(function1.Evaluate(x)) == y (to very close approximation). + + virtual real64 EvaluateInverse (real64 y) const; + + }; + +/*****************************************************************************/ + +/// A dng_1d_function that represents the inverse of another dng_1d_function. + +class dng_1d_inverse: public dng_1d_function + { + + protected: + + const dng_1d_function &fFunction; + + public: + + dng_1d_inverse (const dng_1d_function &f); + + virtual bool IsIdentity () const; + + virtual real64 Evaluate (real64 x) const; + + virtual real64 EvaluateInverse (real64 y) const; + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_1d_table.cpp b/dng/dng_1d_table.cpp new file mode 100644 index 0000000..d9cb818 --- /dev/null +++ b/dng/dng_1d_table.cpp @@ -0,0 +1,196 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_1d_table.h" + +#include "dng_1d_function.h" +#include "dng_assertions.h" +#include "dng_memory.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_1d_table::dng_1d_table (uint32 count) + + : fBuffer () + , fTable (NULL) + , fTableCount (count) + + { + + DNG_REQUIRE (count >= kMinTableSize, + "count must be at least kMinTableSize"); + + DNG_REQUIRE ((count & (count - 1)) == 0, + "count must be power of 2"); + + } + +/*****************************************************************************/ + +dng_1d_table::~dng_1d_table () + { + + } + +/*****************************************************************************/ + +void dng_1d_table::SubDivide (const dng_1d_function &function, + uint32 lower, + uint32 upper, + real32 maxDelta) + { + + uint32 range = upper - lower; + + bool subDivide = (range > (fTableCount >> 8)); + + if (!subDivide) + { + + real32 delta = Abs_real32 (fTable [upper] - + fTable [lower]); + + if (delta > maxDelta) + { + + subDivide = true; + + } + + } + + if (subDivide) + { + + uint32 middle = (lower + upper) >> 1; + + fTable [middle] = (real32) function.Evaluate (middle * (1.0 / (real64) fTableCount)); + + if (range > 2) + { + + SubDivide (function, lower, middle, maxDelta); + + SubDivide (function, middle, upper, maxDelta); + + } + + } + + else + { + + real64 y0 = fTable [lower]; + real64 y1 = fTable [upper]; + + real64 delta = (y1 - y0) / (real64) range; + + for (uint32 j = lower + 1; j < upper; j++) + { + + y0 += delta; + + fTable [j] = (real32) y0; + + } + + } + + } + +/*****************************************************************************/ + +void dng_1d_table::Initialize (dng_memory_allocator &allocator, + const dng_1d_function &function, + bool subSample) + { + + fBuffer.Reset (allocator.Allocate ((fTableCount + 2) * sizeof (real32))); + + fTable = fBuffer->Buffer_real32 (); + + if (subSample) + { + + fTable [0 ] = (real32) function.Evaluate (0.0); + fTable [fTableCount] = (real32) function.Evaluate (1.0); + + real32 maxDelta = Max_real32 (Abs_real32 (fTable [fTableCount] - + fTable [0 ]), 1.0f) * + (1.0f / 256.0f); + + SubDivide (function, + 0, + fTableCount, + maxDelta); + + } + + else + { + + for (uint32 j = 0; j <= fTableCount; j++) + { + + real64 x = j * (1.0 / (real64) fTableCount); + + real64 y = function.Evaluate (x); + + fTable [j] = (real32) y; + + } + + } + + fTable [fTableCount + 1] = fTable [fTableCount]; + + } + +/*****************************************************************************/ + +void dng_1d_table::Expand16 (uint16 *table16) const + { + + real64 step = (real64) fTableCount / 65535.0; + + real64 y0 = fTable [0]; + real64 y1 = fTable [1]; + + real64 base = y0 * 65535.0 + 0.5; + real64 slope = (y1 - y0) * 65535.0; + + uint32 index = 1; + real64 fract = 0.0; + + for (uint32 j = 0; j < 0x10000; j++) + { + + table16 [j] = (uint16) (base + slope * fract); + + fract += step; + + if (fract > 1.0) + { + + index += 1; + fract -= 1.0; + + y0 = y1; + y1 = fTable [index]; + + base = y0 * 65535.0 + 0.5; + slope = (y1 - y0) * 65535.0; + + } + + } + + } + +/*****************************************************************************/ diff --git a/dng/dng_1d_table.h b/dng/dng_1d_table.h new file mode 100644 index 0000000..e698fd1 --- /dev/null +++ b/dng/dng_1d_table.h @@ -0,0 +1,126 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Definition of a lookup table based 1D floating-point to floating-point function abstraction using linear interpolation. + */ + +/*****************************************************************************/ + +#ifndef __dng_1d_table__ +#define __dng_1d_table__ + +/*****************************************************************************/ + +#include "dng_assertions.h" +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_types.h" +#include "dng_uncopyable.h" + +/*****************************************************************************/ + +/// \brief A 1D floating-point lookup table using linear interpolation. + +class dng_1d_table: private dng_uncopyable + { + + public: + + /// Constant denoting minimum size of table. + + static const uint32 kMinTableSize = 512; + + private: + + /// Constant denoting default size of table. + + static const uint32 kDefaultTableSize = 4096; + + protected: + + AutoPtr fBuffer; + + real32 *fTable; + + const uint32 fTableCount; + + public: + + /// Table constructor. count must be a power of two + /// and at least kMinTableSize. + + explicit dng_1d_table (uint32 count = kDefaultTableSize); + + virtual ~dng_1d_table (); + + /// Number of table entries. + + uint32 Count () const + { + return fTableCount; + } + + /// Set up table, initialize entries using functiion. + /// This method can throw an exception, e.g. if there is not enough memory. + /// \param allocator Memory allocator from which table memory is allocated. + /// \param function Table is initialized with values of finction.Evalluate(0.0) to function.Evaluate(1.0). + /// \param subSample If true, only sample the function a limited number of times and interpolate. + + void Initialize (dng_memory_allocator &allocator, + const dng_1d_function &function, + bool subSample = false); + + /// Lookup and interpolate mapping for an input. + /// \param x value from 0.0 to 1.0 used as input for mapping + /// \retval Approximation of function.Evaluate(x) + + real32 Interpolate (real32 x) const + { + + real32 y = x * (real32) fTableCount; + + int32 index = (int32) y; + + // Enable vectorization by using DNG_ASSERT instead of DNG_REQUIRE + DNG_ASSERT(!(index < 0 || index >(int32) fTableCount), "dng_1d_table::Interpolate parameter out of range"); + + real32 z = (real32) index; + + real32 fract = y - z; + + return fTable [index ] * (1.0f - fract) + + fTable [index + 1] * ( fract); + + } + + /// Direct access function for table data. + + const real32 * Table () const + { + return fTable; + } + + /// Expand the table to a 16-bit to 16-bit table. + + void Expand16 (uint16 *table16) const; + + private: + + void SubDivide (const dng_1d_function &function, + uint32 lower, + uint32 upper, + real32 maxDelta); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_abort_sniffer.cpp b/dng/dng_abort_sniffer.cpp new file mode 100644 index 0000000..ebd47a6 --- /dev/null +++ b/dng/dng_abort_sniffer.cpp @@ -0,0 +1,316 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_abort_sniffer.h" +#include "dng_assertions.h" + +#include "dng_mutex.h" + +/*****************************************************************************/ + +#if qDNGThreadSafe + +/*****************************************************************************/ + +// TO DO: This priority-based wait mechanism is not compatible with thread +// pools. Putting worker threads to sleep may result in deadlock because +// higher priority work may not make progress (the pool may not be able to +// spin up any new threads). + +class dng_priority_manager + { + + private: + + // Use lower-level mutex and condition_variable for priority manager + // since we don't want to include these in our priority tracking. + + dng_mutex fMutex; + + dng_condition fCondition; + + uint32 fCounter [dng_priority_count]; + + public: + + dng_priority_manager (); + + void Increment (dng_priority priority, + const char *name); + + void Decrement (dng_priority priority, + const char *name); + + void Wait (dng_abort_sniffer *sniffer); + + private: + + dng_priority MinPriority () + { + + // Assumes mutex is locked. + + for (uint32 level = dng_priority_maximum; + level > dng_priority_minimum; + level--) + { + + if (fCounter [level]) + { + return (dng_priority) level; + } + + } + + return dng_priority_minimum; + + } + + }; + +/*****************************************************************************/ + +dng_priority_manager::dng_priority_manager () + + : fMutex ("dng_priority_manager::fMutex") + , fCondition () + + { + + for (uint32 level = dng_priority_minimum; + level <= dng_priority_maximum; + level++) + { + + fCounter [level] = 0; + + } + + } + +/*****************************************************************************/ + +void dng_priority_manager::Increment (dng_priority priority, + const char *name) + { + + dng_lock_mutex lock (&fMutex); + + fCounter [priority] += 1; + + #if 0 + + printf ("increment priority %d (%s) (%d, %d, %d)\n", + (int) priority, + name, + fCounter [dng_priority_low], + fCounter [dng_priority_medium], + fCounter [dng_priority_high]); + + #else + + (void) name; + + #endif + + } + +/*****************************************************************************/ + +void dng_priority_manager::Decrement (dng_priority priority, + const char *name) + { + + dng_priority oldMin = dng_priority_minimum; + dng_priority newMin = dng_priority_minimum; + + { + + dng_lock_mutex lock (&fMutex); + + oldMin = MinPriority (); + + fCounter [priority] -= 1; + + newMin = MinPriority (); + + #if 0 + + printf ("decrement priority %d (%s) (%d, %d, %d)\n", + (int) priority, + name, + fCounter [dng_priority_low], + fCounter [dng_priority_medium], + fCounter [dng_priority_high]); + + #else + + (void) name; + + #endif + + } + + if (newMin < oldMin) + { + + fCondition.Broadcast (); + + } + + } + +/*****************************************************************************/ + +void dng_priority_manager::Wait (dng_abort_sniffer *sniffer) + { + + if (!sniffer) + { + return; + } + + const dng_priority priority = sniffer->Priority (); + + if (priority < dng_priority_maximum) + { + + dng_lock_mutex lock (&fMutex); + + while (priority < MinPriority ()) + { + + fCondition.Wait (fMutex); + + } + + } + + } + +/*****************************************************************************/ + +static dng_priority_manager gPriorityManager; + +/*****************************************************************************/ + +#endif // qDNGThreadSafe + +/*****************************************************************************/ + +dng_set_minimum_priority::dng_set_minimum_priority (dng_priority priority, + const char *name) + + : fPriority (priority) + + { + + #if qDNGThreadSafe + + gPriorityManager.Increment (fPriority, name); + + #endif + + fName.Set (name); + + } + +/*****************************************************************************/ + +dng_set_minimum_priority::~dng_set_minimum_priority () + { + + #if qDNGThreadSafe + + gPriorityManager.Decrement (fPriority, fName.Get ()); + + #endif + + } + +/*****************************************************************************/ + +dng_abort_sniffer::dng_abort_sniffer () + + : fPriority (dng_priority_maximum) + + { + + } + +/*****************************************************************************/ + +dng_abort_sniffer::~dng_abort_sniffer () + { + + } + +/*****************************************************************************/ + +void dng_abort_sniffer::SetPriority (dng_priority priority) + { + + fPriority = priority; + + } + +/*****************************************************************************/ + +bool dng_abort_sniffer::SupportsPriorityWait () const + { + return false; + } + +/*****************************************************************************/ + +void dng_abort_sniffer::SniffForAbort (dng_abort_sniffer *sniffer) + { + + if (sniffer) + { + + #if qDNGThreadSafe + + if (sniffer->SupportsPriorityWait ()) + { + + gPriorityManager.Wait (sniffer); + + } + + #endif + + sniffer->Sniff (); + + } + + } + +/*****************************************************************************/ + +void dng_abort_sniffer::StartTask (const char * /* name */, + real64 /* fract */) + { + + } + +/*****************************************************************************/ + +void dng_abort_sniffer::EndTask () + { + + } + +/*****************************************************************************/ + +void dng_abort_sniffer::UpdateProgress (real64 /* fract */) + { + + } + +/*****************************************************************************/ diff --git a/dng/dng_abort_sniffer.h b/dng/dng_abort_sniffer.h new file mode 100644 index 0000000..8402772 --- /dev/null +++ b/dng/dng_abort_sniffer.h @@ -0,0 +1,241 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Classes supporting user cancellation and progress tracking. + */ + +/*****************************************************************************/ + +#ifndef __dng_abort_sniffer__ +#define __dng_abort_sniffer__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_flags.h" +#include "dng_string.h" +#include "dng_types.h" +#include "dng_uncopyable.h" + +/*****************************************************************************/ + +/// \brief Thread priority level. + +enum dng_priority + { + + dng_priority_low, + dng_priority_medium, + dng_priority_high, + + dng_priority_count, + + dng_priority_minimum = dng_priority_low, + dng_priority_maximum = dng_priority_high + + }; + +/*****************************************************************************/ + +/// \brief Convenience class for setting thread priority level to minimum. + +class dng_set_minimum_priority + { + + private: + + dng_priority fPriority; + + dng_string fName; + + public: + + dng_set_minimum_priority (dng_priority priority, + const char *name); + + ~dng_set_minimum_priority (); + + }; + +/*****************************************************************************/ + +/** \brief Class for signaling user cancellation and receiving progress updates. + * + * DNG SDK clients should derive a host application specific implementation + * from this class. + */ + +class dng_abort_sniffer + { + + friend class dng_sniffer_task; + + private: + + dng_priority fPriority; + + public: + + dng_abort_sniffer (); + + virtual ~dng_abort_sniffer (); + + /// Getter for priority level. + + dng_priority Priority () const + { + return fPriority; + } + + /// Setter for priority level. + + void SetPriority (dng_priority priority); + + /// Check for pending user cancellation or other abort. ThrowUserCanceled + /// will be called if one is pending. This static method is provided as a + /// convenience for quickly testing for an abort and throwing an exception + /// if one is pending. + /// \param sniffer The dng_sniffer to test for a pending abort. Can be NULL, + /// in which case there an abort is never signalled. + + static void SniffForAbort (dng_abort_sniffer *sniffer); + + // A way to call Sniff while bypassing the priority wait. + + void SniffNoPriorityWait () + { + Sniff (); + } + + // Specifies whether or not the sniffer may be called by multiple threads + // in parallel. Default result is false. Subclass must override to return + // true. + + virtual bool ThreadSafe () const + { + return false; + } + + // Specifies whether or not this sniffer may participate in + // priority-based waiting (sleep the current thread on which + // SniffForAbort is called, if another thread has higher priority). + // Default result is false. Subclass must override to return true. + + virtual bool SupportsPriorityWait () const; + + protected: + + /// Should be implemented by derived classes to check for an user + /// cancellation. + + virtual void Sniff () = 0; + + /// Signals the start of a named task withn processing in the DNG SDK. + /// Tasks may be nested. + /// \param name of the task + /// \param fract Percentage of total processing this task is expected to + /// take. From 0.0 to 1.0 . + + virtual void StartTask (const char *name, + real64 fract); + + /// Signals the end of the innermost task that has been started. + + virtual void EndTask (); + + /// Signals progress made on current task. + /// \param fract percentage of processing completed on current task. + /// From 0.0 to 1.0 . + + virtual void UpdateProgress (real64 fract); + + }; + +/******************************************************************************/ + +/// \brief Class to establish scope of a named subtask in DNG processing. +/// +/// Instances of this class are intended to be stack allocated. + +class dng_sniffer_task: private dng_uncopyable + { + + private: + + dng_abort_sniffer *fSniffer; + + public: + + /// Inform a sniffer of a subtask in DNG processing. + /// \param sniffer The sniffer associated with the host on which this + /// processing is occurring. + /// \param name The name of this subtask as a NUL terminated string. + /// \param fract Percentage of total processing this task is expected + /// to take, from 0.0 to 1.0 . + + dng_sniffer_task (dng_abort_sniffer *sniffer, + const char *name = NULL, + real64 fract = 0.0) + + : fSniffer (sniffer) + + { + if (fSniffer) + fSniffer->StartTask (name, fract); + } + + ~dng_sniffer_task () + { + if (fSniffer) + fSniffer->EndTask (); + } + + /// Check for pending user cancellation or other abort. ThrowUserCanceled + /// will be called if one is pending. + + void Sniff () + { + dng_abort_sniffer::SniffForAbort (fSniffer); + } + + /// Update progress on this subtask. + /// \param fract Percentage of processing completed on current task, + /// from 0.0 to 1.0 . + + void UpdateProgress (real64 fract) + { + if (fSniffer) + fSniffer->UpdateProgress (fract); + } + + /// Update progress on this subtask. + /// \param done Amount of task completed in arbitrary integer units. + /// \param total Total size of task in same arbitrary integer units as done. + + void UpdateProgress (uint32 done, + uint32 total) + { + UpdateProgress ((real64) done / + (real64) total); + } + + /// Signal task completed for progress purposes. + + void Finish () + { + UpdateProgress (1.0); + } + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_area_task.cpp b/dng/dng_area_task.cpp new file mode 100644 index 0000000..95f36b9 --- /dev/null +++ b/dng/dng_area_task.cpp @@ -0,0 +1,324 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_area_task.h" + +#include "dng_abort_sniffer.h" +#include "dng_auto_ptr.h" +#include "dng_flags.h" +#include "dng_globals.h" +#include "dng_sdk_limits.h" +#include "dng_tile_iterator.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_area_task::dng_area_task (const char *name) + + : fMaxThreads (kMaxMPThreads) + + , fMinTaskArea (256 * 256) + + , fUnitCell (1, 1) + + , fMaxTileSize (256, 256) + + , fName () + + { + + if (!name) + { + name = "dng_area_task"; + } + + fName.Set (name); + + } + +/*****************************************************************************/ + +dng_area_task::~dng_area_task () + { + + } + +/*****************************************************************************/ + +dng_rect dng_area_task::RepeatingTile1 () const + { + + return dng_rect (); + + } + +/*****************************************************************************/ + +dng_rect dng_area_task::RepeatingTile2 () const + { + + return dng_rect (); + + } + +/*****************************************************************************/ + +dng_rect dng_area_task::RepeatingTile3 () const + { + + return dng_rect (); + + } + +/*****************************************************************************/ + +void dng_area_task::Start (uint32 /* threadCount */, + const dng_rect & /* dstArea */, + const dng_point & /* tileSize */, + dng_memory_allocator * /* allocator */, + dng_abort_sniffer * /* sniffer */) + { + + } + +/*****************************************************************************/ + +void dng_area_task::Finish (uint32 /* threadCount */) + { + + } + +/*****************************************************************************/ + +dng_point dng_area_task::FindTileSize (const dng_rect &area) const + { + + dng_rect repeatingTile1 = RepeatingTile1 (); + dng_rect repeatingTile2 = RepeatingTile2 (); + dng_rect repeatingTile3 = RepeatingTile3 (); + + if (repeatingTile1.IsEmpty ()) + { + repeatingTile1 = area; + } + + if (repeatingTile2.IsEmpty ()) + { + repeatingTile2 = area; + } + + if (repeatingTile3.IsEmpty ()) + { + repeatingTile3 = area; + } + + uint32 repeatV = Min_uint32 (Min_uint32 (repeatingTile1.H (), + repeatingTile2.H ()), + repeatingTile3.H ()); + + uint32 repeatH = Min_uint32 (Min_uint32 (repeatingTile1.W (), + repeatingTile2.W ()), + repeatingTile3.W ()); + + dng_point maxTileSize = MaxTileSize (); + + dng_point tileSize; + + tileSize.v = Min_int32 (repeatV, maxTileSize.v); + tileSize.h = Min_int32 (repeatH, maxTileSize.h); + + // Make Xcode happy (div by zero). + + tileSize.v = Max_int32 (tileSize.v, 1); + tileSize.h = Max_int32 (tileSize.h, 1); + + // What this is doing is, if the smallest repeating image tile is larger than the + // maximum tile size, adjusting the tile size down so that the tiles are as small + // as possible while still having the same number of tiles covering the + // repeat area. This makes the areas more equal in size, making MP + // algorithms work better. + + // The image core team did not understand this code, and disabled it. + // Really stupid idea to turn off code you don't understand! + // I'm undoing this removal, because I think the code is correct and useful. + + uint32 countV = (repeatV + tileSize.v - 1) / tileSize.v; + uint32 countH = (repeatH + tileSize.h - 1) / tileSize.h; + + // Make Xcode happy (div by zero). + + countV = Max_uint32 (countV, 1); + countH = Max_uint32 (countH, 1); + + tileSize.v = (repeatV + countV - 1) / countV; + tileSize.h = (repeatH + countH - 1) / countH; + + // Round up to unit cell size. + + dng_point unitCell = UnitCell (); + + if (unitCell.h != 1 || unitCell.v != 1) + { + tileSize.v = ((tileSize.v + unitCell.v - 1) / unitCell.v) * unitCell.v; + tileSize.h = ((tileSize.h + unitCell.h - 1) / unitCell.h) * unitCell.h; + } + + // But if that is larger than maximum tile size, round down to unit cell size. + + if (tileSize.v > maxTileSize.v) + { + tileSize.v = (maxTileSize.v / unitCell.v) * unitCell.v; + } + + if (tileSize.h > maxTileSize.h) + { + tileSize.h = (maxTileSize.h / unitCell.h) * unitCell.h; + } + + if (gPrintTimings) + { + fprintf (stdout, + "\nRender tile for below: %d x %d\n", + (int32) tileSize.h, + (int32) tileSize.v); + } + + return tileSize; + + } + +/*****************************************************************************/ + +void dng_area_task::ProcessOnThread (uint32 threadIndex, + const dng_rect &area, + const dng_point &tileSize, + dng_abort_sniffer *sniffer, + dng_area_task_progress *progress) + { + + dng_rect repeatingTile1 = RepeatingTile1 (); + dng_rect repeatingTile2 = RepeatingTile2 (); + dng_rect repeatingTile3 = RepeatingTile3 (); + + if (repeatingTile1.IsEmpty ()) + { + repeatingTile1 = area; + } + + if (repeatingTile2.IsEmpty ()) + { + repeatingTile2 = area; + } + + if (repeatingTile3.IsEmpty ()) + { + repeatingTile3 = area; + } + + dng_rect tile1; + + // TODO_EP: Review & document case where these dynamic allocations appeared to have significant overhead + AutoPtr iter1 + (MakeTileIterator (threadIndex, + repeatingTile3, + area)); + + while (iter1->GetOneTile (tile1)) + { + + dng_rect tile2; + + AutoPtr iter2 + (MakeTileIterator (threadIndex, + repeatingTile2, + tile1)); + + while (iter2->GetOneTile (tile2)) + { + + dng_rect tile3; + + AutoPtr iter3 + (MakeTileIterator (threadIndex, + repeatingTile1, + tile2)); + + while (iter3->GetOneTile (tile3)) + { + + dng_rect tile4; + + AutoPtr iter4 + (MakeTileIterator (threadIndex, + tileSize, + tile3)); + + while (iter4->GetOneTile (tile4)) + { + + dng_abort_sniffer::SniffForAbort (sniffer); + + Process (threadIndex, tile4, sniffer); + + if (progress) + { + progress->FinishedTile (tile4); + } + + } + + } + + } + + } + + } + +/*****************************************************************************/ + +dng_base_tile_iterator * dng_area_task::MakeTileIterator (uint32 /* threadIndex */, + const dng_rect &tile, + const dng_rect &area) const + { + + return new dng_tile_forward_iterator (tile, area); + + } + +/*****************************************************************************/ + +dng_base_tile_iterator * dng_area_task::MakeTileIterator (uint32 /* threadIndex */, + const dng_point &tileSize, + const dng_rect &area) const + { + + return new dng_tile_forward_iterator (tileSize, area); + + } + +/*****************************************************************************/ + +void dng_area_task::Perform (dng_area_task &task, + const dng_rect &area, + dng_memory_allocator *allocator, + dng_abort_sniffer *sniffer, + dng_area_task_progress *progress) + { + + dng_point tileSize (task.FindTileSize (area)); + + task.Start (1, area, tileSize, allocator, sniffer); + + task.ProcessOnThread (0, area, tileSize, sniffer, progress); + + task.Finish (1); + + } + +/*****************************************************************************/ diff --git a/dng/dng_area_task.h b/dng/dng_area_task.h new file mode 100644 index 0000000..693f461 --- /dev/null +++ b/dng/dng_area_task.h @@ -0,0 +1,286 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Class to handle partitioning a rectangular image processing operation taking + * into account multiple processing resources and memory constraints. + */ + +/*****************************************************************************/ + +#ifndef __dng_area_task__ +#define __dng_area_task__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_point.h" +#include "dng_string.h" +#include "dng_types.h" +#include "dng_uncopyable.h" + +/*****************************************************************************/ + +class dng_area_task_progress: private dng_uncopyable + { + + public: + + virtual ~dng_area_task_progress () + { + } + + virtual void FinishedTile (const dng_rect & /* tile */) = 0; + + }; + +/*****************************************************************************/ + +/// \brief Abstract class for rectangular processing operations with support +/// for partitioning across multiple processing resources and observing memory +/// constraints. + +class dng_area_task + { + + protected: + + uint32 fMaxThreads; + + uint32 fMinTaskArea; + + dng_point fUnitCell; + + dng_point fMaxTileSize; + + dng_string fName; + + public: + + explicit dng_area_task (const char *name = "unnamed dng_area_task"); + + virtual ~dng_area_task (); + + const char * Name () const + { + return fName.Get (); + } + + /// Getter for the maximum number of threads (resources) that can be + /// used for processing + /// + /// \retval Number of threads, minimum of 1, that can be used for this task. + + virtual uint32 MaxThreads () const + { + return fMaxThreads; + } + + /// Getter for minimum area of a partitioned rectangle. + /// Often it is not profitable to use more resources if it requires + /// partitioning the input into chunks that are too small, as the + /// overhead increases more than the speedup. This method can be + /// ovreridden for a specific task to indicate the smallest area for + /// partitioning. Default is 256x256 pixels. + /// + /// \retval Minimum area for a partitoned tile in order to give performant + /// operation. (Partitions can be smaller due to small inputs and edge cases.) + + virtual uint32 MinTaskArea () const + { + return fMinTaskArea; + } + + /// Getter for dimensions of which partitioned tiles should be a multiple. + /// Various methods of processing prefer certain alignments. The + /// partitioning attempts to construct tiles such that the sizes are a + /// multiple of the dimensions of this point. + /// + /// \retval a point giving preferred alignment in x and y + + virtual dng_point UnitCell () const + { + return fUnitCell; + } + + /// Getter for maximum size of a tile for processing. + /// Often processing will need to allocate temporary buffers or use + /// other resources that are either fixed or in limited supply. The + /// maximum tile size forces further partitioning if the tile is bigger + /// than this size. + /// + /// \retval Maximum tile size allowed for this area task. + + virtual dng_point MaxTileSize () const + { + return fMaxTileSize; + } + + /// Getter for RepeatingTile1. + /// RepeatingTile1, RepeatingTile2, and RepeatingTile3 are used to + /// establish a set of 0 to 3 tile patterns for which the resulting + /// partitions that the final Process method is called on will not cross + /// tile boundaries in any of the tile patterns. This can be used for a + /// processing routine that needs to read from two tiles and write to a + /// third such that all the tiles are aligned and sized in a certain + /// way. A RepeatingTile value is valid if it is non-empty. Higher + /// numbered RepeatingTile patterns are only used if all lower ones are + /// non-empty. A RepeatingTile pattern must be a multiple of UnitCell in + /// size for all constraints of the partitionerr to be met. + + virtual dng_rect RepeatingTile1 () const; + + /// Getter for RepeatingTile2. + /// RepeatingTile1, RepeatingTile2, and RepeatingTile3 are used to + /// establish a set of 0 to 3 tile patterns for which the resulting + /// partitions that the final Process method is called on will not cross + /// tile boundaries in any of the tile patterns. This can be used for a + /// processing routine that needs to read from two tiles and write to a + /// third such that all the tiles are aligned and sized in a certain + /// way. A RepeatingTile value is valid if it is non-empty. Higher + /// numbered RepeatingTile patterns are only used if all lower ones are + /// non-empty. A RepeatingTile pattern must be a multiple of UnitCell in + /// size for all constraints of the partitionerr to be met. + + virtual dng_rect RepeatingTile2 () const; + + /// Getter for RepeatingTile3. + /// RepeatingTile1, RepeatingTile2, and RepeatingTile3 are used to + /// establish a set of 0 to 3 tile patterns for which the resulting + /// partitions that the final Process method is called on will not cross + /// tile boundaries in any of the tile patterns. This can be used for a + /// processing routine that needs to read from two tiles and write to a + /// third such that all the tiles are aligned and sized in a certain + /// way. A RepeatingTile value is valid if it is non-empty. Higher + /// numbered RepeatingTile patterns are only used if all lower ones are + /// non-empty. A RepeatingTile pattern must be a multiple of UnitCell in + /// size for all constraints of the partitionerr to be met. + + virtual dng_rect RepeatingTile3 () const; + + /// Task startup method called before any processing is done on partitions. + /// The Start method is called before any processing is done and can be + /// overridden to allocate temporary buffers, etc. + /// + /// \param threadCount Total number of threads that will be used for processing. + /// Less than or equal to MaxThreads. + /// \param dstArea Area to be processed in the current run of the task. + /// \param tileSize Size of source tiles which will be processed. + /// (Not all tiles will be this size due to edge conditions.) + /// \param allocator dng_memory_allocator to use for allocating temporary buffers, etc. + /// \param sniffer Sniffer to test for user cancellation and to set up progress. + + virtual void Start (uint32 threadCount, + const dng_rect &dstArea, + const dng_point &tileSize, + dng_memory_allocator *allocator, + dng_abort_sniffer *sniffer); + + /// Process one tile or fully partitioned area. This method is + /// overridden by derived classes to implement the actual image + /// processing. Note that the sniffer can be ignored if it is certain + /// that a processing task will complete very quickly. This method + /// should never be called directly but rather accessed via Process. + /// There is no allocator parameter as all allocation should be done in + /// Start. + /// + /// \param threadIndex 0 to threadCount - 1 index indicating which thread this is. + /// (Can be used to get a thread-specific buffer allocated in the Start method.) + /// \param tile Area to process. + /// \param sniffer dng_abort_sniffer to use to check for user cancellation + /// and progress updates. + + virtual void Process (uint32 threadIndex, + const dng_rect &tile, + dng_abort_sniffer *sniffer) = 0; + + /// Task computation finalization and teardown method. Called after all + /// resources have completed processing. Can be overridden to accumulate + /// results and free resources allocated in Start. + /// + /// \param threadCount Number of threads used for processing. Same as value passed to Start. + + virtual void Finish (uint32 threadCount); + + /// Find tile size taking into account repeating tiles, unit cell, and maximum tile size. + /// \param area Computation area for which to find tile size. + /// \retval Tile size as height and width in point. + + dng_point FindTileSize (const dng_rect &area) const; + + /// Handle one resource's worth of partitioned tiles. Called after + /// thread partitioning has already been done. Area may be further + /// subdivided to handle maximum tile size, etc. It will be rare to + /// override this method. + /// + /// \param threadIndex 0 to threadCount - 1 index indicating which thread this is. + /// \param area Tile area partitioned to this resource. + /// \param tileSize size of tiles to use for processing. + /// \param sniffer dng_abort_sniffer to use to check for user cancellation and progress updates. + /// \param progress optional pointer to progress reporting object. + + void ProcessOnThread (uint32 threadIndex, + const dng_rect &area, + const dng_point &tileSize, + dng_abort_sniffer *sniffer, + dng_area_task_progress *progress); + + /// Factory method to make a tile iterator. This iterator will be used + /// by a thread to process tiles in an area in a specific order. The + /// default implementation uses a forward iterator that visits tiles + /// from left to right (inner), top down (outer). Subclasses can + /// override this method to produce tile iterators that visit tiles in + /// different orders. + /// + /// \param threadIndex 0 to threadCount - 1 index indicating which thread this is. + /// \param tile The tile to be traversed within the tile area. + /// \param area Tile area partitioned to this resource. + + virtual dng_base_tile_iterator * MakeTileIterator (uint32 threadIndex, + const dng_rect &tile, + const dng_rect &area) const; + + /// Factory method to make a tile iterator. This iterator will be used + /// by a thread to process tiles in an area in a specific order. The + /// default implementation uses a forward iterator that visits tiles + /// from left to right (inner), top down (outer). Subclasses can + /// override this method to produce tile iterators that visit tiles in + /// different orders. + /// + /// \param threadIndex 0 to threadCount - 1 index indicating which thread this is. + /// \param tileSize The tile size to be traversed within the tile area. + /// \param area Tile area partitioned to this resource. + + virtual dng_base_tile_iterator * MakeTileIterator (uint32 threadIndex, + const dng_point &tileSize, + const dng_rect &area) const; + + /// Default resource partitioner that assumes a single resource to be + /// used for processing. Implementations that are aware of multiple + /// processing resources should override (replace) this method. This is + /// usually done in dng_host::PerformAreaTask. + /// + /// \param task The task to perform. + /// \param area The area on which mage processing should be performed. + /// \param allocator dng_memory_allocator to use for allocating temporary buffers, etc. + /// \param sniffer dng_abort_sniffer to use to check for user cancellation and progress updates. + /// \param progress optional pointer to progress reporting object. + + static void Perform (dng_area_task &task, + const dng_rect &area, + dng_memory_allocator *allocator, + dng_abort_sniffer *sniffer, + dng_area_task_progress *progress); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_assertions.h b/dng/dng_assertions.h new file mode 100644 index 0000000..4a34612 --- /dev/null +++ b/dng/dng_assertions.h @@ -0,0 +1,128 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Conditionally compiled assertion check support. + */ + +/*****************************************************************************/ + +#ifndef __dng_assertions__ +#define __dng_assertions__ + +/*****************************************************************************/ + +#include "dng_exceptions.h" +#include "dng_flags.h" + +/*****************************************************************************/ + +#if qDNGDebug + +/// Platform-specific function to display an assert. + +void dng_show_message (const char *s); + +/// Show a formatted error message. + +void dng_show_message_f (const char *fmt, ...); + +#endif + +/*****************************************************************************/ + +#ifndef DNG_ASSERT + +#if qDNGDebug + +/// Conditionally compiled macro to check an assertion and display a message if +/// it fails and assertions are compiled in via qDNGDebug +/// \param x Predicate which must be true. +/// \param y String to display if x is not true. + +#define DNG_ASSERT(x,y) { if (!(x)) dng_show_message (y); } + +#else + +/// Conditionally compiled macro to check an assertion and display a message if +/// it fails and assertions are compiled in via qDNGDebug +/// \param x Predicate which must be true. +/// \param y String to display if x is not true. + +#define DNG_ASSERT(x,y) + +#endif +#endif + +/*****************************************************************************/ + +#ifndef DNG_REQUIRE + +#if qDNGDebug + +/// Conditionally compiled macro to check an assertion, display a message, and throw +/// an exception if it fails and assertions are compiled in via qDNGDebug +/// \param condition Predicate which must be true. +/// \param msg String to display if condition is not true. + +#define DNG_REQUIRE(condition,msg) \ + do \ + { \ + \ + if (!(condition)) \ + { \ + \ + DNG_ASSERT(condition, msg); \ + \ + ThrowProgramError (msg); \ + \ + } \ + \ + } \ + while (0) + +#else + +/// Conditionally compiled macro to check an assertion, display a message, and throw +/// an exception if it fails and assertions are compiled in via qDNGDebug +/// \param condition Predicate which must be true. +/// \param msg String to display if condition is not true. + +#define DNG_REQUIRE(condition,msg) \ + do \ + { \ + \ + if (!(condition)) \ + { \ + \ + ThrowProgramError (msg); \ + \ + } \ + \ + } \ + while (0) + +#endif +#endif + +/*****************************************************************************/ + +#ifndef DNG_REPORT + +/// Macro to display an informational message +/// \param x String to display. + +#define DNG_REPORT(x) DNG_ASSERT (false, x) + +#endif + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_auto_ptr.h b/dng/dng_auto_ptr.h new file mode 100644 index 0000000..651fa3b --- /dev/null +++ b/dng/dng_auto_ptr.h @@ -0,0 +1,239 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Class to implement std::auto_ptr like functionality even on platforms which do not + * have a full Standard C++ library. + */ + +/*****************************************************************************/ + +#ifndef __dng_auto_ptr__ +#define __dng_auto_ptr__ + +#include + +#include "dng_uncopyable.h" + +/*****************************************************************************/ + +// The following template has similar functionality to the STL auto_ptr, without +// requiring all the weight of STL. + +/*****************************************************************************/ + +/// \brief A class intended to be used in stack scope to hold a pointer from new. The +/// held pointer will be deleted automatically if the scope is left without calling +/// Release on the AutoPtr first. + +template +class AutoPtr: private dng_uncopyable + { + + private: + + T *p_; + + public: + + /// Construct an AutoPtr with no referent. + + AutoPtr () : p_ (0) { } + + /// Construct an AutoPtr which owns the argument pointer. + /// \param p pointer which constructed AutoPtr takes ownership of. p will be + /// deleted on destruction or Reset unless Release is called first. + + explicit AutoPtr (T *p) : p_( p ) { } + + /// Reset is called on destruction. + + ~AutoPtr (); + + /// Call Reset with a pointer from new. Uses T's default constructor. + + void Alloc (); + + /// Return the owned pointer of this AutoPtr, NULL if none. No change in + /// ownership or other effects occur. + + T *Get () const { return p_; } + + /// Return the owned pointer of this AutoPtr, NULL if none. The AutoPtr gives + /// up ownership and takes NULL as its value. + + T *Release (); + + /// If a pointer is owned, it is deleted. Ownership is taken of passed in + /// pointer. + /// \param p pointer which constructed AutoPtr takes ownership of. p will be + /// deleted on destruction or Reset unless Release is called first. + + void Reset (T *p); + + /// If a pointer is owned, it is deleted and the AutoPtr takes NULL as its + /// value. + + void Reset (); + + /// Allows members of the owned pointer to be accessed directly. It is an + /// error to call this if the AutoPtr has NULL as its value. + + T *operator-> () const { return p_; } + + /// Returns a reference to the object that the owned pointer points to. It is + /// an error to call this if the AutoPtr has NULL as its value. + + T &operator* () const { return *p_; } + + /// Swap with another auto ptr. + + friend inline void Swap (AutoPtr< T > &x, AutoPtr< T > &y) + { + T* temp = x.p_; + x.p_ = y.p_; + y.p_ = temp; + } + + }; + +/*****************************************************************************/ + +template +AutoPtr::~AutoPtr () + { + + delete p_; + p_ = 0; + + } + +/*****************************************************************************/ + +template +T *AutoPtr::Release () + { + T *result = p_; + p_ = 0; + return result; + } + +/*****************************************************************************/ + +template +void AutoPtr::Reset (T *p) + { + + if (p_ != p) + { + if (p_ != 0) + delete p_; + p_ = p; + } + + } + +/*****************************************************************************/ + +template +void AutoPtr::Reset () + { + + if (p_ != 0) + { + delete p_; + p_ = 0; + } + + } + +/*****************************************************************************/ + +template +void AutoPtr::Alloc () + { + this->Reset (new T); + } + +/*****************************************************************************/ + +/// \brief A class intended to be used similarly to AutoPtr but for arrays. + +template +class AutoArray: private dng_uncopyable + { + + public: + + /// Construct an AutoArray which owns the argument pointer. + /// \param p_ array pointer which constructed AutoArray takes ownership of. p_ + /// will be deleted on destruction or Reset unless Release is called first. + + explicit AutoArray (T *p_ = 0) : p (p_) { } + + /// Reset is called on destruction. + + ~AutoArray () + { + delete [] p; + p = 0; + } + + /// Return the owned array pointer of this AutoArray, NULL if none. The + /// AutoArray gives up ownership and takes NULL as its value. + + T *Release () + { + T *p_ = p; + p = 0; + return p_; + } + + /// If an array pointer is owned, it is deleted. Ownership is + /// taken of the passed in pointer p_. + /// \param p_ array pointer which constructed AutoArray takes ownership of. p_ + /// will be deleted on destruction or Reset unless Release is called first. + + void Reset (T *p_ = 0) + { + if (p != p_) + { + delete [] p; + p = p_; + } + } + + /// Allows indexing into the AutoArray. It is an error to call this if the + /// AutoArray has NULL as its value. + + T &operator[] (ptrdiff_t i) const + { + return p [i]; + } + + /// Return the owned pointer of this AutoArray, NULL if none. No change in + /// ownership or other effects occur. + + T *Get () const + { + return p; + } + + private: + + // Owned pointer or NULL. + + T *p; + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_bad_pixels.cpp b/dng/dng_bad_pixels.cpp new file mode 100644 index 0000000..810c36e --- /dev/null +++ b/dng/dng_bad_pixels.cpp @@ -0,0 +1,1853 @@ +/*****************************************************************************/ +// Copyright 2008-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_bad_pixels.h" + +#include "dng_filter_task.h" +#include "dng_globals.h" +#include "dng_host.h" +#include "dng_image.h" +#include "dng_negative.h" + +#include + +/*****************************************************************************/ + +dng_opcode_FixBadPixelsConstant::dng_opcode_FixBadPixelsConstant + (uint32 constant, + uint32 bayerPhase) + + : dng_filter_opcode (dngOpcode_FixBadPixelsConstant, + dngVersion_1_3_0_0, + 0) + + , fConstant (constant) + , fBayerPhase (bayerPhase) + + { + + } + +/*****************************************************************************/ + +dng_opcode_FixBadPixelsConstant::dng_opcode_FixBadPixelsConstant + (dng_stream &stream) + + : dng_filter_opcode (dngOpcode_FixBadPixelsConstant, + stream, + "FixBadPixelsConstant") + + , fConstant (0) + , fBayerPhase (0) + + { + + if (stream.Get_uint32 () != 8) + { + ThrowBadFormat (); + } + + fConstant = stream.Get_uint32 (); + fBayerPhase = stream.Get_uint32 (); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Constant: %u\n", (unsigned) fConstant); + + printf ("Bayer Phase: %u\n", (unsigned) fBayerPhase); + + } + + #endif + + } + +/*****************************************************************************/ + +void dng_opcode_FixBadPixelsConstant::PutData (dng_stream &stream) const + { + + stream.Put_uint32 (8); + + stream.Put_uint32 (fConstant ); + stream.Put_uint32 (fBayerPhase); + + } + +/*****************************************************************************/ + +dng_point dng_opcode_FixBadPixelsConstant::SrcRepeat () + { + + return dng_point (2, 2); + + } + +/*****************************************************************************/ + +dng_rect dng_opcode_FixBadPixelsConstant::SrcArea (const dng_rect &dstArea, + const dng_rect & /* imageBounds */) + { + + dng_rect srcArea = dstArea; + + srcArea.t -= 2; + srcArea.l -= 2; + + srcArea.b += 2; + srcArea.r += 2; + + return srcArea; + + } + +/*****************************************************************************/ + +void dng_opcode_FixBadPixelsConstant::Prepare (dng_negative & /* negative */, + uint32 /* threadCount */, + const dng_point & /* tileSize */, + const dng_rect & /* imageBounds */, + uint32 imagePlanes, + uint32 bufferPixelType, + dng_memory_allocator & /* allocator */) + { + + // This opcode is restricted to single channel images. + + if (imagePlanes != 1) + { + + ThrowBadFormat (); + + } + + // This opcode is restricted to 16-bit images. + + if (bufferPixelType != ttShort) + { + + ThrowBadFormat (); + + } + + } + +/*****************************************************************************/ + +void dng_opcode_FixBadPixelsConstant::ProcessArea (dng_negative & /* negative */, + uint32 /* threadIndex */, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer, + const dng_rect &dstArea, + const dng_rect & /* imageBounds */) + { + + dstBuffer.CopyArea (srcBuffer, + dstArea, + 0, + dstBuffer.fPlanes); + + uint16 badPixel = (uint16) fConstant; + + for (int32 dstRow = dstArea.t; dstRow < dstArea.b; dstRow++) + { + + const uint16 *sPtr = srcBuffer.ConstPixel_uint16 (dstRow, dstArea.l, 0); + uint16 *dPtr = dstBuffer.DirtyPixel_uint16 (dstRow, dstArea.l, 0); + + for (int32 dstCol = dstArea.l; dstCol < dstArea.r; dstCol++) + { + + if (*sPtr == badPixel) + { + + uint32 count = 0; + uint32 total = 0; + + uint16 value; + + if (IsGreen (dstRow, dstCol)) // Green pixel + { + + value = sPtr [-srcBuffer.fRowStep - 1]; + + if (value != badPixel) + { + count += 1; + total += value; + } + + value = sPtr [-srcBuffer.fRowStep + 1]; + + if (value != badPixel) + { + count += 1; + total += value; + } + + value = sPtr [srcBuffer.fRowStep - 1]; + + if (value != badPixel) + { + count += 1; + total += value; + } + + value = sPtr [srcBuffer.fRowStep + 1]; + + if (value != badPixel) + { + count += 1; + total += value; + } + + } + + else // Red/blue pixel. + { + + value = sPtr [-srcBuffer.fRowStep * 2]; + + if (value != badPixel) + { + count += 1; + total += value; + } + + value = sPtr [srcBuffer.fRowStep * 2]; + + if (value != badPixel) + { + count += 1; + total += value; + } + + value = sPtr [-2]; + + if (value != badPixel) + { + count += 1; + total += value; + } + + value = sPtr [2]; + + if (value != badPixel) + { + count += 1; + total += value; + } + + } + + if (count == 4) // Most common case. + { + + *dPtr = (uint16) ((total + 2) >> 2); + + } + + else if (count > 0) + { + + *dPtr = (uint16) ((total + (count >> 1)) / count); + + } + + } + + sPtr++; + dPtr++; + + } + + } + + } + +/*****************************************************************************/ + +dng_bad_pixel_list::dng_bad_pixel_list () + + : fBadPoints () + , fBadRects () + + { + + } + +/*****************************************************************************/ + +void dng_bad_pixel_list::AddPoint (const dng_point &pt) + { + + fBadPoints.push_back (pt); + + } + +/*****************************************************************************/ + +void dng_bad_pixel_list::AddRect (const dng_rect &r) + { + + fBadRects.push_back (r); + + } + +/*****************************************************************************/ + +static bool SortBadPoints (const dng_point &a, + const dng_point &b) + { + + if (a.v < b.v) + return true; + + if (a.v > b.v) + return false; + + return a.h < b.h; + + } + +/*****************************************************************************/ + +static bool SortBadRects (const dng_rect &a, + const dng_rect &b) + { + + if (a.t < b.t) + return true; + + if (a.t > b.t) + return false; + + if (a.l < b.l) + return true; + + if (a.l > b.l) + return false; + + if (a.b < b.b) + return true; + + if (a.b > b.b) + return false; + + return a.r < b.r; + + } + +/*****************************************************************************/ + +void dng_bad_pixel_list::Sort () + { + + if (PointCount () > 1) + { + + std::sort (fBadPoints.begin (), + fBadPoints.end (), + SortBadPoints); + + } + + if (RectCount () > 1) + { + + std::sort (fBadRects.begin (), + fBadRects.end (), + SortBadRects); + + } + + } + +/*****************************************************************************/ + +bool dng_bad_pixel_list::IsPointIsolated (uint32 index, + uint32 radius) const + { + + dng_point pt = Point (index); + + // Search backward through bad point list. + + for (int32 j = index - 1; j >= 0; j--) + { + + const dng_point &pt2 = Point (j); + + if (pt2.v < pt.v - (int32) radius) + { + break; + } + + if (Abs_int32 (pt2.h - pt.h) <= radius) + { + return false; + } + + } + + // Search forward through bad point list. + + for (uint32 k = index + 1; k < PointCount (); k++) + { + + const dng_point &pt2 = Point (k); + + if (pt2.v > pt.v + (int32) radius) + { + break; + } + + if (Abs_int32 (pt2.h - pt.h) <= radius) + { + return false; + } + + } + + // Search through bad rectangle list. + + dng_rect testRect (pt.v - radius, + pt.h - radius, + pt.v + radius + 1, + pt.h + radius + 1); + + for (uint32 n = 0; n < RectCount (); n++) + { + + if ((testRect & Rect (n)).NotEmpty ()) + { + return false; + } + + } + + // Did not find point anywhere, so bad pixel is isolated. + + return true; + + } + +/*****************************************************************************/ + +bool dng_bad_pixel_list::IsRectIsolated (uint32 index, + uint32 radius) const + { + + dng_rect testRect = Rect (index); + + testRect.t -= radius; + testRect.l -= radius; + testRect.b += radius; + testRect.r += radius; + + for (uint32 n = 0; n < RectCount (); n++) + { + + if (n != index) + { + + if ((testRect & Rect (n)).NotEmpty ()) + { + return false; + } + + } + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_bad_pixel_list::IsPointValid (const dng_point &pt, + const dng_rect &imageBounds, + uint32 index) const + { + + // The point must be in the image bounds to be valid. + + if (pt.v < imageBounds.t || + pt.h < imageBounds.l || + pt.v >= imageBounds.b || + pt.h >= imageBounds.r) + { + return false; + } + + // Only search the bad point list if we have a starting search index. + + if (index != kNoIndex) + { + + // Search backward through bad point list. + + for (int32 j = index - 1; j >= 0; j--) + { + + const dng_point &pt2 = Point (j); + + if (pt2.v < pt.v) + { + break; + } + + if (pt2 == pt) + { + return false; + } + + } + + // Search forward through bad point list. + + for (uint32 k = index + 1; k < PointCount (); k++) + { + + const dng_point &pt2 = Point (k); + + if (pt2.v > pt.v) + { + break; + } + + if (pt2 == pt) + { + return false; + } + + } + + } + + // Search through bad rectangle list. + + for (uint32 n = 0; n < RectCount (); n++) + { + + const dng_rect &r = Rect (n); + + if (pt.v >= r.t && + pt.h >= r.l && + pt.v < r.b && + pt.h < r.r) + { + return false; + } + + } + + // Did not find point anywhere, so pixel is valid. + + return true; + + } + +/*****************************************************************************/ + +dng_opcode_FixBadPixelsList::dng_opcode_FixBadPixelsList + (AutoPtr &list, + uint32 bayerPhase) + + + : dng_filter_opcode (dngOpcode_FixBadPixelsList, + dngVersion_1_3_0_0, + 0) + + , fList () + + , fBayerPhase (bayerPhase) + + { + + fList.Reset (list.Release ()); + + fList->Sort (); + + } + +/*****************************************************************************/ + +dng_opcode_FixBadPixelsList::dng_opcode_FixBadPixelsList (dng_stream &stream) + + : dng_filter_opcode (dngOpcode_FixBadPixelsList, + stream, + "FixBadPixelsList") + + , fList () + + , fBayerPhase (0) + + { + + uint32 size = stream.Get_uint32 (); + + fBayerPhase = stream.Get_uint32 (); + + uint32 pCount = stream.Get_uint32 (); + uint32 rCount = stream.Get_uint32 (); + + if (size != 12 + pCount * 8 + rCount * 16) + { + ThrowBadFormat (); + } + + fList.Reset (new dng_bad_pixel_list); + + uint32 index; + + for (index = 0; index < pCount; index++) + { + + dng_point pt; + + pt.v = stream.Get_int32 (); + pt.h = stream.Get_int32 (); + + fList->AddPoint (pt); + + } + + for (index = 0; index < rCount; index++) + { + + dng_rect r; + + r.t = stream.Get_int32 (); + r.l = stream.Get_int32 (); + r.b = stream.Get_int32 (); + r.r = stream.Get_int32 (); + + fList->AddRect (r); + + } + + fList->Sort (); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Bayer Phase: %u\n", (unsigned) fBayerPhase); + + printf ("Bad Pixels: %u\n", (unsigned) pCount); + + for (index = 0; index < pCount && index < gDumpLineLimit; index++) + { + printf (" Pixel [%u]: v=%d, h=%d\n", + (unsigned) index, + (int) fList->Point (index).v, + (int) fList->Point (index).h); + } + + if (pCount > gDumpLineLimit) + { + printf (" ... %u bad pixels skipped\n", (unsigned) (pCount - gDumpLineLimit)); + } + + printf ("Bad Rects: %u\n", (unsigned) rCount); + + for (index = 0; index < rCount && index < gDumpLineLimit; index++) + { + printf (" Rect [%u]: t=%d, l=%d, b=%d, r=%d\n", + (unsigned) index, + (int) fList->Rect (index).t, + (int) fList->Rect (index).l, + (int) fList->Rect (index).b, + (int) fList->Rect (index).r); + } + + if (rCount > gDumpLineLimit) + { + printf (" ... %u bad rects skipped\n", (unsigned) (rCount - gDumpLineLimit)); + } + + } + + #endif + + } + +/*****************************************************************************/ + +void dng_opcode_FixBadPixelsList::PutData (dng_stream &stream) const + { + + uint32 pCount = fList->PointCount (); + uint32 rCount = fList->RectCount (); + + stream.Put_uint32 (12 + pCount * 8 + rCount * 16); + + stream.Put_uint32 (fBayerPhase); + + stream.Put_uint32 (pCount); + stream.Put_uint32 (rCount); + + uint32 index; + + for (index = 0; index < pCount; index++) + { + + const dng_point &pt (fList->Point (index)); + + stream.Put_int32 (pt.v); + stream.Put_int32 (pt.h); + + } + + for (index = 0; index < rCount; index++) + { + + const dng_rect &r (fList->Rect (index)); + + stream.Put_int32 (r.t); + stream.Put_int32 (r.l); + stream.Put_int32 (r.b); + stream.Put_int32 (r.r); + + } + + } + +/*****************************************************************************/ + +dng_rect dng_opcode_FixBadPixelsList::SrcArea (const dng_rect &dstArea, + const dng_rect & /* imageBounds */) + { + + int32 padding = 0; + + if (fList->PointCount ()) + { + padding += kBadPointPadding; + } + + if (fList->RectCount ()) + { + padding += kBadRectPadding; + } + + dng_rect srcArea = dstArea; + + srcArea.t -= padding; + srcArea.l -= padding; + + srcArea.b += padding; + srcArea.r += padding; + + return srcArea; + + } + +/*****************************************************************************/ + +dng_point dng_opcode_FixBadPixelsList::SrcRepeat () + { + + return dng_point (2, 2); + + } + +/*****************************************************************************/ + +void dng_opcode_FixBadPixelsList::Prepare (dng_negative & /* negative */, + uint32 /* threadCount */, + const dng_point & /* tileSize */, + const dng_rect & /* imageBounds */, + uint32 imagePlanes, + uint32 bufferPixelType, + dng_memory_allocator & /* allocator */) + { + + // This opcode is restricted to single channel images. + + if (imagePlanes != 1) + { + + ThrowBadFormat (); + + } + + // This opcode is restricted to 16-bit images. + + if (bufferPixelType != ttShort) + { + + ThrowBadFormat (); + + } + + } + +/*****************************************************************************/ + +void dng_opcode_FixBadPixelsList::FixIsolatedPixel (dng_pixel_buffer &buffer, + dng_point &badPoint) + { + + uint16 *p0 = buffer.DirtyPixel_uint16 (badPoint.v - 2, badPoint.h - 2, 0); + uint16 *p1 = buffer.DirtyPixel_uint16 (badPoint.v - 1, badPoint.h - 2, 0); + uint16 *p2 = buffer.DirtyPixel_uint16 (badPoint.v , badPoint.h - 2, 0); + uint16 *p3 = buffer.DirtyPixel_uint16 (badPoint.v + 1, badPoint.h - 2, 0); + uint16 *p4 = buffer.DirtyPixel_uint16 (badPoint.v + 2, badPoint.h - 2, 0); + + uint32 est0; + uint32 est1; + uint32 est2; + uint32 est3; + + uint32 grad0; + uint32 grad1; + uint32 grad2; + uint32 grad3; + + if (IsGreen (badPoint.v, badPoint.h)) // Green pixel + { + + // g00 b01 g02 b03 g04 + // r10 g11 r12 g13 r14 + // g20 b21 g22 b23 g24 + // r30 g31 r32 g33 r34 + // g40 b41 g42 b43 g44 + + int32 b01 = p0 [1]; + int32 g02 = p0 [2]; + int32 b03 = p0 [3]; + + int32 r10 = p1 [0]; + int32 g11 = p1 [1]; + int32 r12 = p1 [2]; + int32 g13 = p1 [3]; + int32 r14 = p1 [4]; + + int32 g20 = p2 [0]; + int32 b21 = p2 [1]; + int32 b23 = p2 [3]; + int32 g24 = p2 [4]; + + int32 r30 = p3 [0]; + int32 g31 = p3 [1]; + int32 r32 = p3 [2]; + int32 g33 = p3 [3]; + int32 r34 = p3 [4]; + + int32 b41 = p4 [1]; + int32 g42 = p4 [2]; + int32 b43 = p4 [3]; + + est0 = g02 + g42; + + grad0 = Abs_int32 (g02 - g42) + + Abs_int32 (g11 - g31) + + Abs_int32 (g13 - g33) + + Abs_int32 (b01 - b21) + + Abs_int32 (b03 - b23) + + Abs_int32 (b21 - b41) + + Abs_int32 (b23 - b43); + + est1 = g11 + g33; + + grad1 = Abs_int32 (g11 - g33) + + Abs_int32 (g02 - g24) + + Abs_int32 (g20 - g42) + + Abs_int32 (b01 - b23) + + Abs_int32 (r10 - r32) + + Abs_int32 (r12 - r34) + + Abs_int32 (b21 - b43); + + est2 = g20 + g24; + + grad2 = Abs_int32 (g20 - g24) + + Abs_int32 (g11 - g13) + + Abs_int32 (g31 - g33) + + Abs_int32 (r10 - r12) + + Abs_int32 (r30 - r32) + + Abs_int32 (r12 - r14) + + Abs_int32 (r32 - r34); + + est3 = g13 + g31; + + grad3 = Abs_int32 (g13 - g31) + + Abs_int32 (g02 - g20) + + Abs_int32 (g24 - g42) + + Abs_int32 (b03 - b21) + + Abs_int32 (r14 - r32) + + Abs_int32 (r12 - r30) + + Abs_int32 (b23 - b41); + + } + + else // Red/blue pixel + { + + // b00 g01 b02 g03 b04 + // g10 r11 g12 r13 g14 + // b20 g21 b22 g23 b24 + // g30 r31 g32 r33 g34 + // b40 g41 b42 g43 b44 + + int32 b00 = p0 [0]; + int32 g01 = p0 [1]; + int32 b02 = p0 [2]; + int32 g03 = p0 [3]; + int32 b04 = p0 [4]; + + int32 g10 = p1 [0]; + int32 r11 = p1 [1]; + int32 g12 = p1 [2]; + int32 r13 = p1 [3]; + int32 g14 = p1 [4]; + + int32 b20 = p2 [0]; + int32 g21 = p2 [1]; + int32 g23 = p2 [3]; + int32 b24 = p2 [4]; + + int32 g30 = p3 [0]; + int32 r31 = p3 [1]; + int32 g32 = p3 [2]; + int32 r33 = p3 [3]; + int32 g34 = p3 [4]; + + int32 b40 = p4 [0]; + int32 g41 = p4 [1]; + int32 b42 = p4 [2]; + int32 g43 = p4 [3]; + int32 b44 = p4 [4]; + + est0 = b02 + b42; + + grad0 = Abs_int32 (b02 - b42) + + Abs_int32 (g12 - g32) + + Abs_int32 (g01 - g21) + + Abs_int32 (g21 - g41) + + Abs_int32 (g03 - g23) + + Abs_int32 (g23 - g43) + + Abs_int32 (r11 - r31) + + Abs_int32 (r13 - r33); + + est1 = b00 + b44; + + grad1 = Abs_int32 (b00 - b44) + + Abs_int32 (r11 - r33) + + Abs_int32 (g01 - g23) + + Abs_int32 (g10 - g32) + + Abs_int32 (g12 - g34) + + Abs_int32 (g21 - g43) + + Abs_int32 (b02 - b24) + + Abs_int32 (b20 - b42); + + est2 = b20 + b24; + + grad2 = Abs_int32 (b20 - b24) + + Abs_int32 (g21 - g23) + + Abs_int32 (g10 - g12) + + Abs_int32 (g12 - g14) + + Abs_int32 (g30 - g32) + + Abs_int32 (g32 - g34) + + Abs_int32 (r11 - r13) + + Abs_int32 (r31 - r33); + + est3 = b04 + b40; + + grad3 = Abs_int32 (b04 - b40) + + Abs_int32 (r13 - r31) + + Abs_int32 (g03 - g21) + + Abs_int32 (g14 - g32) + + Abs_int32 (g12 - g30) + + Abs_int32 (g23 - g41) + + Abs_int32 (b02 - b20) + + Abs_int32 (b24 - b42); + + } + + uint32 minGrad = Min_uint32 (grad0, grad1); + + minGrad = Min_uint32 (minGrad, grad2); + minGrad = Min_uint32 (minGrad, grad3); + + uint32 limit = (minGrad * 3) >> 1; + + uint32 total = 0; + uint32 count = 0; + + if (grad0 <= limit) + { + total += est0; + count += 2; + } + + if (grad1 <= limit) + { + total += est1; + count += 2; + } + + if (grad2 <= limit) + { + total += est2; + count += 2; + } + + if (grad3 <= limit) + { + total += est3; + count += 2; + } + + count = Max_uint32 (count, 1); // Suppress div-by-zero warning. + + uint32 estimate = (total + (count >> 1)) / count; + + p2 [2] = (uint16) estimate; + + } + +/*****************************************************************************/ + +void dng_opcode_FixBadPixelsList::FixClusteredPixel (dng_pixel_buffer &buffer, + uint32 pointIndex, + const dng_rect &imageBounds) + { + + const uint32 kNumSets = 3; + const uint32 kSetSize = 4; + + static const int32 kOffset [kNumSets] [kSetSize] [2] = + { + { + { -1, 1 }, + { -1, -1 }, + { 1, -1 }, + { 1, 1 } + }, + { + { -2, 0 }, + { 2, 0 }, + { 0, -2 }, + { 0, 2 } + }, + { + { -2, -2 }, + { -2, 2 }, + { 2, -2 }, + { 2, 2 } + } + }; + + dng_point badPoint = fList->Point (pointIndex); + + bool isGreen = IsGreen (badPoint.v, badPoint.h); + + uint16 *p = buffer.DirtyPixel_uint16 (badPoint.v, badPoint.h, 0); + + for (uint32 set = 0; set < kNumSets; set++) + { + + if (!isGreen && (kOffset [set] [0] [0] & 1) == 1) + { + continue; + } + + uint32 total = 0; + uint32 count = 0; + + for (uint32 entry = 0; entry < kSetSize; entry++) + { + + dng_point offset (kOffset [set] [entry] [0], + kOffset [set] [entry] [1]); + + if (fList->IsPointValid (badPoint + offset, + imageBounds, + pointIndex)) + { + + total += p [offset.v * buffer.fRowStep + + offset.h * buffer.fColStep]; + + count++; + + } + + } + + if (count) + { + + uint32 estimate = (total + (count >> 1)) / count; + + p [0] = (uint16) estimate; + + return; + + } + + } + + // Unable to patch bad pixel. Leave pixel as is. + + #if qDNGValidate + + char s [256]; + + sprintf (s, "Unable to repair bad pixel, row %d, column %d", + (int) badPoint.v, + (int) badPoint.h); + + ReportWarning (s); + + #endif + + } + +/*****************************************************************************/ + +void dng_opcode_FixBadPixelsList::FixSingleColumn (dng_pixel_buffer &buffer, + const dng_rect &badRect) + { + + int32 cs = buffer.fColStep; + + for (int32 row = badRect.t; row < badRect.b; row++) + { + + uint16 *p0 = buffer.DirtyPixel_uint16 (row - 4, badRect.l - 4, 0); + uint16 *p1 = buffer.DirtyPixel_uint16 (row - 3, badRect.l - 4, 0); + uint16 *p2 = buffer.DirtyPixel_uint16 (row - 2, badRect.l - 4, 0); + uint16 *p3 = buffer.DirtyPixel_uint16 (row - 1, badRect.l - 4, 0); + uint16 *p4 = buffer.DirtyPixel_uint16 (row , badRect.l - 4, 0); + uint16 *p5 = buffer.DirtyPixel_uint16 (row + 1, badRect.l - 4, 0); + uint16 *p6 = buffer.DirtyPixel_uint16 (row + 2, badRect.l - 4, 0); + uint16 *p7 = buffer.DirtyPixel_uint16 (row + 3, badRect.l - 4, 0); + uint16 *p8 = buffer.DirtyPixel_uint16 (row + 4, badRect.l - 4, 0); + + uint32 est0; + uint32 est1; + uint32 est2; + uint32 est3; + uint32 est4; + uint32 est5; + uint32 est6; + + uint32 grad0; + uint32 grad1; + uint32 grad2; + uint32 grad3; + uint32 grad4; + uint32 grad5; + uint32 grad6; + + uint32 lower = 0; + uint32 upper = 0x0FFFF; + + if (IsGreen (row, badRect.l)) // Green pixel + { + + // g00 b01 g02 b03 g04 b05 g06 b07 g08 + // r10 g11 r12 g13 r14 g15 r16 g17 r18 + // g20 b21 g22 b23 g24 b25 g26 b27 g28 + // r30 g31 r32 g33 r34 g35 r36 g37 r38 + // g40 b41 g42 b43 g44 b45 g46 b47 g48 + // r50 g51 r52 g53 r54 g55 r56 g57 r58 + // g60 b61 g62 b63 g64 b65 g66 b67 g68 + // r70 g71 r72 g73 r74 g75 r76 g77 r78 + // g80 b81 g82 b83 g84 b85 g86 b87 g88 + + int32 b03 = p0 [3 * cs]; + int32 b05 = p0 [5 * cs]; + + int32 g11 = p1 [1 * cs]; + int32 g13 = p1 [3 * cs]; + int32 g15 = p1 [5 * cs]; + int32 g17 = p1 [7 * cs]; + + int32 g22 = p2 [2 * cs]; + int32 b23 = p2 [3 * cs]; + int32 b25 = p2 [5 * cs]; + int32 g26 = p2 [6 * cs]; + + int32 r30 = p3 [0 * cs]; + int32 g31 = p3 [1 * cs]; + int32 r32 = p3 [2 * cs]; + int32 g33 = p3 [3 * cs]; + int32 g35 = p3 [5 * cs]; + int32 r36 = p3 [6 * cs]; + int32 g37 = p3 [7 * cs]; + int32 r38 = p3 [8 * cs]; + + int32 g40 = p4 [0 * cs]; + int32 g42 = p4 [2 * cs]; + int32 b43 = p4 [3 * cs]; + int32 b45 = p4 [5 * cs]; + int32 g46 = p4 [6 * cs]; + int32 g48 = p4 [8 * cs]; + + int32 r50 = p5 [0 * cs]; + int32 g51 = p5 [1 * cs]; + int32 r52 = p5 [2 * cs]; + int32 g53 = p5 [3 * cs]; + int32 g55 = p5 [5 * cs]; + int32 r56 = p5 [6 * cs]; + int32 g57 = p5 [7 * cs]; + int32 r58 = p5 [8 * cs]; + + int32 g62 = p6 [2 * cs]; + int32 b63 = p6 [3 * cs]; + int32 b65 = p6 [5 * cs]; + int32 g66 = p6 [6 * cs]; + + int32 g71 = p7 [1 * cs]; + int32 g73 = p7 [3 * cs]; + int32 g75 = p7 [5 * cs]; + int32 g77 = p7 [7 * cs]; + + int32 b83 = p8 [3 * cs]; + int32 b85 = p8 [5 * cs]; + + // In case there is some green split, make an estimate of + // of the local difference between the greens, and adjust + // the estimated green values for the difference + // between the two types of green pixels when estimating + // across green types. + + int32 split = ((g22 + g62 + g26 + g66) * 4 + + (g42 + g46 ) * 8 - + (g11 + g13 + g15 + g17) - + (g31 + g33 + g35 + g37) * 3 - + (g51 + g53 + g55 + g57) * 3 - + (g71 + g73 + g75 + g77) + 16) >> 5; + + est0 = g13 + g75 + split * 2; + + grad0 = Abs_int32 (g13 - g75) + + Abs_int32 (g15 - g46) + + Abs_int32 (g22 - g53) + + Abs_int32 (g35 - g66) + + Abs_int32 (g42 - g73) + + Abs_int32 (b03 - b65) + + Abs_int32 (b23 - b85); + + est1 = g33 + g55 + split * 2; + + grad1 = Abs_int32 (g33 - g55) + + Abs_int32 (g22 - g55) + + Abs_int32 (g33 - g66) + + Abs_int32 (g13 - g35) + + Abs_int32 (g53 - g75) + + Abs_int32 (b23 - b45) + + Abs_int32 (b43 - b65); + + est2 = g31 + g57 + split * 2; + + grad2 = Abs_int32 (g31 - g57) + + Abs_int32 (g33 - g46) + + Abs_int32 (g35 - g48) + + Abs_int32 (g40 - g53) + + Abs_int32 (g42 - g55) + + Abs_int32 (r30 - r56) + + Abs_int32 (r32 - r58); + + est3 = g42 + g46; + + grad3 = Abs_int32 (g42 - g46) * 2 + + Abs_int32 (g33 - g35) + + Abs_int32 (g53 - g55) + + Abs_int32 (b23 - b25) + + Abs_int32 (b43 - b45) + + Abs_int32 (b63 - b65); + + est4 = g37 + g51 + split * 2; + + grad4 = Abs_int32 (g37 - g51) + + Abs_int32 (g35 - g42) + + Abs_int32 (g33 - g40) + + Abs_int32 (g48 - g55) + + Abs_int32 (g46 - g53) + + Abs_int32 (r38 - r52) + + Abs_int32 (r36 - r50); + + est5 = g35 + g53 + split * 2; + + grad5 = Abs_int32 (g35 - g53) + + Abs_int32 (g26 - g53) + + Abs_int32 (g35 - g62) + + Abs_int32 (g15 - g33) + + Abs_int32 (g55 - g73) + + Abs_int32 (b25 - b43) + + Abs_int32 (b45 - b63); + + est6 = g15 + g73 + split * 2; + + grad6 = Abs_int32 (g15 - g73) + + Abs_int32 (g13 - g42) + + Abs_int32 (g26 - g55) + + Abs_int32 (g33 - g62) + + Abs_int32 (g46 - g75) + + Abs_int32 (b05 - b63) + + Abs_int32 (b25 - b83); + + lower = Min_uint32 (Min_uint32 (g33, g35), + Min_uint32 (g53, g55)); + + upper = Max_uint32 (Max_uint32 (g33, g35), + Max_uint32 (g53, g55)); + + lower = Pin_int32 (0, lower + split, 65535); + upper = Pin_int32 (0, upper + split, 65535); + + } + + else // Red/blue pixel + { + + // b00 g01 b02 g03 b04 g05 b06 g07 b08 + // g10 r11 g12 r13 g14 r15 g16 r17 g18 + // b20 g21 b22 g23 b24 g25 b26 g27 b28 + // g30 r31 g32 r33 g34 r35 g36 r37 g38 + // b40 g41 b42 g43 b44 g45 b46 g47 b48 + // g50 r51 g52 r53 g54 r55 g56 r57 g58 + // b60 g61 b62 g63 b64 g65 b66 g67 b68 + // g70 r71 g72 r73 g74 r75 g76 r77 g78 + // b80 g81 b82 g83 b84 g85 b86 g87 b88 + + int32 b02 = p0 [2 * cs]; + int32 g03 = p0 [3 * cs]; + int32 g05 = p0 [5 * cs]; + int32 b06 = p0 [6 * cs]; + + int32 r13 = p1 [3 * cs]; + int32 r15 = p1 [5 * cs]; + + int32 b20 = p2 [0 * cs]; + int32 b22 = p2 [2 * cs]; + int32 g23 = p2 [3 * cs]; + int32 g25 = p2 [5 * cs]; + int32 b26 = p2 [6 * cs]; + int32 b28 = p2 [8 * cs]; + + int32 r31 = p3 [1 * cs]; + int32 g32 = p3 [2 * cs]; + int32 r33 = p3 [3 * cs]; + int32 r35 = p3 [5 * cs]; + int32 g36 = p3 [6 * cs]; + int32 r37 = p3 [7 * cs]; + + int32 g41 = p4 [1 * cs]; + int32 b42 = p4 [2 * cs]; + int32 g43 = p4 [3 * cs]; + int32 g45 = p4 [5 * cs]; + int32 b46 = p4 [6 * cs]; + int32 g47 = p4 [7 * cs]; + + int32 r51 = p5 [1 * cs]; + int32 g52 = p5 [2 * cs]; + int32 r53 = p5 [3 * cs]; + int32 r55 = p5 [5 * cs]; + int32 g56 = p5 [6 * cs]; + int32 r57 = p5 [7 * cs]; + + int32 b60 = p6 [0 * cs]; + int32 b62 = p6 [2 * cs]; + int32 g63 = p6 [3 * cs]; + int32 g65 = p6 [5 * cs]; + int32 b66 = p6 [6 * cs]; + int32 b68 = p6 [8 * cs]; + + int32 r73 = p7 [3 * cs]; + int32 r75 = p7 [5 * cs]; + + int32 b82 = p8 [2 * cs]; + int32 g83 = p8 [3 * cs]; + int32 g85 = p8 [5 * cs]; + int32 b86 = p8 [6 * cs]; + + est0 = b02 + b86; + + grad0 = Abs_int32 (b02 - b86) + + Abs_int32 (r13 - r55) + + Abs_int32 (r33 - r75) + + Abs_int32 (g03 - g45) + + Abs_int32 (g23 - g65) + + Abs_int32 (g43 - g85); + + est1 = b22 + b66; + + grad1 = Abs_int32 (b22 - b66) + + Abs_int32 (r13 - r35) + + Abs_int32 (r33 - r55) + + Abs_int32 (r53 - r75) + + Abs_int32 (g23 - g45) + + Abs_int32 (g43 - g65); + + est2 = b20 + b68; + + grad2 = Abs_int32 (b20 - b68) + + Abs_int32 (r31 - r55) + + Abs_int32 (r33 - r57) + + Abs_int32 (g23 - g47) + + Abs_int32 (g32 - g56) + + Abs_int32 (g41 - g65); + + est3 = b42 + b46; + + grad3 = Abs_int32 (b42 - b46) + + Abs_int32 (r33 - r35) + + Abs_int32 (r53 - r55) + + Abs_int32 (g32 - g36) + + Abs_int32 (g43 - g43) + + Abs_int32 (g52 - g56); + + est4 = b28 + b60; + + grad4 = Abs_int32 (b28 - b60) + + Abs_int32 (r37 - r53) + + Abs_int32 (r35 - r51) + + Abs_int32 (g25 - g41) + + Abs_int32 (g36 - g52) + + Abs_int32 (g47 - g63); + + est5 = b26 + b62; + + grad5 = Abs_int32 (b26 - b62) + + Abs_int32 (r15 - r33) + + Abs_int32 (r35 - r53) + + Abs_int32 (r55 - r73) + + Abs_int32 (g25 - g43) + + Abs_int32 (g45 - g63); + + est6 = b06 + b82; + + grad6 = Abs_int32 (b06 - b82) + + Abs_int32 (r15 - r53) + + Abs_int32 (r35 - r73) + + Abs_int32 (g05 - g43) + + Abs_int32 (g25 - g63) + + Abs_int32 (g45 - g83); + + lower = Min_uint32 (b42, b46); + upper = Max_uint32 (b42, b46); + + } + + uint32 minGrad = Min_uint32 (grad0, grad1); + + minGrad = Min_uint32 (minGrad, grad2); + minGrad = Min_uint32 (minGrad, grad3); + minGrad = Min_uint32 (minGrad, grad4); + minGrad = Min_uint32 (minGrad, grad5); + minGrad = Min_uint32 (minGrad, grad6); + + uint32 limit = (minGrad * 3) >> 1; + + uint32 total = 0; + uint32 count = 0; + + if (grad0 <= limit) + { + total += est0; + count += 2; + } + + if (grad1 <= limit) + { + total += est1; + count += 2; + } + + if (grad2 <= limit) + { + total += est2; + count += 2; + } + + if (grad3 <= limit) + { + total += est3; + count += 2; + } + + if (grad4 <= limit) + { + total += est4; + count += 2; + } + + if (grad5 <= limit) + { + total += est5; + count += 2; + } + + if (grad6 <= limit) + { + total += est6; + count += 2; + } + + count = Max_uint32 (count, 1); // Suppress div-by-zero warning. + + uint32 estimate = (total + (count >> 1)) / count; + + p4 [4] = (uint16) Pin_uint32 (lower, estimate, upper); + + } + + } + +/*****************************************************************************/ + +void dng_opcode_FixBadPixelsList::FixSingleRow (dng_pixel_buffer &buffer, + const dng_rect &badRect) + { + + dng_pixel_buffer tBuffer = buffer; + + tBuffer.fArea = Transpose (buffer.fArea); + + tBuffer.fRowStep = buffer.fColStep; + tBuffer.fColStep = buffer.fRowStep; + + dng_rect tBadRect = Transpose (badRect); + + FixSingleColumn (tBuffer, tBadRect); + + } + +/*****************************************************************************/ + +void dng_opcode_FixBadPixelsList::FixClusteredRect (dng_pixel_buffer &buffer, + const dng_rect &badRect, + const dng_rect &imageBounds) + { + + const uint32 kNumSets = 8; + const uint32 kSetSize = 8; + + static const int32 kOffset [kNumSets] [kSetSize] [2] = + { + { + { -1, 1 }, + { -1, -1 }, + { 1, -1 }, + { 1, 1 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 } + }, + { + { -2, 0 }, + { 2, 0 }, + { 0, -2 }, + { 0, 2 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 } + }, + { + { -2, -2 }, + { -2, 2 }, + { 2, -2 }, + { 2, 2 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 } + }, + { + { -1, -3 }, + { -3, -1 }, + { 1, -3 }, + { 3, -1 }, + { -1, 3 }, + { -3, 1 }, + { 1, 3 }, + { 3, 1 } + }, + { + { -4, 0 }, + { 4, 0 }, + { 0, -4 }, + { 0, 4 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 } + }, + { + { -3, -3 }, + { -3, 3 }, + { 3, -3 }, + { 3, 3 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 } + }, + { + { -2, -4 }, + { -4, -2 }, + { 2, -4 }, + { 4, -2 }, + { -2, 4 }, + { -4, 2 }, + { 2, 4 }, + { 4, 2 } + }, + { + { -4, -4 }, + { -4, 4 }, + { 4, -4 }, + { 4, 4 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 } + } + }; + + bool didFail = false; + + for (int32 row = badRect.t; row < badRect.b; row++) + { + + for (int32 col = badRect.l; col < badRect.r; col++) + { + + uint16 *p = buffer.DirtyPixel_uint16 (row, col, 0); + + bool isGreen = IsGreen (row, col); + + bool didFixPixel = false; + + for (uint32 set = 0; set < kNumSets && !didFixPixel; set++) + { + + if (!isGreen && (kOffset [set] [0] [0] & 1) == 1) + { + continue; + } + + uint32 total = 0; + uint32 count = 0; + + for (uint32 entry = 0; entry < kSetSize; entry++) + { + + dng_point offset (kOffset [set] [entry] [0], + kOffset [set] [entry] [1]); + + if (offset.v == 0 && + offset.h == 0) + { + break; + } + + if (fList->IsPointValid (dng_point (row, col) + offset, + imageBounds)) + { + + total += p [offset.v * buffer.fRowStep + + offset.h * buffer.fColStep]; + + count++; + + } + + } + + if (count) + { + + uint32 estimate = (total + (count >> 1)) / count; + + p [0] = (uint16) estimate; + + didFixPixel = true; + + } + + } + + if (!didFixPixel) + { + + didFail = true; + + } + + } + + } + + #if qDNGValidate + + if (didFail) + { + + ReportWarning ("Unable to repair bad rectangle"); + + } + + #endif + + } + +/*****************************************************************************/ + +void dng_opcode_FixBadPixelsList::ProcessArea (dng_negative & /* negative */, + uint32 /* threadIndex */, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer, + const dng_rect &dstArea, + const dng_rect &imageBounds) + { + + uint32 pointCount = fList->PointCount (); + uint32 rectCount = fList->RectCount (); + + dng_rect fixArea = dstArea; + + if (rectCount) + { + fixArea.t -= kBadRectPadding; + fixArea.l -= kBadRectPadding; + fixArea.b += kBadRectPadding; + fixArea.r += kBadRectPadding; + } + + bool didFixPoint = false; + + if (pointCount) + { + + for (uint32 pointIndex = 0; pointIndex < pointCount; pointIndex++) + { + + dng_point badPoint = fList->Point (pointIndex); + + if (badPoint.v >= fixArea.t && + badPoint.h >= fixArea.l && + badPoint.v < fixArea.b && + badPoint.h < fixArea.r) + { + + bool isIsolated = fList->IsPointIsolated (pointIndex, + kBadPointPadding); + + if (isIsolated && + badPoint.v >= imageBounds.t + kBadPointPadding && + badPoint.h >= imageBounds.l + kBadPointPadding && + badPoint.v < imageBounds.b - kBadPointPadding && + badPoint.h < imageBounds.r - kBadPointPadding) + { + + FixIsolatedPixel (srcBuffer, + badPoint); + + } + + else + { + + FixClusteredPixel (srcBuffer, + pointIndex, + imageBounds); + + } + + didFixPoint = true; + + } + + } + + } + + if (rectCount) + { + + if (didFixPoint) + { + + srcBuffer.RepeatSubArea (imageBounds, + SrcRepeat ().v, + SrcRepeat ().h); + + } + + for (uint32 rectIndex = 0; rectIndex < rectCount; rectIndex++) + { + + dng_rect badRect = fList->Rect (rectIndex); + + dng_rect overlap = dstArea & badRect; + + if (overlap.NotEmpty ()) + { + + bool isIsolated = fList->IsRectIsolated (rectIndex, + kBadRectPadding); + + if (isIsolated && + badRect.r == badRect.l + 1 && + badRect.l >= imageBounds.l + SrcRepeat ().h && + badRect.r <= imageBounds.r - SrcRepeat ().v) + { + + FixSingleColumn (srcBuffer, + overlap); + + } + + else if (isIsolated && + badRect.b == badRect.t + 1 && + badRect.t >= imageBounds.t + SrcRepeat ().h && + badRect.b <= imageBounds.b - SrcRepeat ().v) + { + + FixSingleRow (srcBuffer, + overlap); + + } + + else + { + + FixClusteredRect (srcBuffer, + overlap, + imageBounds); + + } + + } + + } + + } + + dstBuffer.CopyArea (srcBuffer, + dstArea, + 0, + dstBuffer.fPlanes); + + } + +/*****************************************************************************/ diff --git a/dng/dng_bad_pixels.h b/dng/dng_bad_pixels.h new file mode 100644 index 0000000..69554ad --- /dev/null +++ b/dng/dng_bad_pixels.h @@ -0,0 +1,303 @@ +/*****************************************************************************/ +// Copyright 2008-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Opcodes to fix defective pixels, including individual pixels and regions (such as + * defective rows and columns). + */ + +/*****************************************************************************/ + +#ifndef __dng_bad_pixels__ +#define __dng_bad_pixels__ + +/*****************************************************************************/ + +#include "dng_memory.h" +#include "dng_opcodes.h" + +#include + +/*****************************************************************************/ + +/// \brief An opcode to fix individual bad pixels that are marked with a constant +/// value (e.g., 0) in a Bayer image. + +class dng_opcode_FixBadPixelsConstant: public dng_filter_opcode + { + + private: + + uint32 fConstant; + + uint32 fBayerPhase; + + public: + + /// Construct an opcode to fix an individual bad pixels that are marked with + /// a constant value in a Bayer image. + /// \param constant The constant value that indicates a bad pixel. + /// \param bayerPhase The phase of the Bayer mosaic pattern (0, 1, 2, 3). + + dng_opcode_FixBadPixelsConstant (uint32 constant, + uint32 bayerPhase); + + dng_opcode_FixBadPixelsConstant (dng_stream &stream); + + virtual void PutData (dng_stream &stream) const; + + virtual dng_point SrcRepeat (); + + virtual dng_rect SrcArea (const dng_rect &dstArea, + const dng_rect &imageBounds); + + virtual void Prepare (dng_negative &negative, + uint32 threadCount, + const dng_point &tileSize, + const dng_rect &imageBounds, + uint32 imagePlanes, + uint32 bufferPixelType, + dng_memory_allocator &allocator); + + virtual void ProcessArea (dng_negative &negative, + uint32 threadIndex, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer, + const dng_rect &dstArea, + const dng_rect &imageBounds); + + protected: + + bool IsGreen (int32 row, int32 col) const + { + return (((uint32) row + (uint32) col + fBayerPhase + (fBayerPhase >> 1)) & 1) == 0; + } + + }; + +/*****************************************************************************/ + +/// \brief A list of bad pixels and rectangles (usually single rows or columns). + +class dng_bad_pixel_list + { + + public: + + enum + { + kNoIndex = 0xFFFFFFFF + }; + + private: + + // List of bad single pixels. + + dng_std_vector fBadPoints; + + // List of bad rectangles (usually single rows or columns). + + dng_std_vector fBadRects; + + public: + + /// Create an empty bad pixel list. + + dng_bad_pixel_list (); + + /// Returns the number of bad single pixels. + + uint32 PointCount () const + { + return (uint32) fBadPoints.size (); + } + + /// Retrieves the bad single pixel coordinate via the specified list index. + /// + /// \param index The list index from which to retrieve the bad single pixel + /// coordinate. + + const dng_point & Point (uint32 index) const + { + return fBadPoints [index]; + } + + /// Returns the number of bad rectangles. + + uint32 RectCount () const + { + return (uint32) fBadRects.size (); + } + + /// Retrieves the bad rectangle via the specified list index. + /// + /// \param index The list index from which to retrieve the bad rectangle + /// coordinates. + + const dng_rect & Rect (uint32 index) const + { + return fBadRects [index]; + } + + /// Returns true iff there are zero bad single pixels and zero bad + /// rectangles. + + bool IsEmpty () const + { + return PointCount () == 0 && + RectCount () == 0; + } + + /// Returns true iff there is at least one bad single pixel or at least one + /// bad rectangle. + + bool NotEmpty () const + { + return !IsEmpty (); + } + + /// Add the specified coordinate to the list of bad single pixels. + /// + /// \param pt The bad single pixel to add. + + void AddPoint (const dng_point &pt); + + /// Add the specified rectangle to the list of bad rectangles. + /// + /// \param r The bad rectangle to add. + + void AddRect (const dng_rect &r); + + /// Sort the bad single pixels and bad rectangles by coordinates (top to + /// bottom, then left to right). + + void Sort (); + + /// Returns true iff the specified bad single pixel is isolated, i.e., there + /// is no other bad single pixel or bad rectangle that lies within radius + /// pixels of this bad single pixel. + /// + /// \param index The index of the bad single pixel to test. + /// \param radius The pixel radius to test for isolation. + + bool IsPointIsolated (uint32 index, + uint32 radius) const; + + /// Returns true iff the specified bad rectangle is isolated, i.e., there + /// is no other bad single pixel or bad rectangle that lies within radius + /// pixels of this bad rectangle. + /// + /// \param index The index of the bad rectangle to test. + /// \param radius The pixel radius to test for isolation. + + bool IsRectIsolated (uint32 index, + uint32 radius) const; + + /// Returns true iff the specified point is valid, i.e., lies within the + /// specified image bounds, is different from all other bad single pixels, + /// and is not contained in any bad rectangle. The second and third + /// conditions are only checked if provided with a starting search index. + /// + /// \param pt The point to test for validity. + /// \param imageBounds The pt must lie within imageBounds to be valid. + /// \index The search index to use (or kNoIndex, to avoid a search) for + /// checking for validity. + + bool IsPointValid (const dng_point &pt, + const dng_rect &imageBounds, + uint32 index = kNoIndex) const; + + }; + +/*****************************************************************************/ + +/// \brief An opcode to fix lists of bad pixels (indicated by position) in a Bayer +/// image. + +class dng_opcode_FixBadPixelsList: public dng_filter_opcode + { + + protected: + + enum + { + kBadPointPadding = 2, + kBadRectPadding = 4 + }; + + private: + + AutoPtr fList; + + uint32 fBayerPhase; + + public: + + /// Construct an opcode to fix lists of bad pixels (indicated by position) in + /// a Bayer image. + /// \param list The list of bad pixels to fix. + /// \param bayerPhase The phase of the Bayer mosaic pattern (0, 1, 2, 3). + + dng_opcode_FixBadPixelsList (AutoPtr &list, + uint32 bayerPhase); + + dng_opcode_FixBadPixelsList (dng_stream &stream); + + virtual void PutData (dng_stream &stream) const; + + virtual dng_point SrcRepeat (); + + virtual dng_rect SrcArea (const dng_rect &dstArea, + const dng_rect &imageBounds); + + virtual void Prepare (dng_negative &negative, + uint32 threadCount, + const dng_point &tileSize, + const dng_rect &imageBounds, + uint32 imagePlanes, + uint32 bufferPixelType, + dng_memory_allocator &allocator); + + virtual void ProcessArea (dng_negative &negative, + uint32 threadIndex, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer, + const dng_rect &dstArea, + const dng_rect &imageBounds); + + protected: + + bool IsGreen (int32 row, int32 col) const + { + return ((row + col + fBayerPhase + (fBayerPhase >> 1)) & 1) == 0; + } + + virtual void FixIsolatedPixel (dng_pixel_buffer &buffer, + dng_point &badPoint); + + virtual void FixClusteredPixel (dng_pixel_buffer &buffer, + uint32 pointIndex, + const dng_rect &imageBounds); + + virtual void FixSingleColumn (dng_pixel_buffer &buffer, + const dng_rect &badRect); + + virtual void FixSingleRow (dng_pixel_buffer &buffer, + const dng_rect &badRect); + + virtual void FixClusteredRect (dng_pixel_buffer &buffer, + const dng_rect &badRect, + const dng_rect &imageBounds); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_big_table.cpp b/dng/dng_big_table.cpp new file mode 100644 index 0000000..b2bfdcf --- /dev/null +++ b/dng/dng_big_table.cpp @@ -0,0 +1,1950 @@ +/*****************************************************************************/ +// Copyright 2015-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_big_table.h" + +#include "dng_memory_stream.h" +#include "dng_mutex.h" +#include "dng_stream.h" +#include "dng_xmp.h" + +#include "zlib.h" + +#include + +/*****************************************************************************/ + +class dng_big_table_cache + { + + private: + + dng_std_mutex fMutex; + + typedef std::pair RefCountsPair; + + typedef std::map RefCountsMap; + + RefCountsMap fRefCounts; + + std::vector fRecentlyUsed; + + protected: + + enum + { + kDefaultRecentlyUsedLimit = 5 + }; + + uint32 fRecentlyUsedLimit; + + protected: + + dng_big_table_cache (); + + void UseTable (dng_lock_std_mutex &lock, + const dng_fingerprint &fingerprint); + + virtual void CacheIncrement (dng_lock_std_mutex &lock, + const dng_fingerprint &fingerprint); + + virtual void CacheDecrement (dng_lock_std_mutex &lock, + const dng_fingerprint &fingerprint); + + virtual void CacheAdd (dng_lock_std_mutex &lock, + const dng_big_table &table); + + virtual bool CacheExtract (dng_lock_std_mutex &lock, + const dng_fingerprint &fingerprint, + dng_big_table &table); + + virtual void InsertTableData (dng_lock_std_mutex &lock, + const dng_big_table &table) = 0; + + virtual void EraseTableData (dng_lock_std_mutex &lock, + const dng_fingerprint &fingerprint) = 0; + + virtual void ExtractTableData (dng_lock_std_mutex &lock, + const dng_fingerprint &fingerprint, + dng_big_table &table) = 0; + + public: + + virtual ~dng_big_table_cache (); + + void FlushRecentlyUsed (); + + static void Increment (dng_big_table_cache *cache, + const dng_fingerprint &fingerprint); + + static void Decrement (dng_big_table_cache *cache, + const dng_fingerprint &fingerprint); + + static void Add (dng_big_table_cache *cache, + const dng_big_table &table); + + static bool Extract (dng_big_table_cache *cache, + const dng_fingerprint &fingerprint, + dng_big_table &table); + + }; + +/*****************************************************************************/ + +dng_big_table_cache::dng_big_table_cache () + + : fMutex () + + , fRefCounts () + + , fRecentlyUsed () + , fRecentlyUsedLimit (kDefaultRecentlyUsedLimit) + + { + + } + +/*****************************************************************************/ + +dng_big_table_cache::~dng_big_table_cache () + { + + } + +/*****************************************************************************/ + +void dng_big_table_cache::UseTable (dng_lock_std_mutex &lock, + const dng_fingerprint &fingerprint) + { + + // See if fingerprint is in recently used list. + + int32 lastIndex = (int32) fRecentlyUsed.size () - 1; + + for (int32 index = lastIndex; index >= 0; index--) + { + + if (fingerprint == fRecentlyUsed [index]) + { + + // Move to end of list if not already there. + + if (index != lastIndex) + { + + fRecentlyUsed.erase (fRecentlyUsed.begin () + index); + + fRecentlyUsed.push_back (fingerprint); + + } + + // Item is in recently used list, so we are done. + + return; + + } + + } + + // Is the recently used list full? + + if (fRecentlyUsed.size () == fRecentlyUsedLimit) + { + + CacheDecrement (lock, fRecentlyUsed.front ()); + + fRecentlyUsed.erase (fRecentlyUsed.begin ()); + + } + + // Add to end of list. + + fRecentlyUsed.push_back (fingerprint); + + CacheIncrement (lock, fingerprint); + + } + +/*****************************************************************************/ + +void dng_big_table_cache::FlushRecentlyUsed () + { + + dng_lock_std_mutex lock (fMutex); + + while (!fRecentlyUsed.empty ()) + { + + CacheDecrement (lock, fRecentlyUsed.back ()); + + fRecentlyUsed.pop_back (); + + } + + } + +/*****************************************************************************/ + +void dng_big_table_cache::CacheIncrement (dng_lock_std_mutex &lock, + const dng_fingerprint &fingerprint) + { + + if (fingerprint.IsValid ()) + { + + RefCountsMap::iterator it = fRefCounts.find (fingerprint); + + if (it == fRefCounts.end ()) + { + + DNG_REPORT ("dng_big_table_cache::CacheIncrement" + "fingerprint not in cache"); + + } + + else + { + + it->second++; + + UseTable (lock, fingerprint); + + } + + } + + } + +/*****************************************************************************/ + +void dng_big_table_cache::CacheDecrement (dng_lock_std_mutex &lock, + const dng_fingerprint &fingerprint) + { + + if (fingerprint.IsValid ()) + { + + RefCountsMap::iterator it = fRefCounts.find (fingerprint); + + if (it == fRefCounts.end ()) + { + + DNG_REPORT ("dng_big_table_cache::CacheDecrement" + "fingerprint not in cache"); + + } + + else if (--(it->second) == 0) + { + + fRefCounts.erase (it); + + EraseTableData (lock, fingerprint); + + } + + } + + } + +/*****************************************************************************/ + +void dng_big_table_cache::CacheAdd (dng_lock_std_mutex &lock, + const dng_big_table &table) + { + + if (table.Fingerprint ().IsValid ()) + { + + RefCountsMap::iterator it = fRefCounts.find (table.Fingerprint ()); + + if (it == fRefCounts.end ()) + { + + fRefCounts.insert (RefCountsPair (table.Fingerprint (), 1)); + + InsertTableData (lock, table); + + } + + else + { + + it->second++; + + } + + UseTable (lock, table.Fingerprint ()); + + } + + } + +/*****************************************************************************/ + +bool dng_big_table_cache::CacheExtract (dng_lock_std_mutex &lock, + const dng_fingerprint &fingerprint, + dng_big_table &table) + { + + if (fingerprint.IsValid ()) + { + + RefCountsMap::iterator it = fRefCounts.find (fingerprint); + + if (it != fRefCounts.end ()) + { + + it->second++; + + ExtractTableData (lock, fingerprint, table); + + UseTable (lock, fingerprint); + + return true; + + } + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_big_table_cache::Increment (dng_big_table_cache *cache, + const dng_fingerprint &fingerprint) + { + + if (cache) + { + + dng_lock_std_mutex lock (cache->fMutex); + + cache->CacheIncrement (lock, fingerprint); + + } + + } + +/*****************************************************************************/ + +void dng_big_table_cache::Decrement (dng_big_table_cache *cache, + const dng_fingerprint &fingerprint) + { + + if (cache) + { + + dng_lock_std_mutex lock (cache->fMutex); + + cache->CacheDecrement (lock, fingerprint); + + } + + } + +/*****************************************************************************/ + +void dng_big_table_cache::Add (dng_big_table_cache *cache, + const dng_big_table &table) + { + + if (cache) + { + + dng_lock_std_mutex lock (cache->fMutex); + + cache->CacheAdd (lock, table); + + } + + } + +/*****************************************************************************/ + +bool dng_big_table_cache::Extract (dng_big_table_cache *cache, + const dng_fingerprint &fingerprint, + dng_big_table &table) + { + + if (cache) + { + + dng_lock_std_mutex lock (cache->fMutex); + + return cache->CacheExtract (lock, fingerprint, table); + + } + + return false; + + } + +/*****************************************************************************/ + +class dng_look_table_cache : public dng_big_table_cache + { + + private: + + typedef std::pair TableDataPair; + + typedef std::map TableDataMap; + + TableDataMap fTableData; + + public: + + dng_look_table_cache () + + : fTableData () + + { + } + + virtual void InsertTableData (dng_lock_std_mutex & /* lock */, + const dng_big_table &table) + { + + const dng_look_table *lookTable = static_cast + + (&table); + + fTableData.insert (TableDataPair (lookTable->Fingerprint (), + lookTable->fData)); + + } + + virtual void EraseTableData (dng_lock_std_mutex & /* lock */, + const dng_fingerprint &fingerprint) + { + + TableDataMap::iterator it = fTableData.find (fingerprint); + + if (it != fTableData.end ()) + { + + fTableData.erase (it); + + } + + else + { + + DNG_REPORT ("dng_look_table_cache::EraseTableData" + "fingerprint not in cache"); + + } + + } + + virtual void ExtractTableData (dng_lock_std_mutex & /* lock */, + const dng_fingerprint &fingerprint, + dng_big_table &table) + { + + TableDataMap::iterator it = fTableData.find (fingerprint); + + if (it != fTableData.end ()) + { + + dng_look_table *lookTable = static_cast + + (&table); + + lookTable->fData = it->second; + + } + + else + { + + DNG_REPORT ("dng_look_table_cache::ExtractTableData" + "fingerprint not in cache"); + + } + + } + + }; + +static dng_look_table_cache gLookTableCache; + +/*****************************************************************************/ + +class dng_rgb_table_cache : public dng_big_table_cache + { + + private: + + typedef std::pair TableDataPair; + + typedef std::map TableDataMap; + + TableDataMap fTableData; + + public: + + dng_rgb_table_cache () + + : fTableData () + + { + } + + virtual void InsertTableData (dng_lock_std_mutex & /* lock */, + const dng_big_table &table) + { + + const dng_rgb_table *rgbTable = static_cast + + (&table); + + fTableData.insert (TableDataPair (rgbTable->Fingerprint (), + rgbTable->fData)); + + } + + virtual void EraseTableData (dng_lock_std_mutex & /* lock */, + const dng_fingerprint &fingerprint) + { + + TableDataMap::iterator it = fTableData.find (fingerprint); + + if (it != fTableData.end ()) + { + + fTableData.erase (it); + + } + + else + { + + DNG_REPORT ("dng_rgb_table_cache::EraseTableData" + "fingerprint not in cache"); + + } + + } + + virtual void ExtractTableData (dng_lock_std_mutex & /* lock */, + const dng_fingerprint &fingerprint, + dng_big_table &table) + { + + TableDataMap::iterator it = fTableData.find (fingerprint); + + if (it != fTableData.end ()) + { + + dng_rgb_table *rgbTable = static_cast + + (&table); + + rgbTable->fData = it->second; + + } + + else + { + + DNG_REPORT ("dng_rgb_table_cache::ExtractTableData" + "fingerprint not in cache"); + + } + + } + + }; + +static dng_rgb_table_cache gRGBTableCache; + +/*****************************************************************************/ + +void dng_big_table_cache_flush () + { + + gLookTableCache.FlushRecentlyUsed (); + + gRGBTableCache.FlushRecentlyUsed (); + + } + +/*****************************************************************************/ + +dng_big_table::dng_big_table (dng_big_table_cache *cache) + + : fFingerprint () + , fCache (cache) + , fIsMissing (false) + + { + + } + +/*****************************************************************************/ + +dng_big_table::dng_big_table (const dng_big_table &table) + + : fFingerprint (table.fFingerprint) + , fCache (table.fCache ) + , fIsMissing (false ) + + { + + dng_big_table_cache::Increment (fCache, fFingerprint); + + } + +/*****************************************************************************/ + +dng_big_table & dng_big_table::operator= (const dng_big_table &table) + { + + if (fFingerprint != table.fFingerprint || + fCache != table.fCache ) + { + + dng_big_table_cache::Decrement (fCache, fFingerprint); + + fFingerprint = table.fFingerprint; + fCache = table.fCache; + + dng_big_table_cache::Increment (fCache, fFingerprint); + + } + + return *this; + + } + +/*****************************************************************************/ + +dng_big_table::~dng_big_table () + { + + dng_big_table_cache::Decrement (fCache, fFingerprint); + + } + +/*****************************************************************************/ + +const dng_fingerprint & dng_big_table::Fingerprint () const + { + + return fFingerprint; + + } + +/*****************************************************************************/ + +void dng_big_table::RecomputeFingerprint () + { + + dng_big_table_cache::Decrement (fCache, fFingerprint); + + fFingerprint.Clear (); + + if (IsValid ()) + { + + { + + dng_md5_printer_stream stream; + + stream.SetLittleEndian (); + + PutStream (stream, true); + + fFingerprint = stream.Result (); + + } + + // Try extract first to force sharing of table data memory if + // table data is already in cache. + + if (!dng_big_table_cache::Extract (fCache, fFingerprint, *this)) + { + + // Otherwise add to cache. + + dng_big_table_cache::Add (fCache, *this); + + } + + } + + } + +/*****************************************************************************/ + +bool dng_big_table::DecodeFromBinary (const uint8 *compressedData, + uint32 compressedSize, + dng_memory_allocator &allocator) + { + + // Decompress the data. + + AutoPtr block3; + + { + + if (compressedSize < 5) + { + return false; + } + + // Uncompressed size is stored in first four bytes of decoded data, + // little endian order. + + uint32 uncompressedSize = (((uint32) compressedData [0]) ) + + (((uint32) compressedData [1]) << 8) + + (((uint32) compressedData [2]) << 16) + + (((uint32) compressedData [3]) << 24); + + block3.Reset (allocator.Allocate (uncompressedSize)); + + uLongf destLen = uncompressedSize; + + int zResult = ::uncompress (block3->Buffer_uint8 (), + &destLen, + compressedData + 4, + compressedSize - 4); + + if (zResult != Z_OK) + { + return false; + } + + } + + // Now read in the table data from the uncompressed stream. + + { + + dng_stream stream (block3->Buffer (), + block3->LogicalSize ()); + + stream.SetLittleEndian (); + + GetStream (stream); + + block3.Reset (); + + } + + // Force recomputation of fingerprint. + + RecomputeFingerprint (); + + return true; + + } + +/*****************************************************************************/ + +bool dng_big_table::DecodeFromString (const dng_string &block1, + dng_memory_allocator &allocator) + { + + // Decode the text to binary. + + AutoPtr block2; + + uint32 compressedSize = 0; + + { + + // This binary to text encoding is very similar to the Z85 + // encoding, but the exact charactor set has been adjusted to + // encode more cleanly into XMP. + + static uint8 kDecodeTable [96] = + { + + 0xFF, // space + 0x44, // ! + 0xFF, // " + 0x54, // # + 0x53, // $ + 0x52, // % + 0xFF, // & + 0x49, // ' + 0x4B, // ( + 0x4C, // ) + 0x46, // * + 0x41, // + + 0xFF, // , + 0x3F, // - + 0x3E, // . + 0x45, // / + + 0x00, 0x01, 0x02, 0x03, 0x04, // 01234 + 0x05, 0x06, 0x07, 0x08, 0x09, // 56789 + + 0x40, // : + 0xFF, // ; + 0xFF, // < + 0x42, // = + 0xFF, // > + 0x47, // ? + 0x51, // @ + + 0x24, 0x25, 0x26, 0x27, 0x28, // ABCDE + 0x29, 0x2A, 0x2B, 0x2C, 0x2D, // FGHIJ + 0x2E, 0x2F, 0x30, 0x31, 0x32, // KLMNO + 0x33, 0x34, 0x35, 0x36, 0x37, // PQRST + 0x38, 0x39, 0x3A, 0x3B, 0x3C, // UVWXY + 0x3D, // Z + + 0x4D, // [ + 0xFF, // backslash + 0x4E, // ] + 0x43, // ^ + 0xFF, // _ + 0x48, // ` + + 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, // abcde + 0x0F, 0x10, 0x11, 0x12, 0x13, // fghij + 0x14, 0x15, 0x16, 0x17, 0x18, // klmno + 0x19, 0x1A, 0x1B, 0x1C, 0x1D, // pqrst + 0x1E, 0x1F, 0x20, 0x21, 0x22, // uvwxy + 0x23, // z + + 0x4F, // { + 0x4A, // | + 0x50, // } + 0xFF, // ~ + 0xFF // del + + }; + + uint32 encodedSize = block1.Length (); + + uint32 maxCompressedSize = (encodedSize + 4) / 5 * 4; + + block2.Reset (allocator.Allocate (maxCompressedSize)); + + uint32 phase = 0; + uint32 value; + + uint8 *dPtr = block2->Buffer_uint8 (); + + for (const char *sPtr = block1.Get (); *sPtr; sPtr++) + { + + uint8 e = (uint8) *sPtr; + + if (e < 32 || e > 127) + { + continue; + } + + uint32 d = kDecodeTable [e - 32]; + + if (d > 85) + { + continue; + } + + phase++; + + if (phase == 1) + { + value = d; + } + + else if (phase == 2) + { + value += d * 85; + } + + else if (phase == 3) + { + value += d * (85 * 85); + } + + else if (phase == 4) + { + value += d * (85 * 85 * 85); + } + + else + { + + value += d * (85 * 85 * 85 * 85); + + dPtr [0] = (uint8) (value ); + dPtr [1] = (uint8) (value >> 8); + dPtr [2] = (uint8) (value >> 16); + dPtr [3] = (uint8) (value >> 24); + + dPtr += 4; + + compressedSize += 4; + + phase = 0; + + } + + } + + if (phase > 3) + { + + dPtr [2] = (uint8) (value >> 16); + + compressedSize++; + + } + + if (phase > 2) + { + + dPtr [1] = (uint8) (value >> 8); + + compressedSize++; + + } + + if (phase > 1) + { + + dPtr [0] = (uint8) (value); + + compressedSize++; + + } + + } + + return DecodeFromBinary (block2->Buffer_uint8 (), + compressedSize, + allocator); + + } + +/*****************************************************************************/ + +dng_memory_block* dng_big_table::EncodeAsBinary (dng_memory_allocator &allocator, + uint32 &compressedSize) const + { + + // Stream to a binary block.. + + AutoPtr block1; + + { + + dng_memory_stream stream (allocator); + + stream.SetLittleEndian (); + + PutStream (stream, false); + + block1.Reset (stream.AsMemoryBlock (allocator)); + + } + + // Compress the block. + + AutoPtr block2; + + { + + uint32 uncompressedSize = block1->LogicalSize (); + + uint32 safeCompressedSize = uncompressedSize + (uncompressedSize >> 8) + 64; + + block2.Reset (allocator.Allocate (safeCompressedSize + 4)); + + // Store uncompressed size in first four bytes of compressed block. + + uint8 *dPtr = block2->Buffer_uint8 (); + + dPtr [0] = (uint8) (uncompressedSize ); + dPtr [1] = (uint8) (uncompressedSize >> 8); + dPtr [2] = (uint8) (uncompressedSize >> 16); + dPtr [3] = (uint8) (uncompressedSize >> 24); + + uLongf dCount = safeCompressedSize; + + int zResult = ::compress2 (dPtr + 4, + &dCount, + block1->Buffer_uint8 (), + uncompressedSize, + Z_DEFAULT_COMPRESSION); + + if (zResult != Z_OK) + { + ThrowMemoryFull (); + } + + compressedSize = (uint32) dCount + 4; + + block1.Reset (); + + } + + return block2.Release (); + + } + +/*****************************************************************************/ + +dng_memory_block* dng_big_table::EncodeAsString (dng_memory_allocator &allocator) const + { + + // Get compressed binary data. + + uint32 compressedSize; + + AutoPtr block2 (EncodeAsBinary (allocator, compressedSize)); + + // Encode binary to text. + + AutoPtr block3; + + { + + // This binary to text encoding is very similar to the Z85 + // encoding, but the exact charactor set has been adjusted to + // encode more cleanly into XMP. + + static const char *kEncodeTable = + "0123456789" + "abcdefghij" + "klmnopqrst" + "uvwxyzABCD" + "EFGHIJKLMN" + "OPQRSTUVWX" + "YZ.-:+=^!/" + "*?`'|()[]{" + "}@%$#"; + + uint32 safeEncodedSize = compressedSize + + (compressedSize >> 2) + + (compressedSize >> 6) + + 16; + + block3.Reset (allocator.Allocate (safeEncodedSize)); + + uint8 *sPtr = block2->Buffer_uint8 (); + + sPtr [compressedSize ] = 0; + sPtr [compressedSize + 1] = 0; + sPtr [compressedSize + 2] = 0; + + uint8 *dPtr = block3->Buffer_uint8 (); + + while (compressedSize) + { + + uint32 x0 = (((uint32) sPtr [0]) ) + + (((uint32) sPtr [1]) << 8) + + (((uint32) sPtr [2]) << 16) + + (((uint32) sPtr [3]) << 24); + + sPtr += 4; + + uint32 x1 = x0 / 85; + + *dPtr++ = kEncodeTable [x0 - x1 * 85]; + + uint32 x2 = x1 / 85; + + *dPtr++ = kEncodeTable [x1 - x2 * 85]; + + if (!--compressedSize) + break; + + uint32 x3 = x2 / 85; + + *dPtr++ = kEncodeTable [x2 - x3 * 85]; + + if (!--compressedSize) + break; + + uint32 x4 = x3 / 85; + + *dPtr++ = kEncodeTable [x3 - x4 * 85]; + + if (!--compressedSize) + break; + + *dPtr++ = kEncodeTable [x4]; + + compressedSize--; + + } + + *dPtr = 0; + + block2.Reset (); + + } + + return block3.Release (); + + } + +/*****************************************************************************/ + +bool dng_big_table::ExtractFromCache (const dng_fingerprint &fingerprint) + { + + if (dng_big_table_cache::Extract (fCache, fingerprint, *this)) + { + + fFingerprint = fingerprint; + + return true; + + } + + return false; + + } + +/*****************************************************************************/ + +bool dng_big_table::ReadTableFromXMP (const dng_xmp &xmp, + const char *ns, + const dng_fingerprint &fingerprint) + { + + // Read in the table data. + + dng_string tablePath; + + tablePath.Set ("Table_"); + + tablePath.Append (dng_xmp::EncodeFingerprint (fingerprint).Get ()); + + dng_string block1; + + if (!xmp.GetString (ns, + tablePath.Get (), + block1)) + { + + DNG_REPORT ("Missing big table data"); + + return false; + + } + + bool ok = DecodeFromString (block1, + xmp.Allocator ()); + + block1.Clear (); + + // Validate fingerprint match. + + DNG_ASSERT (Fingerprint () == fingerprint, + "dng_big_table fingerprint mismatch"); + + // It worked! + + return ok; + + } + +/*****************************************************************************/ + +bool dng_big_table::ReadFromXMP (const dng_xmp &xmp, + const char *ns, + const char *path, + dng_big_table_storage &storage) + { + + dng_fingerprint fingerprint; + + if (!xmp.GetFingerprint (ns, path, fingerprint)) + { + + return false; + + } + + // See if we can skip reading the table data, and just grab from cache. + + if (ExtractFromCache (fingerprint)) + { + + return true; + + } + + // Next see if we can get the table from the storage object. + + if (storage.ReadTable (*this, fingerprint, xmp.Allocator ())) + { + + return true; + + } + + // Read in the table data. + + if (ReadTableFromXMP (xmp, ns, fingerprint)) + { + + return true; + + } + + // Unable to find table data anywhere. Notify storage object. + + storage.MissingTable (fingerprint); + + // Also make a note that this table is missing. + + SetMissing (); + + return false; + + } + +/*****************************************************************************/ + +void dng_big_table::WriteToXMP (dng_xmp &xmp, + const char *ns, + const char *path, + dng_big_table_storage &storage) const + { + + const dng_fingerprint &fingerprint = Fingerprint (); + + if (!fingerprint.IsValid () || IsMissing ()) + { + + xmp.Remove (ns, path); + + return; + + } + + xmp.SetFingerprint (ns, path, fingerprint); + + // See if we can just use the storage object to store the table. + + if (storage.WriteTable (*this, fingerprint, xmp.Allocator ())) + { + + return; + + } + + dng_string tablePath; + + tablePath.Set ("Table_"); + + tablePath.Append (dng_xmp::EncodeFingerprint (fingerprint).Get ()); + + if (xmp.Exists (ns, tablePath.Get ())) + { + + return; + + } + + AutoPtr block; + + block.Reset (EncodeAsString (xmp.Allocator ())); + + xmp.Set (ns, + tablePath.Get (), + block->Buffer_char ()); + + } + +/*****************************************************************************/ + +dng_big_table_storage::dng_big_table_storage () + { + + } + +/*****************************************************************************/ + +dng_big_table_storage::~dng_big_table_storage () + { + + } + +/*****************************************************************************/ + +bool dng_big_table_storage::ReadTable (dng_big_table & /* table */, + const dng_fingerprint & /* fingerprint */, + dng_memory_allocator & /* allocator */) + { + + return false; + + } + +/*****************************************************************************/ + +bool dng_big_table_storage::WriteTable (const dng_big_table & /* table */, + const dng_fingerprint & /* fingerprint */, + dng_memory_allocator & /* allocator */) + { + + return false; + + } + +/*****************************************************************************/ + +void dng_big_table_storage::MissingTable (const dng_fingerprint & /* fingerprint */) + { + + } + +/*****************************************************************************/ + +dng_look_table::dng_look_table () + + : dng_big_table (&gLookTableCache) + + , fData () + , fAmount (1.0) + + { + + } + +/*****************************************************************************/ + +dng_look_table::dng_look_table (const dng_look_table &table) + + : dng_big_table (table) + + , fData (table.fData) + , fAmount (table.fAmount) + + { + + } + +/*****************************************************************************/ + +dng_look_table & dng_look_table::operator= (const dng_look_table &table) + { + + dng_big_table::operator= (table); + + fData = table.fData; + fAmount = table.fAmount; + + return *this; + + } + +/*****************************************************************************/ + +dng_look_table::~dng_look_table () + { + + } + +/*****************************************************************************/ + +void dng_look_table::Set (const dng_hue_sat_map &map, + uint32 encoding) + { + + fData.fMap = map; + fData.fEncoding = encoding; + + fData.ComputeMonochrome (); + + RecomputeFingerprint (); + + } + +/*****************************************************************************/ + +bool dng_look_table::IsValid () const + { + + if (IsMissing ()) + { + return false; + } + + return fData.fMap.IsValid (); + + } + +/*****************************************************************************/ + +void dng_look_table::SetInvalid () + { + + *this = dng_look_table (); + + } + +/*****************************************************************************/ + +void dng_look_table::GetStream (dng_stream &stream) + { + + table_data data; + + if (stream.Get_uint32 () != btt_LookTable) + { + ThrowBadFormat ("Not a look table"); + } + + uint32 version = stream.Get_uint32 (); + + if (version != kLookTableVersion1 && + version != kLookTableVersion2) + { + ThrowBadFormat ("Unknown look table version"); + } + + uint32 hueDivisions = stream.Get_uint32 (); + uint32 satDivisions = stream.Get_uint32 (); + uint32 valDivisions = stream.Get_uint32 (); + + if (hueDivisions < 1 || hueDivisions > kMaxHueSamples || + satDivisions < 1 || satDivisions > kMaxSatSamples || + valDivisions < 1 || valDivisions > kMaxValSamples || + (dng_safe_uint32 (hueDivisions) * + dng_safe_uint32 (satDivisions) * + dng_safe_uint32 (valDivisions)).Get () > kMaxTotalSamples) + { + ThrowBadFormat (); + } + + data.fMap.SetDivisions (hueDivisions, + satDivisions, + valDivisions); + + uint32 count = data.fMap.DeltasCount (); + + dng_hue_sat_map::HSBModify * deltas = data.fMap.GetDeltas (); + + for (uint32 index = 0; index < count; index++) + { + + deltas->fHueShift = stream.Get_real32 (); + deltas->fSatScale = stream.Get_real32 (); + deltas->fValScale = stream.Get_real32 (); + + deltas++; + + } + + data.fMap.AssignNewUniqueRuntimeFingerprint (); + + data.fEncoding = stream.Get_uint32 (); + + if (data.fEncoding != encoding_Linear && + data.fEncoding != encoding_sRGB) + { + ThrowBadFormat ("Unknown look table encoding"); + } + + if (version != kLookTableVersion1) + { + + data.fMinAmount = stream.Get_real64 (); + data.fMaxAmount = stream.Get_real64 (); + + if (data.fMinAmount < 0.0 || data.fMinAmount > 1.0 || data.fMaxAmount < 1.0) + { + ThrowBadFormat ("Invalid min/max amount for look table"); + } + + } + + else + { + + data.fMinAmount = 1.0; + data.fMaxAmount = 1.0; + + } + + data.ComputeMonochrome (); + + fData = data; + + } + +/*****************************************************************************/ + +void dng_look_table::PutStream (dng_stream &stream, + bool /* forFingerprint */) const + { + + DNG_REQUIRE (IsValid (), "Invalid Look Table"); + + stream.Put_uint32 (btt_LookTable); + + uint32 version = kLookTableVersion1; + + if (fData.fMinAmount != 1.0 || + fData.fMaxAmount != 1.0) + { + version = kLookTableVersion2; + } + + stream.Put_uint32 (version); + + uint32 hueDivisions; + uint32 satDivisions; + uint32 valDivisions; + + fData.fMap.GetDivisions (hueDivisions, + satDivisions, + valDivisions); + + stream.Put_uint32 (hueDivisions); + stream.Put_uint32 (satDivisions); + stream.Put_uint32 (valDivisions); + + uint32 count = fData.fMap.DeltasCount (); + + const dng_hue_sat_map::HSBModify * deltas = fData.fMap.GetConstDeltas (); + + for (uint32 index = 0; index < count; index++) + { + + stream.Put_real32 (deltas->fHueShift); + stream.Put_real32 (deltas->fSatScale); + stream.Put_real32 (deltas->fValScale); + + deltas++; + + } + + stream.Put_uint32 (fData.fEncoding); + + if (version != kLookTableVersion1) + { + + stream.Put_real64 (fData.fMinAmount); + stream.Put_real64 (fData.fMaxAmount); + + } + + } + +/*****************************************************************************/ + +dng_rgb_table::dng_rgb_table () + + : dng_big_table (&gRGBTableCache) + + , fData () + , fAmount (1.0) + + { + + } + +/*****************************************************************************/ + +dng_rgb_table::dng_rgb_table (const dng_rgb_table &table) + + : dng_big_table (table) + + , fData (table.fData) + , fAmount (table.fAmount) + + { + + } + +/*****************************************************************************/ + +dng_rgb_table & dng_rgb_table::operator= (const dng_rgb_table &table) + { + + dng_big_table::operator= (table); + + fData = table.fData; + fAmount = table.fAmount; + + return *this; + + } + +/*****************************************************************************/ + +dng_rgb_table::~dng_rgb_table () + { + + } + +/*****************************************************************************/ + +bool dng_rgb_table::IsValid () const + { + + if (IsMissing ()) + { + return false; + } + + // If table itself is invalid, then invalid. + + if (fData.fDimensions == 0) + return false; + + // If table has some effect, then valid. + + if (fAmount > 0.0) + return true; + + // Does the matrix itself do any clipping? + + if (fData.fPrimaries == primaries_ProPhoto || + fData.fGamut == gamut_extend ) + return false; + + // Table is a NOP but there is some gamut clipping. + + return true; + + } + +/*****************************************************************************/ + +void dng_rgb_table::SetInvalid () + { + + *this = dng_rgb_table (); + + } + +/*****************************************************************************/ + +void dng_rgb_table::Set (uint32 dimensions, + uint32 divisions, + dng_ref_counted_block samples) + { + + if (dimensions == 1) + { + + if (divisions < kMinDivisions1D || + divisions > kMaxDivisions1D) + { + + ThrowProgramError ("Bad 1D divisions"); + + } + + if (samples.LogicalSize () != divisions * 4 * sizeof (uint16)) + { + + ThrowProgramError ("Bad 1D sample count"); + + } + + } + + else if (dimensions == 3) + { + + if (divisions < kMinDivisions3D || + divisions > kMaxDivisions3D_InMemory) + { + + ThrowProgramError ("Bad 3D divisions"); + + } + + if (samples.LogicalSize () != divisions * + divisions * + divisions * 4 * sizeof (uint16)) + { + + ThrowProgramError ("Bad 3D sample count"); + + } + + } + + else + { + + ThrowProgramError ("Bad dimensions"); + + } + + fData.fDimensions = dimensions; + + fData.fDivisions = divisions; + + fData.fSamples = samples; + + fData.ComputeMonochrome (); + + RecomputeFingerprint (); + + } + +/*****************************************************************************/ + +void dng_rgb_table::GetStream (dng_stream &stream) + { + + table_data data; + + if (stream.Get_uint32 () != btt_RGBTable) + { + ThrowBadFormat ("Not a RGB table"); + } + + if (stream.Get_uint32 () != kRGBTableVersion) + { + ThrowBadFormat ("Unknown RGB table version"); + } + + data.fDimensions = stream.Get_uint32 (); + + data.fDivisions = stream.Get_uint32 (); + + if (data.fDimensions == 1) + { + + if (data.fDivisions < kMinDivisions1D || + data.fDivisions > kMaxDivisions1D) + { + ThrowBadFormat ("Invalid 1D divisions"); + } + + } + + else if (data.fDimensions == 3) + { + + if (data.fDivisions < kMinDivisions3D || + data.fDivisions > kMaxDivisions3D) + { + ThrowBadFormat ("Invalid 3D divisions"); + } + + } + + else + { + ThrowBadFormat ("Invalid dimensions"); + } + + uint16 nopValue [kMaxDivisions1D > kMaxDivisions3D ? kMaxDivisions1D + : kMaxDivisions3D]; + + for (uint32 index = 0; index < data.fDivisions; index++) + { + + nopValue [index] = (uint16) + ((index * 0x0FFFF + (data.fDivisions >> 1)) / + (data.fDivisions - 1)); + + } + + if (data.fDimensions == 1) + { + + data.fSamples.Allocate (data.fDivisions * 4 * sizeof (uint16)); + + uint16 *samples = data.fSamples.Buffer_uint16 (); + + for (uint32 index = 0; index < data.fDivisions; index++) + { + + samples [0] = stream.Get_uint16 () + nopValue [index]; + samples [1] = stream.Get_uint16 () + nopValue [index]; + samples [2] = stream.Get_uint16 () + nopValue [index]; + samples [3] = 0; + + samples += 4; + + } + + } + + else + { + + data.fSamples.Allocate (data.fDivisions * + data.fDivisions * + data.fDivisions * 4 * sizeof (uint16)); + + uint16 *samples = data.fSamples.Buffer_uint16 (); + + for (uint32 rIndex = 0; rIndex < data.fDivisions; rIndex++) + { + + for (uint32 gIndex = 0; gIndex < data.fDivisions; gIndex++) + { + + for (uint32 bIndex = 0; bIndex < data.fDivisions; bIndex++) + { + + samples [0] = stream.Get_uint16 () + nopValue [rIndex]; + samples [1] = stream.Get_uint16 () + nopValue [gIndex]; + samples [2] = stream.Get_uint16 () + nopValue [bIndex]; + samples [3] = 0; + + samples += 4; + + } + + } + + } + + } + + uint32 primaries = stream.Get_uint32 (); + + if (primaries >= primaries_count) + { + ThrowBadFormat ("Unknown RGB table primaries"); + } + + data.fPrimaries = (primaries_enum) primaries; + + uint32 gamma = stream.Get_uint32 (); + + if (gamma >= gamma_count) + { + ThrowBadFormat ("Unknown RGB table gamma"); + } + + data.fGamma = (gamma_enum) gamma; + + uint32 gamut = stream.Get_uint32 (); + + if (gamut >= gamut_count) + { + ThrowBadFormat ("Unknown RGB table gamut processing option"); + } + + data.fGamut = (gamut_enum) gamut; + + data.fMinAmount = stream.Get_real64 (); + data.fMaxAmount = stream.Get_real64 (); + + if (data.fMinAmount < 0.0 || data.fMinAmount > 1.0 || data.fMaxAmount < 1.0) + { + ThrowBadFormat ("Invalid min/max amount for RGB table"); + } + + data.ComputeMonochrome (); + + fData = data; + + } + +/*****************************************************************************/ + +void dng_rgb_table::PutStream (dng_stream &stream, + bool /* forFingerprint */) const + { + + DNG_REQUIRE (IsValid (), "Invalid RGB Table"); + + stream.Put_uint32 (btt_RGBTable); + + stream.Put_uint32 (kRGBTableVersion); + + stream.Put_uint32 (fData.fDimensions); + + stream.Put_uint32 (fData.fDivisions); + + uint16 nopValue [kMaxDivisions1D > kMaxDivisions3D_InMemory ? kMaxDivisions1D + : kMaxDivisions3D_InMemory]; + + for (uint32 index = 0; index < fData.fDivisions; index++) + { + + nopValue [index] = (uint16) + ((index * 0x0FFFF + (fData.fDivisions >> 1)) / + (fData.fDivisions - 1)); + + } + + const uint16 *samples = fData.fSamples.Buffer_uint16 (); + + if (fData.fDimensions == 1) + { + + for (uint32 index = 0; index < fData.fDivisions; index++) + { + + stream.Put_uint16 (samples [0] - nopValue [index]); + stream.Put_uint16 (samples [1] - nopValue [index]); + stream.Put_uint16 (samples [2] - nopValue [index]); + + samples += 4; + + } + + } + + else + { + + for (uint32 rIndex = 0; rIndex < fData.fDivisions; rIndex++) + { + + for (uint32 gIndex = 0; gIndex < fData.fDivisions; gIndex++) + { + + for (uint32 bIndex = 0; bIndex < fData.fDivisions; bIndex++) + { + + stream.Put_uint16 (samples [0] - nopValue [rIndex]); + stream.Put_uint16 (samples [1] - nopValue [gIndex]); + stream.Put_uint16 (samples [2] - nopValue [bIndex]); + + samples += 4; + + } + + } + + } + + } + + stream.Put_uint32 ((uint32) fData.fPrimaries); + + stream.Put_uint32 ((uint32) fData.fGamma); + + stream.Put_uint32 ((uint32) fData.fGamut); + + stream.Put_real64 (fData.fMinAmount); + stream.Put_real64 (fData.fMaxAmount); + + } + +/*****************************************************************************/ diff --git a/dng/dng_big_table.h b/dng/dng_big_table.h new file mode 100644 index 0000000..a1ae678 --- /dev/null +++ b/dng/dng_big_table.h @@ -0,0 +1,670 @@ +/*****************************************************************************/ +// Copyright 2015-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef __dng_big_table__ +#define __dng_big_table__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_camera_profile.h" + +/*****************************************************************************/ + +class dng_big_table_cache; +class dng_big_table_storage; + +/*****************************************************************************/ + +void dng_big_table_cache_flush (); + +/*****************************************************************************/ + +class dng_big_table + { + + protected: + + enum BigTableTypeEnum + { + btt_LookTable = 0, + btt_RGBTable = 1 + }; + + private: + + dng_fingerprint fFingerprint; + + dng_big_table_cache * fCache; + + bool fIsMissing; + + protected: + + dng_big_table (dng_big_table_cache *cache); + + dng_big_table (const dng_big_table &table); + + dng_big_table & operator= (const dng_big_table &table); + + public: + + virtual ~dng_big_table (); + + bool IsMissing () const + { + return fIsMissing; + } + + void SetMissing () + { + fIsMissing = true; + } + + virtual bool IsValid () const = 0; + + const dng_fingerprint & Fingerprint () const; + + bool DecodeFromBinary (const uint8 * compressedData, + uint32 compressedSize, + dng_memory_allocator &allocator); + + bool DecodeFromString (const dng_string &block1, + dng_memory_allocator &allocator); + + dng_memory_block * EncodeAsBinary (dng_memory_allocator &allocator, + uint32 &compressedSize) const; + + dng_memory_block * EncodeAsString (dng_memory_allocator &allocator) const; + + bool ExtractFromCache (const dng_fingerprint &fingerprint); + + bool ReadTableFromXMP (const dng_xmp &xmp, + const char *ns, + const dng_fingerprint &fingerprint); + + bool ReadFromXMP (const dng_xmp &xmp, + const char *ns, + const char *path, + dng_big_table_storage &storage); + + void WriteToXMP (dng_xmp &xmp, + const char *ns, + const char *path, + dng_big_table_storage &storage) const; + + protected: + + void RecomputeFingerprint (); + + virtual void GetStream (dng_stream &stream) = 0; + + virtual void PutStream (dng_stream &stream, + bool forFingerprint) const = 0; + + }; + +/*****************************************************************************/ + +class dng_big_table_storage + { + + public: + + dng_big_table_storage (); + + virtual ~dng_big_table_storage (); + + virtual bool ReadTable (dng_big_table &table, + const dng_fingerprint &fingerprint, + dng_memory_allocator &allocator); + + virtual bool WriteTable (const dng_big_table &table, + const dng_fingerprint &fingerprint, + dng_memory_allocator &allocator); + + virtual void MissingTable (const dng_fingerprint &fingerprint); + + }; + +/*****************************************************************************/ + +class dng_look_table : public dng_big_table + { + + friend class dng_look_table_cache; + + public: + + enum + { + + // Tables are are allowed to trade off resolution + // between dimensions, but need to keep total + // samples below this limit. This results in + // 216K memory footprint (12 byte per sample) + // which is similar in size to the RGB table + // size limit. + + kMaxTotalSamples = 36 * 32 * 16, + + // Also each must be within their own limits. + + kMaxHueSamples = 360, + kMaxSatSamples = 256, + kMaxValSamples = 256 + + }; + + private: + + enum + { + kLookTableVersion1 = 1, + kLookTableVersion2 = 2 + }; + + // Table data affecting fingerprint and caching. + + struct table_data + { + + // 3-D hue/sat table to apply a "look". + + dng_hue_sat_map fMap; + + // Value (V of HSV) encoding for look table. + + uint32 fEncoding; + + // Minimum and maximum scale amounts supported by table. + + real64 fMinAmount; + real64 fMaxAmount; + + // Does this table have only monochrome output (when amount is 1.0)? + + bool fMonochrome; + + // Constructor to set defaults. + + table_data () + + : fMap () + , fEncoding (encoding_Linear) + , fMinAmount (1.0) + , fMaxAmount (1.0) + , fMonochrome (false) + + { + } + + // Compute monchrome flag. + + void ComputeMonochrome () + { + + fMonochrome = true; + + uint32 count = fMap.DeltasCount (); + + dng_hue_sat_map::HSBModify * deltas = fMap.GetDeltas (); + + for (uint32 index = 0; index < count; index++) + { + + if (deltas [index] . fSatScale != 0.0f) + { + + fMonochrome = false; + + return; + + } + + } + + } + + }; + + table_data fData; + + // Amount to apply at runtime (does not affect fingerprint). + + real64 fAmount; + + public: + + dng_look_table (); + + dng_look_table (const dng_look_table &table); + + dng_look_table & operator= (const dng_look_table &table); + + virtual ~dng_look_table (); + + bool operator== (const dng_look_table &table) const + { + return Fingerprint () == table.Fingerprint () && + Amount () == table.Amount () && + IsMissing () == table.IsMissing (); + } + + bool operator!= (const dng_look_table &table) const + { + return !(*this == table); + } + + void Set (const dng_hue_sat_map &map, + uint32 encoding); + + virtual bool IsValid () const; + + void SetInvalid (); + + real64 MinAmount () const + { + return fData.fMinAmount; + } + + real64 MaxAmount () const + { + return fData.fMaxAmount; + } + + void SetAmountRange (real64 minAmount, + real64 maxAmount) + { + + fData.fMinAmount = Pin_real64 (0.0, + Round_int32 (minAmount * 100.0) * 0.01, + 1.0); + + fData.fMaxAmount = Pin_real64 (1.0, + Round_int32 (maxAmount * 100.0) * 0.01, + 2.0); + + fAmount = Pin_real64 (fData.fMinAmount, fAmount, fData.fMaxAmount); + + RecomputeFingerprint (); + + } + + real64 Amount () const + { + return fAmount; + } + + void SetAmount (real64 amount) + { + + fAmount = Pin_real64 (fData.fMinAmount, + Round_int32 (amount * 100.0) * 0.01, + fData.fMaxAmount); + + // Not part of fingerprint. + + } + + const dng_hue_sat_map & Map () const + { + return fData.fMap; + } + + uint32 Encoding () const + { + return fData.fEncoding; + } + + bool Monochrome () const + { + return IsValid () && fAmount == 1.0 && fData.fMonochrome; + } + + protected: + + virtual void GetStream (dng_stream &stream); + + virtual void PutStream (dng_stream &stream, + bool forFingerprint) const; + + }; + +/*****************************************************************************/ + +class dng_rgb_table : public dng_big_table + { + + friend class dng_rgb_table_cache; + + public: + + enum + { + + kMinDivisions1D = 2, + kMaxDivisions1D = 4096, + + kMinDivisions3D = 2, + kMaxDivisions3D = 32, + + kMaxDivisions3D_InMemory = 128 + + }; + + enum primaries_enum + { + + primaries_sRGB = 0, + primaries_Adobe, + primaries_ProPhoto, + primaries_P3, + primaries_Rec2020, + + primaries_count + + }; + + enum gamma_enum + { + + gamma_Linear = 0, + gamma_sRGB, + gamma_1_8, + gamma_2_2, + gamma_Rec2020, + + gamma_count + + }; + + enum gamut_enum + { + + gamut_clip = 0, + gamut_extend, + + gamut_count + + }; + + private: + + enum + { + kRGBTableVersion = 1 + }; + + // Table data affecting fingerprint and caching. + + struct table_data + { + + // Number of dimensions of the table (1 or 3). + + uint32 fDimensions; + + // Number of samples per side of table. + + uint32 fDivisions; + + // Sample data. 16-bit unsigned encoding. + // Right zero padded to 64 bits per sample (i.e. RGB0). + + dng_ref_counted_block fSamples; + + // Color primaries for table. + + primaries_enum fPrimaries; + + // Gamma encoding for table. + + gamma_enum fGamma; + + // Gamut processing option for table. + + gamut_enum fGamut; + + // Minimum and maximum scale amounts supported by table. + + real64 fMinAmount; + real64 fMaxAmount; + + // Does this table have only monochrome output (when amount is 1.0)? + + bool fMonochrome; + + // Constructor to set defaults. + + table_data () + + : fDimensions (0) + , fDivisions (0) + , fSamples () + , fPrimaries (primaries_sRGB) + , fGamma (gamma_sRGB) + , fGamut (gamut_clip) + , fMinAmount (0.0) + , fMaxAmount (2.0) + , fMonochrome (false) + + { + } + + // Compute monchrome flag. + + void ComputeMonochrome () + { + + if (fPrimaries != primaries_ProPhoto && + fGamut != gamut_clip) + { + + fMonochrome = false; + + return; + + } + + if (fDimensions != 3) + { + + fMonochrome = false; + + return; + + } + + fMonochrome = true; + + uint32 count = fDivisions * fDivisions * fDivisions; + + const uint16 * sample = fSamples.Buffer_uint16 (); + + for (uint32 index = 0; index < count; index++) + { + + if (sample [0] != sample [1] || + sample [0] != sample [2]) + { + + fMonochrome = false; + + return; + + } + + sample += 4; + + } + + } + + }; + + table_data fData; + + // Amount to apply at runtime (does not affect fingerprint). + + real64 fAmount; + + public: + + dng_rgb_table (); + + dng_rgb_table (const dng_rgb_table &table); + + dng_rgb_table & operator= (const dng_rgb_table &table); + + virtual ~dng_rgb_table (); + + bool operator== (const dng_rgb_table &table) const + { + return Fingerprint () == table.Fingerprint () && + Amount () == table.Amount () && + IsMissing () == table.IsMissing (); + } + + bool operator!= (const dng_rgb_table &table) const + { + return !(*this == table); + } + + virtual bool IsValid () const; + + void SetInvalid (); + + primaries_enum Primaries () const + { + return fData.fPrimaries; + } + + void SetPrimaries (primaries_enum primaries) + { + + fData.fPrimaries = primaries; + + fData.ComputeMonochrome (); + + RecomputeFingerprint (); + + } + + gamma_enum Gamma () const + { + return fData.fGamma; + } + + void SetGamma (gamma_enum gamma) + { + + fData.fGamma = gamma; + + RecomputeFingerprint (); + + } + + gamut_enum Gamut () const + { + return fData.fGamut; + } + + void SetGamut (gamut_enum gamut) + { + + fData.fGamut = gamut; + + fData.ComputeMonochrome (); + + RecomputeFingerprint (); + + } + + real64 MinAmount () const + { + return fData.fMinAmount; + } + + real64 MaxAmount () const + { + return fData.fMaxAmount; + } + + void SetAmountRange (real64 minAmount, + real64 maxAmount) + { + + fData.fMinAmount = Pin_real64 (0.0, + Round_int32 (minAmount * 100.0) * 0.01, + 1.0); + + fData.fMaxAmount = Pin_real64 (1.0, + Round_int32 (maxAmount * 100.0) * 0.01, + 2.0); + + fAmount = Pin_real64 (fData.fMinAmount, fAmount, fData.fMaxAmount); + + RecomputeFingerprint (); + + } + + real64 Amount () const + { + return fAmount; + } + + void SetAmount (real64 amount) + { + + fAmount = Pin_real64 (fData.fMinAmount, + Round_int32 (amount * 100.0) * 0.01, + fData.fMaxAmount); + + // Not part of fingerprint. + + } + + uint32 Dimensions () const + { + return fData.fDimensions; + } + + uint32 Divisions () const + { + return fData.fDivisions; + } + + const uint16 * Samples () const + { + return fData.fSamples.Buffer_uint16 (); + } + + bool Monochrome () const + { + return IsValid () && fAmount == 1.0 && fData.fMonochrome; + } + + void Set (uint32 dimensions, + uint32 divisions, + dng_ref_counted_block samples); + + protected: + + virtual void GetStream (dng_stream &stream); + + virtual void PutStream (dng_stream &stream, + bool forFingerprint) const; + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_bottlenecks.cpp b/dng/dng_bottlenecks.cpp new file mode 100644 index 0000000..4c857ef --- /dev/null +++ b/dng/dng_bottlenecks.cpp @@ -0,0 +1,66 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_bottlenecks.h" + +#include "dng_reference.h" + +/*****************************************************************************/ + +dng_suite gDNGSuite = + { + RefZeroBytes, + RefCopyBytes, + RefSwapBytes16, + RefSwapBytes32, + RefSetArea8, + RefSetArea, + RefSetArea, + RefCopyArea8, + RefCopyArea16, + RefCopyArea32, + RefCopyArea8_16, + RefCopyArea8_S16, + RefCopyArea8_32, + RefCopyArea16_S16, + RefCopyArea16_32, + RefCopyArea8_R32, + RefCopyArea16_R32, + RefCopyAreaS16_R32, + RefCopyAreaR32_8, + RefCopyAreaR32_16, + RefCopyAreaR32_S16, + RefRepeatArea8, + RefRepeatArea16, + RefRepeatArea32, + RefShiftRight16, + RefBilinearRow16, + RefBilinearRow32, + RefBaselineABCtoRGB, + RefBaselineABCDtoRGB, + RefBaselineHueSatMap, + RefBaselineRGBtoGray, + RefBaselineRGBtoRGB, + RefBaseline1DTable, + RefBaselineRGBTone, + RefResampleDown16, + RefResampleDown32, + RefResampleAcross16, + RefResampleAcross32, + RefEqualBytes, + RefEqualArea8, + RefEqualArea16, + RefEqualArea32, + RefVignetteMask16, + RefVignette16, + RefVignette32, + RefMapArea16, + RefBaselineMapPoly32 + }; + +/*****************************************************************************/ diff --git a/dng/dng_bottlenecks.h b/dng/dng_bottlenecks.h new file mode 100644 index 0000000..6405c92 --- /dev/null +++ b/dng/dng_bottlenecks.h @@ -0,0 +1,1752 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Indirection mechanism for performance-critical routines that might be replaced + * with hand-optimized or hardware-specific implementations. + */ + +/*****************************************************************************/ + +#ifndef __dng_bottlenecks__ +#define __dng_bottlenecks__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_types.h" + +/*****************************************************************************/ + +typedef void (ZeroBytesProc) + (void *dPtr, + uint32 count); + +typedef void (CopyBytesProc) + (const void *sPtr, + void *dPtr, + uint32 count); + +/*****************************************************************************/ + +typedef void (SwapBytes16Proc) + (uint16 *dPtr, + uint32 count); + +typedef void (SwapBytes32Proc) + (uint32 *dPtr, + uint32 count); + +/*****************************************************************************/ + +typedef void (SetArea8Proc) + (uint8 *dPtr, + uint8 value, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep); + +typedef void (SetArea16Proc) + (uint16 *dPtr, + uint16 value, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep); + +typedef void (SetArea32Proc) + (uint32 *dPtr, + uint32 value, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep); + +/*****************************************************************************/ + +typedef void (CopyArea8Proc) + (const uint8 *sPtr, + uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +typedef void (CopyArea16Proc) + (const uint16 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +typedef void (CopyArea32Proc) + (const uint32 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +typedef void (CopyArea8_16Proc) + (const uint8 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +typedef void (CopyArea8_S16Proc) + (const uint8 *sPtr, + int16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +typedef void (CopyArea8_32Proc) + (const uint8 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +typedef void (CopyArea16_S16Proc) + (const uint16 *sPtr, + int16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +typedef void (CopyArea16_32Proc) + (const uint16 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +typedef void (CopyArea8_R32Proc) + (const uint8 *sPtr, + real32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange); + +typedef void (CopyArea16_R32Proc) + (const uint16 *sPtr, + real32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange); + +typedef void (CopyAreaS16_R32Proc) + (const int16 *sPtr, + real32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange); + +typedef void (CopyAreaR32_8Proc) + (const real32 *sPtr, + uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange); + +typedef void (CopyAreaR32_16Proc) + (const real32 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange); + +typedef void (CopyAreaR32_S16Proc) + (const real32 *sPtr, + int16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange); + +/*****************************************************************************/ + +typedef void (RepeatArea8Proc) + (const uint8 *sPtr, + uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 repeatV, + uint32 repeatH, + uint32 phaseV, + uint32 phaseH); + +typedef void (RepeatArea16Proc) + (const uint16 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 repeatV, + uint32 repeatH, + uint32 phaseV, + uint32 phaseH); + +typedef void (RepeatArea32Proc) + (const uint32 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 repeatV, + uint32 repeatH, + uint32 phaseV, + uint32 phaseH); + +/*****************************************************************************/ + +typedef void (ShiftRight16Proc) + (uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 shift); + +/*****************************************************************************/ + +typedef void (BilinearRow16Proc) + (const uint16 *sPtr, + uint16 *dPtr, + uint32 cols, + uint32 patPhase, + uint32 patCount, + const uint32 * kernCounts, + const int32 * const * kernOffsets, + const uint16 * const * kernWeights, + uint32 sShift); + +typedef void (BilinearRow32Proc) + (const real32 *sPtr, + real32 *dPtr, + uint32 cols, + uint32 patPhase, + uint32 patCount, + const uint32 * kernCounts, + const int32 * const * kernOffsets, + const real32 * const * kernWeights, + uint32 sShift); + +/*****************************************************************************/ + +typedef void (BaselineABCtoRGBProc) + (const real32 *sPtrA, + const real32 *sPtrB, + const real32 *sPtrC, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_vector &cameraWhite, + const dng_matrix &cameraToRGB); + +typedef void (BaselineABCDtoRGBProc) + (const real32 *sPtrA, + const real32 *sPtrB, + const real32 *sPtrC, + const real32 *sPtrD, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_vector &cameraWhite, + const dng_matrix &cameraToRGB); + +/*****************************************************************************/ + +typedef void (BaselineHueSatMapProc) + (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_hue_sat_map &lut, + const dng_1d_table *encodeTable, + const dng_1d_table *decodeTable); + +/*****************************************************************************/ + +typedef void (BaselineGrayToRGBProc) + (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrG, + uint32 count, + const dng_matrix &matrix); + +typedef void (BaselineRGBtoRGBProc) + (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_matrix &matrix); + +/*****************************************************************************/ + +typedef void (Baseline1DTableProc) + (const real32 *sPtr, + real32 *dPtr, + uint32 count, + const dng_1d_table &table); + +/*****************************************************************************/ + +typedef void (BaselineRGBToneProc) + (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_1d_table &table); + +/*****************************************************************************/ + +typedef void (ResampleDown16Proc) + (const uint16 *sPtr, + uint16 *dPtr, + uint32 sCount, + int32 sRowStep, + const int16 *wPtr, + uint32 wCount, + uint32 pixelRange); + +typedef void (ResampleDown32Proc) + (const real32 *sPtr, + real32 *dPtr, + uint32 sCount, + int32 sRowStep, + const real32 *wPtr, + uint32 wCount); + +/*****************************************************************************/ + +typedef void (ResampleAcross16Proc) + (const uint16 *sPtr, + uint16 *dPtr, + uint32 dCount, + const int32 *coord, + const int16 *wPtr, + uint32 wCount, + uint32 wStep, + uint32 pixelRange); + +typedef void (ResampleAcross32Proc) + (const real32 *sPtr, + real32 *dPtr, + uint32 dCount, + const int32 *coord, + const real32 *wPtr, + uint32 wCount, + uint32 wStep); + +/*****************************************************************************/ + +typedef bool (EqualBytesProc) + (const void *sPtr, + const void *dPtr, + uint32 count); + +typedef bool (EqualArea8Proc) + (const uint8 *sPtr, + const uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +typedef bool (EqualArea16Proc) + (const uint16 *sPtr, + const uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +typedef bool (EqualArea32Proc) + (const uint32 *sPtr, + const uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +/*****************************************************************************/ + +typedef void (VignetteMask16Proc) + (uint16 *mPtr, + uint32 rows, + uint32 cols, + int32 rowStep, + int64 offsetH, + int64 offsetV, + int64 stepH, + int64 stepV, + uint32 tBits, + const uint16 *table); + +typedef void (Vignette16Proc) + (int16 *sPtr, + const uint16 *mPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sPlaneStep, + int32 mRowStep, + uint32 mBits); + +/*****************************************************************************/ + +typedef void (Vignette32Proc) + (real32 *sPtr, + const uint16 *mPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sPlaneStep, + int32 mRowStep, + uint32 mBits, + uint16 blackLevel); + +/*****************************************************************************/ + +typedef void (MapArea16Proc) + (uint16 *dPtr, + uint32 count0, + uint32 count1, + uint32 count2, + int32 step0, + int32 step1, + int32 step2, + const uint16 *map); + +/*****************************************************************************/ + +typedef void (BaselineMapPoly32Proc) + (real32 *dPtr, + const int32 rowStep, + const uint32 rows, + const uint32 cols, + const uint32 rowPitch, + const uint32 colPitch, + const real32 *coefficients, + const uint32 degree, + uint16 blackLevel); + +/*****************************************************************************/ + +struct dng_suite + { + ZeroBytesProc *ZeroBytes; + CopyBytesProc *CopyBytes; + SwapBytes16Proc *SwapBytes16; + SwapBytes32Proc *SwapBytes32; + SetArea8Proc *SetArea8; + SetArea16Proc *SetArea16; + SetArea32Proc *SetArea32; + CopyArea8Proc *CopyArea8; + CopyArea16Proc *CopyArea16; + CopyArea32Proc *CopyArea32; + CopyArea8_16Proc *CopyArea8_16; + CopyArea8_S16Proc *CopyArea8_S16; + CopyArea8_32Proc *CopyArea8_32; + CopyArea16_S16Proc *CopyArea16_S16; + CopyArea16_32Proc *CopyArea16_32; + CopyArea8_R32Proc *CopyArea8_R32; + CopyArea16_R32Proc *CopyArea16_R32; + CopyAreaS16_R32Proc *CopyAreaS16_R32; + CopyAreaR32_8Proc *CopyAreaR32_8; + CopyAreaR32_16Proc *CopyAreaR32_16; + CopyAreaR32_S16Proc *CopyAreaR32_S16; + RepeatArea8Proc *RepeatArea8; + RepeatArea16Proc *RepeatArea16; + RepeatArea32Proc *RepeatArea32; + ShiftRight16Proc *ShiftRight16; + BilinearRow16Proc *BilinearRow16; + BilinearRow32Proc *BilinearRow32; + BaselineABCtoRGBProc *BaselineABCtoRGB; + BaselineABCDtoRGBProc *BaselineABCDtoRGB; + BaselineHueSatMapProc *BaselineHueSatMap; + BaselineGrayToRGBProc *BaselineRGBtoGray; + BaselineRGBtoRGBProc *BaselineRGBtoRGB; + Baseline1DTableProc *Baseline1DTable; + BaselineRGBToneProc *BaselineRGBTone; + ResampleDown16Proc *ResampleDown16; + ResampleDown32Proc *ResampleDown32; + ResampleAcross16Proc *ResampleAcross16; + ResampleAcross32Proc *ResampleAcross32; + EqualBytesProc *EqualBytes; + EqualArea8Proc *EqualArea8; + EqualArea16Proc *EqualArea16; + EqualArea32Proc *EqualArea32; + VignetteMask16Proc *VignetteMask16; + Vignette16Proc *Vignette16; + Vignette32Proc *Vignette32; + MapArea16Proc *MapArea16; + BaselineMapPoly32Proc *BaselineMapPoly32; + }; + +/*****************************************************************************/ + +extern dng_suite gDNGSuite; + +/*****************************************************************************/ + +inline void DoZeroBytes (void *dPtr, + uint32 count) + { + + (gDNGSuite.ZeroBytes) (dPtr, + count); + + } + +inline void DoCopyBytes (const void *sPtr, + void *dPtr, + uint32 count) + { + + (gDNGSuite.CopyBytes) (sPtr, + dPtr, + count); + + } + +/*****************************************************************************/ + +inline void DoSwapBytes16 (uint16 *dPtr, + uint32 count) + { + + (gDNGSuite.SwapBytes16) (dPtr, + count); + + } + +inline void DoSwapBytes32 (uint32 *dPtr, + uint32 count) + { + + (gDNGSuite.SwapBytes32) (dPtr, + count); + + } + +/*****************************************************************************/ + +inline void DoSetArea8 (uint8 *dPtr, + uint8 value, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep) + { + + (gDNGSuite.SetArea8) (dPtr, + value, + rows, + cols, + planes, + rowStep, + colStep, + planeStep); + + } + +inline void DoSetArea16 (uint16 *dPtr, + uint16 value, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep) + { + + (gDNGSuite.SetArea16) (dPtr, + value, + rows, + cols, + planes, + rowStep, + colStep, + planeStep); + + } + +inline void DoSetArea32 (uint32 *dPtr, + uint32 value, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep) + { + + (gDNGSuite.SetArea32) (dPtr, + value, + rows, + cols, + planes, + rowStep, + colStep, + planeStep); + + } + +/*****************************************************************************/ + +inline void DoCopyArea8 (const uint8 *sPtr, + uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + (gDNGSuite.CopyArea8) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + } + +inline void DoCopyArea16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + (gDNGSuite.CopyArea16) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + } + +inline void DoCopyArea32 (const uint32 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + (gDNGSuite.CopyArea32) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + } + +inline void DoCopyArea8_16 (const uint8 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + (gDNGSuite.CopyArea8_16) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + } + +inline void DoCopyArea8_S16 (const uint8 *sPtr, + int16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + (gDNGSuite.CopyArea8_S16) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + } + +inline void DoCopyArea8_32 (const uint8 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + (gDNGSuite.CopyArea8_32) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + } + +inline void DoCopyArea16_S16 (const uint16 *sPtr, + int16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + (gDNGSuite.CopyArea16_S16) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + } + +inline void DoCopyArea16_32 (const uint16 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + (gDNGSuite.CopyArea16_32) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + } + +inline void DoCopyArea8_R32 (const uint8 *sPtr, + real32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange) + { + + (gDNGSuite.CopyArea8_R32) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep, + pixelRange); + + } + +inline void DoCopyArea16_R32 (const uint16 *sPtr, + real32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange) + { + + (gDNGSuite.CopyArea16_R32) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep, + pixelRange); + + } + +inline void DoCopyAreaS16_R32 (const int16 *sPtr, + real32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange) + { + + (gDNGSuite.CopyAreaS16_R32) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep, + pixelRange); + + } + +inline void DoCopyAreaR32_8 (const real32 *sPtr, + uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange) + { + + (gDNGSuite.CopyAreaR32_8) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep, + pixelRange); + + } + +inline void DoCopyAreaR32_16 (const real32 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange) + { + + (gDNGSuite.CopyAreaR32_16) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep, + pixelRange); + + } + +inline void DoCopyAreaR32_S16 (const real32 *sPtr, + int16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange) + { + + (gDNGSuite.CopyAreaR32_S16) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep, + pixelRange); + + } + +/*****************************************************************************/ + +inline void DoRepeatArea8 (const uint8 *sPtr, + uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 repeatV, + uint32 repeatH, + uint32 phaseV, + uint32 phaseH) + { + + (gDNGSuite.RepeatArea8) (sPtr, + dPtr, + rows, + cols, + planes, + rowStep, + colStep, + planeStep, + repeatV, + repeatH, + phaseV, + phaseH); + + } + +inline void DoRepeatArea16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 repeatV, + uint32 repeatH, + uint32 phaseV, + uint32 phaseH) + { + + (gDNGSuite.RepeatArea16) (sPtr, + dPtr, + rows, + cols, + planes, + rowStep, + colStep, + planeStep, + repeatV, + repeatH, + phaseV, + phaseH); + + } + +inline void DoRepeatArea32 (const uint32 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 repeatV, + uint32 repeatH, + uint32 phaseV, + uint32 phaseH) + { + + (gDNGSuite.RepeatArea32) (sPtr, + dPtr, + rows, + cols, + planes, + rowStep, + colStep, + planeStep, + repeatV, + repeatH, + phaseV, + phaseH); + + } + +/*****************************************************************************/ + +inline void DoShiftRight16 (uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 shift) + { + + (gDNGSuite.ShiftRight16) (dPtr, + rows, + cols, + planes, + rowStep, + colStep, + planeStep, + shift); + + } + +/*****************************************************************************/ + +inline void DoBilinearRow16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 cols, + uint32 patPhase, + uint32 patCount, + const uint32 * kernCounts, + const int32 * const * kernOffsets, + const uint16 * const * kernWeights, + uint32 sShift) + { + + (gDNGSuite.BilinearRow16) (sPtr, + dPtr, + cols, + patPhase, + patCount, + kernCounts, + kernOffsets, + kernWeights, + sShift); + + } + +inline void DoBilinearRow32 (const real32 *sPtr, + real32 *dPtr, + uint32 cols, + uint32 patPhase, + uint32 patCount, + const uint32 * kernCounts, + const int32 * const * kernOffsets, + const real32 * const * kernWeights, + uint32 sShift) + { + + (gDNGSuite.BilinearRow32) (sPtr, + dPtr, + cols, + patPhase, + patCount, + kernCounts, + kernOffsets, + kernWeights, + sShift); + + } + +/*****************************************************************************/ + +inline void DoBaselineABCtoRGB (const real32 *sPtrA, + const real32 *sPtrB, + const real32 *sPtrC, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_vector &cameraWhite, + const dng_matrix &cameraToRGB) + { + + (gDNGSuite.BaselineABCtoRGB) (sPtrA, + sPtrB, + sPtrC, + dPtrR, + dPtrG, + dPtrB, + count, + cameraWhite, + cameraToRGB); + + } + +inline void DoBaselineABCDtoRGB (const real32 *sPtrA, + const real32 *sPtrB, + const real32 *sPtrC, + const real32 *sPtrD, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_vector &cameraWhite, + const dng_matrix &cameraToRGB) + { + + (gDNGSuite.BaselineABCDtoRGB) (sPtrA, + sPtrB, + sPtrC, + sPtrD, + dPtrR, + dPtrG, + dPtrB, + count, + cameraWhite, + cameraToRGB); + + } + +/*****************************************************************************/ + +inline void DoBaselineHueSatMap (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_hue_sat_map &lut, + const dng_1d_table *encodeTable, + const dng_1d_table *decodeTable) + { + + (gDNGSuite.BaselineHueSatMap) (sPtrR, + sPtrG, + sPtrB, + dPtrR, + dPtrG, + dPtrB, + count, + lut, + encodeTable, + decodeTable); + + } + +/*****************************************************************************/ + +inline void DoBaselineRGBtoGray (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrG, + uint32 count, + const dng_matrix &matrix) + { + + (gDNGSuite.BaselineRGBtoGray) (sPtrR, + sPtrG, + sPtrB, + dPtrG, + count, + matrix); + + } + +inline void DoBaselineRGBtoRGB (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_matrix &matrix) + { + + (gDNGSuite.BaselineRGBtoRGB) (sPtrR, + sPtrG, + sPtrB, + dPtrR, + dPtrG, + dPtrB, + count, + matrix); + + } + +/*****************************************************************************/ + +inline void DoBaseline1DTable (const real32 *sPtr, + real32 *dPtr, + uint32 count, + const dng_1d_table &table) + { + + (gDNGSuite.Baseline1DTable) (sPtr, + dPtr, + count, + table); + + } + +/*****************************************************************************/ + +inline void DoBaselineRGBTone (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_1d_table &table) + { + + (gDNGSuite.BaselineRGBTone) (sPtrR, + sPtrG, + sPtrB, + dPtrR, + dPtrG, + dPtrB, + count, + table); + + } + +/*****************************************************************************/ + +inline void DoResampleDown16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 sCount, + int32 sRowStep, + const int16 *wPtr, + uint32 wCount, + uint32 pixelRange) + { + + (gDNGSuite.ResampleDown16) (sPtr, + dPtr, + sCount, + sRowStep, + wPtr, + wCount, + pixelRange); + + } + +inline void DoResampleDown32 (const real32 *sPtr, + real32 *dPtr, + uint32 sCount, + int32 sRowStep, + const real32 *wPtr, + uint32 wCount) + { + + (gDNGSuite.ResampleDown32) (sPtr, + dPtr, + sCount, + sRowStep, + wPtr, + wCount); + + } + +/*****************************************************************************/ + +inline void DoResampleAcross16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 dCount, + const int32 *coord, + const int16 *wPtr, + uint32 wCount, + uint32 wStep, + uint32 pixelRange) + { + + (gDNGSuite.ResampleAcross16) (sPtr, + dPtr, + dCount, + coord, + wPtr, + wCount, + wStep, + pixelRange); + + } + +inline void DoResampleAcross32 (const real32 *sPtr, + real32 *dPtr, + uint32 dCount, + const int32 *coord, + const real32 *wPtr, + uint32 wCount, + uint32 wStep) + { + + (gDNGSuite.ResampleAcross32) (sPtr, + dPtr, + dCount, + coord, + wPtr, + wCount, + wStep); + + } + +/*****************************************************************************/ + +inline bool DoEqualBytes (const void *sPtr, + const void *dPtr, + uint32 count) + { + + return (gDNGSuite.EqualBytes) (sPtr, + dPtr, + count); + + } + +inline bool DoEqualArea8 (const uint8 *sPtr, + const uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + return (gDNGSuite.EqualArea8) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + } + +inline bool DoEqualArea16 (const uint16 *sPtr, + const uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + return (gDNGSuite.EqualArea16) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + } + +inline bool DoEqualArea32 (const uint32 *sPtr, + const uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + return (gDNGSuite.EqualArea32) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + } + +/*****************************************************************************/ + +inline void DoVignetteMask16 (uint16 *mPtr, + uint32 rows, + uint32 cols, + int32 rowStep, + int64 offsetH, + int64 offsetV, + int64 stepH, + int64 stepV, + uint32 tBits, + const uint16 *table) + { + + (gDNGSuite.VignetteMask16) (mPtr, + rows, + cols, + rowStep, + offsetH, + offsetV, + stepH, + stepV, + tBits, + table); + + } + +/*****************************************************************************/ + +inline void DoVignette16 (int16 *sPtr, + const uint16 *mPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sPlaneStep, + int32 mRowStep, + uint32 mBits) + { + + (gDNGSuite.Vignette16) (sPtr, + mPtr, + rows, + cols, + planes, + sRowStep, + sPlaneStep, + mRowStep, + mBits); + + } + +/*****************************************************************************/ + +inline void DoVignette32 (real32 *sPtr, + const uint16 *mPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sPlaneStep, + int32 mRowStep, + uint32 mBits, + uint16 blackLevel) + { + + (gDNGSuite.Vignette32) (sPtr, + mPtr, + rows, + cols, + planes, + sRowStep, + sPlaneStep, + mRowStep, + mBits, + blackLevel); + + } + +/*****************************************************************************/ + +inline void DoMapArea16 (uint16 *dPtr, + uint32 count0, + uint32 count1, + uint32 count2, + int32 step0, + int32 step1, + int32 step2, + const uint16 *map) + { + + (gDNGSuite.MapArea16) (dPtr, + count0, + count1, + count2, + step0, + step1, + step2, + map); + + } + +/*****************************************************************************/ + +inline void DoBaselineMapPoly32 (real32 *dPtr, + const int32 rowStep, + const uint32 rows, + const uint32 cols, + const uint32 rowPitch, + const uint32 colPitch, + const real32 *coefficients, + const uint32 degree, + uint16 blackLevel) + { + + (gDNGSuite.BaselineMapPoly32) (dPtr, + rowStep, + rows, + cols, + rowPitch, + colPitch, + coefficients, + degree, + blackLevel); + + } + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_camera_profile.cpp b/dng/dng_camera_profile.cpp new file mode 100644 index 0000000..81e470f --- /dev/null +++ b/dng/dng_camera_profile.cpp @@ -0,0 +1,1434 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_camera_profile.h" + +#include "dng_1d_table.h" +#include "dng_assertions.h" +#include "dng_color_space.h" +#include "dng_host.h" +#include "dng_exceptions.h" +#include "dng_image_writer.h" +#include "dng_info.h" +#include "dng_parse_utils.h" +#include "dng_tag_codes.h" +#include "dng_tag_types.h" +#include "dng_temperature.h" +#include "dng_xy_coord.h" + +/*****************************************************************************/ + +const char * kProfileName_Embedded = "Embedded"; + +const char * kAdobeCalibrationSignature = "com.adobe"; + +/*****************************************************************************/ + +dng_camera_profile::dng_camera_profile () + + : fName () + , fCalibrationIlluminant1 (lsUnknown) + , fCalibrationIlluminant2 (lsUnknown) + , fColorMatrix1 () + , fColorMatrix2 () + , fForwardMatrix1 () + , fForwardMatrix2 () + , fReductionMatrix1 () + , fReductionMatrix2 () + , fFingerprint () + , fCopyright () + , fEmbedPolicy (pepAllowCopying) + , fHueSatDeltas1 () + , fHueSatDeltas2 () + , fHueSatMapEncoding (encoding_Linear) + , fLookTable () + , fLookTableEncoding (encoding_Linear) + , fBaselineExposureOffset (0, 100) + , fDefaultBlackRender (defaultBlackRender_Auto) + , fToneCurve () + , fProfileCalibrationSignature () + , fUniqueCameraModelRestriction () + , fWasReadFromDNG (false) + , fWasReadFromDisk (false) + , fWasBuiltinMatrix (false) + , fWasStubbed (false) + + { + + fToneCurve.SetInvalid (); + + } + +/*****************************************************************************/ + +dng_camera_profile::~dng_camera_profile () + { + + } + +/*****************************************************************************/ + +real64 dng_camera_profile::IlluminantToTemperature (uint32 light) + { + + switch (light) + { + + case lsStandardLightA: + case lsTungsten: + { + return 2850.0; + } + + case lsISOStudioTungsten: + { + return 3200.0; + } + + case lsD50: + { + return 5000.0; + } + + case lsD55: + case lsDaylight: + case lsFineWeather: + case lsFlash: + case lsStandardLightB: + { + return 5500.0; + } + + case lsD65: + case lsStandardLightC: + case lsCloudyWeather: + { + return 6500.0; + } + + case lsD75: + case lsShade: + { + return 7500.0; + } + + case lsDaylightFluorescent: + { + return (5700.0 + 7100.0) * 0.5; + } + + case lsDayWhiteFluorescent: + { + return (4600.0 + 5500.0) * 0.5; + } + + case lsCoolWhiteFluorescent: + case lsFluorescent: + { + return (3800.0 + 4500.0) * 0.5; + } + + case lsWhiteFluorescent: + { + return (3250.0 + 3800.0) * 0.5; + } + + case lsWarmWhiteFluorescent: + { + return (2600.0 + 3250.0) * 0.5; + } + + default: + { + return 0.0; + } + + } + + } + +/******************************************************************************/ + +void dng_camera_profile::NormalizeColorMatrix (dng_matrix &m) + { + + if (m.NotEmpty ()) + { + + // Find scale factor to normalize the matrix. + + dng_vector coord = m * PCStoXYZ (); + + real64 maxCoord = coord.MaxEntry (); + + if (maxCoord > 0.0 && (maxCoord < 0.99 || maxCoord > 1.01)) + { + + m.Scale (1.0 / maxCoord); + + } + + // Round to four decimal places. + + m.Round (10000); + + } + + } + +/******************************************************************************/ + +void dng_camera_profile::SetColorMatrix1 (const dng_matrix &m) + { + + fColorMatrix1 = m; + + NormalizeColorMatrix (fColorMatrix1); + + ClearFingerprint (); + + } + +/******************************************************************************/ + +void dng_camera_profile::SetColorMatrix2 (const dng_matrix &m) + { + + fColorMatrix2 = m; + + NormalizeColorMatrix (fColorMatrix2); + + ClearFingerprint (); + + } + +/******************************************************************************/ + +// Make sure the forward matrix maps to exactly the PCS. + +void dng_camera_profile::NormalizeForwardMatrix (dng_matrix &m) + { + + if (m.NotEmpty ()) + { + + dng_vector cameraOne; + + cameraOne.SetIdentity (m.Cols ()); + + dng_vector xyz = m * cameraOne; + + m = PCStoXYZ ().AsDiagonal () * + Invert (xyz.AsDiagonal ()) * + m; + + } + + } + +/******************************************************************************/ + +void dng_camera_profile::SetForwardMatrix1 (const dng_matrix &m) + { + + fForwardMatrix1 = m; + + fForwardMatrix1.Round (10000); + + ClearFingerprint (); + + } + +/******************************************************************************/ + +void dng_camera_profile::SetForwardMatrix2 (const dng_matrix &m) + { + + fForwardMatrix2 = m; + + fForwardMatrix2.Round (10000); + + ClearFingerprint (); + + } + +/*****************************************************************************/ + +void dng_camera_profile::SetReductionMatrix1 (const dng_matrix &m) + { + + fReductionMatrix1 = m; + + fReductionMatrix1.Round (10000); + + ClearFingerprint (); + + } + +/******************************************************************************/ + +void dng_camera_profile::SetReductionMatrix2 (const dng_matrix &m) + { + + fReductionMatrix2 = m; + + fReductionMatrix2.Round (10000); + + ClearFingerprint (); + + } + +/*****************************************************************************/ + +bool dng_camera_profile::HasColorMatrix1 () const + { + + return fColorMatrix1.Cols () == 3 && + fColorMatrix1.Rows () > 1; + + } + +/*****************************************************************************/ + +bool dng_camera_profile::HasColorMatrix2 () const + { + + return fColorMatrix2.Cols () == 3 && + fColorMatrix2.Rows () == fColorMatrix1.Rows (); + + } + +/*****************************************************************************/ + +void dng_camera_profile::SetHueSatDeltas1 (const dng_hue_sat_map &deltas1) + { + + fHueSatDeltas1 = deltas1; + + ClearFingerprint (); + + } + +/*****************************************************************************/ + +void dng_camera_profile::SetHueSatDeltas2 (const dng_hue_sat_map &deltas2) + { + + fHueSatDeltas2 = deltas2; + + ClearFingerprint (); + + } + +/*****************************************************************************/ + +void dng_camera_profile::SetLookTable (const dng_hue_sat_map &table) + { + + fLookTable = table; + + ClearFingerprint (); + + } + +/*****************************************************************************/ + +static void FingerprintMatrix (dng_md5_printer_stream &printer, + const dng_matrix &matrix) + { + + tag_matrix tag (0, matrix); + + // Tag's Put routine doesn't write the header, only the data + + tag.Put (printer); + + } + +/*****************************************************************************/ + +static void FingerprintHueSatMap (dng_md5_printer_stream &printer, + const dng_hue_sat_map &map) + { + + if (map.IsNull ()) + return; + + uint32 hues; + uint32 sats; + uint32 vals; + + map.GetDivisions (hues, sats, vals); + + printer.Put_uint32 (hues); + printer.Put_uint32 (sats); + printer.Put_uint32 (vals); + + for (uint32 val = 0; val < vals; val++) + for (uint32 hue = 0; hue < hues; hue++) + for (uint32 sat = 0; sat < sats; sat++) + { + + dng_hue_sat_map::HSBModify modify; + + map.GetDelta (hue, sat, val, modify); + + printer.Put_real32 (modify.fHueShift); + printer.Put_real32 (modify.fSatScale); + printer.Put_real32 (modify.fValScale); + + } + + } + +/*****************************************************************************/ + +void dng_camera_profile::CalculateFingerprint () const + { + + DNG_ASSERT (!fWasStubbed, "CalculateFingerprint on stubbed profile"); + + dng_md5_printer_stream printer; + + // MD5 hash is always calculated on little endian data. + + printer.SetLittleEndian (); + + // The data that we fingerprint closely matches that saved + // by the profile_tag_set class in dng_image_writer.cpp, with + // the exception of the fingerprint itself. + + if (HasColorMatrix1 ()) + { + + uint32 colorChannels = ColorMatrix1 ().Rows (); + + printer.Put_uint16 ((uint16) fCalibrationIlluminant1); + + FingerprintMatrix (printer, fColorMatrix1); + + if (fForwardMatrix1.Rows () == fColorMatrix1.Cols () && + fForwardMatrix1.Cols () == fColorMatrix1.Rows ()) + { + + FingerprintMatrix (printer, fForwardMatrix1); + + } + + if (colorChannels > 3 && fReductionMatrix1.Rows () * + fReductionMatrix1.Cols () == colorChannels * 3) + { + + FingerprintMatrix (printer, fReductionMatrix1); + + } + + if (HasColorMatrix2 ()) + { + + printer.Put_uint16 ((uint16) fCalibrationIlluminant2); + + FingerprintMatrix (printer, fColorMatrix2); + + if (fForwardMatrix2.Rows () == fColorMatrix2.Cols () && + fForwardMatrix2.Cols () == fColorMatrix2.Rows ()) + { + + FingerprintMatrix (printer, fForwardMatrix2); + + } + + if (colorChannels > 3 && fReductionMatrix2.Rows () * + fReductionMatrix2.Cols () == colorChannels * 3) + { + + FingerprintMatrix (printer, fReductionMatrix2); + + } + + } + + printer.Put (fName.Get (), + fName.Length ()); + + printer.Put (fProfileCalibrationSignature.Get (), + fProfileCalibrationSignature.Length ()); + + printer.Put_uint32 (fEmbedPolicy); + + printer.Put (fCopyright.Get (), + fCopyright.Length ()); + + bool haveHueSat1 = HueSatDeltas1 ().IsValid (); + + bool haveHueSat2 = HueSatDeltas2 ().IsValid () && + HasColorMatrix2 (); + + if (haveHueSat1) + { + + FingerprintHueSatMap (printer, fHueSatDeltas1); + + } + + if (haveHueSat2) + { + + FingerprintHueSatMap (printer, fHueSatDeltas2); + + } + + if (haveHueSat1 || haveHueSat2) + { + + if (fHueSatMapEncoding != 0) + { + + printer.Put_uint32 (fHueSatMapEncoding); + + } + + } + + if (fLookTable.IsValid ()) + { + + FingerprintHueSatMap (printer, fLookTable); + + if (fLookTableEncoding != 0) + { + + printer.Put_uint32 (fLookTableEncoding); + + } + + } + + if (fBaselineExposureOffset.IsValid ()) + { + + if (fBaselineExposureOffset.As_real64 () != 0.0) + { + + printer.Put_real64 (fBaselineExposureOffset.As_real64 ()); + + } + + } + + if (fDefaultBlackRender != 0) + { + + printer.Put_int32 (fDefaultBlackRender); + + } + + if (fToneCurve.IsValid ()) + { + + for (uint32 i = 0; i < fToneCurve.fCoord.size (); i++) + { + + printer.Put_real32 ((real32) fToneCurve.fCoord [i].h); + printer.Put_real32 ((real32) fToneCurve.fCoord [i].v); + + } + + } + + } + + fFingerprint = printer.Result (); + + } + +/******************************************************************************/ + +dng_fingerprint dng_camera_profile::UniqueID () const + { + + dng_md5_printer_stream printer; + + // MD5 hash is always calculated on little endian data. + + printer.SetLittleEndian (); + + // Start with the existing fingerprint. + + if (!fFingerprint.IsValid ()) + CalculateFingerprint (); + + printer.Put (fFingerprint.data, + (uint32) sizeof (fFingerprint.data)); + + // Also include the UniqueCameraModelRestriction tag. + + printer.Put (fUniqueCameraModelRestriction.Get (), + fUniqueCameraModelRestriction.Length ()); + + // Add any other needed fields here. + + // ... + + return printer.Result (); + + } + +/******************************************************************************/ + +bool dng_camera_profile::ValidForwardMatrix (const dng_matrix &m) + { + + const real64 kThreshold = 0.01; + + if (m.NotEmpty ()) + { + + dng_vector cameraOne; + + cameraOne.SetIdentity (m.Cols ()); + + dng_vector xyz = m * cameraOne; + + dng_vector pcs = PCStoXYZ (); + + if (Abs_real64 (xyz [0] - pcs [0]) > kThreshold || + Abs_real64 (xyz [1] - pcs [1]) > kThreshold || + Abs_real64 (xyz [2] - pcs [2]) > kThreshold) + { + + return false; + + } + + } + + return true; + + } + +/******************************************************************************/ + +bool dng_camera_profile::IsValid (uint32 channels) const + { + + // For Monochrome images, we ignore the camera profile. + + if (channels == 1) + { + + return true; + + } + + // ColorMatrix1 is required for all color images. + + if (fColorMatrix1.Cols () != 3 || + fColorMatrix1.Rows () != channels) + { + + #if qDNGValidate + + ReportError ("ColorMatrix1 is wrong size"); + + #endif + + return false; + + } + + // ColorMatrix2 is optional, but it must be valid if present. + + if (fColorMatrix2.Cols () != 0 || + fColorMatrix2.Rows () != 0) + { + + if (fColorMatrix2.Cols () != 3 || + fColorMatrix2.Rows () != channels) + { + + #if qDNGValidate + + ReportError ("ColorMatrix2 is wrong size"); + + #endif + + return false; + + } + + } + + // ForwardMatrix1 is optional, but it must be valid if present. + + if (fForwardMatrix1.Cols () != 0 || + fForwardMatrix1.Rows () != 0) + { + + if (fForwardMatrix1.Rows () != 3 || + fForwardMatrix1.Cols () != channels) + { + + #if qDNGValidate + + ReportError ("ForwardMatrix1 is wrong size"); + + #endif + + return false; + + } + + // Make sure ForwardMatrix1 does a valid mapping. + + if (!ValidForwardMatrix (fForwardMatrix1)) + { + + #if qDNGValidate + + ReportError ("ForwardMatrix1 does not map equal camera values to XYZ D50"); + + #endif + + return false; + + } + + } + + // ForwardMatrix2 is optional, but it must be valid if present. + + if (fForwardMatrix2.Cols () != 0 || + fForwardMatrix2.Rows () != 0) + { + + if (fForwardMatrix2.Rows () != 3 || + fForwardMatrix2.Cols () != channels) + { + + #if qDNGValidate + + ReportError ("ForwardMatrix2 is wrong size"); + + #endif + + return false; + + } + + // Make sure ForwardMatrix2 does a valid mapping. + + if (!ValidForwardMatrix (fForwardMatrix2)) + { + + #if qDNGValidate + + ReportError ("ForwardMatrix2 does not map equal camera values to XYZ D50"); + + #endif + + return false; + + } + + } + + // ReductionMatrix1 is optional, but it must be valid if present. + + if (fReductionMatrix1.Cols () != 0 || + fReductionMatrix1.Rows () != 0) + { + + if (fReductionMatrix1.Cols () != channels || + fReductionMatrix1.Rows () != 3) + { + + #if qDNGValidate + + ReportError ("ReductionMatrix1 is wrong size"); + + #endif + + return false; + + } + + } + + // ReductionMatrix2 is optional, but it must be valid if present. + + if (fReductionMatrix2.Cols () != 0 || + fReductionMatrix2.Rows () != 0) + { + + if (fReductionMatrix2.Cols () != channels || + fReductionMatrix2.Rows () != 3) + { + + #if qDNGValidate + + ReportError ("ReductionMatrix2 is wrong size"); + + #endif + + return false; + + } + + } + + // Make sure ColorMatrix1 is invertable. + + try + { + + if (fReductionMatrix1.NotEmpty ()) + { + + (void) Invert (fColorMatrix1, + fReductionMatrix1); + + } + + else + { + + (void) Invert (fColorMatrix1); + + } + + } + + catch (...) + { + + #if qDNGValidate + + ReportError ("ColorMatrix1 is not invertable"); + + #endif + + return false; + + } + + // Make sure ColorMatrix2 is invertable. + + if (fColorMatrix2.NotEmpty ()) + { + + try + { + + if (fReductionMatrix2.NotEmpty ()) + { + + (void) Invert (fColorMatrix2, + fReductionMatrix2); + + } + + else + { + + (void) Invert (fColorMatrix2); + + } + + } + + catch (...) + { + + #if qDNGValidate + + ReportError ("ColorMatrix2 is not invertable"); + + #endif + + return false; + + } + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_camera_profile::EqualData (const dng_camera_profile &profile) const + { + + return fCalibrationIlluminant1 == profile.fCalibrationIlluminant1 && + fCalibrationIlluminant2 == profile.fCalibrationIlluminant2 && + fColorMatrix1 == profile.fColorMatrix1 && + fColorMatrix2 == profile.fColorMatrix2 && + fForwardMatrix1 == profile.fForwardMatrix1 && + fForwardMatrix2 == profile.fForwardMatrix2 && + fReductionMatrix1 == profile.fReductionMatrix1 && + fReductionMatrix2 == profile.fReductionMatrix2 && + fHueSatDeltas1 == profile.fHueSatDeltas1 && + fHueSatDeltas2 == profile.fHueSatDeltas2 && + fHueSatMapEncoding == profile.fHueSatMapEncoding && + fLookTable == profile.fLookTable && + fLookTableEncoding == profile.fLookTableEncoding && + fDefaultBlackRender == profile.fDefaultBlackRender && + fToneCurve == profile.fToneCurve && + fBaselineExposureOffset.As_real64 () == profile.fBaselineExposureOffset.As_real64 () && + fProfileCalibrationSignature == profile.fProfileCalibrationSignature; + + } + +/*****************************************************************************/ + +void dng_camera_profile::ReadHueSatMap (dng_stream &stream, + dng_hue_sat_map &hueSatMap, + uint32 hues, + uint32 sats, + uint32 vals, + bool skipSat0) + { + + hueSatMap.SetDivisions (hues, sats, vals); + + for (uint32 val = 0; val < vals; val++) + { + + for (uint32 hue = 0; hue < hues; hue++) + { + + for (uint32 sat = skipSat0 ? 1 : 0; sat < sats; sat++) + { + + dng_hue_sat_map::HSBModify modify; + + modify.fHueShift = stream.Get_real32 (); + modify.fSatScale = stream.Get_real32 (); + modify.fValScale = stream.Get_real32 (); + + hueSatMap.SetDelta (hue, sat, val, modify); + + } + + } + + } + + hueSatMap.AssignNewUniqueRuntimeFingerprint (); + + } + +/*****************************************************************************/ + +DNG_ATTRIB_NO_SANITIZE("unsigned-integer-overflow") +void dng_camera_profile::Parse (dng_stream &stream, + dng_camera_profile_info &profileInfo) + { + + SetUniqueCameraModelRestriction (profileInfo.fUniqueCameraModel.Get ()); + + if (profileInfo.fProfileName.NotEmpty ()) + { + + SetName (profileInfo.fProfileName.Get ()); + + } + + SetCopyright (profileInfo.fProfileCopyright.Get ()); + + SetEmbedPolicy (profileInfo.fEmbedPolicy); + + SetCalibrationIlluminant1 (profileInfo.fCalibrationIlluminant1); + + SetColorMatrix1 (profileInfo.fColorMatrix1); + + if (profileInfo.fForwardMatrix1.NotEmpty ()) + { + + SetForwardMatrix1 (profileInfo.fForwardMatrix1); + + } + + if (profileInfo.fReductionMatrix1.NotEmpty ()) + { + + SetReductionMatrix1 (profileInfo.fReductionMatrix1); + + } + + if (profileInfo.fColorMatrix2.NotEmpty ()) + { + + SetCalibrationIlluminant2 (profileInfo.fCalibrationIlluminant2); + + SetColorMatrix2 (profileInfo.fColorMatrix2); + + if (profileInfo.fForwardMatrix2.NotEmpty ()) + { + + SetForwardMatrix2 (profileInfo.fForwardMatrix2); + + } + + if (profileInfo.fReductionMatrix2.NotEmpty ()) + { + + SetReductionMatrix2 (profileInfo.fReductionMatrix2); + + } + + } + + SetProfileCalibrationSignature (profileInfo.fProfileCalibrationSignature.Get ()); + + if (profileInfo.fHueSatDeltas1Offset != 0 && + profileInfo.fHueSatDeltas1Count != 0) + { + + TempBigEndian setEndianness (stream, profileInfo.fBigEndian); + + stream.SetReadPosition (profileInfo.fHueSatDeltas1Offset); + + bool skipSat0 = (profileInfo.fHueSatDeltas1Count == profileInfo.fProfileHues * + (profileInfo.fProfileSats - 1) * + profileInfo.fProfileVals * 3); + + ReadHueSatMap (stream, + fHueSatDeltas1, + profileInfo.fProfileHues, + profileInfo.fProfileSats, + profileInfo.fProfileVals, + skipSat0); + + } + + if (profileInfo.fHueSatDeltas2Offset != 0 && + profileInfo.fHueSatDeltas2Count != 0) + { + + TempBigEndian setEndianness (stream, profileInfo.fBigEndian); + + stream.SetReadPosition (profileInfo.fHueSatDeltas2Offset); + + bool skipSat0 = (profileInfo.fHueSatDeltas2Count == profileInfo.fProfileHues * + (profileInfo.fProfileSats - 1) * + profileInfo.fProfileVals * 3); + + ReadHueSatMap (stream, + fHueSatDeltas2, + profileInfo.fProfileHues, + profileInfo.fProfileSats, + profileInfo.fProfileVals, + skipSat0); + + } + + if (profileInfo.fLookTableOffset != 0 && + profileInfo.fLookTableCount != 0) + { + + TempBigEndian setEndianness (stream, profileInfo.fBigEndian); + + stream.SetReadPosition (profileInfo.fLookTableOffset); + + bool skipSat0 = (profileInfo.fLookTableCount == profileInfo.fLookTableHues * + (profileInfo.fLookTableSats - 1) * + profileInfo.fLookTableVals * 3); + + ReadHueSatMap (stream, + fLookTable, + profileInfo.fLookTableHues, + profileInfo.fLookTableSats, + profileInfo.fLookTableVals, + skipSat0); + + } + + if ((profileInfo.fToneCurveCount & 1) == 0) + { + + TempBigEndian setEndianness (stream, profileInfo.fBigEndian); + + stream.SetReadPosition (profileInfo.fToneCurveOffset); + + uint32 points = profileInfo.fToneCurveCount / 2; + + if (points > kMaxToneCurvePoints) + { + ThrowProgramError ("Too many tone curve points"); + } + + fToneCurve.fCoord.resize (points); + + for (size_t i = 0; i < points; i++) + { + + dng_point_real64 point; + + point.h = stream.Get_real32 (); + point.v = stream.Get_real32 (); + + fToneCurve.fCoord [i] = point; + + } + + } + + SetHueSatMapEncoding (profileInfo.fHueSatMapEncoding); + + SetLookTableEncoding (profileInfo.fLookTableEncoding); + + SetBaselineExposureOffset (profileInfo.fBaselineExposureOffset.As_real64 ()); + + SetDefaultBlackRender (profileInfo.fDefaultBlackRender); + + } + +/*****************************************************************************/ + +bool dng_camera_profile::ParseExtended (dng_stream &stream) + { + + try + { + + dng_camera_profile_info profileInfo; + + if (!profileInfo.ParseExtended (stream)) + { + return false; + } + + Parse (stream, profileInfo); + + return true; + + } + + catch (...) + { + + // Eat parsing errors. + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_camera_profile::SetFourColorBayer () + { + + uint32 j; + + if (!IsValid (3)) + { + ThrowProgramError (); + } + + if (fColorMatrix1.NotEmpty ()) + { + + dng_matrix m (4, 3); + + for (j = 0; j < 3; j++) + { + m [0] [j] = fColorMatrix1 [0] [j]; + m [1] [j] = fColorMatrix1 [1] [j]; + m [2] [j] = fColorMatrix1 [2] [j]; + m [3] [j] = fColorMatrix1 [1] [j]; + } + + fColorMatrix1 = m; + + } + + if (fColorMatrix2.NotEmpty ()) + { + + dng_matrix m (4, 3); + + for (j = 0; j < 3; j++) + { + m [0] [j] = fColorMatrix2 [0] [j]; + m [1] [j] = fColorMatrix2 [1] [j]; + m [2] [j] = fColorMatrix2 [2] [j]; + m [3] [j] = fColorMatrix2 [1] [j]; + } + + fColorMatrix2 = m; + + } + + fReductionMatrix1.Clear (); + fReductionMatrix2.Clear (); + + fForwardMatrix1.Clear (); + fForwardMatrix2.Clear (); + + } + +/*****************************************************************************/ + +dng_hue_sat_map * dng_camera_profile::HueSatMapForWhite (const dng_xy_coord &white) const + { + + if (fHueSatDeltas1.IsValid ()) + { + + // If we only have the first table, just use it for any color temperature. + + if (!fHueSatDeltas2.IsValid ()) + { + + return new dng_hue_sat_map (fHueSatDeltas1); + + } + + // Else we need to interpolate based on color temperature. + + real64 temperature1 = CalibrationTemperature1 (); + real64 temperature2 = CalibrationTemperature2 (); + + if (temperature1 <= 0.0 || + temperature2 <= 0.0 || + temperature1 == temperature2) + { + + return new dng_hue_sat_map (fHueSatDeltas1); + + } + + bool reverseOrder = temperature1 > temperature2; + + if (reverseOrder) + { + real64 temp = temperature1; + temperature1 = temperature2; + temperature2 = temp; + } + + // Convert to temperature/offset space. + + dng_temperature td (white); + + // Find fraction to weight the first calibration. + + real64 g; + + if (td.Temperature () <= temperature1) + g = 1.0; + + else if (td.Temperature () >= temperature2) + g = 0.0; + + else + { + + real64 invT = 1.0 / td.Temperature (); + + g = (invT - (1.0 / temperature2)) / + ((1.0 / temperature1) - (1.0 / temperature2)); + + } + + // Fix up if we swapped the order. + + if (reverseOrder) + { + g = 1.0 - g; + } + + // Do the interpolation. + + return dng_hue_sat_map::Interpolate (HueSatDeltas1 (), + HueSatDeltas2 (), + g); + + } + + return NULL; + + } + +/*****************************************************************************/ + +void dng_camera_profile::Stub () + { + + (void) Fingerprint (); + + dng_hue_sat_map nullTable; + + fHueSatDeltas1 = nullTable; + fHueSatDeltas2 = nullTable; + + fLookTable = nullTable; + + fToneCurve.SetInvalid (); + + fWasStubbed = true; + + } + +/*****************************************************************************/ + +void SplitCameraProfileName (const dng_string &name, + dng_string &baseName, + int32 &version) + { + + baseName = name; + + version = 0; + + uint32 len = baseName.Length (); + + if (len == 7 && baseName.StartsWith ("ACR ", true)) + { + + if (name.Get () [len - 3] >= '0' && + name.Get () [len - 3] <= '9' && + name.Get () [len - 2] == '.' && + name.Get () [len - 1] >= '0' && + name.Get () [len - 1] <= '9') + + baseName.Truncate (3); + + version = ((int32) (name.Get () [len - 3] - '0')) * 10 + + ((int32) (name.Get () [len - 1] - '0')); + + return; + + } + + if (len > 5 && baseName.EndsWith (" beta")) + { + + baseName.Truncate (len - 5); + + version += -10; + + } + + else if (len > 7) + { + + char lastChar = name.Get () [len - 1]; + + if (lastChar >= '0' && lastChar <= '9') + { + + dng_string temp = name; + + temp.Truncate (len - 1); + + if (temp.EndsWith (" beta ")) + { + + baseName.Truncate (len - 7); + + version += ((int32) (lastChar - '0')) - 10; + + } + + } + + } + + len = baseName.Length (); + + if (len > 3) + { + + char lastChar = name.Get () [len - 1]; + + if (lastChar >= '0' && lastChar <= '9') + { + + dng_string temp = name; + + temp.Truncate (len - 1); + + if (temp.EndsWith (" v")) + { + + baseName.Truncate (len - 3); + + version += ((int32) (lastChar - '0')) * 100; + + } + + } + + } + + } + +/*****************************************************************************/ + +void BuildHueSatMapEncodingTable (dng_memory_allocator &allocator, + uint32 encoding, + AutoPtr &encodeTable, + AutoPtr &decodeTable, + bool subSample) + { + + encodeTable.Reset (); + decodeTable.Reset (); + + switch (encoding) + { + + case encoding_Linear: + { + + break; + + } + + case encoding_sRGB: + { + + encodeTable.Reset (new dng_1d_table); + decodeTable.Reset (new dng_1d_table); + + const dng_1d_function & curve = dng_function_GammaEncode_sRGB::Get (); + + encodeTable->Initialize (allocator, + curve, + subSample); + + const dng_1d_inverse inverse (curve); + + decodeTable->Initialize (allocator, + inverse, + subSample); + + break; + + } + + default: + { + + DNG_REPORT ("Unsupported hue sat map / look table encoding."); + + break; + + } + + } + + } + +/*****************************************************************************/ diff --git a/dng/dng_camera_profile.h b/dng/dng_camera_profile.h new file mode 100644 index 0000000..fc2a00a --- /dev/null +++ b/dng/dng_camera_profile.h @@ -0,0 +1,879 @@ +/******************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/******************************************************************************/ + +/** \file + * Support for DNG camera color profile information. + * Per the \ref spec_dng "DNG 1.1.0 specification", a DNG file can store up to + * two sets of color profile information for a camera in the DNG file from that + * camera. The second set is optional and when there are two sets, they represent + * profiles made under different illumination. + * + * Profiling information is optionally separated into two parts. One part represents + * a profile for a reference camera. (ColorMatrix1 and ColorMatrix2 here.) The + * second is a per-camera calibration that takes into account unit-to-unit variation. + * This is designed to allow replacing the reference color matrix with one of one's + * own construction while maintaining any unit-specific calibration the camera + * manufacturer may have provided. + * + * See Appendix 6 of the \ref spec_dng "DNG 1.1.0 specification" for more information. + */ + +#ifndef __dng_camera_profile__ +#define __dng_camera_profile__ + +/******************************************************************************/ + +#include "dng_auto_ptr.h" +#include "dng_assertions.h" +#include "dng_classes.h" +#include "dng_fingerprint.h" +#include "dng_hue_sat_map.h" +#include "dng_matrix.h" +#include "dng_string.h" +#include "dng_tag_values.h" +#include "dng_tone_curve.h" + +/******************************************************************************/ + +extern const char * kProfileName_Embedded; + +extern const char * kAdobeCalibrationSignature; + +/******************************************************************************/ + +/// \brief An ID for a camera profile consisting of a name and optional fingerprint. + +class dng_camera_profile_id + { + + private: + + dng_string fName; + + dng_fingerprint fFingerprint; + + public: + + /// Construct an invalid camera profile ID (empty name and fingerprint). + + dng_camera_profile_id () + + : fName () + , fFingerprint () + + { + } + + /// Construct a camera profile ID with the specified name and no fingerprint. + /// \param name The name of the camera profile ID. + + dng_camera_profile_id (const char *name) + + : fName () + , fFingerprint () + + { + fName.Set (name); + } + + /// Construct a camera profile ID with the specified name and no fingerprint. + /// \param name The name of the camera profile ID. + + dng_camera_profile_id (const dng_string &name) + + : fName (name) + , fFingerprint () + + { + } + + /// Construct a camera profile ID with the specified name and fingerprint. + /// \param name The name of the camera profile ID. + /// \param fingerprint The fingerprint of the camera profile ID. + + dng_camera_profile_id (const char *name, + const dng_fingerprint &fingerprint) + + : fName () + , fFingerprint (fingerprint) + + { + fName.Set (name); + DNG_ASSERT (!fFingerprint.IsValid () || fName.NotEmpty (), + "Cannot have profile fingerprint without name"); + } + + /// Construct a camera profile ID with the specified name and fingerprint. + /// \param name The name of the camera profile ID. + /// \param fingerprint The fingerprint of the camera profile ID. + + dng_camera_profile_id (const dng_string &name, + const dng_fingerprint &fingerprint) + + : fName (name) + , fFingerprint (fingerprint) + + { + DNG_ASSERT (!fFingerprint.IsValid () || fName.NotEmpty (), + "Cannot have profile fingerprint without name"); + } + + /// Getter for the name of the camera profile ID. + /// \retval The name of the camera profile ID. + + const dng_string & Name () const + { + return fName; + } + + /// Getter for the fingerprint of the camera profile ID. + /// \retval The fingerprint of the camera profile ID. + + const dng_fingerprint & Fingerprint () const + { + return fFingerprint; + } + + /// Test for equality of two camera profile IDs. + /// \param id The id of the camera profile ID to compare. + + bool operator== (const dng_camera_profile_id &id) const + { + return fName == id.fName && + fFingerprint == id.fFingerprint; + } + + /// Test for inequality of two camera profile IDs. + /// \param id The id of the camera profile ID to compare. + + bool operator!= (const dng_camera_profile_id &id) const + { + return !(*this == id); + } + + /// Returns true iff the camera profile ID is valid. + + bool IsValid () const + { + return fName.NotEmpty (); // Fingerprint is optional. + } + + /// Resets the name and fingerprint, thereby making this camera profile ID + /// invalid. + + void Clear () + { + *this = dng_camera_profile_id (); + } + + }; + +/******************************************************************************/ + +/// \brief Container for DNG camera color profile and calibration data. + +class dng_camera_profile + { + + protected: + + // Name of this camera profile. + + dng_string fName; + + // Light sources for up to two calibrations. These use the EXIF + // encodings for illuminant and are used to distinguish which + // matrix to use. + + uint32 fCalibrationIlluminant1; + uint32 fCalibrationIlluminant2; + + // Color matrices for up to two calibrations. + + // These matrices map XYZ values to non-white balanced camera values. + // Adobe needs to go that direction in order to determine the clipping + // points for highlight recovery logic based on the white point. If + // cameras were all 3-color, the matrix could be stored as a forward matrix, + // but we need the backwards matrix to deal with 4-color cameras. + + dng_matrix fColorMatrix1; + dng_matrix fColorMatrix2; + + // These matrices map white balanced camera values to XYZ chromatically + // adapted to D50 (the ICC profile PCS white point). If the matrices + // exist, then this implies that white balancing should be done by scaling + // camera values with a diagonal matrix. + + dng_matrix fForwardMatrix1; + dng_matrix fForwardMatrix2; + + // Dimensionality reduction hints for more than three color cameras. + // This is an optional matrix that maps the camera's color components + // to 3 components. These are only used if the forward matrices don't + // exist, and are used invert the color matrices. + + dng_matrix fReductionMatrix1; + dng_matrix fReductionMatrix2; + + // MD5 hash for all data bits of the profile. + + mutable dng_fingerprint fFingerprint; + + // Copyright notice from creator of profile. + + dng_string fCopyright; + + // Rules for how this profile can be embedded and/or copied. + + uint32 fEmbedPolicy; + + // 2-D (or 3-D) hue/sat tables to modify colors. + + dng_hue_sat_map fHueSatDeltas1; + dng_hue_sat_map fHueSatDeltas2; + + // Value (V of HSV) encoding for hue/sat tables. + + uint32 fHueSatMapEncoding; + + // 3-D hue/sat table to apply a "look". + + dng_hue_sat_map fLookTable; + + // Value (V of HSV) encoding for look table. + + uint32 fLookTableEncoding; + + // Baseline exposure offset. When using this profile, this offset value is + // added to the BaselineExposure value for the negative to determine the + // overall baseline exposure to apply. + + dng_srational fBaselineExposureOffset; + + // Default black rendering. + + uint32 fDefaultBlackRender; + + // The "as shot" tone curve for this profile. Check IsValid method + // to tell if one exists in profile. + + dng_tone_curve fToneCurve; + + // If this string matches the fCameraCalibrationSignature of the + // negative, then use the calibration matrix values from the negative. + + dng_string fProfileCalibrationSignature; + + // If non-empty, only allow use of this profile with camera having + // same unique model name. + + dng_string fUniqueCameraModelRestriction; + + // Was this profile read from inside a DNG file? (If so, we wnat + // to be sure to include it again when writing out an updated + // DNG file) + + bool fWasReadFromDNG; + + // Was this profile read from disk (i.e., an external profile)? (If so, we + // may need to refresh when changes are made externally to the profile + // directory.) + + bool fWasReadFromDisk; + + // Was this profile a built-in "Matrix" profile? (If so, we may need to + // refresh -- i.e., remove it from the list of available profiles -- when + // changes are made externally to the profile directory.) + + bool fWasBuiltinMatrix; + + // Was this profile stubbed to save memory (and no longer valid + // for building color conversion tables)? + + bool fWasStubbed; + + public: + + dng_camera_profile (); + + virtual ~dng_camera_profile (); + + // API for profile name: + + /// Setter for camera profile name. + /// \param name Name to use for this camera profile. + + void SetName (const char *name) + { + fName.Set (name); + ClearFingerprint (); + } + + /// Getter for camera profile name. + /// \retval Name of profile. + + const dng_string & Name () const + { + return fName; + } + + /// Test if this name is embedded. + /// \retval true if the name matches the name of the embedded camera profile. + + bool NameIsEmbedded () const + { + return fName.Matches (kProfileName_Embedded, true); + } + + // API for calibration illuminants: + + /// Setter for first of up to two light sources used for calibration. + /// Uses the EXIF encodings for illuminant and is used to distinguish which + /// matrix to use. + /// Corresponds to the DNG CalibrationIlluminant1 tag. + + void SetCalibrationIlluminant1 (uint32 light) + { + fCalibrationIlluminant1 = light; + ClearFingerprint (); + } + + /// Setter for second of up to two light sources used for calibration. + /// Uses the EXIF encodings for illuminant and is used to distinguish which + /// matrix to use. + /// Corresponds to the DNG CalibrationIlluminant2 tag. + + void SetCalibrationIlluminant2 (uint32 light) + { + fCalibrationIlluminant2 = light; + ClearFingerprint (); + } + + /// Getter for first of up to two light sources used for calibration. + /// Uses the EXIF encodings for illuminant and is used to distinguish which + /// matrix to use. + /// Corresponds to the DNG CalibrationIlluminant1 tag. + + uint32 CalibrationIlluminant1 () const + { + return fCalibrationIlluminant1; + } + + /// Getter for second of up to two light sources used for calibration. + /// Uses the EXIF encodings for illuminant and is used to distinguish which + /// matrix to use. + /// Corresponds to the DNG CalibrationIlluminant2 tag. + + uint32 CalibrationIlluminant2 () const + { + return fCalibrationIlluminant2; + } + + /// Getter for first of up to two light sources used for calibration, returning + /// result as color temperature. + + real64 CalibrationTemperature1 () const + { + return IlluminantToTemperature (CalibrationIlluminant1 ()); + } + + /// Getter for second of up to two light sources used for calibration, returning + /// result as color temperature. + + real64 CalibrationTemperature2 () const + { + return IlluminantToTemperature (CalibrationIlluminant2 ()); + } + + // API for color matrices: + + /// Utility function to normalize the scale of the color matrix. + + static void NormalizeColorMatrix (dng_matrix &m); + + /// Setter for first of up to two color matrices used for reference camera calibrations. + /// These matrices map XYZ values to camera values. The DNG SDK needs to map colors + /// that direction in order to determine the clipping points for + /// highlight recovery logic based on the white point. If cameras + /// were all three-color, the matrix could be stored as a forward matrix. + /// The inverse matrix is requried to support four-color cameras. + + void SetColorMatrix1 (const dng_matrix &m); + + /// Setter for second of up to two color matrices used for reference camera calibrations. + /// These matrices map XYZ values to camera values. The DNG SDK needs to map colors + /// that direction in order to determine the clipping points for + /// highlight recovery logic based on the white point. If cameras + /// were all three-color, the matrix could be stored as a forward matrix. + /// The inverse matrix is requried to support four-color cameras. + + void SetColorMatrix2 (const dng_matrix &m); + + /// Predicate to test if first camera matrix is set + + bool HasColorMatrix1 () const; + + /// Predicate to test if second camera matrix is set + + bool HasColorMatrix2 () const; + + /// Getter for first of up to two color matrices used for calibrations. + + const dng_matrix & ColorMatrix1 () const + { + return fColorMatrix1; + } + + /// Getter for second of up to two color matrices used for calibrations. + + const dng_matrix & ColorMatrix2 () const + { + return fColorMatrix2; + } + + // API for forward matrices: + + /// Utility function to normalize the scale of the forward matrix. + + static void NormalizeForwardMatrix (dng_matrix &m); + + /// Setter for first of up to two forward matrices used for calibrations. + + void SetForwardMatrix1 (const dng_matrix &m); + + /// Setter for second of up to two forward matrices used for calibrations. + + void SetForwardMatrix2 (const dng_matrix &m); + + /// Getter for first of up to two forward matrices used for calibrations. + + const dng_matrix & ForwardMatrix1 () const + { + return fForwardMatrix1; + } + + /// Getter for second of up to two forward matrices used for calibrations. + + const dng_matrix & ForwardMatrix2 () const + { + return fForwardMatrix2; + } + + // API for reduction matrices: + + /// Setter for first of up to two dimensionality reduction hints for four-color cameras. + /// This is an optional matrix that maps four components to three. + /// See Appendix 6 of the \ref spec_dng "DNG 1.1.0 specification." + + void SetReductionMatrix1 (const dng_matrix &m); + + /// Setter for second of up to two dimensionality reduction hints for four-color cameras. + /// This is an optional matrix that maps four components to three. + /// See Appendix 6 of the \ref spec_dng "DNG 1.1.0 specification." + + void SetReductionMatrix2 (const dng_matrix &m); + + /// Getter for first of up to two dimensionality reduction hints for four color cameras. + + const dng_matrix & ReductionMatrix1 () const + { + return fReductionMatrix1; + } + + /// Getter for second of up to two dimensionality reduction hints for four color cameras. + + const dng_matrix & ReductionMatrix2 () const + { + return fReductionMatrix2; + } + + /// Getter function from profile fingerprint. + + const dng_fingerprint &Fingerprint () const + { + + if (!fFingerprint.IsValid ()) + CalculateFingerprint (); + + return fFingerprint; + + } + + /// Getter for camera profile unique ID. Use this ID for uniquely + /// identifying profiles (e.g., for syncing purposes). + + dng_fingerprint UniqueID () const; + + /// Getter for camera profile id. + /// \retval ID of profile. + + dng_camera_profile_id ProfileID () const + { + return dng_camera_profile_id (Name (), Fingerprint ()); + } + + /// Setter for camera profile copyright. + /// \param copyright Copyright string to use for this camera profile. + + void SetCopyright (const char *copyright) + { + fCopyright.Set (copyright); + ClearFingerprint (); + } + + /// Getter for camera profile copyright. + /// \retval Copyright string for profile. + + const dng_string & Copyright () const + { + return fCopyright; + } + + // Accessors for embed policy. + + /// Setter for camera profile embed policy. + /// \param policy Policy to use for this camera profile. + + void SetEmbedPolicy (uint32 policy) + { + fEmbedPolicy = policy; + ClearFingerprint (); + } + + /// Getter for camera profile embed policy. + /// \retval Policy for profile. + + uint32 EmbedPolicy () const + { + return fEmbedPolicy; + } + + /// Returns true iff the profile is legal to embed in a DNG, per the + /// profile's embed policy. + + bool IsLegalToEmbed () const + { + return WasReadFromDNG () || + EmbedPolicy () == pepAllowCopying || + EmbedPolicy () == pepEmbedIfUsed || + EmbedPolicy () == pepNoRestrictions; + } + + // Accessors for hue sat maps. + + /// Returns true iff the profile has a valid HueSatMap color table. + + bool HasHueSatDeltas () const + { + return fHueSatDeltas1.IsValid (); + } + + /// Getter for first HueSatMap color table (for calibration illuminant 1). + + const dng_hue_sat_map & HueSatDeltas1 () const + { + return fHueSatDeltas1; + } + + /// Setter for first HueSatMap color table (for calibration illuminant 1). + + void SetHueSatDeltas1 (const dng_hue_sat_map &deltas1); + + /// Getter for second HueSatMap color table (for calibration illuminant 2). + + const dng_hue_sat_map & HueSatDeltas2 () const + { + return fHueSatDeltas2; + } + + /// Setter for second HueSatMap color table (for calibration illuminant 2). + + void SetHueSatDeltas2 (const dng_hue_sat_map &deltas2); + + // Accessors for hue sat map encoding. + + /// Returns the hue sat map encoding (see ProfileHueSatMapEncoding tag). + + uint32 HueSatMapEncoding () const + { + return fHueSatMapEncoding; + } + + /// Sets the hue sat map encoding (see ProfileHueSatMapEncoding tag) to the + /// specified encoding. + + void SetHueSatMapEncoding (uint32 encoding) + { + fHueSatMapEncoding = encoding; + ClearFingerprint (); + } + + // Accessors for look table. + + /// Returns true if the profile has a LookTable. + + bool HasLookTable () const + { + return fLookTable.IsValid (); + } + + /// Getter for LookTable. + + const dng_hue_sat_map & LookTable () const + { + return fLookTable; + } + + /// Setter for LookTable. + + void SetLookTable (const dng_hue_sat_map &table); + + // Accessors for look table encoding. + + /// Returns the LookTable encoding (see ProfileLookTableEncoding tag). + + uint32 LookTableEncoding () const + { + return fLookTableEncoding; + } + + /// Sets the LookTable encoding (see ProfileLookTableEncoding tag) to the + /// specified encoding. + + void SetLookTableEncoding (uint32 encoding) + { + fLookTableEncoding = encoding; + ClearFingerprint (); + } + + // Accessors for baseline exposure offset. + + /// Sets the baseline exposure offset of the profile (see + /// BaselineExposureOffset tag) to the specified value. + + void SetBaselineExposureOffset (real64 exposureOffset) + { + fBaselineExposureOffset.Set_real64 (exposureOffset, 100); + ClearFingerprint (); + } + + /// Returns the baseline exposure offset of the profile (see + /// BaselineExposureOffset tag). + + const dng_srational & BaselineExposureOffset () const + { + return fBaselineExposureOffset; + } + + // Accessors for default black render. + + /// Sets the default black render of the profile (see DefaultBlackRender tag) + /// to the specified option. + + void SetDefaultBlackRender (uint32 defaultBlackRender) + { + fDefaultBlackRender = defaultBlackRender; + ClearFingerprint (); + } + + /// Returns the default black render of the profile (see DefaultBlackRender + /// tag). + + uint32 DefaultBlackRender () const + { + return fDefaultBlackRender; + } + + // Accessors for tone curve. + + /// Returns the tone curve of the profile. + + const dng_tone_curve & ToneCurve () const + { + return fToneCurve; + } + + /// Sets the tone curve of the profile to the specified curve. + + void SetToneCurve (const dng_tone_curve &curve) + { + fToneCurve = curve; + ClearFingerprint (); + } + + // Accessors for profile calibration signature. + + /// Sets the profile calibration signature (see ProfileCalibrationSignature + /// tag) to the specified string. + + void SetProfileCalibrationSignature (const char *signature) + { + fProfileCalibrationSignature.Set (signature); + ClearFingerprint (); + } + + /// Returns the profile calibration signature (see ProfileCalibrationSignature + /// tag) of the profile. + + const dng_string & ProfileCalibrationSignature () const + { + return fProfileCalibrationSignature; + } + + /// Setter for camera unique model name to restrict use of this profile. + /// \param camera Camera unique model name designating only camera this + /// profile can be used with. (Empty string for no restriction.) + + void SetUniqueCameraModelRestriction (const char *camera) + { + fUniqueCameraModelRestriction.Set (camera); + // Not included in fingerprint, so don't need ClearFingerprint (). + } + + /// Getter for camera unique model name to restrict use of this profile. + /// \retval Unique model name of only camera this profile can be used with + /// or empty if no restriction. + + const dng_string & UniqueCameraModelRestriction () const + { + return fUniqueCameraModelRestriction; + } + + // Accessors for was read from DNG flag. + + /// Sets internal flag to indicate this profile was originally read from a + /// DNG file. + + void SetWasReadFromDNG (bool state = true) + { + fWasReadFromDNG = state; + } + + /// Was this profile read from a DNG? + + bool WasReadFromDNG () const + { + return fWasReadFromDNG; + } + + // Accessors for was read from disk flag. + + /// Sets internal flag to indicate this profile was originally read from + /// disk. + + void SetWasReadFromDisk (bool state = true) + { + fWasReadFromDisk = state; + } + + /// Was this profile read from disk? + + bool WasReadFromDisk () const + { + return fWasReadFromDisk; + } + + // Accessors for was built-in matrix flag. + + /// Sets internal flag to indicate this profile was originally a built-in + /// matrix profile. + + void SetWasBuiltinMatrix (bool state = true) + { + fWasBuiltinMatrix = state; + } + + /// Was this profile a built-in matrix profile? + + bool WasBuiltinMatrix () const + { + return fWasBuiltinMatrix; + } + + /// Determines if this a valid profile for this number of color channels? + /// \retval true if the profile is valid. + + bool IsValid (uint32 channels) const; + + /// Predicate to check if two camera profiles are colorwise equal, thus ignores + /// the profile name. + /// \param profile Camera profile to compare to. + + bool EqualData (const dng_camera_profile &profile) const; + + /// Parse profile from dng_camera_profile_info data. + + void Parse (dng_stream &stream, + dng_camera_profile_info &profileInfo); + + /// Parse from an extended profile stream, which is similar to stand alone + /// TIFF file. + + bool ParseExtended (dng_stream &stream); + + /// Convert from a three-color to a four-color Bayer profile. + + virtual void SetFourColorBayer (); + + /// Find the hue/sat table to use for a given white point, if any. + /// The calling routine owns the resulting table. + + dng_hue_sat_map * HueSatMapForWhite (const dng_xy_coord &white) const; + + /// Stub out the profile (free memory used by large tables). + + void Stub (); + + /// Was this profile stubbed? + + bool WasStubbed () const + { + return fWasStubbed; + } + + protected: + + static real64 IlluminantToTemperature (uint32 light); + + void ClearFingerprint () + { + fFingerprint.Clear (); + } + + void CalculateFingerprint () const; + + static bool ValidForwardMatrix (const dng_matrix &m); + + static void ReadHueSatMap (dng_stream &stream, + dng_hue_sat_map &hueSatMap, + uint32 hues, + uint32 sats, + uint32 vals, + bool skipSat0); + + }; + +/******************************************************************************/ + +void SplitCameraProfileName (const dng_string &name, + dng_string &baseName, + int32 &version); + +/*****************************************************************************/ + +void BuildHueSatMapEncodingTable (dng_memory_allocator &allocator, + uint32 encoding, + AutoPtr &encodeTable, + AutoPtr &decodeTable, + bool subSample); + +/******************************************************************************/ + +#endif + +/******************************************************************************/ diff --git a/dng/dng_classes.h b/dng/dng_classes.h new file mode 100644 index 0000000..c204342 --- /dev/null +++ b/dng/dng_classes.h @@ -0,0 +1,108 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/*** \file + * Forward class declarations to avoid having to include many .h files in most places. + */ + +/*****************************************************************************/ + +#ifndef __dng_classes__ +#define __dng_classes__ + +/*****************************************************************************/ + +class dng_1d_function; +class dng_1d_table; +class dng_abort_sniffer; +class dng_area_task; +class dng_area_task_progress; +class dng_base_tile_iterator; +class dng_basic_tag_set; +class dng_big_table; +class dng_camera_profile; +class dng_camera_profile_id; +class dng_camera_profile_info; +class dng_color_space; +class dng_color_spec; +class dng_date_time; +class dng_date_time_info; +class dng_exif; +class dng_fingerprint; +class dng_host; +class dng_hue_sat_map; +class dng_ifd; +class dng_image; +class dng_image_preview; +class dng_image_writer; +class dng_info; +class dng_iptc; +class dng_jpeg_image; +class dng_jpeg_preview; +class dng_linearization_info; +class dng_local_string; +class dng_look_table; +class dng_matrix; +class dng_matrix_3by3; +class dng_matrix_4by3; +class dng_md5_printer; +class dng_memory_allocator; +class dng_memory_block; +class dng_memory_data; +class dng_memory_stream; +class dng_metadata; +class dng_mosaic_info; +class dng_mutex; +class dng_noise_function; +class dng_noise_profile; +class dng_opcode; +class dng_opcode_list; +class dng_orientation; +class dng_negative; +class dng_pixel_buffer; +class dng_point; +class dng_point_real64; +class dng_preview; +class dng_preview_info; +class dng_preview_list; +class dng_raw_preview; +class dng_read_image; +class dng_rect; +class dng_rect_real64; +class dng_ref_counted_block; +class dng_render; +class dng_resample_function; +class dng_resolution; +class dng_rgb_table; +class dng_set_minimum_priority; +class dng_shared; +class dng_spline_solver; +class dng_srational; +class dng_stream; +class dng_string; +class dng_string_list; +class dng_tiff_directory; +class dng_tile_buffer; +class dng_time_zone; +class dng_tone_curve; +class dng_urational; +class dng_vector; +class dng_vector_3; +class dng_xmp; +class dng_xmp_sdk; +class dng_xy_coord; + +/*****************************************************************************/ + +struct dng_xmp_namespace; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_color_space.cpp b/dng/dng_color_space.cpp new file mode 100644 index 0000000..40df398 --- /dev/null +++ b/dng/dng_color_space.cpp @@ -0,0 +1,1067 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_color_space.h" + +#include "dng_1d_table.h" +#include "dng_exceptions.h" +#include "dng_flags.h" +#include "dng_matrix.h" +#include "dng_spline.h" +#include "dng_utils.h" +#include "dng_xy_coord.h" + +/*****************************************************************************/ + +real64 dng_function_GammaEncode_sRGB::Evaluate (real64 x) const + { + + if (x <= 0.0031308) + return x * 12.92; + + else + return 1.055 * pow (x, 1.0 / 2.4) - 0.055; + + } + +/*****************************************************************************/ + +real64 dng_function_GammaEncode_sRGB::EvaluateInverse (real64 y) const + { + + if (y <= 0.0031308 * 12.92) + return y * (1.0 / 12.92); + + else + return pow ((y + 0.055) * (1.0 / 1.055), 2.4); + + } + +/*****************************************************************************/ + +const dng_1d_function & dng_function_GammaEncode_sRGB::Get () + { + + static dng_function_GammaEncode_sRGB static_function; + + return static_function; + + } + +/*****************************************************************************/ + +real64 dng_function_GammaEncode_1_8::Evaluate (real64 x) const + { + + const real64 gamma = 1.0 / 1.8; + + const real64 slope0 = 32.0; + + const real64 x1 = 8.2118790552e-4; // pow (slope0, 1.0 / (gamma - 1.0)) * 2.0 + + const real64 y1 = 0.019310851; // pow (x1, gamma) + + const real64 slope1 = 13.064306598; // gamma * pow (x1, gamma - 1.0) + + if (x <= x1) + return EvaluateSplineSegment (x, + 0.0, + 0.0, + slope0, + x1, + y1, + slope1); + + else + return pow (x, gamma); + + } + +/*****************************************************************************/ + +real64 dng_function_GammaEncode_1_8::EvaluateInverse (real64 y) const + { + + if (y > 0.0 && y < 0.019310851) + { + + return dng_1d_function::EvaluateInverse (y); + + } + + return pow (y, 1.8); + + } + +/*****************************************************************************/ + +const dng_1d_function & dng_function_GammaEncode_1_8::Get () + { + + static dng_function_GammaEncode_1_8 static_function; + + return static_function; + + } + +/*****************************************************************************/ + +real64 dng_function_GammaEncode_2_2::Evaluate (real64 x) const + { + + const real64 gamma = 1.0 / 2.2; + + const real64 slope0 = 32.0; + + const real64 x1 = 0.0034800731; // pow (slope0, 1.0 / (gamma - 1.0)) * 2.0 + + const real64 y1 = 0.0763027458; // pow (x1, gamma) + + const real64 slope1 = 9.9661890075; // gamma * pow (x1, gamma - 1.0) + + if (x <= x1) + return EvaluateSplineSegment (x, + 0.0, + 0.0, + slope0, + x1, + y1, + slope1); + + else + return pow (x, gamma); + + } + +/*****************************************************************************/ + +real64 dng_function_GammaEncode_2_2::EvaluateInverse (real64 y) const + { + + if (y > 0.0 && y < 0.0763027458) + { + + return dng_1d_function::EvaluateInverse (y); + + } + + return pow (y, 2.2); + + } + +/*****************************************************************************/ + +const dng_1d_function & dng_function_GammaEncode_2_2::Get () + { + + static dng_function_GammaEncode_2_2 static_function; + + return static_function; + + } + +/*****************************************************************************/ + +dng_color_space::dng_color_space () + + : fMatrixToPCS () + , fMatrixFromPCS () + + { + + } + +/*****************************************************************************/ + +dng_color_space::~dng_color_space () + { + + } + +/*****************************************************************************/ + +void dng_color_space::SetMonochrome () + { + + fMatrixToPCS = PCStoXYZ ().AsColumn (); + + dng_matrix m (1, 3); + + m [0] [0] = 0.0; + m [0] [1] = 1.0; + m [0] [2] = 0.0; + + fMatrixFromPCS = m; + + } + +/*****************************************************************************/ + +void dng_color_space::SetMatrixToPCS (const dng_matrix_3by3 &M) + { + + // The matrix values are often rounded, so adjust to + // get them to convert device white exactly to the PCS. + + dng_vector_3 W1 = M * dng_vector_3 (1.0, 1.0, 1.0); + dng_vector_3 W2 = PCStoXYZ (); + + real64 s0 = W2 [0] / W1 [0]; + real64 s1 = W2 [1] / W1 [1]; + real64 s2 = W2 [2] / W1 [2]; + + dng_matrix_3by3 S (s0, 0, 0, + 0, s1, 0, + 0, 0, s2); + + fMatrixToPCS = S * M; + + // Find reverse matrix. + + fMatrixFromPCS = Invert (fMatrixToPCS); + + } + +/*****************************************************************************/ + +const dng_1d_function & dng_color_space::GammaFunction () const + { + + return dng_1d_identity::Get (); + + } + +/*****************************************************************************/ + +bool dng_color_space::ICCProfile (uint32 &size, + const uint8 *&data) const + { + + size = 0; + data = NULL; + + return false; + + } + +/*****************************************************************************/ + +dng_space_sRGB::dng_space_sRGB () + { + + SetMatrixToPCS (dng_matrix_3by3 (0.4361, 0.3851, 0.1431, + 0.2225, 0.7169, 0.0606, + 0.0139, 0.0971, 0.7141)); + + } + +/*****************************************************************************/ + +const dng_1d_function & dng_space_sRGB::GammaFunction () const + { + + return dng_function_GammaEncode_sRGB::Get (); + + } + +/*****************************************************************************/ + +bool dng_space_sRGB::ICCProfile (uint32 &size, + const uint8 *&data) const + + { + + static const uint8 ksRGBProfileData [] = + { + 0x00, 0x00, 0x0C, 0x48, 0x4C, 0x69, 0x6E, 0x6F, 0x02, 0x10, 0x00, 0x00, + 0x6D, 0x6E, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5A, 0x20, + 0x07, 0xCE, 0x00, 0x02, 0x00, 0x09, 0x00, 0x06, 0x00, 0x31, 0x00, 0x00, + 0x61, 0x63, 0x73, 0x70, 0x4D, 0x53, 0x46, 0x54, 0x00, 0x00, 0x00, 0x00, + 0x49, 0x45, 0x43, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xD6, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x2D, 0x48, 0x50, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, + 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x01, 0x50, 0x00, 0x00, 0x00, 0x33, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x01, 0x84, 0x00, 0x00, 0x00, 0x6C, + 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x00, 0x14, + 0x62, 0x6B, 0x70, 0x74, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, 0x14, + 0x72, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x00, 0x14, + 0x67, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x02, 0x2C, 0x00, 0x00, 0x00, 0x14, + 0x62, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x14, + 0x64, 0x6D, 0x6E, 0x64, 0x00, 0x00, 0x02, 0x54, 0x00, 0x00, 0x00, 0x70, + 0x64, 0x6D, 0x64, 0x64, 0x00, 0x00, 0x02, 0xC4, 0x00, 0x00, 0x00, 0x88, + 0x76, 0x75, 0x65, 0x64, 0x00, 0x00, 0x03, 0x4C, 0x00, 0x00, 0x00, 0x86, + 0x76, 0x69, 0x65, 0x77, 0x00, 0x00, 0x03, 0xD4, 0x00, 0x00, 0x00, 0x24, + 0x6C, 0x75, 0x6D, 0x69, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x00, 0x14, + 0x6D, 0x65, 0x61, 0x73, 0x00, 0x00, 0x04, 0x0C, 0x00, 0x00, 0x00, 0x24, + 0x74, 0x65, 0x63, 0x68, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x0C, + 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04, 0x3C, 0x00, 0x00, 0x08, 0x0C, + 0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04, 0x3C, 0x00, 0x00, 0x08, 0x0C, + 0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04, 0x3C, 0x00, 0x00, 0x08, 0x0C, + 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6F, 0x70, 0x79, + 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, + 0x39, 0x38, 0x20, 0x48, 0x65, 0x77, 0x6C, 0x65, 0x74, 0x74, 0x2D, 0x50, + 0x61, 0x63, 0x6B, 0x61, 0x72, 0x64, 0x20, 0x43, 0x6F, 0x6D, 0x70, 0x61, + 0x6E, 0x79, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x12, 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, + 0x36, 0x31, 0x39, 0x36, 0x36, 0x2D, 0x32, 0x2E, 0x31, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x73, 0x52, 0x47, + 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2D, 0x32, + 0x2E, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF3, 0x51, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x16, 0xCC, + 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x59, 0x5A, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6F, 0xA2, 0x00, 0x00, 0x38, 0xF5, + 0x00, 0x00, 0x03, 0x90, 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x62, 0x99, 0x00, 0x00, 0xB7, 0x85, 0x00, 0x00, 0x18, 0xDA, + 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xA0, + 0x00, 0x00, 0x0F, 0x84, 0x00, 0x00, 0xB6, 0xCF, 0x64, 0x65, 0x73, 0x63, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x49, 0x45, 0x43, 0x20, + 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x77, 0x77, 0x2E, 0x69, + 0x65, 0x63, 0x2E, 0x63, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x16, 0x49, 0x45, 0x43, 0x20, 0x68, 0x74, 0x74, + 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x77, 0x77, 0x2E, 0x69, 0x65, 0x63, 0x2E, + 0x63, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2E, + 0x49, 0x45, 0x43, 0x20, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2D, 0x32, 0x2E, + 0x31, 0x20, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x52, 0x47, + 0x42, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x75, 0x72, 0x20, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x20, 0x2D, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x49, 0x45, 0x43, + 0x20, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2D, 0x32, 0x2E, 0x31, 0x20, 0x44, + 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x52, 0x47, 0x42, 0x20, 0x63, + 0x6F, 0x6C, 0x6F, 0x75, 0x72, 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, + 0x2D, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x2C, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6E, 0x63, + 0x65, 0x20, 0x56, 0x69, 0x65, 0x77, 0x69, 0x6E, 0x67, 0x20, 0x43, 0x6F, + 0x6E, 0x64, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x49, + 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2D, 0x32, 0x2E, 0x31, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x52, + 0x65, 0x66, 0x65, 0x72, 0x65, 0x6E, 0x63, 0x65, 0x20, 0x56, 0x69, 0x65, + 0x77, 0x69, 0x6E, 0x67, 0x20, 0x43, 0x6F, 0x6E, 0x64, 0x69, 0x74, 0x69, + 0x6F, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, + 0x36, 0x36, 0x2D, 0x32, 0x2E, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x69, 0x65, 0x77, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0xA4, 0xFE, 0x00, 0x14, 0x5F, 0x2E, + 0x00, 0x10, 0xCF, 0x14, 0x00, 0x03, 0xED, 0xCC, 0x00, 0x04, 0x13, 0x0B, + 0x00, 0x03, 0x5C, 0x9E, 0x00, 0x00, 0x00, 0x01, 0x58, 0x59, 0x5A, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x09, 0x56, 0x00, 0x50, 0x00, 0x00, + 0x00, 0x57, 0x1F, 0xE7, 0x6D, 0x65, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x8F, + 0x00, 0x00, 0x00, 0x02, 0x73, 0x69, 0x67, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x52, 0x54, 0x20, 0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x0F, + 0x00, 0x14, 0x00, 0x19, 0x00, 0x1E, 0x00, 0x23, 0x00, 0x28, 0x00, 0x2D, + 0x00, 0x32, 0x00, 0x37, 0x00, 0x3B, 0x00, 0x40, 0x00, 0x45, 0x00, 0x4A, + 0x00, 0x4F, 0x00, 0x54, 0x00, 0x59, 0x00, 0x5E, 0x00, 0x63, 0x00, 0x68, + 0x00, 0x6D, 0x00, 0x72, 0x00, 0x77, 0x00, 0x7C, 0x00, 0x81, 0x00, 0x86, + 0x00, 0x8B, 0x00, 0x90, 0x00, 0x95, 0x00, 0x9A, 0x00, 0x9F, 0x00, 0xA4, + 0x00, 0xA9, 0x00, 0xAE, 0x00, 0xB2, 0x00, 0xB7, 0x00, 0xBC, 0x00, 0xC1, + 0x00, 0xC6, 0x00, 0xCB, 0x00, 0xD0, 0x00, 0xD5, 0x00, 0xDB, 0x00, 0xE0, + 0x00, 0xE5, 0x00, 0xEB, 0x00, 0xF0, 0x00, 0xF6, 0x00, 0xFB, 0x01, 0x01, + 0x01, 0x07, 0x01, 0x0D, 0x01, 0x13, 0x01, 0x19, 0x01, 0x1F, 0x01, 0x25, + 0x01, 0x2B, 0x01, 0x32, 0x01, 0x38, 0x01, 0x3E, 0x01, 0x45, 0x01, 0x4C, + 0x01, 0x52, 0x01, 0x59, 0x01, 0x60, 0x01, 0x67, 0x01, 0x6E, 0x01, 0x75, + 0x01, 0x7C, 0x01, 0x83, 0x01, 0x8B, 0x01, 0x92, 0x01, 0x9A, 0x01, 0xA1, + 0x01, 0xA9, 0x01, 0xB1, 0x01, 0xB9, 0x01, 0xC1, 0x01, 0xC9, 0x01, 0xD1, + 0x01, 0xD9, 0x01, 0xE1, 0x01, 0xE9, 0x01, 0xF2, 0x01, 0xFA, 0x02, 0x03, + 0x02, 0x0C, 0x02, 0x14, 0x02, 0x1D, 0x02, 0x26, 0x02, 0x2F, 0x02, 0x38, + 0x02, 0x41, 0x02, 0x4B, 0x02, 0x54, 0x02, 0x5D, 0x02, 0x67, 0x02, 0x71, + 0x02, 0x7A, 0x02, 0x84, 0x02, 0x8E, 0x02, 0x98, 0x02, 0xA2, 0x02, 0xAC, + 0x02, 0xB6, 0x02, 0xC1, 0x02, 0xCB, 0x02, 0xD5, 0x02, 0xE0, 0x02, 0xEB, + 0x02, 0xF5, 0x03, 0x00, 0x03, 0x0B, 0x03, 0x16, 0x03, 0x21, 0x03, 0x2D, + 0x03, 0x38, 0x03, 0x43, 0x03, 0x4F, 0x03, 0x5A, 0x03, 0x66, 0x03, 0x72, + 0x03, 0x7E, 0x03, 0x8A, 0x03, 0x96, 0x03, 0xA2, 0x03, 0xAE, 0x03, 0xBA, + 0x03, 0xC7, 0x03, 0xD3, 0x03, 0xE0, 0x03, 0xEC, 0x03, 0xF9, 0x04, 0x06, + 0x04, 0x13, 0x04, 0x20, 0x04, 0x2D, 0x04, 0x3B, 0x04, 0x48, 0x04, 0x55, + 0x04, 0x63, 0x04, 0x71, 0x04, 0x7E, 0x04, 0x8C, 0x04, 0x9A, 0x04, 0xA8, + 0x04, 0xB6, 0x04, 0xC4, 0x04, 0xD3, 0x04, 0xE1, 0x04, 0xF0, 0x04, 0xFE, + 0x05, 0x0D, 0x05, 0x1C, 0x05, 0x2B, 0x05, 0x3A, 0x05, 0x49, 0x05, 0x58, + 0x05, 0x67, 0x05, 0x77, 0x05, 0x86, 0x05, 0x96, 0x05, 0xA6, 0x05, 0xB5, + 0x05, 0xC5, 0x05, 0xD5, 0x05, 0xE5, 0x05, 0xF6, 0x06, 0x06, 0x06, 0x16, + 0x06, 0x27, 0x06, 0x37, 0x06, 0x48, 0x06, 0x59, 0x06, 0x6A, 0x06, 0x7B, + 0x06, 0x8C, 0x06, 0x9D, 0x06, 0xAF, 0x06, 0xC0, 0x06, 0xD1, 0x06, 0xE3, + 0x06, 0xF5, 0x07, 0x07, 0x07, 0x19, 0x07, 0x2B, 0x07, 0x3D, 0x07, 0x4F, + 0x07, 0x61, 0x07, 0x74, 0x07, 0x86, 0x07, 0x99, 0x07, 0xAC, 0x07, 0xBF, + 0x07, 0xD2, 0x07, 0xE5, 0x07, 0xF8, 0x08, 0x0B, 0x08, 0x1F, 0x08, 0x32, + 0x08, 0x46, 0x08, 0x5A, 0x08, 0x6E, 0x08, 0x82, 0x08, 0x96, 0x08, 0xAA, + 0x08, 0xBE, 0x08, 0xD2, 0x08, 0xE7, 0x08, 0xFB, 0x09, 0x10, 0x09, 0x25, + 0x09, 0x3A, 0x09, 0x4F, 0x09, 0x64, 0x09, 0x79, 0x09, 0x8F, 0x09, 0xA4, + 0x09, 0xBA, 0x09, 0xCF, 0x09, 0xE5, 0x09, 0xFB, 0x0A, 0x11, 0x0A, 0x27, + 0x0A, 0x3D, 0x0A, 0x54, 0x0A, 0x6A, 0x0A, 0x81, 0x0A, 0x98, 0x0A, 0xAE, + 0x0A, 0xC5, 0x0A, 0xDC, 0x0A, 0xF3, 0x0B, 0x0B, 0x0B, 0x22, 0x0B, 0x39, + 0x0B, 0x51, 0x0B, 0x69, 0x0B, 0x80, 0x0B, 0x98, 0x0B, 0xB0, 0x0B, 0xC8, + 0x0B, 0xE1, 0x0B, 0xF9, 0x0C, 0x12, 0x0C, 0x2A, 0x0C, 0x43, 0x0C, 0x5C, + 0x0C, 0x75, 0x0C, 0x8E, 0x0C, 0xA7, 0x0C, 0xC0, 0x0C, 0xD9, 0x0C, 0xF3, + 0x0D, 0x0D, 0x0D, 0x26, 0x0D, 0x40, 0x0D, 0x5A, 0x0D, 0x74, 0x0D, 0x8E, + 0x0D, 0xA9, 0x0D, 0xC3, 0x0D, 0xDE, 0x0D, 0xF8, 0x0E, 0x13, 0x0E, 0x2E, + 0x0E, 0x49, 0x0E, 0x64, 0x0E, 0x7F, 0x0E, 0x9B, 0x0E, 0xB6, 0x0E, 0xD2, + 0x0E, 0xEE, 0x0F, 0x09, 0x0F, 0x25, 0x0F, 0x41, 0x0F, 0x5E, 0x0F, 0x7A, + 0x0F, 0x96, 0x0F, 0xB3, 0x0F, 0xCF, 0x0F, 0xEC, 0x10, 0x09, 0x10, 0x26, + 0x10, 0x43, 0x10, 0x61, 0x10, 0x7E, 0x10, 0x9B, 0x10, 0xB9, 0x10, 0xD7, + 0x10, 0xF5, 0x11, 0x13, 0x11, 0x31, 0x11, 0x4F, 0x11, 0x6D, 0x11, 0x8C, + 0x11, 0xAA, 0x11, 0xC9, 0x11, 0xE8, 0x12, 0x07, 0x12, 0x26, 0x12, 0x45, + 0x12, 0x64, 0x12, 0x84, 0x12, 0xA3, 0x12, 0xC3, 0x12, 0xE3, 0x13, 0x03, + 0x13, 0x23, 0x13, 0x43, 0x13, 0x63, 0x13, 0x83, 0x13, 0xA4, 0x13, 0xC5, + 0x13, 0xE5, 0x14, 0x06, 0x14, 0x27, 0x14, 0x49, 0x14, 0x6A, 0x14, 0x8B, + 0x14, 0xAD, 0x14, 0xCE, 0x14, 0xF0, 0x15, 0x12, 0x15, 0x34, 0x15, 0x56, + 0x15, 0x78, 0x15, 0x9B, 0x15, 0xBD, 0x15, 0xE0, 0x16, 0x03, 0x16, 0x26, + 0x16, 0x49, 0x16, 0x6C, 0x16, 0x8F, 0x16, 0xB2, 0x16, 0xD6, 0x16, 0xFA, + 0x17, 0x1D, 0x17, 0x41, 0x17, 0x65, 0x17, 0x89, 0x17, 0xAE, 0x17, 0xD2, + 0x17, 0xF7, 0x18, 0x1B, 0x18, 0x40, 0x18, 0x65, 0x18, 0x8A, 0x18, 0xAF, + 0x18, 0xD5, 0x18, 0xFA, 0x19, 0x20, 0x19, 0x45, 0x19, 0x6B, 0x19, 0x91, + 0x19, 0xB7, 0x19, 0xDD, 0x1A, 0x04, 0x1A, 0x2A, 0x1A, 0x51, 0x1A, 0x77, + 0x1A, 0x9E, 0x1A, 0xC5, 0x1A, 0xEC, 0x1B, 0x14, 0x1B, 0x3B, 0x1B, 0x63, + 0x1B, 0x8A, 0x1B, 0xB2, 0x1B, 0xDA, 0x1C, 0x02, 0x1C, 0x2A, 0x1C, 0x52, + 0x1C, 0x7B, 0x1C, 0xA3, 0x1C, 0xCC, 0x1C, 0xF5, 0x1D, 0x1E, 0x1D, 0x47, + 0x1D, 0x70, 0x1D, 0x99, 0x1D, 0xC3, 0x1D, 0xEC, 0x1E, 0x16, 0x1E, 0x40, + 0x1E, 0x6A, 0x1E, 0x94, 0x1E, 0xBE, 0x1E, 0xE9, 0x1F, 0x13, 0x1F, 0x3E, + 0x1F, 0x69, 0x1F, 0x94, 0x1F, 0xBF, 0x1F, 0xEA, 0x20, 0x15, 0x20, 0x41, + 0x20, 0x6C, 0x20, 0x98, 0x20, 0xC4, 0x20, 0xF0, 0x21, 0x1C, 0x21, 0x48, + 0x21, 0x75, 0x21, 0xA1, 0x21, 0xCE, 0x21, 0xFB, 0x22, 0x27, 0x22, 0x55, + 0x22, 0x82, 0x22, 0xAF, 0x22, 0xDD, 0x23, 0x0A, 0x23, 0x38, 0x23, 0x66, + 0x23, 0x94, 0x23, 0xC2, 0x23, 0xF0, 0x24, 0x1F, 0x24, 0x4D, 0x24, 0x7C, + 0x24, 0xAB, 0x24, 0xDA, 0x25, 0x09, 0x25, 0x38, 0x25, 0x68, 0x25, 0x97, + 0x25, 0xC7, 0x25, 0xF7, 0x26, 0x27, 0x26, 0x57, 0x26, 0x87, 0x26, 0xB7, + 0x26, 0xE8, 0x27, 0x18, 0x27, 0x49, 0x27, 0x7A, 0x27, 0xAB, 0x27, 0xDC, + 0x28, 0x0D, 0x28, 0x3F, 0x28, 0x71, 0x28, 0xA2, 0x28, 0xD4, 0x29, 0x06, + 0x29, 0x38, 0x29, 0x6B, 0x29, 0x9D, 0x29, 0xD0, 0x2A, 0x02, 0x2A, 0x35, + 0x2A, 0x68, 0x2A, 0x9B, 0x2A, 0xCF, 0x2B, 0x02, 0x2B, 0x36, 0x2B, 0x69, + 0x2B, 0x9D, 0x2B, 0xD1, 0x2C, 0x05, 0x2C, 0x39, 0x2C, 0x6E, 0x2C, 0xA2, + 0x2C, 0xD7, 0x2D, 0x0C, 0x2D, 0x41, 0x2D, 0x76, 0x2D, 0xAB, 0x2D, 0xE1, + 0x2E, 0x16, 0x2E, 0x4C, 0x2E, 0x82, 0x2E, 0xB7, 0x2E, 0xEE, 0x2F, 0x24, + 0x2F, 0x5A, 0x2F, 0x91, 0x2F, 0xC7, 0x2F, 0xFE, 0x30, 0x35, 0x30, 0x6C, + 0x30, 0xA4, 0x30, 0xDB, 0x31, 0x12, 0x31, 0x4A, 0x31, 0x82, 0x31, 0xBA, + 0x31, 0xF2, 0x32, 0x2A, 0x32, 0x63, 0x32, 0x9B, 0x32, 0xD4, 0x33, 0x0D, + 0x33, 0x46, 0x33, 0x7F, 0x33, 0xB8, 0x33, 0xF1, 0x34, 0x2B, 0x34, 0x65, + 0x34, 0x9E, 0x34, 0xD8, 0x35, 0x13, 0x35, 0x4D, 0x35, 0x87, 0x35, 0xC2, + 0x35, 0xFD, 0x36, 0x37, 0x36, 0x72, 0x36, 0xAE, 0x36, 0xE9, 0x37, 0x24, + 0x37, 0x60, 0x37, 0x9C, 0x37, 0xD7, 0x38, 0x14, 0x38, 0x50, 0x38, 0x8C, + 0x38, 0xC8, 0x39, 0x05, 0x39, 0x42, 0x39, 0x7F, 0x39, 0xBC, 0x39, 0xF9, + 0x3A, 0x36, 0x3A, 0x74, 0x3A, 0xB2, 0x3A, 0xEF, 0x3B, 0x2D, 0x3B, 0x6B, + 0x3B, 0xAA, 0x3B, 0xE8, 0x3C, 0x27, 0x3C, 0x65, 0x3C, 0xA4, 0x3C, 0xE3, + 0x3D, 0x22, 0x3D, 0x61, 0x3D, 0xA1, 0x3D, 0xE0, 0x3E, 0x20, 0x3E, 0x60, + 0x3E, 0xA0, 0x3E, 0xE0, 0x3F, 0x21, 0x3F, 0x61, 0x3F, 0xA2, 0x3F, 0xE2, + 0x40, 0x23, 0x40, 0x64, 0x40, 0xA6, 0x40, 0xE7, 0x41, 0x29, 0x41, 0x6A, + 0x41, 0xAC, 0x41, 0xEE, 0x42, 0x30, 0x42, 0x72, 0x42, 0xB5, 0x42, 0xF7, + 0x43, 0x3A, 0x43, 0x7D, 0x43, 0xC0, 0x44, 0x03, 0x44, 0x47, 0x44, 0x8A, + 0x44, 0xCE, 0x45, 0x12, 0x45, 0x55, 0x45, 0x9A, 0x45, 0xDE, 0x46, 0x22, + 0x46, 0x67, 0x46, 0xAB, 0x46, 0xF0, 0x47, 0x35, 0x47, 0x7B, 0x47, 0xC0, + 0x48, 0x05, 0x48, 0x4B, 0x48, 0x91, 0x48, 0xD7, 0x49, 0x1D, 0x49, 0x63, + 0x49, 0xA9, 0x49, 0xF0, 0x4A, 0x37, 0x4A, 0x7D, 0x4A, 0xC4, 0x4B, 0x0C, + 0x4B, 0x53, 0x4B, 0x9A, 0x4B, 0xE2, 0x4C, 0x2A, 0x4C, 0x72, 0x4C, 0xBA, + 0x4D, 0x02, 0x4D, 0x4A, 0x4D, 0x93, 0x4D, 0xDC, 0x4E, 0x25, 0x4E, 0x6E, + 0x4E, 0xB7, 0x4F, 0x00, 0x4F, 0x49, 0x4F, 0x93, 0x4F, 0xDD, 0x50, 0x27, + 0x50, 0x71, 0x50, 0xBB, 0x51, 0x06, 0x51, 0x50, 0x51, 0x9B, 0x51, 0xE6, + 0x52, 0x31, 0x52, 0x7C, 0x52, 0xC7, 0x53, 0x13, 0x53, 0x5F, 0x53, 0xAA, + 0x53, 0xF6, 0x54, 0x42, 0x54, 0x8F, 0x54, 0xDB, 0x55, 0x28, 0x55, 0x75, + 0x55, 0xC2, 0x56, 0x0F, 0x56, 0x5C, 0x56, 0xA9, 0x56, 0xF7, 0x57, 0x44, + 0x57, 0x92, 0x57, 0xE0, 0x58, 0x2F, 0x58, 0x7D, 0x58, 0xCB, 0x59, 0x1A, + 0x59, 0x69, 0x59, 0xB8, 0x5A, 0x07, 0x5A, 0x56, 0x5A, 0xA6, 0x5A, 0xF5, + 0x5B, 0x45, 0x5B, 0x95, 0x5B, 0xE5, 0x5C, 0x35, 0x5C, 0x86, 0x5C, 0xD6, + 0x5D, 0x27, 0x5D, 0x78, 0x5D, 0xC9, 0x5E, 0x1A, 0x5E, 0x6C, 0x5E, 0xBD, + 0x5F, 0x0F, 0x5F, 0x61, 0x5F, 0xB3, 0x60, 0x05, 0x60, 0x57, 0x60, 0xAA, + 0x60, 0xFC, 0x61, 0x4F, 0x61, 0xA2, 0x61, 0xF5, 0x62, 0x49, 0x62, 0x9C, + 0x62, 0xF0, 0x63, 0x43, 0x63, 0x97, 0x63, 0xEB, 0x64, 0x40, 0x64, 0x94, + 0x64, 0xE9, 0x65, 0x3D, 0x65, 0x92, 0x65, 0xE7, 0x66, 0x3D, 0x66, 0x92, + 0x66, 0xE8, 0x67, 0x3D, 0x67, 0x93, 0x67, 0xE9, 0x68, 0x3F, 0x68, 0x96, + 0x68, 0xEC, 0x69, 0x43, 0x69, 0x9A, 0x69, 0xF1, 0x6A, 0x48, 0x6A, 0x9F, + 0x6A, 0xF7, 0x6B, 0x4F, 0x6B, 0xA7, 0x6B, 0xFF, 0x6C, 0x57, 0x6C, 0xAF, + 0x6D, 0x08, 0x6D, 0x60, 0x6D, 0xB9, 0x6E, 0x12, 0x6E, 0x6B, 0x6E, 0xC4, + 0x6F, 0x1E, 0x6F, 0x78, 0x6F, 0xD1, 0x70, 0x2B, 0x70, 0x86, 0x70, 0xE0, + 0x71, 0x3A, 0x71, 0x95, 0x71, 0xF0, 0x72, 0x4B, 0x72, 0xA6, 0x73, 0x01, + 0x73, 0x5D, 0x73, 0xB8, 0x74, 0x14, 0x74, 0x70, 0x74, 0xCC, 0x75, 0x28, + 0x75, 0x85, 0x75, 0xE1, 0x76, 0x3E, 0x76, 0x9B, 0x76, 0xF8, 0x77, 0x56, + 0x77, 0xB3, 0x78, 0x11, 0x78, 0x6E, 0x78, 0xCC, 0x79, 0x2A, 0x79, 0x89, + 0x79, 0xE7, 0x7A, 0x46, 0x7A, 0xA5, 0x7B, 0x04, 0x7B, 0x63, 0x7B, 0xC2, + 0x7C, 0x21, 0x7C, 0x81, 0x7C, 0xE1, 0x7D, 0x41, 0x7D, 0xA1, 0x7E, 0x01, + 0x7E, 0x62, 0x7E, 0xC2, 0x7F, 0x23, 0x7F, 0x84, 0x7F, 0xE5, 0x80, 0x47, + 0x80, 0xA8, 0x81, 0x0A, 0x81, 0x6B, 0x81, 0xCD, 0x82, 0x30, 0x82, 0x92, + 0x82, 0xF4, 0x83, 0x57, 0x83, 0xBA, 0x84, 0x1D, 0x84, 0x80, 0x84, 0xE3, + 0x85, 0x47, 0x85, 0xAB, 0x86, 0x0E, 0x86, 0x72, 0x86, 0xD7, 0x87, 0x3B, + 0x87, 0x9F, 0x88, 0x04, 0x88, 0x69, 0x88, 0xCE, 0x89, 0x33, 0x89, 0x99, + 0x89, 0xFE, 0x8A, 0x64, 0x8A, 0xCA, 0x8B, 0x30, 0x8B, 0x96, 0x8B, 0xFC, + 0x8C, 0x63, 0x8C, 0xCA, 0x8D, 0x31, 0x8D, 0x98, 0x8D, 0xFF, 0x8E, 0x66, + 0x8E, 0xCE, 0x8F, 0x36, 0x8F, 0x9E, 0x90, 0x06, 0x90, 0x6E, 0x90, 0xD6, + 0x91, 0x3F, 0x91, 0xA8, 0x92, 0x11, 0x92, 0x7A, 0x92, 0xE3, 0x93, 0x4D, + 0x93, 0xB6, 0x94, 0x20, 0x94, 0x8A, 0x94, 0xF4, 0x95, 0x5F, 0x95, 0xC9, + 0x96, 0x34, 0x96, 0x9F, 0x97, 0x0A, 0x97, 0x75, 0x97, 0xE0, 0x98, 0x4C, + 0x98, 0xB8, 0x99, 0x24, 0x99, 0x90, 0x99, 0xFC, 0x9A, 0x68, 0x9A, 0xD5, + 0x9B, 0x42, 0x9B, 0xAF, 0x9C, 0x1C, 0x9C, 0x89, 0x9C, 0xF7, 0x9D, 0x64, + 0x9D, 0xD2, 0x9E, 0x40, 0x9E, 0xAE, 0x9F, 0x1D, 0x9F, 0x8B, 0x9F, 0xFA, + 0xA0, 0x69, 0xA0, 0xD8, 0xA1, 0x47, 0xA1, 0xB6, 0xA2, 0x26, 0xA2, 0x96, + 0xA3, 0x06, 0xA3, 0x76, 0xA3, 0xE6, 0xA4, 0x56, 0xA4, 0xC7, 0xA5, 0x38, + 0xA5, 0xA9, 0xA6, 0x1A, 0xA6, 0x8B, 0xA6, 0xFD, 0xA7, 0x6E, 0xA7, 0xE0, + 0xA8, 0x52, 0xA8, 0xC4, 0xA9, 0x37, 0xA9, 0xA9, 0xAA, 0x1C, 0xAA, 0x8F, + 0xAB, 0x02, 0xAB, 0x75, 0xAB, 0xE9, 0xAC, 0x5C, 0xAC, 0xD0, 0xAD, 0x44, + 0xAD, 0xB8, 0xAE, 0x2D, 0xAE, 0xA1, 0xAF, 0x16, 0xAF, 0x8B, 0xB0, 0x00, + 0xB0, 0x75, 0xB0, 0xEA, 0xB1, 0x60, 0xB1, 0xD6, 0xB2, 0x4B, 0xB2, 0xC2, + 0xB3, 0x38, 0xB3, 0xAE, 0xB4, 0x25, 0xB4, 0x9C, 0xB5, 0x13, 0xB5, 0x8A, + 0xB6, 0x01, 0xB6, 0x79, 0xB6, 0xF0, 0xB7, 0x68, 0xB7, 0xE0, 0xB8, 0x59, + 0xB8, 0xD1, 0xB9, 0x4A, 0xB9, 0xC2, 0xBA, 0x3B, 0xBA, 0xB5, 0xBB, 0x2E, + 0xBB, 0xA7, 0xBC, 0x21, 0xBC, 0x9B, 0xBD, 0x15, 0xBD, 0x8F, 0xBE, 0x0A, + 0xBE, 0x84, 0xBE, 0xFF, 0xBF, 0x7A, 0xBF, 0xF5, 0xC0, 0x70, 0xC0, 0xEC, + 0xC1, 0x67, 0xC1, 0xE3, 0xC2, 0x5F, 0xC2, 0xDB, 0xC3, 0x58, 0xC3, 0xD4, + 0xC4, 0x51, 0xC4, 0xCE, 0xC5, 0x4B, 0xC5, 0xC8, 0xC6, 0x46, 0xC6, 0xC3, + 0xC7, 0x41, 0xC7, 0xBF, 0xC8, 0x3D, 0xC8, 0xBC, 0xC9, 0x3A, 0xC9, 0xB9, + 0xCA, 0x38, 0xCA, 0xB7, 0xCB, 0x36, 0xCB, 0xB6, 0xCC, 0x35, 0xCC, 0xB5, + 0xCD, 0x35, 0xCD, 0xB5, 0xCE, 0x36, 0xCE, 0xB6, 0xCF, 0x37, 0xCF, 0xB8, + 0xD0, 0x39, 0xD0, 0xBA, 0xD1, 0x3C, 0xD1, 0xBE, 0xD2, 0x3F, 0xD2, 0xC1, + 0xD3, 0x44, 0xD3, 0xC6, 0xD4, 0x49, 0xD4, 0xCB, 0xD5, 0x4E, 0xD5, 0xD1, + 0xD6, 0x55, 0xD6, 0xD8, 0xD7, 0x5C, 0xD7, 0xE0, 0xD8, 0x64, 0xD8, 0xE8, + 0xD9, 0x6C, 0xD9, 0xF1, 0xDA, 0x76, 0xDA, 0xFB, 0xDB, 0x80, 0xDC, 0x05, + 0xDC, 0x8A, 0xDD, 0x10, 0xDD, 0x96, 0xDE, 0x1C, 0xDE, 0xA2, 0xDF, 0x29, + 0xDF, 0xAF, 0xE0, 0x36, 0xE0, 0xBD, 0xE1, 0x44, 0xE1, 0xCC, 0xE2, 0x53, + 0xE2, 0xDB, 0xE3, 0x63, 0xE3, 0xEB, 0xE4, 0x73, 0xE4, 0xFC, 0xE5, 0x84, + 0xE6, 0x0D, 0xE6, 0x96, 0xE7, 0x1F, 0xE7, 0xA9, 0xE8, 0x32, 0xE8, 0xBC, + 0xE9, 0x46, 0xE9, 0xD0, 0xEA, 0x5B, 0xEA, 0xE5, 0xEB, 0x70, 0xEB, 0xFB, + 0xEC, 0x86, 0xED, 0x11, 0xED, 0x9C, 0xEE, 0x28, 0xEE, 0xB4, 0xEF, 0x40, + 0xEF, 0xCC, 0xF0, 0x58, 0xF0, 0xE5, 0xF1, 0x72, 0xF1, 0xFF, 0xF2, 0x8C, + 0xF3, 0x19, 0xF3, 0xA7, 0xF4, 0x34, 0xF4, 0xC2, 0xF5, 0x50, 0xF5, 0xDE, + 0xF6, 0x6D, 0xF6, 0xFB, 0xF7, 0x8A, 0xF8, 0x19, 0xF8, 0xA8, 0xF9, 0x38, + 0xF9, 0xC7, 0xFA, 0x57, 0xFA, 0xE7, 0xFB, 0x77, 0xFC, 0x07, 0xFC, 0x98, + 0xFD, 0x29, 0xFD, 0xBA, 0xFE, 0x4B, 0xFE, 0xDC, 0xFF, 0x6D, 0xFF, 0xFF + }; + + size = sizeof (ksRGBProfileData); + data = ksRGBProfileData; + + return true; + + } + +/*****************************************************************************/ + +const dng_color_space & dng_space_sRGB::Get () + { + + static dng_space_sRGB static_space; + + return static_space; + + } + +/*****************************************************************************/ + +dng_space_AdobeRGB::dng_space_AdobeRGB () + { + + SetMatrixToPCS (dng_matrix_3by3 (0.6097, 0.2053, 0.1492, + 0.3111, 0.6257, 0.0632, + 0.0195, 0.0609, 0.7446)); + + } + +/*****************************************************************************/ + +const dng_1d_function & dng_space_AdobeRGB::GammaFunction () const + { + + return dng_function_GammaEncode_2_2::Get (); + + } + +/*****************************************************************************/ + +bool dng_space_AdobeRGB::ICCProfile (uint32 &size, + const uint8 *&data) const + + { + + static const uint8 kAdobeRGBProfileData [] = + { + 0x00, 0x00, 0x02, 0x30, 0x41, 0x44, 0x42, 0x45, 0x02, 0x10, 0x00, 0x00, + 0x6D, 0x6E, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5A, 0x20, + 0x07, 0xCF, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x61, 0x63, 0x73, 0x70, 0x41, 0x50, 0x50, 0x4C, 0x00, 0x00, 0x00, 0x00, + 0x6E, 0x6F, 0x6E, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xD6, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x2D, 0x41, 0x44, 0x42, 0x45, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, + 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x32, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x01, 0x30, 0x00, 0x00, 0x00, 0x6B, + 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x01, 0x9C, 0x00, 0x00, 0x00, 0x14, + 0x62, 0x6B, 0x70, 0x74, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00, 0x14, + 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xC4, 0x00, 0x00, 0x00, 0x0E, + 0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xD4, 0x00, 0x00, 0x00, 0x0E, + 0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xE4, 0x00, 0x00, 0x00, 0x0E, + 0x72, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x14, + 0x67, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, 0x14, + 0x62, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x02, 0x1C, 0x00, 0x00, 0x00, 0x14, + 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6F, 0x70, 0x79, + 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x31, 0x39, 0x39, 0x39, 0x20, 0x41, + 0x64, 0x6F, 0x62, 0x65, 0x20, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x73, + 0x20, 0x49, 0x6E, 0x63, 0x6F, 0x72, 0x70, 0x6F, 0x72, 0x61, 0x74, 0x65, + 0x64, 0x00, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x11, 0x41, 0x64, 0x6F, 0x62, 0x65, 0x20, 0x52, 0x47, + 0x42, 0x20, 0x28, 0x31, 0x39, 0x39, 0x38, 0x29, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF3, 0x51, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x16, 0xCC, + 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x75, 0x72, 0x76, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x33, 0x00, 0x00, + 0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x02, 0x33, 0x00, 0x00, 0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x02, 0x33, 0x00, 0x00, 0x58, 0x59, 0x5A, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9C, 0x18, 0x00, 0x00, 0x4F, 0xA5, + 0x00, 0x00, 0x04, 0xFC, 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x34, 0x8D, 0x00, 0x00, 0xA0, 0x2C, 0x00, 0x00, 0x0F, 0x95, + 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x31, + 0x00, 0x00, 0x10, 0x2F, 0x00, 0x00, 0xBE, 0x9C + }; + + size = sizeof (kAdobeRGBProfileData); + data = kAdobeRGBProfileData; + + return true; + + } + +/*****************************************************************************/ + +const dng_color_space & dng_space_AdobeRGB::Get () + { + + static dng_space_AdobeRGB static_space; + + return static_space; + + } + +/*****************************************************************************/ + +dng_space_ColorMatch::dng_space_ColorMatch () + { + + SetMatrixToPCS (dng_matrix_3by3 (0.5094, 0.3208, 0.1339, + 0.2749, 0.6581, 0.0670, + 0.0243, 0.1087, 0.6919)); + + } + +/*****************************************************************************/ + +const dng_1d_function & dng_space_ColorMatch::GammaFunction () const + { + + return dng_function_GammaEncode_1_8::Get (); + + } + +/*****************************************************************************/ + +bool dng_space_ColorMatch::ICCProfile (uint32 &size, + const uint8 *&data) const + + { + + static const uint8 kColorMatchProfileData [] = + { + 0x00, 0x00, 0x02, 0x30, 0x41, 0x44, 0x42, 0x45, 0x02, 0x10, 0x00, 0x00, + 0x6D, 0x6E, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5A, 0x20, + 0x07, 0xCF, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x61, 0x63, 0x73, 0x70, 0x41, 0x50, 0x50, 0x4C, 0x00, 0x00, 0x00, 0x00, + 0x6E, 0x6F, 0x6E, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xD6, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x2D, 0x41, 0x44, 0x42, 0x45, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, + 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x32, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x01, 0x30, 0x00, 0x00, 0x00, 0x69, + 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x01, 0x9C, 0x00, 0x00, 0x00, 0x14, + 0x62, 0x6B, 0x70, 0x74, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00, 0x14, + 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xC4, 0x00, 0x00, 0x00, 0x0E, + 0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xD4, 0x00, 0x00, 0x00, 0x0E, + 0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xE4, 0x00, 0x00, 0x00, 0x0E, + 0x72, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x14, + 0x67, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, 0x14, + 0x62, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x02, 0x1C, 0x00, 0x00, 0x00, 0x14, + 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6F, 0x70, 0x79, + 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x31, 0x39, 0x39, 0x39, 0x20, 0x41, + 0x64, 0x6F, 0x62, 0x65, 0x20, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x73, + 0x20, 0x49, 0x6E, 0x63, 0x6F, 0x72, 0x70, 0x6F, 0x72, 0x61, 0x74, 0x65, + 0x64, 0x00, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0F, 0x43, 0x6F, 0x6C, 0x6F, 0x72, 0x4D, 0x61, 0x74, + 0x63, 0x68, 0x20, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF6, 0xDC, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x3A, + 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x75, 0x72, 0x76, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xCD, 0x00, 0x00, + 0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0xCD, 0x00, 0x00, 0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0xCD, 0x00, 0x00, 0x58, 0x59, 0x5A, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x6B, 0x00, 0x00, 0x46, 0x63, + 0x00, 0x00, 0x06, 0x36, 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x52, 0x23, 0x00, 0x00, 0xA8, 0x79, 0x00, 0x00, 0x1B, 0xD7, + 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x48, + 0x00, 0x00, 0x11, 0x25, 0x00, 0x00, 0xB1, 0x20 + }; + + size = sizeof (kColorMatchProfileData); + data = kColorMatchProfileData; + + return true; + + } + +/*****************************************************************************/ + +const dng_color_space & dng_space_ColorMatch::Get () + { + + static dng_space_ColorMatch static_space; + + return static_space; + + } + +/*****************************************************************************/ + +dng_space_ProPhoto::dng_space_ProPhoto () + { + + SetMatrixToPCS (dng_matrix_3by3 (0.7977, 0.1352, 0.0313, + 0.2880, 0.7119, 0.0001, + 0.0000, 0.0000, 0.8249)); + + } + +/*****************************************************************************/ + +const dng_1d_function & dng_space_ProPhoto::GammaFunction () const + { + + return dng_function_GammaEncode_1_8::Get (); + + } + +/*****************************************************************************/ + +bool dng_space_ProPhoto::ICCProfile (uint32 &size, + const uint8 *&data) const + + { + + static const uint8 kProPhotoProfileData [] = + { + 0x00, 0x00, 0x03, 0xAC, 0x4B, 0x43, 0x4D, 0x53, 0x02, 0x10, 0x00, 0x00, + 0x6D, 0x6E, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5A, 0x20, + 0x07, 0xCE, 0x00, 0x0C, 0x00, 0x01, 0x00, 0x12, 0x00, 0x3A, 0x00, 0x15, + 0x61, 0x63, 0x73, 0x70, 0x4D, 0x53, 0x46, 0x54, 0x00, 0x00, 0x00, 0x00, + 0x4B, 0x4F, 0x44, 0x41, 0x52, 0x4F, 0x4D, 0x4D, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xD6, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x2B, 0x4B, 0x4F, 0x44, 0x41, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, + 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x48, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x01, 0x5C, 0x00, 0x00, 0x00, 0x83, + 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x14, + 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x0E, + 0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x0E, + 0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x0E, + 0x72, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, 0x14, + 0x67, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x00, 0x14, + 0x62, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x02, 0x2C, 0x00, 0x00, 0x00, 0x14, + 0x64, 0x6D, 0x6E, 0x64, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x6E, + 0x64, 0x6D, 0x64, 0x64, 0x00, 0x00, 0x02, 0xB0, 0x00, 0x00, 0x00, 0xD1, + 0x6D, 0x6D, 0x6F, 0x64, 0x00, 0x00, 0x03, 0x84, 0x00, 0x00, 0x00, 0x28, + 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6F, 0x70, 0x79, + 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x45, 0x61, + 0x73, 0x74, 0x6D, 0x61, 0x6E, 0x20, 0x4B, 0x6F, 0x64, 0x61, 0x6B, 0x20, + 0x43, 0x6F, 0x6D, 0x70, 0x61, 0x6E, 0x79, 0x2C, 0x20, 0x31, 0x39, 0x39, + 0x39, 0x2C, 0x20, 0x61, 0x6C, 0x6C, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, + 0x73, 0x20, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x2E, 0x00, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, + 0x50, 0x72, 0x6F, 0x50, 0x68, 0x6F, 0x74, 0x6F, 0x20, 0x52, 0x47, 0x42, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0xFE, 0xFF, 0x00, + 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x50, 0x00, 0x68, 0x00, 0x6F, 0x00, + 0x74, 0x00, 0x6F, 0x00, 0x20, 0x00, 0x52, 0x00, 0x47, 0x00, 0x42, 0x00, + 0x00, 0x00, 0x00, 0x0D, 0x50, 0x72, 0x6F, 0x50, 0x68, 0x6F, 0x74, 0x6F, + 0x20, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xD6, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x2C, 0x63, 0x75, 0x72, 0x76, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xCD, 0x00, 0x00, + 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x34, + 0x00, 0x00, 0x49, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x58, 0x59, 0x5A, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x9C, 0x00, 0x00, 0xB6, 0x3E, + 0x00, 0x00, 0x00, 0x00, 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0xD3, 0x2D, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x4B, 0x4F, 0x44, 0x41, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0xFE, 0xFF, 0x00, 0x4B, 0x00, 0x4F, 0x00, 0x44, 0x00, 0x41, + 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x06, 0x4B, 0x4F, 0x44, 0x41, 0x4B, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x27, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6E, 0x63, + 0x65, 0x20, 0x4F, 0x75, 0x74, 0x70, 0x75, 0x74, 0x20, 0x4D, 0x65, 0x64, + 0x69, 0x75, 0x6D, 0x20, 0x4D, 0x65, 0x74, 0x72, 0x69, 0x63, 0x28, 0x52, + 0x4F, 0x4D, 0x4D, 0x29, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x28, 0xFE, 0xFF, 0x00, 0x52, 0x00, 0x65, 0x00, 0x66, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x65, 0x00, 0x6E, 0x00, 0x63, 0x00, 0x65, 0x00, + 0x20, 0x00, 0x4F, 0x00, 0x75, 0x00, 0x74, 0x00, 0x70, 0x00, 0x75, 0x00, + 0x74, 0x00, 0x20, 0x00, 0x4D, 0x00, 0x65, 0x00, 0x64, 0x00, 0x69, 0x00, + 0x75, 0x00, 0x6D, 0x00, 0x20, 0x00, 0x4D, 0x00, 0x65, 0x00, 0x74, 0x00, + 0x72, 0x00, 0x69, 0x00, 0x63, 0x00, 0x28, 0x00, 0x52, 0x00, 0x4F, 0x00, + 0x4D, 0x00, 0x4D, 0x00, 0x29, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x27, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6E, 0x63, 0x65, 0x20, + 0x4F, 0x75, 0x74, 0x70, 0x75, 0x74, 0x20, 0x4D, 0x65, 0x64, 0x69, 0x75, + 0x6D, 0x20, 0x4D, 0x65, 0x74, 0x72, 0x69, 0x63, 0x28, 0x52, 0x4F, 0x4D, + 0x4D, 0x29, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6D, 0x6D, 0x6F, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, + 0x00, 0x00, 0x9D, 0x03, 0x01, 0x01, 0x01, 0x01, 0xB0, 0xCF, 0x3B, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + size = sizeof (kProPhotoProfileData); + data = kProPhotoProfileData; + + return true; + + } + +/*****************************************************************************/ + +const dng_color_space & dng_space_ProPhoto::Get () + { + + static dng_space_ProPhoto static_space; + + return static_space; + + } + +/*****************************************************************************/ + +dng_space_GrayGamma18::dng_space_GrayGamma18 () + { + + SetMonochrome (); + + } + +/*****************************************************************************/ + +const dng_1d_function & dng_space_GrayGamma18::GammaFunction () const + { + + return dng_function_GammaEncode_1_8::Get (); + + } + +/*****************************************************************************/ + +bool dng_space_GrayGamma18::ICCProfile (uint32 &size, + const uint8 *&data) const + + { + + static const uint8 kGamma18ProfileData [] = + { + 0x00, 0x00, 0x01, 0x98, 0x41, 0x44, 0x42, 0x45, 0x02, 0x10, 0x00, 0x00, + 0x6D, 0x6E, 0x74, 0x72, 0x47, 0x52, 0x41, 0x59, 0x58, 0x59, 0x5A, 0x20, + 0x07, 0xCF, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x61, 0x63, 0x73, 0x70, 0x41, 0x50, 0x50, 0x4C, 0x00, 0x00, 0x00, 0x00, + 0x6E, 0x6F, 0x6E, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xD6, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x2D, 0x41, 0x44, 0x42, 0x45, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x32, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, 0x69, + 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x01, 0x60, 0x00, 0x00, 0x00, 0x14, + 0x62, 0x6B, 0x70, 0x74, 0x00, 0x00, 0x01, 0x74, 0x00, 0x00, 0x00, 0x14, + 0x6B, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x00, 0x0E, + 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6F, 0x70, 0x79, + 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x31, 0x39, 0x39, 0x39, 0x20, 0x41, + 0x64, 0x6F, 0x62, 0x65, 0x20, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x73, + 0x20, 0x49, 0x6E, 0x63, 0x6F, 0x72, 0x70, 0x6F, 0x72, 0x61, 0x74, 0x65, + 0x64, 0x00, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0F, 0x47, 0x72, 0x61, 0x79, 0x20, 0x47, 0x61, 0x6D, + 0x6D, 0x61, 0x20, 0x31, 0x2E, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF3, 0x54, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x16, 0xCF, + 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x75, 0x72, 0x76, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xCD, 0x00, 0x00 + }; + + size = sizeof (kGamma18ProfileData); + data = kGamma18ProfileData; + + return true; + + } + +/*****************************************************************************/ + +const dng_color_space & dng_space_GrayGamma18::Get () + { + + static dng_space_GrayGamma18 static_space; + + return static_space; + + } + +/*****************************************************************************/ + +dng_space_GrayGamma22::dng_space_GrayGamma22 () + { + + SetMonochrome (); + + } + +/*****************************************************************************/ + +const dng_1d_function & dng_space_GrayGamma22::GammaFunction () const + { + + return dng_function_GammaEncode_2_2::Get (); + + } + +/*****************************************************************************/ + +bool dng_space_GrayGamma22::ICCProfile (uint32 &size, + const uint8 *&data) const + + { + + static const uint8 kGamma22ProfileData [] = + { + 0x00, 0x00, 0x01, 0x98, 0x41, 0x44, 0x42, 0x45, 0x02, 0x10, 0x00, 0x00, + 0x6D, 0x6E, 0x74, 0x72, 0x47, 0x52, 0x41, 0x59, 0x58, 0x59, 0x5A, 0x20, + 0x07, 0xCF, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x61, 0x63, 0x73, 0x70, 0x41, 0x50, 0x50, 0x4C, 0x00, 0x00, 0x00, 0x00, + 0x6E, 0x6F, 0x6E, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xD6, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x2D, 0x41, 0x44, 0x42, 0x45, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x32, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, 0x69, + 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x01, 0x60, 0x00, 0x00, 0x00, 0x14, + 0x62, 0x6B, 0x70, 0x74, 0x00, 0x00, 0x01, 0x74, 0x00, 0x00, 0x00, 0x14, + 0x6B, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x00, 0x0E, + 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6F, 0x70, 0x79, + 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x31, 0x39, 0x39, 0x39, 0x20, 0x41, + 0x64, 0x6F, 0x62, 0x65, 0x20, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x73, + 0x20, 0x49, 0x6E, 0x63, 0x6F, 0x72, 0x70, 0x6F, 0x72, 0x61, 0x74, 0x65, + 0x64, 0x00, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0F, 0x47, 0x72, 0x61, 0x79, 0x20, 0x47, 0x61, 0x6D, + 0x6D, 0x61, 0x20, 0x32, 0x2E, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF3, 0x54, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x16, 0xCF, + 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x75, 0x72, 0x76, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x33, 0x00, 0x00 + }; + + size = sizeof (kGamma22ProfileData); + data = kGamma22ProfileData; + + return true; + + } + +/*****************************************************************************/ + +const dng_color_space & dng_space_GrayGamma22::Get () + { + + static dng_space_GrayGamma22 static_space; + + return static_space; + + } + +/*****************************************************************************/ + +dng_space_fakeRGB::dng_space_fakeRGB () + { + + SetMatrixToPCS (dng_matrix_3by3 (0.6097, 0.2053, 0.1492, + 0.3111, 0.6257, 0.0632, + 0.0195, 0.0609, 0.7446)); + + } + +/*****************************************************************************/ + +const dng_color_space & dng_space_fakeRGB::Get () + { + + static dng_space_fakeRGB static_space; + + return static_space; + + } + +/*****************************************************************************/ diff --git a/dng/dng_color_space.h b/dng/dng_color_space.h new file mode 100644 index 0000000..86539a9 --- /dev/null +++ b/dng/dng_color_space.h @@ -0,0 +1,346 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Standard gamma functions and color spaces used within the DNG SDK. + */ + +#ifndef __dng_color_space__ +#define __dng_color_space__ + +/*****************************************************************************/ + +#include "dng_1d_function.h" +#include "dng_classes.h" +#include "dng_matrix.h" +#include "dng_types.h" + +/*****************************************************************************/ + +/// \brief A dng_1d_function for gamma encoding in sRGB color space + +class dng_function_GammaEncode_sRGB: public dng_1d_function + { + + public: + + virtual real64 Evaluate (real64 x) const; + + virtual real64 EvaluateInverse (real64 y) const; + + static const dng_1d_function & Get (); + + }; + +/*****************************************************************************/ + +/// \brief A dng_1d_function for gamma encoding with 1.8 gamma. + +class dng_function_GammaEncode_1_8: public dng_1d_function + { + + public: + + virtual real64 Evaluate (real64 x) const; + + virtual real64 EvaluateInverse (real64 y) const; + + static const dng_1d_function & Get (); + + }; + +/*****************************************************************************/ + +/// \brief A dng_1d_function for gamma encoding with 2.2 gamma. + +class dng_function_GammaEncode_2_2: public dng_1d_function + { + + public: + + virtual real64 Evaluate (real64 x) const; + + virtual real64 EvaluateInverse (real64 y) const; + + static const dng_1d_function & Get (); + + }; + +/*****************************************************************************/ + +/// \brief An abstract color space + +class dng_color_space + { + + protected: + + dng_matrix fMatrixToPCS; + + dng_matrix fMatrixFromPCS; + + public: + + virtual ~dng_color_space (); + + /// Return a matrix which transforms source data in this color space into the + /// Profile Connection Space. + + const dng_matrix & MatrixToPCS () const + { + return fMatrixToPCS; + } + + /// Return a matrix which transforms Profile Connection Space data into this + /// color space. + + const dng_matrix & MatrixFromPCS () const + { + return fMatrixFromPCS; + } + + /// Predicate which is true if this color space is monochrome (has only a + /// single column). + + bool IsMonochrome () const + { + return fMatrixToPCS.Cols () == 1; + } + + /// Getter for the gamma function for this color space. + + virtual const dng_1d_function & GammaFunction () const; + + /// Returns true if this color space is linear. (I.e. has gamma 1.0.) + + bool IsLinear () const + { + return GammaFunction ().IsIdentity (); + } + + /// Map an input value through this color space's encoding gamma. + + real64 GammaEncode (real64 x) const + { + return GammaFunction ().Evaluate (x); + } + + /// Map an input value through this color space's decoding gamma (inverse of + /// the encoding gamma). + + real64 GammaDecode (real64 y) const + { + return GammaFunction ().EvaluateInverse (y); + } + + /// Getter for ICC profile, if this color space has one. + /// \param size Out parameter which receives size on return. + /// \param data Receives bytes of profile. + /// \retval Returns true if this color space has an ICC profile, false otherwise. + + virtual bool ICCProfile (uint32 &size, + const uint8 *&data) const; + + protected: + + dng_color_space (); + + void SetMonochrome (); + + void SetMatrixToPCS (const dng_matrix_3by3 &M); + + }; + +/*****************************************************************************/ + +/// Singleton class for sRGB color space. + +class dng_space_sRGB: public dng_color_space + { + + protected: + + dng_space_sRGB (); + + public: + + /// Returns dng_function_GammaEncode_sRGB + + virtual const dng_1d_function & GammaFunction () const; + + /// Returns sRGB IEC61966-2.1 ICC profile + + virtual bool ICCProfile (uint32 &size, + const uint8 *&data) const; + + /// Static method for getting single global instance of this color space. + + static const dng_color_space & Get (); + + }; + +/*****************************************************************************/ + +/// \brief Singleton class for AdobeRGB color space. + +class dng_space_AdobeRGB: public dng_color_space + { + + protected: + + dng_space_AdobeRGB (); + + public: + + /// Returns dng_function_GammaEncode_2_2 + + virtual const dng_1d_function & GammaFunction () const; + + /// Returns AdobeRGB (1998) ICC profile + + virtual bool ICCProfile (uint32 &size, + const uint8 *&data) const; + + /// Static method for getting single global instance of this color space. + + static const dng_color_space & Get (); + + }; + +/*****************************************************************************/ + +/// \brief Singleton class for ColorMatch color space. + +class dng_space_ColorMatch: public dng_color_space + { + + protected: + + dng_space_ColorMatch (); + + public: + + /// Returns dng_function_GammaEncode_1_8 + + virtual const dng_1d_function & GammaFunction () const; + + /// Returns ColorMatch RGB ICC profile + + virtual bool ICCProfile (uint32 &size, + const uint8 *&data) const; + + /// Static method for getting single global instance of this color space. + + static const dng_color_space & Get (); + + }; + +/*****************************************************************************/ + +/// \brief Singleton class for ProPhoto RGB color space. + +class dng_space_ProPhoto: public dng_color_space + { + + protected: + + dng_space_ProPhoto (); + + public: + + /// Returns dng_function_GammaEncode_1_8 + + virtual const dng_1d_function & GammaFunction () const; + + /// Returns ProPhoto RGB ICC profile + + virtual bool ICCProfile (uint32 &size, + const uint8 *&data) const; + + /// Static method for getting single global instance of this color space. + + static const dng_color_space & Get (); + + }; + +/*****************************************************************************/ + +/// \brief Singleton class for gamma 1.8 grayscale color space. + +class dng_space_GrayGamma18: public dng_color_space + { + + protected: + + dng_space_GrayGamma18 (); + + public: + + /// Returns dng_function_GammaEncode_1_8 + + virtual const dng_1d_function & GammaFunction () const; + + /// Returns simple grayscale gamma 1.8 ICC profile + + virtual bool ICCProfile (uint32 &size, + const uint8 *&data) const; + + /// Static method for getting single global instance of this color space. + + static const dng_color_space & Get (); + + }; + +/*****************************************************************************/ + +/// \brief Singleton class for gamma 2.2 grayscale color space. + +class dng_space_GrayGamma22: public dng_color_space + { + + protected: + + dng_space_GrayGamma22 (); + + public: + + /// Returns dng_function_GammaEncode_2_2 + + virtual const dng_1d_function & GammaFunction () const; + + /// Returns simple grayscale gamma 2.2 ICC profile + + virtual bool ICCProfile (uint32 &size, + const uint8 *&data) const; + + /// Static method for getting single global instance of this color space. + + static const dng_color_space & Get (); + + }; + +/*****************************************************************************/ + +class dng_space_fakeRGB: public dng_color_space + { + + protected: + + dng_space_fakeRGB (); + + public: + + static const dng_color_space & Get (); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_color_spec.cpp b/dng/dng_color_spec.cpp new file mode 100644 index 0000000..f63d972 --- /dev/null +++ b/dng/dng_color_spec.cpp @@ -0,0 +1,554 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_color_spec.h" + +#include "dng_assertions.h" +#include "dng_camera_profile.h" +#include "dng_exceptions.h" +#include "dng_matrix.h" +#include "dng_negative.h" +#include "dng_temperature.h" +#include "dng_utils.h" +#include "dng_xy_coord.h" + +/*****************************************************************************/ + +dng_matrix_3by3 MapWhiteMatrix (const dng_xy_coord &white1, + const dng_xy_coord &white2) + { + + // Use the linearized Bradford adaptation matrix. + + dng_matrix_3by3 Mb ( 0.8951, 0.2664, -0.1614, + -0.7502, 1.7135, 0.0367, + 0.0389, -0.0685, 1.0296); + + dng_vector_3 w1 = Mb * XYtoXYZ (white1); + dng_vector_3 w2 = Mb * XYtoXYZ (white2); + + // Negative white coordinates are kind of meaningless. + + w1 [0] = Max_real64 (w1 [0], 0.0); + w1 [1] = Max_real64 (w1 [1], 0.0); + w1 [2] = Max_real64 (w1 [2], 0.0); + + w2 [0] = Max_real64 (w2 [0], 0.0); + w2 [1] = Max_real64 (w2 [1], 0.0); + w2 [2] = Max_real64 (w2 [2], 0.0); + + // Limit scaling to something reasonable. + + dng_matrix_3by3 A; + + A [0] [0] = Pin_real64 (0.1, w1 [0] > 0.0 ? w2 [0] / w1 [0] : 10.0, 10.0); + A [1] [1] = Pin_real64 (0.1, w1 [1] > 0.0 ? w2 [1] / w1 [1] : 10.0, 10.0); + A [2] [2] = Pin_real64 (0.1, w1 [2] > 0.0 ? w2 [2] / w1 [2] : 10.0, 10.0); + + dng_matrix_3by3 B = Invert (Mb) * A * Mb; + + return B; + + } + +/******************************************************************************/ + +dng_color_spec::dng_color_spec (const dng_negative &negative, + const dng_camera_profile *profile) + + : fChannels (negative.ColorChannels ()) + + , fTemperature1 (0.0) + , fTemperature2 (0.0) + + , fColorMatrix1 () + , fColorMatrix2 () + + , fForwardMatrix1 () + , fForwardMatrix2 () + + , fReductionMatrix1 () + , fReductionMatrix2 () + + , fCameraCalibration1 () + , fCameraCalibration2 () + + , fAnalogBalance () + + , fWhiteXY () + + , fCameraWhite () + , fCameraToPCS () + + , fPCStoCamera () + + { + + if (fChannels > 1) + { + + if (!profile || !profile->IsValid (fChannels)) + { + ThrowBadFormat (); + } + + if (profile->WasStubbed ()) + { + ThrowProgramError ("Using stubbed profile"); + } + + fTemperature1 = profile->CalibrationTemperature1 (); + fTemperature2 = profile->CalibrationTemperature2 (); + + fColorMatrix1 = profile->ColorMatrix1 (); + fColorMatrix2 = profile->ColorMatrix2 (); + + fForwardMatrix1 = profile->ForwardMatrix1 (); + fForwardMatrix2 = profile->ForwardMatrix2 (); + + fReductionMatrix1 = profile->ReductionMatrix1 (); + fReductionMatrix2 = profile->ReductionMatrix2 (); + + fCameraCalibration1.SetIdentity (fChannels); + fCameraCalibration2.SetIdentity (fChannels); + + if (negative. CameraCalibrationSignature () == + profile->ProfileCalibrationSignature ()) + { + + if (negative.CameraCalibration1 ().Rows () == fChannels && + negative.CameraCalibration1 ().Cols () == fChannels) + { + + fCameraCalibration1 = negative.CameraCalibration1 (); + + } + + if (negative.CameraCalibration2 ().Rows () == fChannels && + negative.CameraCalibration2 ().Cols () == fChannels) + { + + fCameraCalibration2 = negative.CameraCalibration2 (); + + } + + } + + fAnalogBalance = dng_matrix (fChannels, fChannels); + + for (uint32 j = 0; j < fChannels; j++) + { + + fAnalogBalance [j] [j] = negative.AnalogBalance (j); + + } + + dng_camera_profile::NormalizeForwardMatrix (fForwardMatrix1); + + fColorMatrix1 = fAnalogBalance * fCameraCalibration1 * fColorMatrix1; + + if (!profile->HasColorMatrix2 () || + fTemperature1 <= 0.0 || + fTemperature2 <= 0.0 || + fTemperature1 == fTemperature2) + { + + fTemperature1 = 5000.0; + fTemperature2 = 5000.0; + + fColorMatrix2 = fColorMatrix1; + fForwardMatrix2 = fForwardMatrix1; + fReductionMatrix2 = fReductionMatrix1; + fCameraCalibration2 = fCameraCalibration1; + + } + + else + { + + dng_camera_profile::NormalizeForwardMatrix (fForwardMatrix2); + + fColorMatrix2 = fAnalogBalance * fCameraCalibration2 * fColorMatrix2; + + // Swap values if temperatures are out of order. + + if (fTemperature1 > fTemperature2) + { + + real64 temp = fTemperature1; + fTemperature1 = fTemperature2; + fTemperature2 = temp; + + dng_matrix T = fColorMatrix1; + fColorMatrix1 = fColorMatrix2; + fColorMatrix2 = T; + + T = fForwardMatrix1; + fForwardMatrix1 = fForwardMatrix2; + fForwardMatrix2 = T; + + T = fReductionMatrix1; + fReductionMatrix1 = fReductionMatrix2; + fReductionMatrix2 = T; + + T = fCameraCalibration1; + fCameraCalibration1 = fCameraCalibration2; + fCameraCalibration2 = T; + + } + + } + + } + + } + +/*****************************************************************************/ + +dng_matrix dng_color_spec::FindXYZtoCamera (const dng_xy_coord &white, + dng_matrix *forwardMatrix, + dng_matrix *reductionMatrix, + dng_matrix *cameraCalibration) + { + + // Convert to temperature/offset space. + + dng_temperature td (white); + + // Find fraction to weight the first calibration. + + real64 g; + + if (td.Temperature () <= fTemperature1) + g = 1.0; + + else if (td.Temperature () >= fTemperature2) + g = 0.0; + + else + { + + real64 invT = 1.0 / td.Temperature (); + + g = (invT - (1.0 / fTemperature2)) / + ((1.0 / fTemperature1) - (1.0 / fTemperature2)); + + } + + // Interpolate the color matrix. + + dng_matrix colorMatrix; + + if (g >= 1.0) + colorMatrix = fColorMatrix1; + + else if (g <= 0.0) + colorMatrix = fColorMatrix2; + + else + colorMatrix = (g ) * fColorMatrix1 + + (1.0 - g) * fColorMatrix2; + + // Interpolate forward matrix, if any. + + if (forwardMatrix) + { + + bool has1 = fForwardMatrix1.NotEmpty (); + bool has2 = fForwardMatrix2.NotEmpty (); + + if (has1 && has2) + { + + if (g >= 1.0) + *forwardMatrix = fForwardMatrix1; + + else if (g <= 0.0) + *forwardMatrix = fForwardMatrix2; + + else + *forwardMatrix = (g ) * fForwardMatrix1 + + (1.0 - g) * fForwardMatrix2; + + } + + else if (has1) + { + + *forwardMatrix = fForwardMatrix1; + + } + + else if (has2) + { + + *forwardMatrix = fForwardMatrix2; + + } + + else + { + + forwardMatrix->Clear (); + + } + + } + + // Interpolate reduction matrix, if any. + + if (reductionMatrix) + { + + bool has1 = fReductionMatrix1.NotEmpty (); + bool has2 = fReductionMatrix2.NotEmpty (); + + if (has1 && has2) + { + + if (g >= 1.0) + *reductionMatrix = fReductionMatrix1; + + else if (g <= 0.0) + *reductionMatrix = fReductionMatrix2; + + else + *reductionMatrix = (g ) * fReductionMatrix1 + + (1.0 - g) * fReductionMatrix2; + + } + + else if (has1) + { + + *reductionMatrix = fReductionMatrix1; + + } + + else if (has2) + { + + *reductionMatrix = fReductionMatrix2; + + } + + else + { + + reductionMatrix->Clear (); + + } + + } + + // Interpolate camera calibration matrix. + + if (cameraCalibration) + { + + if (g >= 1.0) + *cameraCalibration = fCameraCalibration1; + + else if (g <= 0.0) + *cameraCalibration = fCameraCalibration2; + + else + *cameraCalibration = (g ) * fCameraCalibration1 + + (1.0 - g) * fCameraCalibration2; + + } + + // Return the interpolated color matrix. + + return colorMatrix; + + } + +/*****************************************************************************/ + +void dng_color_spec::SetWhiteXY (const dng_xy_coord &white) + { + + fWhiteXY = white; + + // Deal with monochrome cameras. + + if (fChannels == 1) + { + + fCameraWhite.SetIdentity (1); + + fCameraToPCS = PCStoXYZ ().AsColumn (); + + return; + + } + + // Interpolate an matric values for this white point. + + dng_matrix colorMatrix; + dng_matrix forwardMatrix; + dng_matrix reductionMatrix; + dng_matrix cameraCalibration; + + colorMatrix = FindXYZtoCamera (fWhiteXY, + &forwardMatrix, + &reductionMatrix, + &cameraCalibration); + + // Find the camera white values. + + fCameraWhite = colorMatrix * XYtoXYZ (fWhiteXY); + + real64 whiteScale = 1.0 / MaxEntry (fCameraWhite); + + for (uint32 j = 0; j < fChannels; j++) + { + + // We don't support non-positive values for camera neutral values. + + fCameraWhite [j] = Pin_real64 (0.001, + whiteScale * fCameraWhite [j], + 1.0); + + } + + // Find PCS to Camera transform. Scale matrix so PCS white can just be + // reached when the first camera channel saturates + + fPCStoCamera = colorMatrix * MapWhiteMatrix (PCStoXY (), fWhiteXY); + + real64 scale = MaxEntry (fPCStoCamera * PCStoXYZ ()); + + fPCStoCamera = (1.0 / scale) * fPCStoCamera; + + // If we have a forward matrix, then just use that. + + if (forwardMatrix.NotEmpty ()) + { + + dng_matrix individualToReference = Invert (fAnalogBalance * cameraCalibration); + + dng_vector refCameraWhite = individualToReference * fCameraWhite; + + fCameraToPCS = forwardMatrix * + Invert (refCameraWhite.AsDiagonal ()) * + individualToReference; + + } + + // Else we need to use the adapt in XYZ method. + + else + { + + // Invert this PCS to camera matrix. Note that if there are more than three + // camera channels, this inversion is non-unique. + + fCameraToPCS = Invert (fPCStoCamera, reductionMatrix); + + } + + } + +/*****************************************************************************/ + +const dng_xy_coord & dng_color_spec::WhiteXY () const + { + + DNG_ASSERT (fWhiteXY.IsValid (), "Using invalid WhiteXY"); + + return fWhiteXY; + + } + +/*****************************************************************************/ + +const dng_vector & dng_color_spec::CameraWhite () const + { + + DNG_ASSERT (fCameraWhite.NotEmpty (), "Using invalid CameraWhite"); + + return fCameraWhite; + + } + +/*****************************************************************************/ + +const dng_matrix & dng_color_spec::CameraToPCS () const + { + + DNG_ASSERT (fCameraToPCS.NotEmpty (), "Using invalid CameraToPCS"); + + return fCameraToPCS; + + } + +/*****************************************************************************/ + +const dng_matrix & dng_color_spec::PCStoCamera () const + { + + DNG_ASSERT (fPCStoCamera.NotEmpty (), "Using invalid PCStoCamera"); + + return fPCStoCamera; + + } + +/*****************************************************************************/ + +dng_xy_coord dng_color_spec::NeutralToXY (const dng_vector &neutral) + { + + const uint32 kMaxPasses = 30; + + if (fChannels == 1) + { + + return PCStoXY (); + + } + + dng_xy_coord last = D50_xy_coord (); + + for (uint32 pass = 0; pass < kMaxPasses; pass++) + { + + dng_matrix xyzToCamera = FindXYZtoCamera (last); + + dng_xy_coord next = XYZtoXY (Invert (xyzToCamera) * neutral); + + if (Abs_real64 (next.x - last.x) + + Abs_real64 (next.y - last.y) < 0.0000001) + { + + return next; + + } + + // If we reach the limit without converging, we are most likely + // in a two value oscillation. So take the average of the last + // two estimates and give up. + + if (pass == kMaxPasses - 1) + { + + next.x = (last.x + next.x) * 0.5; + next.y = (last.y + next.y) * 0.5; + + } + + last = next; + + } + + return last; + + } + +/*****************************************************************************/ diff --git a/dng/dng_color_spec.h b/dng/dng_color_spec.h new file mode 100644 index 0000000..5eb3a81 --- /dev/null +++ b/dng/dng_color_spec.h @@ -0,0 +1,141 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Class for holding a specific color transform. +*/ + +#ifndef __dng_color_spec__ +#define __dng_color_spec__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_matrix.h" +#include "dng_types.h" +#include "dng_xy_coord.h" + +/*****************************************************************************/ + +/// \brief Compute a 3x3 matrix which maps colors from white point white1 to +/// white point white2 +/// +/// Uses linearized Bradford adaptation matrix to compute a mapping from +/// colors measured with one white point (white1) to another (white2). + +dng_matrix_3by3 MapWhiteMatrix (const dng_xy_coord &white1, + const dng_xy_coord &white2); + +/*****************************************************************************/ + +/// Color transform taking into account white point and camera calibration and +/// individual calibration from DNG negative. + +class dng_color_spec + { + + private: + + uint32 fChannels; + + real64 fTemperature1; + real64 fTemperature2; + + dng_matrix fColorMatrix1; + dng_matrix fColorMatrix2; + + dng_matrix fForwardMatrix1; + dng_matrix fForwardMatrix2; + + dng_matrix fReductionMatrix1; + dng_matrix fReductionMatrix2; + + dng_matrix fCameraCalibration1; + dng_matrix fCameraCalibration2; + + dng_matrix fAnalogBalance; + + dng_xy_coord fWhiteXY; + + dng_vector fCameraWhite; + dng_matrix fCameraToPCS; + + dng_matrix fPCStoCamera; + + public: + + /// Read calibration info from DNG negative and construct a + /// dng_color_spec. + + dng_color_spec (const dng_negative &negative, + const dng_camera_profile *profile); + + virtual ~dng_color_spec () + { + } + + /// Number of channels used for this color transform. Three + /// for most cameras. + + uint32 Channels () const + { + return fChannels; + } + + /// Setter for white point. Value is as XY colorspace coordinate. + /// \param white White point to set as an XY value. + + void SetWhiteXY (const dng_xy_coord &white); + + /// Getter for white point. Value is as XY colorspace coordinate. + /// \retval XY value of white point. + + const dng_xy_coord & WhiteXY () const; + + /// Return white point in camera native color coordinates. + /// \retval A dng_vector with components ranging from 0.0 to 1.0 + /// that is normalized such that one component is equal to 1.0 . + + const dng_vector & CameraWhite () const; + + /// Getter for camera to Profile Connection Space color transform. + /// \retval A transform that takes into account all camera calibration + /// transforms and white point. + + const dng_matrix & CameraToPCS () const; + + /// Getter for Profile Connection Space to camera color transform. + /// \retval A transform that takes into account all camera calibration + /// transforms and white point. + + const dng_matrix & PCStoCamera () const; + + /// Return the XY value to use for SetWhiteXY for a given camera color + /// space coordinate as the white point. + /// \param neutral A camera color space value to use for white point. + /// Components range from 0.0 to 1.0 and should be normalized such that + /// the largest value is 1.0 . + /// \retval White point in XY space that makes neutral map to this + /// XY value as closely as possible. + + dng_xy_coord NeutralToXY (const dng_vector &neutral); + + private: + + dng_matrix FindXYZtoCamera (const dng_xy_coord &white, + dng_matrix *forwardMatrix = NULL, + dng_matrix *reductionMatrix = NULL, + dng_matrix *cameraCalibration = NULL); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_date_time.cpp b/dng/dng_date_time.cpp new file mode 100644 index 0000000..961723e --- /dev/null +++ b/dng/dng_date_time.cpp @@ -0,0 +1,1050 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_date_time.h" + +#include "dng_exceptions.h" +#include "dng_globals.h" +#include "dng_mutex.h" +#include "dng_stream.h" +#include "dng_string.h" +#include "dng_utils.h" + +#include + +#if qMacOS +#include +#endif + +#if qWinOS +#include +#endif + +/******************************************************************************/ + +dng_date_time::dng_date_time () + + : fYear (0) + , fMonth (0) + , fDay (0) + , fHour (0) + , fMinute (0) + , fSecond (0) + + { + + } + +/******************************************************************************/ + +dng_date_time::dng_date_time (uint32 year, + uint32 month, + uint32 day, + uint32 hour, + uint32 minute, + uint32 second) + + : fYear (year) + , fMonth (month) + , fDay (day) + , fHour (hour) + , fMinute (minute) + , fSecond (second) + + { + + } + +/******************************************************************************/ + +bool dng_date_time::IsValid () const + { + + return fYear >= 1 && fYear <= 9999 && + fMonth >= 1 && fMonth <= 12 && + fDay >= 1 && fDay <= 31 && + fHour <= 23 && + fMinute <= 59 && + fSecond <= 59; + + } + +/*****************************************************************************/ + +void dng_date_time::Clear () + { + + *this = dng_date_time (); + + } + +/*****************************************************************************/ + +static uint32 DateTimeParseU32 (const char *&s) + { + + uint32 x = 0; + + while (*s == ' ' || *s == ':') + s++; + + while (*s >= '0' && *s <= '9') + { + x = x * 10 + (uint32) (*(s++) - '0'); + } + + return x; + + } + +/*****************************************************************************/ + +bool dng_date_time::Parse (const char *s) + { + + fYear = DateTimeParseU32 (s); + fMonth = DateTimeParseU32 (s); + fDay = DateTimeParseU32 (s); + fHour = DateTimeParseU32 (s); + fMinute = DateTimeParseU32 (s); + fSecond = DateTimeParseU32 (s); + + return IsValid (); + + } + +/*****************************************************************************/ + +dng_string dng_time_zone::Encode_ISO_8601 () const + { + + dng_string result; + + if (IsValid ()) + { + + if (OffsetMinutes () == 0) + { + + result.Set ("Z"); + + } + + else + { + + char s [64]; + + int offset = OffsetMinutes (); + + if (offset > 0) + { + + sprintf (s, "+%02d:%02d", offset / 60, offset % 60); + + } + + else + { + + offset = -offset; + + sprintf (s, "-%02d:%02d", offset / 60, offset % 60); + + } + + result.Set (s); + + } + + } + + return result; + + } + +/*****************************************************************************/ + +dng_date_time_info::dng_date_time_info () + + : fDateOnly (true) + , fDateTime () + , fSubseconds () + , fTimeZone () + + { + + } + +/*****************************************************************************/ + +bool dng_date_time_info::IsValid () const + { + + return fDateTime.IsValid (); + + } + +/*****************************************************************************/ + +void dng_date_time_info::SetDate (uint32 year, + uint32 month, + uint32 day) + { + + fDateTime.fYear = year; + fDateTime.fMonth = month; + fDateTime.fDay = day; + + } + +/*****************************************************************************/ + +void dng_date_time_info::SetTime (uint32 hour, + uint32 minute, + uint32 second) + { + + fDateOnly = false; + + fDateTime.fHour = hour; + fDateTime.fMinute = minute; + fDateTime.fSecond = second; + + } + +/*****************************************************************************/ + +void dng_date_time_info::SetOffsetTime (const dng_string &s) + { + + // Initialize zone to invalid. + + dng_time_zone zone; + + SetZone (zone); + + // Parse EXIF OffsetTime format. + + if (s.Length () == 6 && + (s.Get () [0] == '+' || s.Get () [0] == '-') && + (s.Get () [1] >= '0' && s.Get () [1] <= '1') && + (s.Get () [2] >= '0' && s.Get () [2] <= '9') && + (s.Get () [3] == ':') && + (s.Get () [4] >= '0' && s.Get () [4] <= '5') && + (s.Get () [5] >= '0' && s.Get () [5] <= '9')) + { + + int32 hours = (s.Get () [1] - '0') * 10 + + (s.Get () [2] - '0'); + + int32 minutes = (s.Get () [4] - '0') * 10 + + (s.Get () [5] - '0'); + + int32 offset = hours * 60 + minutes; + + if (s.Get () [0] == '-') + { + offset = -offset; + } + + zone.SetOffsetMinutes (offset); + + if (zone.IsValid ()) + { + + SetZone (zone); + + } + + } + + } + +/*****************************************************************************/ + +dng_string dng_date_time_info::OffsetTime () const + { + + dng_string result; + + if (TimeZone ().IsValid ()) + { + + int32 offset = TimeZone ().OffsetMinutes (); + + char s [7]; + + s [0] = (offset >= 0) ? '+' : '-'; + + offset = Abs_int32 (offset); + + uint32 hours = offset / 60; + uint32 minutes = offset % 60; + + s [1] = (hours / 10) + '0'; + s [2] = (hours % 10) + '0'; + + s [3] = ':'; + + s [4] = (minutes / 10) + '0'; + s [5] = (minutes % 10) + '0'; + + result.Set (s); + + } + + else + { + + result.Set (" : "); + + } + + return result; + + } + +/*****************************************************************************/ + +void dng_date_time_info::Decode_ISO_8601 (const char *s) + { + + Clear (); + + uint32 len = (uint32) strlen (s); + + if (!len) + { + return; + } + + unsigned year = 0; + unsigned month = 0; + unsigned day = 0; + + if (sscanf (s, + "%u-%u-%u", + &year, + &month, + &day) != 3) + { + return; + } + + SetDate ((uint32) year, + (uint32) month, + (uint32) day); + + if (fDateTime.NotValid ()) + { + Clear (); + return; + } + + for (uint32 j = 0; j < len; j++) + { + + if (s [j] == 'T') + { + + unsigned hour = 0; + unsigned minute = 0; + unsigned second = 0; + + int items = sscanf (s + j + 1, + "%u:%u:%u", + &hour, + &minute, + &second); + + if (items >= 2 && items <= 3) + { + + SetTime ((uint32) hour, + (uint32) minute, + (uint32) second); + + if (fDateTime.NotValid ()) + { + Clear (); + return; + } + + if (items == 3) + { + + for (uint32 k = j + 1; k < len; k++) + { + + if (s [k] == '.') + { + + while (++k < len && s [k] >= '0' && s [k] <= '9') + { + + char ss [2]; + + ss [0] = s [k]; + ss [1] = 0; + + fSubseconds.Append (ss); + + } + + break; + + } + + } + + } + + for (uint32 k = j + 1; k < len; k++) + { + + if (s [k] == 'Z') + { + + fTimeZone.SetOffsetMinutes (0); + + break; + + } + + if (s [k] == '+' || s [k] == '-') + { + + int32 sign = (s [k] == '-' ? -1 : 1); + + unsigned tzhour = 0; + unsigned tzmin = 0; + + if (sscanf (s + k + 1, + "%u:%u", + &tzhour, + &tzmin) > 0) + { + + fTimeZone.SetOffsetMinutes (sign * (tzhour * 60 + tzmin)); + + } + + break; + + } + + } + + } + + break; + + } + + } + + } + +/*****************************************************************************/ + +dng_string dng_date_time_info::Encode_ISO_8601 () const + { + + dng_string result; + + if (IsValid ()) + { + + char s [256]; + + sprintf (s, + "%04u-%02u-%02u", + (unsigned) fDateTime.fYear, + (unsigned) fDateTime.fMonth, + (unsigned) fDateTime.fDay); + + result.Set (s); + + if (!fDateOnly) + { + + sprintf (s, + "T%02u:%02u:%02u", + (unsigned) fDateTime.fHour, + (unsigned) fDateTime.fMinute, + (unsigned) fDateTime.fSecond); + + result.Append (s); + + if (fSubseconds.NotEmpty ()) + { + + bool subsecondsValid = true; + + uint32 len = fSubseconds.Length (); + + for (uint32 index = 0; index < len; index++) + { + + if (fSubseconds.Get () [index] < '0' || + fSubseconds.Get () [index] > '9') + { + subsecondsValid = false; + break; + } + + } + + if (subsecondsValid) + { + result.Append ("."); + result.Append (fSubseconds.Get ()); + } + + } + + if (gDNGUseFakeTimeZonesInXMP) + { + + // Kludge: Early versions of the XMP toolkit assume Zulu time + // if the time zone is missing. It is safer for fill in the + // local time zone. + + dng_time_zone tempZone = fTimeZone; + + if (tempZone.NotValid ()) + { + tempZone = LocalTimeZone (fDateTime); + } + + result.Append (tempZone.Encode_ISO_8601 ().Get ()); + + } + + else + { + + // MWG: Now we don't fill in the local time zone. So only + // add the time zone if it is known and valid. + + if (fTimeZone.IsValid ()) + { + result.Append (fTimeZone.Encode_ISO_8601 ().Get ()); + } + + } + + } + + } + + return result; + + } + +/*****************************************************************************/ + +void dng_date_time_info::Decode_IPTC_Date (const char *s) + { + + if (strlen (s) == 8) + { + + unsigned year = 0; + unsigned month = 0; + unsigned day = 0; + + if (sscanf (s, + "%4u%2u%2u", + &year, + &month, + &day) == 3) + { + + SetDate ((uint32) year, + (uint32) month, + (uint32) day); + + } + + } + + } + +/*****************************************************************************/ + +dng_string dng_date_time_info::Encode_IPTC_Date () const + { + + dng_string result; + + if (IsValid ()) + { + + char s [64]; + + sprintf (s, + "%04u%02u%02u", + (unsigned) fDateTime.fYear, + (unsigned) fDateTime.fMonth, + (unsigned) fDateTime.fDay); + + result.Set (s); + + } + + return result; + + } + +/*****************************************************************************/ + +void dng_date_time_info::Decode_IPTC_Time (const char *s) + { + + if (strlen (s) == 11) + { + + char time [12]; + + memcpy (time, s, sizeof (time)); + + if (time [6] == '+' || + time [6] == '-') + { + + int tzsign = (time [6] == '-') ? -1 : 1; + + time [6] = 0; + + unsigned hour = 0; + unsigned minute = 0; + unsigned second = 0; + unsigned tzhour = 0; + unsigned tzmin = 0; + + if (sscanf (time, + "%2u%2u%2u", + &hour, + &minute, + &second) == 3 && + sscanf (time + 7, + "%2u%2u", + &tzhour, + &tzmin) == 2) + { + + dng_time_zone zone; + + zone.SetOffsetMinutes (tzsign * (tzhour * 60 + tzmin)); + + if (zone.IsValid ()) + { + + SetTime ((uint32) hour, + (uint32) minute, + (uint32) second); + + SetZone (zone); + + } + + } + + } + + } + + else if (strlen (s) == 6) + { + + unsigned hour = 0; + unsigned minute = 0; + unsigned second = 0; + + if (sscanf (s, + "%2u%2u%2u", + &hour, + &minute, + &second) == 3) + { + + SetTime ((uint32) hour, + (uint32) minute, + (uint32) second); + + } + + } + + else if (strlen (s) == 4) + { + + unsigned hour = 0; + unsigned minute = 0; + + if (sscanf (s, + "%2u%2u", + &hour, + &minute) == 2) + { + + SetTime ((uint32) hour, + (uint32) minute, + 0); + + } + + } + + } + +/*****************************************************************************/ + +dng_string dng_date_time_info::Encode_IPTC_Time () const + { + + dng_string result; + + if (IsValid () && !fDateOnly) + { + + char s [64]; + + if (fTimeZone.IsValid ()) + { + + sprintf (s, + "%02u%02u%02u%c%02u%02u", + (unsigned) fDateTime.fHour, + (unsigned) fDateTime.fMinute, + (unsigned) fDateTime.fSecond, + (int) (fTimeZone.OffsetMinutes () >= 0 ? '+' : '-'), + (unsigned) (Abs_int32 (fTimeZone.OffsetMinutes ()) / 60), + (unsigned) (Abs_int32 (fTimeZone.OffsetMinutes ()) % 60)); + + } + + else + { + + sprintf (s, + "%02u%02u%02u", + (unsigned) fDateTime.fHour, + (unsigned) fDateTime.fMinute, + (unsigned) fDateTime.fSecond); + + } + + result.Set (s); + + } + + return result; + + } + +/*****************************************************************************/ + +static dng_std_mutex gDateTimeMutex; + +/*****************************************************************************/ + +void CurrentDateTimeAndZone (dng_date_time_info &info) + { + + time_t sec; + + time (&sec); + + tm t; + tm zt; + + { + + dng_lock_std_mutex lock (gDateTimeMutex); + + t = *localtime (&sec); + zt = *gmtime (&sec); + + } + + dng_date_time dt; + + dt.fYear = t.tm_year + 1900; + dt.fMonth = t.tm_mon + 1; + dt.fDay = t.tm_mday; + dt.fHour = t.tm_hour; + dt.fMinute = t.tm_min; + dt.fSecond = t.tm_sec; + + info.SetDateTime (dt); + + int tzHour = t.tm_hour - zt.tm_hour; + int tzMin = t.tm_min - zt.tm_min; + + bool zonePositive = (t.tm_year > zt.tm_year) || + (t.tm_year == zt.tm_year && t.tm_yday > zt.tm_yday) || + (t.tm_year == zt.tm_year && t.tm_yday == zt.tm_yday && tzHour > 0) || + (t.tm_year == zt.tm_year && t.tm_yday == zt.tm_yday && tzHour == 0 && tzMin >= 0); + + tzMin += tzHour * 60; + + if (zonePositive) + { + + while (tzMin < 0) + tzMin += 24 * 60; + + } + + else + { + + while (tzMin > 0) + tzMin -= 24 * 60; + + } + + dng_time_zone zone; + + zone.SetOffsetMinutes (tzMin); + + info.SetZone (zone); + + } + +/*****************************************************************************/ + +void DecodeUnixTime (uint32 unixTime, dng_date_time &dt) + { + + time_t sec = (time_t) unixTime; + + tm t; + + { + + dng_lock_std_mutex lock (gDateTimeMutex); + + #if qMacOS && !defined(__MACH__) + + // Macintosh CFM stores time in local time zone. + + tm *tp = localtime (&sec); + + #else + + // Macintosh Mach-O and Windows stores time in Zulu time. + + tm *tp = gmtime (&sec); + + #endif + + if (!tp) + { + dt.Clear (); + return; + } + + t = *tp; + + } + + dt.fYear = t.tm_year + 1900; + dt.fMonth = t.tm_mon + 1; + dt.fDay = t.tm_mday; + dt.fHour = t.tm_hour; + dt.fMinute = t.tm_min; + dt.fSecond = t.tm_sec; + + } + +/*****************************************************************************/ + +dng_time_zone LocalTimeZone (const dng_date_time &dt) + { + + dng_time_zone result; + + if (dt.IsValid ()) + { + + #if qMacOS + + CFTimeZoneRef zoneRef = CFTimeZoneCopyDefault (); + + CFReleaseHelper zoneRefDeleter (zoneRef); + + if (zoneRef) + { + + // New path that doesn't use deprecated CFGregorian-based APIs. + + CFCalendarRef calendar = + CFCalendarCreateWithIdentifier (kCFAllocatorDefault, + kCFGregorianCalendar); + + CFReleaseHelper calendarDeleter (calendar); + + CFAbsoluteTime absTime; + + if (CFCalendarComposeAbsoluteTime (calendar, + &absTime, + "yMdHms", + dt.fYear, + dt.fMonth, + dt.fDay, + dt.fHour, + dt.fMinute, + dt.fSecond)) + { + + CFTimeInterval secondsDelta = CFTimeZoneGetSecondsFromGMT (zoneRef, absTime); + + result.SetOffsetSeconds (Round_int32 (secondsDelta)); + + if (result.IsValid ()) + { + return result; + } + + } + + } + + #endif + + #if qWinOS + + if (GetTimeZoneInformation != NULL && + SystemTimeToTzSpecificLocalTime != NULL && + SystemTimeToFileTime != NULL) + { + + TIME_ZONE_INFORMATION tzInfo; + + DWORD x = GetTimeZoneInformation (&tzInfo); + + SYSTEMTIME localST; + + memset (&localST, 0, sizeof (localST)); + + localST.wYear = (WORD) dt.fYear; + localST.wMonth = (WORD) dt.fMonth; + localST.wDay = (WORD) dt.fDay; + localST.wHour = (WORD) dt.fHour; + localST.wMinute = (WORD) dt.fMinute; + localST.wSecond = (WORD) dt.fSecond; + + SYSTEMTIME utcST; + + if (TzSpecificLocalTimeToSystemTime (&tzInfo, &localST, &utcST)) + { + + FILETIME localFT; + FILETIME utcFT; + + (void) SystemTimeToFileTime (&localST, &localFT); + (void) SystemTimeToFileTime (&utcST , &utcFT ); + + uint64 time1 = (((uint64) localFT.dwHighDateTime) << 32) + localFT.dwLowDateTime; + uint64 time2 = (((uint64) utcFT .dwHighDateTime) << 32) + utcFT .dwLowDateTime; + + // FILETIMEs are in units to 100 ns. Convert to seconds. + + int64 time1Sec = time1 / 10000000; + int64 time2Sec = time2 / 10000000; + + int32 delta = (int32) (time1Sec - time2Sec); + + result.SetOffsetSeconds (delta); + + if (result.IsValid ()) + { + return result; + } + + } + + } + + #endif + + } + + // Figure out local time zone. + + dng_date_time_info current_info; + + CurrentDateTimeAndZone (current_info); + + result = current_info.TimeZone (); + + return result; + + } + +/*****************************************************************************/ + +dng_date_time_storage_info::dng_date_time_storage_info () + + : fOffset (kDNGStreamInvalidOffset ) + , fFormat (dng_date_time_format_unknown) + + { + + } + +/*****************************************************************************/ + +dng_date_time_storage_info::dng_date_time_storage_info (uint64 offset, + dng_date_time_format format) + + : fOffset (offset) + , fFormat (format) + + { + + } + +/*****************************************************************************/ + +bool dng_date_time_storage_info::IsValid () const + { + + return fOffset != kDNGStreamInvalidOffset; + + } + +/*****************************************************************************/ + +uint64 dng_date_time_storage_info::Offset () const + { + + if (!IsValid ()) + ThrowProgramError (); + + return fOffset; + + } + +/*****************************************************************************/ + +dng_date_time_format dng_date_time_storage_info::Format () const + { + + if (!IsValid ()) + ThrowProgramError (); + + return fFormat; + + } + +/*****************************************************************************/ diff --git a/dng/dng_date_time.h b/dng/dng_date_time.h new file mode 100644 index 0000000..feaa641 --- /dev/null +++ b/dng/dng_date_time.h @@ -0,0 +1,388 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Functions and classes for working with dates and times in DNG files. + */ + +/*****************************************************************************/ + +#ifndef __dng_date_time__ +#define __dng_date_time__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_string.h" +#include "dng_types.h" + +/*****************************************************************************/ + +/// \brief Class for holding a date/time and converting to and from relevant +/// date/time formats + +class dng_date_time + { + + public: + + uint32 fYear; + uint32 fMonth; + uint32 fDay; + uint32 fHour; + uint32 fMinute; + uint32 fSecond; + + public: + + /// Construct an invalid date/time + + dng_date_time (); + + /// Construct a date/time with specific values. + /// \param year Year to use as actual integer value, such as 2006. + /// \param month Month to use from 1 - 12, where 1 is January. + /// \param day Day of month to use from 1 -31, where 1 is the first. + /// \param hour Hour of day to use from 0 - 23, where 0 is midnight. + /// \param minute Minute of hour to use from 0 - 59. + /// \param second Second of minute to use from 0 - 59. + + dng_date_time (uint32 year, + uint32 month, + uint32 day, + uint32 hour, + uint32 minute, + uint32 second); + + /// Predicate to determine if a date is valid. + /// \retval true if all fields are within range. + + bool IsValid () const; + + /// Predicate to determine if a date is invalid. + /// \retval true if any field is out of range. + + bool NotValid () const + { + return !IsValid (); + } + + /// Equal operator. + + bool operator== (const dng_date_time &dt) const + { + return fYear == dt.fYear && + fMonth == dt.fMonth && + fDay == dt.fDay && + fHour == dt.fHour && + fMinute == dt.fMinute && + fSecond == dt.fSecond; + } + + // Not-equal operator. + + bool operator!= (const dng_date_time &dt) const + { + return !(*this == dt); + } + + /// Set date to an invalid value. + + void Clear (); + + /// Parse an EXIF format date string. + /// \param s Input date string to parse. + /// \retval true if date was parsed successfully and date is valid. + + bool Parse (const char *s); + + }; + +/*****************************************************************************/ + +/// \brief Class for holding a time zone. + +class dng_time_zone + { + + private: + + enum + { + + kMaxOffsetHours = 15, + kMinOffsetHours = -kMaxOffsetHours, + + kMaxOffsetMinutes = kMaxOffsetHours * 60, + kMinOffsetMinutes = kMinOffsetHours * 60, + + kInvalidOffset = kMinOffsetMinutes - 1 + + }; + + // Offset from GMT in minutes. Positive numbers are + // ahead of GMT, negative number are behind GMT. + + int32 fOffsetMinutes; + + public: + + dng_time_zone () + : fOffsetMinutes (kInvalidOffset) + { + } + + void Clear () + { + fOffsetMinutes = kInvalidOffset; + } + + void SetOffsetHours (int32 offset) + { + fOffsetMinutes = offset * 60; + } + + void SetOffsetMinutes (int32 offset) + { + fOffsetMinutes = offset; + } + + void SetOffsetSeconds (int32 offset) + { + fOffsetMinutes = (offset > 0) ? ((offset + 30) / 60) + : ((offset - 30) / 60); + } + + bool IsValid () const + { + return fOffsetMinutes >= kMinOffsetMinutes && + fOffsetMinutes <= kMaxOffsetMinutes; + } + + bool NotValid () const + { + return !IsValid (); + } + + int32 OffsetMinutes () const + { + return fOffsetMinutes; + } + + bool IsExactHourOffset () const + { + return IsValid () && ((fOffsetMinutes % 60) == 0); + } + + int32 ExactHourOffset () const + { + return fOffsetMinutes / 60; + } + + dng_string Encode_ISO_8601 () const; + + }; + +/*****************************************************************************/ + +/// \brief Class for holding complete data/time/zone information. + +class dng_date_time_info + { + + private: + + // Is only the date valid and not the time? + + bool fDateOnly; + + // Date and time. + + dng_date_time fDateTime; + + // Subseconds string (stored in a separate tag in EXIF). + + dng_string fSubseconds; + + // Time zone, if known. + + dng_time_zone fTimeZone; + + public: + + dng_date_time_info (); + + bool IsValid () const; + + bool NotValid () const + { + return !IsValid (); + } + + void Clear () + { + *this = dng_date_time_info (); + } + + bool IsDateOnly () const + { + return fDateOnly; + } + + const dng_date_time & DateTime () const + { + return fDateTime; + } + + void SetDateTime (const dng_date_time &dt) + { + fDateOnly = false; + fDateTime = dt; + } + + const dng_string & Subseconds () const + { + return fSubseconds; + } + + void SetSubseconds (const dng_string &s) + { + fSubseconds = s; + } + + const dng_time_zone & TimeZone () const + { + return fTimeZone; + } + + void SetZone (const dng_time_zone &zone) + { + fTimeZone = zone; + } + + void ClearZone () + { + fTimeZone.Clear (); + } + + void SetOffsetTime (const dng_string &s); + + dng_string OffsetTime () const; + + void Decode_ISO_8601 (const char *s); + + dng_string Encode_ISO_8601 () const; + + void Decode_IPTC_Date (const char *s); + + dng_string Encode_IPTC_Date () const; + + void Decode_IPTC_Time (const char *s); + + dng_string Encode_IPTC_Time () const; + + private: + + void SetDate (uint32 year, + uint32 month, + uint32 day); + + void SetTime (uint32 hour, + uint32 minute, + uint32 second); + + }; + +/*****************************************************************************/ + +/// Get the current date/time and timezone. +/// \param info Receives current data/time/zone. + +void CurrentDateTimeAndZone (dng_date_time_info &info); + +/*****************************************************************************/ + +/// Convert UNIX "seconds since Jan 1, 1970" time to a dng_date_time + +void DecodeUnixTime (uint32 unixTime, dng_date_time &dt); + +/*****************************************************************************/ + +/// Return timezone of current location at a given date. +/// \param dt Date at which to compute timezone difference. (For example, used +/// to determine Daylight Savings, etc.) +/// \retval Time zone for date/time dt. + +dng_time_zone LocalTimeZone (const dng_date_time &dt); + +/*****************************************************************************/ + +/// Tag to encode date represenation format + +enum dng_date_time_format + { + dng_date_time_format_unknown = 0, /// Date format not known + dng_date_time_format_exif = 1, /// EXIF date string + dng_date_time_format_unix_little_endian = 2, /// 32-bit UNIX time as 4-byte little endian + dng_date_time_format_unix_big_endian = 3 /// 32-bit UNIX time as 4-byte big endian + }; + +/*****************************************************************************/ + +/// \brief Store file offset from which date was read. +/// +/// Used internally by Adobe to update date in original file. +/// \warning Use at your own risk. + +class dng_date_time_storage_info + { + + private: + + uint64 fOffset; + + dng_date_time_format fFormat; + + public: + + /// The default constructor initializes to an invalid state. + + dng_date_time_storage_info (); + + /// Construct with file offset and date format. + + dng_date_time_storage_info (uint64 offset, + dng_date_time_format format); + + /// Predicate to determine if an offset is valid. + /// \retval true if offset is valid. + + bool IsValid () const; + + // The accessors throw if the data is not valid. + + /// Getter for offset in file. + /// \exception dng_exception with fErrorCode equal to dng_error_unknown + /// if offset is not valid. + + uint64 Offset () const; + + /// Get for format date was originally stored in file. Throws a + /// dng_error_unknown exception if offset is invalid. + /// \exception dng_exception with fErrorCode equal to dng_error_unknown + /// if offset is not valid. + + dng_date_time_format Format () const; + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_errors.h b/dng/dng_errors.h new file mode 100644 index 0000000..c8fbd97 --- /dev/null +++ b/dng/dng_errors.h @@ -0,0 +1,54 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Error code values. + */ + +/*****************************************************************************/ + +#ifndef __dng_errors__ +#define __dng_errors__ + +/*****************************************************************************/ + +#include "dng_types.h" + +/*****************************************************************************/ + +/// Type for all errors used in DNG SDK. Generally held inside a dng_exception. + +typedef int32 dng_error_code; + +enum + { + dng_error_none = 0, //!< No error. Success. + dng_error_unknown = 100000, //!< Logic or program error or other unclassifiable error. + dng_error_not_yet_implemented, //!< Functionality requested is not yet implemented. + dng_error_silent, //!< An error which should not be signalled to user. + dng_error_user_canceled, //!< Processing stopped by user (or host application) request + dng_error_host_insufficient, //!< Necessary host functionality is not present. + dng_error_memory, //!< Out of memory. + dng_error_bad_format, //!< File format is not valid. + dng_error_matrix_math, //!< Matrix has wrong shape, is badly conditioned, or similar problem. + dng_error_open_file, //!< Could not open file. + dng_error_read_file, //!< Error reading file. + dng_error_write_file, //!< Error writing file. + dng_error_end_of_file, //!< Unexpected end of file. + dng_error_file_is_damaged, //!< File is damaged in some way. + dng_error_image_too_big_dng, //!< Image is too big to save as DNG. + dng_error_image_too_big_tiff, //!< Image is too big to save as TIFF. + dng_error_unsupported_dng, //!< DNG version is unsupported. + dng_error_overflow //!< Arithmetic overflow. + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_exceptions.cpp b/dng/dng_exceptions.cpp new file mode 100644 index 0000000..7c61301 --- /dev/null +++ b/dng/dng_exceptions.cpp @@ -0,0 +1,219 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_exceptions.h" + +#include "dng_flags.h" +#include "dng_globals.h" + +/*****************************************************************************/ + +#ifndef qDNGReportErrors +// assuming this isn't enable on Win, because it's using printf, but an app can redirect that to console +#define qDNGReportErrors ((qDNGDebug && qMacOS) || qDNGValidate) +#endif + +/*****************************************************************************/ + +void ReportWarning (const char *message, + const char *sub_message) + { + + #if qDNGReportErrors + + #ifdef cr_logw + + cr_logs("report", 2, NULL, 0, cr_logfunc(), "%s %s\n", message, sub_message ? sub_message : ""); + + #else + + if (sub_message) + fprintf (stderr, "*** Warning: %s (%s) ***\n", message, sub_message); + else + fprintf (stderr, "*** Warning: %s ***\n", message); + + #endif + + #else + + (void) message; + (void) sub_message; + + #endif + + } + +/*****************************************************************************/ + +void ReportError (const char *message, + const char *sub_message) + { + + #if qDNGReportErrors + + #ifdef cr_loge + + cr_logs("report", 3, NULL, 0, cr_logfunc(), "%s %s\n", message, sub_message ? sub_message : ""); + + #else + + if (sub_message) + fprintf (stderr, "*** Error: %s (%s) ***\n", message, sub_message); + else + fprintf (stderr, "*** Error: %s ***\n", message); + + #endif + + #else + + (void) message; + (void) sub_message; + + #endif + + } + +/*****************************************************************************/ + +void Throw_dng_error (dng_error_code err, + const char *message, + const char *sub_message, + bool silent) + { + + #if qDNGReportErrors + + { + + if (!message) + { + + switch (err) + { + + case dng_error_none: + case dng_error_silent: + case dng_error_user_canceled: + { + break; + } + + case dng_error_not_yet_implemented: + { + message = "Not yet implemented"; + break; + } + + case dng_error_host_insufficient: + { + message = "Host insufficient"; + break; + } + + case dng_error_memory: + { + message = "Unable to allocate memory"; + break; + } + + case dng_error_bad_format: + { + message = "File format is invalid"; + break; + } + + case dng_error_matrix_math: + { + message = "Matrix math error"; + break; + } + + case dng_error_open_file: + { + message = "Unable to open file"; + break; + } + + case dng_error_read_file: + { + message = "File read error"; + break; + } + + case dng_error_write_file: + { + message = "File write error"; + break; + } + + case dng_error_end_of_file: + { + message = "Unexpected end-of-file"; + break; + } + + case dng_error_file_is_damaged: + { + message = "File is damaged"; + break; + } + + case dng_error_image_too_big_dng: + { + message = "Image is too big to save as DNG"; + break; + } + + case dng_error_image_too_big_tiff: + { + message = "Image is too big to save as TIFF"; + break; + } + + case dng_error_unsupported_dng: + { + message = "DNG version is unsupported"; + break; + } + + case dng_error_overflow: + { + message = "Arithmetic overflow/underflow"; + break; + } + + default: + { + message = "Unknown error"; + break; + } + + } + + } + + if (message && !silent) + { + ReportError (message, sub_message); + } + + } + + #else + + (void) message; + (void) sub_message; + (void) silent; + + #endif + + throw dng_exception (err); + + } + +/*****************************************************************************/ diff --git a/dng/dng_exceptions.h b/dng/dng_exceptions.h new file mode 100644 index 0000000..e3afa7d --- /dev/null +++ b/dng/dng_exceptions.h @@ -0,0 +1,319 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * C++ exception support for DNG SDK. +*/ + +/*****************************************************************************/ + +#ifndef __dng_exceptions__ +#define __dng_exceptions__ + +/*****************************************************************************/ + +#include "dng_errors.h" +#include "dng_flags.h" + +/*****************************************************************************/ + +#ifndef DNG_NO_RETURN +#ifdef __GNUC__ +#define DNG_NO_RETURN __attribute__((noreturn)) +#else +#define DNG_NO_RETURN +#endif +#endif + +/*****************************************************************************/ + +/// Display a warning message. Note that this may just eat the message. + +void ReportWarning (const char *message, + const char *sub_message = NULL); + +/*****************************************************************************/ + +/// Display an error message. Note that this may just eat the message. + +void ReportError (const char *message, + const char *sub_message = NULL); + +/*****************************************************************************/ + +/// \brief All exceptions thrown by the DNG SDK use this exception class. + +class dng_exception + { + + private: + + dng_error_code fErrorCode; + + public: + + /// Construct an exception representing the given error code. + /// \param code Error code this exception is for. + + dng_exception (dng_error_code code) + + : fErrorCode (code) + + { + } + + virtual ~dng_exception () + { + } + + /// Getter for error code of this exception + /// \retval The error code of this exception. + + dng_error_code ErrorCode () const + { + return fErrorCode; + } + + }; + +/******************************************************************************/ + +/// \brief Throw an exception based on an arbitrary error code. + +void Throw_dng_error (dng_error_code err, + const char * message = NULL, + const char * sub_message = NULL, + bool silent = false) DNG_NO_RETURN; + +/******************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code if +/// error_code is not dng_error_none . + +inline void Fail_dng_error (dng_error_code err) + { + + if (err != dng_error_none) + { + + Throw_dng_error (err); + + } + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_unknown . + +inline void ThrowProgramError (const char * sub_message = NULL) + { + + Throw_dng_error (dng_error_unknown, NULL, sub_message); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_overflow. + +inline void ThrowOverflow (const char * sub_message = NULL) + { + + Throw_dng_error (dng_error_overflow, NULL, sub_message); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_not_yet_implemented . + +inline void ThrowNotYetImplemented (const char * sub_message = NULL) + { + + Throw_dng_error (dng_error_not_yet_implemented, NULL, sub_message); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_silent . + +inline void ThrowSilentError () + { + + Throw_dng_error (dng_error_silent); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_user_canceled . + +inline void ThrowUserCanceled () + { + + Throw_dng_error (dng_error_user_canceled); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_host_insufficient . + +inline void ThrowHostInsufficient (const char * sub_message = NULL, + bool silent = false) + { + + Throw_dng_error (dng_error_host_insufficient, NULL, sub_message, silent); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_memory . + +inline void ThrowMemoryFull (const char * sub_message = NULL) + { + + Throw_dng_error (dng_error_memory, NULL, sub_message); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_bad_format . + +inline void ThrowBadFormat (const char * sub_message = NULL) + { + + Throw_dng_error (dng_error_bad_format, NULL, sub_message); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_matrix_math . + +inline void ThrowMatrixMath (const char * sub_message = NULL) + { + + Throw_dng_error (dng_error_matrix_math, NULL, sub_message); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_open_file . + +inline void ThrowOpenFile (const char * sub_message = NULL, bool silent = false) + { + + Throw_dng_error (dng_error_open_file, NULL, sub_message, silent); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_read_file . + +inline void ThrowReadFile (const char *sub_message = NULL) + { + + Throw_dng_error (dng_error_read_file, NULL, sub_message); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_write_file . + +inline void ThrowWriteFile (const char *sub_message = NULL) + { + + Throw_dng_error (dng_error_write_file, NULL, sub_message); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_end_of_file . + +inline void ThrowEndOfFile (const char *sub_message = NULL) + { + + Throw_dng_error (dng_error_end_of_file, NULL, sub_message); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_file_is_damaged . + +inline void ThrowFileIsDamaged () + { + + Throw_dng_error (dng_error_file_is_damaged); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_image_too_big_dng . + +inline void ThrowImageTooBigDNG () + { + + Throw_dng_error (dng_error_image_too_big_dng); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_image_too_big_tiff . + +inline void ThrowImageTooBigTIFF () + { + + Throw_dng_error (dng_error_image_too_big_tiff); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_unsupported_dng . + +inline void ThrowUnsupportedDNG () + { + + Throw_dng_error (dng_error_unsupported_dng); + + } + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_exif.cpp b/dng/dng_exif.cpp new file mode 100644 index 0000000..517d9e4 --- /dev/null +++ b/dng/dng_exif.cpp @@ -0,0 +1,4754 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_exif.h" + +#include "dng_tag_codes.h" +#include "dng_tag_types.h" +#include "dng_parse_utils.h" +#include "dng_globals.h" +#include "dng_exceptions.h" +#include "dng_tag_values.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_exif::dng_exif () + + : fImageDescription () + , fMake () + , fModel () + , fSoftware () + , fArtist () + , fCopyright () + , fCopyright2 () + , fUserComment () + + , fDateTime () + , fDateTimeStorageInfo () + + , fDateTimeOriginal () + , fDateTimeOriginalStorageInfo () + + , fDateTimeDigitized () + , fDateTimeDigitizedStorageInfo () + + , fTIFF_EP_StandardID (0) + , fExifVersion (0) + , fFlashPixVersion (0) + + , fExposureTime () + , fFNumber () + , fShutterSpeedValue () + , fApertureValue () + , fBrightnessValue () + , fExposureBiasValue () + , fMaxApertureValue () + , fFocalLength () + , fDigitalZoomRatio () + , fExposureIndex () + , fSubjectDistance () + , fGamma () + + , fBatteryLevelR () + , fBatteryLevelA () + + , fExposureProgram (0xFFFFFFFF) + , fMeteringMode (0xFFFFFFFF) + , fLightSource (0xFFFFFFFF) + , fFlash (0xFFFFFFFF) + , fFlashMask (0x0000FFFF) + , fSensingMethod (0xFFFFFFFF) + , fColorSpace (0xFFFFFFFF) + , fFileSource (0xFFFFFFFF) + , fSceneType (0xFFFFFFFF) + , fCustomRendered (0xFFFFFFFF) + , fExposureMode (0xFFFFFFFF) + , fWhiteBalance (0xFFFFFFFF) + , fSceneCaptureType (0xFFFFFFFF) + , fGainControl (0xFFFFFFFF) + , fContrast (0xFFFFFFFF) + , fSaturation (0xFFFFFFFF) + , fSharpness (0xFFFFFFFF) + , fSubjectDistanceRange (0xFFFFFFFF) + , fSelfTimerMode (0xFFFFFFFF) + , fImageNumber (0xFFFFFFFF) + + , fFocalLengthIn35mmFilm (0) + + , fSensitivityType (0) + , fStandardOutputSensitivity (0) + , fRecommendedExposureIndex (0) + , fISOSpeed (0) + , fISOSpeedLatitudeyyy (0) + , fISOSpeedLatitudezzz (0) + + , fSubjectAreaCount (0) + + , fComponentsConfiguration (0) + + , fCompresssedBitsPerPixel () + + , fPixelXDimension (0) + , fPixelYDimension (0) + + , fFocalPlaneXResolution () + , fFocalPlaneYResolution () + + , fFocalPlaneResolutionUnit (0xFFFFFFFF) + + , fCFARepeatPatternRows (0) + , fCFARepeatPatternCols (0) + + , fImageUniqueID () + + , fGPSVersionID (0) + , fGPSLatitudeRef () + , fGPSLongitudeRef () + , fGPSAltitudeRef (0xFFFFFFFF) + , fGPSAltitude () + , fGPSSatellites () + , fGPSStatus () + , fGPSMeasureMode () + , fGPSDOP () + , fGPSSpeedRef () + , fGPSSpeed () + , fGPSTrackRef () + , fGPSTrack () + , fGPSImgDirectionRef () + , fGPSImgDirection () + , fGPSMapDatum () + , fGPSDestLatitudeRef () + , fGPSDestLongitudeRef () + , fGPSDestBearingRef () + , fGPSDestBearing () + , fGPSDestDistanceRef () + , fGPSDestDistance () + , fGPSProcessingMethod () + , fGPSAreaInformation () + , fGPSDateStamp () + , fGPSDifferential (0xFFFFFFFF) + , fGPSHPositioningError () + + , fInteroperabilityIndex () + + , fInteroperabilityVersion (0) + + , fRelatedImageFileFormat () + + , fRelatedImageWidth (0) + , fRelatedImageLength (0) + + , fCameraSerialNumber () + + , fLensID () + , fLensMake () + , fLensName () + , fLensSerialNumber () + + , fLensNameWasReadFromExif (false) + + , fApproxFocusDistance () + + , fFlashCompensation () + + , fOwnerName () + , fFirmware () + + , fTemperature () + , fHumidity () + , fPressure () + , fWaterDepth () + , fAcceleration () + , fCameraElevationAngle () + + , fTitle () + + { + + uint32 j; + uint32 k; + + fISOSpeedRatings [0] = 0; + fISOSpeedRatings [1] = 0; + fISOSpeedRatings [2] = 0; + + for (j = 0; j < kMaxCFAPattern; j++) + for (k = 0; k < kMaxCFAPattern; k++) + { + fCFAPattern [j] [k] = 255; + } + + memset (fLensDistortInfo, 0, sizeof (fLensDistortInfo)); + + } + +/*****************************************************************************/ + +dng_exif::~dng_exif () + { + + } + +/*****************************************************************************/ + +dng_exif * dng_exif::Clone () const + { + + dng_exif *result = new dng_exif (*this); + + if (!result) + { + ThrowMemoryFull (); + } + + return result; + + } + +/*****************************************************************************/ + +void dng_exif::SetEmpty () + { + + *this = dng_exif (); + + } + +/*****************************************************************************/ + +void dng_exif::CopyGPSFrom (const dng_exif &exif) + { + + fGPSVersionID = exif.fGPSVersionID; + fGPSLatitudeRef = exif.fGPSLatitudeRef; + fGPSLatitude [0] = exif.fGPSLatitude [0]; + fGPSLatitude [1] = exif.fGPSLatitude [1]; + fGPSLatitude [2] = exif.fGPSLatitude [2]; + fGPSLongitudeRef = exif.fGPSLongitudeRef; + fGPSLongitude [0] = exif.fGPSLongitude [0]; + fGPSLongitude [1] = exif.fGPSLongitude [1]; + fGPSLongitude [2] = exif.fGPSLongitude [2]; + fGPSAltitudeRef = exif.fGPSAltitudeRef; + fGPSAltitude = exif.fGPSAltitude; + fGPSTimeStamp [0] = exif.fGPSTimeStamp [0]; + fGPSTimeStamp [1] = exif.fGPSTimeStamp [1]; + fGPSTimeStamp [2] = exif.fGPSTimeStamp [2]; + fGPSSatellites = exif.fGPSSatellites; + fGPSStatus = exif.fGPSStatus; + fGPSMeasureMode = exif.fGPSMeasureMode; + fGPSDOP = exif.fGPSDOP; + fGPSSpeedRef = exif.fGPSSpeedRef; + fGPSSpeed = exif.fGPSSpeed; + fGPSTrackRef = exif.fGPSTrackRef; + fGPSTrack = exif.fGPSTrack; + fGPSImgDirectionRef = exif.fGPSImgDirectionRef; + fGPSImgDirection = exif.fGPSImgDirection; + fGPSMapDatum = exif.fGPSMapDatum; + fGPSDestLatitudeRef = exif.fGPSDestLatitudeRef; + fGPSDestLatitude [0] = exif.fGPSDestLatitude [0]; + fGPSDestLatitude [1] = exif.fGPSDestLatitude [1]; + fGPSDestLatitude [2] = exif.fGPSDestLatitude [2]; + fGPSDestLongitudeRef = exif.fGPSDestLongitudeRef; + fGPSDestLongitude [0] = exif.fGPSDestLongitude [0]; + fGPSDestLongitude [1] = exif.fGPSDestLongitude [1]; + fGPSDestLongitude [2] = exif.fGPSDestLongitude [2]; + fGPSDestBearingRef = exif.fGPSDestBearingRef; + fGPSDestBearing = exif.fGPSDestBearing; + fGPSDestDistanceRef = exif.fGPSDestDistanceRef; + fGPSDestDistance = exif.fGPSDestDistance; + fGPSProcessingMethod = exif.fGPSProcessingMethod; + fGPSAreaInformation = exif.fGPSAreaInformation; + fGPSDateStamp = exif.fGPSDateStamp; + fGPSDifferential = exif.fGPSDifferential; + fGPSHPositioningError = exif.fGPSHPositioningError; + + } + +/*****************************************************************************/ + +// Fix up common errors and rounding issues with EXIF exposure times. + +real64 dng_exif::SnapExposureTime (real64 et) + { + + // Protection against invalid values. + + if (et <= 0.0) + return 0.0; + + // If near a standard shutter speed, snap to it. + + static const real64 kStandardSpeed [] = + { + 30.0, + 25.0, + 20.0, + 15.0, + 13.0, + 10.0, + 8.0, + 6.0, + 5.0, + 4.0, + 3.2, + 3.0, + 2.5, + 2.0, + 1.6, + 1.5, + 1.3, + 1.0, + 0.8, + 0.7, + 0.6, + 0.5, + 0.4, + 0.3, + 1.0 / 4.0, + 1.0 / 5.0, + 1.0 / 6.0, + 1.0 / 8.0, + 1.0 / 10.0, + 1.0 / 13.0, + 1.0 / 15.0, + 1.0 / 20.0, + 1.0 / 25.0, + 1.0 / 30.0, + 1.0 / 40.0, + 1.0 / 45.0, + 1.0 / 50.0, + 1.0 / 60.0, + 1.0 / 80.0, + 1.0 / 90.0, + 1.0 / 100.0, + 1.0 / 125.0, + 1.0 / 160.0, + 1.0 / 180.0, + 1.0 / 200.0, + 1.0 / 250.0, + 1.0 / 320.0, + 1.0 / 350.0, + 1.0 / 400.0, + 1.0 / 500.0, + 1.0 / 640.0, + 1.0 / 750.0, + 1.0 / 800.0, + 1.0 / 1000.0, + 1.0 / 1250.0, + 1.0 / 1500.0, + 1.0 / 1600.0, + 1.0 / 2000.0, + 1.0 / 2500.0, + 1.0 / 3000.0, + 1.0 / 3200.0, + 1.0 / 4000.0, + 1.0 / 5000.0, + 1.0 / 6000.0, + 1.0 / 6400.0, + 1.0 / 8000.0, + 1.0 / 10000.0, + 1.0 / 12000.0, + 1.0 / 12800.0, + 1.0 / 16000.0 + }; + + uint32 count = sizeof (kStandardSpeed ) / + sizeof (kStandardSpeed [0]); + + for (uint32 fudge = 0; fudge <= 1; fudge++) + { + + real64 testSpeed = et; + + if (fudge == 1) + { + + // Often APEX values are rounded to a power of two, + // which results in non-standard shutter speeds. + + if (et >= 0.1) + { + + // No fudging slower than 1/10 second + + break; + + } + + else if (et >= 0.01) + { + + // Between 1/10 and 1/100 the commonly misrounded + // speeds are 1/15, 1/30, 1/60, which are often encoded as + // 1/16, 1/32, 1/64. Try fudging and see if we get + // near a standard speed. + + testSpeed *= 16.0 / 15.0; + + } + + else + { + + // Faster than 1/100, the commonly misrounded + // speeds are 1/125, 1/250, 1/500, etc., which + // are often encoded as 1/128, 1/256, 1/512. + + testSpeed *= 128.0 / 125.0; + + } + + } + + for (uint32 index = 0; index < count; index++) + { + + if (testSpeed >= kStandardSpeed [index] * 0.98 && + testSpeed <= kStandardSpeed [index] * 1.02) + { + + return kStandardSpeed [index]; + + } + + } + + } + + // We are not near any standard speeds. Round the non-standard value to something + // that looks reasonable. + + if (et >= 10.0) + { + + // Round to nearest second. + + et = floor (et + 0.5); + + } + + else if (et >= 0.5) + { + + // Round to nearest 1/10 second + + et = floor (et * 10.0 + 0.5) * 0.1; + + } + + else if (et >= 1.0 / 20.0) + { + + // Round to an exact inverse. + + et = 1.0 / floor (1.0 / et + 0.5); + + } + + else if (et >= 1.0 / 130.0) + { + + // Round inverse to multiple of 5 + + et = 0.2 / floor (0.2 / et + 0.5); + + } + + else if (et >= 1.0 / 750.0) + { + + // Round inverse to multiple of 10 + + et = 0.1 / floor (0.1 / et + 0.5); + + } + + else if (et >= 1.0 / 1300.0) + { + + // Round inverse to multiple of 50 + + et = 0.02 / floor (0.02 / et + 0.5); + + } + + else if (et >= 1.0 / 15000.0) + { + + // Round inverse to multiple of 100 + + et = 0.01 / floor (0.01 / et + 0.5); + + } + + else + { + + // Round inverse to multiple of 1000 + + et = 0.001 / floor (0.001 / et + 0.5); + + } + + return et; + + } + +/*****************************************************************************/ + +void dng_exif::SetExposureTime (real64 et, bool snap) + { + + fExposureTime.Clear (); + + fShutterSpeedValue.Clear (); + + if (snap) + { + + et = SnapExposureTime (et); + + } + + if (et >= 1.0 / 1073741824.0 && et <= 1073741824.0) + { + + if (et >= 100.0) + { + + fExposureTime.Set_real64 (et, 1); + + } + + else if (et >= 1.0) + { + + fExposureTime.Set_real64 (et, 10); + + fExposureTime.ReduceByFactor (10); + + } + + else if (et <= 0.1) + { + + fExposureTime = dng_urational (1, Round_uint32 (1.0 / et)); + + } + + else + { + + fExposureTime.Set_real64 (et, 100); + + fExposureTime.ReduceByFactor (10); + + for (uint32 f = 2; f <= 9; f++) + { + + real64 z = 1.0 / (real64) f / et; + + if (z >= 0.99 && z <= 1.01) + { + + fExposureTime = dng_urational (1, f); + + break; + + } + + } + + } + + // Now mirror this value to the ShutterSpeedValue field. + + et = fExposureTime.As_real64 (); + + fShutterSpeedValue.Set_real64 (-log (et) / log (2.0), 1000000); + + fShutterSpeedValue.ReduceByFactor (10); + fShutterSpeedValue.ReduceByFactor (10); + fShutterSpeedValue.ReduceByFactor (10); + fShutterSpeedValue.ReduceByFactor (10); + fShutterSpeedValue.ReduceByFactor (10); + fShutterSpeedValue.ReduceByFactor (10); + + } + + } + +/*****************************************************************************/ + +void dng_exif::SetShutterSpeedValue (real64 ss) + { + + if (fExposureTime.NotValid ()) + { + + real64 et = pow (2.0, -ss); + + SetExposureTime (et, true); + + } + + } + +/******************************************************************************/ + +dng_urational dng_exif::EncodeFNumber (real64 fs) + { + + dng_urational y; + + if (fs > 10.0) + { + + y.Set_real64 (fs, 1); + + } + + else if (fs < 1.0) + { + + y.Set_real64 (fs, 100); + + y.ReduceByFactor (10); + y.ReduceByFactor (10); + + } + + else + { + + y.Set_real64 (fs, 10); + + y.ReduceByFactor (10); + + } + + return y; + + } + +/*****************************************************************************/ + +void dng_exif::SetFNumber (real64 fs) + { + + fFNumber.Clear (); + + fApertureValue.Clear (); + + // Allow f-number values less than 1.0 (e.g., f/0.95), even though they would + // correspond to negative APEX values, which the EXIF specification does not + // support (ApertureValue is a rational, not srational). The ApertureValue tag + // will be omitted in the case where fs < 1.0. + + if (fs > 0.0 && fs <= 32768.0) + { + + fFNumber = EncodeFNumber (fs); + + // Now mirror this value to the ApertureValue field. + + real64 av = FNumberToApertureValue (fFNumber); + + if (av >= 0.0 && av <= 99.99) + { + + fApertureValue.Set_real64 (av, 1000000); + + fApertureValue.ReduceByFactor (10); + fApertureValue.ReduceByFactor (10); + fApertureValue.ReduceByFactor (10); + fApertureValue.ReduceByFactor (10); + fApertureValue.ReduceByFactor (10); + fApertureValue.ReduceByFactor (10); + + } + + } + + } + +/*****************************************************************************/ + +void dng_exif::SetApertureValue (real64 av) + { + + if (fFNumber.NotValid ()) + { + + SetFNumber (ApertureValueToFNumber (av)); + + } + + } + +/*****************************************************************************/ + +real64 dng_exif::ApertureValueToFNumber (real64 av) + { + + return pow (2.0, 0.5 * av); + + } + +/*****************************************************************************/ + +real64 dng_exif::ApertureValueToFNumber (const dng_urational &av) + { + + return ApertureValueToFNumber (av.As_real64 ()); + + } + +/*****************************************************************************/ + +real64 dng_exif::FNumberToApertureValue (real64 fNumber) + { + + return 2.0 * log (fNumber) / log (2.0); + + } + +/*****************************************************************************/ + +real64 dng_exif::FNumberToApertureValue (const dng_urational &fNumber) + { + + return FNumberToApertureValue (fNumber.As_real64 ()); + + } + +/*****************************************************************************/ + +void dng_exif::UpdateDateTime (const dng_date_time_info &dt) + { + + fDateTime = dt; + + } + +/*****************************************************************************/ + +bool dng_exif::AtLeastVersion0230 () const + { + + return fExifVersion >= DNG_CHAR4 ('0','2','3','0'); + + } + +/*****************************************************************************/ + +bool dng_exif::AtLeastVersion0231 () const + { + + return fExifVersion >= DNG_CHAR4 ('0','2','3','1'); + + } + +/*****************************************************************************/ + +void dng_exif::SetVersion0231 () + { + + fExifVersion = DNG_CHAR4 ('0','2','3','1'); + + } + +/*****************************************************************************/ + +bool dng_exif::HasLensDistortInfo () const + { + + return (fLensDistortInfo [0] . IsValid () && + fLensDistortInfo [1] . IsValid () && + fLensDistortInfo [2] . IsValid () && + fLensDistortInfo [3] . IsValid ()); + + } + +/*****************************************************************************/ + +void dng_exif::SetLensDistortInfo (const dng_vector ¶ms) + { + + if (params.Count () != 4) + { + return; + } + + fLensDistortInfo [0] . Set_real64 (params [0]); + fLensDistortInfo [1] . Set_real64 (params [1]); + fLensDistortInfo [2] . Set_real64 (params [2]); + fLensDistortInfo [3] . Set_real64 (params [3]); + + } + +/*****************************************************************************/ + +bool dng_exif::ParseTag (dng_stream &stream, + dng_shared &shared, + uint32 parentCode, + bool isMainIFD, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset) + { + + if (parentCode == 0) + { + + if (Parse_ifd0 (stream, + shared, + parentCode, + tagCode, + tagType, + tagCount, + tagOffset)) + { + + return true; + + } + + } + + if (parentCode == 0 || isMainIFD) + { + + if (Parse_ifd0_main (stream, + shared, + parentCode, + tagCode, + tagType, + tagCount, + tagOffset)) + { + + return true; + + } + + } + + if (parentCode == 0 || + parentCode == tcExifIFD) + { + + if (Parse_ifd0_exif (stream, + shared, + parentCode, + tagCode, + tagType, + tagCount, + tagOffset)) + { + + return true; + + } + + } + + if (parentCode == tcGPSInfo) + { + + if (Parse_gps (stream, + shared, + parentCode, + tagCode, + tagType, + tagCount, + tagOffset)) + { + + return true; + + } + + } + + if (parentCode == tcInteroperabilityIFD) + { + + if (Parse_interoperability (stream, + shared, + parentCode, + tagCode, + tagType, + tagCount, + tagOffset)) + { + + return true; + + } + + } + + return false; + + } + +/*****************************************************************************/ + +// Parses tags that should only appear in IFD 0. + +bool dng_exif::Parse_ifd0 (dng_stream &stream, + dng_shared & /* shared */, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 /* tagOffset */) + { + + switch (tagCode) + { + + case tcImageDescription: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fImageDescription); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ImageDescription: "); + + DumpString (fImageDescription); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcMake: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fMake); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Make: "); + + DumpString (fMake); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcModel: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fModel); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Model: "); + + DumpString (fModel); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcSoftware: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fSoftware); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Software: "); + + DumpString (fSoftware); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcDateTime: + { + + uint64 tagPosition = stream.PositionInOriginalFile (); + + dng_date_time dt; + + if (!ParseDateTimeTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + dt)) + { + return false; + } + + fDateTime.SetDateTime (dt); + + fDateTimeStorageInfo = dng_date_time_storage_info (tagPosition, + dng_date_time_format_exif); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("DateTime: "); + + DumpDateTime (fDateTime.DateTime ()); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcArtist: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fArtist); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Artist: "); + + DumpString (fArtist); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcCopyright: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseDualStringTag (stream, + parentCode, + tagCode, + tagCount, + fCopyright, + fCopyright2); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Copyright: "); + + DumpString (fCopyright); + + if (fCopyright2.Get () [0] != 0) + { + + printf (" "); + + DumpString (fCopyright2); + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcTIFF_EP_StandardID: + { + + CheckTagType (parentCode, tagCode, tagType, ttByte); + + CheckTagCount (parentCode, tagCode, tagCount, 4); + + uint32 b0 = stream.Get_uint8 (); + uint32 b1 = stream.Get_uint8 (); + uint32 b2 = stream.Get_uint8 (); + uint32 b3 = stream.Get_uint8 (); + + fTIFF_EP_StandardID = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + + #if qDNGValidate + + if (gVerbose) + { + printf ("TIFF/EPStandardID: %u.%u.%u.%u\n", + (unsigned) b0, + (unsigned) b1, + (unsigned) b2, + (unsigned) b3); + } + + #endif + + break; + + } + + case tcCameraSerialNumber: + case tcKodakCameraSerialNumber: // Kodak uses a very similar tag. + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fCameraSerialNumber); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("%s: ", LookupTagCode (parentCode, tagCode)); + + DumpString (fCameraSerialNumber); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcLensInfo: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 4)) + return false; + + fLensInfo [0] = stream.TagValue_urational (tagType); + fLensInfo [1] = stream.TagValue_urational (tagType); + fLensInfo [2] = stream.TagValue_urational (tagType); + fLensInfo [3] = stream.TagValue_urational (tagType); + + // Some third party software wrote zero rather and undefined values + // for unknown entries. Work around this bug. + + for (uint32 j = 0; j < 4; j++) + { + + if (fLensInfo [j].IsValid () && fLensInfo [j].As_real64 () <= 0.0) + { + + fLensInfo [j] = dng_urational (0, 0); + + #if qDNGValidate + + ReportWarning ("Zero entry in LensInfo tag--should be undefined"); + + #endif + + } + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("LensInfo: "); + + real64 minFL = fLensInfo [0].As_real64 (); + real64 maxFL = fLensInfo [1].As_real64 (); + + if (minFL == maxFL) + printf ("%0.1f mm", minFL); + else + printf ("%0.1f-%0.1f mm", minFL, maxFL); + + if (fLensInfo [2].d) + { + + real64 minFS = fLensInfo [2].As_real64 (); + real64 maxFS = fLensInfo [3].As_real64 (); + + if (minFS == maxFS) + printf (" f/%0.1f", minFS); + else + printf (" f/%0.1f-%0.1f", minFS, maxFS); + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + default: + { + + return false; + + } + + } + + return true; + + } + +/*****************************************************************************/ + +// Parses tags that should only appear in IFD 0 or the main image IFD. + +bool dng_exif::Parse_ifd0_main (dng_stream &stream, + dng_shared & /* shared */, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 /* tagOffset */) + { + + switch (tagCode) + { + + case tcFocalPlaneXResolution: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fFocalPlaneXResolution = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("FocalPlaneXResolution: %0.4f\n", + fFocalPlaneXResolution.As_real64 ()); + + } + + #endif + + break; + + } + + case tcFocalPlaneYResolution: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fFocalPlaneYResolution = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("FocalPlaneYResolution: %0.4f\n", + fFocalPlaneYResolution.As_real64 ()); + + } + + #endif + + break; + + } + + case tcFocalPlaneResolutionUnit: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fFocalPlaneResolutionUnit = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("FocalPlaneResolutionUnit: %s\n", + LookupResolutionUnit (fFocalPlaneResolutionUnit)); + + } + + #endif + + break; + + } + + case tcSensingMethod: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fSensingMethod = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("SensingMethod: %s\n", + LookupSensingMethod (fSensingMethod)); + + } + + #endif + + break; + + } + + default: + { + + return false; + + } + + } + + return true; + + } + +/*****************************************************************************/ + +// Parses tags that should only appear in IFD 0 or EXIF IFD. + +bool dng_exif::Parse_ifd0_exif (dng_stream &stream, + dng_shared & /* shared */, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 /* tagOffset */) + { + + switch (tagCode) + { + + case tcBatteryLevel: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational, ttAscii); + + if (tagType == ttAscii) + { + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fBatteryLevelA); + + } + + else + { + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fBatteryLevelR = stream.TagValue_urational (tagType); + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("BatteryLevel: "); + + if (tagType == ttAscii) + { + + DumpString (fBatteryLevelA); + + } + + else + { + + printf ("%0.2f", fBatteryLevelR.As_real64 ()); + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcExposureTime: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + dng_urational et = stream.TagValue_urational (tagType); + + SetExposureTime (et.As_real64 (), true); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ExposureTime: "); + + DumpExposureTime (et.As_real64 ()); + + printf ("\n"); + + } + + if (et.As_real64 () <= 0.0) + { + + ReportWarning ("The ExposureTime is <= 0"); + + } + + #endif + + break; + + } + + case tcFNumber: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + dng_urational fs = stream.TagValue_urational (tagType); + + // Sometimes "unknown" is recorded as zero. + + if (fs.As_real64 () <= 0.0) + { + fs.Clear (); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("FNumber: f/%0.2f\n", + fs.As_real64 ()); + + } + + #endif + + SetFNumber (fs.As_real64 ()); + + break; + + } + + case tcExposureProgram: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fExposureProgram = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ExposureProgram: %s\n", + LookupExposureProgram (fExposureProgram)); + + } + + #endif + + break; + + } + + case tcISOSpeedRatings: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1, 3); + + for (uint32 j = 0; j < tagCount && j < 3; j++) + { + + fISOSpeedRatings [j] = stream.TagValue_uint32 (tagType); + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ISOSpeedRatings:"); + + for (uint32 j = 0; j < tagCount && j < 3; j++) + { + + printf (" %u", (unsigned) fISOSpeedRatings [j]); + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcSensitivityType: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fSensitivityType = (uint32) stream.Get_uint16 (); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("SensitivityType: %s\n", + LookupSensitivityType (fSensitivityType)); + + } + + #endif + + break; + + } + + case tcStandardOutputSensitivity: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fStandardOutputSensitivity = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("StandardOutputSensitivity: %u\n", + (unsigned) fStandardOutputSensitivity); + + } + + #endif + + break; + + } + + case tcRecommendedExposureIndex: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fRecommendedExposureIndex = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("RecommendedExposureIndex: %u\n", + (unsigned) fRecommendedExposureIndex); + + } + + #endif + + break; + + } + + case tcISOSpeed: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fISOSpeed = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ISOSpeed: %u\n", + (unsigned) fISOSpeed); + + } + + #endif + + break; + + } + + case tcISOSpeedLatitudeyyy: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fISOSpeedLatitudeyyy = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ISOSpeedLatitudeyyy: %u\n", + (unsigned) fISOSpeedLatitudeyyy); + + } + + #endif + + break; + + } + + case tcISOSpeedLatitudezzz: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fISOSpeedLatitudezzz = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ISOSpeedLatitudezzz: %u\n", + (unsigned) fISOSpeedLatitudezzz); + + } + + #endif + + break; + + } + + case tcTimeZoneOffset: + { + + CheckTagType (parentCode, tagCode, tagType, ttSShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1, 2); + + dng_time_zone zoneOriginal; + + zoneOriginal.SetOffsetHours (stream.TagValue_int32 (tagType)); + + fDateTimeOriginal.SetZone (zoneOriginal); + + #if 0 // MWG: Not filling in time zones unless we are sure. + + // Note that there is no "TimeZoneOffsetDigitized" field, so + // we assume the same tone zone as the original. + + fDateTimeDigitized.SetZone (zoneOriginal); + + #endif + + dng_time_zone zoneCurrent; + + if (tagCount >= 2) + { + + zoneCurrent.SetOffsetHours (stream.TagValue_int32 (tagType)); + + fDateTime.SetZone (zoneCurrent); + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("TimeZoneOffset: DateTimeOriginal = %d", + (int) zoneOriginal.ExactHourOffset ()); + + if (tagCount >= 2) + { + + printf (", DateTime = %d", + (int) zoneCurrent.ExactHourOffset ()); + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcSelfTimerMode: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fSelfTimerMode = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("SelfTimerMode: "); + + if (fSelfTimerMode) + { + + printf ("%u sec", (unsigned) fSelfTimerMode); + + } + + else + { + + printf ("Off"); + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcExifVersion: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + CheckTagCount (parentCode, tagCode, tagCount, 4); + + uint32 b0 = stream.Get_uint8 (); + uint32 b1 = stream.Get_uint8 (); + uint32 b2 = stream.Get_uint8 (); + uint32 b3 = stream.Get_uint8 (); + + fExifVersion = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + + #if qDNGValidate + + if (gVerbose) + { + + real64 x = (b0 - '0') * 10.00 + + (b1 - '0') * 1.00 + + (b2 - '0') * 0.10 + + (b3 - '0') * 0.01; + + printf ("ExifVersion: %0.2f\n", x); + + } + + #endif + + break; + + } + + case tcDateTimeOriginal: + { + + uint64 tagPosition = stream.PositionInOriginalFile (); + + dng_date_time dt; + + if (!ParseDateTimeTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + dt)) + { + return false; + } + + fDateTimeOriginal.SetDateTime (dt); + + fDateTimeOriginalStorageInfo = dng_date_time_storage_info (tagPosition, + dng_date_time_format_exif); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("DateTimeOriginal: "); + + DumpDateTime (fDateTimeOriginal.DateTime ()); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcDateTimeDigitized: + { + + uint64 tagPosition = stream.PositionInOriginalFile (); + + dng_date_time dt; + + if (!ParseDateTimeTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + dt)) + { + return false; + } + + fDateTimeDigitized.SetDateTime (dt); + + fDateTimeDigitizedStorageInfo = dng_date_time_storage_info (tagPosition, + dng_date_time_format_exif); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("DateTimeDigitized: "); + + DumpDateTime (fDateTimeDigitized.DateTime ()); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcOffsetTime: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + dng_string offsetTime; + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + offsetTime); + + fDateTime.SetOffsetTime (offsetTime); + + // The offset time tags were added to EXIF spec 2.3.1. + // We need EXIF spec version to figure out legacy fake time + // zones in XMP, so force a correct exif spec version if + // these EXIF tags are used. + + if (!AtLeastVersion0231 ()) + { + SetVersion0231 (); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("OffsetTime: "); + + DumpString (offsetTime); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcOffsetTimeOriginal: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + dng_string offsetTime; + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + offsetTime); + + fDateTimeOriginal.SetOffsetTime (offsetTime); + + // The offset time tags were added to EXIF spec 2.3.1. + // We need EXIF spec version to figure out legacy fake time + // zones in XMP, so force a correct exif spec version if + // these EXIF tags are used. + + if (!AtLeastVersion0231 ()) + { + SetVersion0231 (); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("OffsetTimeOriginal: "); + + DumpString (offsetTime); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcOffsetTimeDigitized: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + dng_string offsetTime; + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + offsetTime); + + fDateTimeDigitized.SetOffsetTime (offsetTime); + + // The offset time tags were added to EXIF spec 2.3.1. + // We need EXIF spec version to figure out legacy fake time + // zones in XMP, so force a correct exif spec version if + // these EXIF tags are used. + + if (!AtLeastVersion0231 ()) + { + SetVersion0231 (); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("OffsetTimeDigitized: "); + + DumpString (offsetTime); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcComponentsConfiguration: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + CheckTagCount (parentCode, tagCode, tagCount, 4); + + uint32 b0 = stream.Get_uint8 (); + uint32 b1 = stream.Get_uint8 (); + uint32 b2 = stream.Get_uint8 (); + uint32 b3 = stream.Get_uint8 (); + + fComponentsConfiguration = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ComponentsConfiguration: %s %s %s %s\n", + LookupComponent (b0), + LookupComponent (b1), + LookupComponent (b2), + LookupComponent (b3)); + + } + + #endif + + break; + + } + + case tcCompressedBitsPerPixel: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fCompresssedBitsPerPixel = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CompressedBitsPerPixel: %0.2f\n", + fCompresssedBitsPerPixel.As_real64 ()); + + } + + #endif + + break; + + } + + case tcShutterSpeedValue: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + dng_srational ss = stream.TagValue_srational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ShutterSpeedValue: "); + + real64 x = pow (2.0, -ss.As_real64 ()); + + DumpExposureTime (x); + + printf ("\n"); + + } + + // The ExposureTime and ShutterSpeedValue tags should be consistent. + + if (fExposureTime.IsValid ()) + { + + real64 et = fExposureTime.As_real64 (); + + real64 tv1 = -1.0 * log (et) / log (2.0); + + real64 tv2 = ss.As_real64 (); + + // Make sure they are within 0.25 APEX values. + + if (Abs_real64 (tv1 - tv2) > 0.25) + { + + ReportWarning ("The ExposureTime and ShutterSpeedValue tags have conflicting values"); + + } + + } + + #endif + + SetShutterSpeedValue (ss.As_real64 ()); + + break; + + } + + case tcApertureValue: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + dng_urational av = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + real64 x = pow (2.0, 0.5 * av.As_real64 ()); + + printf ("ApertureValue: f/%0.2f\n", x); + + } + + // The FNumber and ApertureValue tags should be consistent. + + if (fFNumber.IsValid () && av.IsValid ()) + { + + real64 fs = fFNumber.As_real64 (); + + real64 av1 = FNumberToApertureValue (fs); + + real64 av2 = av.As_real64 (); + + if (Abs_real64 (av1 - av2) > 0.25) + { + + ReportWarning ("The FNumber and ApertureValue tags have conflicting values"); + + } + + } + + #endif + + SetApertureValue (av.As_real64 ()); + + break; + + } + + case tcBrightnessValue: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fBrightnessValue = stream.TagValue_srational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("BrightnessValue: %0.2f\n", + fBrightnessValue.As_real64 ()); + + } + + #endif + + break; + + } + + case tcExposureBiasValue: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fExposureBiasValue = stream.TagValue_srational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ExposureBiasValue: %0.2f\n", + fExposureBiasValue.As_real64 ()); + + } + + #endif + + break; + + } + + case tcMaxApertureValue: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fMaxApertureValue = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + real64 x = pow (2.0, 0.5 * fMaxApertureValue.As_real64 ()); + + printf ("MaxApertureValue: f/%0.1f\n", x); + + } + + #endif + + break; + + } + + case tcSubjectDistance: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fSubjectDistance = stream.TagValue_urational (tagType); + + fApproxFocusDistance = fSubjectDistance; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("SubjectDistance: %u/%u\n", + (unsigned) fSubjectDistance.n, + (unsigned) fSubjectDistance.d); + + } + + #endif + + break; + + } + + case tcMeteringMode: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fMeteringMode = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("MeteringMode: %s\n", + LookupMeteringMode (fMeteringMode)); + + } + + #endif + + break; + + } + + case tcLightSource: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fLightSource = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("LightSource: %s\n", + LookupLightSource (fLightSource)); + + } + + #endif + + break; + + } + + case tcFlash: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fFlash = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Flash: %u\n", (unsigned) fFlash); + + if ((fFlash >> 5) & 1) + { + printf (" No flash function\n"); + } + + else + { + + if (fFlash & 0x1) + { + + printf (" Flash fired\n"); + + switch ((fFlash >> 1) & 0x3) + { + + case 2: + printf (" Strobe return light not detected\n"); + break; + + case 3: + printf (" Strobe return light detected\n"); + break; + + } + + } + + else + { + printf (" Flash did not fire\n"); + } + + switch ((fFlash >> 3) & 0x3) + { + + case 1: + printf (" Compulsory flash firing\n"); + break; + + case 2: + printf (" Compulsory flash suppression\n"); + break; + + case 3: + printf (" Auto mode\n"); + break; + + } + + if ((fFlash >> 6) & 1) + { + printf (" Red-eye reduction supported\n"); + } + + } + + } + + #endif + + break; + + } + + case tcFocalLength: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fFocalLength = stream.TagValue_urational (tagType); + + // Sometimes "unknown" is recorded as zero. + + if (fFocalLength.As_real64 () <= 0.0) + { + fFocalLength.Clear (); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("FocalLength: %0.1f mm\n", + fFocalLength.As_real64 ()); + + } + + #endif + + break; + + } + + case tcImageNumber: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fImageNumber = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("ImageNumber: %u\n", (unsigned) fImageNumber); + } + + #endif + + break; + + } + + case tcExposureIndex: + case tcExposureIndexExif: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fExposureIndex = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("%s: ISO %0.1f\n", + LookupTagCode (parentCode, tagCode), + fExposureIndex.As_real64 ()); + + } + + #endif + + break; + + } + + case tcUserComment: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + ParseEncodedStringTag (stream, + parentCode, + tagCode, + tagCount, + fUserComment); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("UserComment: "); + + DumpString (fUserComment); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcSubsecTime: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + dng_string subsecs; + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + subsecs); + + fDateTime.SetSubseconds (subsecs); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("SubsecTime: "); + + DumpString (subsecs); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcSubsecTimeOriginal: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + dng_string subsecs; + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + subsecs); + + fDateTimeOriginal.SetSubseconds (subsecs); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("SubsecTimeOriginal: "); + + DumpString (subsecs); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcSubsecTimeDigitized: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + dng_string subsecs; + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + subsecs); + + fDateTimeDigitized.SetSubseconds (subsecs); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("SubsecTimeDigitized: "); + + DumpString (subsecs); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcTemperature: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fTemperature = stream.TagValue_srational (tagType); + + if (!AtLeastVersion0231 ()) + { + SetVersion0231 (); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Temperature: %0.1f\n", + fTemperature.As_real64 ()); + + } + + #endif + + break; + + } + + case tcHumidity: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fHumidity = stream.TagValue_urational (tagType); + + if (!AtLeastVersion0231 ()) + { + SetVersion0231 (); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Humidity: %0.1f\n", + fHumidity.As_real64 ()); + + } + + #endif + + break; + + } + + case tcPressure: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fPressure = stream.TagValue_urational (tagType); + + if (!AtLeastVersion0231 ()) + { + SetVersion0231 (); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Pressure: %0.1f\n", + fPressure.As_real64 ()); + + } + + #endif + + break; + + } + + case tcWaterDepth: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fWaterDepth = stream.TagValue_srational (tagType); + + if (!AtLeastVersion0231 ()) + { + SetVersion0231 (); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("WaterDepth: %0.1f\n", + fWaterDepth.As_real64 ()); + + } + + #endif + + break; + + } + + case tcAcceleration: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fAcceleration = stream.TagValue_urational (tagType); + + if (!AtLeastVersion0231 ()) + { + SetVersion0231 (); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Acceleration: %0.1f\n", + fAcceleration.As_real64 ()); + + } + + #endif + + break; + + } + + case tcCameraElevationAngle: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fCameraElevationAngle = stream.TagValue_srational (tagType); + + if (!AtLeastVersion0231 ()) + { + SetVersion0231 (); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CameraElevationAngle: %0.1f\n", + fCameraElevationAngle.As_real64 ()); + + } + + #endif + + break; + + } + + case tcFlashPixVersion: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + CheckTagCount (parentCode, tagCode, tagCount, 4); + + uint32 b0 = stream.Get_uint8 (); + uint32 b1 = stream.Get_uint8 (); + uint32 b2 = stream.Get_uint8 (); + uint32 b3 = stream.Get_uint8 (); + + fFlashPixVersion = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + + #if qDNGValidate + + if (gVerbose) + { + + real64 x = (b0 - '0') * 10.00 + + (b1 - '0') * 1.00 + + (b2 - '0') * 0.10 + + (b3 - '0') * 0.01; + + printf ("FlashPixVersion: %0.2f\n", x); + + } + + #endif + + break; + + } + + case tcColorSpace: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fColorSpace = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ColorSpace: %s\n", + LookupColorSpace (fColorSpace)); + + } + + #endif + + break; + + } + + case tcPixelXDimension: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fPixelXDimension = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("PixelXDimension: %u\n", (unsigned) fPixelXDimension); + } + + #endif + + break; + + } + + case tcPixelYDimension: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fPixelYDimension = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("PixelYDimension: %u\n", (unsigned) fPixelYDimension); + } + + #endif + + break; + + } + + case tcFocalPlaneXResolutionExif: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fFocalPlaneXResolution = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("FocalPlaneXResolutionExif: %0.4f\n", + fFocalPlaneXResolution.As_real64 ()); + + } + + #endif + + break; + + } + + case tcFocalPlaneYResolutionExif: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fFocalPlaneYResolution = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("FocalPlaneYResolutionExif: %0.4f\n", + fFocalPlaneYResolution.As_real64 ()); + + } + + #endif + + break; + + } + + case tcFocalPlaneResolutionUnitExif: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fFocalPlaneResolutionUnit = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("FocalPlaneResolutionUnitExif: %s\n", + LookupResolutionUnit (fFocalPlaneResolutionUnit)); + + } + + #endif + + break; + + } + + case tcSensingMethodExif: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fSensingMethod = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("SensingMethodExif: %s\n", + LookupSensingMethod (fSensingMethod)); + + } + + #endif + + break; + + } + + case tcFileSource: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fFileSource = stream.Get_uint8 (); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("FileSource: %s\n", + LookupFileSource (fFileSource)); + + } + + #endif + + break; + + } + + case tcSceneType: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fSceneType = stream.Get_uint8 (); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("SceneType: %s\n", + LookupSceneType (fSceneType)); + + } + + #endif + + break; + + } + + case tcCFAPatternExif: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + if (tagCount <= 4) + { + return false; + } + + uint32 cols = stream.Get_uint16 (); + uint32 rows = stream.Get_uint16 (); + + if (tagCount != 4 + cols * rows) + { + return false; + } + + if (cols < 1 || cols > kMaxCFAPattern || + rows < 1 || rows > kMaxCFAPattern) + { + return false; + } + + fCFARepeatPatternCols = cols; + fCFARepeatPatternRows = rows; + + // Note that the Exif spec stores this array in a different + // scan order than the TIFF-EP spec. + + for (uint32 j = 0; j < fCFARepeatPatternCols; j++) + for (uint32 k = 0; k < fCFARepeatPatternRows; k++) + { + + fCFAPattern [k] [j] = stream.Get_uint8 (); + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CFAPatternExif:\n"); + + for (uint32 j = 0; j < fCFARepeatPatternRows; j++) + { + + int32 spaces = 4; + + for (uint32 k = 0; k < fCFARepeatPatternCols; k++) + { + + while (spaces-- > 0) + { + printf (" "); + } + + const char *name = LookupCFAColor (fCFAPattern [j] [k]); + + spaces = 9 - (int32) strlen (name); + + printf ("%s", name); + + } + + printf ("\n"); + + } + + } + + #endif + + break; + + } + + case tcCustomRendered: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fCustomRendered = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CustomRendered: %s\n", + LookupCustomRendered (fCustomRendered)); + + } + + #endif + + break; + + } + + case tcExposureMode: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fExposureMode = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ExposureMode: %s\n", + LookupExposureMode (fExposureMode)); + + } + + #endif + + break; + + } + + case tcWhiteBalance: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fWhiteBalance = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("WhiteBalance: %s\n", + LookupWhiteBalance (fWhiteBalance)); + + } + + #endif + + break; + + } + + case tcDigitalZoomRatio: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fDigitalZoomRatio = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("DigitalZoomRatio: "); + + if (fDigitalZoomRatio.n == 0 || + fDigitalZoomRatio.d == 0) + { + + printf ("Not used\n"); + + } + + else + { + + printf ("%0.2f\n", fDigitalZoomRatio.As_real64 ()); + + } + + } + + #endif + + break; + + } + + case tcFocalLengthIn35mmFilm: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fFocalLengthIn35mmFilm = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("FocalLengthIn35mmFilm: %u mm\n", + (unsigned) fFocalLengthIn35mmFilm); + + } + + #endif + + break; + + } + + case tcSceneCaptureType: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fSceneCaptureType = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("SceneCaptureType: %s\n", + LookupSceneCaptureType (fSceneCaptureType)); + + } + + #endif + + break; + + } + + case tcGainControl: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fGainControl = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("GainControl: %s\n", + LookupGainControl (fGainControl)); + + } + + #endif + + break; + + } + + case tcContrast: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fContrast = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Contrast: %s\n", + LookupContrast (fContrast)); + + } + + #endif + + break; + + } + + case tcSaturation: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fSaturation = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Saturation: %s\n", + LookupSaturation (fSaturation)); + + } + + #endif + + break; + + } + + case tcSharpness: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fSharpness = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Sharpness: %s\n", + LookupSharpness (fSharpness)); + + } + + #endif + + break; + + } + + case tcSubjectDistanceRange: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fSubjectDistanceRange = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("SubjectDistanceRange: %s\n", + LookupSubjectDistanceRange (fSubjectDistanceRange)); + + } + + #endif + + break; + + } + + case tcSubjectArea: + case tcSubjectLocation: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 2, 4)) + { + return false; + } + + if (tagCode == tcSubjectLocation) + { + CheckTagCount (parentCode, tagCode, tagCount, 2); + } + + fSubjectAreaCount = tagCount; + + for (uint32 j = 0; j < tagCount; j++) + { + + fSubjectArea [j] = stream.TagValue_uint32 (tagType); + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("%s:", LookupTagCode (parentCode, tagCode)); + + for (uint32 j = 0; j < fSubjectAreaCount; j++) + { + + printf (" %u", (unsigned) fSubjectArea [j]); + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcGamma: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fGamma = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Gamma: %0.2f\n", + fGamma.As_real64 ()); + + } + + #endif + + break; + + } + + case tcImageUniqueID: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttAscii)) + return false; + + if (!CheckTagCount (parentCode, tagCode, tagCount, 33)) + return false; + + dng_string s; + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + s); + + if (s.Length () != 32) + return false; + + dng_fingerprint f; + + for (uint32 j = 0; j < 32; j++) + { + + char c = ForceUppercase (s.Get () [j]); + + uint32 digit; + + if (c >= '0' && c <= '9') + { + digit = c - '0'; + } + + else if (c >= 'A' && c <= 'F') + { + digit = c - 'A' + 10; + } + + else + return false; + + f.data [j >> 1] *= 16; + f.data [j >> 1] += (uint8) digit; + + } + + fImageUniqueID = f; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ImageUniqueID: "); + + DumpFingerprint (fImageUniqueID); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcCameraOwnerNameExif: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fOwnerName); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CameraOwnerName: "); + + DumpString (fOwnerName); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcCameraSerialNumberExif: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fCameraSerialNumber); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("%s: ", LookupTagCode (parentCode, tagCode)); + + DumpString (fCameraSerialNumber); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcLensSpecificationExif: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 4)) + return false; + + fLensInfo [0] = stream.TagValue_urational (tagType); + fLensInfo [1] = stream.TagValue_urational (tagType); + fLensInfo [2] = stream.TagValue_urational (tagType); + fLensInfo [3] = stream.TagValue_urational (tagType); + + // Some third party software wrote zero rather than undefined values for + // unknown entries. Work around this bug. + + for (uint32 j = 0; j < 4; j++) + { + + if (fLensInfo [j].IsValid () && fLensInfo [j].As_real64 () <= 0.0) + { + + fLensInfo [j] = dng_urational (0, 0); + + #if qDNGValidate + + ReportWarning ("Zero entry in LensSpecification tag--should be undefined"); + + #endif + + } + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("LensSpecificationExif: "); + + real64 minFL = fLensInfo [0].As_real64 (); + real64 maxFL = fLensInfo [1].As_real64 (); + + if (minFL == maxFL) + printf ("%0.1f mm", minFL); + else + printf ("%0.1f-%0.1f mm", minFL, maxFL); + + if (fLensInfo [2].d) + { + + real64 minFS = fLensInfo [2].As_real64 (); + real64 maxFS = fLensInfo [3].As_real64 (); + + if (minFS == maxFS) + printf (" f/%0.1f", minFS); + else + printf (" f/%0.1f-%0.1f", minFS, maxFS); + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcLensMakeExif: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fLensMake); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("%s: ", LookupTagCode (parentCode, tagCode)); + + DumpString (fLensMake); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcLensModelExif: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fLensName); + + fLensNameWasReadFromExif = fLensName.NotEmpty (); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("%s: ", LookupTagCode (parentCode, tagCode)); + + DumpString (fLensName); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcLensSerialNumberExif: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fLensSerialNumber); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("%s: ", LookupTagCode (parentCode, tagCode)); + + DumpString (fLensSerialNumber); + + printf ("\n"); + + } + + #endif + + break; + + } + + default: + { + + return false; + + } + + } + + return true; + + } + +/*****************************************************************************/ + +// Parses tags that should only appear in GPS IFD + +bool dng_exif::Parse_gps (dng_stream &stream, + dng_shared & /* shared */, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 /* tagOffset */) + { + + switch (tagCode) + { + + case tcGPSVersionID: + { + + CheckTagType (parentCode, tagCode, tagType, ttByte); + + CheckTagCount (parentCode, tagCode, tagCount, 4); + + uint32 b0 = stream.Get_uint8 (); + uint32 b1 = stream.Get_uint8 (); + uint32 b2 = stream.Get_uint8 (); + uint32 b3 = stream.Get_uint8 (); + + fGPSVersionID = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + + #if qDNGValidate + + if (gVerbose) + { + printf ("GPSVersionID: %u.%u.%u.%u\n", + (unsigned) b0, + (unsigned) b1, + (unsigned) b2, + (unsigned) b3); + } + + #endif + + break; + + } + + case tcGPSLatitudeRef: + case tcGPSLongitudeRef: + case tcGPSSatellites: + case tcGPSStatus: + case tcGPSMeasureMode: + case tcGPSSpeedRef: + case tcGPSTrackRef: + case tcGPSImgDirectionRef: + case tcGPSMapDatum: + case tcGPSDestLatitudeRef: + case tcGPSDestLongitudeRef: + case tcGPSDestBearingRef: + case tcGPSDestDistanceRef: + case tcGPSDateStamp: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttAscii)) + return false; + + dng_string *s; + + switch (tagCode) + { + + case tcGPSLatitudeRef: + s = &fGPSLatitudeRef; + break; + + case tcGPSLongitudeRef: + s = &fGPSLongitudeRef; + break; + + case tcGPSSatellites: + s = &fGPSSatellites; + break; + + case tcGPSStatus: + s = &fGPSStatus; + break; + + case tcGPSMeasureMode: + s = &fGPSMeasureMode; + break; + + case tcGPSSpeedRef: + s = &fGPSSpeedRef; + break; + + case tcGPSTrackRef: + s = &fGPSTrackRef; + break; + + case tcGPSImgDirectionRef: + s = &fGPSImgDirectionRef; + break; + + case tcGPSMapDatum: + s = &fGPSMapDatum; + break; + + case tcGPSDestLatitudeRef: + s = &fGPSDestLatitudeRef; + break; + + case tcGPSDestLongitudeRef: + s = &fGPSDestLongitudeRef; + break; + + case tcGPSDestBearingRef: + s = &fGPSDestBearingRef; + break; + + case tcGPSDestDistanceRef: + s = &fGPSDestDistanceRef; + break; + + case tcGPSDateStamp: + s = &fGPSDateStamp; + break; + + default: + return false; + + } + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + *s); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("%s: ", LookupTagCode (parentCode, tagCode)); + + DumpString (*s); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcGPSLatitude: + case tcGPSLongitude: + case tcGPSTimeStamp: + case tcGPSDestLatitude: + case tcGPSDestLongitude: + { + + // Should really be ttRational per EXIF spec, but allow + // ttSRational too because some JPEGs from Nexus 5 + // apparently use ttSRational type. + + if (!CheckTagType (parentCode, tagCode, tagType, ttRational) && + !CheckTagType (parentCode, tagCode, tagType, ttSRational)) + return false; + + if (!CheckTagCount (parentCode, tagCode, tagCount, 3)) + return false; + + dng_urational *u; + + switch (tagCode) + { + + case tcGPSLatitude: + u = fGPSLatitude; + break; + + case tcGPSLongitude: + u = fGPSLongitude; + break; + + case tcGPSTimeStamp: + u = fGPSTimeStamp; + break; + + case tcGPSDestLatitude: + u = fGPSDestLatitude; + break; + + case tcGPSDestLongitude: + u = fGPSDestLongitude; + break; + + default: + return false; + + } + + u [0] = stream.TagValue_urational (tagType); + u [1] = stream.TagValue_urational (tagType); + u [2] = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("%s:", LookupTagCode (parentCode, tagCode)); + + for (uint32 j = 0; j < 3; j++) + { + + if (u [j].d == 0) + printf (" -"); + + else + printf (" %0.4f", u [j].As_real64 ()); + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcGPSAltitudeRef: + { + + CheckTagType (parentCode, tagCode, tagType, ttByte); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fGPSAltitudeRef = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("GPSAltitudeRef: "); + + switch (fGPSAltitudeRef) + { + + case 0: + printf ("Sea level"); + break; + + case 1: + printf ("Sea level reference (negative value)"); + break; + + default: + printf ("%u", (unsigned) fGPSAltitudeRef); + break; + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcGPSAltitude: + case tcGPSDOP: + case tcGPSSpeed: + case tcGPSTrack: + case tcGPSImgDirection: + case tcGPSDestBearing: + case tcGPSDestDistance: + case tcGPSHPositioningError: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttRational)) + return false; + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + dng_urational *u; + + switch (tagCode) + { + + case tcGPSAltitude: + u = &fGPSAltitude; + break; + + case tcGPSDOP: + u = &fGPSDOP; + break; + + case tcGPSSpeed: + u = &fGPSSpeed; + break; + + case tcGPSTrack: + u = &fGPSTrack; + break; + + case tcGPSImgDirection: + u = &fGPSImgDirection; + break; + + case tcGPSDestBearing: + u = &fGPSDestBearing; + break; + + case tcGPSDestDistance: + u = &fGPSDestDistance; + break; + + case tcGPSHPositioningError: + u = &fGPSHPositioningError; + break; + + default: + return false; + + } + + *u = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("%s:", LookupTagCode (parentCode, tagCode)); + + if (u->d == 0) + printf (" -"); + + else + printf (" %0.4f", u->As_real64 ()); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcGPSProcessingMethod: + case tcGPSAreaInformation: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttUndefined)) + return false; + + dng_string *s; + + switch (tagCode) + { + + case tcGPSProcessingMethod: + s = &fGPSProcessingMethod; + break; + + case tcGPSAreaInformation: + s = &fGPSAreaInformation; + break; + + default: + return false; + + } + + ParseEncodedStringTag (stream, + parentCode, + tagCode, + tagCount, + *s); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("%s: ", LookupTagCode (parentCode, tagCode)); + + DumpString (*s); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcGPSDifferential: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fGPSDifferential = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("GPSDifferential: "); + + switch (fGPSDifferential) + { + + case 0: + printf ("Measurement without differential correction"); + break; + + case 1: + printf ("Differential correction applied"); + break; + + default: + printf ("%u", (unsigned) fGPSDifferential); + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + default: + { + + return false; + + } + + } + + return true; + + } + +/*****************************************************************************/ + +// Parses tags that should only appear in Interoperability IFD + +bool dng_exif::Parse_interoperability (dng_stream &stream, + dng_shared & /* shared */, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 /* tagOffset */) + { + + switch (tagCode) + { + + case tcInteroperabilityIndex: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + CheckTagCount (parentCode, tagCode, tagCount, 4); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fInteroperabilityIndex); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("InteroperabilityIndex: "); + + DumpString (fInteroperabilityIndex); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcInteroperabilityVersion: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + CheckTagCount (parentCode, tagCode, tagCount, 4); + + uint32 b0 = stream.Get_uint8 (); + uint32 b1 = stream.Get_uint8 (); + uint32 b2 = stream.Get_uint8 (); + uint32 b3 = stream.Get_uint8 (); + + fInteroperabilityVersion = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + + #if qDNGValidate + + if (gVerbose) + { + + real64 x = (b0 - '0') * 10.00 + + (b1 - '0') * 1.00 + + (b2 - '0') * 0.10 + + (b3 - '0') * 0.01; + + printf ("InteroperabilityVersion: %0.2f\n", x); + + } + + #endif + + break; + + } + + case tcRelatedImageFileFormat: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fRelatedImageFileFormat); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("RelatedImageFileFormat: "); + + DumpString (fRelatedImageFileFormat); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcRelatedImageWidth: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fRelatedImageWidth = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("RelatedImageWidth: %u\n", (unsigned) fRelatedImageWidth); + } + + #endif + + break; + + } + + case tcRelatedImageLength: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fRelatedImageLength = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("RelatedImageLength: %u\n", (unsigned) fRelatedImageLength); + } + + #endif + + break; + + } + + default: + { + + return false; + + } + + } + + return true; + + } + +/*****************************************************************************/ + +void dng_exif::PostParse (dng_host & /* host */, + dng_shared & /* shared */) + { + + #if qDNGValidate + + const real64 kAPEX_Slop = 0.25; + + // Sanity check on MaxApertureValue. + + if (fMaxApertureValue.d) + { + + real64 mav = fMaxApertureValue.As_real64 (); + + // Compare against ApertureValue or FNumber. + + real64 av = mav; + + if (fApertureValue.d) + { + + av = fApertureValue.As_real64 (); + + } + + else if (fFNumber.d) + { + + real64 fs = fFNumber.As_real64 (); + + if (fs >= 1.0) + { + + av = FNumberToApertureValue (fs); + + } + + } + + if (mav > av + kAPEX_Slop) + { + + ReportWarning ("MaxApertureValue conflicts with ApertureValue and/or FNumber"); + + } + + // Compare against LensInfo + + if (fLensInfo [2].d && fLensInfo [3].d) + { + + real64 fs1 = fLensInfo [2].As_real64 (); + real64 fs2 = fLensInfo [3].As_real64 (); + + if (fs1 >= 1.0 && fs2 >= 1.0 && fs2 >= fs1) + { + + real64 av1 = FNumberToApertureValue (fs1); + real64 av2 = FNumberToApertureValue (fs2); + + // Wide angle adapters might create an effective + // wide FS, and tele-extenders always result + // in a higher FS. + + if (mav < av1 - kAPEX_Slop - 1.0 || + mav > av2 + kAPEX_Slop + 2.0) + { + + ReportWarning ("Possible MaxApertureValue conflict with LensInfo"); + + } + + } + + } + + } + + // Sanity check on FocalLength. + + if (fFocalLength.d) + { + + real64 fl = fFocalLength.As_real64 (); + + if (fl < 1.0) + { + + ReportWarning ("FocalLength is less than 1.0 mm (legal but unlikely)"); + + } + + else if (fLensInfo [0].d && fLensInfo [1].d) + { + + real64 minFL = fLensInfo [0].As_real64 (); + real64 maxFL = fLensInfo [1].As_real64 (); + + // Allow for wide-angle converters and tele-extenders. + + if (fl < minFL * 0.6 || + fl > maxFL * 2.1) + { + + ReportWarning ("Possible FocalLength conflict with LensInfo"); + + } + + } + + } + + #endif + + // Mirror DateTimeOriginal to DateTime. + + if (fDateTime.NotValid () && fDateTimeOriginal.IsValid ()) + { + + fDateTime = fDateTimeOriginal; + + } + + // Mirror EXIF 2.3 sensitivity tags to ISOSpeedRatings. + + if (fISOSpeedRatings [0] == 0 || fISOSpeedRatings [0] == 65535) + { + + // Prefer Recommended Exposure Index, then Standard Output Sensitivity, then + // ISO Speed, then Exposure Index. + + if (fRecommendedExposureIndex != 0 && + (fSensitivityType == stRecommendedExposureIndex || + fSensitivityType == stSOSandREI || + fSensitivityType == stREIandISOSpeed || + fSensitivityType == stSOSandREIandISOSpeed)) + { + + fISOSpeedRatings [0] = fRecommendedExposureIndex; + + } + + else if (fStandardOutputSensitivity != 0 && + (fSensitivityType == stStandardOutputSensitivity || + fSensitivityType == stSOSandREI || + fSensitivityType == stSOSandISOSpeed || + fSensitivityType == stSOSandREIandISOSpeed)) + { + + fISOSpeedRatings [0] = fStandardOutputSensitivity; + + } + + else if (fISOSpeed != 0 && + (fSensitivityType == stISOSpeed || + fSensitivityType == stSOSandISOSpeed || + fSensitivityType == stREIandISOSpeed || + fSensitivityType == stSOSandREIandISOSpeed)) + { + + fISOSpeedRatings [0] = fISOSpeed; + + } + + } + + // Mirror ExposureIndex to ISOSpeedRatings. + + if (fExposureIndex.IsValid () && fISOSpeedRatings [0] == 0) + { + + fISOSpeedRatings [0] = Round_uint32 (fExposureIndex.As_real64 ()); + + } + + // Kodak sets the GPSAltitudeRef without setting the GPSAltitude. + + if (fGPSAltitude.NotValid ()) + { + + fGPSAltitudeRef = 0xFFFFFFFF; + + } + + // If there is no valid GPS data, clear the GPS version number. + + if (fGPSLatitude [0].NotValid () && + fGPSLongitude [0].NotValid () && + fGPSAltitude .NotValid () && + fGPSTimeStamp [0].NotValid () && + fGPSDateStamp .IsEmpty ()) + { + + fGPSVersionID = 0; + + } + + } + +/*****************************************************************************/ diff --git a/dng/dng_exif.h b/dng/dng_exif.h new file mode 100644 index 0000000..644998b --- /dev/null +++ b/dng/dng_exif.h @@ -0,0 +1,377 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * EXIF read access support. See the \ref spec_exif "EXIF specification" for full + * description of tags. + */ + +/*****************************************************************************/ + +#ifndef __dng_exif__ +#define __dng_exif__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_date_time.h" +#include "dng_fingerprint.h" +#include "dng_types.h" +#include "dng_matrix.h" +#include "dng_rational.h" +#include "dng_string.h" +#include "dng_stream.h" +#include "dng_sdk_limits.h" + +/*****************************************************************************/ + +/// \brief Container class for parsing and holding EXIF tags. +/// +/// Public member fields are documented in \ref spec_exif "EXIF specification." + +class dng_exif + { + + public: + + dng_string fImageDescription; + dng_string fMake; + dng_string fModel; + dng_string fSoftware; + dng_string fArtist; + dng_string fCopyright; + dng_string fCopyright2; + dng_string fUserComment; + + dng_date_time_info fDateTime; + dng_date_time_storage_info fDateTimeStorageInfo; + + dng_date_time_info fDateTimeOriginal; + dng_date_time_storage_info fDateTimeOriginalStorageInfo; + + dng_date_time_info fDateTimeDigitized; + dng_date_time_storage_info fDateTimeDigitizedStorageInfo; + + uint32 fTIFF_EP_StandardID; + uint32 fExifVersion; + uint32 fFlashPixVersion; + + dng_urational fExposureTime; + dng_urational fFNumber; + dng_srational fShutterSpeedValue; + dng_urational fApertureValue; + dng_srational fBrightnessValue; + dng_srational fExposureBiasValue; + dng_urational fMaxApertureValue; + dng_urational fFocalLength; + dng_urational fDigitalZoomRatio; + dng_urational fExposureIndex; + dng_urational fSubjectDistance; + dng_urational fGamma; + + dng_urational fBatteryLevelR; + dng_string fBatteryLevelA; + + uint32 fExposureProgram; + uint32 fMeteringMode; + uint32 fLightSource; + uint32 fFlash; + uint32 fFlashMask; + uint32 fSensingMethod; + uint32 fColorSpace; + uint32 fFileSource; + uint32 fSceneType; + uint32 fCustomRendered; + uint32 fExposureMode; + uint32 fWhiteBalance; + uint32 fSceneCaptureType; + uint32 fGainControl; + uint32 fContrast; + uint32 fSaturation; + uint32 fSharpness; + uint32 fSubjectDistanceRange; + uint32 fSelfTimerMode; + uint32 fImageNumber; + + uint32 fFocalLengthIn35mmFilm; + + uint32 fISOSpeedRatings [3]; // EXIF 2.3: PhotographicSensitivity. + + // Sensitivity tags added in EXIF 2.3. + + uint32 fSensitivityType; + uint32 fStandardOutputSensitivity; + uint32 fRecommendedExposureIndex; + uint32 fISOSpeed; + uint32 fISOSpeedLatitudeyyy; + uint32 fISOSpeedLatitudezzz; + + uint32 fSubjectAreaCount; + uint32 fSubjectArea [4]; + + uint32 fComponentsConfiguration; + + dng_urational fCompresssedBitsPerPixel; + + uint32 fPixelXDimension; + uint32 fPixelYDimension; + + dng_urational fFocalPlaneXResolution; + dng_urational fFocalPlaneYResolution; + + uint32 fFocalPlaneResolutionUnit; + + uint32 fCFARepeatPatternRows; + uint32 fCFARepeatPatternCols; + + uint8 fCFAPattern [kMaxCFAPattern] [kMaxCFAPattern]; + + dng_fingerprint fImageUniqueID; + + uint32 fGPSVersionID; + dng_string fGPSLatitudeRef; + dng_urational fGPSLatitude [3]; + dng_string fGPSLongitudeRef; + dng_urational fGPSLongitude [3]; + uint32 fGPSAltitudeRef; + dng_urational fGPSAltitude; + dng_urational fGPSTimeStamp [3]; + dng_string fGPSSatellites; + dng_string fGPSStatus; + dng_string fGPSMeasureMode; + dng_urational fGPSDOP; + dng_string fGPSSpeedRef; + dng_urational fGPSSpeed; + dng_string fGPSTrackRef; + dng_urational fGPSTrack; + dng_string fGPSImgDirectionRef; + dng_urational fGPSImgDirection; + dng_string fGPSMapDatum; + dng_string fGPSDestLatitudeRef; + dng_urational fGPSDestLatitude [3]; + dng_string fGPSDestLongitudeRef; + dng_urational fGPSDestLongitude [3]; + dng_string fGPSDestBearingRef; + dng_urational fGPSDestBearing; + dng_string fGPSDestDistanceRef; + dng_urational fGPSDestDistance; + dng_string fGPSProcessingMethod; + dng_string fGPSAreaInformation; + dng_string fGPSDateStamp; + uint32 fGPSDifferential; + dng_urational fGPSHPositioningError; + + dng_string fInteroperabilityIndex; + + uint32 fInteroperabilityVersion; + + dng_string fRelatedImageFileFormat; + + uint32 fRelatedImageWidth; + uint32 fRelatedImageLength; + + dng_string fCameraSerialNumber; // EXIF 2.3: BodySerialNumber. + + dng_urational fLensInfo [4]; // EXIF 2.3: LensSpecification. + + dng_string fLensID; + dng_string fLensMake; + dng_string fLensName; // EXIF 2.3: LensModel. + dng_string fLensSerialNumber; + + // Was the lens name field read from a LensModel tag? + + bool fLensNameWasReadFromExif; + + // Private field to hold the approximate focus distance of the lens, in + // meters. This value is often coarsely measured/reported and hence should be + // interpreted only as a rough estimate of the true distance from the plane + // of focus (in object space) to the focal plane. It is still useful for the + // purposes of applying lens corrections. + + dng_urational fApproxFocusDistance; + + dng_srational fFlashCompensation; + + dng_string fOwnerName; // EXIF 2.3: CameraOwnerName. + dng_string fFirmware; + + // EXIF 2.3.1: + + dng_srational fTemperature; + dng_urational fHumidity; + dng_urational fPressure; + dng_srational fWaterDepth; + dng_urational fAcceleration; + dng_srational fCameraElevationAngle; + + // Not really part of EXIF, but some formats may use. + + dng_string fTitle; + + // Image-specific radial distortion correction metadata that can be + // used later during (UI-driven) lens profile corrections. Same model + // as DNG opcode model. + + dng_srational fLensDistortInfo [4]; + + public: + + dng_exif (); + + virtual ~dng_exif (); + + /// Make clone. + + virtual dng_exif * Clone () const; + + /// Clear all EXIF fields. + + void SetEmpty (); + + /// Copy all GPS-related fields. + /// \param exif Source object from which to copy GPS fields. + + void CopyGPSFrom (const dng_exif &exif); + + /// Utility to fix up common errors and rounding issues with EXIF exposure + /// times. + + static real64 SnapExposureTime (real64 et); + + /// Set exposure time and shutter speed fields. Optionally fix up common + /// errors and rounding issues with EXIF exposure times. + /// \param et Exposure time in seconds. + /// \param snap Set to true to fix up common errors and rounding issues with + /// EXIF exposure times. + + void SetExposureTime (real64 et, + bool snap = true); + + /// Set shutter speed value (APEX units) and exposure time. + /// \param ss Shutter speed in APEX units. + + void SetShutterSpeedValue (real64 ss); + + /// Utility to encode f-number as a rational. + /// \param fs The f-number to encode. + + static dng_urational EncodeFNumber (real64 fs); + + /// Set the FNumber and ApertureValue fields. + /// \param fs The f-number to set. + + void SetFNumber (real64 fs); + + /// Set the FNumber and ApertureValue fields. + /// \param av The aperture value (APEX units). + + void SetApertureValue (real64 av); + + /// Utility to convert aperture value (APEX units) to f-number. + /// \param av The aperture value (APEX units) to convert. + + static real64 ApertureValueToFNumber (real64 av); + + /// Utility to convert aperture value (APEX units) to f-number. + /// \param av The aperture value (APEX units) to convert. + + static real64 ApertureValueToFNumber (const dng_urational &av); + + /// Utility to convert f-number to aperture value (APEX units). + /// \param fNumber The f-number to convert. + + static real64 FNumberToApertureValue (real64 fNumber); + + /// Utility to convert f-number to aperture value (APEX units). + /// \param fNumber The f-number to convert. + + static real64 FNumberToApertureValue (const dng_urational &fNumber); + + /// Set the DateTime field. + /// \param dt The DateTime value. + + void UpdateDateTime (const dng_date_time_info &dt); + + /// Returns true iff the EXIF version is at least 2.3. + + bool AtLeastVersion0230 () const; + + /// Returns true iff the EXIF version is at least 2.3.1. + + bool AtLeastVersion0231 () const; + + /// Sets the EXIF version to 2.3.1. + + void SetVersion0231 (); + + bool HasLensDistortInfo () const; + + void SetLensDistortInfo (const dng_vector ¶ms); + + virtual bool ParseTag (dng_stream &stream, + dng_shared &shared, + uint32 parentCode, + bool isMainIFD, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset); + + virtual void PostParse (dng_host &host, + dng_shared &shared); + + protected: + + virtual bool Parse_ifd0 (dng_stream &stream, + dng_shared &shared, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset); + + virtual bool Parse_ifd0_main (dng_stream &stream, + dng_shared &shared, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset); + + virtual bool Parse_ifd0_exif (dng_stream &stream, + dng_shared &shared, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset); + + virtual bool Parse_gps (dng_stream &stream, + dng_shared &shared, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset); + + virtual bool Parse_interoperability (dng_stream &stream, + dng_shared &shared, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_fast_module.h b/dng/dng_fast_module.h new file mode 100644 index 0000000..71e4f87 --- /dev/null +++ b/dng/dng_fast_module.h @@ -0,0 +1,26 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Include file to set optimization to highest level for performance-critical routines. + * Normal files should have otpimization set to normal level to save code size as there is less + * cache pollution this way. + */ + +/*****************************************************************************/ + +// Include this file in modules that contain routines that should be as fast +// as possible, even at the expense of slight code size increases. + +/*****************************************************************************/ + +#ifdef _MSC_VER +#pragma optimize ("t", on) +#endif + +/*****************************************************************************/ diff --git a/dng/dng_file_stream.cpp b/dng/dng_file_stream.cpp new file mode 100644 index 0000000..c0df9c8 --- /dev/null +++ b/dng/dng_file_stream.cpp @@ -0,0 +1,182 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_file_stream.h" + +#include "dng_exceptions.h" + +/*****************************************************************************/ + +dng_file_stream::dng_file_stream (const char *filename, + bool output, + uint32 bufferSize) + + : dng_stream ((dng_abort_sniffer *) NULL, + bufferSize, + 0) + + , fFile (NULL) + + { + + fFile = fopen (filename, output ? "wb" : "rb"); + + if (!fFile) + { + + #if qDNGValidate + + ReportError ("Unable to open file", + filename); + + ThrowSilentError (); + + #else + + ThrowOpenFile (); + + #endif + + } + + } + +/*****************************************************************************/ + +#if qWinOS + +/*****************************************************************************/ + +dng_file_stream::dng_file_stream (const wchar_t *filename, + bool output, + uint32 bufferSize) + + : dng_stream ((dng_abort_sniffer *) NULL, + bufferSize, + 0) + + , fFile (NULL) + + { + + fFile = _wfopen (filename, output ? L"wb" : L"rb"); + + if (!fFile) + { + + #if qDNGValidate + + char filenameCString[256]; + + size_t returnCount; + + wcstombs_s (&returnCount, + filenameCString, + 256, + filename, + _TRUNCATE); + + ReportError ("Unable to open file", + filenameCString); + + ThrowSilentError (); + + #else + + ThrowOpenFile (); + + #endif // qDNGValidate + + } + + } + +/*****************************************************************************/ + +#endif // qWinOS + +/*****************************************************************************/ + +dng_file_stream::~dng_file_stream () + { + + if (fFile) + { + fclose (fFile); + fFile = NULL; + } + + } + +/*****************************************************************************/ + +uint64 dng_file_stream::DoGetLength () + { + + if (fseek (fFile, 0, SEEK_END) != 0) + { + + ThrowReadFile (); + + } + + return (uint64) ftell (fFile); + + } + +/*****************************************************************************/ + +void dng_file_stream::DoRead (void *data, + uint32 count, + uint64 offset) + { + + if (fseek (fFile, (long) offset, SEEK_SET) != 0) + { + + ThrowReadFile (); + + } + + uint32 bytesRead = (uint32) fread (data, 1, count, fFile); + + if (bytesRead != count) + { + + ThrowReadFile (); + + } + + } + +/*****************************************************************************/ + +void dng_file_stream::DoWrite (const void *data, + uint32 count, + uint64 offset) + { + + if (fseek (fFile, (uint32) offset, SEEK_SET) != 0) + { + + ThrowWriteFile (); + + } + + uint32 bytesWritten = (uint32) fwrite (data, 1, count, fFile); + + if (bytesWritten != count) + { + + ThrowWriteFile (); + + } + + } + +/*****************************************************************************/ diff --git a/dng/dng_file_stream.h b/dng/dng_file_stream.h new file mode 100644 index 0000000..2d4e819 --- /dev/null +++ b/dng/dng_file_stream.h @@ -0,0 +1,72 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Simple, portable, file read/write support. + */ + +/*****************************************************************************/ + +#ifndef __dng_file_stream__ +#define __dng_file_stream__ + +/*****************************************************************************/ + +#include "dng_stream.h" + +/*****************************************************************************/ + +/// \brief A stream to/from a disk file. See dng_stream for read/write interface + +class dng_file_stream: public dng_stream + { + + private: + + FILE *fFile; + + public: + + /// Open a stream on a file. + /// \param filename Pathname in platform synax. + /// \param output Set to true if writing, false otherwise. + /// \param bufferSize size of internal buffer to use. Defaults to 4k. + + dng_file_stream (const char *filename, + bool output = false, + uint32 bufferSize = kDefaultBufferSize); + + #if qWinOS + + dng_file_stream (const wchar_t *filename, + bool output = false, + uint32 bufferSize = kDefaultBufferSize); + + #endif // qWinOS + + virtual ~dng_file_stream (); + + protected: + + virtual uint64 DoGetLength (); + + virtual void DoRead (void *data, + uint32 count, + uint64 offset); + + virtual void DoWrite (const void *data, + uint32 count, + uint64 offset); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_filter_task.cpp b/dng/dng_filter_task.cpp new file mode 100644 index 0000000..fd2858c --- /dev/null +++ b/dng/dng_filter_task.cpp @@ -0,0 +1,157 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_filter_task.h" + +#include "dng_bottlenecks.h" +#include "dng_exceptions.h" +#include "dng_image.h" +#include "dng_memory.h" +#include "dng_tag_types.h" +#include "dng_tag_values.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_filter_task::dng_filter_task (const char *name, + const dng_image &srcImage, + dng_image &dstImage) + + : dng_area_task (name) + + , fSrcImage (srcImage) + , fDstImage (dstImage) + + , fSrcPlane (0 ) + , fSrcPlanes (srcImage.Planes ()) + , fSrcPixelType (srcImage.PixelType ()) + + , fDstPlane (0 ) + , fDstPlanes (dstImage.Planes ()) + , fDstPixelType (dstImage.PixelType ()) + + , fSrcRepeat (1, 1) + , fSrcTileSize (0, 0) + + { + + } + +/*****************************************************************************/ + +dng_filter_task::~dng_filter_task () + { + + } + +/*****************************************************************************/ + +void dng_filter_task::Start (uint32 threadCount, + const dng_rect & /* dstArea */, + const dng_point &tileSize, + dng_memory_allocator *allocator, + dng_abort_sniffer * /* sniffer */) + { + + fSrcTileSize = SrcTileSize (tileSize); + + uint32 srcBufferSize = ComputeBufferSize (fSrcPixelType, + fSrcTileSize, + fSrcPlanes, + padSIMDBytes); + + uint32 dstBufferSize = ComputeBufferSize (fDstPixelType, + tileSize, + fDstPlanes, + padSIMDBytes); + + for (uint32 threadIndex = 0; threadIndex < threadCount; threadIndex++) + { + + fSrcBuffer [threadIndex] . Reset (allocator->Allocate (srcBufferSize)); + + fDstBuffer [threadIndex] . Reset (allocator->Allocate (dstBufferSize)); + + // Zero buffers so any pad bytes have defined values. + + DoZeroBytes (fSrcBuffer [threadIndex]->Buffer (), + fSrcBuffer [threadIndex]->LogicalSize ()); + + DoZeroBytes (fDstBuffer [threadIndex]->Buffer (), + fDstBuffer [threadIndex]->LogicalSize ()); + + } + + } + +/*****************************************************************************/ + +void dng_filter_task::Process (uint32 threadIndex, + const dng_rect &area, + dng_abort_sniffer * /* sniffer */) + { + + // Find source area for this destination area. + + dng_rect srcArea = SrcArea (area); + + // Safety check. + + int32 src_area_w; + int32 src_area_h; + + if (!ConvertUint32ToInt32 (srcArea.W (), + &src_area_w) || + !ConvertUint32ToInt32 (srcArea.H (), + &src_area_h) || + src_area_w > fSrcTileSize.h || + src_area_h > fSrcTileSize.v) + { + + ThrowMemoryFull ("Area exceeds tile size."); + + } + + // Setup srcBuffer. + + dng_pixel_buffer srcBuffer (srcArea, + fSrcPlane, + fSrcPlanes, + fSrcPixelType, + pcRowInterleavedAlignSIMD, + fSrcBuffer [threadIndex]->Buffer ()); + + // Setup dstBuffer. + + dng_pixel_buffer dstBuffer (area, + fDstPlane, + fDstPlanes, + fDstPixelType, + pcRowInterleavedAlignSIMD, + fDstBuffer [threadIndex]->Buffer ()); + + // Get source pixels. + + fSrcImage.Get (srcBuffer, + dng_image::edge_repeat, + fSrcRepeat.v, + fSrcRepeat.h); + + // Process area. + + ProcessArea (threadIndex, + srcBuffer, + dstBuffer); + + // Save result pixels. + + fDstImage.Put (dstBuffer); + + } + +/*****************************************************************************/ diff --git a/dng/dng_filter_task.h b/dng/dng_filter_task.h new file mode 100644 index 0000000..ca72dd2 --- /dev/null +++ b/dng/dng_filter_task.h @@ -0,0 +1,155 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Specialization of dng_area_task for processing an area from one dng_image to an + * area of another. + */ + +/*****************************************************************************/ + +#ifndef __dng_filter_task__ +#define __dng_filter_task__ + +/*****************************************************************************/ + +#include "dng_area_task.h" +#include "dng_auto_ptr.h" +#include "dng_point.h" +#include "dng_rect.h" +#include "dng_sdk_limits.h" + +/*****************************************************************************/ + +/// \brief Represents a task which filters an area of a source dng_image to an area +/// of a destination dng_image. + +class dng_filter_task: public dng_area_task + { + + protected: + + const dng_image &fSrcImage; + + dng_image &fDstImage; + + uint32 fSrcPlane; + uint32 fSrcPlanes; + uint32 fSrcPixelType; + + uint32 fDstPlane; + uint32 fDstPlanes; + uint32 fDstPixelType; + + dng_point fSrcRepeat; + dng_point fSrcTileSize; + + AutoPtr fSrcBuffer [kMaxMPThreads]; + AutoPtr fDstBuffer [kMaxMPThreads]; + + public: + + /// Construct a filter task given a source and destination images. + /// \param srcImage Image from which source pixels are read. + /// \param dstImage Image to which result pixels are written. + + dng_filter_task (const char *name, + const dng_image &srcImage, + dng_image &dstImage); + + virtual ~dng_filter_task (); + + /// Compute the source area needed for a given destination area. Default + /// implementation assumes destination area is equal to source area for all + /// cases. + /// + /// \param dstArea Area to for which pixels will be computed. + /// + /// \retval The source area needed as input to calculate the requested + /// destination area. + + virtual dng_rect SrcArea (const dng_rect &dstArea) + { + return dstArea; + } + + /// Given a destination tile size, calculate input tile size. Simlar to + /// SrcArea, and should seldom be overridden. + /// + /// \param dstTileSize The destination tile size that is targeted for output. + /// + /// \retval The source tile size needed to compute a tile of the destination + /// size. + + virtual dng_point SrcTileSize (const dng_point &dstTileSize) + { + return SrcArea (dng_rect (dstTileSize)).Size (); + } + + /// Implements filtering operation from one buffer to another. Source and + /// destination pixels are set up in member fields of this class. Ideally, no + /// allocation should be done in this routine. + /// + /// \param threadIndex The thread on which this routine is being called, + /// between 0 and threadCount - 1 for the threadCount passed to Start method. + /// + /// \param srcBuffer Input area and source pixels. + /// + /// \param dstBuffer Output area and destination pixels. + + virtual void ProcessArea (uint32 threadIndex, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer) = 0; + + /// Called prior to processing on specific threads. Can be used to allocate + /// per-thread memory buffers, etc. + /// + /// \param threadCount Total number of threads that will be used for + /// processing. Less than or equal to MaxThreads of dng_area_task. + /// + /// \param tileSize Size of source tiles which will be processed. (Not all + /// tiles will be this size due to edge conditions.) + /// + /// \param allocator dng_memory_allocator to use for allocating temporary + /// buffers, etc. + /// + /// \param sniffer Sniffer to test for user cancellation and to set up + /// progress. + + virtual void Start (uint32 threadCount, + const dng_rect &dstArea, + const dng_point &tileSize, + dng_memory_allocator *allocator, + dng_abort_sniffer *sniffer); + + /// Process one tile or partitioned area. Should not be overridden. Instead, + /// override ProcessArea, which is where to implement filter processing for a + /// specific type of dng_filter_task. There is no allocator parameter as all + /// allocation should be done in Start. + /// + /// \param threadIndex 0 to threadCount - 1 index indicating which thread + /// this is. (Can be used to get a thread-specific buffer allocated in the + /// Start method.) + /// + /// \param area Size of tiles to be used for sizing buffers, etc. (Edges of + /// processing can be smaller.) + /// + /// \param sniffer dng_abort_sniffer to use to check for user cancellation + /// and progress updates. + + virtual void Process (uint32 threadIndex, + const dng_rect &area, + dng_abort_sniffer *sniffer); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_fingerprint.cpp b/dng/dng_fingerprint.cpp new file mode 100644 index 0000000..cd6f652 --- /dev/null +++ b/dng/dng_fingerprint.cpp @@ -0,0 +1,618 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_fingerprint.h" + +#include "dng_assertions.h" +#include "dng_flags.h" + +/*****************************************************************************/ + +dng_fingerprint::dng_fingerprint () + { + + for (uint32 j = 0; j < kDNGFingerprintSize; j++) + { + + data [j] = 0; + + } + + } + +/*****************************************************************************/ + +dng_fingerprint::dng_fingerprint (const char *hex) + { + + if (!hex || strlen (hex) != kDNGFingerprintSize * 2 || !FromUtf8HexString (hex)) + { + + Clear (); + + } + + } + +/*****************************************************************************/ + +bool dng_fingerprint::IsNull () const + { + + for (uint32 j = 0; j < kDNGFingerprintSize; j++) + { + + if (data [j] != 0) + { + + return false; + + } + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_fingerprint::operator== (const dng_fingerprint &print) const + { + + for (uint32 j = 0; j < kDNGFingerprintSize; j++) + { + + if (data [j] != print.data [j]) + { + + return false; + + } + + } + + return true; + + } + +/******************************************************************************/ + +bool dng_fingerprint::operator< (const dng_fingerprint &print) const + { + + for (uint32 j = 0; j < kDNGFingerprintSize; j++) + { + + if (data [j] != print.data [j]) + { + + return data [j] < print.data [j]; + + } + + } + + return false; + + } + +/******************************************************************************/ + +uint32 dng_fingerprint::Collapse32 () const + { + + uint32 x = 0; + + for (uint32 j = 0; j < 4; j++) + { + + uint32 y = 0; + + for (uint32 k = 0; k < 4; k++) + { + + y = (y << 8) + (uint32) data [j * 4 + k]; + + } + + x = x ^ y; + + } + + return x; + + } + +/******************************************************************************/ + +static char NumToHexChar (unsigned int c) + { + + if (c < 10) + { + return (char) ('0' + c); + } + + else + { + return (char) ('A' + c - 10); + } + + } + +/*****************************************************************************/ + +void dng_fingerprint::ToUtf8HexString (char resultStr [2 * kDNGFingerprintSize + 1]) const + { + + for (size_t i = 0; i < kDNGFingerprintSize; i++) + { + + unsigned char c = data [i]; + + resultStr [i * 2 ] = NumToHexChar (c >> 4); + resultStr [i * 2 + 1] = NumToHexChar (c & 15); + + } + + resultStr [kDNGFingerprintSize * 2] = '\0'; + + } + +/******************************************************************************/ + +static int HexCharToNum (char hexChar) + { + + if (hexChar >= '0' && hexChar <= '9') + { + return hexChar - '0'; + } + + else if (hexChar >= 'A' && hexChar <= 'F') + { + return hexChar - 'A' + 10; + } + + else if (hexChar >= 'a' && hexChar <= 'f') + { + return hexChar - 'a' + 10; + } + + return -1; + + } + +/*****************************************************************************/ + +bool dng_fingerprint::FromUtf8HexString (const char inputStr [2 * kDNGFingerprintSize + 1]) + { + + for (size_t i = 0; i < kDNGFingerprintSize; i++) + { + + int highNibble = HexCharToNum (inputStr [i * 2]); + + if (highNibble < 0) + { + return false; + } + + int lowNibble = HexCharToNum (inputStr [i * 2 + 1]); + + if (lowNibble < 0) + { + return false; + } + + data [i] = (uint8) ((highNibble << 4) + lowNibble); + + } + + return true; + + } + +/******************************************************************************/ + +// Derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm + +// Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +// rights reserved. +// +// License to copy and use this software is granted provided that it +// is identified as the "RSA Data Security, Inc. MD5 Message-Digest +// Algorithm" in all material mentioning or referencing this software +// or this function. +// +// License is also granted to make and use derivative works provided +// that such works are identified as "derived from the RSA Data +// Security, Inc. MD5 Message-Digest Algorithm" in all material +// mentioning or referencing the derived work. +// +// RSA Data Security, Inc. makes no representations concerning either +// the merchantability of this software or the suitability of this +// software for any particular purpose. It is provided "as is" +// without express or implied warranty of any kind. +// +// These notices must be retained in any copies of any part of this +// documentation and/or software. + +/******************************************************************************/ + +dng_md5_printer::dng_md5_printer () + + : final (false) + , result () + + { + + Reset (); + + } + +/******************************************************************************/ + +void dng_md5_printer::Reset () + { + + // No bits processed yet. + + count [0] = 0; + count [1] = 0; + + // Load magic initialization constants. + + state [0] = 0x67452301; + state [1] = 0xefcdab89; + state [2] = 0x98badcfe; + state [3] = 0x10325476; + + // Not finalized yet. + + final = false; + + } + +/******************************************************************************/ + +void dng_md5_printer::Process (const void *data, + uint32 inputLen) + { + + DNG_ASSERT (!final, "Fingerprint already finalized!"); + + const uint8 *input = (const uint8 *) data; + + // Compute number of bytes mod 64 + + uint32 index = (count [0] >> 3) & 0x3F; + + // Update number of bits + + if ((count [0] += inputLen << 3) < (inputLen << 3)) + { + count [1]++; + } + + count [1] += inputLen >> 29; + + // Transform as many times as possible. + + uint32 i = 0; + + uint32 partLen = 64 - index; + + if (inputLen >= partLen) + { + + memcpy (&buffer [index], + input, + partLen); + + MD5Transform (state, buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + { + + MD5Transform (state, &input [i]); + + } + + index = 0; + + } + + // Buffer remaining input + + memcpy (&buffer [index], + &input [i], + inputLen - i); + + } + +/******************************************************************************/ + +const dng_fingerprint & dng_md5_printer::Result () + { + + if (!final) + { + + static uint8 PADDING [64] = + { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + // Save number of bits + + uint8 bits [8]; + + Encode (bits, count, 8); + + // Pad out to 56 mod 64. + + uint32 index = (count [0] >> 3) & 0x3f; + + uint32 padLen = (index < 56) ? (56 - index) : (120 - index); + + Process (PADDING, padLen); + + // Append length (before padding) + + Process (bits, 8); + + // Store state in digest + + Encode (result.data, state, 16); + + // We are now finalized. + + final = true; + + } + + return result; + + } + +/******************************************************************************/ + +// Encodes input (uint32) into output (uint8). Assumes len is +// a multiple of 4. + +void dng_md5_printer::Encode (uint8 *output, + const uint32 *input, + uint32 len) + { + + uint32 i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + { + output [j ] = (uint8) ((input [i] ) & 0xff); + output [j+1] = (uint8) ((input [i] >> 8) & 0xff); + output [j+2] = (uint8) ((input [i] >> 16) & 0xff); + output [j+3] = (uint8) ((input [i] >> 24) & 0xff); + } + + } + +/******************************************************************************/ + +// Decodes input (uint8) into output (uint32). Assumes len is +// a multiple of 4. + +void dng_md5_printer::Decode (uint32 *output, + const uint8 *input, + uint32 len) + { + + // Check for non-aligned case. + + if (((uintptr) input) & 3) + { + + uint32 i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + { + + output [i] = (((uint32) input [j ]) ) | + (((uint32) input [j+1]) << 8) | + (((uint32) input [j+2]) << 16) | + (((uint32) input [j+3]) << 24); + + } + + } + + // Else use optimized code for aligned case. + + else + { + + len = len >> 2; + + const uint32 *sPtr = (const uint32 *) input; + + uint32 *dPtr = output; + + while (len--) + { + + #if qDNGBigEndian + + uint32 data = *(sPtr++); + + data = (data >> 24) | + ((data >> 8) & 0x0000FF00) | + ((data << 8) & 0x00FF0000) | + (data << 24); + + *(dPtr++) = data; + + #else + + *(dPtr++) = *(sPtr++); + + #endif + + } + + } + + } + +/******************************************************************************/ + +// MD5 basic transformation. Transforms state based on block. + +DNG_ATTRIB_NO_SANITIZE("unsigned-integer-overflow") +void dng_md5_printer::MD5Transform (uint32 state [4], + const uint8 block [64]) + { + + enum + { + S11 = 7, + S12 = 12, + S13 = 17, + S14 = 22, + S21 = 5, + S22 = 9, + S23 = 14, + S24 = 20, + S31 = 4, + S32 = 11, + S33 = 16, + S34 = 23, + S41 = 6, + S42 = 10, + S43 = 15, + S44 = 21 + }; + + #if qDNGBigEndian + + uint32 x [16]; + + Decode (x, block, 64); + + #else + + uint32 temp [16]; + + const uint32 *x; + + if (((uintptr) block) & 3) + { + + Decode (temp, block, 64); + + x = temp; + + } + + else + x = (const uint32 *) block; + + #endif + + uint32 a = state [0]; + uint32 b = state [1]; + uint32 c = state [2]; + uint32 d = state [3]; + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state [0] += a; + state [1] += b; + state [2] += c; + state [3] += d; + + } + +/*****************************************************************************/ + +// End of RSA Data Security, Inc. derived code. + +/*****************************************************************************/ diff --git a/dng/dng_fingerprint.h b/dng/dng_fingerprint.h new file mode 100644 index 0000000..809c3c3 --- /dev/null +++ b/dng/dng_fingerprint.h @@ -0,0 +1,400 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Fingerprint (cryptographic hashing) support for generating strong hashes of image + * data. + */ + +/*****************************************************************************/ + +#ifndef __dng_fingerprint__ +#define __dng_fingerprint__ + +/*****************************************************************************/ + +#include "dng_exceptions.h" +#include "dng_types.h" +#include "dng_stream.h" + +#include + +/*****************************************************************************/ + +/// \brief Container fingerprint (MD5 only at present). + +class dng_fingerprint + { + + public: + + static const size_t kDNGFingerprintSize = 16; + + uint8 data [kDNGFingerprintSize]; + + public: + + dng_fingerprint (); + + dng_fingerprint (const char *hex); + + /// Check if fingerprint is all zeros. + + bool IsNull () const; + + /// Same as IsNull but expresses intention of testing validity. + + bool IsValid () const + { + return !IsNull (); + } + + /// Set to all zeros, a value used to indicate an invalid fingerprint. + + void Clear () + { + *this = dng_fingerprint (); + } + + /// Test if two fingerprints are equal. + + bool operator== (const dng_fingerprint &print) const; + + /// Test if two fingerprints are not equal. + + bool operator!= (const dng_fingerprint &print) const + { + return !(*this == print); + } + + /// Comparision test for fingerprints. + + bool operator< (const dng_fingerprint &print) const; + + /// Produce a 32-bit hash value from fingerprint used for faster hashing of + /// fingerprints. + + uint32 Collapse32 () const; + + /// Convert fingerprint to UTF-8 string. + /// + /// \param resultStr The output array to which the UTF-8 encoding of the + /// fingerprint will be written. + + void ToUtf8HexString (char resultStr [2 * kDNGFingerprintSize + 1]) const; + + /// Convert UTF-8 string to fingerprint. Returns true on success, false on + /// failure. + /// + /// \param inputStr The input array from which the UTF-8 encoding of the + /// fingerprint will be read. + /// + /// \retval True indicates success. + + bool FromUtf8HexString (const char inputStr [2 * kDNGFingerprintSize + 1]); + + }; + +/*****************************************************************************/ + +/// \brief Utility to compare fingerprints (e.g., for sorting). + +struct dng_fingerprint_less_than + { + + /// Less-than comparison. + + bool operator() (const dng_fingerprint &a, + const dng_fingerprint &b) const + { + + return memcmp (a.data, + b.data, + sizeof (a.data)) < 0; + + } + + }; + +/******************************************************************************/ + +/// \brief Utility to hash fingerprints (e.g., for hashtables). + +struct dng_fingerprint_hash + { + + /// Hash function. + + size_t operator () (const dng_fingerprint &digest) const + { + + return (size_t) digest.Collapse32 (); + + } + + }; + +/******************************************************************************/ + +// Derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm. + +// Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +// rights reserved. +// +// License to copy and use this software is granted provided that it +// is identified as the "RSA Data Security, Inc. MD5 Message-Digest +// Algorithm" in all material mentioning or referencing this software +// or this function. +// +// License is also granted to make and use derivative works provided +// that such works are identified as "derived from the RSA Data +// Security, Inc. MD5 Message-Digest Algorithm" in all material +// mentioning or referencing the derived work. +// +// RSA Data Security, Inc. makes no representations concerning either +// the merchantability of this software or the suitability of this +// software for any particular purpose. It is provided "as is" +// without express or implied warranty of any kind. +// +// These notices must be retained in any copies of any part of this +// documentation and/or software. + +/// \brief Class to hash binary data to a fingerprint using the MD5 Message-Digest +/// Algorithm. + +class dng_md5_printer + { + + public: + + dng_md5_printer (); + + virtual ~dng_md5_printer () + { + } + + /// Reset the fingerprint. + + void Reset (); + + /// Append the data to the stream to be hashed. + /// \param data The data to be hashed. + /// \param inputLen The length of data, in bytes. + + void Process (const void *data, + uint32 inputLen); + + /// Append the string to the stream to be hashed. + /// \param text The string to be hashed. + + void Process (const char *text) + { + + Process (text, (uint32) strlen (text)); + + } + + /// Get the fingerprint (i.e., result of the hash). + + const dng_fingerprint & Result (); + + private: + + static void Encode (uint8 *output, + const uint32 *input, + uint32 len); + + static void Decode (uint32 *output, + const uint8 *input, + uint32 len); + + // F, G, H and I are basic MD5 functions. + + static inline uint32 F (uint32 x, + uint32 y, + uint32 z) + { + return (x & y) | (~x & z); + } + + static inline uint32 G (uint32 x, + uint32 y, + uint32 z) + { + return (x & z) | (y & ~z); + } + + static inline uint32 H (uint32 x, + uint32 y, + uint32 z) + { + return x ^ y ^ z; + } + + static inline uint32 I (uint32 x, + uint32 y, + uint32 z) + { + return y ^ (x | ~z); + } + + // FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. + + DNG_ATTRIB_NO_SANITIZE("unsigned-integer-overflow") + static inline void FF (uint32 &a, + uint32 b, + uint32 c, + uint32 d, + uint32 x, + uint32 s, + uint32 ac) + { + a += F (b, c, d) + x + ac; + a = (a << s) | (a >> (32 - s)); + a += b; + } + + DNG_ATTRIB_NO_SANITIZE("unsigned-integer-overflow") + static inline void GG (uint32 &a, + uint32 b, + uint32 c, + uint32 d, + uint32 x, + uint32 s, + uint32 ac) + { + a += G (b, c, d) + x + ac; + a = (a << s) | (a >> (32 - s)); + a += b; + } + + DNG_ATTRIB_NO_SANITIZE("unsigned-integer-overflow") + static inline void HH (uint32 &a, + uint32 b, + uint32 c, + uint32 d, + uint32 x, + uint32 s, + uint32 ac) + { + a += H (b, c, d) + x + ac; + a = (a << s) | (a >> (32 - s)); + a += b; + } + + DNG_ATTRIB_NO_SANITIZE("unsigned-integer-overflow") + static inline void II (uint32 &a, + uint32 b, + uint32 c, + uint32 d, + uint32 x, + uint32 s, + uint32 ac) + { + a += I (b, c, d) + x + ac; + a = (a << s) | (a >> (32 - s)); + a += b; + } + + static void MD5Transform (uint32 state [4], + const uint8 block [64]); + + private: + + uint32 state [4]; + + uint32 count [2]; + + uint8 buffer [64]; + + bool final; + + dng_fingerprint result; + + }; + +/*****************************************************************************/ + +/// \brief A dng_stream based interface to the MD5 printing logic. + +class dng_md5_printer_stream : public dng_stream, dng_md5_printer + { + + private: + + uint64 fNextOffset; + + public: + + /// Create an empty MD5 printer stream. + + dng_md5_printer_stream () + + : fNextOffset (0) + + { + } + + virtual uint64 DoGetLength () + { + + return fNextOffset; + + } + + virtual void DoRead (void * /* data */, + uint32 /* count */, + uint64 /* offset */) + { + + ThrowProgramError (); + + } + + virtual void DoSetLength (uint64 length) + { + + if (length != fNextOffset) + { + ThrowProgramError (); + } + + } + + virtual void DoWrite (const void *data, + uint32 count2, + uint64 offset) + { + + if (offset != fNextOffset) + { + ThrowProgramError (); + } + + Process (data, count2); + + fNextOffset += count2; + + } + + const dng_fingerprint & Result () + { + + Flush (); + + return dng_md5_printer::Result (); + + } + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_flags.h b/dng/dng_flags.h new file mode 100644 index 0000000..cda8867 --- /dev/null +++ b/dng/dng_flags.h @@ -0,0 +1,402 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Conditional compilation flags for DNG SDK. + * + * All conditional compilation macros for the DNG SDK begin with a lowercase 'q'. + */ + +/*****************************************************************************/ + +#ifndef __dng_flags__ +#define __dng_flags__ + +/*****************************************************************************/ + +/// \def qMacOS +/// 1 if compiling for Mac OS X. + +/// \def qWinOS +/// 1 if compiling for Windows. + +// Make sure a platform is defined + +#if !(defined(qMacOS) || defined(qWinOS) || defined(qAndroid) || defined(qiPhone) || defined(qLinux)) +#include "RawEnvironment.h" +#endif + +// This requires a force include or compiler define. These are the unique platforms. + +#if !(defined(qMacOS) || defined(qWinOS) || defined(qAndroid) || defined(qiPhone) || defined(qLinux)) +#error Unable to figure out platform +#endif + +/*****************************************************************************/ + +// Platforms. +// Zeros out any undefined platforms, so that #if can be used in place of #ifdef. + +#ifndef qMacOS +#define qMacOS 0 +#endif + +#ifndef qiPhone +#define qiPhone 0 +#endif + +#ifndef qiPhoneSimulator +#define qiPhoneSimulator 0 +#endif + +#ifndef qAndroid +#define qAndroid 0 +#endif + +#ifndef qWinOS +#define qWinOS 0 +#endif + +#ifndef qWinRT +#define qWinRT 0 +#endif + +#ifndef qLinux +#define qLinux 0 +#endif + +#ifndef qWeb +#define qWeb 0 +#endif + +/*****************************************************************************/ + +#if qiPhoneSimulator +#if !qiPhone +#error "qiPhoneSimulator set and not qiPhone" +#endif +#endif + +#if qWinRT +#if !qWinOS +#error "qWinRT set but not qWinOS" +#endif +#endif + +/*****************************************************************************/ + +// arm and neon support + +// arm detect (apple vs. win) +#if defined(__arm__) || defined(__arm64__) || defined(_M_ARM) +#define qARM 1 +#endif + +// arm_neon detect +#if defined(__ARM_NEON__) || defined(_M_ARM) +#define qARMNeon 1 +#endif + +#ifndef qARM +#define qARM 0 +#endif + +#ifndef qARMNeon +#define qARMNeon 0 +#endif + +/*****************************************************************************/ + +// Establish WIN32 and WIN64 definitions. + +#if defined(_WIN32) && !defined(WIN32) +#define WIN32 1 +#endif + +#if defined(_WIN64) && !defined(WIN64) +#define WIN64 1 +#endif + +/*****************************************************************************/ + +/// \def qDNGDebug +/// 1 if debug code is compiled in, 0 otherwise. Enables assertions and other debug +/// checks in exchange for slower processing. + +// Figure out if debug build or not. + +#ifndef qDNGDebug + +#if defined(Debug) +#define qDNGDebug Debug + +#elif defined(_DEBUG) +#define qDNGDebug _DEBUG + +#else +#define qDNGDebug 0 + +#endif +#endif + +/*****************************************************************************/ +// Support Intel Thread Building Blocks (TBB)? +// +// This flag needs to be configured via the project, because there are sources +// outside the cr_sdk (such as the CTJPEG and ACE libs) that need to use the +// same flag to determine whether to use TBB or not. +// +// By default, configure to 0 (disabled). + +#ifndef qCRSupportTBB +#define qCRSupportTBB 0 +#endif + +#if qCRSupportTBB +#ifndef TBB_DEPRECATED +#define TBB_DEPRECATED 0 +#endif +#endif + +// This is not really a switch, but rather a shorthand for determining whether +// or not we're building a particular translation unit (source file) using the +// Intel Compiler. + +#ifndef qDNGIntelCompiler +#if defined(__INTEL_COMPILER) +#define qDNGIntelCompiler (__INTEL_COMPILER >= 1700) +#else +#define qDNGIntelCompiler 0 +#endif +#endif + +/*****************************************************************************/ + +// Figure out byte order. + +/// \def qDNGBigEndian +/// 1 if this target platform is big endian (e.g. PowerPC Macintosh), else 0. +/// +/// \def qDNGLittleEndian +/// 1 if this target platform is little endian (e.g. x86 processors), else 0. + +#ifndef qDNGBigEndian + +#if defined(qDNGLittleEndian) +#define qDNGBigEndian !qDNGLittleEndian + +#elif defined(__POWERPC__) +#define qDNGBigEndian 1 + +#elif defined(__INTEL__) +#define qDNGBigEndian 0 + +#elif defined(_M_IX86) +#define qDNGBigEndian 0 + +#elif defined(_M_X64) || defined(__amd64__) +#define qDNGBigEndian 0 + +#elif defined(__LITTLE_ENDIAN__) +#define qDNGBigEndian 0 + +#elif defined(__BIG_ENDIAN__) +#define qDNGBigEndian 1 + +#elif defined(_ARM_) +#define qDNGBigEndian 0 + +#else + +#ifndef qXCodeRez +#error Unable to figure out byte order. +#endif + +#endif +#endif + +#ifndef qXCodeRez + +#ifndef qDNGLittleEndian +#define qDNGLittleEndian !qDNGBigEndian +#endif + +#endif + +/*****************************************************************************/ + +/// \def qDNG64Bit +/// 1 if this target platform uses 64-bit addresses, 0 otherwise. + +#ifndef qDNG64Bit + +#if qMacOS + +#ifdef __LP64__ +#if __LP64__ +#define qDNG64Bit 1 +#endif +#endif + +#elif qWinOS + +#ifdef WIN64 +#if WIN64 +#define qDNG64Bit 1 +#endif +#endif + +#elif qLinux + +#ifdef __LP64__ +#if __LP64__ +#define qDNG64Bit 1 +#endif +#endif + +#endif + +#ifndef qDNG64Bit +#define qDNG64Bit 0 +#endif + +#endif + +/*****************************************************************************/ + +/// \def qDNGThreadSafe +/// 1 if target platform has thread support and threadsafe libraries, 0 otherwise. + +#ifndef qDNGThreadSafe +#define qDNGThreadSafe (qMacOS || qWinOS) +#endif + +/*****************************************************************************/ + +/// \def qDNGValidateTarget +/// 1 if dng_validate command line tool is being built, 0 otherwise. + +#ifndef qDNGValidateTarget +#define qDNGValidateTarget 0 +#endif + +/*****************************************************************************/ + +/// \def qDNGValidate +/// 1 if DNG validation code is enabled, 0 otherwise. + +#ifndef qDNGValidate +#define qDNGValidate qDNGValidateTarget +#endif + +/*****************************************************************************/ + +/// \def qDNGPrintMessages +/// 1 if dng_show_message should use fprintf to stderr. 0 if it should use a platform +/// specific interrupt mechanism. + +#ifndef qDNGPrintMessages +#define qDNGPrintMessages qDNGValidate +#endif + +/*****************************************************************************/ + +// Experimental features -- work in progress for Lightroom and Camera Raw +// major releases. Turn this off for Lightroom & Camera Raw dot releases. + +#ifndef qDNGExperimental +#define qDNGExperimental 1 +#endif + +/*****************************************************************************/ + +/// \def qDNGXMPFiles +/// 1 to use XMPFiles. + +#ifndef qDNGXMPFiles +#define qDNGXMPFiles 1 +#endif + +/*****************************************************************************/ + +/// \def qDNGXMPDocOps +/// 1 to use XMPDocOps. + +#ifndef qDNGXMPDocOps +#define qDNGXMPDocOps (!qDNGValidateTarget) +#endif + +/*****************************************************************************/ + +/// \def qDNGUseLibJPEG +/// 1 to use open-source libjpeg for lossy jpeg processing. + +#ifndef qDNGUseLibJPEG +#define qDNGUseLibJPEG qDNGValidateTarget +#endif + +/*****************************************************************************/ + +#ifndef qDNGAVXSupport +#define qDNGAVXSupport ((qMacOS || qWinOS) && qDNG64Bit && !qARM && 1) +#endif + +#if qDNGAVXSupport && !(qDNG64Bit && !qARM) +#error AVX support is enabled when 64-bit support is not or ARM is +#endif + +/*****************************************************************************/ + +#ifndef qDNGSupportVC5 +#define qDNGSupportVC5 (1) +#endif + +/*****************************************************************************/ + +/// \def qDNGUsingSanitizer +/// Set to 1 when using a Sanitizer tool. + +#ifndef qDNGUsingSanitizer +#define qDNGUsingSanitizer (0) +#endif + +/*****************************************************************************/ + +#ifndef DNG_ATTRIB_NO_SANITIZE +#if qDNGUsingSanitizer && defined(__clang__) +#define DNG_ATTRIB_NO_SANITIZE(type) __attribute__((no_sanitize(type))) +#else +#define DNG_ATTRIB_NO_SANITIZE(type) +#endif +#endif + +/*****************************************************************************/ + +/// \def qDNGDepthSupport +/// 1 to add support for depth maps in DNG format. +/// Deprecated 2018-09-19. + +#ifdef __cplusplus +#define qDNGDepthSupport #error +#endif + +/*****************************************************************************/ + +/// \def qDNGPreserveBlackPoint +/// 1 to add support for non-zero black point in early raw pipeline. +/// Deprecated 2018-09-19. + +#ifdef __cplusplus +#define qDNGPreserveBlackPoint #error +#endif + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_gain_map.cpp b/dng/dng_gain_map.cpp new file mode 100644 index 0000000..2e2dd99 --- /dev/null +++ b/dng/dng_gain_map.cpp @@ -0,0 +1,644 @@ +/*****************************************************************************/ +// Copyright 2008-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_gain_map.h" + +#include "dng_exceptions.h" +#include "dng_globals.h" +#include "dng_host.h" +#include "dng_negative.h" +#include "dng_pixel_buffer.h" +#include "dng_stream.h" +#include "dng_tag_values.h" + +/*****************************************************************************/ + +class dng_gain_map_interpolator + { + + private: + + const dng_gain_map &fMap; + + dng_point_real64 fScale; + dng_point_real64 fOffset; + + int32 fColumn; + int32 fPlane; + + uint32 fRowIndex1; + uint32 fRowIndex2; + real32 fRowFract; + + int32 fResetColumn; + + real32 fValueBase; + real32 fValueStep; + real32 fValueIndex; + + public: + + dng_gain_map_interpolator (const dng_gain_map &map, + const dng_rect &mapBounds, + int32 row, + int32 column, + uint32 plane); + + real32 Interpolate () const + { + + return fValueBase + fValueStep * fValueIndex; + + } + + void Increment () + { + + if (++fColumn >= fResetColumn) + { + + ResetColumn (); + + } + + else + { + + fValueIndex += 1.0f; + + } + + } + + private: + + real32 InterpolateEntry (uint32 colIndex); + + void ResetColumn (); + + }; + +/*****************************************************************************/ + +dng_gain_map_interpolator::dng_gain_map_interpolator (const dng_gain_map &map, + const dng_rect &mapBounds, + int32 row, + int32 column, + uint32 plane) + + : fMap (map) + + , fScale (1.0 / mapBounds.H (), + 1.0 / mapBounds.W ()) + + , fOffset (0.5 - mapBounds.t, + 0.5 - mapBounds.l) + + , fColumn (column) + , fPlane (plane) + + , fRowIndex1 (0) + , fRowIndex2 (0) + , fRowFract (0.0f) + + , fResetColumn (0) + + , fValueBase (0.0f) + , fValueStep (0.0f) + , fValueIndex (0.0f) + + { + + real64 rowIndexF = (fScale.v * (row + fOffset.v) - + fMap.Origin ().v) / fMap.Spacing ().v; + + if (rowIndexF <= 0.0) + { + + fRowIndex1 = 0; + fRowIndex2 = 0; + + fRowFract = 0.0f; + + } + + else + { + + if (fMap.Points ().v < 1) + { + ThrowProgramError ("Empty gain map"); + } + + uint32 lastRow = static_cast (fMap.Points ().v - 1); + + if (rowIndexF >= static_cast (lastRow)) + { + + fRowIndex1 = lastRow; + fRowIndex2 = fRowIndex1; + + fRowFract = 0.0f; + + } + + else + { + + // If we got here, we know that rowIndexF can safely be converted to + // a uint32 and that static_cast (rowIndexF) < lastRow. This + // implies fRowIndex2 <= lastRow below. + + fRowIndex1 = static_cast (rowIndexF); + fRowIndex2 = fRowIndex1 + 1; + + fRowFract = (real32) (rowIndexF - (real64) fRowIndex1); + + } + + } + + ResetColumn (); + + } + +/*****************************************************************************/ + +real32 dng_gain_map_interpolator::InterpolateEntry (uint32 colIndex) + { + + return fMap.Entry (fRowIndex1, colIndex, fPlane) * (1.0f - fRowFract) + + fMap.Entry (fRowIndex2, colIndex, fPlane) * ( fRowFract); + + } + +/*****************************************************************************/ + +void dng_gain_map_interpolator::ResetColumn () + { + + real64 colIndexF = ((fScale.h * (fColumn + fOffset.h)) - + fMap.Origin ().h) / fMap.Spacing ().h; + + if (colIndexF <= 0.0) + { + + fValueBase = InterpolateEntry (0); + + fValueStep = 0.0f; + + fResetColumn = (int32) ceil (fMap.Origin ().h / fScale.h - fOffset.h); + + } + + else + { + + if (fMap.Points ().h < 1) + { + ThrowProgramError ("Empty gain map"); + } + + uint32 lastCol = static_cast (fMap.Points ().h - 1); + + if (colIndexF >= static_cast (lastCol)) + { + + fValueBase = InterpolateEntry (lastCol); + + fValueStep = 0.0f; + + fResetColumn = 0x7FFFFFFF; + + } + + else + { + + // If we got here, we know that colIndexF can safely be converted + // to a uint32 and that static_cast (colIndexF) < lastCol. + // This implies colIndex + 1 <= lastCol, i.e. the argument to + // InterpolateEntry() below is valid. + + uint32 colIndex = static_cast (colIndexF); + + real64 base = InterpolateEntry (colIndex); + real64 delta = InterpolateEntry (colIndex + 1) - base; + + fValueBase = (real32) (base + delta * (colIndexF - (real64) colIndex)); + + fValueStep = (real32) ((delta * fScale.h) / fMap.Spacing ().h); + + fResetColumn = (int32) ceil (((colIndex + 1) * fMap.Spacing ().h + + fMap.Origin ().h) / fScale.h - fOffset.h); + + } + + } + + fValueIndex = 0.0f; + + } + +/*****************************************************************************/ + +DNG_ATTRIB_NO_SANITIZE("unsigned-integer-overflow") +dng_gain_map::dng_gain_map (dng_memory_allocator &allocator, + const dng_point &points, + const dng_point_real64 &spacing, + const dng_point_real64 &origin, + uint32 planes) + + : fPoints (points) + , fSpacing (spacing) + , fOrigin (origin) + , fPlanes (planes) + + , fRowStep (planes * points.h) + + , fBuffer () + + { + + fBuffer.Reset (allocator.Allocate (ComputeBufferSize (ttFloat, + fPoints, + fPlanes, + padSIMDBytes))); + + } + +/*****************************************************************************/ + +real32 dng_gain_map::Interpolate (int32 row, + int32 col, + uint32 plane, + const dng_rect &bounds) const + { + + dng_gain_map_interpolator interp (*this, + bounds, + row, + col, + plane); + + return interp.Interpolate (); + + } + +/*****************************************************************************/ + +uint32 dng_gain_map::PutStreamSize () const + { + + return 44 + fPoints.v * fPoints.h * fPlanes * 4; + + } + +/*****************************************************************************/ + +void dng_gain_map::PutStream (dng_stream &stream) const + { + + stream.Put_uint32 (fPoints.v); + stream.Put_uint32 (fPoints.h); + + stream.Put_real64 (fSpacing.v); + stream.Put_real64 (fSpacing.h); + + stream.Put_real64 (fOrigin.v); + stream.Put_real64 (fOrigin.h); + + stream.Put_uint32 (fPlanes); + + for (int32 rowIndex = 0; rowIndex < fPoints.v; rowIndex++) + { + + for (int32 colIndex = 0; colIndex < fPoints.h; colIndex++) + { + + for (uint32 plane = 0; plane < fPlanes; plane++) + { + + stream.Put_real32 (Entry (rowIndex, + colIndex, + plane)); + + } + + } + + } + + } + +/*****************************************************************************/ + +dng_gain_map * dng_gain_map::GetStream (dng_host &host, + dng_stream &stream) + { + + dng_point mapPoints; + + mapPoints.v = stream.Get_uint32 (); + mapPoints.h = stream.Get_uint32 (); + + dng_point_real64 mapSpacing; + + mapSpacing.v = stream.Get_real64 (); + mapSpacing.h = stream.Get_real64 (); + + dng_point_real64 mapOrigin; + + mapOrigin.v = stream.Get_real64 (); + mapOrigin.h = stream.Get_real64 (); + + uint32 mapPlanes = stream.Get_uint32 (); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Points: v=%d, h=%d\n", + (int) mapPoints.v, + (int) mapPoints.h); + + printf ("Spacing: v=%.6f, h=%.6f\n", + mapSpacing.v, + mapSpacing.h); + + printf ("Origin: v=%.6f, h=%.6f\n", + mapOrigin.v, + mapOrigin.h); + + printf ("Planes: %u\n", + (unsigned) mapPlanes); + + } + + #endif + + if (mapPoints.v == 1) + { + mapSpacing.v = 1.0; + mapOrigin.v = 0.0; + } + + if (mapPoints.h == 1) + { + mapSpacing.h = 1.0; + mapOrigin.h = 0.0; + } + + if (mapPoints.v < 1 || + mapPoints.h < 1 || + mapSpacing.v <= 0.0 || + mapSpacing.h <= 0.0 || + mapPlanes < 1) + { + ThrowBadFormat (); + } + + AutoPtr map (new dng_gain_map (host.Allocator (), + mapPoints, + mapSpacing, + mapOrigin, + mapPlanes)); + + #if qDNGValidate + + uint32 linesPrinted = 0; + uint32 linesSkipped = 0; + + #endif + + for (int32 rowIndex = 0; rowIndex < mapPoints.v; rowIndex++) + { + + for (int32 colIndex = 0; colIndex < mapPoints.h; colIndex++) + { + + for (uint32 plane = 0; plane < mapPlanes; plane++) + { + + real32 x = stream.Get_real32 (); + + map->Entry (rowIndex, colIndex, plane) = x; + + #if qDNGValidate + + if (gVerbose) + { + + if (linesPrinted < gDumpLineLimit) + { + + printf (" Map [%3u] [%3u] [%u] = %.4f\n", + (unsigned) rowIndex, + (unsigned) colIndex, + (unsigned) plane, + x); + + linesPrinted++; + + } + + else + linesSkipped++; + + } + + #endif + + } + + } + + } + + #if qDNGValidate + + if (linesSkipped) + { + + printf (" ... %u map entries skipped\n", (unsigned) linesSkipped); + + } + + #endif + + return map.Release (); + + } + +/*****************************************************************************/ + +dng_opcode_GainMap::dng_opcode_GainMap (const dng_area_spec &areaSpec, + AutoPtr &gainMap) + + : dng_inplace_opcode (dngOpcode_GainMap, + dngVersion_1_3_0_0, + kFlag_None) + + , fAreaSpec (areaSpec) + + , fGainMap () + + { + + fGainMap.Reset (gainMap.Release ()); + + } + +/*****************************************************************************/ + +dng_opcode_GainMap::dng_opcode_GainMap (dng_host &host, + dng_stream &stream) + + : dng_inplace_opcode (dngOpcode_GainMap, + stream, + "GainMap") + + , fAreaSpec () + + , fGainMap () + + { + + uint32 byteCount = stream.Get_uint32 (); + + uint64 startPosition = stream.Position (); + + fAreaSpec.GetData (stream); + + fGainMap.Reset (dng_gain_map::GetStream (host, stream)); + + if (stream.Position () != startPosition + byteCount) + { + ThrowBadFormat (); + } + + } + +/*****************************************************************************/ + +void dng_opcode_GainMap::PutData (dng_stream &stream) const + { + + stream.Put_uint32 (dng_area_spec::kDataSize + + fGainMap->PutStreamSize ()); + + fAreaSpec.PutData (stream); + + fGainMap->PutStream (stream); + + } + +/*****************************************************************************/ + +void dng_opcode_GainMap::ProcessArea (dng_negative &negative, + uint32 /* threadIndex */, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect &imageBounds) + { + + dng_rect overlap = fAreaSpec.Overlap (dstArea); + + if (overlap.NotEmpty ()) + { + + uint16 blackLevel = (Stage () >= 2) ? negative.Stage3BlackLevel () : 0; + + real32 blackScale1 = 1.0f; + real32 blackScale2 = 1.0f; + real32 blackOffset1 = 0.0f; + real32 blackOffset2 = 0.0f; + + if (blackLevel != 0) + { + + blackOffset2 = ((real32) blackLevel) / 65535.0f; + blackScale2 = 1.0f - blackOffset2; + blackScale1 = 1.0f / blackScale2; + blackOffset1 = 1.0f - blackScale1; + + } + + uint32 cols = overlap.W (); + + uint32 colPitch = fAreaSpec.ColPitch (); + + colPitch = Min_uint32 (colPitch, cols); + + for (uint32 plane = fAreaSpec.Plane (); + plane < fAreaSpec.Plane () + fAreaSpec.Planes () && + plane < buffer.Planes (); + plane++) + { + + uint32 mapPlane = Min_uint32 (plane, fGainMap->Planes () - 1); + + for (int32 row = overlap.t; row < overlap.b; row += fAreaSpec.RowPitch ()) + { + + real32 *dPtr = buffer.DirtyPixel_real32 (row, overlap.l, plane); + + dng_gain_map_interpolator interp (*fGainMap, + imageBounds, + row, + overlap.l, + mapPlane); + + if (blackLevel != 0) + { + + for (uint32 col = 0; col < cols; col += colPitch) + { + + dPtr [col] = dPtr [col] * blackScale1 + blackOffset1; + + } + + } + + for (uint32 col = 0; col < cols; col += colPitch) + { + + real32 gain = interp.Interpolate (); + + dPtr [col] = Min_real32 (dPtr [col] * gain, 1.0f); + + for (uint32 j = 0; j < colPitch; j++) + { + interp.Increment (); + } + + } + + if (blackLevel != 0) + { + + for (uint32 col = 0; col < cols; col += colPitch) + { + + dPtr [col] = dPtr [col] * blackScale2 + blackOffset2; + + } + + } + + } + + } + + } + + } + +/*****************************************************************************/ diff --git a/dng/dng_gain_map.h b/dng/dng_gain_map.h new file mode 100644 index 0000000..3595a65 --- /dev/null +++ b/dng/dng_gain_map.h @@ -0,0 +1,200 @@ +/*****************************************************************************/ +// Copyright 2008-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Opcode to fix 2D uniformity defects, such as shading. + */ + +/*****************************************************************************/ + +#ifndef __dng_gain_map__ +#define __dng_gain_map__ + +/*****************************************************************************/ + +#include "dng_memory.h" +#include "dng_misc_opcodes.h" +#include "dng_tag_types.h" +#include "dng_uncopyable.h" + +/*****************************************************************************/ + +/// \brief Holds a discrete (i.e., sampled) 2D representation of a gain map. This is +/// effectively an image containing scale factors. + +class dng_gain_map: private dng_uncopyable + { + + private: + + dng_point fPoints; + + dng_point_real64 fSpacing; + + dng_point_real64 fOrigin; + + uint32 fPlanes; + + uint32 fRowStep; + + AutoPtr fBuffer; + + public: + + /// Construct a gain map with the specified memory allocator, number of + /// samples (points), sample spacing, origin, and number of color planes. + + dng_gain_map (dng_memory_allocator &allocator, + const dng_point &points, + const dng_point_real64 &spacing, + const dng_point_real64 &origin, + uint32 planes); + + /// The number of samples in the horizontal and vertical directions. + + const dng_point & Points () const + { + return fPoints; + } + + /// The space between adjacent samples in the horizontal and vertical + /// directions. + + const dng_point_real64 & Spacing () const + { + return fSpacing; + } + + /// The 2D coordinate for the first (i.e., top-left-most) sample. + + const dng_point_real64 & Origin () const + { + return fOrigin; + } + + /// The number of color planes. + + uint32 Planes () const + { + return fPlanes; + } + + /// Getter for a gain map sample (specified by row, column, and plane). + + real32 & Entry (uint32 rowIndex, + uint32 colIndex, + uint32 plane) + { + + return *(fBuffer->Buffer_real32 () + + rowIndex * fRowStep + + colIndex * fPlanes + + plane); + + } + + /// Getter for a gain map sample (specified by row index, column index, and + /// plane index). + + const real32 & Entry (uint32 rowIndex, + uint32 colIndex, + uint32 plane) const + { + + return *(fBuffer->Buffer_real32 () + + rowIndex * fRowStep + + colIndex * fPlanes + + plane); + + } + + /// Compute the interpolated gain (i.e., scale factor) at the specified pixel + /// position and color plane, within the specified image bounds (in pixels). + + real32 Interpolate (int32 row, + int32 col, + uint32 plane, + const dng_rect &bounds) const; + + /// The number of bytes needed to hold the gain map data. + + uint32 PutStreamSize () const; + + /// Write the gain map to the specified stream. + + void PutStream (dng_stream &stream) const; + + /// Read a gain map from the specified stream. + + static dng_gain_map * GetStream (dng_host &host, + dng_stream &stream); + + }; + +/*****************************************************************************/ + +/// \brief An opcode to fix 2D spatially-varying light falloff or color casts (i.e., +/// uniformity issues). This is commonly due to shading. + +class dng_opcode_GainMap: public dng_inplace_opcode, + private dng_uncopyable + { + + private: + + dng_area_spec fAreaSpec; + + AutoPtr fGainMap; + + public: + + /// Construct a GainMap opcode for the specified image area and the specified + /// gain map. + + dng_opcode_GainMap (const dng_area_spec &areaSpec, + AutoPtr &gainMap); + + /// Construct a GainMap opcode from the specified stream. + + dng_opcode_GainMap (dng_host &host, + dng_stream &stream); + + /// Write the opcode to the specified stream. + + virtual void PutData (dng_stream &stream) const; + + /// The pixel data type of this opcode. + + virtual uint32 BufferPixelType (uint32 /* imagePixelType */) + { + return ttFloat; + } + + /// The adjusted bounds (processing area) of this opcode. It is limited to + /// the intersection of the specified image area and the GainMap area. + + virtual dng_rect ModifiedBounds (const dng_rect &imageBounds) + { + return fAreaSpec.Overlap (imageBounds); + } + + /// Apply the gain map. + + virtual void ProcessArea (dng_negative &negative, + uint32 threadIndex, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect &imageBounds); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_globals.cpp b/dng/dng_globals.cpp new file mode 100644 index 0000000..9e2b38f --- /dev/null +++ b/dng/dng_globals.cpp @@ -0,0 +1,52 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_globals.h" +#include "dng_simd_type.h" + +/*****************************************************************************/ + +#if qDNGValidate + +bool gVerbose = false; + +uint32 gDumpLineLimit = 100; + +#endif + +/******************************************************************************/ + +bool gDNGUseFakeTimeZonesInXMP = false; + +/*****************************************************************************/ + +bool gDNGShowTimers = true; + +/*****************************************************************************/ + +uint32 gDNGStreamBlockSize = 4096; + +uint32 gDNGMaxStreamBufferSize = 1024 * 1024; + +/*****************************************************************************/ + +bool gImagecore = false; + +bool gPrintTimings = false; + +bool gPrintAsserts = true; + +bool gBreakOnAsserts = true; + +/*****************************************************************************/ + +// This is declared in dng_simd_type.h + +SIMDType gDNGMaxSIMD = Scalar; + +/*****************************************************************************/ diff --git a/dng/dng_globals.h b/dng/dng_globals.h new file mode 100644 index 0000000..0f11ed0 --- /dev/null +++ b/dng/dng_globals.h @@ -0,0 +1,85 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Definitions of global variables controling DNG SDK behavior. + */ + +/*****************************************************************************/ + +#ifndef __dng_globals__ +#define __dng_globals__ + +/*****************************************************************************/ + +#include "dng_flags.h" +#include "dng_types.h" + +/*****************************************************************************/ + +#if qDNGValidate + +/// When validation (qValidate) is turned on, this global enables verbose +/// output about DNG tags and other properties. + +extern bool gVerbose; + +/// When validation (qValidate) is turned on, and verbose mode (gVerbose) is +/// enabled, limits the number of lines of text that are dumped for each tag. + +extern uint32 gDumpLineLimit; + +#endif + +/*****************************************************************************/ + +// Print out results from dng_timers? + +extern bool gDNGShowTimers; + +/******************************************************************************/ + +// MWG says don't use fake time zones in XMP, but there is some +// old software that requires them to work correctly. + +extern bool gDNGUseFakeTimeZonesInXMP; + +/*****************************************************************************/ + +// Stream block size. Choose a size that the OS likes for file system +// efficent read/write alignment. + +extern uint32 gDNGStreamBlockSize; + +// Maximum stream buffer size to use on large reads and writes. + +extern uint32 gDNGMaxStreamBufferSize; + +/*****************************************************************************/ + +// Are we running as part of the imagecore library? + +extern bool gImagecore; + +// Print out timing info for area tasks? + +extern bool gPrintTimings; + +// Print assert messages? + +extern bool gPrintAsserts; + +// Break into debugger on asserts? + +extern bool gBreakOnAsserts; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_host.cpp b/dng/dng_host.cpp new file mode 100644 index 0000000..017f1c7 --- /dev/null +++ b/dng/dng_host.cpp @@ -0,0 +1,587 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_host.h" + +#include "dng_abort_sniffer.h" +#include "dng_area_task.h" +#include "dng_bad_pixels.h" +#include "dng_exceptions.h" +#include "dng_exif.h" +#include "dng_gain_map.h" +#include "dng_ifd.h" +#include "dng_lens_correction.h" +#include "dng_memory.h" +#include "dng_misc_opcodes.h" +#include "dng_negative.h" +#include "dng_resample.h" +#include "dng_shared.h" +#include "dng_simple_image.h" +#include "dng_xmp.h" + +/*****************************************************************************/ + +dng_host::dng_host (dng_memory_allocator *allocator, + dng_abort_sniffer *sniffer) + + : fAllocator (allocator) + , fSniffer (sniffer) + + , fNeedsMeta (true) + , fNeedsImage (true) + , fForPreview (false) + , fMinimumSize (0) + , fPreferredSize (0) + , fMaximumSize (0) + , fCropFactor (1.0) + , fSaveDNGVersion (dngVersion_None) + , fSaveLinearDNG (false) + , fKeepOriginalFile (false) + , fForFastSaveToDNG (false) + , fFastSaveToDNGSize (0) + , fPreserveStage2 (false) + + { + + } + +/*****************************************************************************/ + +dng_host::~dng_host () + { + + } + +/*****************************************************************************/ + +dng_memory_allocator & dng_host::Allocator () + { + + if (fAllocator) + { + + return *fAllocator; + + } + + else + { + + return gDefaultDNGMemoryAllocator; + + } + + } + +/*****************************************************************************/ + +dng_memory_block * dng_host::Allocate (uint32 logicalSize) + { + + return Allocator ().Allocate (logicalSize); + + } + +/*****************************************************************************/ + +void dng_host::SniffForAbort () + { + + dng_abort_sniffer::SniffForAbort (Sniffer ()); + + } + +/*****************************************************************************/ + +void dng_host::ValidateSizes () + { + + // The maximum size limits the other two sizes. + + if (MaximumSize ()) + { + SetMinimumSize (Min_uint32 (MinimumSize (), MaximumSize ())); + SetPreferredSize (Min_uint32 (PreferredSize (), MaximumSize ())); + } + + // If we have a preferred size, it limits the minimum size. + + if (PreferredSize ()) + { + SetMinimumSize (Min_uint32 (MinimumSize (), PreferredSize ())); + } + + // Else find default value for preferred size. + + else + { + + // If preferred size is zero, then we want the maximim + // size image. + + if (MaximumSize ()) + { + SetPreferredSize (MaximumSize ()); + } + + } + + // If we don't have a minimum size, find default. + + if (!MinimumSize ()) + { + + // A common size for embedded thumbnails is 120 by 160 pixels, + // So allow 120 by 160 pixels to be used for thumbnails when the + // preferred size is 256 pixel. + + if (PreferredSize () >= 160 && PreferredSize () <= 256) + { + SetMinimumSize (160); + } + + // Many sensors are near a multiple of 1024 pixels in size, but after + // the default crop, they are a just under. We can get an extra factor + // of size reduction if we allow a slight undershoot in the final size + // when computing large previews. + + else if (PreferredSize () >= 490 && PreferredSize () <= 512) + { + SetMinimumSize (490); + } + + else if (PreferredSize () >= 980 && PreferredSize () <= 1024) + { + SetMinimumSize (980); + } + + else if (PreferredSize () >= 1470 && PreferredSize () <= 1536) + { + SetMinimumSize (1470); + } + + else if (PreferredSize () >= 1960 && PreferredSize () <= 2048) + { + SetMinimumSize (1960); + } + + else if (PreferredSize () >= 2400 && PreferredSize () <= 2560) + { + SetMinimumSize (2400); + } + + // The following resolutions are typically on HiDPI displays where a + // greater degree of upsampling remains visually ok for previews. The + // following ratios are all based on 20% upsampling in a linear + // dimension. + + else if (PreferredSize () >= 2448 && PreferredSize () <= 2880) + { + SetMinimumSize (2448); + } + + // 1st-generation Surface Book. + + else if (PreferredSize () >= 2560 && PreferredSize () <= 3000) + { + SetMinimumSize (2560); + } + + // 4K (actually 3840). + + else if (PreferredSize () >= 3480 && PreferredSize () <= 4096) + { + SetMinimumSize (3480); + } + + // Surface Studio. + + else if (PreferredSize () >= 3824 && PreferredSize () <= 4500) + { + SetMinimumSize (3824); + } + + // 5K. + + else if (PreferredSize () >= 4352 && PreferredSize () <= 5120) + { + SetMinimumSize (4352); + } + + // 8K. + + else if (PreferredSize () >= 6528 && PreferredSize () <= 7680) + { + SetMinimumSize (6528); + } + + // Else minimum size is same as preferred size. + + else + { + SetMinimumSize (PreferredSize ()); + } + + } + + } + +/*****************************************************************************/ + +uint32 dng_host::SaveDNGVersion () const + { + + return fSaveDNGVersion; + + } + +/*****************************************************************************/ + +bool dng_host::SaveLinearDNG (const dng_negative & /* negative */) const + { + + return fSaveLinearDNG; + + } + +/*****************************************************************************/ + +bool dng_host::IsTransientError (dng_error_code code) + { + + switch (code) + { + + case dng_error_memory: + case dng_error_user_canceled: + { + return true; + } + + default: + break; + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_host::PerformAreaTask (dng_area_task &task, + const dng_rect &area, + dng_area_task_progress *progress) + { + + dng_area_task::Perform (task, + area, + &Allocator (), + Sniffer (), + progress); + + } + +/*****************************************************************************/ + +uint32 dng_host::PerformAreaTaskThreads () + { + + return 1; + + } + +/*****************************************************************************/ + +dng_exif * dng_host::Make_dng_exif () + { + + dng_exif *result = new dng_exif (); + + if (!result) + { + + ThrowMemoryFull (); + + } + + return result; + + } + +/*****************************************************************************/ + +dng_xmp * dng_host::Make_dng_xmp () + { + + dng_xmp *result = new dng_xmp (Allocator ()); + + if (!result) + { + + ThrowMemoryFull (); + + } + + return result; + + } + +/*****************************************************************************/ + +dng_shared * dng_host::Make_dng_shared () + { + + dng_shared *result = new dng_shared (); + + if (!result) + { + + ThrowMemoryFull (); + + } + + return result; + + } + +/*****************************************************************************/ + +dng_ifd * dng_host::Make_dng_ifd () + { + + dng_ifd *result = new dng_ifd (); + + if (!result) + { + + ThrowMemoryFull (); + + } + + return result; + + } + +/*****************************************************************************/ + +dng_negative * dng_host::Make_dng_negative () + { + + return dng_negative::Make (*this); + + } + +/*****************************************************************************/ + +dng_image * dng_host::Make_dng_image (const dng_rect &bounds, + uint32 planes, + uint32 pixelType) + { + + dng_image *result = new dng_simple_image (bounds, + planes, + pixelType, + Allocator ()); + + if (!result) + { + + ThrowMemoryFull (); + + } + + return result; + + } + +/*****************************************************************************/ + +dng_opcode * dng_host::Make_dng_opcode (uint32 opcodeID, + dng_stream &stream) + { + + dng_opcode *result = NULL; + + switch (opcodeID) + { + + case dngOpcode_WarpRectilinear: + { + + result = new dng_opcode_WarpRectilinear (stream); + + break; + + } + + case dngOpcode_WarpFisheye: + { + + result = new dng_opcode_WarpFisheye (stream); + + break; + + } + + case dngOpcode_FixVignetteRadial: + { + + result = new dng_opcode_FixVignetteRadial (stream); + + break; + + } + + case dngOpcode_FixBadPixelsConstant: + { + + result = new dng_opcode_FixBadPixelsConstant (stream); + + break; + + } + + case dngOpcode_FixBadPixelsList: + { + + result = new dng_opcode_FixBadPixelsList (stream); + + break; + + } + + case dngOpcode_TrimBounds: + { + + result = new dng_opcode_TrimBounds (stream); + + break; + + } + + case dngOpcode_MapTable: + { + + result = new dng_opcode_MapTable (*this, + stream); + + break; + + } + + case dngOpcode_MapPolynomial: + { + + result = new dng_opcode_MapPolynomial (stream); + + break; + + } + + case dngOpcode_GainMap: + { + + result = new dng_opcode_GainMap (*this, + stream); + + break; + + } + + case dngOpcode_DeltaPerRow: + { + + result = new dng_opcode_DeltaPerRow (*this, + stream); + + break; + + } + + case dngOpcode_DeltaPerColumn: + { + + result = new dng_opcode_DeltaPerColumn (*this, + stream); + + break; + + } + + case dngOpcode_ScalePerRow: + { + + result = new dng_opcode_ScalePerRow (*this, + stream); + + break; + + } + + case dngOpcode_ScalePerColumn: + { + + result = new dng_opcode_ScalePerColumn (*this, + stream); + + break; + + } + + default: + { + + result = new dng_opcode_Unknown (*this, + opcodeID, + stream); + + } + + } + + if (!result) + { + + ThrowMemoryFull (); + + } + + return result; + + } + +/*****************************************************************************/ + +void dng_host::ApplyOpcodeList (dng_opcode_list &list, + dng_negative &negative, + AutoPtr &image) + { + + list.Apply (*this, + negative, + image); + + } + +/*****************************************************************************/ + +void dng_host::ResampleImage (const dng_image &srcImage, + dng_image &dstImage) + { + + ::ResampleImage (*this, + srcImage, + dstImage, + srcImage.Bounds (), + dstImage.Bounds (), + dng_resample_bicubic::Get ()); + + } + +/*****************************************************************************/ diff --git a/dng/dng_host.h b/dng/dng_host.h new file mode 100644 index 0000000..5fcac43 --- /dev/null +++ b/dng/dng_host.h @@ -0,0 +1,444 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Class definition for dng_host, initial point of contact and control between + * host application and DNG SDK. + */ + +/*****************************************************************************/ + +#ifndef __dng_host__ +#define __dng_host__ + +/*****************************************************************************/ + +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_errors.h" +#include "dng_types.h" +#include "dng_uncopyable.h" + +/*****************************************************************************/ + +/// \brief The main class for communication between the application and the +/// DNG SDK. Used to customize memory allocation and other behaviors. +/// +/// dng_host allows setting parameters for the DNG conversion, mediates callback +/// style interactions between the host application and the DNG SDK, and allows +/// controlling certain internal behavior of the SDK such as memory allocation. +/// Many applications will be able to use the default implementation of dng_host +/// by just setting the dng_memory_allocator and dng_abort_sniffer in the +/// constructor. More complex interactions will require deriving a class from +/// dng_host. +/// +/// Multiple dng_host objects can be allocated in a single process. This may +/// be useful for DNG processing on separate threads. (Distinct dng_host objects +/// are completely threadsafe for read/write. The application is responsible for +/// establishing mutual exclusion for read/write access to a single dng_host +/// object if it is used in multiple threads.) + +class dng_host: private dng_uncopyable + { + + private: + + dng_memory_allocator *fAllocator; + + dng_abort_sniffer *fSniffer; + + // Does the host require all the image metadata (vs. just checking + // to see if the file is readable)? + + bool fNeedsMeta; + + // Does the host require actual image data (vs. just getting metadata + // or just checking to see if the file is readable)? + + bool fNeedsImage; + + // If we need the image data, can it be read at preview quality? + + bool fForPreview; + + // If non-zero, the minimum size (longer of the two pixel dimensions) + // image to read. If zero, or if the full size image is smaller than + // this, read the full size image. + + uint32 fMinimumSize; + + // What is the preferred size for a preview image? This can + // be slightly larger than the minimum size. Zero if we want + // the full resolution image. + + uint32 fPreferredSize; + + // What is the maximum size for a preview image? Zero if there + // is no maximum size limit. + + uint32 fMaximumSize; + + // The fraction of the image kept after a crop. This is used to + // adjust the sizes to take into account the cropping that + // will be peformed. + + real64 fCropFactor; + + // What DNG version should we keep enough data to save? + + uint32 fSaveDNGVersion; + + // Do we want to force saving to a linear DNG? + + bool fSaveLinearDNG; + + // Keep the original raw file data block? + + bool fKeepOriginalFile; + + // Is this host being used to perform a negative read for fast + // conversion to DNG? + + bool fForFastSaveToDNG; + + uint32 fFastSaveToDNGSize; + + bool fPreserveStage2; + + public: + + /// Allocate a dng_host object, possiblly with custom allocator and sniffer. + /// \param allocator Allows controlling all memory allocation done via this + /// dng_host. Defaults to singleton global dng_memory_allocator, which calls + /// new/delete dng_malloc_block for appropriate size. + /// \param sniffer Used to periodically check if pending DNG conversions + /// should be aborted and to communicate progress updates. Defaults to singleton + /// global dng_abort_sniffer, which never aborts and ignores progress updated. + + dng_host (dng_memory_allocator *allocator = NULL, + dng_abort_sniffer *sniffer = NULL); + + /// Clean up direct memory for dng_host. Memory allocator and abort sniffer + /// are not deleted. Objects such as dng_image and others returned from + /// host can still be used after host is deleted. + + virtual ~dng_host (); + + /// Getter for host's memory allocator. + + dng_memory_allocator & Allocator (); + + /// Alocate a new dng_memory_block using the host's memory allocator. + /// Uses the Allocator() property of host to allocate a new block of memory. + /// Will call ThrowMemoryFull if block cannot be allocated. + /// \param logicalSize Number of usable bytes returned dng_memory_block + /// must contain. + + virtual dng_memory_block * Allocate (uint32 logicalSize); + + /// Setter for host's abort sniffer. + + void SetSniffer (dng_abort_sniffer *sniffer) + { + fSniffer = sniffer; + } + + /// Getter for host's abort sniffer. + + dng_abort_sniffer * Sniffer () + { + return fSniffer; + } + + /// Check for pending abort. Should call ThrowUserCanceled if an abort + /// is pending. + + virtual void SniffForAbort (); + + /// Setter for flag determining whether all XMP metadata should be parsed. + /// Defaults to true. One might not want metadata when doing a quick check + /// to see if a file is readable. + /// \param needs If true, metadata is needed. + + void SetNeedsMeta (bool needs) + { + fNeedsMeta = needs; + } + + /// Getter for flag determining whether all XMP metadata should be parsed. + + bool NeedsMeta () const + { + return fNeedsMeta; + } + + /// Setter for flag determining whether DNG image data is needed. Defaults + /// to true. Image data might not be needed for applications which only + /// manipulate metadata. + /// \param needs If true, image data is needed. + + void SetNeedsImage (bool needs) + { + fNeedsImage = needs; + } + + /// Setter for flag determining whether DNG image data is needed. + + bool NeedsImage () const + { + return fNeedsImage; + } + + /// Setter for flag determining whether image should be preview quality, + /// or full quality. + /// \param preview If true, rendered images are for preview. + + void SetForPreview (bool preview) + { + fForPreview = preview; + } + + /// Getter for flag determining whether image should be preview quality. + /// Preview quality images may be rendered more quickly. Current DNG SDK + /// does not change rendering behavior based on this flag, but derived + /// versions may use this getter to choose between a slower more accurate path + /// and a faster "good enough for preview" one. Data produce with ForPreview set + /// to true should not be written back to a DNG file, except as a preview image. + + bool ForPreview () const + { + return fForPreview; + } + + /// Setter for the minimum preview size. + /// \param size Minimum pixel size (long side of image). + + void SetMinimumSize (uint32 size) + { + fMinimumSize = size; + } + + /// Getter for the minimum preview size. + + uint32 MinimumSize () const + { + return fMinimumSize; + } + + /// Setter for the preferred preview size. + /// \param size Preferred pixel size (long side of image). + + void SetPreferredSize (uint32 size) + { + fPreferredSize = size; + } + + /// Getter for the preferred preview size. + + uint32 PreferredSize () const + { + return fPreferredSize; + } + + /// Setter for the maximum preview size. + /// \param size Maximum pixel size (long side of image). + + void SetMaximumSize (uint32 size) + { + fMaximumSize = size; + } + + /// Getter for the maximum preview size. + + uint32 MaximumSize () const + { + return fMaximumSize; + } + + /// Setter for the perform fast save to DNG. + /// \param flag True if the host is being used to perform a negative + /// read for fast conversion to DNG, false otherwise. + + void SetForFastSaveToDNG (bool flag, + uint32 size) + { + fForFastSaveToDNG = flag; + fFastSaveToDNGSize = size; + } + + /// Getter for the Boolean value that indicates whether this host is + /// being used to perform a negative read for fast conversion to DNG. + + bool ForFastSaveToDNG () const + { + return fForFastSaveToDNG; + } + + uint32 FastSaveToDNGSize () const + { + return fFastSaveToDNGSize; + } + + /// Setter for the cropping factor. + /// \param cropFactor Fraction of image to be used after crop. + + void SetCropFactor (real64 cropFactor) + { + fCropFactor = cropFactor; + } + + /// Getter for the cropping factor. + + real64 CropFactor () const + { + return fCropFactor; + } + + /// Makes sures minimum, preferred, and maximum sizes are reasonable. + + void ValidateSizes (); + + /// Setter for what version to save DNG file compatible with. + /// \param version What version to save DNG file compatible with. + + void SetSaveDNGVersion (uint32 version) + { + fSaveDNGVersion = version; + } + + /// Getter for what version to save DNG file compatible with. + + virtual uint32 SaveDNGVersion () const; + + /// Setter for flag determining whether to force saving a linear DNG file. + /// \param linear If true, we should force saving a linear DNG file. + + void SetSaveLinearDNG (bool linear) + { + fSaveLinearDNG = linear; + } + + /// Getter for flag determining whether to save a linear DNG file. + + virtual bool SaveLinearDNG (const dng_negative &negative) const; + + /// Setter for flag determining whether to keep original RAW file data. + /// \param keep If true, origianl RAW data will be kept. + + void SetKeepOriginalFile (bool keep) + { + fKeepOriginalFile = keep; + } + + /// Getter for flag determining whether to keep original RAW file data. + + bool KeepOriginalFile () + { + return fKeepOriginalFile; + } + + /// Determine if an error is the result of a temporary, but planned-for + /// occurence such as user cancellation or memory exhaustion. This method is + /// sometimes used to determine whether to try and continue processing a DNG + /// file despite errors in the file format, etc. In such cases, processing will + /// be continued if IsTransientError returns false. This is so that user cancellation + /// and memory exhaustion always terminate processing. + /// \param code Error to test for transience. + + virtual bool IsTransientError (dng_error_code code); + + /// General top-level botttleneck for image processing tasks. + /// Default implementation calls dng_area_task::PerformAreaTask method on + /// task. Can be overridden in derived classes to support multiprocessing, + /// for example. + /// \param task Image processing task to perform on area. + /// \param area Rectangle over which to perform image processing task. + + virtual void PerformAreaTask (dng_area_task &task, + const dng_rect &area, + dng_area_task_progress *progress = NULL); + + /// How many multiprocessing threads does PerformAreaTask use? + /// Default implementation always returns 1 since it is single threaded. + + virtual uint32 PerformAreaTaskThreads (); + + /// Factory method for dng_exif class. Can be used to customize allocation or + /// to ensure a derived class is used instead of dng_exif. + + virtual dng_exif * Make_dng_exif (); + + /// Factory method for dng_xmp class. Can be used to customize allocation or + /// to ensure a derived class is used instead of dng_xmp. + + virtual dng_xmp * Make_dng_xmp (); + + /// Factory method for dng_shared class. Can be used to customize allocation + /// or to ensure a derived class is used instead of dng_shared. + + virtual dng_shared * Make_dng_shared (); + + /// Factory method for dng_ifd class. Can be used to customize allocation or + /// to ensure a derived class is used instead of dng_ifd. + + virtual dng_ifd * Make_dng_ifd (); + + /// Factory method for dng_negative class. Can be used to customize allocation + /// or to ensure a derived class is used instead of dng_negative. + + virtual dng_negative * Make_dng_negative (); + + /// Factory method for dng_image class. Can be used to customize allocation + /// or to ensure a derived class is used instead of dng_simple_image. + + virtual dng_image * Make_dng_image (const dng_rect &bounds, + uint32 planes, + uint32 pixelType); + + /// Factory method for parsing dng_opcode based classs. Can be used to + /// override opcode implementations. + + virtual dng_opcode * Make_dng_opcode (uint32 opcodeID, + dng_stream &stream); + + /// Factory method to apply a dng_opcode_list. Can be used to override + /// opcode list applications. + + virtual void ApplyOpcodeList (dng_opcode_list &list, + dng_negative &negative, + AutoPtr &image); + + /// Factory method to resample an image. Can be used to override + /// image method used to resample images. + + virtual void ResampleImage (const dng_image &srcImage, + dng_image &dstImage); + + /// Getter for flag determining whether we should preserve the stage 2 + /// image after building the stage 3 image. + + bool WantsPreserveStage2 () const + { + return fPreserveStage2; + } + + /// Setter for flag determining whether we should preserve the stage 2 + /// image after building the stage 3 image. + + void SetWantsPreserveStage2 (bool flag) + { + fPreserveStage2 = flag; + } + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_hue_sat_map.cpp b/dng/dng_hue_sat_map.cpp new file mode 100644 index 0000000..c0db058 --- /dev/null +++ b/dng/dng_hue_sat_map.cpp @@ -0,0 +1,405 @@ +/*****************************************************************************/ +// Copyright 2007-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_hue_sat_map.h" + +#include "dng_assertions.h" +#include "dng_auto_ptr.h" +#include "dng_bottlenecks.h" +#include "dng_exceptions.h" +#include "dng_host.h" + +/*****************************************************************************/ + +std::atomic dng_hue_sat_map::sRuntimeFingerprintCounter (0); + +/*****************************************************************************/ + +dng_hue_sat_map::dng_hue_sat_map () + + : fHueDivisions (0) + , fSatDivisions (0) + , fValDivisions (0) + , fHueStep (0) + , fValStep (0) + , fRuntimeFingerprint () + , fDeltas () + + { + + } + +/*****************************************************************************/ + +dng_hue_sat_map::dng_hue_sat_map (const dng_hue_sat_map &src) + + : fHueDivisions (0) + , fSatDivisions (0) + , fValDivisions (0) + , fHueStep (0) + , fValStep (0) + , fRuntimeFingerprint () + , fDeltas () + + { + + *this = src; + + } + +/*****************************************************************************/ + +dng_hue_sat_map &dng_hue_sat_map::operator= (const dng_hue_sat_map &rhs) + { + + if (this != &rhs) + { + + if (!rhs.IsValid ()) + { + + SetInvalid (); + + } + + else + { + + fHueDivisions = rhs.fHueDivisions; + fSatDivisions = rhs.fSatDivisions; + fValDivisions = rhs.fValDivisions; + + fHueStep = rhs.fHueStep; + fValStep = rhs.fValStep; + + fRuntimeFingerprint = rhs.fRuntimeFingerprint; + + fDeltas = rhs.fDeltas; + + } + + } + + return *this; + + } + +/*****************************************************************************/ + +dng_hue_sat_map::~dng_hue_sat_map () + { + + } + +/*****************************************************************************/ + +void dng_hue_sat_map::SetDivisions (uint32 hueDivisions, + uint32 satDivisions, + uint32 valDivisions) + { + + DNG_ASSERT (hueDivisions >= 1, "Must have at least 1 hue division."); + DNG_ASSERT (satDivisions >= 2, "Must have at least 2 sat divisions."); + + if (valDivisions == 0) + valDivisions = 1; + + if (hueDivisions == fHueDivisions && + satDivisions == fSatDivisions && + valDivisions == fValDivisions) + { + return; + } + + fHueDivisions = hueDivisions; + fSatDivisions = satDivisions; + fValDivisions = valDivisions; + + fHueStep = satDivisions; + fValStep = hueDivisions * fHueStep; + + dng_safe_uint32 size (DeltasCount ()); + + size *= (uint32) sizeof (HSBModify); + + fDeltas.Allocate (size.Get ()); + + DoZeroBytes (fDeltas.Buffer (), size.Get ()); + + fRuntimeFingerprint.Clear (); + + } + +/*****************************************************************************/ + +void dng_hue_sat_map::GetDelta (uint32 hueDiv, + uint32 satDiv, + uint32 valDiv, + HSBModify &modify) const + { + + if (hueDiv >= fHueDivisions || + satDiv >= fSatDivisions || + valDiv >= fValDivisions || + fDeltas.Buffer () == NULL) + { + + DNG_REPORT ("Bad parameters to dng_hue_sat_map::GetDelta"); + + ThrowProgramError (); + + } + + int32 offset = valDiv * fValStep + + hueDiv * fHueStep + + satDiv; + + const HSBModify *deltas = GetConstDeltas (); + + modify.fHueShift = deltas [offset].fHueShift; + modify.fSatScale = deltas [offset].fSatScale; + modify.fValScale = deltas [offset].fValScale; + + } + +/*****************************************************************************/ + +void dng_hue_sat_map::SetDeltaKnownWriteable (uint32 hueDiv, + uint32 satDiv, + uint32 valDiv, + const HSBModify &modify) + { + + if (hueDiv >= fHueDivisions || + satDiv >= fSatDivisions || + valDiv >= fValDivisions || + fDeltas.Buffer () == NULL) + { + + DNG_REPORT ("Bad parameters to dng_hue_sat_map::SetDelta"); + + ThrowProgramError (); + + } + + // Set this entry. + + int32 offset = valDiv * fValStep + + hueDiv * fHueStep + + satDiv; + + SafeGetDeltas () [offset] = modify; + + // The zero saturation entry is required to have a value scale + // of 1.0f. + + if (satDiv == 0) + { + + if (modify.fValScale != 1.0f) + { + + #if qDNGValidate + + ReportWarning ("Value scale for zero saturation entries must be 1.0"); + + #endif + + SafeGetDeltas () [offset] . fValScale = 1.0f; + + } + + } + + // If we are settings the first saturation entry and we have not + // set the zero saturation entry yet, fill in the zero saturation entry + // by extrapolating first saturation entry. + + if (satDiv == 1) + { + + HSBModify zeroSatModify; + + GetDelta (hueDiv, 0, valDiv, zeroSatModify); + + if (zeroSatModify.fValScale != 1.0f) + { + + zeroSatModify.fHueShift = modify.fHueShift; + zeroSatModify.fSatScale = modify.fSatScale; + zeroSatModify.fValScale = 1.0f; + + SetDelta (hueDiv, 0, valDiv, zeroSatModify); + + } + + } + + } + +/*****************************************************************************/ + +void dng_hue_sat_map::AssignNewUniqueRuntimeFingerprint () + { + + const uint64 uid = ++sRuntimeFingerprintCounter; + + dng_md5_printer printer; + printer.Process (&uid, sizeof (uid)); + fRuntimeFingerprint = printer.Result (); + + } + +/*****************************************************************************/ + +bool dng_hue_sat_map::operator== (const dng_hue_sat_map &rhs) const + { + + if (fHueDivisions != rhs.fHueDivisions || + fSatDivisions != rhs.fSatDivisions || + fValDivisions != rhs.fValDivisions) + return false; + + if (!IsValid ()) + return true; + + return memcmp (GetConstDeltas (), + rhs.GetConstDeltas (), + DeltasCount () * sizeof (HSBModify)) == 0; + + } + +/*****************************************************************************/ + +dng_hue_sat_map * dng_hue_sat_map::Interpolate (const dng_hue_sat_map &map1, + const dng_hue_sat_map &map2, + real64 weight1) + { + + if (weight1 >= 1.0) + { + + if (!map1.IsValid ()) + { + + DNG_REPORT ("map1 is not valid"); + + ThrowProgramError (); + + } + + return new dng_hue_sat_map (map1); + + } + + if (weight1 <= 0.0) + { + + if (!map2.IsValid ()) + { + DNG_REPORT ("map2 is not valid"); + + ThrowProgramError (); + + } + + return new dng_hue_sat_map (map2); + + } + + // Both maps must be valid if we are using both. + + if (!map1.IsValid () || !map2.IsValid ()) + { + + DNG_REPORT ("map1 or map2 is not valid"); + + ThrowProgramError (); + + } + + // Must have the same dimensions. + + if (map1.fHueDivisions != map2.fHueDivisions || + map1.fSatDivisions != map2.fSatDivisions || + map1.fValDivisions != map2.fValDivisions) + { + + DNG_REPORT ("map1 and map2 have different sizes"); + + ThrowProgramError (); + + } + + // Make table to hold interpolated results. + + AutoPtr result (new dng_hue_sat_map); + + result->SetDivisions (map1.fHueDivisions, + map1.fSatDivisions, + map1.fValDivisions); + + // Interpolate between the tables. + + real32 w1 = (real32) weight1; + real32 w2 = 1.0f - w1; + + const HSBModify *data1 = map1.GetConstDeltas (); + const HSBModify *data2 = map2.GetConstDeltas (); + + HSBModify *data3 = result->SafeGetDeltas (); + + uint32 count = map1.DeltasCount (); + + for (uint32 index = 0; index < count; index++) + { + + data3->fHueShift = w1 * data1->fHueShift + + w2 * data2->fHueShift; + + data3->fSatScale = w1 * data1->fSatScale + + w2 * data2->fSatScale; + + data3->fValScale = w1 * data1->fValScale + + w2 * data2->fValScale; + + data1++; + data2++; + data3++; + + } + + // Compute a fingerprint based on the inputs for the new dng_hue_sat_map + // so that repeated interpolations of the same objects with the same + // parameters produce the same fingerprint each time. + + { + + dng_md5_printer printer; + + printer.Process ("Interpolate", 11); + + printer.Process (&weight1, sizeof(weight1)); + + printer.Process (map1.RuntimeFingerprint ().data, + dng_fingerprint::kDNGFingerprintSize); + + printer.Process (map2.RuntimeFingerprint ().data, + dng_fingerprint::kDNGFingerprintSize); + + result->SetRuntimeFingerprint (printer.Result ()); + + } + + // Return interpolated tables. + + return result.Release (); + + } + +/*****************************************************************************/ diff --git a/dng/dng_hue_sat_map.h b/dng/dng_hue_sat_map.h new file mode 100644 index 0000000..2531e6e --- /dev/null +++ b/dng/dng_hue_sat_map.h @@ -0,0 +1,258 @@ +/*****************************************************************************/ +// Copyright 2007-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Table-based color correction data structure. + */ + +/*****************************************************************************/ + +#ifndef __dng_hue_sat_map__ +#define __dng_hue_sat_map__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_fingerprint.h" +#include "dng_ref_counted_block.h" +#include "dng_safe_arithmetic.h" +#include "dng_types.h" + +#include + +/*****************************************************************************/ + +/// \brief A 3D table that maps HSV (hue, saturation, and value) floating-point +/// input coordinates in the range [0,1] to delta signals. The table must have at +/// least 1 sample in the hue dimension, at least 2 samples in the saturation +/// dimension, and at least 1 sample in the value dimension. Tables are stored in +/// value-hue-saturation order. + +class dng_hue_sat_map + { + + public: + + /// HSV delta signal. fHueShift is a delta value specified in degrees. + /// This parameter, added to the original hue, determines the output hue. A + /// value of 0 means no change. fSatScale and fValScale are + /// scale factors that are applied to saturation and value components, + /// respectively. These scale factors, multiplied by the original saturation + /// and value, determine the output saturation and value. A scale factor of + /// 1.0 means no change. + + struct HSBModify + { + real32 fHueShift; + real32 fSatScale; + real32 fValScale; + }; + + private: + + uint32 fHueDivisions; + uint32 fSatDivisions; + uint32 fValDivisions; + + uint32 fHueStep; + uint32 fValStep; + + // This fingerprint is intended to be used for certain rendering + // optimizations. Also, it can vary from session to session. + // In general, dng_hue_sat_map objects loaded from raw data + // will be given unique values for the session while dng_hue_sat_map + // objects derived from other dng_hue_sat_map objects will be + // given fingerprint values based on their inputs so that if they + // are recomputed, they get the same value (again, in that sesssion). + + dng_fingerprint fRuntimeFingerprint; + + static std::atomic sRuntimeFingerprintCounter; + + dng_ref_counted_block fDeltas; + + HSBModify *SafeGetDeltas () + { + return (HSBModify *) fDeltas.Buffer_real32 (); + } + + public: + + /// Construct an empty (and invalid) hue sat map. + + dng_hue_sat_map (); + + /// Copy an existing hue sat map. + + dng_hue_sat_map (const dng_hue_sat_map &src); + + /// Copy an existing hue sat map. + + dng_hue_sat_map & operator= (const dng_hue_sat_map &rhs); + + /// Destructor. + + virtual ~dng_hue_sat_map (); + + /// Is this hue sat map invalid? + + bool IsNull () const + { + return !IsValid (); + } + + /// Is this hue sat map valid? + + bool IsValid () const + { + + return fHueDivisions > 0 && + fSatDivisions > 1 && + fValDivisions > 0 && + fDeltas.Buffer (); + + } + + /// Clear the hue sat map, making it invalid. + + void SetInvalid () + { + + fHueDivisions = 0; + fSatDivisions = 0; + fValDivisions = 0; + + fHueStep = 0; + fValStep = 0; + + fRuntimeFingerprint.Clear (); + + fDeltas.Clear (); + + } + + /// Get the table dimensions (number of samples in each dimension). + + void GetDivisions (uint32 &hueDivisions, + uint32 &satDivisions, + uint32 &valDivisions) const + { + hueDivisions = fHueDivisions; + satDivisions = fSatDivisions; + valDivisions = fValDivisions; + } + + /// Set the table dimensions (number of samples in each dimension). This + /// erases any existing table data. + + void SetDivisions (uint32 hueDivisions, + uint32 satDivisions, + uint32 valDivisions = 1); + + /// Get a specific table entry, specified by table indices. + + void GetDelta (uint32 hueDiv, + uint32 satDiv, + uint32 valDiv, + HSBModify &modify) const; + + /// Make sure the table is writeable. + + void EnsureWriteable () + { + fDeltas.EnsureWriteable (); + } + + /// Set a specific table entry, specified by table indices. + + void SetDelta (uint32 hueDiv, + uint32 satDiv, + uint32 valDiv, + const HSBModify &modify) + { + + EnsureWriteable (); + + SetDeltaKnownWriteable (hueDiv, + satDiv, + valDiv, + modify); + + } + + /// Same as SetDelta, without checking that the table is writeable. + + void SetDeltaKnownWriteable (uint32 hueDiv, + uint32 satDiv, + uint32 valDiv, + const HSBModify &modify); + + /// Get the total number of samples (across all dimensions). + + uint32 DeltasCount () const + { + return (dng_safe_uint32 (fValDivisions) * + dng_safe_uint32 (fHueDivisions) * + dng_safe_uint32 (fSatDivisions)).Get (); + } + + /// Direct read/write access to table entries. The entries are stored in + /// value-hue-saturation order (outer to inner). + + HSBModify *GetDeltas () + { + + EnsureWriteable (); + + return (HSBModify *) fDeltas.Buffer_real32 (); + + } + + /// Direct read-only access to table entries. The entries are stored in + /// value-hue-saturation order (outer to inner). + + const HSBModify *GetConstDeltas () const + { + return (const HSBModify *) fDeltas.Buffer_real32 (); + } + + void AssignNewUniqueRuntimeFingerprint (); + + /// Set Fingerprint. Rare use cases want to set the fingerprint. + + void SetRuntimeFingerprint (const dng_fingerprint fingerprint) + { + fRuntimeFingerprint = fingerprint; + } + + /// Get the runtime fingerprint of this hue sat map. + + const dng_fingerprint & RuntimeFingerprint () const + { + return fRuntimeFingerprint; + } + + /// Equality test. + + bool operator== (const dng_hue_sat_map &rhs) const; + + /// Compute a linearly-interpolated hue sat map (i.e., delta and scale factors) + /// from the specified tables, with the specified weight. map1 and map2 must + /// have the same dimensions. + + static dng_hue_sat_map * Interpolate (const dng_hue_sat_map &map1, + const dng_hue_sat_map &map2, + real64 weight1); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_ifd.cpp b/dng/dng_ifd.cpp new file mode 100644 index 0000000..4926aae --- /dev/null +++ b/dng/dng_ifd.cpp @@ -0,0 +1,4542 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_ifd.h" + +#include "dng_exceptions.h" +#include "dng_flags.h" +#include "dng_globals.h" +#include "dng_ifd.h" +#include "dng_types.h" +#include "dng_parse_utils.h" +#include "dng_read_image.h" +#include "dng_stream.h" +#include "dng_tag_codes.h" +#include "dng_tag_types.h" +#include "dng_tag_values.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_preview_info::dng_preview_info () + + : fIsPrimary (true) + , fApplicationName () + , fApplicationVersion () + , fSettingsName () + , fSettingsDigest () + , fColorSpace (previewColorSpace_MaxEnum) + , fDateTime () + , fRawToPreviewGain (1.0) + , fCacheVersion (0) + + { + + } + +/*****************************************************************************/ + +dng_preview_info::~dng_preview_info () + { + + } + +/*****************************************************************************/ + +dng_ifd::dng_ifd () + + : fUsesNewSubFileType (false) + , fNewSubFileType (0) + + , fImageWidth (0) + , fImageLength (0) + + , fCompression (ccUncompressed) + , fPredictor (cpNullPredictor) + + , fPhotometricInterpretation (0xFFFFFFFF) + + , fFillOrder (1) + + , fOrientation (0) + , fOrientationType (0) + , fOrientationOffset (kDNGStreamInvalidOffset) + , fOrientationBigEndian (false) + + , fSamplesPerPixel (1) + + , fPlanarConfiguration (pcInterleaved) + + , fXResolution (0.0) + , fYResolution (0.0) + , fResolutionUnit (0) + + , fUsesStrips (false) + , fUsesTiles (false) + + , fTileWidth (0) + , fTileLength (0) + + , fTileOffsetsType (0) + , fTileOffsetsCount (0) + , fTileOffsetsOffset (0) + + , fTileByteCountsType (0) + , fTileByteCountsCount (0) + , fTileByteCountsOffset (0) + + , fSubIFDsCount (0) + , fSubIFDsOffset (0) + + , fExtraSamplesCount (0) + + , fJPEGTablesCount (0) + , fJPEGTablesOffset (0) + + , fJPEGInterchangeFormat (0) + , fJPEGInterchangeFormatLength (0) + + , fYCbCrCoefficientR (0.0) + , fYCbCrCoefficientG (0.0) + , fYCbCrCoefficientB (0.0) + + , fYCbCrSubSampleH (0) + , fYCbCrSubSampleV (0) + + , fYCbCrPositioning (0) + + , fCFARepeatPatternRows (0) + , fCFARepeatPatternCols (0) + + , fCFALayout (1) + + , fLinearizationTableType (0) + , fLinearizationTableCount (0) + , fLinearizationTableOffset (0) + + , fBlackLevelRepeatRows (1) + , fBlackLevelRepeatCols (1) + + , fBlackLevelDeltaHType (0) + , fBlackLevelDeltaHCount (0) + , fBlackLevelDeltaHOffset (0) + + , fBlackLevelDeltaVType (0) + , fBlackLevelDeltaVCount (0) + , fBlackLevelDeltaVOffset (0) + + , fDefaultScaleH (1, 1) + , fDefaultScaleV (1, 1) + + , fBestQualityScale (1, 1) + + , fDefaultCropOriginH (0, 1) + , fDefaultCropOriginV (0, 1) + + , fDefaultCropSizeH () + , fDefaultCropSizeV () + + , fDefaultUserCropT (0, 1) + , fDefaultUserCropL (0, 1) + , fDefaultUserCropB (1, 1) + , fDefaultUserCropR (1, 1) + + , fBayerGreenSplit (0) + + , fChromaBlurRadius () + + , fAntiAliasStrength (1, 1) + + , fActiveArea () + + , fMaskedAreaCount (0) + + , fRowInterleaveFactor (1) + + , fSubTileBlockRows (1) + , fSubTileBlockCols (1) + + , fPreviewInfo () + + , fOpcodeList1Count (0) + , fOpcodeList1Offset (0) + + , fOpcodeList2Count (0) + , fOpcodeList2Offset (0) + + , fOpcodeList3Count (0) + , fOpcodeList3Offset (0) + + , fNoiseProfile () + + , fEnhanceParams () + + , fBaselineSharpness (0, 0) + + , fNoiseReductionApplied (0, 0) + + , fLosslessJPEGBug16 (false) + + , fSampleBitShift (0) + + , fThisIFD (0) + , fNextIFD (0) + + , fCompressionQuality (-1) + + , fPatchFirstJPEGByte (false) + + { + + uint32 j; + uint32 k; + uint32 n; + + for (j = 0; j < kMaxSamplesPerPixel; j++) + { + fBitsPerSample [j] = 0; + } + + for (j = 0; j < kMaxTileInfo; j++) + { + fTileOffset [j] = 0; + fTileByteCount [j] = 0; + } + + for (j = 0; j < kMaxSamplesPerPixel; j++) + { + fExtraSamples [j] = esUnspecified; + } + + for (j = 0; j < kMaxSamplesPerPixel; j++) + { + fSampleFormat [j] = sfUnsignedInteger; + } + + for (j = 0; j < 6; j++) + { + fReferenceBlackWhite [j] = 0.0; + } + + for (j = 0; j < kMaxCFAPattern; j++) + for (k = 0; k < kMaxCFAPattern; k++) + { + fCFAPattern [j] [k] = 255; + } + + for (j = 0; j < kMaxColorPlanes; j++) + { + fCFAPlaneColor [j] = (uint8) (j < 3 ? j : 255); + } + + for (j = 0; j < kMaxBlackPattern; j++) + for (k = 0; k < kMaxBlackPattern; k++) + for (n = 0; n < kMaxSamplesPerPixel; n++) + { + fBlackLevel [j] [k] [n] = 0.0; + } + + for (j = 0; j < kMaxSamplesPerPixel; j++) + { + fWhiteLevel [j] = -1.0; // Don't know real default yet. + } + + } + +/*****************************************************************************/ + +dng_ifd::~dng_ifd () + { + + } + +/*****************************************************************************/ + +dng_ifd * dng_ifd::Clone () const + { + + return new dng_ifd (*this); + + } + +/*****************************************************************************/ + +// Parses tags that should only appear in IFDs that contain images. + +bool dng_ifd::ParseTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset) + { + + uint32 j; + uint32 k; + uint32 n; + + switch (tagCode) + { + + case tcNewSubFileType: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fUsesNewSubFileType = true; + + fNewSubFileType = stream.TagValue_uint32 (tagType); + + fPreviewInfo.fIsPrimary = (fNewSubFileType == sfPreviewImage); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("NewSubFileType: %s\n", + LookupNewSubFileType (fNewSubFileType)); + + } + + #endif + + break; + + } + + case tcImageWidth: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fImageWidth = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("ImageWidth: %u\n", (unsigned) fImageWidth); + } + + #endif + + break; + + } + + case tcImageLength: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fImageLength = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("ImageLength: %u\n", (unsigned) fImageLength); + } + + #endif + + break; + + } + + case tcBitsPerSample: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1, 0x0FFFF); + + #if qDNGValidate + + if (gVerbose) + { + printf ("BitsPerSample:"); + } + + #endif + + bool extrasMatch = true; + + for (j = 0; j < tagCount; j++) + { + + uint32 x = stream.TagValue_uint32 (tagType); + + const uint32 maxBitsPerSample = 32; + + if (j < kMaxSamplesPerPixel) + { + + if (x > maxBitsPerSample) + { + //ThrowBadFormat ("BitsPerSample out of bounds"); + DNG_REPORT ("BitsPerSample > 32"); + x = maxBitsPerSample; + } + + fBitsPerSample [j] = x; + + } + + else if (x != fBitsPerSample [kMaxSamplesPerPixel - 1]) + { + extrasMatch = false; + } + + #if qDNGValidate + + if (gVerbose) + { + printf (" %u", (unsigned) x); + } + + #endif + + } + + #if qDNGValidate + + if (gVerbose) + { + printf ("\n"); + } + + #endif + + if (!extrasMatch) + { + + #if qDNGValidate + + ReportError ("BitsPerSample not constant"); + + #endif + + ThrowBadFormat (); + + } + + break; + + } + + case tcCompression: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fCompression = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Compression: %s\n", + LookupCompression (fCompression)); + + } + + #endif + + // Correct a common TIFF writer mistake. + + if (fCompression == 0) + { + + #if qDNGValidate + + { + + char message [256]; + + sprintf (message, + "%s has invalid zero compression code", + LookupParentCode (parentCode)); + + ReportWarning (message); + + } + + #endif + + fCompression = ccUncompressed; + + } + + break; + + } + + case tcPhotometricInterpretation: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fPhotometricInterpretation = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("PhotometricInterpretation: %s\n", + LookupPhotometricInterpretation (fPhotometricInterpretation)); + + } + + #endif + + break; + + } + + case tcFillOrder: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fFillOrder = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("FillOrder: %u\n", (unsigned) fFillOrder); + } + + #endif + + break; + + } + + case tcStripOffsets: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + fUsesStrips = true; + + fTileOffsetsType = tagType; + fTileOffsetsCount = tagCount; + fTileOffsetsOffset = tagOffset; + + if (tagCount <= kMaxTileInfo) + { + + for (j = 0; j < tagCount; j++) + { + + fTileOffset [j] = stream.TagValue_uint32 (tagType); + + } + + } + + #if qDNGValidate + + if (gVerbose) + { + + stream.SetReadPosition (tagOffset); + + DumpTagValues (stream, + "Offset", + parentCode, + tagCode, + tagType, + tagCount); + + } + + #endif + + break; + + } + + case tcOrientation: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fOrientationType = tagType; + fOrientationOffset = stream.PositionInOriginalFile (); + fOrientationBigEndian = stream.BigEndian (); + + fOrientation = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Orientation: %s\n", + LookupOrientation (fOrientation)); + + } + + #endif + + break; + + } + + case tcSamplesPerPixel: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fSamplesPerPixel = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("SamplesPerPixel: %u\n", (unsigned) fSamplesPerPixel); + } + + #endif + + break; + + } + + case tcRowsPerStrip: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fUsesStrips = true; + + fTileLength = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("RowsPerStrip: %u\n", (unsigned) fTileLength); + } + + #endif + + break; + + } + + case tcStripByteCounts: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + fUsesStrips = true; + + fTileByteCountsType = tagType; + fTileByteCountsCount = tagCount; + fTileByteCountsOffset = tagOffset; + + if (tagCount <= kMaxTileInfo) + { + + for (j = 0; j < tagCount; j++) + { + + fTileByteCount [j] = stream.TagValue_uint32 (tagType); + + } + + } + + #if qDNGValidate + + if (gVerbose) + { + + stream.SetReadPosition (tagOffset); + + DumpTagValues (stream, + "Count", + parentCode, + tagCode, + tagType, + tagCount); + + } + + #endif + + break; + + } + + case tcXResolution: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fXResolution = stream.TagValue_real64 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("XResolution: %0.2f\n", fXResolution); + } + + #endif + + break; + + } + + case tcYResolution: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fYResolution = stream.TagValue_real64 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("YResolution: %0.2f\n", fYResolution); + } + + #endif + + break; + + } + + case tcPlanarConfiguration: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fPlanarConfiguration = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("PlanarConfiguration: %u\n", (unsigned) fPlanarConfiguration); + } + + #endif + + break; + + } + + case tcResolutionUnit: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fResolutionUnit = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ResolutionUnit: %s\n", + LookupResolutionUnit (fResolutionUnit)); + + } + + #endif + + break; + + } + + case tcPredictor: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fPredictor = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Predictor: %s\n", + LookupPredictor (fPredictor)); + + } + + #endif + + break; + + } + + case tcTileWidth: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fUsesTiles = true; + + fTileWidth = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("TileWidth: %u\n", (unsigned) fTileWidth); + } + + #endif + + break; + + } + + case tcTileLength: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fUsesTiles = true; + + fTileLength = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("TileLength: %u\n", (unsigned) fTileLength); + } + + #endif + + break; + + } + + case tcTileOffsets: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + fUsesTiles = true; + + fTileOffsetsType = tagType; + fTileOffsetsCount = tagCount; + fTileOffsetsOffset = tagOffset; + + if (tagCount <= kMaxTileInfo) + { + + for (j = 0; j < tagCount; j++) + { + + fTileOffset [j] = stream.TagValue_uint32 (tagType); + + } + + } + + #if qDNGValidate + + if (gVerbose) + { + + stream.SetReadPosition (tagOffset); + + DumpTagValues (stream, + "Offset", + parentCode, + tagCode, + tagType, + tagCount); + + } + + #endif + + break; + + } + + case tcTileByteCounts: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + fUsesTiles = true; + + fTileByteCountsType = tagType; + fTileByteCountsCount = tagCount; + fTileByteCountsOffset = tagOffset; + + if (tagCount <= kMaxTileInfo) + { + + for (j = 0; j < tagCount; j++) + { + + fTileByteCount [j] = stream.TagValue_uint32 (tagType); + + } + + } + + #if qDNGValidate + + if (gVerbose) + { + + stream.SetReadPosition (tagOffset); + + DumpTagValues (stream, + "Count", + parentCode, + tagCode, + tagType, + tagCount); + + } + + #endif + + break; + + } + + case tcSubIFDs: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD); + + fSubIFDsCount = tagCount; + fSubIFDsOffset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + DumpTagValues (stream, + "IFD", + parentCode, + tagCode, + ttLong, + tagCount); + + } + + #endif + + break; + + } + + case tcExtraSamples: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1, fSamplesPerPixel); + + #if qDNGValidate + + if (gVerbose) + { + printf ("ExtraSamples:"); + } + + #endif + + fExtraSamplesCount = tagCount; + + for (j = 0; j < tagCount; j++) + { + + uint32 x = stream.TagValue_uint32 (tagType); + + if (j < kMaxSamplesPerPixel) + { + fExtraSamples [j] = x; + } + + #if qDNGValidate + + if (gVerbose) + { + printf (" %u", (unsigned) x); + } + + #endif + + } + + #if qDNGValidate + + if (gVerbose) + { + printf ("\n"); + } + + #endif + + break; + + } + + case tcSampleFormat: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, fSamplesPerPixel); + + #if qDNGValidate + + if (gVerbose) + { + printf ("SampleFormat:"); + } + + #endif + + bool extrasMatch = true; + + for (j = 0; j < tagCount; j++) + { + + uint32 x = stream.TagValue_uint32 (tagType); + + if (j < kMaxSamplesPerPixel) + { + fSampleFormat [j] = x; + } + + else if (x != fSampleFormat [kMaxSamplesPerPixel - 1]) + { + extrasMatch = false; + } + + #if qDNGValidate + + if (gVerbose) + { + printf (" %s", LookupSampleFormat (x)); + } + + #endif + + } + + #if qDNGValidate + + if (gVerbose) + { + printf ("\n"); + } + + #endif + + if (!extrasMatch) + { + + #if qDNGValidate + + ReportError ("SampleFormat not constant"); + + #endif + + ThrowBadFormat (); + + } + + break; + + } + + case tcJPEGTables: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + fJPEGTablesCount = tagCount; + fJPEGTablesOffset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("JPEGTables: count = %u, offset = %u\n", + (unsigned) fJPEGTablesCount, + (unsigned) fJPEGTablesOffset); + + } + + #endif + + break; + + } + + case tcJPEGInterchangeFormat: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fJPEGInterchangeFormat = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("JPEGInterchangeFormat: %u\n", + (unsigned) fJPEGInterchangeFormat); + } + + #endif + + break; + + } + + case tcJPEGInterchangeFormatLength: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fJPEGInterchangeFormatLength = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("JPEGInterchangeFormatLength: %u\n", + (unsigned) fJPEGInterchangeFormatLength); + } + + #endif + + break; + + } + + case tcYCbCrCoefficients: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 3)) + { + return false; + } + + fYCbCrCoefficientR = stream.TagValue_real64 (tagType); + fYCbCrCoefficientG = stream.TagValue_real64 (tagType); + fYCbCrCoefficientB = stream.TagValue_real64 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("YCbCrCoefficients: R = %0.3f, G = %0.3f, B = %0.3f\n", + fYCbCrCoefficientR, + fYCbCrCoefficientG, + fYCbCrCoefficientB); + + } + + #endif + + break; + + } + + case tcYCbCrSubSampling: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) + { + return false; + } + + fYCbCrSubSampleH = stream.TagValue_uint32 (tagType); + fYCbCrSubSampleV = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("YCbCrSubSampling: H = %u, V = %u\n", + (unsigned) fYCbCrSubSampleH, + (unsigned) fYCbCrSubSampleV); + + } + + #endif + + break; + + } + + case tcYCbCrPositioning: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fYCbCrPositioning = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("YCbCrPositioning: %u\n", + (unsigned) fYCbCrPositioning); + + } + + #endif + + break; + + } + + case tcReferenceBlackWhite: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 6)) + { + return false; + } + + for (j = 0; j < 6; j++) + { + fReferenceBlackWhite [j] = stream.TagValue_real64 (tagType); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ReferenceBlackWhite: %0.1f %0.1f %0.1f %0.1f %0.1f %0.1f\n", + fReferenceBlackWhite [0], + fReferenceBlackWhite [1], + fReferenceBlackWhite [2], + fReferenceBlackWhite [3], + fReferenceBlackWhite [4], + fReferenceBlackWhite [5]); + + } + + #endif + + break; + + } + + case tcCFARepeatPatternDim: + { + + CheckCFA (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) + { + return false; + } + + fCFARepeatPatternRows = stream.TagValue_uint32 (tagType); + fCFARepeatPatternCols = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CFARepeatPatternDim: Rows = %u, Cols = %u\n", + (unsigned) fCFARepeatPatternRows, + (unsigned) fCFARepeatPatternCols); + + } + + #endif + + break; + + } + + case tcCFAPattern: + { + + CheckCFA (parentCode, tagCode, fPhotometricInterpretation); + + if (!CheckTagType (parentCode, tagCode, tagType, ttByte)) + { + return false; + } + + if (!CheckTagCount (parentCode, tagCode, tagCount, fCFARepeatPatternRows * + fCFARepeatPatternCols)) + { + return false; + } + + if (fCFARepeatPatternRows < 1 || fCFARepeatPatternRows > kMaxCFAPattern || + fCFARepeatPatternCols < 1 || fCFARepeatPatternCols > kMaxCFAPattern) + { + return false; + } + + // Note that the Exif spec stores this array in a different + // scan order than the TIFF-EP spec. + + for (j = 0; j < fCFARepeatPatternRows; j++) + for (k = 0; k < fCFARepeatPatternCols; k++) + { + + fCFAPattern [j] [k] = stream.Get_uint8 (); + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CFAPattern:\n"); + + for (j = 0; j < fCFARepeatPatternRows; j++) + { + + int32 spaces = 4; + + for (k = 0; k < fCFARepeatPatternCols; k++) + { + + while (spaces-- > 0) + { + printf (" "); + } + + const char *name = LookupCFAColor (fCFAPattern [j] [k]); + + spaces = 9 - (int32) strlen (name); + + printf ("%s", name); + + } + + printf ("\n"); + + } + + } + + #endif + + break; + + } + + case tcCFAPlaneColor: + { + + CheckCFA (parentCode, tagCode, fPhotometricInterpretation); + + if (!CheckTagType (parentCode, tagCode, tagType, ttByte)) + { + return false; + } + + if (!CheckTagCount (parentCode, tagCode, tagCount, 3, kMaxColorPlanes)) + { + return false; + } + + for (j = 0; j < kMaxColorPlanes; j++) + { + + if (j < tagCount) + fCFAPlaneColor [j] = stream.Get_uint8 (); + + else + fCFAPlaneColor [j] = 255; + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CFAPlaneColor:"); + + for (j = 0; j < tagCount; j++) + { + + printf (" %s", LookupCFAColor (fCFAPlaneColor [j])); + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcCFALayout: + { + + CheckCFA (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fCFALayout = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CFALayout: %s\n", + LookupCFALayout (fCFALayout)); + + } + + #endif + + break; + + } + + case tcLinearizationTable: + { + + CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + fLinearizationTableType = tagType; + fLinearizationTableCount = tagCount; + fLinearizationTableOffset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + DumpTagValues (stream, + "Table", + parentCode, + tagCode, + tagType, + tagCount); + + } + + #endif + + break; + + } + + case tcBlackLevelRepeatDim: + { + + CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) + { + return false; + } + + fBlackLevelRepeatRows = stream.TagValue_uint32 (tagType); + fBlackLevelRepeatCols = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("BlackLevelRepeatDim: Rows = %u, Cols = %u\n", + (unsigned) fBlackLevelRepeatRows, + (unsigned) fBlackLevelRepeatCols); + + } + + #endif + + break; + + } + + case tcBlackLevel: + { + + CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong, ttRational); + + if (!CheckTagCount (parentCode, tagCode, tagCount, fBlackLevelRepeatRows * + fBlackLevelRepeatCols * + fSamplesPerPixel)) + { + return false; + } + + if (fBlackLevelRepeatRows < 1 || fBlackLevelRepeatRows > kMaxBlackPattern || + fBlackLevelRepeatCols < 1 || fBlackLevelRepeatCols > kMaxBlackPattern || + fSamplesPerPixel < 1 || fSamplesPerPixel > kMaxSamplesPerPixel) + { + return false; + } + + for (j = 0; j < fBlackLevelRepeatRows; j++) + for (k = 0; k < fBlackLevelRepeatCols; k++) + for (n = 0; n < fSamplesPerPixel; n++) + { + + fBlackLevel [j] [k] [n] = stream.TagValue_real64 (tagType); + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("BlackLevel:"); + + if (fBlackLevelRepeatRows == 1 && + fBlackLevelRepeatCols == 1) + { + + for (n = 0; n < fSamplesPerPixel; n++) + { + printf (" %0.2f", fBlackLevel [0] [0] [n]); + } + + printf ("\n"); + + } + + else + { + + printf ("\n"); + + for (n = 0; n < fSamplesPerPixel; n++) + { + + if (fSamplesPerPixel > 1) + { + printf (" Sample: %u\n", (unsigned) n); + } + + for (j = 0; j < fBlackLevelRepeatRows; j++) + { + + printf (" "); + + for (k = 0; k < fBlackLevelRepeatCols; k++) + { + + printf (" %8.2f", fBlackLevel [j] [k] [n]); + + } + + printf ("\n"); + + } + + } + + } + + } + + #endif + + break; + + } + + case tcBlackLevelDeltaH: + { + + CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + fBlackLevelDeltaHType = tagType; + fBlackLevelDeltaHCount = tagCount; + fBlackLevelDeltaHOffset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + DumpTagValues (stream, + "Delta", + parentCode, + tagCode, + tagType, + tagCount); + + } + + #endif + + break; + + } + + case tcBlackLevelDeltaV: + { + + CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + fBlackLevelDeltaVType = tagType; + fBlackLevelDeltaVCount = tagCount; + fBlackLevelDeltaVOffset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + DumpTagValues (stream, + "Delta", + parentCode, + tagCode, + tagType, + tagCount); + + } + + #endif + + break; + + } + + case tcWhiteLevel: + { + + CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + if (!CheckTagCount (parentCode, tagCode, tagCount, fSamplesPerPixel)) + return false; + + for (j = 0; j < tagCount && j < kMaxSamplesPerPixel; j++) + { + + fWhiteLevel [j] = stream.TagValue_real64 (tagType); + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("WhiteLevel:"); + + for (j = 0; j < tagCount && j < kMaxSamplesPerPixel; j++) + { + + printf (" %0.0f", fWhiteLevel [j]); + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcDefaultScale: + { + + CheckMainIFD (parentCode, tagCode, fNewSubFileType); + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) + return false; + + fDefaultScaleH = stream.TagValue_urational (tagType); + fDefaultScaleV = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("DefaultScale: H = %0.4f V = %0.4f\n", + fDefaultScaleH.As_real64 (), + fDefaultScaleV.As_real64 ()); + + } + + #endif + + break; + + } + + case tcDefaultCropOrigin: + { + + CheckMainIFD (parentCode, tagCode, fNewSubFileType); + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong, ttRational); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) + return false; + + fDefaultCropOriginH = stream.TagValue_urational (tagType); + fDefaultCropOriginV = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("DefaultCropOrigin: H = %0.2f V = %0.2f\n", + fDefaultCropOriginH.As_real64 (), + fDefaultCropOriginV.As_real64 ()); + + } + + #endif + + break; + + } + + case tcDefaultCropSize: + { + + CheckMainIFD (parentCode, tagCode, fNewSubFileType); + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong, ttRational); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) + return false; + + fDefaultCropSizeH = stream.TagValue_urational (tagType); + fDefaultCropSizeV = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("DefaultCropSize: H = %0.2f V = %0.2f\n", + fDefaultCropSizeH.As_real64 (), + fDefaultCropSizeV.As_real64 ()); + + } + + #endif + + break; + + } + + case tcDefaultUserCrop: + { + + CheckMainIFD (parentCode, tagCode, fNewSubFileType); + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 4)) + return false; + + fDefaultUserCropT = stream.TagValue_urational (tagType); + fDefaultUserCropL = stream.TagValue_urational (tagType); + fDefaultUserCropB = stream.TagValue_urational (tagType); + fDefaultUserCropR = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("DefaultUserCrop: T = %0.2lf L = %0.2lf B = %0.2lf R = %0.2lf\n", + (double) fDefaultUserCropT.As_real64 (), + (double) fDefaultUserCropL.As_real64 (), + (double) fDefaultUserCropB.As_real64 (), + (double) fDefaultUserCropR.As_real64 ()); + + + } + + #endif // qDNGValidate + + break; + + } + + case tcBayerGreenSplit: + { + + CheckCFA (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fBayerGreenSplit = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("BayerGreenSplit: %u\n", (unsigned) fBayerGreenSplit); + } + + #endif + + break; + + } + + case tcChromaBlurRadius: + { + + CheckMainIFD (parentCode, tagCode, fNewSubFileType); + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fChromaBlurRadius = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ChromaBlurRadius: %0.2f\n", + fChromaBlurRadius.As_real64 ()); + + } + + #endif + + break; + + } + + case tcAntiAliasStrength: + { + + CheckMainIFD (parentCode, tagCode, fNewSubFileType); + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fAntiAliasStrength = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("AntiAliasStrength: %0.2f\n", + fAntiAliasStrength.As_real64 ()); + + } + + #endif + + break; + + } + + case tcBestQualityScale: + { + + CheckMainIFD (parentCode, tagCode, fNewSubFileType); + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fBestQualityScale = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("BestQualityScale: %0.4f\n", + fBestQualityScale.As_real64 ()); + + } + + #endif + + break; + + } + + case tcActiveArea: + { + + CheckMainIFD (parentCode, tagCode, fNewSubFileType); + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 4)) + return false; + + fActiveArea.t = stream.TagValue_int32 (tagType); + fActiveArea.l = stream.TagValue_int32 (tagType); + fActiveArea.b = stream.TagValue_int32 (tagType); + fActiveArea.r = stream.TagValue_int32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ActiveArea: T = %d L = %d B = %d R = %d\n", + (int) fActiveArea.t, + (int) fActiveArea.l, + (int) fActiveArea.b, + (int) fActiveArea.r); + + } + + #endif + + break; + + } + + case tcMaskedAreas: + { + + CheckMainIFD (parentCode, tagCode, fNewSubFileType); + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + uint32 rect_count = tagCount / 4; + + if (!CheckTagCount (parentCode, tagCode, tagCount, rect_count * 4)) + return false; + + fMaskedAreaCount = rect_count; + + if (fMaskedAreaCount > kMaxMaskedAreas) + fMaskedAreaCount = kMaxMaskedAreas; + + for (j = 0; j < fMaskedAreaCount; j++) + { + + fMaskedArea [j].t = stream.TagValue_int32 (tagType); + fMaskedArea [j].l = stream.TagValue_int32 (tagType); + fMaskedArea [j].b = stream.TagValue_int32 (tagType); + fMaskedArea [j].r = stream.TagValue_int32 (tagType); + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("MaskedAreas: %u\n", (unsigned) fMaskedAreaCount); + + for (j = 0; j < fMaskedAreaCount; j++) + { + + printf (" Area [%u]: T = %d L = %d B = %d R = %d\n", + (unsigned) j, + (int) fMaskedArea [j].t, + (int) fMaskedArea [j].l, + (int) fMaskedArea [j].b, + (int) fMaskedArea [j].r); + + } + + } + + #endif + + break; + + } + + case tcPreviewApplicationName: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fPreviewInfo.fApplicationName, + false); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("PreviewApplicationName: "); + + DumpString (fPreviewInfo.fApplicationName); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcPreviewApplicationVersion: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fPreviewInfo.fApplicationVersion, + false); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("PreviewApplicationVersion: "); + + DumpString (fPreviewInfo.fApplicationVersion); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcPreviewSettingsName: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fPreviewInfo.fSettingsName, + false); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("PreviewSettingsName: "); + + DumpString (fPreviewInfo.fSettingsName); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcPreviewSettingsDigest: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttByte)) + return false; + + if (!CheckTagCount (parentCode, tagCode, tagCount, 16)) + return false; + + stream.Get (fPreviewInfo.fSettingsDigest.data, 16); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("PreviewSettingsDigest: "); + + DumpFingerprint (fPreviewInfo.fSettingsDigest); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcPreviewColorSpace: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fPreviewInfo.fColorSpace = (PreviewColorSpaceEnum) + stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("PreviewColorSpace: %s\n", + LookupPreviewColorSpace ((uint32) fPreviewInfo.fColorSpace)); + + } + + #endif + + break; + + } + + case tcPreviewDateTime: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fPreviewInfo.fDateTime, + false); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("PreviewDateTime: "); + + DumpString (fPreviewInfo.fDateTime); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcRowInterleaveFactor: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 1)) + return false; + + fRowInterleaveFactor = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("RowInterleaveFactor: %u\n", + (unsigned) fRowInterleaveFactor); + + } + + #endif + + break; + + } + + case tcSubTileBlockSize: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) + return false; + + fSubTileBlockRows = stream.TagValue_uint32 (tagType); + fSubTileBlockCols = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("SubTileBlockSize: rows = %u, cols = %u\n", + (unsigned) fSubTileBlockRows, + (unsigned) fSubTileBlockCols); + + } + + #endif + + break; + + } + + case tcOpcodeList1: + { + + CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + fOpcodeList1Count = tagCount; + fOpcodeList1Offset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("OpcodeList1: count = %u, offset = %u\n", + (unsigned) fOpcodeList1Count, + (unsigned) fOpcodeList1Offset); + + } + + #endif + + break; + + } + + case tcOpcodeList2: + { + + CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + fOpcodeList2Count = tagCount; + fOpcodeList2Offset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("OpcodeList2: count = %u, offset = %u\n", + (unsigned) fOpcodeList2Count, + (unsigned) fOpcodeList2Offset); + + } + + #endif + + break; + + } + + case tcOpcodeList3: + { + + CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + fOpcodeList3Count = tagCount; + fOpcodeList3Offset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("OpcodeList3: count = %u, offset = %u\n", + (unsigned) fOpcodeList3Count, + (unsigned) fOpcodeList3Offset); + + } + + #endif + + break; + + } + + case tcRawToPreviewGain: + { + + #if qDNGValidate + + if (fNewSubFileType != sfPreviewImage) + { + + char message [256]; + + sprintf (message, + "%s %s is not allowed IFDs with NewSubFileType != PreviewImage", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + #endif + + CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttDouble); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 1)) + return false; + + fPreviewInfo.fRawToPreviewGain = stream.TagValue_real64 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("RawToPreviewGain = %f\n", + fPreviewInfo.fRawToPreviewGain); + + } + + #endif + + break; + + } + + case tcNoiseProfile: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttDouble)) + return false; + + // This tag will be parsed even in non-raw IFDs (such as + // thumbnails, previews, etc.) to support legacy DNGs that have + // the tag in the wrong IFD, but we'll now issue a warning. + // (Turn off the warning for IFD0 since we are writing it + // there for backward compatiblity). + + if (parentCode != 0) + { + + CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation); + + } + + // Must be an even, positive number of doubles in a noise profile. + + if (!tagCount || (tagCount & 1)) + return false; + + // Determine number of planes (i.e., half the number of doubles). + + const uint32 numPlanes = Pin_uint32 (0, + tagCount >> 1, + kMaxColorPlanes); + + // Parse the noise function parameters. + + dng_std_vector noiseFunctions; + + for (uint32 i = 0; i < numPlanes; i++) + { + + const real64 scale = stream.TagValue_real64 (tagType); + const real64 offset = stream.TagValue_real64 (tagType); + + noiseFunctions.push_back (dng_noise_function (scale, offset)); + + } + + // Store the noise profile. + + fNoiseProfile = dng_noise_profile (noiseFunctions); + + // Debug. + + #if qDNGValidate + + if (gVerbose) + { + + printf ("NoiseProfile:\n"); + + printf (" Planes: %u\n", (unsigned) numPlanes); + + for (uint32 plane = 0; plane < numPlanes; plane++) + { + + printf (" Noise function for plane %u: scale = %.20lf, offset = %.20lf\n", + (unsigned) plane, + noiseFunctions [plane].Scale (), + noiseFunctions [plane].Offset ()); + + } + + } + + #endif + + break; + + } + + case tcCacheVersion: + { + + #if qDNGValidate + + if (fNewSubFileType != sfPreviewImage) + { + + char message [256]; + + sprintf (message, + "%s %s is not allowed IFDs with NewSubFileType != PreviewImage", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + #endif + + CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 1)) + return false; + + fPreviewInfo.fCacheVersion = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CacheVersion = 0x%x\n", + (unsigned) fPreviewInfo.fCacheVersion); + + } + + #endif + + break; + + } + + case tcEnhanceParams: + { + + #if qDNGValidate + + if (fNewSubFileType != sfEnhancedImage) + { + + char message [256]; + + sprintf (message, + "%s %s is not allowed IFDs with NewSubFileType != EnhancedImage", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + #endif + + CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fEnhanceParams, + false); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("EnhanceParams: "); + + DumpString (fEnhanceParams); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcBaselineSharpness: + { + + if (fNewSubFileType != sfEnhancedImage) + { + return false; + } + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fBaselineSharpness = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("BaselineSharpness (EnhancedImage): %0.2f\n", + fBaselineSharpness.As_real64 ()); + + } + + #endif + + break; + + } + + case tcNoiseReductionApplied: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttRational)) + return false; + + if (!CheckTagCount (parentCode, tagCode, tagCount, 1)) + return false; + + // This tag will be parsed even in non-raw IFDs (such as + // thumbnails, previews, etc.) to support legacy DNGs that have + // the tag in the wrong IFD, but we'll now issue a warning. + // (Turn off the warning for IFD0 since we are writing it + // there for backward compatiblity). + + if (parentCode != 0) + { + + CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation); + + } + + fNoiseReductionApplied = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("NoiseReductionApplied: %u/%u\n", + (unsigned) fNoiseReductionApplied.n, + (unsigned) fNoiseReductionApplied.d); + + } + + #endif + + break; + + } + + default: + { + + return false; + + } + + } + + return true; + + } + +/*****************************************************************************/ + +void dng_ifd::PostParse () + { + + uint32 j; + uint32 k; + + // There is only one PlanarConfiguration for single sample imaages. + + if (fSamplesPerPixel == 1) + { + fPlanarConfiguration = pcInterleaved; + } + + // Default tile size. + + if (fTileWidth == 0) + { + fTileWidth = fImageWidth; + } + + if (fTileLength == 0) + { + fTileLength = fImageLength; + } + + // Default ActiveArea. + + dng_rect imageArea (0, 0, fImageLength, fImageWidth); + + if (fActiveArea.IsZero ()) + { + fActiveArea = imageArea; + } + + // Default crop size. + + if (fDefaultCropSizeH.d == 0) + { + fDefaultCropSizeH = dng_urational (fActiveArea.W (), 1); + } + + if (fDefaultCropSizeV.d == 0) + { + fDefaultCropSizeV = dng_urational (fActiveArea.H (), 1); + } + + // Default white level. + + uint32 defaultWhite = (fSampleFormat [0] == sfFloatingPoint) ? + 1 : + (uint32) ((((uint64) 1) << fBitsPerSample [0]) - 1); + + for (j = 0; j < kMaxSamplesPerPixel; j++) + { + + if (fWhiteLevel [j] < 0.0) + { + fWhiteLevel [j] = (real64) defaultWhite; + } + + } + + // Check AntiAliasStrength. + + if (fAntiAliasStrength.As_real64 () < 0.0 || + fAntiAliasStrength.As_real64 () > 1.0) + { + + #if qDNGValidate + + ReportWarning ("Invalid AntiAliasStrength"); + + #endif + + fAntiAliasStrength = dng_urational (1, 1); + + } + + // Check MaskedAreas. + + for (j = 0; j < fMaskedAreaCount; j++) + { + + const dng_rect &r = fMaskedArea [j]; + + if (r.IsEmpty () || ((r & imageArea) != r)) + { + + #if qDNGValidate + + ReportWarning ("Invalid MaskedArea"); + + #endif + + fMaskedAreaCount = 0; + + break; + + } + + if ((r & fActiveArea).NotEmpty ()) + { + + #if qDNGValidate + + ReportWarning ("MaskedArea overlaps ActiveArea"); + + #endif + + fMaskedAreaCount = 0; + + break; + + } + + for (k = 0; k < j; k++) + { + + if ((r & fMaskedArea [k]).NotEmpty ()) + { + + #if qDNGValidate + + ReportWarning ("MaskedAreas overlap each other"); + + #endif + + fMaskedAreaCount = 0; + + break; + + } + + } + + } + + // Check NoiseProfile. + + if (!fNoiseProfile.IsValid () && fNoiseProfile.NumFunctions () != 0) + { + + #if qDNGValidate + + ReportWarning ("Invalid NoiseProfile"); + + #endif + + fNoiseProfile = dng_noise_profile (); + + } + + } + +/*****************************************************************************/ + +bool dng_ifd::IsValidCFA (dng_shared &shared, + uint32 parentCode) + { + + uint32 j; + uint32 k; + uint32 n; + + #if !qDNGValidate + + (void) parentCode; // Unused + + #endif + + if (fCFARepeatPatternRows < 1 || fCFARepeatPatternRows > kMaxCFAPattern || + fCFARepeatPatternCols < 1 || fCFARepeatPatternCols > kMaxCFAPattern) + { + + #if qDNGValidate + + ReportError ("Missing or invalid CFAPatternRepeatDim", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + uint32 count [kMaxColorPlanes]; + + for (n = 0; n < shared.fCameraProfile.fColorPlanes; n++) + { + count [n] = 0; + } + + for (j = 0; j < fCFARepeatPatternRows; j++) + { + + for (k = 0; k < fCFARepeatPatternCols; k++) + { + + bool found = false; + + for (n = 0; n < shared.fCameraProfile.fColorPlanes; n++) + { + + if (fCFAPattern [j] [k] == fCFAPlaneColor [n]) + { + found = true; + count [n] ++; + break; + } + + } + + if (!found) + { + + #if qDNGValidate + + ReportError ("CFAPattern contains colors not included in the CFAPlaneColor tag", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + } + + for (n = 0; n < shared.fCameraProfile.fColorPlanes; n++) + { + + if (count [n] == 0) + { + + #if qDNGValidate + + ReportError ("CFAPattern does not contain all the colors in the CFAPlaneColor tag", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + if (fCFALayout < 1 || fCFALayout > 9) + { + + #if qDNGValidate + + ReportError ("Invalid CFALayout", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_ifd::IsValidDNG (dng_shared &shared, + uint32 parentCode) + { + + uint32 j; + + bool isFloatingPoint = (fSampleFormat [0] == sfFloatingPoint); + + dng_rect imageArea (0, 0, fImageLength, fImageWidth); + + uint32 defaultWhite = isFloatingPoint ? + 1 : + (uint32) ((((uint64) 1) << fBitsPerSample [0]) - 1); + + bool isMonochrome = (shared.fCameraProfile.fColorPlanes == 1); + bool isColor = !isMonochrome; + + bool isMainIFD = (fNewSubFileType == sfMainImage); + + // Check NewSubFileType. + + if (!fUsesNewSubFileType) + { + + #if qDNGValidate + + ReportError ("Missing NewSubFileType", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (fNewSubFileType != sfMainImage && + fNewSubFileType != sfPreviewImage && + fNewSubFileType != sfTransparencyMask && + fNewSubFileType != sfPreviewMask && + fNewSubFileType != sfDepthMap && + fNewSubFileType != sfPreviewDepthMap && + fNewSubFileType != sfEnhancedImage && + fNewSubFileType != sfAltPreviewImage) + { + + #if qDNGValidate + + ReportError ("Unexpected NewSubFileType", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + // Check ImageWidth and ImageLength. + + if (fImageWidth < 1) + { + + #if qDNGValidate + + ReportError ("Missing or invalid ImageWidth", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (fImageLength < 1) + { + + #if qDNGValidate + + ReportError ("Missing or invalid ImageLength", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (fImageWidth > kMaxImageSide || + fImageLength > kMaxImageSide) + { + + #if qDNGValidate + + ReportWarning ("Image size is larger than supported"); + + #endif + + return false; + + } + + // Check PhotometricInterpretation. + + if (fNewSubFileType == sfTransparencyMask || + fNewSubFileType == sfPreviewMask) + { + + if (fPhotometricInterpretation != piTransparencyMask) + { + + #if qDNGValidate + + ReportError ("NewSubFileType requires PhotometricInterpretation = TransparencyMask", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + else if (fNewSubFileType == sfDepthMap || + fNewSubFileType == sfPreviewDepthMap) + { + + if (fPhotometricInterpretation != piDepth) + { + + #if qDNGValidate + + ReportError ("NewSubFileType requires PhotometricInterpretation = Depth", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + else + { + + switch (fPhotometricInterpretation) + { + + case piBlackIsZero: + case piRGB: + case piYCbCr: + { + + if (isMainIFD) + { + + #if qDNGValidate + + ReportError ("PhotometricInterpretation requires NewSubFileType = 1", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + break; + + } + + case piCFA: + { + + if (!isMainIFD) + { + + #if qDNGValidate + + ReportError ("PhotometricInterpretation requires NewSubFileType = 0", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + break; + + } + + case piLinearRaw: + break; + + default: + { + + #if qDNGValidate + + ReportError ("Missing or invalid PhotometricInterpretation", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + } + + switch (fPhotometricInterpretation) + { + + case piBlackIsZero: + { + + // Allow black in white previews even in color images since the + // raw processing software may be converting to grayscale. + + if (isColor && isMainIFD) + { + + #if qDNGValidate + + ReportError ("PhotometricInterpretation forbids use of ColorMatrix1 tag", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + break; + + } + + case piRGB: + case piYCbCr: + { + + // Allow color previews even in monochrome DNG files, since the + // raw procesing software may be adding color effects. + + break; + + } + + case piCFA: + { + + if (isMonochrome) + { + + #if qDNGValidate + + ReportError ("PhotometricInterpretation requires use of ColorMatrix1 tag", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + break; + + } + + } + + if (isFloatingPoint) + { + + if (fPhotometricInterpretation != piCFA && + fPhotometricInterpretation != piLinearRaw && + fPhotometricInterpretation != piTransparencyMask) + { + + #if qDNGValidate + + ReportError ("Floating point data requires PhotometricInterpretation CFA or LinearRaw or TransparencyMask", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + // Check SamplesPerPixel and BitsPerSample. + + uint32 minSamplesPerPixel = 1; + uint32 maxSamplesPerPixel = 1; + + uint32 minBitsPerSample = 8; + uint32 maxBitsPerSample = 16; + + switch (fPhotometricInterpretation) + { + + case piBlackIsZero: + break; + + case piRGB: + case piYCbCr: + { + minSamplesPerPixel = 3; + maxSamplesPerPixel = 3; + break; + } + + case piCFA: + { + maxSamplesPerPixel = kMaxSamplesPerPixel; + maxBitsPerSample = 32; + break; + } + + case piLinearRaw: + { + minSamplesPerPixel = shared.fCameraProfile.fColorPlanes; + maxSamplesPerPixel = shared.fCameraProfile.fColorPlanes; + maxBitsPerSample = 32; + break; + } + + case piTransparencyMask: + { + minBitsPerSample = 8; + maxBitsPerSample = 16; + break; + } + + case piDepth: + { + minBitsPerSample = 8; + maxBitsPerSample = 16; + break; + } + + } + + if (isFloatingPoint) + { + minBitsPerSample = 16; + maxBitsPerSample = 32; + } + + if (fSamplesPerPixel < minSamplesPerPixel || + fSamplesPerPixel > maxSamplesPerPixel) + { + + #if qDNGValidate + + ReportError ("Missing or invalid SamplesPerPixel", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + for (j = 0; j < kMaxSamplesPerPixel; j++) + { + + if (j < fSamplesPerPixel) + { + + if (fBitsPerSample [j] < minBitsPerSample || + fBitsPerSample [j] > maxBitsPerSample) + { + + #if qDNGValidate + + ReportError ("Missing or invalid BitsPerSample", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (isFloatingPoint && + fBitsPerSample [j] != 16 && + fBitsPerSample [j] != 24 && + fBitsPerSample [j] != 32) + { + + #if qDNGValidate + + ReportError ("Invalid BitsPerSample for floating point", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (minBitsPerSample == 8 && + maxBitsPerSample == 16 && + fBitsPerSample [j] != 8 && + fBitsPerSample [j] != 16) + { + + #if qDNGValidate + + ReportError ("Rendered previews and integer masks require 8 or 16 bits per sample", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (j > 0 && fBitsPerSample [j] != fBitsPerSample [0]) + { + + #if qDNGValidate + + ReportError ("BitsPerSample not equal for all samples", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + else + { + + if (fBitsPerSample [j] != 0) + { + + #if qDNGValidate + + ReportError ("Too many values specified in BitsPerSample", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + } + + // Check Compression. + + switch (fCompression) + { + + case ccUncompressed: + break; + + #if qDNGSupportVC5 + + case ccVc5: + break; + + #endif // qDNGSupportVC5 + + case ccJPEG: + { + + if (fPhotometricInterpretation == piRGB) + { + + #if qDNGValidate + + ReportError ("JPEG previews should use PhotometricInterpretation = YCbYb", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (fBitsPerSample [0] > 16) + { + + #if qDNGValidate + + ReportError ("JPEG compression is limited to 16 bits/sample", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + break; + + } + + case ccLossyJPEG: + { + + if (fPhotometricInterpretation != piLinearRaw) + { + + #if qDNGValidate + + ReportError ("Lossy JPEG compression code requires PhotometricInterpretation = LinearRaw", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (fBitsPerSample [0] != 8) + { + + #if qDNGValidate + + ReportError ("Lossy JPEG compression is limited to 8 bits/sample", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + break; + + } + + case ccDeflate: + { + + if (!isFloatingPoint && + fBitsPerSample [0] != 32 && + fPhotometricInterpretation != piTransparencyMask && + fPhotometricInterpretation != piDepth) + { + + #if qDNGValidate + + ReportError ("ZIP compression is limited to floating point, 32-bit integer," + " transparency masks, and depth maps", + LookupParentCode (parentCode)); + + #endif + + } + + break; + + } + + default: + { + + #if qDNGValidate + + ReportError ("Unsupported Compression", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + // Check Predictor. + + if (isFloatingPoint && fCompression == ccDeflate && + (fPredictor == cpFloatingPoint || + fPredictor == cpFloatingPointX2 || + fPredictor == cpFloatingPointX4)) + { + + // These combinations are supported. + + } + + else if (!isFloatingPoint && fCompression == ccDeflate && + (fPredictor == cpHorizontalDifference || + fPredictor == cpHorizontalDifferenceX2 || + fPredictor == cpHorizontalDifferenceX4)) + { + + // These combinations are supported. + + } + + else if (fPredictor != cpNullPredictor) + { + + #if qDNGValidate + + ReportError ("Unsupported Predictor", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + // Check FillOrder. + + if (fFillOrder != 1) + { + + #if qDNGValidate + + ReportError ("Unsupported FillOrder", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + // Check PlanarConfiguration. + + if (fPlanarConfiguration != pcInterleaved) + { + + #if qDNGValidate + + ReportError ("Unsupported PlanarConfiguration", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + // Check ExtraSamples. + + if (fExtraSamplesCount != 0) + { + + #if qDNGValidate + + ReportError ("Unsupported ExtraSamples", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + // Check SampleFormat. + + for (j = 0; j < fSamplesPerPixel; j++) + { + + if (fSampleFormat [j] != (isFloatingPoint ? sfFloatingPoint : sfUnsignedInteger)) + { + + #if qDNGValidate + + ReportError ("Unsupported SampleFormat", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + // Check Orientation. + + if (fOrientation > 9) + { + + #if qDNGValidate + + ReportError ("Unknown Orientation", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + #if qDNGValidate + + if (fOrientation != 0 && parentCode != 0) + { + + ReportWarning ("Unexpected Orientation tag", + LookupParentCode (parentCode)); + + } + + if (fOrientation == 0 && parentCode == 0) + { + + ReportWarning ("Missing Orientation tag", + LookupParentCode (parentCode)); + + } + + #endif + + // Check Strips vs. Tiles. + + if (!fUsesStrips && !fUsesTiles) + { + + #if qDNGValidate + + ReportError ("IFD uses neither strips nor tiles", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (fUsesStrips && fUsesTiles) + { + + #if qDNGValidate + + ReportError ("IFD uses both strips and tiles", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + // Check tile info. + + uint32 tilesWide = (fImageWidth + fTileWidth - 1) / fTileWidth; + uint32 tilesHigh = (fImageLength + fTileLength - 1) / fTileLength; + + uint32 tileCount = tilesWide * tilesHigh; + + if (fTileOffsetsCount != tileCount) + { + + #if qDNGValidate + + ReportError ("Missing or invalid Strip/TileOffsets", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (fTileByteCountsCount != tileCount) + { + + #if qDNGValidate + + ReportError ("Missing or invalid Strip/TileByteCounts", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + // Check CFA pattern. + + if (fPhotometricInterpretation == piCFA) + { + + if (!IsValidCFA (shared, parentCode)) + { + + return false; + + } + + } + + // Check ActiveArea. + + if (((fActiveArea & imageArea) != fActiveArea) || fActiveArea.IsEmpty ()) + { + + #if qDNGValidate + + ReportError ("Invalid ActiveArea", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (fActiveArea != imageArea) + { + + if (shared.fDNGBackwardVersion < dngVersion_1_1_0_0) + { + + #if qDNGValidate + + ReportError ("Non-default ActiveArea tag not allowed in this DNG version", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + // Check LinearizationTable. + + if (fLinearizationTableCount) + { + + if (fLinearizationTableType != ttShort) + { + + #if qDNGValidate + + ReportError ("Invalidate LinearizationTable type", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (fLinearizationTableCount < 2 || + fLinearizationTableCount > 65536) + { + + #if qDNGValidate + + ReportError ("Invalidate LinearizationTable count", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (isFloatingPoint || fBitsPerSample [0] > 16) + { + + #if qDNGValidate + + ReportError ("Linearization table not allowed for this data type", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + // Check BlackLevelRepeatDim. + + if (fBlackLevelRepeatRows < 1 || fBlackLevelRepeatRows > kMaxBlackPattern || + fBlackLevelRepeatCols < 1 || fBlackLevelRepeatCols > kMaxBlackPattern) + { + + #if qDNGValidate + + ReportError ("Invalid BlackLevelRepeatDim", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + // Check BlackLevelDeltaH. + + if (fBlackLevelDeltaHCount != 0 && + fBlackLevelDeltaHCount != fActiveArea.W ()) + { + + #if qDNGValidate + + ReportError ("Invalid BlackLevelDeltaH count", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + // Check BlackLevelDeltaV. + + if (fBlackLevelDeltaVCount != 0 && + fBlackLevelDeltaVCount != fActiveArea.H ()) + { + + #if qDNGValidate + + ReportError ("Invalid BlackLevelDeltaV count", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + // Check WhiteLevel. + + real64 maxWhite = fLinearizationTableCount ? 65535.0 + : (real64) defaultWhite; + + for (j = 0; j < fSamplesPerPixel; j++) + { + + if (fWhiteLevel [j] < 1.0 || (fWhiteLevel [j] > maxWhite && !isFloatingPoint)) + { + + #if qDNGValidate + + ReportError ("Invalid WhiteLevel", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + // Check BlackLevel. + + for (j = 0; j < kMaxBlackPattern; j++) + { + + for (uint32 k = 0; k < kMaxBlackPattern; k++) + { + + for (uint32 s = 0; s < kMaxSamplesPerPixel; s++) + { + + const real64 black = fBlackLevel [j][k][s]; + + if (black >= fWhiteLevel [s]) + { + + #if qDNGValidate + + ReportError ("Invalid BlackLevel", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + } + + } + + // Check DefaultScale. + + if (fDefaultScaleH.As_real64 () <= 0.0 || + fDefaultScaleV.As_real64 () <= 0.0) + { + + #if qDNGValidate + + ReportError ("Invalid DefaultScale"); + + #endif + + return false; + + } + + // Check BestQualityScale. + + if (fBestQualityScale.As_real64 () < 1.0) + { + + #if qDNGValidate + + ReportError ("Invalid BestQualityScale"); + + #endif + + return false; + + } + + // Check DefaultCropOrigin. + + if (fDefaultCropOriginH.As_real64 () < 0.0 || + fDefaultCropOriginV.As_real64 () < 0.0 || + fDefaultCropOriginH.As_real64 () >= (real64) fActiveArea.W () || + fDefaultCropOriginV.As_real64 () >= (real64) fActiveArea.H ()) + { + + #if qDNGValidate + + ReportError ("Invalid DefaultCropOrigin"); + + #endif + + return false; + + } + + // Check DefaultCropSize. + + if (fDefaultCropSizeH.As_real64 () <= 0.0 || + fDefaultCropSizeV.As_real64 () <= 0.0 || + fDefaultCropSizeH.As_real64 () > (real64) fActiveArea.W () || + fDefaultCropSizeV.As_real64 () > (real64) fActiveArea.H ()) + { + + #if qDNGValidate + + ReportError ("Invalid DefaultCropSize"); + + #endif + + return false; + + } + + // Check DefaultCrop area. + + if (fDefaultCropOriginH.As_real64 () + + fDefaultCropSizeH .As_real64 () > (real64) fActiveArea.W () || + fDefaultCropOriginV.As_real64 () + + fDefaultCropSizeV .As_real64 () > (real64) fActiveArea.H ()) + { + + #if qDNGValidate + + ReportError ("Default crop extends outside ActiveArea"); + + #endif + + return false; + + } + + // Check DefaultUserCrop. + + if (fDefaultUserCropT.As_real64 () < 0.0 || + fDefaultUserCropL.As_real64 () < 0.0 || + fDefaultUserCropB.As_real64 () > 1.0 || + fDefaultUserCropR.As_real64 () > 1.0 || + fDefaultUserCropT.As_real64 () >= fDefaultUserCropB.As_real64 () || + fDefaultUserCropL.As_real64 () >= fDefaultUserCropR.As_real64 ()) + { + + #if qDNGValidate + + ReportError ("Invalid DefaultUserCrop"); + + #endif // qDNGValidate + + return false; + + } + + // The default crop and default user crop tags are not allowed for the + // non-main image. If they are there, at least require that they be NOPs. + + if (!isMainIFD) + { + + if (Round_int32 (fDefaultCropOriginH.As_real64 ()) != 0 || + Round_int32 (fDefaultCropOriginV.As_real64 ()) != 0) + { + + #if qDNGValidate + + ReportError ("non-default DefaultCropOrigin on non-main image"); + + #endif + + return false; + + } + + if (Round_int32 (fDefaultCropSizeH.As_real64 ()) != (int32) fImageWidth || + Round_int32 (fDefaultCropSizeV.As_real64 ()) != (int32) fImageLength) + { + + #if qDNGValidate + + ReportError ("non-default DefaultCropSize on non-main image"); + + #endif + + return false; + + } + + if (fDefaultUserCropT.As_real64 () != 0.0 || + fDefaultUserCropL.As_real64 () != 0.0 || + fDefaultUserCropB.As_real64 () != 1.0 || + fDefaultUserCropR.As_real64 () != 1.0) + { + + #if qDNGValidate + + ReportError ("non-default DefaultCUserCrop on non-main image"); + + #endif // qDNGValidate + + return false; + + } + + } + + // Warning if too little padding on CFA image. + + #if qDNGValidate + + if (fPhotometricInterpretation == piCFA) + { + + const real64 kMinPad = 1.9; + + if (fDefaultCropOriginH.As_real64 () < kMinPad) + { + + ReportWarning ("Too little padding on left edge of CFA image", + "possible interpolation artifacts"); + + } + + if (fDefaultCropOriginV.As_real64 () < kMinPad) + { + + ReportWarning ("Too little padding on top edge of CFA image", + "possible interpolation artifacts"); + + } + + if (fDefaultCropOriginH.As_real64 () + + fDefaultCropSizeH .As_real64 () > (real64) fActiveArea.W () - kMinPad) + { + + ReportWarning ("Too little padding on right edge of CFA image", + "possible interpolation artifacts"); + + } + + if (fDefaultCropOriginV.As_real64 () + + fDefaultCropSizeV .As_real64 () > (real64) fActiveArea.H () - kMinPad) + { + + ReportWarning ("Too little padding on bottom edge of CFA image", + "possible interpolation artifacts"); + + } + + } + + #endif + + // Check RowInterleaveFactor + + if (fRowInterleaveFactor != 1) + { + + if (fRowInterleaveFactor < 1 || + fRowInterleaveFactor > fImageLength) + { + + #if qDNGValidate + + ReportError ("RowInterleaveFactor out of valid range", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (shared.fDNGBackwardVersion < dngVersion_1_2_0_0) + { + + #if qDNGValidate + + ReportError ("Non-default RowInterleaveFactor tag not allowed in this DNG version", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + // Check SubTileBlockSize + + if (fSubTileBlockRows != 1 || fSubTileBlockCols != 1) + { + + if (fSubTileBlockRows < 2 || fSubTileBlockRows > fTileLength || + fSubTileBlockCols < 1 || fSubTileBlockCols > fTileWidth) + { + + #if qDNGValidate + + ReportError ("SubTileBlockSize out of valid range", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if ((fTileLength % fSubTileBlockRows) != 0 || + (fTileWidth % fSubTileBlockCols) != 0) + { + + #if qDNGValidate + + ReportError ("TileSize not exact multiple of SubTileBlockSize", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (shared.fDNGBackwardVersion < dngVersion_1_2_0_0) + { + + #if qDNGValidate + + ReportError ("Non-default SubTileBlockSize tag not allowed in this DNG version", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + return true; + + } + +/*****************************************************************************/ + +uint32 dng_ifd::TilesAcross () const + { + + if (fTileWidth) + { + + uint64 width64 = (uint64) fTileWidth; + + return (uint32) (((fImageWidth + width64) - 1) / width64); + + } + + return 0; + + } + +/*****************************************************************************/ + +uint32 dng_ifd::TilesDown () const + { + + if (fTileLength) + { + + // Use 64-bit math to prevent overflow. RowsPerStrip (assigned to + // fImageLength during parsing) may be 2^32 - 1, indicating a single + // strip. + + uint64 length64 = (uint64) fTileLength; + + return (uint32) (((fImageLength + length64) - 1) / length64); + + } + + return 0; + + } + +/*****************************************************************************/ + +uint32 dng_ifd::TilesPerImage () const + { + + uint32 total = TilesAcross () * TilesDown (); + + if (fPlanarConfiguration == pcPlanar) + { + + total *= fSamplesPerPixel; + + } + + return total; + + } + +/*****************************************************************************/ + +dng_rect dng_ifd::TileArea (uint32 rowIndex, + uint32 colIndex) const + { + + dng_rect r; + + r.t = rowIndex * fTileLength; + r.b = r.t + fTileLength; + + r.l = colIndex * fTileWidth; + r.r = r.l + fTileWidth; + + // If this IFD is using strips rather than tiles, the last strip + // is trimmed so it does not extend beyond the end of the image. + + if (fUsesStrips) + { + + r.b = Min_uint32 (r.b, fImageLength); + + } + + return r; + + } + +/*****************************************************************************/ + +uint32 dng_ifd::TileByteCount (const dng_rect &tile) const + { + + if (fCompression == ccUncompressed) + { + + uint32 bitsPerRow = tile.W () * + fBitsPerSample [0]; + + if (fPlanarConfiguration == pcInterleaved) + { + + bitsPerRow *= fSamplesPerPixel; + + } + + uint32 bytesPerRow = (bitsPerRow + 7) >> 3; + + if (fPlanarConfiguration == pcRowInterleaved) + { + + bytesPerRow *= fSamplesPerPixel; + + } + + return bytesPerRow * tile.H (); + + } + + return 0; + + } + +/*****************************************************************************/ + +void dng_ifd::SetSingleStrip () + { + + fTileWidth = fImageWidth; + fTileLength = fImageLength; + + fUsesTiles = false; + fUsesStrips = true; + + } + +/*****************************************************************************/ + +void dng_ifd::FindTileSize (uint32 bytesPerTile, + uint32 cellH, + uint32 cellV) + { + + uint32 bytesPerSample = fSamplesPerPixel * + ((fBitsPerSample [0] + 7) >> 3); + + uint32 samplesPerTile = bytesPerTile / bytesPerSample; + + uint32 tileSide = Round_uint32 (sqrt ((real64) samplesPerTile)); + + fTileWidth = Min_uint32 (fImageWidth, tileSide); + + uint32 across = TilesAcross (); + + DNG_REQUIRE (across > 0, "Bad number of tiles across in dng_ifd::FindTileSize"); + + fTileWidth = (fImageWidth + across - 1) / across; + + fTileWidth = ((fTileWidth + cellH - 1) / cellH) * cellH; + + fTileLength = Pin_uint32 (1, + samplesPerTile / fTileWidth, + fImageLength); + + uint32 down = TilesDown (); + + fTileLength = (fImageLength + down - 1) / down; + + fTileLength = ((fTileLength + cellV - 1) / cellV) * cellV; + + fUsesTiles = true; + fUsesStrips = false; + + } + +/*****************************************************************************/ + +void dng_ifd::FindStripSize (uint32 bytesPerStrip, + uint32 cellV) + { + + uint32 bytesPerSample = fSamplesPerPixel * + ((fBitsPerSample [0] + 7) >> 3); + + uint32 samplesPerStrip = bytesPerStrip / bytesPerSample; + + fTileWidth = fImageWidth; + + fTileLength = Pin_uint32 (1, + samplesPerStrip / fTileWidth, + fImageLength); + + uint32 down = TilesDown (); + + fTileLength = (fImageLength + down - 1) / down; + + fTileLength = ((fTileLength + cellV - 1) / cellV) * cellV; + + fUsesTiles = false; + fUsesStrips = true; + + } + +/*****************************************************************************/ + +uint32 dng_ifd::PixelType () const + { + + if (fSampleFormat [0] == sfFloatingPoint) + { + return ttFloat; + } + + if (fBitsPerSample [0] <= 8) + { + return ttByte; + } + + else if (fBitsPerSample [0] <= 16) + { + return ttShort; + } + + return ttLong; + + } + +/*****************************************************************************/ + +bool dng_ifd::IsBaselineJPEG () const + { + + if (fBitsPerSample [0] != 8) + { + return false; + } + + if (fSampleFormat [0] != sfUnsignedInteger) + { + return false; + } + + if (fCompression == ccLossyJPEG) + { + return true; + } + + if (fCompression != ccJPEG) + { + return false; + } + + switch (fPhotometricInterpretation) + { + + case piBlackIsZero: + { + return (fSamplesPerPixel == 1); + } + + case piYCbCr: + { + return (fSamplesPerPixel == 3 ) && + (fPlanarConfiguration == pcInterleaved); + } + + default: + break; + + } + + return false; + + } + +/*****************************************************************************/ + +bool dng_ifd::CanRead () const + { + + dng_read_image reader; + + return reader.CanRead (*this); + + } + +/*****************************************************************************/ + +void dng_ifd::ReadImage (dng_host &host, + dng_stream &stream, + dng_image &image, + dng_jpeg_image *jpegImage, + dng_fingerprint *jpegDigest) const + { + + dng_read_image reader; + + reader.Read (host, + *this, + stream, + image, + jpegImage, + jpegDigest); + + } + +/*****************************************************************************/ diff --git a/dng/dng_ifd.h b/dng/dng_ifd.h new file mode 100644 index 0000000..2253807 --- /dev/null +++ b/dng/dng_ifd.h @@ -0,0 +1,311 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * DNG image file directory support. + */ + +/*****************************************************************************/ + +#ifndef __dng_ifd__ +#define __dng_ifd__ + +/*****************************************************************************/ + +#include "dng_fingerprint.h" +#include "dng_negative.h" +#include "dng_rect.h" +#include "dng_shared.h" +#include "dng_stream.h" +#include "dng_string.h" +#include "dng_sdk_limits.h" +#include "dng_tag_values.h" + +/*****************************************************************************/ + +class dng_preview_info + { + + public: + + bool fIsPrimary; + + dng_string fApplicationName; + + dng_string fApplicationVersion; + + dng_string fSettingsName; + + dng_fingerprint fSettingsDigest; + + PreviewColorSpaceEnum fColorSpace; + + dng_string fDateTime; + + real64 fRawToPreviewGain; + + uint32 fCacheVersion; + + public: + + dng_preview_info (); + + ~dng_preview_info (); + + }; + +/*****************************************************************************/ + +/// \brief Container for a single image file directory of a digital negative. +/// +/// See \ref spec_dng "DNG 1.1.0 specification" for documentation of specific tags. + +class dng_ifd + { + + public: + + bool fUsesNewSubFileType; + + uint32 fNewSubFileType; + + uint32 fImageWidth; + uint32 fImageLength; + + uint32 fBitsPerSample [kMaxSamplesPerPixel]; + + uint32 fCompression; + + uint32 fPredictor; + + uint32 fPhotometricInterpretation; + + uint32 fFillOrder; + + uint32 fOrientation; + uint32 fOrientationType; + uint64 fOrientationOffset; + bool fOrientationBigEndian; + + uint32 fSamplesPerPixel; + + uint32 fPlanarConfiguration; + + real64 fXResolution; + real64 fYResolution; + + uint32 fResolutionUnit; + + bool fUsesStrips; + bool fUsesTiles; + + uint32 fTileWidth; + uint32 fTileLength; + + enum + { + kMaxTileInfo = 32 + }; + + uint32 fTileOffsetsType; + uint32 fTileOffsetsCount; + uint64 fTileOffsetsOffset; + uint64 fTileOffset [kMaxTileInfo]; + + uint32 fTileByteCountsType; + uint32 fTileByteCountsCount; + uint64 fTileByteCountsOffset; + uint32 fTileByteCount [kMaxTileInfo]; + + uint32 fSubIFDsCount; + uint64 fSubIFDsOffset; + + uint32 fExtraSamplesCount; + uint32 fExtraSamples [kMaxSamplesPerPixel]; + + uint32 fSampleFormat [kMaxSamplesPerPixel]; + + uint32 fJPEGTablesCount; + uint64 fJPEGTablesOffset; + + uint64 fJPEGInterchangeFormat; + uint32 fJPEGInterchangeFormatLength; + + real64 fYCbCrCoefficientR; + real64 fYCbCrCoefficientG; + real64 fYCbCrCoefficientB; + + uint32 fYCbCrSubSampleH; + uint32 fYCbCrSubSampleV; + + uint32 fYCbCrPositioning; + + real64 fReferenceBlackWhite [6]; + + uint32 fCFARepeatPatternRows; + uint32 fCFARepeatPatternCols; + + uint8 fCFAPattern [kMaxCFAPattern] [kMaxCFAPattern]; + + uint8 fCFAPlaneColor [kMaxColorPlanes]; + + uint32 fCFALayout; + + uint32 fLinearizationTableType; + uint32 fLinearizationTableCount; + uint64 fLinearizationTableOffset; + + uint32 fBlackLevelRepeatRows; + uint32 fBlackLevelRepeatCols; + + real64 fBlackLevel [kMaxBlackPattern] [kMaxBlackPattern] [kMaxSamplesPerPixel]; + + uint32 fBlackLevelDeltaHType; + uint32 fBlackLevelDeltaHCount; + uint64 fBlackLevelDeltaHOffset; + + uint32 fBlackLevelDeltaVType; + uint32 fBlackLevelDeltaVCount; + uint64 fBlackLevelDeltaVOffset; + + real64 fWhiteLevel [kMaxSamplesPerPixel]; + + dng_urational fDefaultScaleH; + dng_urational fDefaultScaleV; + + dng_urational fBestQualityScale; + + dng_urational fDefaultCropOriginH; + dng_urational fDefaultCropOriginV; + + dng_urational fDefaultCropSizeH; + dng_urational fDefaultCropSizeV; + + dng_urational fDefaultUserCropT; + dng_urational fDefaultUserCropL; + dng_urational fDefaultUserCropB; + dng_urational fDefaultUserCropR; + + uint32 fBayerGreenSplit; + + dng_urational fChromaBlurRadius; + + dng_urational fAntiAliasStrength; + + dng_rect fActiveArea; + + uint32 fMaskedAreaCount; + dng_rect fMaskedArea [kMaxMaskedAreas]; + + uint32 fRowInterleaveFactor; + + uint32 fSubTileBlockRows; + uint32 fSubTileBlockCols; + + dng_preview_info fPreviewInfo; + + uint32 fOpcodeList1Count; + uint64 fOpcodeList1Offset; + + uint32 fOpcodeList2Count; + uint64 fOpcodeList2Offset; + + uint32 fOpcodeList3Count; + uint64 fOpcodeList3Offset; + + dng_noise_profile fNoiseProfile; + + dng_string fEnhanceParams; + + dng_urational fBaselineSharpness; + + dng_urational fNoiseReductionApplied; + + bool fLosslessJPEGBug16; + + uint32 fSampleBitShift; + + uint64 fThisIFD; + uint64 fNextIFD; + + int32 fCompressionQuality; + + bool fPatchFirstJPEGByte; + + public: + + dng_ifd (); + + virtual ~dng_ifd (); + + virtual dng_ifd * Clone () const; + + virtual bool ParseTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset); + + virtual void PostParse (); + + virtual bool IsValidDNG (dng_shared &shared, + uint32 parentCode); + + dng_rect Bounds () const + { + return dng_rect (0, + 0, + fImageLength, + fImageWidth); + } + + uint32 TilesAcross () const; + + uint32 TilesDown () const; + + uint32 TilesPerImage () const; + + dng_rect TileArea (uint32 rowIndex, + uint32 colIndex) const; + + virtual uint32 TileByteCount (const dng_rect &tile) const; + + void SetSingleStrip (); + + void FindTileSize (uint32 bytesPerTile = 128 * 1024, + uint32 cellH = 16, + uint32 cellV = 16); + + void FindStripSize (uint32 bytesPerStrip = 128 * 1024, + uint32 cellV = 16); + + virtual uint32 PixelType () const; + + virtual bool IsBaselineJPEG () const; + + virtual bool CanRead () const; + + virtual void ReadImage (dng_host &host, + dng_stream &stream, + dng_image &image, + dng_jpeg_image *jpegImage = NULL, + dng_fingerprint *jpegDigest = NULL) const; + + protected: + + virtual bool IsValidCFA (dng_shared &shared, + uint32 parentCode); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_image.cpp b/dng/dng_image.cpp new file mode 100644 index 0000000..811a783 --- /dev/null +++ b/dng/dng_image.cpp @@ -0,0 +1,854 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_image.h" + +#include "dng_assertions.h" +#include "dng_exceptions.h" +#include "dng_orientation.h" +#include "dng_pixel_buffer.h" +#include "dng_tag_types.h" +#include "dng_tile_iterator.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_tile_buffer::dng_tile_buffer (const dng_image &image, + const dng_rect &tile, + bool dirty) + + : fImage (image) + , fRefData (NULL) + + { + + fImage.AcquireTileBuffer (*this, + tile, + dirty); + + } + +/*****************************************************************************/ + +dng_tile_buffer::~dng_tile_buffer () + { + + fImage.ReleaseTileBuffer (*this); + + } + +/*****************************************************************************/ + +dng_const_tile_buffer::dng_const_tile_buffer (const dng_image &image, + const dng_rect &tile) + + : dng_tile_buffer (image, tile, false) + + { + + } + +/*****************************************************************************/ + +dng_const_tile_buffer::~dng_const_tile_buffer () + { + + } + +/*****************************************************************************/ + +dng_dirty_tile_buffer::dng_dirty_tile_buffer (dng_image &image, + const dng_rect &tile) + + : dng_tile_buffer (image, tile, true) + + { + + } + +/*****************************************************************************/ + +dng_dirty_tile_buffer::~dng_dirty_tile_buffer () + { + + } + +/*****************************************************************************/ + +dng_image::dng_image (const dng_rect &bounds, + uint32 planes, + uint32 pixelType) + + : fBounds (bounds) + , fPlanes (planes) + , fPixelType (pixelType) + + { + + if (bounds.IsEmpty () || planes == 0 || PixelSize () == 0) + { + + #if qDNGValidate + + ReportError ("Fuzz: Attempt to create zero size image"); + + #endif + + ThrowBadFormat (); + + } + + // Allow up to 2 * kMaxImageSide to deal with intermediate image objects + // (e.g., rotated and padded). + + static const uint32 kLimit = 2 * kMaxImageSide; + + if (bounds.W () > kLimit || + bounds.H () > kLimit) + { + + ThrowBadFormat ("dng_image bounds too large"); + + } + + } + +/*****************************************************************************/ + +dng_image::~dng_image () + { + + } + +/*****************************************************************************/ + +dng_image * dng_image::Clone () const + { + + ThrowProgramError ("Clone is not supported by this dng_image subclass"); + + return NULL; + + } + +/*****************************************************************************/ + +void dng_image::SetPixelType (uint32 pixelType) + { + + if (TagTypeSize (pixelType) != PixelSize ()) + { + + ThrowProgramError ("Cannot change pixel size for existing image"); + + } + + fPixelType = pixelType; + + } + +/*****************************************************************************/ + +uint32 dng_image::PixelSize () const + { + + return TagTypeSize (PixelType ()); + + } + +/*****************************************************************************/ + +uint32 dng_image::PixelRange () const + { + + switch (fPixelType) + { + + case ttByte: + case ttSByte: + { + return 0x0FF; + } + + case ttShort: + case ttSShort: + { + return 0x0FFFF; + } + + case ttLong: + case ttSLong: + { + return 0xFFFFFFFF; + } + + default: + break; + + } + + return 0; + + } + +/*****************************************************************************/ + +dng_rect dng_image::RepeatingTile () const + { + + return fBounds; + + } + +/*****************************************************************************/ + +void dng_image::AcquireTileBuffer (dng_tile_buffer & /* buffer */, + const dng_rect & /* area */, + bool /* dirty */) const + { + + ThrowProgramError (); + + } + +/*****************************************************************************/ + +void dng_image::ReleaseTileBuffer (dng_tile_buffer & /* buffer */) const + { + + } + +/*****************************************************************************/ + +void dng_image::DoGet (dng_pixel_buffer &buffer) const + { + + dng_rect tile; + + dng_tile_iterator iter (*this, buffer.fArea); + + while (iter.GetOneTile (tile)) + { + + dng_const_tile_buffer tileBuffer (*this, tile); + + buffer.CopyArea (tileBuffer, + tile, + buffer.fPlane, + buffer.fPlanes); + + } + + } + +/*****************************************************************************/ + +void dng_image::DoPut (const dng_pixel_buffer &buffer) + { + + dng_rect tile; + + dng_tile_iterator iter (*this, buffer.fArea); + + while (iter.GetOneTile (tile)) + { + + dng_dirty_tile_buffer tileBuffer (*this, tile); + + tileBuffer.CopyArea (buffer, + tile, + buffer.fPlane, + buffer.fPlanes); + + } + + } + +/*****************************************************************************/ + +void dng_image::GetRepeat (dng_pixel_buffer &buffer, + const dng_rect &srcArea, + const dng_rect &dstArea) const + { + + // If we already have the entire srcArea in the + // buffer, we can just repeat that. + + if ((srcArea & buffer.fArea) == srcArea) + { + + buffer.RepeatArea (srcArea, + dstArea); + + } + + // Else we first need to get the srcArea into the buffer area. + + else + { + + // Find repeating pattern size. + + dng_point repeat = srcArea.Size (); + + // Find pattern phase at top-left corner of destination area. + + dng_point phase = dng_pixel_buffer::RepeatPhase (srcArea, + dstArea); + + // Find new source area at top-left of dstArea. + + dng_rect newArea = srcArea + (dstArea.TL () - + srcArea.TL ()); + + // Find quadrant split coordinates. + + int32 splitV = newArea.t + repeat.v - phase.v; + int32 splitH = newArea.l + repeat.h - phase.h; + + // Top-left quadrant. + + dng_rect dst1 (dng_rect (newArea.t, + newArea.l, + splitV, + splitH) & dstArea); + + if (dst1.NotEmpty ()) + { + + dng_pixel_buffer temp (buffer); + + temp.fArea = dst1 + (srcArea.TL () - + dstArea.TL () + + dng_point (phase.v, phase.h)); + + temp.fData = buffer.DirtyPixel (dst1.t, + dst1.l, + buffer.fPlane); + + DoGet (temp); + + } + + // Top-right quadrant. + + dng_rect dst2 (dng_rect (newArea.t, + splitH, + splitV, + newArea.r) & dstArea); + + if (dst2.NotEmpty ()) + { + + dng_pixel_buffer temp (buffer); + + temp.fArea = dst2 + (srcArea.TL () - + dstArea.TL () + + dng_point (phase.v, -phase.h)); + + temp.fData = buffer.DirtyPixel (dst2.t, + dst2.l, + buffer.fPlane); + + DoGet (temp); + + } + + // Bottom-left quadrant. + + dng_rect dst3 (dng_rect (splitV, + newArea.l, + newArea.b, + splitH) & dstArea); + + if (dst3.NotEmpty ()) + { + + dng_pixel_buffer temp (buffer); + + temp.fArea = dst3 + (srcArea.TL () - + dstArea.TL () + + dng_point (-phase.v, phase.h)); + + temp.fData = buffer.DirtyPixel (dst3.t, + dst3.l, + buffer.fPlane); + + DoGet (temp); + + } + + // Bottom-right quadrant. + + dng_rect dst4 (dng_rect (splitV, + splitH, + newArea.b, + newArea.r) & dstArea); + + if (dst4.NotEmpty ()) + { + + dng_pixel_buffer temp (buffer); + + temp.fArea = dst4 + (srcArea.TL () - + dstArea.TL () + + dng_point (-phase.v, -phase.h)); + + temp.fData = buffer.DirtyPixel (dst4.t, + dst4.l, + buffer.fPlane); + + DoGet (temp); + + } + + // Replicate this new source area. + + buffer.RepeatArea (newArea, + dstArea); + + } + + } + +/*****************************************************************************/ + +void dng_image::GetEdge (dng_pixel_buffer &buffer, + edge_option edgeOption, + const dng_rect &srcArea, + const dng_rect &dstArea) const + { + + switch (edgeOption) + { + + case edge_zero: + { + + buffer.SetZero (dstArea, + buffer.fPlane, + buffer.fPlanes); + + break; + + } + + case edge_repeat: + { + + GetRepeat (buffer, + srcArea, + dstArea); + + break; + + } + + case edge_repeat_zero_last: + { + + if (buffer.fPlanes > 1) + { + + dng_pixel_buffer buffer1 (buffer); + + buffer1.fPlanes--; + + GetEdge (buffer1, + edge_repeat, + srcArea, + dstArea); + + } + + dng_pixel_buffer buffer2 (buffer); + + buffer2.fPlane = buffer.fPlanes - 1; + buffer2.fPlanes = 1; + + buffer2.fData = buffer.DirtyPixel (buffer2.fArea.t, + buffer2.fArea.l, + buffer2.fPlane); + + GetEdge (buffer2, + edge_zero, + srcArea, + dstArea); + + break; + + } + + default: + { + + ThrowProgramError (); + + } + + } + + } + +/*****************************************************************************/ + +void dng_image::Get (dng_pixel_buffer &buffer, + edge_option edgeOption, + uint32 repeatV, + uint32 repeatH) const + { + + // Find the overlap with the image bounds. + + dng_rect overlap = buffer.fArea & fBounds; + + // Move the overlapping pixels. + + if (overlap.NotEmpty ()) + { + + dng_pixel_buffer temp (buffer); + + temp.fArea = overlap; + + temp.fData = buffer.DirtyPixel (overlap.t, + overlap.l, + buffer.fPlane); + + DoGet (temp); + + } + + // See if we need to pad the edge values. + + if ((edgeOption != edge_none) && (overlap != buffer.fArea)) + { + + dng_rect areaT (buffer.fArea); + dng_rect areaL (buffer.fArea); + dng_rect areaB (buffer.fArea); + dng_rect areaR (buffer.fArea); + + areaT.b = Min_int32 (areaT.b, fBounds.t); + areaL.r = Min_int32 (areaL.r, fBounds.l); + areaB.t = Max_int32 (areaB.t, fBounds.b); + areaR.l = Max_int32 (areaR.l, fBounds.r); + + dng_rect areaH (buffer.fArea); + dng_rect areaV (buffer.fArea); + + areaH.l = Max_int32 (areaH.l, fBounds.l); + areaH.r = Min_int32 (areaH.r, fBounds.r); + + areaV.t = Max_int32 (areaV.t, fBounds.t); + areaV.b = Min_int32 (areaV.b, fBounds.b); + + // Top left. + + dng_rect areaTL = areaT & areaL; + + if (areaTL.NotEmpty ()) + { + + GetEdge (buffer, + edgeOption, + dng_rect (fBounds.t, + fBounds.l, + fBounds.t + repeatV, + fBounds.l + repeatH), + areaTL); + + } + + // Top middle. + + dng_rect areaTM = areaT & areaH; + + if (areaTM.NotEmpty ()) + { + + GetEdge (buffer, + edgeOption, + dng_rect (fBounds.t, + areaTM.l, + fBounds.t + repeatV, + areaTM.r), + areaTM); + + } + + // Top right. + + dng_rect areaTR = areaT & areaR; + + if (areaTR.NotEmpty ()) + { + + GetEdge (buffer, + edgeOption, + dng_rect (fBounds.t, + fBounds.r - repeatH, + fBounds.t + repeatV, + fBounds.r), + areaTR); + + } + + // Left middle. + + dng_rect areaLM = areaL & areaV; + + if (areaLM.NotEmpty ()) + { + + GetEdge (buffer, + edgeOption, + dng_rect (areaLM.t, + fBounds.l, + areaLM.b, + fBounds.l + repeatH), + areaLM); + + } + + // Right middle. + + dng_rect areaRM = areaR & areaV; + + if (areaRM.NotEmpty ()) + { + + GetEdge (buffer, + edgeOption, + dng_rect (areaRM.t, + fBounds.r - repeatH, + areaRM.b, + fBounds.r), + areaRM); + + } + + // Bottom left. + + dng_rect areaBL = areaB & areaL; + + if (areaBL.NotEmpty ()) + { + + GetEdge (buffer, + edgeOption, + dng_rect (fBounds.b - repeatV, + fBounds.l, + fBounds.b, + fBounds.l + repeatH), + areaBL); + + } + + // Bottom middle. + + dng_rect areaBM = areaB & areaH; + + if (areaBM.NotEmpty ()) + { + + GetEdge (buffer, + edgeOption, + dng_rect (fBounds.b - repeatV, + areaBM.l, + fBounds.b, + areaBM.r), + areaBM); + + } + + // Bottom right. + + dng_rect areaBR = areaB & areaR; + + if (areaBR.NotEmpty ()) + { + + GetEdge (buffer, + edgeOption, + dng_rect (fBounds.b - repeatV, + fBounds.r - repeatH, + fBounds.b, + fBounds.r), + areaBR); + + } + + } + + } + +/*****************************************************************************/ + +void dng_image::Put (const dng_pixel_buffer &buffer) + { + + // Move the overlapping pixels. + + dng_rect overlap = buffer.fArea & fBounds; + + if (overlap.NotEmpty ()) + { + + dng_pixel_buffer temp (buffer); + + temp.fArea = overlap; + + temp.fData = (void *) buffer.ConstPixel (overlap.t, + overlap.l, + buffer.fPlane); + + // Move the overlapping planes. + + if (temp.fPlane < Planes ()) + { + + temp.fPlanes = Min_uint32 (temp.fPlanes, + Planes () - temp.fPlane); + + DoPut (temp); + + } + + } + + } + +/*****************************************************************************/ + +void dng_image::Trim (const dng_rect &r) + { + + if (r != Bounds ()) + { + + ThrowProgramError ("Trim is not support by this dng_image subclass"); + + } + + } + +/*****************************************************************************/ + +void dng_image::Rotate (const dng_orientation &orientation) + { + + if (orientation != dng_orientation::Normal ()) + { + + ThrowProgramError ("Rotate is not support by this dng_image subclass"); + + } + + } + +/*****************************************************************************/ + +void dng_image::DoCopyArea (const dng_image &src, + const dng_rect &area, + uint32 srcPlane, + uint32 dstPlane, + uint32 planes) + { + + if (&src == this) + return; + + dng_tile_iterator destIter(*this, area); + dng_rect destTileArea; + + while (destIter.GetOneTile(destTileArea)) + { + dng_tile_iterator srcIter(src, destTileArea); + dng_rect srcTileArea; + + while (srcIter.GetOneTile(srcTileArea)) + { + + dng_dirty_tile_buffer destTile(*this, srcTileArea); + dng_const_tile_buffer srcTile(src, srcTileArea); + + destTile.CopyArea (srcTile, srcTileArea, srcPlane, dstPlane, planes); + + } + + } + + } + +/*****************************************************************************/ + +bool dng_image::EqualArea (const dng_image &src, + const dng_rect &area, + uint32 plane, + uint32 planes) const + { + + if (&src == this) + return true; + + dng_tile_iterator destIter (*this, area); + + dng_rect destTileArea; + + while (destIter.GetOneTile (destTileArea)) + { + + dng_tile_iterator srcIter (src, destTileArea); + + dng_rect srcTileArea; + + while (srcIter.GetOneTile (srcTileArea)) + { + + dng_const_tile_buffer destTile (*this, srcTileArea); + dng_const_tile_buffer srcTile (src , srcTileArea); + + if (!destTile.EqualArea (srcTile, srcTileArea, plane, planes)) + { + return false; + } + + } + + } + + return true; + + } + +/*****************************************************************************/ + +void dng_image::SetConstant (uint32 value, + const dng_rect &area) + { + + dng_tile_iterator iter (*this, area); + + dng_rect tileArea; + + while (iter.GetOneTile (tileArea)) + { + + dng_dirty_tile_buffer buffer (*this, tileArea); + + buffer.SetConstant (tileArea, + 0, + fPlanes, + value); + + } + + } + +/*****************************************************************************/ diff --git a/dng/dng_image.h b/dng/dng_image.h new file mode 100644 index 0000000..28633b5 --- /dev/null +++ b/dng/dng_image.h @@ -0,0 +1,433 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Support for working with image data in DNG SDK. + */ + +/*****************************************************************************/ + +#ifndef __dng_image__ +#define __dng_image__ + +/*****************************************************************************/ + +#include "dng_assertions.h" +#include "dng_classes.h" +#include "dng_pixel_buffer.h" +#include "dng_point.h" +#include "dng_rect.h" +#include "dng_tag_types.h" +#include "dng_types.h" +#include "dng_uncopyable.h" + +/*****************************************************************************/ + +/// \brief Class to get resource acquisition is instantiation behavior for tile +/// buffers. Can be dirty or constant tile access. + +class dng_tile_buffer: public dng_pixel_buffer, + private dng_uncopyable + { + + protected: + + const dng_image &fImage; + + void *fRefData; + + protected: + + /// Obtain a tile from an image. + /// \param image Image tile will come from. + /// \param tile Rectangle denoting extent of tile. + /// \param dirty Flag indicating whether this is read-only or read-write acesss. + + dng_tile_buffer (const dng_image &image, + const dng_rect &tile, + bool dirty); + + virtual ~dng_tile_buffer (); + + public: + + void SetRefData (void *refData) + { + fRefData = refData; + } + + void * GetRefData () const + { + return fRefData; + } + + }; + +/*****************************************************************************/ + +/// \brief Class to get resource acquisition is instantiation behavior for +/// constant (read-only) tile buffers. + +class dng_const_tile_buffer: public dng_tile_buffer + { + + public: + + /// Obtain a read-only tile from an image. + /// \param image Image tile will come from. + /// \param tile Rectangle denoting extent of tile. + + dng_const_tile_buffer (const dng_image &image, + const dng_rect &tile); + + virtual ~dng_const_tile_buffer (); + + }; + +/*****************************************************************************/ + +/// \brief Class to get resource acquisition is instantiation behavior for +/// dirty (writable) tile buffers. + +class dng_dirty_tile_buffer: public dng_tile_buffer + { + + public: + + /// Obtain a writable tile from an image. + /// \param image Image tile will come from. + /// \param tile Rectangle denoting extent of tile. + + dng_dirty_tile_buffer (dng_image &image, + const dng_rect &tile); + + virtual ~dng_dirty_tile_buffer (); + + }; + +/*****************************************************************************/ + +/// \brief Base class for holding image data in DNG SDK. See dng_simple_image +/// for derived class most often used in DNG SDK. + +class dng_image + { + + friend class dng_tile_buffer; + + protected: + + // Bounds for this image. + + dng_rect fBounds; + + // Number of image planes. + + uint32 fPlanes; + + // Basic pixel type (TIFF tag type code). + + uint32 fPixelType; + + public: + + /// How to handle requests to get image areas outside the image bounds. + + enum edge_option + { + + /// Leave edge pixels unchanged. + + edge_none, + + /// Pad with zeros. + + edge_zero, + + /// Repeat edge pixels. + + edge_repeat, + + /// Repeat edge pixels, except for last plane which is zero padded. + + edge_repeat_zero_last + + }; + + protected: + + dng_image (const dng_rect &bounds, + uint32 planes, + uint32 pixelType); + + public: + + virtual ~dng_image (); + + virtual dng_image * Clone () const; + + /// Getter method for bounds of an image. + + const dng_rect & Bounds () const + { + return fBounds; + } + + /// Getter method for size of an image. + + dng_point Size () const + { + return Bounds ().Size (); + } + + /// Getter method for width of an image. + + uint32 Width () const + { + return Bounds ().W (); + } + + /// Getter method for height of an image. + + uint32 Height () const + { + return Bounds ().H (); + } + + /// Getter method for number of planes in an image. + + uint32 Planes () const + { + return fPlanes; + } + + /// Getter for pixel type. + /// \retval See dng_tagtypes.h . Valid values are ttByte, ttShort, ttSShort, + /// ttLong, ttFloat . + + uint32 PixelType () const + { + return fPixelType; + } + + /// Setter for pixel type. + /// \param pixelType The new pixel type . + + virtual void SetPixelType (uint32 pixelType); + + /// Getter for pixel size. + /// \retval Size, in bytes, of pixel type for this image . + + uint32 PixelSize () const; + + /// Getter for pixel range. + /// For unsigned types, range is 0 to return value. + /// For signed types, range is return value - 0x8000U. + /// For ttFloat type, pixel range is 0.0 to 1.0 and this routine returns 1. + + uint32 PixelRange () const; + + /// Getter for best "tile stride" for accessing image. + + virtual dng_rect RepeatingTile () const; + + /// Get a pixel buffer of data on image with proper edge padding. + /// \param buffer Receives resulting pixel buffer. + /// \param edgeOption edge_option describing how to pad edges. + /// \param repeatV Amount of repeated padding needed in vertical for + /// edge_repeat and edge_repeat_zero_last edgeOption cases. + /// \param repeatH Amount of repeated padding needed in horizontal for + /// edge_repeat and edge_repeat_zero_last edgeOption cases. + + void Get (dng_pixel_buffer &buffer, + edge_option edgeOption = edge_none, + uint32 repeatV = 1, + uint32 repeatH = 1) const; + + /// Put a pixel buffer into image. + /// \param buffer Pixel buffer to copy from. + + void Put (const dng_pixel_buffer &buffer); + + /// Shrink bounds of image to given rectangle. + /// \param r Rectangle to crop to. + + virtual void Trim (const dng_rect &r); + + /// Rotate image to reflect given orientation change. + /// \param orientation Directive to rotate image in a certain way. + + virtual void Rotate (const dng_orientation &orientation); + + /// Copy image data from an area of one image to same area of another. + /// \param src Image to copy from. + /// \param area Rectangle of images to copy. + /// \param srcPlane Plane to start copying in src. + /// \param dstPlane Plane to start copying in this. + /// \param planes Number of planes to copy. + + void CopyArea (const dng_image &src, + const dng_rect &area, + uint32 srcPlane, + uint32 dstPlane, + uint32 planes) + { + + DoCopyArea (src, area, srcPlane, dstPlane, planes); + + } + + /// Copy image data from an area of one image to same area of another. + /// \param src Image to copy from. + /// \param area Rectangle of images to copy. + /// \param plane Plane to start copying in src and this. + /// \param planes Number of planes to copy. + + void CopyArea (const dng_image &src, + const dng_rect &area, + uint32 plane, + uint32 planes) + { + + DoCopyArea (src, area, plane, plane, planes); + + } + + /// Return true if the contents of an area of the image are the same as those of another. + /// \param rhs Image to compare against. + /// \param area Rectangle of image to test. + /// \param plane Plane to start comparing. + /// \param planes Number of planes to compare. + + virtual bool EqualArea (const dng_image &rhs, + const dng_rect &area, + uint32 plane, + uint32 planes) const; + + // Routines to set the entire image to a constant value. + + void SetConstant_uint8 (uint8 value, + const dng_rect &area) + { + + DNG_ASSERT (fPixelType == ttByte, "Mismatched pixel type"); + + SetConstant ((uint32) value, area); + + } + + void SetConstant_uint8 (uint8 value) + { + SetConstant (value, Bounds ()); + } + + void SetConstant_uint16 (uint16 value, + const dng_rect &area) + { + + DNG_ASSERT (fPixelType == ttShort, "Mismatched pixel type"); + + SetConstant ((uint32) value, area); + + } + + void SetConstant_uint16 (uint16 value) + { + SetConstant_uint16 (value, Bounds ()); + } + + void SetConstant_int16 (int16 value, + const dng_rect &area) + { + + DNG_ASSERT (fPixelType == ttSShort, "Mismatched pixel type"); + + SetConstant ((uint32) (uint16) value, area); + + } + + void SetConstant_int16 (int16 value) + { + SetConstant_int16 (value, Bounds ()); + } + + void SetConstant_uint32 (uint32 value, + const dng_rect &area) + { + + DNG_ASSERT (fPixelType == ttLong, "Mismatched pixel type"); + + SetConstant (value, area); + + } + + void SetConstant_uint32 (uint32 value) + { + SetConstant_uint32 (value, Bounds ()); + } + + void SetConstant_real32 (real32 value, + const dng_rect &area) + { + + DNG_ASSERT (fPixelType == ttFloat, "Mismatched pixel type"); + + union + { + uint32 i; + real32 f; + } x; + + x.f = value; + + SetConstant (x.i, area); + + } + + void SetConstant_real32 (real32 value) + { + SetConstant_real32 (value, Bounds ()); + } + + virtual void GetRepeat (dng_pixel_buffer &buffer, + const dng_rect &srcArea, + const dng_rect &dstArea) const; + + protected: + + virtual void AcquireTileBuffer (dng_tile_buffer &buffer, + const dng_rect &area, + bool dirty) const; + + virtual void ReleaseTileBuffer (dng_tile_buffer &buffer) const; + + virtual void DoGet (dng_pixel_buffer &buffer) const; + + virtual void DoPut (const dng_pixel_buffer &buffer); + + virtual void DoCopyArea (const dng_image &src, + const dng_rect &area, + uint32 srcPlane, + uint32 dstPlane, + uint32 planes); + + void GetEdge (dng_pixel_buffer &buffer, + edge_option edgeOption, + const dng_rect &srcArea, + const dng_rect &dstArea) const; + + virtual void SetConstant (uint32 value, + const dng_rect &area); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_image_writer.cpp b/dng/dng_image_writer.cpp new file mode 100644 index 0000000..39327fb --- /dev/null +++ b/dng/dng_image_writer.cpp @@ -0,0 +1,7499 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_image_writer.h" + +#include "dng_abort_sniffer.h" +#include "dng_area_task.h" +#include "dng_bottlenecks.h" +#include "dng_camera_profile.h" +#include "dng_color_space.h" +#include "dng_exceptions.h" +#include "dng_exif.h" +#include "dng_flags.h" +#include "dng_globals.h" +#include "dng_host.h" +#include "dng_ifd.h" +#include "dng_image.h" +#include "dng_jpeg_image.h" +#include "dng_lossless_jpeg.h" +#include "dng_memory.h" +#include "dng_memory_stream.h" +#include "dng_negative.h" +#include "dng_pixel_buffer.h" +#include "dng_preview.h" +#include "dng_read_image.h" +#include "dng_safe_arithmetic.h" +#include "dng_stream.h" +#include "dng_string_list.h" +#include "dng_tag_codes.h" +#include "dng_tag_values.h" +#include "dng_utils.h" +#include "dng_xmp.h" + +#include "zlib.h" + +#if qDNGUseLibJPEG +#include "jpeglib.h" +#include "jerror.h" +#endif + +#include + +/*****************************************************************************/ + +// Defines for testing DNG 1.2 features. + +//#define qTestRowInterleave 2 + +//#define qTestSubTileBlockRows 2 +//#define qTestSubTileBlockCols 2 + +/*****************************************************************************/ + +dng_resolution::dng_resolution () + + : fXResolution () + , fYResolution () + + , fResolutionUnit (0) + + { + + } + +/******************************************************************************/ + +static void SpoolAdobeData (dng_stream &stream, + const dng_metadata *metadata, + const dng_jpeg_preview *preview, + const dng_memory_block *imageResources) + { + + TempBigEndian tempEndian (stream); + + if (metadata && metadata->GetXMP ()) + { + + bool marked = false; + + if (metadata->GetXMP ()->GetBoolean (XMP_NS_XAP_RIGHTS, + "Marked", + marked)) + { + + stream.Put_uint32 (DNG_CHAR4 ('8','B','I','M')); + stream.Put_uint16 (1034); + stream.Put_uint16 (0); + + stream.Put_uint32 (1); + + stream.Put_uint8 (marked ? 1 : 0); + + stream.Put_uint8 (0); + + } + + dng_string webStatement; + + if (metadata->GetXMP ()->GetString (XMP_NS_XAP_RIGHTS, + "WebStatement", + webStatement)) + { + + dng_memory_data buffer; + + uint32 size = webStatement.Get_SystemEncoding (buffer); + + if (size > 0) + { + + stream.Put_uint32 (DNG_CHAR4 ('8','B','I','M')); + stream.Put_uint16 (1035); + stream.Put_uint16 (0); + + stream.Put_uint32 (size); + + stream.Put (buffer.Buffer (), size); + + if (size & 1) + stream.Put_uint8 (0); + + } + + } + + } + + if (preview) + { + + preview->SpoolAdobeThumbnail (stream); + + } + + if (metadata && metadata->IPTCLength ()) + { + + dng_fingerprint iptcDigest = metadata->IPTCDigest (); + + if (iptcDigest.IsValid ()) + { + + stream.Put_uint32 (DNG_CHAR4 ('8','B','I','M')); + stream.Put_uint16 (1061); + stream.Put_uint16 (0); + + stream.Put_uint32 (16); + + stream.Put (iptcDigest.data, 16); + + } + + } + + if (imageResources) + { + + uint32 size = imageResources->LogicalSize (); + + stream.Put (imageResources->Buffer (), size); + + if (size & 1) + stream.Put_uint8 (0); + + } + + } + +/******************************************************************************/ + +static dng_memory_block * BuildAdobeData (dng_host &host, + const dng_metadata *metadata, + const dng_jpeg_preview *preview, + const dng_memory_block *imageResources) + { + + dng_memory_stream stream (host.Allocator ()); + + SpoolAdobeData (stream, + metadata, + preview, + imageResources); + + return stream.AsMemoryBlock (host.Allocator ()); + + } + +/*****************************************************************************/ + +tag_string::tag_string (uint16 code, + const dng_string &s, + bool forceASCII) + + : tiff_tag (code, ttAscii, 0) + + , fString (s) + + { + + if (forceASCII) + { + + // Metadata working group recommendation - go ahead + // write UTF-8 into ASCII tag strings, rather than + // actually force the strings to ASCII. There is a matching + // change on the reading side to assume UTF-8 if the string + // contains a valid UTF-8 string. + // + // fString.ForceASCII (); + + } + + else if (!fString.IsASCII ()) + { + + fType = ttByte; + + } + + fCount = fString.Length () + 1; + + } + +/*****************************************************************************/ + +void tag_string::Put (dng_stream &stream) const + { + + stream.Put (fString.Get (), Size ()); + + } + +/*****************************************************************************/ + +tag_encoded_text::tag_encoded_text (uint16 code, + const dng_string &text) + + : tiff_tag (code, ttUndefined, 0) + + , fText (text) + + , fUTF16 () + + { + + if (fText.IsASCII ()) + { + + fCount = 8 + fText.Length (); + + } + + else + { + + fCount = 8 + fText.Get_UTF16 (fUTF16) * 2; + + } + + } + +/*****************************************************************************/ + +void tag_encoded_text::Put (dng_stream &stream) const + { + + if (fUTF16.Buffer ()) + { + + stream.Put ("UNICODE\000", 8); + + uint32 chars = (fCount - 8) >> 1; + + const uint16 *buf = fUTF16.Buffer_uint16 (); + + for (uint32 j = 0; j < chars; j++) + { + + stream.Put_uint16 (buf [j]); + + } + + } + + else + { + + stream.Put ("ASCII\000\000\000", 8); + + stream.Put (fText.Get (), fCount - 8); + + } + + } + +/*****************************************************************************/ + +void tag_data_ptr::Put (dng_stream &stream) const + { + + // If we are swapping bytes, we need to swap with the right size + // entries. + + if (stream.SwapBytes ()) + { + + switch (Type ()) + { + + // Two byte entries. + + case ttShort: + case ttSShort: + case ttUnicode: + { + + const uint16 *p = (const uint16 *) fData; + + uint32 entries = (Size () >> 1); + + for (uint32 j = 0; j < entries; j++) + { + + stream.Put_uint16 (p [j]); + + } + + return; + + } + + // Four byte entries. + + case ttLong: + case ttSLong: + case ttRational: + case ttSRational: + case ttIFD: + case ttFloat: + case ttComplex: + { + + const uint32 *p = (const uint32 *) fData; + + uint32 entries = (Size () >> 2); + + for (uint32 j = 0; j < entries; j++) + { + + stream.Put_uint32 (p [j]); + + } + + return; + + } + + // Eight byte entries. + + case ttDouble: + { + + const real64 *p = (const real64 *) fData; + + uint32 entries = (Size () >> 3); + + for (uint32 j = 0; j < entries; j++) + { + + stream.Put_real64 (p [j]); + + } + + return; + + } + + // Entries don't need to be byte swapped. Fall through + // to non-byte swapped case. + + default: + { + + break; + + } + + } + + } + + // Non-byte swapped case. + + stream.Put (fData, Size ()); + + } + +/******************************************************************************/ + +tag_matrix::tag_matrix (uint16 code, + const dng_matrix &m) + + : tag_srational_ptr (code, fEntry, m.Rows () * m.Cols ()) + + { + + uint32 index = 0; + + for (uint32 r = 0; r < m.Rows (); r++) + for (uint32 c = 0; c < m.Cols (); c++) + { + + fEntry [index].Set_real64 (m [r] [c], 10000); + + index++; + + } + + } + +/******************************************************************************/ + +tag_icc_profile::tag_icc_profile (const void *profileData, + uint32 profileSize) + + : tag_data_ptr (tcICCProfile, + ttUndefined, + 0, + NULL) + + { + + if (profileData && profileSize) + { + + SetCount (profileSize); + SetData (profileData); + + } + + } + +/******************************************************************************/ + +void tag_cfa_pattern::Put (dng_stream &stream) const + { + + stream.Put_uint16 ((uint16) fCols); + stream.Put_uint16 ((uint16) fRows); + + for (uint32 col = 0; col < fCols; col++) + for (uint32 row = 0; row < fRows; row++) + { + + stream.Put_uint8 (fPattern [row * kMaxCFAPattern + col]); + + } + + } + +/******************************************************************************/ + +tag_exif_date_time::tag_exif_date_time (uint16 code, + const dng_date_time &dt) + + : tag_data_ptr (code, ttAscii, 20, fData) + + { + + if (dt.IsValid ()) + { + + sprintf (fData, + "%04d:%02d:%02d %02d:%02d:%02d", + (int) dt.fYear, + (int) dt.fMonth, + (int) dt.fDay, + (int) dt.fHour, + (int) dt.fMinute, + (int) dt.fSecond); + + } + + } + +/******************************************************************************/ + +tag_iptc::tag_iptc (const void *data, + uint32 length) + + : tiff_tag (tcIPTC_NAA, ttLong, (length + 3) >> 2) + + , fData (data ) + , fLength (length) + + { + + } + +/******************************************************************************/ + +void tag_iptc::Put (dng_stream &stream) const + { + + // Note: For historical compatiblity reasons, the standard TIFF data + // type for IPTC data is ttLong, but without byte swapping. This really + // should be ttUndefined, but doing the right thing would break some + // existing readers. + + stream.Put (fData, fLength); + + // Pad with zeros to get to long word boundary. + + uint32 extra = fCount * 4 - fLength; + + while (extra--) + { + stream.Put_uint8 (0); + } + + } + +/******************************************************************************/ + +tag_xmp::tag_xmp (const dng_xmp *xmp) + + : tag_uint8_ptr (tcXMP, NULL, 0) + + , fBuffer () + + { + + if (xmp) + { + + fBuffer.Reset (xmp->Serialize (true)); + + if (fBuffer.Get ()) + { + + SetData (fBuffer->Buffer_uint8 ()); + + SetCount (fBuffer->LogicalSize ()); + + } + + } + + } + +/******************************************************************************/ + +void dng_tiff_directory::Add (const tiff_tag *tag) + { + + if (fEntries >= kMaxEntries) + { + ThrowProgramError (); + } + + // Tags must be sorted in increasing order of tag code. + + uint32 index = fEntries; + + for (uint32 j = 0; j < fEntries; j++) + { + + if (tag->Code () < fTag [j]->Code ()) + { + index = j; + break; + } + + } + + for (uint32 k = fEntries; k > index; k--) + { + + fTag [k] = fTag [k - 1]; + + } + + fTag [index] = tag; + + fEntries++; + + } + +/******************************************************************************/ + +uint32 dng_tiff_directory::Size () const + { + + if (!fEntries) return 0; + + uint32 size = fEntries * 12 + 6; + + for (uint32 index = 0; index < fEntries; index++) + { + + uint32 tagSize = fTag [index]->Size (); + + if (tagSize > 4) + { + + size += (tagSize + 1) & ~1; + + } + + } + + return size; + + } + +/******************************************************************************/ + +void dng_tiff_directory::Put (dng_stream &stream, + OffsetsBase offsetsBase, + uint32 explicitBase) const + { + + if (!fEntries) return; + + uint32 index; + + uint32 bigData = fEntries * 12 + 6; + + if (offsetsBase == offsetsRelativeToStream) + bigData += (uint32) stream.Position (); + + else if (offsetsBase == offsetsRelativeToExplicitBase) + bigData += explicitBase; + + stream.Put_uint16 ((uint16) fEntries); + + for (index = 0; index < fEntries; index++) + { + + const tiff_tag &tag = *fTag [index]; + + stream.Put_uint16 (tag.Code ()); + stream.Put_uint16 (tag.Type ()); + stream.Put_uint32 (tag.Count ()); + + uint32 size = tag.Size (); + + if (size <= 4) + { + + tag.Put (stream); + + while (size < 4) + { + stream.Put_uint8 (0); + size++; + } + + } + + else + { + + stream.Put_uint32 (bigData); + + bigData += (size + 1) & ~1; + + } + + } + + stream.Put_uint32 (fChained); // Next IFD offset + + for (index = 0; index < fEntries; index++) + { + + const tiff_tag &tag = *fTag [index]; + + uint32 size = tag.Size (); + + if (size > 4) + { + + tag.Put (stream); + + if (size & 1) + stream.Put_uint8 (0); + + } + + } + + } + +/******************************************************************************/ + +dng_basic_tag_set::dng_basic_tag_set (dng_tiff_directory &directory, + const dng_ifd &info) + + : fNewSubFileType (tcNewSubFileType, info.fNewSubFileType) + + , fImageWidth (tcImageWidth , info.fImageWidth ) + , fImageLength (tcImageLength, info.fImageLength) + + , fPhotoInterpretation (tcPhotometricInterpretation, + (uint16) info.fPhotometricInterpretation) + + , fFillOrder (tcFillOrder, 1) + + , fSamplesPerPixel (tcSamplesPerPixel, (uint16) info.fSamplesPerPixel) + + , fBitsPerSample (tcBitsPerSample, + fBitsPerSampleData, + info.fSamplesPerPixel) + + , fStrips (info.fUsesStrips) + + , fTileWidth (tcTileWidth, info.fTileWidth) + + , fTileLength (fStrips ? tcRowsPerStrip : tcTileLength, + info.fTileLength) + + , fTileInfoBuffer (info.TilesPerImage (), 8) + + , fTileOffsetData (fTileInfoBuffer.Buffer_uint32 ()) + + , fTileOffsets (fStrips ? tcStripOffsets : tcTileOffsets, + fTileOffsetData, + info.TilesPerImage ()) + + , fTileByteCountData (fTileOffsetData + info.TilesPerImage ()) + + , fTileByteCounts (fStrips ? tcStripByteCounts : tcTileByteCounts, + fTileByteCountData, + info.TilesPerImage ()) + + , fPlanarConfiguration (tcPlanarConfiguration, pcInterleaved) + + , fCompression (tcCompression, (uint16) info.fCompression) + , fPredictor (tcPredictor , (uint16) info.fPredictor ) + + , fExtraSamples (tcExtraSamples, + fExtraSamplesData, + info.fExtraSamplesCount) + + , fSampleFormat (tcSampleFormat, + fSampleFormatData, + info.fSamplesPerPixel) + + , fRowInterleaveFactor (tcRowInterleaveFactor, + (uint16) info.fRowInterleaveFactor) + + , fSubTileBlockSize (tcSubTileBlockSize, + fSubTileBlockSizeData, + 2) + + { + + uint32 j; + + for (j = 0; j < info.fSamplesPerPixel; j++) + { + + fBitsPerSampleData [j] = (uint16) info.fBitsPerSample [0]; + + } + + directory.Add (&fNewSubFileType); + + directory.Add (&fImageWidth); + directory.Add (&fImageLength); + + directory.Add (&fPhotoInterpretation); + + directory.Add (&fSamplesPerPixel); + + directory.Add (&fBitsPerSample); + + if (info.fBitsPerSample [0] != 8 && + info.fBitsPerSample [0] != 16 && + info.fBitsPerSample [0] != 32) + { + + directory.Add (&fFillOrder); + + } + + if (!fStrips) + { + + directory.Add (&fTileWidth); + + } + + directory.Add (&fTileLength); + + directory.Add (&fTileOffsets); + directory.Add (&fTileByteCounts); + + directory.Add (&fPlanarConfiguration); + + directory.Add (&fCompression); + + if (info.fPredictor != cpNullPredictor) + { + + directory.Add (&fPredictor); + + } + + if (info.fExtraSamplesCount != 0) + { + + for (j = 0; j < info.fExtraSamplesCount; j++) + { + fExtraSamplesData [j] = (uint16) info.fExtraSamples [j]; + } + + directory.Add (&fExtraSamples); + + } + + if (info.fSampleFormat [0] != sfUnsignedInteger) + { + + for (j = 0; j < info.fSamplesPerPixel; j++) + { + fSampleFormatData [j] = (uint16) info.fSampleFormat [j]; + } + + directory.Add (&fSampleFormat); + + } + + if (info.fRowInterleaveFactor != 1) + { + + directory.Add (&fRowInterleaveFactor); + + } + + if (info.fSubTileBlockRows != 1 || + info.fSubTileBlockCols != 1) + { + + fSubTileBlockSizeData [0] = (uint16) info.fSubTileBlockRows; + fSubTileBlockSizeData [1] = (uint16) info.fSubTileBlockCols; + + directory.Add (&fSubTileBlockSize); + + } + + } + +/******************************************************************************/ + +exif_tag_set::exif_tag_set (dng_tiff_directory &directory, + const dng_exif &exif, + bool makerNoteSafe, + const void *makerNoteData, + uint32 makerNoteLength, + bool insideDNG) + + : fExifIFD () + , fGPSIFD () + + , fExifLink (tcExifIFD, 0) + , fGPSLink (tcGPSInfo, 0) + + , fAddedExifLink (false) + , fAddedGPSLink (false) + + , fExifVersion (tcExifVersion, ttUndefined, 4, fExifVersionData) + + , fExposureTime (tcExposureTime , exif.fExposureTime ) + , fShutterSpeedValue (tcShutterSpeedValue, exif.fShutterSpeedValue) + + , fFNumber (tcFNumber , exif.fFNumber ) + , fApertureValue (tcApertureValue, exif.fApertureValue) + + , fBrightnessValue (tcBrightnessValue, exif.fBrightnessValue) + + , fExposureBiasValue (tcExposureBiasValue, exif.fExposureBiasValue) + + , fMaxApertureValue (tcMaxApertureValue , exif.fMaxApertureValue) + + , fSubjectDistance (tcSubjectDistance, exif.fSubjectDistance) + + , fFocalLength (tcFocalLength, exif.fFocalLength) + + // Special case: the EXIF 2.2 standard represents ISO speed ratings with 2 bytes, + // which cannot hold ISO speed ratings above 65535 (e.g., 102400). In these + // cases, we write the maximum representable ISO speed rating value in the EXIF + // tag, i.e., 65535. + + , fISOSpeedRatings (tcISOSpeedRatings, + (uint16) Min_uint32 (65535, + exif.fISOSpeedRatings [0])) + + , fSensitivityType (tcSensitivityType, (uint16) exif.fSensitivityType) + + , fStandardOutputSensitivity (tcStandardOutputSensitivity, exif.fStandardOutputSensitivity) + + , fRecommendedExposureIndex (tcRecommendedExposureIndex, exif.fRecommendedExposureIndex) + + , fISOSpeed (tcISOSpeed, exif.fISOSpeed) + + , fISOSpeedLatitudeyyy (tcISOSpeedLatitudeyyy, exif.fISOSpeedLatitudeyyy) + + , fISOSpeedLatitudezzz (tcISOSpeedLatitudezzz, exif.fISOSpeedLatitudezzz) + + , fFlash (tcFlash, (uint16) exif.fFlash) + + , fExposureProgram (tcExposureProgram, (uint16) exif.fExposureProgram) + + , fMeteringMode (tcMeteringMode, (uint16) exif.fMeteringMode) + + , fLightSource (tcLightSource, (uint16) exif.fLightSource) + + , fSensingMethod (tcSensingMethodExif, (uint16) exif.fSensingMethod) + + , fFocalLength35mm (tcFocalLengthIn35mmFilm, (uint16) exif.fFocalLengthIn35mmFilm) + + , fFileSourceData ((uint8) exif.fFileSource) + , fFileSource (tcFileSource, ttUndefined, 1, &fFileSourceData) + + , fSceneTypeData ((uint8) exif.fSceneType) + , fSceneType (tcSceneType, ttUndefined, 1, &fSceneTypeData) + + , fCFAPattern (tcCFAPatternExif, + exif.fCFARepeatPatternRows, + exif.fCFARepeatPatternCols, + &exif.fCFAPattern [0] [0]) + + , fCustomRendered (tcCustomRendered , (uint16) exif.fCustomRendered ) + , fExposureMode (tcExposureMode , (uint16) exif.fExposureMode ) + , fWhiteBalance (tcWhiteBalance , (uint16) exif.fWhiteBalance ) + , fSceneCaptureType (tcSceneCaptureType , (uint16) exif.fSceneCaptureType ) + , fGainControl (tcGainControl , (uint16) exif.fGainControl ) + , fContrast (tcContrast , (uint16) exif.fContrast ) + , fSaturation (tcSaturation , (uint16) exif.fSaturation ) + , fSharpness (tcSharpness , (uint16) exif.fSharpness ) + , fSubjectDistanceRange (tcSubjectDistanceRange, (uint16) exif.fSubjectDistanceRange) + + , fDigitalZoomRatio (tcDigitalZoomRatio, exif.fDigitalZoomRatio) + + , fExposureIndex (tcExposureIndexExif, exif.fExposureIndex) + + , fImageNumber (tcImageNumber, exif.fImageNumber) + + , fSelfTimerMode (tcSelfTimerMode, (uint16) exif.fSelfTimerMode) + + , fBatteryLevelA (tcBatteryLevel, exif.fBatteryLevelA) + , fBatteryLevelR (tcBatteryLevel, exif.fBatteryLevelR) + + , fColorSpace (tcColorSpace, (uint16) exif.fColorSpace) + + , fFocalPlaneXResolution (tcFocalPlaneXResolutionExif, exif.fFocalPlaneXResolution) + , fFocalPlaneYResolution (tcFocalPlaneYResolutionExif, exif.fFocalPlaneYResolution) + + , fFocalPlaneResolutionUnit (tcFocalPlaneResolutionUnitExif, (uint16) exif.fFocalPlaneResolutionUnit) + + , fSubjectArea (tcSubjectArea, fSubjectAreaData, exif.fSubjectAreaCount) + + , fLensInfo (tcLensInfo, fLensInfoData, 4) + + , fDateTime (tcDateTime , exif.fDateTime .DateTime ()) + , fDateTimeOriginal (tcDateTimeOriginal , exif.fDateTimeOriginal .DateTime ()) + , fDateTimeDigitized (tcDateTimeDigitized, exif.fDateTimeDigitized.DateTime ()) + + , fSubsecTime (tcSubsecTime, exif.fDateTime .Subseconds ()) + , fSubsecTimeOriginal (tcSubsecTimeOriginal, exif.fDateTimeOriginal .Subseconds ()) + , fSubsecTimeDigitized (tcSubsecTimeDigitized, exif.fDateTimeDigitized.Subseconds ()) + + , fOffsetTime (tcOffsetTime, exif.fDateTime .OffsetTime ()) + , fOffsetTimeOriginal (tcOffsetTimeOriginal, exif.fDateTimeOriginal .OffsetTime ()) + , fOffsetTimeDigitized (tcOffsetTimeDigitized, exif.fDateTimeDigitized.OffsetTime ()) + + , fMake (tcMake, exif.fMake) + + , fModel (tcModel, exif.fModel) + + , fArtist (tcArtist, exif.fArtist) + + , fSoftware (tcSoftware, exif.fSoftware) + + , fCopyright (tcCopyright, exif.fCopyright) + + , fImageDescription (tcImageDescription, exif.fImageDescription) + + , fSerialNumber (tcCameraSerialNumber, exif.fCameraSerialNumber) + + , fMakerNoteSafety (tcMakerNoteSafety, makerNoteSafe ? 1 : 0) + + , fMakerNote (tcMakerNote, ttUndefined, makerNoteLength, makerNoteData) + + , fUserComment (tcUserComment, exif.fUserComment) + + , fImageUniqueID (tcImageUniqueID, ttAscii, 33, fImageUniqueIDData) + + // EXIF 2.3 tags. + + , fCameraOwnerName (tcCameraOwnerNameExif, exif.fOwnerName ) + , fBodySerialNumber (tcCameraSerialNumberExif, exif.fCameraSerialNumber) + , fLensSpecification (tcLensSpecificationExif, fLensInfoData, 4 ) + , fLensMake (tcLensMakeExif, exif.fLensMake ) + , fLensModel (tcLensModelExif, exif.fLensName ) + , fLensSerialNumber (tcLensSerialNumberExif, exif.fLensSerialNumber ) + + // EXIF 2.3.1 tags. + + , fTemperature (tcTemperature, exif.fTemperature ) + , fHumidity (tcHumidity, exif.fHumidity ) + , fPressure (tcPressure, exif.fPressure ) + , fWaterDepth (tcWaterDepth, exif.fWaterDepth ) + , fAcceleration (tcAcceleration, exif.fAcceleration ) + , fCameraElevationAngle (tcCameraElevationAngle, exif.fCameraElevationAngle) + + , fGPSVersionID (tcGPSVersionID, fGPSVersionData, 4) + + , fGPSLatitudeRef (tcGPSLatitudeRef, exif.fGPSLatitudeRef) + , fGPSLatitude (tcGPSLatitude, exif.fGPSLatitude, 3) + + , fGPSLongitudeRef (tcGPSLongitudeRef, exif.fGPSLongitudeRef) + , fGPSLongitude (tcGPSLongitude, exif.fGPSLongitude, 3) + + , fGPSAltitudeRef (tcGPSAltitudeRef, (uint8) exif.fGPSAltitudeRef) + , fGPSAltitude (tcGPSAltitude, exif.fGPSAltitude ) + + , fGPSTimeStamp (tcGPSTimeStamp, exif.fGPSTimeStamp, 3) + + , fGPSSatellites (tcGPSSatellites , exif.fGPSSatellites ) + , fGPSStatus (tcGPSStatus , exif.fGPSStatus ) + , fGPSMeasureMode (tcGPSMeasureMode, exif.fGPSMeasureMode) + + , fGPSDOP (tcGPSDOP, exif.fGPSDOP) + + , fGPSSpeedRef (tcGPSSpeedRef, exif.fGPSSpeedRef) + , fGPSSpeed (tcGPSSpeed , exif.fGPSSpeed ) + + , fGPSTrackRef (tcGPSTrackRef, exif.fGPSTrackRef) + , fGPSTrack (tcGPSTrack , exif.fGPSTrack ) + + , fGPSImgDirectionRef (tcGPSImgDirectionRef, exif.fGPSImgDirectionRef) + , fGPSImgDirection (tcGPSImgDirection , exif.fGPSImgDirection ) + + , fGPSMapDatum (tcGPSMapDatum, exif.fGPSMapDatum) + + , fGPSDestLatitudeRef (tcGPSDestLatitudeRef, exif.fGPSDestLatitudeRef) + , fGPSDestLatitude (tcGPSDestLatitude, exif.fGPSDestLatitude, 3) + + , fGPSDestLongitudeRef (tcGPSDestLongitudeRef, exif.fGPSDestLongitudeRef) + , fGPSDestLongitude (tcGPSDestLongitude, exif.fGPSDestLongitude, 3) + + , fGPSDestBearingRef (tcGPSDestBearingRef, exif.fGPSDestBearingRef) + , fGPSDestBearing (tcGPSDestBearing , exif.fGPSDestBearing ) + + , fGPSDestDistanceRef (tcGPSDestDistanceRef, exif.fGPSDestDistanceRef) + , fGPSDestDistance (tcGPSDestDistance , exif.fGPSDestDistance ) + + , fGPSProcessingMethod (tcGPSProcessingMethod, exif.fGPSProcessingMethod) + , fGPSAreaInformation (tcGPSAreaInformation , exif.fGPSAreaInformation ) + + , fGPSDateStamp (tcGPSDateStamp, exif.fGPSDateStamp) + + , fGPSDifferential (tcGPSDifferential, (uint16) exif.fGPSDifferential) + + , fGPSHPositioningError (tcGPSHPositioningError, exif.fGPSHPositioningError) + + { + + if (exif.fExifVersion) + { + + fExifVersionData [0] = (uint8) (exif.fExifVersion >> 24); + fExifVersionData [1] = (uint8) (exif.fExifVersion >> 16); + fExifVersionData [2] = (uint8) (exif.fExifVersion >> 8); + fExifVersionData [3] = (uint8) (exif.fExifVersion ); + + fExifIFD.Add (&fExifVersion); + + } + + if (exif.fExposureTime.IsValid ()) + { + fExifIFD.Add (&fExposureTime); + } + + if (exif.fShutterSpeedValue.IsValid ()) + { + fExifIFD.Add (&fShutterSpeedValue); + } + + if (exif.fFNumber.IsValid ()) + { + fExifIFD.Add (&fFNumber); + } + + if (exif.fApertureValue.IsValid ()) + { + fExifIFD.Add (&fApertureValue); + } + + if (exif.fBrightnessValue.IsValid ()) + { + fExifIFD.Add (&fBrightnessValue); + } + + if (exif.fExposureBiasValue.IsValid ()) + { + fExifIFD.Add (&fExposureBiasValue); + } + + if (exif.fMaxApertureValue.IsValid ()) + { + fExifIFD.Add (&fMaxApertureValue); + } + + if (exif.fSubjectDistance.IsValid ()) + { + fExifIFD.Add (&fSubjectDistance); + } + + if (exif.fFocalLength.IsValid ()) + { + fExifIFD.Add (&fFocalLength); + } + + if (exif.fISOSpeedRatings [0] != 0) + { + fExifIFD.Add (&fISOSpeedRatings); + } + + if (exif.fFlash <= 0x0FFFF) + { + fExifIFD.Add (&fFlash); + } + + if (exif.fExposureProgram <= 0x0FFFF) + { + fExifIFD.Add (&fExposureProgram); + } + + if (exif.fMeteringMode <= 0x0FFFF) + { + fExifIFD.Add (&fMeteringMode); + } + + if (exif.fLightSource <= 0x0FFFF) + { + fExifIFD.Add (&fLightSource); + } + + if (exif.fSensingMethod <= 0x0FFFF) + { + fExifIFD.Add (&fSensingMethod); + } + + if (exif.fFocalLengthIn35mmFilm != 0) + { + fExifIFD.Add (&fFocalLength35mm); + } + + if (exif.fFileSource <= 0x0FF) + { + fExifIFD.Add (&fFileSource); + } + + if (exif.fSceneType <= 0x0FF) + { + fExifIFD.Add (&fSceneType); + } + + if (exif.fCFARepeatPatternRows && + exif.fCFARepeatPatternCols) + { + fExifIFD.Add (&fCFAPattern); + } + + if (exif.fCustomRendered <= 0x0FFFF) + { + fExifIFD.Add (&fCustomRendered); + } + + if (exif.fExposureMode <= 0x0FFFF) + { + fExifIFD.Add (&fExposureMode); + } + + if (exif.fWhiteBalance <= 0x0FFFF) + { + fExifIFD.Add (&fWhiteBalance); + } + + if (exif.fSceneCaptureType <= 0x0FFFF) + { + fExifIFD.Add (&fSceneCaptureType); + } + + if (exif.fGainControl <= 0x0FFFF) + { + fExifIFD.Add (&fGainControl); + } + + if (exif.fContrast <= 0x0FFFF) + { + fExifIFD.Add (&fContrast); + } + + if (exif.fSaturation <= 0x0FFFF) + { + fExifIFD.Add (&fSaturation); + } + + if (exif.fSharpness <= 0x0FFFF) + { + fExifIFD.Add (&fSharpness); + } + + if (exif.fSubjectDistanceRange <= 0x0FFFF) + { + fExifIFD.Add (&fSubjectDistanceRange); + } + + if (exif.fDigitalZoomRatio.IsValid ()) + { + fExifIFD.Add (&fDigitalZoomRatio); + } + + if (exif.fExposureIndex.IsValid ()) + { + fExifIFD.Add (&fExposureIndex); + } + + if (insideDNG) // TIFF-EP only tags + { + + if (exif.fImageNumber != 0xFFFFFFFF) + { + directory.Add (&fImageNumber); + } + + if (exif.fSelfTimerMode <= 0x0FFFF) + { + directory.Add (&fSelfTimerMode); + } + + if (exif.fBatteryLevelA.NotEmpty ()) + { + directory.Add (&fBatteryLevelA); + } + + else if (exif.fBatteryLevelR.IsValid ()) + { + directory.Add (&fBatteryLevelR); + } + + } + + if (exif.fColorSpace == 1 || + exif.fColorSpace == 0xFFFF) + { + fExifIFD.Add (&fColorSpace); + } + + if (exif.fFocalPlaneXResolution.IsValid ()) + { + fExifIFD.Add (&fFocalPlaneXResolution); + } + + if (exif.fFocalPlaneYResolution.IsValid ()) + { + fExifIFD.Add (&fFocalPlaneYResolution); + } + + if (exif.fFocalPlaneResolutionUnit <= 0x0FFFF) + { + fExifIFD.Add (&fFocalPlaneResolutionUnit); + } + + if (exif.fSubjectAreaCount) + { + + fSubjectAreaData [0] = (uint16) exif.fSubjectArea [0]; + fSubjectAreaData [1] = (uint16) exif.fSubjectArea [1]; + fSubjectAreaData [2] = (uint16) exif.fSubjectArea [2]; + fSubjectAreaData [3] = (uint16) exif.fSubjectArea [3]; + + fExifIFD.Add (&fSubjectArea); + + } + + if (exif.fLensInfo [0].IsValid () && + exif.fLensInfo [1].IsValid ()) + { + + fLensInfoData [0] = exif.fLensInfo [0]; + fLensInfoData [1] = exif.fLensInfo [1]; + fLensInfoData [2] = exif.fLensInfo [2]; + fLensInfoData [3] = exif.fLensInfo [3]; + + if (insideDNG) + { + directory.Add (&fLensInfo); + } + + } + + if (exif.fDateTime.IsValid ()) + { + + directory.Add (&fDateTime); + + if (exif.fDateTime.Subseconds ().NotEmpty ()) + { + fExifIFD.Add (&fSubsecTime); + } + + } + + if (exif.fDateTimeOriginal.IsValid ()) + { + + fExifIFD.Add (&fDateTimeOriginal); + + if (exif.fDateTimeOriginal.Subseconds ().NotEmpty ()) + { + fExifIFD.Add (&fSubsecTimeOriginal); + } + + } + + if (exif.fDateTimeDigitized.IsValid ()) + { + + fExifIFD.Add (&fDateTimeDigitized); + + if (exif.fDateTimeDigitized.Subseconds ().NotEmpty ()) + { + fExifIFD.Add (&fSubsecTimeDigitized); + } + + } + + if (exif.fMake.NotEmpty ()) + { + directory.Add (&fMake); + } + + if (exif.fModel.NotEmpty ()) + { + directory.Add (&fModel); + } + + if (exif.fArtist.NotEmpty ()) + { + directory.Add (&fArtist); + } + + if (exif.fSoftware.NotEmpty ()) + { + directory.Add (&fSoftware); + } + + if (exif.fCopyright.NotEmpty ()) + { + directory.Add (&fCopyright); + } + + if (exif.fImageDescription.NotEmpty ()) + { + directory.Add (&fImageDescription); + } + + if (exif.fCameraSerialNumber.NotEmpty () && insideDNG) + { + directory.Add (&fSerialNumber); + } + + if (makerNoteSafe && makerNoteData) + { + + directory.Add (&fMakerNoteSafety); + + fExifIFD.Add (&fMakerNote); + + } + + if (exif.fUserComment.NotEmpty ()) + { + fExifIFD.Add (&fUserComment); + } + + if (exif.fImageUniqueID.IsValid ()) + { + + for (uint32 j = 0; j < 16; j++) + { + + sprintf (fImageUniqueIDData + j * 2, + "%02X", + (unsigned) exif.fImageUniqueID.data [j]); + + } + + fExifIFD.Add (&fImageUniqueID); + + } + + if (exif.AtLeastVersion0230 ()) + { + + if (exif.fSensitivityType != 0) + { + + fExifIFD.Add (&fSensitivityType); + + } + + // Sensitivity tags. Do not write these extra tags unless the SensitivityType + // and PhotographicSensitivity (i.e., ISOSpeedRatings) values are valid. + + if (exif.fSensitivityType != 0 && + exif.fISOSpeedRatings [0] != 0) + { + + // Standard Output Sensitivity (SOS). + + if (exif.fStandardOutputSensitivity != 0) + { + fExifIFD.Add (&fStandardOutputSensitivity); + } + + // Recommended Exposure Index (REI). + + if (exif.fRecommendedExposureIndex != 0) + { + fExifIFD.Add (&fRecommendedExposureIndex); + } + + // ISO Speed. + + if (exif.fISOSpeed != 0) + { + + fExifIFD.Add (&fISOSpeed); + + if (exif.fISOSpeedLatitudeyyy != 0 && + exif.fISOSpeedLatitudezzz != 0) + { + + fExifIFD.Add (&fISOSpeedLatitudeyyy); + fExifIFD.Add (&fISOSpeedLatitudezzz); + + } + + } + + } + + if (exif.fOwnerName.NotEmpty ()) + { + fExifIFD.Add (&fCameraOwnerName); + } + + if (exif.fCameraSerialNumber.NotEmpty ()) + { + fExifIFD.Add (&fBodySerialNumber); + } + + if (exif.fLensInfo [0].IsValid () && + exif.fLensInfo [1].IsValid ()) + { + fExifIFD.Add (&fLensSpecification); + } + + if (exif.fLensMake.NotEmpty ()) + { + fExifIFD.Add (&fLensMake); + } + + if (exif.fLensName.NotEmpty ()) + { + fExifIFD.Add (&fLensModel); + } + + if (exif.fLensSerialNumber.NotEmpty ()) + { + fExifIFD.Add (&fLensSerialNumber); + } + + } + + if (exif.AtLeastVersion0231 ()) + { + + if (exif.fDateTime.IsValid () && + exif.fDateTime.TimeZone ().IsValid ()) + { + fExifIFD.Add (&fOffsetTime); + } + + if (exif.fDateTimeOriginal.IsValid () && + exif.fDateTimeOriginal.TimeZone ().IsValid ()) + { + fExifIFD.Add (&fOffsetTimeOriginal); + } + + if (exif.fDateTimeDigitized.IsValid () && + exif.fDateTimeDigitized.TimeZone ().IsValid ()) + { + fExifIFD.Add (&fOffsetTimeDigitized); + } + + if (exif.fTemperature.IsValid ()) + { + fExifIFD.Add (&fTemperature); + } + + if (exif.fHumidity.IsValid ()) + { + fExifIFD.Add (&fHumidity); + } + + if (exif.fPressure.IsValid ()) + { + fExifIFD.Add (&fPressure); + } + + if (exif.fWaterDepth.IsValid ()) + { + fExifIFD.Add (&fWaterDepth); + } + + if (exif.fAcceleration.IsValid ()) + { + fExifIFD.Add (&fAcceleration); + } + + if (exif.fCameraElevationAngle.IsValid ()) + { + fExifIFD.Add (&fCameraElevationAngle); + } + + } + + if (exif.fGPSVersionID) + { + + fGPSVersionData [0] = (uint8) (exif.fGPSVersionID >> 24); + fGPSVersionData [1] = (uint8) (exif.fGPSVersionID >> 16); + fGPSVersionData [2] = (uint8) (exif.fGPSVersionID >> 8); + fGPSVersionData [3] = (uint8) (exif.fGPSVersionID ); + + fGPSIFD.Add (&fGPSVersionID); + + } + + if (exif.fGPSLatitudeRef.NotEmpty () && + exif.fGPSLatitude [0].IsValid ()) + { + fGPSIFD.Add (&fGPSLatitudeRef); + fGPSIFD.Add (&fGPSLatitude ); + } + + if (exif.fGPSLongitudeRef.NotEmpty () && + exif.fGPSLongitude [0].IsValid ()) + { + fGPSIFD.Add (&fGPSLongitudeRef); + fGPSIFD.Add (&fGPSLongitude ); + } + + if (exif.fGPSAltitudeRef <= 0x0FF) + { + fGPSIFD.Add (&fGPSAltitudeRef); + } + + if (exif.fGPSAltitude.IsValid ()) + { + fGPSIFD.Add (&fGPSAltitude); + } + + if (exif.fGPSTimeStamp [0].IsValid ()) + { + fGPSIFD.Add (&fGPSTimeStamp); + } + + if (exif.fGPSSatellites.NotEmpty ()) + { + fGPSIFD.Add (&fGPSSatellites); + } + + if (exif.fGPSStatus.NotEmpty ()) + { + fGPSIFD.Add (&fGPSStatus); + } + + if (exif.fGPSMeasureMode.NotEmpty ()) + { + fGPSIFD.Add (&fGPSMeasureMode); + } + + if (exif.fGPSDOP.IsValid ()) + { + fGPSIFD.Add (&fGPSDOP); + } + + if (exif.fGPSSpeedRef.NotEmpty ()) + { + fGPSIFD.Add (&fGPSSpeedRef); + } + + if (exif.fGPSSpeed.IsValid ()) + { + fGPSIFD.Add (&fGPSSpeed); + } + + if (exif.fGPSTrackRef.NotEmpty ()) + { + fGPSIFD.Add (&fGPSTrackRef); + } + + if (exif.fGPSTrack.IsValid ()) + { + fGPSIFD.Add (&fGPSTrack); + } + + if (exif.fGPSImgDirectionRef.NotEmpty ()) + { + fGPSIFD.Add (&fGPSImgDirectionRef); + } + + if (exif.fGPSImgDirection.IsValid ()) + { + fGPSIFD.Add (&fGPSImgDirection); + } + + if (exif.fGPSMapDatum.NotEmpty ()) + { + fGPSIFD.Add (&fGPSMapDatum); + } + + if (exif.fGPSDestLatitudeRef.NotEmpty () && + exif.fGPSDestLatitude [0].IsValid ()) + { + fGPSIFD.Add (&fGPSDestLatitudeRef); + fGPSIFD.Add (&fGPSDestLatitude ); + } + + if (exif.fGPSDestLongitudeRef.NotEmpty () && + exif.fGPSDestLongitude [0].IsValid ()) + { + fGPSIFD.Add (&fGPSDestLongitudeRef); + fGPSIFD.Add (&fGPSDestLongitude ); + } + + if (exif.fGPSDestBearingRef.NotEmpty ()) + { + fGPSIFD.Add (&fGPSDestBearingRef); + } + + if (exif.fGPSDestBearing.IsValid ()) + { + fGPSIFD.Add (&fGPSDestBearing); + } + + if (exif.fGPSDestDistanceRef.NotEmpty ()) + { + fGPSIFD.Add (&fGPSDestDistanceRef); + } + + if (exif.fGPSDestDistance.IsValid ()) + { + fGPSIFD.Add (&fGPSDestDistance); + } + + if (exif.fGPSProcessingMethod.NotEmpty ()) + { + fGPSIFD.Add (&fGPSProcessingMethod); + } + + if (exif.fGPSAreaInformation.NotEmpty ()) + { + fGPSIFD.Add (&fGPSAreaInformation); + } + + if (exif.fGPSDateStamp.NotEmpty ()) + { + fGPSIFD.Add (&fGPSDateStamp); + } + + if (exif.fGPSDifferential <= 0x0FFFF) + { + fGPSIFD.Add (&fGPSDifferential); + } + + if (exif.AtLeastVersion0230 ()) + { + + if (exif.fGPSHPositioningError.IsValid ()) + { + fGPSIFD.Add (&fGPSHPositioningError); + } + + } + + AddLinks (directory); + + } + +/******************************************************************************/ + +void exif_tag_set::AddLinks (dng_tiff_directory &directory) + { + + if (fExifIFD.Size () != 0 && !fAddedExifLink) + { + + directory.Add (&fExifLink); + + fAddedExifLink = true; + + } + + if (fGPSIFD.Size () != 0 && !fAddedGPSLink) + { + + directory.Add (&fGPSLink); + + fAddedGPSLink = true; + + } + + } + +/******************************************************************************/ + +class range_tag_set + { + + private: + + uint32 fActiveAreaData [4]; + + tag_uint32_ptr fActiveArea; + + uint32 fMaskedAreaData [kMaxMaskedAreas * 4]; + + tag_uint32_ptr fMaskedAreas; + + tag_uint16_ptr fLinearizationTable; + + uint16 fBlackLevelRepeatDimData [2]; + + tag_uint16_ptr fBlackLevelRepeatDim; + + dng_urational fBlackLevelData [kMaxBlackPattern * + kMaxBlackPattern * + kMaxSamplesPerPixel]; + + tag_urational_ptr fBlackLevel; + + dng_memory_data fBlackLevelDeltaHData; + dng_memory_data fBlackLevelDeltaVData; + + tag_srational_ptr fBlackLevelDeltaH; + tag_srational_ptr fBlackLevelDeltaV; + + uint16 fWhiteLevelData16 [kMaxSamplesPerPixel]; + uint32 fWhiteLevelData32 [kMaxSamplesPerPixel]; + + tag_uint16_ptr fWhiteLevel16; + tag_uint32_ptr fWhiteLevel32; + + public: + + range_tag_set (dng_tiff_directory &directory, + const dng_negative &negative); + + }; + +/******************************************************************************/ + +range_tag_set::range_tag_set (dng_tiff_directory &directory, + const dng_negative &negative) + + : fActiveArea (tcActiveArea, + fActiveAreaData, + 4) + + , fMaskedAreas (tcMaskedAreas, + fMaskedAreaData, + 0) + + , fLinearizationTable (tcLinearizationTable, + NULL, + 0) + + , fBlackLevelRepeatDim (tcBlackLevelRepeatDim, + fBlackLevelRepeatDimData, + 2) + + , fBlackLevel (tcBlackLevel, + fBlackLevelData) + + , fBlackLevelDeltaHData () + , fBlackLevelDeltaVData () + + , fBlackLevelDeltaH (tcBlackLevelDeltaH) + , fBlackLevelDeltaV (tcBlackLevelDeltaV) + + , fWhiteLevel16 (tcWhiteLevel, + fWhiteLevelData16) + + , fWhiteLevel32 (tcWhiteLevel, + fWhiteLevelData32) + + { + + const dng_image &rawImage (negative.RawImage ()); + + const dng_linearization_info *rangeInfo = negative.GetLinearizationInfo (); + + if (rangeInfo) + { + + // ActiveArea: + + { + + const dng_rect &r = rangeInfo->fActiveArea; + + if (r.NotEmpty ()) + { + + fActiveAreaData [0] = r.t; + fActiveAreaData [1] = r.l; + fActiveAreaData [2] = r.b; + fActiveAreaData [3] = r.r; + + directory.Add (&fActiveArea); + + } + + } + + // MaskedAreas: + + if (rangeInfo->fMaskedAreaCount) + { + + fMaskedAreas.SetCount (rangeInfo->fMaskedAreaCount * 4); + + for (uint32 index = 0; index < rangeInfo->fMaskedAreaCount; index++) + { + + const dng_rect &r = rangeInfo->fMaskedArea [index]; + + fMaskedAreaData [index * 4 + 0] = r.t; + fMaskedAreaData [index * 4 + 1] = r.l; + fMaskedAreaData [index * 4 + 2] = r.b; + fMaskedAreaData [index * 4 + 3] = r.r; + + } + + directory.Add (&fMaskedAreas); + + } + + // LinearizationTable: + + if (rangeInfo->fLinearizationTable.Get ()) + { + + fLinearizationTable.SetData (rangeInfo->fLinearizationTable->Buffer_uint16 () ); + fLinearizationTable.SetCount (rangeInfo->fLinearizationTable->LogicalSize () >> 1); + + directory.Add (&fLinearizationTable); + + } + + // BlackLevelRepeatDim: + + { + + fBlackLevelRepeatDimData [0] = (uint16) rangeInfo->fBlackLevelRepeatRows; + fBlackLevelRepeatDimData [1] = (uint16) rangeInfo->fBlackLevelRepeatCols; + + directory.Add (&fBlackLevelRepeatDim); + + } + + // BlackLevel: + + { + + uint32 index = 0; + + for (uint16 v = 0; v < rangeInfo->fBlackLevelRepeatRows; v++) + { + + for (uint32 h = 0; h < rangeInfo->fBlackLevelRepeatCols; h++) + { + + for (uint32 c = 0; c < rawImage.Planes (); c++) + { + + fBlackLevelData [index++] = rangeInfo->BlackLevel (v, h, c); + + } + + } + + } + + fBlackLevel.SetCount (rangeInfo->fBlackLevelRepeatRows * + rangeInfo->fBlackLevelRepeatCols * rawImage.Planes ()); + + directory.Add (&fBlackLevel); + + } + + // BlackLevelDeltaH: + + if (rangeInfo->ColumnBlackCount ()) + { + + uint32 count = rangeInfo->ColumnBlackCount (); + + fBlackLevelDeltaHData.Allocate (count, sizeof (dng_srational)); + + dng_srational *blacks = (dng_srational *) fBlackLevelDeltaHData.Buffer (); + + for (uint32 col = 0; col < count; col++) + { + + blacks [col] = rangeInfo->ColumnBlack (col); + + } + + fBlackLevelDeltaH.SetData (blacks); + fBlackLevelDeltaH.SetCount (count ); + + directory.Add (&fBlackLevelDeltaH); + + } + + // BlackLevelDeltaV: + + if (rangeInfo->RowBlackCount ()) + { + + uint32 count = rangeInfo->RowBlackCount (); + + fBlackLevelDeltaVData.Allocate (count, sizeof (dng_srational)); + + dng_srational *blacks = (dng_srational *) fBlackLevelDeltaVData.Buffer (); + + for (uint32 row = 0; row < count; row++) + { + + blacks [row] = rangeInfo->RowBlack (row); + + } + + fBlackLevelDeltaV.SetData (blacks); + fBlackLevelDeltaV.SetCount (count ); + + directory.Add (&fBlackLevelDeltaV); + + } + + } + + else if (negative.RawImageBlackLevel ()) + { + + for (uint32 c = 0; c < rawImage.Planes (); c++) + { + + fBlackLevelData [c] = dng_urational (negative.RawImageBlackLevel (), 1); + + } + + fBlackLevel.SetCount (rawImage.Planes ()); + + directory.Add (&fBlackLevel); + + } + + // WhiteLevel: + + // Only use the 32-bit data type if we must use it since there + // are some lazy (non-Adobe) DNG readers out there. + + bool needs32 = false; + + fWhiteLevel16.SetCount (rawImage.Planes ()); + fWhiteLevel32.SetCount (rawImage.Planes ()); + + for (uint32 c = 0; c < fWhiteLevel16.Count (); c++) + { + + fWhiteLevelData32 [c] = negative.WhiteLevel (c); + + if (fWhiteLevelData32 [c] > 0x0FFFF) + { + needs32 = true; + } + + fWhiteLevelData16 [c] = (uint16) fWhiteLevelData32 [c]; + + } + + if (needs32) + { + directory.Add (&fWhiteLevel32); + } + + else + { + directory.Add (&fWhiteLevel16); + } + + } + +/******************************************************************************/ + +class mosaic_tag_set + { + + private: + + uint16 fCFARepeatPatternDimData [2]; + + tag_uint16_ptr fCFARepeatPatternDim; + + uint8 fCFAPatternData [kMaxCFAPattern * + kMaxCFAPattern]; + + tag_uint8_ptr fCFAPattern; + + uint8 fCFAPlaneColorData [kMaxColorPlanes]; + + tag_uint8_ptr fCFAPlaneColor; + + tag_uint16 fCFALayout; + + tag_uint32 fGreenSplit; + + public: + + mosaic_tag_set (dng_tiff_directory &directory, + const dng_mosaic_info &info); + + }; + +/******************************************************************************/ + +mosaic_tag_set::mosaic_tag_set (dng_tiff_directory &directory, + const dng_mosaic_info &info) + + : fCFARepeatPatternDim (tcCFARepeatPatternDim, + fCFARepeatPatternDimData, + 2) + + , fCFAPattern (tcCFAPattern, + fCFAPatternData) + + , fCFAPlaneColor (tcCFAPlaneColor, + fCFAPlaneColorData) + + , fCFALayout (tcCFALayout, + (uint16) info.fCFALayout) + + , fGreenSplit (tcBayerGreenSplit, + info.fBayerGreenSplit) + + { + + if (info.IsColorFilterArray ()) + { + + // CFARepeatPatternDim: + + fCFARepeatPatternDimData [0] = (uint16) info.fCFAPatternSize.v; + fCFARepeatPatternDimData [1] = (uint16) info.fCFAPatternSize.h; + + directory.Add (&fCFARepeatPatternDim); + + // CFAPattern: + + fCFAPattern.SetCount (info.fCFAPatternSize.v * + info.fCFAPatternSize.h); + + for (int32 r = 0; r < info.fCFAPatternSize.v; r++) + { + + for (int32 c = 0; c < info.fCFAPatternSize.h; c++) + { + + fCFAPatternData [r * info.fCFAPatternSize.h + c] = info.fCFAPattern [r] [c]; + + } + + } + + directory.Add (&fCFAPattern); + + // CFAPlaneColor: + + fCFAPlaneColor.SetCount (info.fColorPlanes); + + for (uint32 j = 0; j < info.fColorPlanes; j++) + { + + fCFAPlaneColorData [j] = info.fCFAPlaneColor [j]; + + } + + directory.Add (&fCFAPlaneColor); + + // CFALayout: + + fCFALayout.Set ((uint16) info.fCFALayout); + + directory.Add (&fCFALayout); + + // BayerGreenSplit: (only include if the pattern is a Bayer pattern) + + if (info.fCFAPatternSize == dng_point (2, 2) && + info.fColorPlanes == 3) + { + + directory.Add (&fGreenSplit); + + } + + } + + } + +/******************************************************************************/ + +class color_tag_set + { + + private: + + uint32 fColorChannels; + + tag_matrix fCameraCalibration1; + tag_matrix fCameraCalibration2; + + tag_string fCameraCalibrationSignature; + + tag_string fAsShotProfileName; + + dng_urational fAnalogBalanceData [4]; + + tag_urational_ptr fAnalogBalance; + + dng_urational fAsShotNeutralData [4]; + + tag_urational_ptr fAsShotNeutral; + + dng_urational fAsShotWhiteXYData [2]; + + tag_urational_ptr fAsShotWhiteXY; + + tag_urational fLinearResponseLimit; + + public: + + color_tag_set (dng_tiff_directory &directory, + const dng_negative &negative); + + }; + +/******************************************************************************/ + +color_tag_set::color_tag_set (dng_tiff_directory &directory, + const dng_negative &negative) + + : fColorChannels (negative.ColorChannels ()) + + , fCameraCalibration1 (tcCameraCalibration1, + negative.CameraCalibration1 ()) + + , fCameraCalibration2 (tcCameraCalibration2, + negative.CameraCalibration2 ()) + + , fCameraCalibrationSignature (tcCameraCalibrationSignature, + negative.CameraCalibrationSignature ()) + + , fAsShotProfileName (tcAsShotProfileName, + negative.AsShotProfileName ()) + + , fAnalogBalance (tcAnalogBalance, + fAnalogBalanceData, + fColorChannels) + + , fAsShotNeutral (tcAsShotNeutral, + fAsShotNeutralData, + fColorChannels) + + , fAsShotWhiteXY (tcAsShotWhiteXY, + fAsShotWhiteXYData, + 2) + + , fLinearResponseLimit (tcLinearResponseLimit, + negative.LinearResponseLimitR ()) + + { + + if (fColorChannels > 1) + { + + uint32 channels2 = fColorChannels * fColorChannels; + + if (fCameraCalibration1.Count () == channels2) + { + + directory.Add (&fCameraCalibration1); + + } + + if (fCameraCalibration2.Count () == channels2) + { + + directory.Add (&fCameraCalibration2); + + } + + if (fCameraCalibration1.Count () == channels2 || + fCameraCalibration2.Count () == channels2) + { + + if (negative.CameraCalibrationSignature ().NotEmpty ()) + { + + directory.Add (&fCameraCalibrationSignature); + + } + + } + + if (negative.AsShotProfileName ().NotEmpty ()) + { + + directory.Add (&fAsShotProfileName); + + } + + for (uint32 j = 0; j < fColorChannels; j++) + { + + fAnalogBalanceData [j] = negative.AnalogBalanceR (j); + + } + + directory.Add (&fAnalogBalance); + + if (negative.HasCameraNeutral ()) + { + + for (uint32 k = 0; k < fColorChannels; k++) + { + + fAsShotNeutralData [k] = negative.CameraNeutralR (k); + + } + + directory.Add (&fAsShotNeutral); + + } + + else if (negative.HasCameraWhiteXY ()) + { + + negative.GetCameraWhiteXY (fAsShotWhiteXYData [0], + fAsShotWhiteXYData [1]); + + directory.Add (&fAsShotWhiteXY); + + } + + directory.Add (&fLinearResponseLimit); + + } + + } + +/******************************************************************************/ + +class profile_tag_set + { + + private: + + tag_uint16 fCalibrationIlluminant1; + tag_uint16 fCalibrationIlluminant2; + + tag_matrix fColorMatrix1; + tag_matrix fColorMatrix2; + + tag_matrix fForwardMatrix1; + tag_matrix fForwardMatrix2; + + tag_matrix fReductionMatrix1; + tag_matrix fReductionMatrix2; + + tag_string fProfileName; + + tag_string fProfileCalibrationSignature; + + tag_uint32 fEmbedPolicyTag; + + tag_string fCopyrightTag; + + uint32 fHueSatMapDimData [3]; + + tag_uint32_ptr fHueSatMapDims; + + tag_data_ptr fHueSatData1; + tag_data_ptr fHueSatData2; + + tag_uint32 fHueSatMapEncodingTag; + + uint32 fLookTableDimData [3]; + + tag_uint32_ptr fLookTableDims; + + tag_data_ptr fLookTableData; + + tag_uint32 fLookTableEncodingTag; + + tag_srational fBaselineExposureOffsetTag; + + tag_uint32 fDefaultBlackRenderTag; + + dng_memory_data fToneCurveBuffer; + + tag_data_ptr fToneCurveTag; + + public: + + profile_tag_set (dng_tiff_directory &directory, + const dng_camera_profile &profile); + + }; + +/******************************************************************************/ + +profile_tag_set::profile_tag_set (dng_tiff_directory &directory, + const dng_camera_profile &profile) + + : fCalibrationIlluminant1 (tcCalibrationIlluminant1, + (uint16) profile.CalibrationIlluminant1 ()) + + , fCalibrationIlluminant2 (tcCalibrationIlluminant2, + (uint16) profile.CalibrationIlluminant2 ()) + + , fColorMatrix1 (tcColorMatrix1, + profile.ColorMatrix1 ()) + + , fColorMatrix2 (tcColorMatrix2, + profile.ColorMatrix2 ()) + + , fForwardMatrix1 (tcForwardMatrix1, + profile.ForwardMatrix1 ()) + + , fForwardMatrix2 (tcForwardMatrix2, + profile.ForwardMatrix2 ()) + + , fReductionMatrix1 (tcReductionMatrix1, + profile.ReductionMatrix1 ()) + + , fReductionMatrix2 (tcReductionMatrix2, + profile.ReductionMatrix2 ()) + + , fProfileName (tcProfileName, + profile.Name (), + false) + + , fProfileCalibrationSignature (tcProfileCalibrationSignature, + profile.ProfileCalibrationSignature (), + false) + + , fEmbedPolicyTag (tcProfileEmbedPolicy, + profile.EmbedPolicy ()) + + , fCopyrightTag (tcProfileCopyright, + profile.Copyright (), + false) + + , fHueSatMapDims (tcProfileHueSatMapDims, + fHueSatMapDimData, + 3) + + , fHueSatData1 (tcProfileHueSatMapData1, + ttFloat, + profile.HueSatDeltas1 ().DeltasCount () * 3, + profile.HueSatDeltas1 ().GetConstDeltas ()) + + , fHueSatData2 (tcProfileHueSatMapData2, + ttFloat, + profile.HueSatDeltas2 ().DeltasCount () * 3, + profile.HueSatDeltas2 ().GetConstDeltas ()) + + , fHueSatMapEncodingTag (tcProfileHueSatMapEncoding, + profile.HueSatMapEncoding ()) + + , fLookTableDims (tcProfileLookTableDims, + fLookTableDimData, + 3) + + , fLookTableData (tcProfileLookTableData, + ttFloat, + profile.LookTable ().DeltasCount () * 3, + profile.LookTable ().GetConstDeltas ()) + + , fLookTableEncodingTag (tcProfileLookTableEncoding, + profile.LookTableEncoding ()) + + , fBaselineExposureOffsetTag (tcBaselineExposureOffset, + profile.BaselineExposureOffset ()) + + , fDefaultBlackRenderTag (tcDefaultBlackRender, + profile.DefaultBlackRender ()) + + , fToneCurveBuffer () + + , fToneCurveTag (tcProfileToneCurve, + ttFloat, + 0, + NULL) + + { + + if (profile.HasColorMatrix1 ()) + { + + uint32 colorChannels = profile.ColorMatrix1 ().Rows (); + + directory.Add (&fCalibrationIlluminant1); + + directory.Add (&fColorMatrix1); + + if (fForwardMatrix1.Count () == colorChannels * 3) + { + + directory.Add (&fForwardMatrix1); + + } + + if (colorChannels > 3 && fReductionMatrix1.Count () == colorChannels * 3) + { + + directory.Add (&fReductionMatrix1); + + } + + if (profile.HasColorMatrix2 ()) + { + + directory.Add (&fCalibrationIlluminant2); + + directory.Add (&fColorMatrix2); + + if (fForwardMatrix2.Count () == colorChannels * 3) + { + + directory.Add (&fForwardMatrix2); + + } + + if (colorChannels > 3 && fReductionMatrix2.Count () == colorChannels * 3) + { + + directory.Add (&fReductionMatrix2); + + } + + } + + if (profile.Name ().NotEmpty ()) + { + + directory.Add (&fProfileName); + + } + + if (profile.ProfileCalibrationSignature ().NotEmpty ()) + { + + directory.Add (&fProfileCalibrationSignature); + + } + + directory.Add (&fEmbedPolicyTag); + + if (profile.Copyright ().NotEmpty ()) + { + + directory.Add (&fCopyrightTag); + + } + + bool haveHueSat1 = profile.HueSatDeltas1 ().IsValid (); + + bool haveHueSat2 = profile.HueSatDeltas2 ().IsValid () && + profile.HasColorMatrix2 (); + + if (haveHueSat1 || haveHueSat2) + { + + uint32 hueDivs = 0; + uint32 satDivs = 0; + uint32 valDivs = 0; + + if (haveHueSat1) + { + + profile.HueSatDeltas1 ().GetDivisions (hueDivs, + satDivs, + valDivs); + + } + + else + { + + profile.HueSatDeltas2 ().GetDivisions (hueDivs, + satDivs, + valDivs); + + } + + fHueSatMapDimData [0] = hueDivs; + fHueSatMapDimData [1] = satDivs; + fHueSatMapDimData [2] = valDivs; + + directory.Add (&fHueSatMapDims); + + // Don't bother including the ProfileHueSatMapEncoding tag unless it's + // non-linear. + + if (profile.HueSatMapEncoding () != encoding_Linear) + { + + directory.Add (&fHueSatMapEncodingTag); + + } + + } + + if (haveHueSat1) + { + + directory.Add (&fHueSatData1); + + } + + if (haveHueSat2) + { + + directory.Add (&fHueSatData2); + + } + + if (profile.HasLookTable ()) + { + + uint32 hueDivs = 0; + uint32 satDivs = 0; + uint32 valDivs = 0; + + profile.LookTable ().GetDivisions (hueDivs, + satDivs, + valDivs); + + fLookTableDimData [0] = hueDivs; + fLookTableDimData [1] = satDivs; + fLookTableDimData [2] = valDivs; + + directory.Add (&fLookTableDims); + + directory.Add (&fLookTableData); + + // Don't bother including the ProfileLookTableEncoding tag unless it's + // non-linear. + + if (profile.LookTableEncoding () != encoding_Linear) + { + + directory.Add (&fLookTableEncodingTag); + + } + + } + + // Don't bother including the BaselineExposureOffset tag unless it's both + // valid and non-zero. + + if (profile.BaselineExposureOffset ().IsValid ()) + { + + if (profile.BaselineExposureOffset ().As_real64 () != 0.0) + { + + directory.Add (&fBaselineExposureOffsetTag); + + } + + } + + if (profile.DefaultBlackRender () != defaultBlackRender_Auto) + { + + directory.Add (&fDefaultBlackRenderTag); + + } + + if (profile.ToneCurve ().IsValid ()) + { + + // Tone curve stored as pairs of 32-bit coordinates. Probably could do with + // 16-bits here, but should be small number of points so... + + uint32 toneCurvePoints = (uint32) (profile.ToneCurve ().fCoord.size ()); + + fToneCurveBuffer.Allocate (dng_safe_uint32 (toneCurvePoints) * 2u, + sizeof (real32)); + + real32 *points = fToneCurveBuffer.Buffer_real32 (); + + fToneCurveTag.SetCount (toneCurvePoints * 2); + fToneCurveTag.SetData (points); + + for (uint32 i = 0; i < toneCurvePoints; i++) + { + + // Transpose coordinates so they are in a more expected + // order (domain -> range). + + points [i * 2 ] = (real32) profile.ToneCurve ().fCoord [i].h; + points [i * 2 + 1] = (real32) profile.ToneCurve ().fCoord [i].v; + + } + + directory.Add (&fToneCurveTag); + + } + + } + + } + +/******************************************************************************/ + +tiff_dng_extended_color_profile::tiff_dng_extended_color_profile + (const dng_camera_profile &profile) + + : fProfile (profile) + + { + + } + +/******************************************************************************/ + +void tiff_dng_extended_color_profile::Put (dng_stream &stream, + bool includeModelRestriction) + { + + // Profile header. + + stream.Put_uint16 (stream.BigEndian () ? byteOrderMM : byteOrderII); + + stream.Put_uint16 (magicExtendedProfile); + + stream.Put_uint32 (8); + + // Profile tags. + + profile_tag_set tagSet (*this, fProfile); + + // Camera this profile is for. + + tag_string cameraModelTag (tcUniqueCameraModel, + fProfile.UniqueCameraModelRestriction ()); + + if (includeModelRestriction) + { + + if (fProfile.UniqueCameraModelRestriction ().NotEmpty ()) + { + + Add (&cameraModelTag); + + } + + } + + // Write it all out. + + dng_tiff_directory::Put (stream, offsetsRelativeToExplicitBase, 8); + + } + +/*****************************************************************************/ + +tag_dng_noise_profile::tag_dng_noise_profile (const dng_noise_profile &profile) + + : tag_data_ptr (tcNoiseProfile, + ttDouble, + 2 * profile.NumFunctions (), + fValues) + + { + + DNG_REQUIRE (profile.NumFunctions () <= kMaxColorPlanes, + "Too many noise functions in tag_dng_noise_profile."); + + for (uint32 i = 0; i < profile.NumFunctions (); i++) + { + + fValues [(2 * i) ] = profile.NoiseFunction (i).Scale (); + fValues [(2 * i) + 1] = profile.NoiseFunction (i).Offset (); + + } + + } + +/*****************************************************************************/ + +dng_image_writer::dng_image_writer () + { + + } + +/*****************************************************************************/ + +dng_image_writer::~dng_image_writer () + { + + } + +/*****************************************************************************/ + +uint32 dng_image_writer::CompressedBufferSize (const dng_ifd &ifd, + uint32 uncompressedSize) + { + + const dng_safe_uint32 safeUncompressedSize (uncompressedSize); + + switch (ifd.fCompression) + { + + case ccLZW: + { + + // Add lots of slop for LZW to expand data. + + return (safeUncompressedSize * 2u + 1024u).Get (); + + } + + case ccDeflate: + { + + // ZLib says maximum is source size + 0.1% + 12 bytes. + + const dng_safe_uint32 temp (uncompressedSize >> 8); + + return (safeUncompressedSize + temp + 64u).Get (); + + } + + case ccJPEG: + { + + // If we are saving lossless JPEG from an 8-bit image, reserve + // space to pad the data out to 16-bits. + + if (ifd.fBitsPerSample [0] <= 8) + { + + return (safeUncompressedSize * 2u).Get (); + + } + + break; + + } + + default: + break; + + } + + return 0; + + } + +/******************************************************************************/ + +static void EncodeDelta8 (uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 channels) + { + + const uint32 dRowStep = cols * channels; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = cols - 1; col > 0; col--) + { + + for (uint32 channel = 0; channel < channels; channel++) + { + + dPtr [col * channels + channel] -= dPtr [(col - 1) * channels + channel]; + + } + + } + + dPtr += dRowStep; + + } + + } + +/******************************************************************************/ + +static void EncodeDelta16 (uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 channels) + { + + const uint32 dRowStep = cols * channels; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = cols - 1; col > 0; col--) + { + + for (uint32 channel = 0; channel < channels; channel++) + { + + dPtr [col * channels + channel] -= dPtr [(col - 1) * channels + channel]; + + } + + } + + dPtr += dRowStep; + + } + + } + +/******************************************************************************/ + +static void EncodeDelta32 (uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 channels) + { + + const uint32 dRowStep = cols * channels; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = cols - 1; col > 0; col--) + { + + for (uint32 channel = 0; channel < channels; channel++) + { + + dPtr [col * channels + channel] -= dPtr [(col - 1) * channels + channel]; + + } + + } + + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +inline void EncodeDeltaBytes (uint8 *bytePtr, int32 cols, int32 channels) + { + + if (channels == 1) + { + + bytePtr += (cols - 1); + + uint8 this0 = bytePtr [0]; + + for (int32 col = 1; col < cols; col++) + { + + uint8 prev0 = bytePtr [-1]; + + this0 -= prev0; + + bytePtr [0] = this0; + + this0 = prev0; + + bytePtr -= 1; + + } + + } + + else if (channels == 3) + { + + bytePtr += (cols - 1) * 3; + + uint8 this0 = bytePtr [0]; + uint8 this1 = bytePtr [1]; + uint8 this2 = bytePtr [2]; + + for (int32 col = 1; col < cols; col++) + { + + uint8 prev0 = bytePtr [-3]; + uint8 prev1 = bytePtr [-2]; + uint8 prev2 = bytePtr [-1]; + + this0 -= prev0; + this1 -= prev1; + this2 -= prev2; + + bytePtr [0] = this0; + bytePtr [1] = this1; + bytePtr [2] = this2; + + this0 = prev0; + this1 = prev1; + this2 = prev2; + + bytePtr -= 3; + + } + + } + + else + { + + uint32 rowBytes = cols * channels; + + bytePtr += rowBytes - 1; + + for (uint32 col = channels; col < rowBytes; col++) + { + + bytePtr [0] -= bytePtr [-channels]; + + bytePtr--; + + } + + } + + } + +/*****************************************************************************/ + +static void EncodeFPDelta (uint8 *buffer, + uint8 *temp, + int32 cols, + int32 channels, + int32 bytesPerSample) + { + + int32 rowIncrement = cols * channels; + + if (bytesPerSample == 2) + { + + const uint8 *src = buffer; + + #if qDNGBigEndian + uint8 *dst0 = temp; + uint8 *dst1 = temp + rowIncrement; + #else + uint8 *dst1 = temp; + uint8 *dst0 = temp + rowIncrement; + #endif + + for (int32 col = 0; col < rowIncrement; ++col) + { + + dst0 [col] = src [0]; + dst1 [col] = src [1]; + + src += 2; + + } + + } + + else if (bytesPerSample == 3) + { + + const uint8 *src = buffer; + + uint8 *dst0 = temp; + uint8 *dst1 = temp + rowIncrement; + uint8 *dst2 = temp + rowIncrement * 2; + + for (int32 col = 0; col < rowIncrement; ++col) + { + + dst0 [col] = src [0]; + dst1 [col] = src [1]; + dst2 [col] = src [2]; + + src += 3; + + } + + } + + else + { + + const uint8 *src = buffer; + + #if qDNGBigEndian + uint8 *dst0 = temp; + uint8 *dst1 = temp + rowIncrement; + uint8 *dst2 = temp + rowIncrement * 2; + uint8 *dst3 = temp + rowIncrement * 3; + #else + uint8 *dst3 = temp; + uint8 *dst2 = temp + rowIncrement; + uint8 *dst1 = temp + rowIncrement * 2; + uint8 *dst0 = temp + rowIncrement * 3; + #endif + + for (int32 col = 0; col < rowIncrement; ++col) + { + + dst0 [col] = src [0]; + dst1 [col] = src [1]; + dst2 [col] = src [2]; + dst3 [col] = src [3]; + + src += 4; + + } + + } + + EncodeDeltaBytes (temp, cols*bytesPerSample, channels); + + memcpy (buffer, temp, cols*bytesPerSample*channels); + + } + +/*****************************************************************************/ + +void dng_image_writer::EncodePredictor (dng_host &host, + const dng_ifd &ifd, + dng_pixel_buffer &buffer, + AutoPtr &tempBuffer) + { + + switch (ifd.fPredictor) + { + + case cpHorizontalDifference: + case cpHorizontalDifferenceX2: + case cpHorizontalDifferenceX4: + { + + int32 xFactor = 1; + + if (ifd.fPredictor == cpHorizontalDifferenceX2) + { + xFactor = 2; + } + + else if (ifd.fPredictor == cpHorizontalDifferenceX4) + { + xFactor = 4; + } + + switch (buffer.fPixelType) + { + + case ttByte: + { + + EncodeDelta8 ((uint8 *) buffer.fData, + buffer.fArea.H (), + buffer.fArea.W () / xFactor, + buffer.fPlanes * xFactor); + + return; + + } + + case ttShort: + { + + EncodeDelta16 ((uint16 *) buffer.fData, + buffer.fArea.H (), + buffer.fArea.W () / xFactor, + buffer.fPlanes * xFactor); + + return; + + } + + case ttLong: + { + + EncodeDelta32 ((uint32 *) buffer.fData, + buffer.fArea.H (), + buffer.fArea.W () / xFactor, + buffer.fPlanes * xFactor); + + return; + + } + + default: + break; + + } + + break; + + } + + case cpFloatingPoint: + case cpFloatingPointX2: + case cpFloatingPointX4: + { + + int32 xFactor = 1; + + if (ifd.fPredictor == cpFloatingPointX2) + { + xFactor = 2; + } + + else if (ifd.fPredictor == cpFloatingPointX4) + { + xFactor = 4; + } + + if (buffer.fRowStep < 0) + { + ThrowProgramError ("Row step may not be negative"); + } + + dng_safe_uint32 tempBufferSize = + dng_safe_uint32 (buffer.fPixelSize) * + static_cast (buffer.fRowStep); + + if (!tempBuffer.Get () || + tempBuffer->LogicalSize () < tempBufferSize.Get ()) + { + + tempBuffer.Reset (host.Allocate (tempBufferSize.Get ())); + + } + + for (int32 row = buffer.fArea.t; row < buffer.fArea.b; row++) + { + + EncodeFPDelta ((uint8 *) buffer.DirtyPixel (row, buffer.fArea.l, buffer.fPlane), + tempBuffer->Buffer_uint8 (), + buffer.fArea.W () / xFactor, + buffer.fPlanes * xFactor, + buffer.fPixelSize); + + } + + return; + + } + + default: + break; + + } + + if (ifd.fPredictor != cpNullPredictor) + { + + ThrowProgramError (); + + } + + } + +/*****************************************************************************/ + +void dng_image_writer::ByteSwapBuffer (dng_host & /* host */, + dng_pixel_buffer &buffer) + { + + uint32 pixels = buffer.fRowStep * buffer.fArea.H (); + + switch (buffer.fPixelSize) + { + + case 2: + { + + DoSwapBytes16 ((uint16 *) buffer.fData, + pixels); + + break; + + } + + case 4: + { + + DoSwapBytes32 ((uint32 *) buffer.fData, + pixels); + + break; + + } + + default: + break; + + } + + } + +/*****************************************************************************/ + +void dng_image_writer::ReorderSubTileBlocks (const dng_ifd &ifd, + dng_pixel_buffer &buffer, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer) + { + + uint32 blockRows = ifd.fSubTileBlockRows; + uint32 blockCols = ifd.fSubTileBlockCols; + + uint32 rowBlocks = buffer.fArea.H () / blockRows; + uint32 colBlocks = buffer.fArea.W () / blockCols; + + int32 rowStep = buffer.fRowStep * buffer.fPixelSize; + int32 colStep = buffer.fColStep * buffer.fPixelSize; + + int32 rowBlockStep = rowStep * blockRows; + int32 colBlockStep = colStep * blockCols; + + uint32 blockColBytes = blockCols * buffer.fPlanes * buffer.fPixelSize; + + const uint8 *s0 = uncompressedBuffer->Buffer_uint8 (); + uint8 *d0 = subTileBlockBuffer->Buffer_uint8 (); + + for (uint32 rowBlock = 0; rowBlock < rowBlocks; rowBlock++) + { + + const uint8 *s1 = s0; + + for (uint32 colBlock = 0; colBlock < colBlocks; colBlock++) + { + + const uint8 *s2 = s1; + + for (uint32 blockRow = 0; blockRow < blockRows; blockRow++) + { + + for (uint32 j = 0; j < blockColBytes; j++) + { + + d0 [j] = s2 [j]; + + } + + d0 += blockColBytes; + + s2 += rowStep; + + } + + s1 += colBlockStep; + + } + + s0 += rowBlockStep; + + } + + // Copy back reordered pixels. + + DoCopyBytes (subTileBlockBuffer->Buffer (), + uncompressedBuffer->Buffer (), + uncompressedBuffer->LogicalSize ()); + + } + +/******************************************************************************/ + +class dng_lzw_compressor: private dng_uncopyable + { + + private: + + enum + { + kResetCode = 256, + kEndCode = 257, + kTableSize = 4096 + }; + + // Compressor nodes have two son pointers. The low order bit of + // the next code determines which pointer is used. This cuts the + // number of nodes searched for the next code by two on average. + + struct LZWCompressorNode + { + int16 final; + int16 son0; + int16 son1; + int16 brother; + }; + + dng_memory_data fBuffer; + + LZWCompressorNode *fTable; + + uint8 *fDstPtr; + + int32 fBitOffset; + + int32 fNextCode; + + int32 fCodeSize; + + public: + + dng_lzw_compressor (); + + void Compress (const uint8 *sPtr, + uint8 *dPtr, + uint32 sCount, + uint32 &dCount); + + private: + + void InitTable (); + + int32 SearchTable (int32 w, int32 k) const + { + + DNG_ASSERT ((w >= 0) && (w <= kTableSize), + "Bad w value in dng_lzw_compressor::SearchTable"); + + int32 son0 = fTable [w] . son0; + int32 son1 = fTable [w] . son1; + + // Branchless version of: + // int32 code = (k & 1) ? son1 : son0; + + int32 code = son0 + ((-((int32) (k & 1))) & (son1 - son0)); + + while (code > 0 && fTable [code].final != k) + { + code = fTable [code].brother; + } + + return code; + + } + + void AddTable (int32 w, int32 k); + + void PutCodeWord (int32 code); + + }; + +/******************************************************************************/ + +dng_lzw_compressor::dng_lzw_compressor () + + : fBuffer () + , fTable (NULL) + , fDstPtr (NULL) + , fBitOffset (0) + , fNextCode (0) + , fCodeSize (0) + + { + + fBuffer.Allocate (kTableSize, sizeof (LZWCompressorNode)); + + fTable = (LZWCompressorNode *) fBuffer.Buffer (); + + } + +/******************************************************************************/ + +void dng_lzw_compressor::InitTable () + { + + fCodeSize = 9; + + fNextCode = 258; + + LZWCompressorNode *node = &fTable [0]; + + for (int32 code = 0; code < 256; ++code) + { + + node->final = (int16) code; + node->son0 = -1; + node->son1 = -1; + node->brother = -1; + + node++; + + } + + } + +/******************************************************************************/ + +void dng_lzw_compressor::AddTable (int32 w, int32 k) + { + + DNG_ASSERT ((w >= 0) && (w <= kTableSize), + "Bad w value in dng_lzw_compressor::AddTable"); + + LZWCompressorNode *node = &fTable [w]; + + int32 nextCode = fNextCode; + + DNG_ASSERT ((nextCode >= 0) && (nextCode <= kTableSize), + "Bad fNextCode value in dng_lzw_compressor::AddTable"); + + LZWCompressorNode *node2 = &fTable [nextCode]; + + fNextCode++; + + int32 oldSon; + + if( k&1 ) + { + oldSon = node->son1; + node->son1 = (int16) nextCode; + } + else + { + oldSon = node->son0; + node->son0 = (int16) nextCode; + } + + node2->final = (int16) k; + node2->son0 = -1; + node2->son1 = -1; + node2->brother = (int16) oldSon; + + if (nextCode == (1 << fCodeSize) - 1) + { + if (fCodeSize != 12) + fCodeSize++; + } + + } + +/******************************************************************************/ + +void dng_lzw_compressor::PutCodeWord (int32 code) + { + + int32 bit = (int32) (fBitOffset & 7); + + int32 offset1 = fBitOffset >> 3; + int32 offset2 = (fBitOffset + fCodeSize - 1) >> 3; + + int32 shift1 = (fCodeSize + bit) - 8; + int32 shift2 = (fCodeSize + bit) - 16; + + uint8 byte1 = (uint8) (code >> shift1); + + uint8 *dstPtr1 = fDstPtr + offset1; + uint8 *dstPtr3 = fDstPtr + offset2; + + if (offset1 + 1 == offset2) + { + + uint8 byte2 = (uint8) (code << (-shift2)); + + if (bit) + *dstPtr1 |= byte1; + else + *dstPtr1 = byte1; + + *dstPtr3 = byte2; + + } + + else + { + + int32 shift3 = (fCodeSize + bit) - 24; + + uint8 byte2 = (uint8) (code >> shift2); + uint8 byte3 = (uint8) (code << (-shift3)); + + uint8 *dstPtr2 = fDstPtr + (offset1 + 1); + + if (bit) + *dstPtr1 |= byte1; + else + *dstPtr1 = byte1; + + *dstPtr2 = byte2; + + *dstPtr3 = byte3; + + } + + fBitOffset += fCodeSize; + + } + +/******************************************************************************/ + +void dng_lzw_compressor::Compress (const uint8 *sPtr, + uint8 *dPtr, + uint32 sCount, + uint32 &dCount) + { + + fDstPtr = dPtr; + + fBitOffset = 0; + + InitTable (); + + PutCodeWord (kResetCode); + + int32 code = -1; + + int32 pixel; + + if (sCount > 0) + { + + pixel = *sPtr; + sPtr = sPtr + 1; + code = pixel; + + sCount--; + + while (sCount--) + { + + pixel = *sPtr; + sPtr = sPtr + 1; + + int32 newCode = SearchTable (code, pixel); + + if (newCode == -1) + { + + PutCodeWord (code); + + if (fNextCode < 4093) + { + AddTable (code, pixel); + } + else + { + PutCodeWord (kResetCode); + InitTable (); + } + + code = pixel; + + } + + else + code = newCode; + + } + + } + + if (code != -1) + { + PutCodeWord (code); + AddTable (code, 0); + } + + PutCodeWord (kEndCode); + + dCount = (fBitOffset + 7) >> 3; + + } + +/*****************************************************************************/ + +#if qDNGUseLibJPEG + +/*****************************************************************************/ + +static void dng_error_exit (j_common_ptr cinfo) + { + + // Output message. + + (*cinfo->err->output_message) (cinfo); + + // Convert to a dng_exception. + + switch (cinfo->err->msg_code) + { + + case JERR_OUT_OF_MEMORY: + { + ThrowMemoryFull (); + break; + } + + default: + { + ThrowBadFormat (); + } + + } + + } + +/*****************************************************************************/ + +static void dng_output_message (j_common_ptr cinfo) + { + + // Format message to string. + + char buffer [JMSG_LENGTH_MAX]; + + (*cinfo->err->format_message) (cinfo, buffer); + + // Report the libjpeg message as a warning. + + ReportWarning ("libjpeg", buffer); + + } + +/*****************************************************************************/ + +struct dng_jpeg_stream_dest + { + + struct jpeg_destination_mgr pub; + + dng_stream *fStream; + + uint8 fBuffer [4096]; + + }; + +/*****************************************************************************/ + +static void dng_init_destination (j_compress_ptr cinfo) + { + + dng_jpeg_stream_dest *dest = (dng_jpeg_stream_dest *) cinfo->dest; + + dest->pub.next_output_byte = dest->fBuffer; + dest->pub.free_in_buffer = sizeof (dest->fBuffer); + + } + +/*****************************************************************************/ + +static boolean dng_empty_output_buffer (j_compress_ptr cinfo) + { + + dng_jpeg_stream_dest *dest = (dng_jpeg_stream_dest *) cinfo->dest; + + dest->fStream->Put (dest->fBuffer, sizeof (dest->fBuffer)); + + dest->pub.next_output_byte = dest->fBuffer; + dest->pub.free_in_buffer = sizeof (dest->fBuffer); + + return TRUE; + + } + +/*****************************************************************************/ + +static void dng_term_destination (j_compress_ptr cinfo) + { + + dng_jpeg_stream_dest *dest = (dng_jpeg_stream_dest *) cinfo->dest; + + uint32 datacount = sizeof (dest->fBuffer) - + (uint32) dest->pub.free_in_buffer; + + if (datacount) + { + dest->fStream->Put (dest->fBuffer, datacount); + } + + } + +/*****************************************************************************/ + +static void jpeg_set_adobe_quality (struct jpeg_compress_struct *cinfo, + int32 quality) + { + + // If out of range, map to default. + + if (quality < 0 || quality > 12) + { + quality = 10; + } + + // Adobe turns off chroma downsampling at high quality levels. + + bool useChromaDownsampling = (quality <= 6); + + // Approximate mapping from Adobe quality levels to LibJPEG levels. + + const int kLibJPEGQuality [13] = + { + 5, 11, 23, 34, 46, 63, 76, 77, 86, 90, 94, 97, 99 + }; + + quality = kLibJPEGQuality [quality]; + + jpeg_set_quality (cinfo, quality, TRUE); + + // LibJPEG defaults to always using chroma downsampling. Turn if off + // if we need it off to match Adobe. + + if (!useChromaDownsampling) + { + + cinfo->comp_info [0].h_samp_factor = 1; + cinfo->comp_info [0].h_samp_factor = 1; + + } + + } + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ + +void dng_image_writer::WriteData (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_pixel_buffer &buffer, + AutoPtr &compressedBuffer, + bool /* usingMultipleThreads */) + { + + switch (ifd.fCompression) + { + + case ccUncompressed: + { + + // Special case support for when we save to 8-bits from + // 16-bit data. + + if (ifd.fBitsPerSample [0] == 8 && buffer.fPixelType == ttShort) + { + + uint32 count = buffer.fRowStep * + buffer.fArea.H (); + + const uint16 *sPtr = (const uint16 *) buffer.fData; + + for (uint32 j = 0; j < count; j++) + { + + stream.Put_uint8 ((uint8) sPtr [j]); + + } + + } + + else + { + + // Swap bytes if required. + + if (stream.SwapBytes ()) + { + + ByteSwapBuffer (host, buffer); + + } + + // Write the bytes. + + stream.Put (buffer.fData, buffer.fRowStep * + buffer.fArea.H () * + buffer.fPixelSize); + + } + + break; + + } + + case ccLZW: + case ccDeflate: + { + + // Both these compression algorithms are byte based. The floating + // point predictor already does byte ordering, so don't ever swap + // when using it. + + if (stream.SwapBytes () && ifd.fPredictor != cpFloatingPoint) + { + + ByteSwapBuffer (host, + buffer); + + } + + // Run the compression algorithm. + + uint32 sBytes = buffer.fRowStep * + buffer.fArea.H () * + buffer.fPixelSize; + + uint8 *sBuffer = (uint8 *) buffer.fData; + + uint32 dBytes = 0; + + uint8 *dBuffer = compressedBuffer->Buffer_uint8 (); + + if (ifd.fCompression == ccLZW) + { + + dng_lzw_compressor lzwCompressor; + + lzwCompressor.Compress (sBuffer, + dBuffer, + sBytes, + dBytes); + + } + + else + { + + uLongf dCount = compressedBuffer->LogicalSize (); + + int32 level = Z_DEFAULT_COMPRESSION; + + if (ifd.fCompressionQuality >= Z_BEST_SPEED && + ifd.fCompressionQuality <= Z_BEST_COMPRESSION) + { + + level = ifd.fCompressionQuality; + + } + + int zResult = ::compress2 (dBuffer, + &dCount, + sBuffer, + sBytes, + level); + + if (zResult != Z_OK) + { + + ThrowMemoryFull (); + + } + + dBytes = (uint32) dCount; + + } + + if (dBytes > compressedBuffer->LogicalSize ()) + { + + ThrowOverflow ("Compression output buffer overflow"); + + } + + stream.Put (dBuffer, dBytes); + + return; + + } + + case ccJPEG: + { + + dng_pixel_buffer temp (buffer); + + if (buffer.fPixelType == ttByte) + { + + // The lossless JPEG encoder needs 16-bit data, so if we are + // are saving 8 bit data, we need to pad it out to 16-bits. + + temp.fData = compressedBuffer->Buffer (); + + temp.fPixelType = ttShort; + temp.fPixelSize = 2; + + temp.CopyArea (buffer, + buffer.fArea, + buffer.fPlane, + buffer.fPlanes); + + } + + EncodeLosslessJPEG ((const uint16 *) temp.fData, + temp.fArea.H (), + temp.fArea.W (), + temp.fPlanes, + ifd.fBitsPerSample [0], + temp.fRowStep, + temp.fColStep, + stream); + + break; + + } + + #if qDNGUseLibJPEG + + case ccLossyJPEG: + { + + struct jpeg_compress_struct cinfo; + + // Setup the error manager. + + struct jpeg_error_mgr jerr; + + cinfo.err = jpeg_std_error (&jerr); + + jerr.error_exit = dng_error_exit; + jerr.output_message = dng_output_message; + + try + { + + // Create the compression context. + + jpeg_create_compress (&cinfo); + + // Setup the destination manager to write to stream. + + dng_jpeg_stream_dest dest; + + dest.fStream = &stream; + + dest.pub.init_destination = dng_init_destination; + dest.pub.empty_output_buffer = dng_empty_output_buffer; + dest.pub.term_destination = dng_term_destination; + + cinfo.dest = &dest.pub; + + // Setup basic image info. + + cinfo.image_width = buffer.fArea.W (); + cinfo.image_height = buffer.fArea.H (); + cinfo.input_components = buffer.fPlanes; + + switch (buffer.fPlanes) + { + + case 1: + cinfo.in_color_space = JCS_GRAYSCALE; + break; + + case 3: + cinfo.in_color_space = JCS_RGB; + break; + + case 4: + cinfo.in_color_space = JCS_CMYK; + break; + + default: + ThrowProgramError (); + + } + + // Setup the compression parameters. + + jpeg_set_defaults (&cinfo); + + jpeg_set_adobe_quality (&cinfo, ifd.fCompressionQuality); + + // Write the JPEG header. + + jpeg_start_compress (&cinfo, TRUE); + + // Write the scanlines. + + for (int32 row = buffer.fArea.t; row < buffer.fArea.b; row++) + { + + uint8 *sampArray [1]; + + sampArray [0] = buffer.DirtyPixel_uint8 (row, + buffer.fArea.l, + 0); + + jpeg_write_scanlines (&cinfo, sampArray, 1); + + } + + // Cleanup. + + jpeg_finish_compress (&cinfo); + + jpeg_destroy_compress (&cinfo); + + } + + catch (...) + { + + jpeg_destroy_compress (&cinfo); + + throw; + + } + + return; + + } + + #endif + + default: + { + + ThrowProgramError (); + + } + + } + + } + +/******************************************************************************/ + +void dng_image_writer::EncodeJPEGPreview (dng_host &host, + const dng_image &image, + dng_jpeg_preview &preview, + int32 quality) + { + + #if qDNGUseLibJPEG + + dng_memory_stream stream (host.Allocator ()); + + struct jpeg_compress_struct cinfo; + + // Setup the error manager. + + struct jpeg_error_mgr jerr; + + cinfo.err = jpeg_std_error (&jerr); + + jerr.error_exit = dng_error_exit; + jerr.output_message = dng_output_message; + + try + { + + // Create the compression context. + + jpeg_create_compress (&cinfo); + + // Setup the destination manager to write to stream. + + dng_jpeg_stream_dest dest; + + dest.fStream = &stream; + + dest.pub.init_destination = dng_init_destination; + dest.pub.empty_output_buffer = dng_empty_output_buffer; + dest.pub.term_destination = dng_term_destination; + + cinfo.dest = &dest.pub; + + // Setup basic image info. + + cinfo.image_width = image.Bounds ().W (); + cinfo.image_height = image.Bounds ().H (); + cinfo.input_components = image.Planes (); + + switch (image.Planes ()) + { + + case 1: + cinfo.in_color_space = JCS_GRAYSCALE; + break; + + case 3: + cinfo.in_color_space = JCS_RGB; + break; + + default: + ThrowProgramError (); + + } + + // Setup the compression parameters. + + jpeg_set_defaults (&cinfo); + + jpeg_set_adobe_quality (&cinfo, quality); + + // Find some preview information based on the compression settings. + + preview.fPreviewSize = image.Size (); + + if (image.Planes () == 1) + { + + preview.fPhotometricInterpretation = piBlackIsZero; + + } + + else + { + + preview.fPhotometricInterpretation = piYCbCr; + + preview.fYCbCrSubSampling.h = cinfo.comp_info [0].h_samp_factor; + preview.fYCbCrSubSampling.v = cinfo.comp_info [0].v_samp_factor; + + } + + // Write the JPEG header. + + jpeg_start_compress (&cinfo, TRUE); + + // Write the scanlines. + + dng_pixel_buffer buffer (image.Bounds (), + 0, + image.Planes (), + ttByte, + pcInterleaved, + NULL); + + AutoPtr bufferData (host.Allocate (buffer.fRowStep)); + + buffer.fData = bufferData->Buffer (); + + for (uint32 row = 0; row < cinfo.image_height; row++) + { + + buffer.fArea.t = row; + buffer.fArea.b = row + 1; + + image.Get (buffer); + + uint8 *sampArray [1]; + + sampArray [0] = buffer.DirtyPixel_uint8 (row, + buffer.fArea.l, + 0); + + jpeg_write_scanlines (&cinfo, sampArray, 1); + + } + + // Cleanup. + + jpeg_finish_compress (&cinfo); + + jpeg_destroy_compress (&cinfo); + + } + + catch (...) + { + + jpeg_destroy_compress (&cinfo); + + throw; + + } + + preview.fCompressedData.Reset (stream.AsMemoryBlock (host.Allocator ())); + + #else + + (void) host; + (void) image; + (void) preview; + (void) quality; + + ThrowProgramError ("No JPEG encoder"); + + #endif + + } + +/*****************************************************************************/ + +void dng_image_writer::WriteTile (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + const dng_image &image, + const dng_rect &tileArea, + uint32 fakeChannels, + AutoPtr &compressedBuffer, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer, + AutoPtr &tempBuffer, + bool usingMultipleThreads) + { + + // Create pixel buffer to hold uncompressed tile. + + dng_pixel_buffer buffer (tileArea, + 0, + ifd.fSamplesPerPixel, + image.PixelType (), + pcInterleaved, + uncompressedBuffer->Buffer ()); + + // Get the uncompressed data. + + image.Get (buffer, dng_image::edge_zero); + + // Deal with sub-tile blocks. + + if (ifd.fSubTileBlockRows > 1) + { + + ReorderSubTileBlocks (ifd, + buffer, + uncompressedBuffer, + subTileBlockBuffer); + + } + + // Floating point depth conversion. + + if (ifd.fSampleFormat [0] == sfFloatingPoint) + { + + if (ifd.fBitsPerSample [0] == 16) + { + + uint32 *srcPtr = (uint32 *) buffer.fData; + uint16 *dstPtr = (uint16 *) buffer.fData; + + uint32 pixels = tileArea.W () * tileArea.H () * buffer.fPlanes; + + for (uint32 j = 0; j < pixels; j++) + { + + dstPtr [j] = DNG_FloatToHalf (srcPtr [j]); + + } + + buffer.fPixelSize = 2; + + } + + if (ifd.fBitsPerSample [0] == 24) + { + + uint32 *srcPtr = (uint32 *) buffer.fData; + uint8 *dstPtr = (uint8 *) buffer.fData; + + uint32 pixels = tileArea.W () * tileArea.H () * buffer.fPlanes; + + if (stream.BigEndian () || ifd.fPredictor == cpFloatingPoint || + ifd.fPredictor == cpFloatingPointX2 || + ifd.fPredictor == cpFloatingPointX4) + { + + for (uint32 j = 0; j < pixels; j++) + { + + DNG_FloatToFP24 (srcPtr [j], dstPtr); + + dstPtr += 3; + + } + + } + + else + { + + for (uint32 j = 0; j < pixels; j++) + { + + uint8 output [3]; + + DNG_FloatToFP24 (srcPtr [j], output); + + dstPtr [0] = output [2]; + dstPtr [1] = output [1]; + dstPtr [2] = output [0]; + + dstPtr += 3; + + } + + } + + buffer.fPixelSize = 3; + + } + + } + + // Run predictor. + + EncodePredictor (host, + ifd, + buffer, + tempBuffer); + + // Adjust pixel buffer for fake channels. + + if (fakeChannels > 1) + { + + buffer.fPlanes *= fakeChannels; + buffer.fColStep *= fakeChannels; + + buffer.fArea.r = buffer.fArea.l + (buffer.fArea.W () / fakeChannels); + + } + + // Compress (if required) and write out the data. + + WriteData (host, + ifd, + stream, + buffer, + compressedBuffer, + usingMultipleThreads); + + } + +/*****************************************************************************/ + +dng_write_tiles_task::dng_write_tiles_task + (dng_image_writer &imageWriter, + dng_host &host, + const dng_ifd &ifd, + dng_basic_tag_set &basic, + dng_stream &stream, + const dng_image &image, + uint32 fakeChannels, + uint32 tilesDown, + uint32 tilesAcross, + uint32 compressedSize, + uint32 uncompressedSize) + + : dng_area_task ("dng_write_tiles_task") + + , fImageWriter (imageWriter) + , fHost (host) + , fIFD (ifd) + , fBasic (basic) + , fStream (stream) + , fImage (image) + , fFakeChannels (fakeChannels) + , fTilesDown (tilesDown) + , fTilesAcross (tilesAcross) + , fCompressedSize (compressedSize) + , fUncompressedSize (uncompressedSize) + , fNextTileIndex (0) + , fMutex ("dng_write_tiles_task") + , fCondition () + , fTaskFailed (false) + , fWriteTileIndex (0) + + { + + fMinTaskArea = 16 * 16; + fUnitCell = dng_point (16, 16); + fMaxTileSize = dng_point (16, 16); + + } + +/*****************************************************************************/ + +void dng_write_tiles_task::Process (uint32 /* threadIndex */, + const dng_rect & /* tile */, + dng_abort_sniffer *sniffer) + { + + try + { + + AutoPtr compressedBuffer; + AutoPtr uncompressedBuffer; + AutoPtr subTileBlockBuffer; + AutoPtr tempBuffer; + + if (fCompressedSize) + { + compressedBuffer.Reset (fHost.Allocate (fCompressedSize)); + } + + if (fUncompressedSize) + { + uncompressedBuffer.Reset (fHost.Allocate (fUncompressedSize)); + } + + if (fIFD.fSubTileBlockRows > 1 && fUncompressedSize) + { + subTileBlockBuffer.Reset (fHost.Allocate (fUncompressedSize)); + } + + while (true) + { + + // Find tile index to compress. + + // Note: fNextTileIndex is atomic + + uint32 tileIndex = fNextTileIndex++; + + if (tileIndex >= fTilesDown * fTilesAcross) + { + return; + } + + // Encode the tile. This may be done concurrently. + + uint32 tileByteCount = 0; + + dng_memory_stream tileStream (fHost.Allocator ()); + + ProcessTask (tileIndex, + compressedBuffer, + uncompressedBuffer, + subTileBlockBuffer, + tempBuffer, + tileByteCount, + tileStream, + sniffer); + + // Wait until it is our turn to write tile. + + { + + dng_lock_mutex lock (&fMutex); + + while (!fTaskFailed && + fWriteTileIndex != tileIndex) + { + + fCondition.Wait (fMutex); + + } + + // If the task failed in another thread, that thread already + // threw an exception. + + if (fTaskFailed) + return; + + } + + // Write the encoded tile to the output stream. This must be done + // sequentially in ascending order of tileIndex (enforced by the + // above 'wait'). + + WriteTask (tileIndex, + tileByteCount, + tileStream, + sniffer); + + // Let other threads know it is safe to write to stream. + + { + + dng_lock_mutex lock (&fMutex); + + // If the task failed in another thread, that thread already + // threw an exception. + + if (fTaskFailed) + return; + + fWriteTileIndex++; + + fCondition.Broadcast (); + + } + + } + + } + + catch (...) + { + + // If first to fail, wake up any threads waiting on condition. + + bool needBroadcast = false; + + { + + dng_lock_mutex lock (&fMutex); + + needBroadcast = !fTaskFailed; + fTaskFailed = true; + + } + + if (needBroadcast) + fCondition.Broadcast (); + + throw; + + } + + } + +/*****************************************************************************/ + +void dng_write_tiles_task::ProcessTask + (uint32 tileIndex, + AutoPtr &compressedBuffer, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer, + AutoPtr &tempBuffer, + uint32 &tileByteCount, // output + dng_memory_stream &tileStream, // output + dng_abort_sniffer *sniffer) + { + + // This routine may be executed concurrently. + + dng_abort_sniffer::SniffForAbort (sniffer); + + // Compress tile. + + uint32 rowIndex = tileIndex / fTilesAcross; + + uint32 colIndex = tileIndex - rowIndex * fTilesAcross; + + dng_rect tileArea = fIFD.TileArea (rowIndex, colIndex); + + tileStream.SetLittleEndian (fStream.LittleEndian ()); + + dng_host host (&fHost.Allocator (), + sniffer); + + fImageWriter.WriteTile (host, + fIFD, + tileStream, + fImage, + tileArea, + fFakeChannels, + compressedBuffer, + uncompressedBuffer, + subTileBlockBuffer, + tempBuffer, + true); + + tileStream.Flush (); + + tileByteCount = (uint32) tileStream.Length (); + + tileStream.SetReadPosition (0); + + } + +/*****************************************************************************/ + +void dng_write_tiles_task::WriteTask (uint32 tileIndex, + uint32 tileByteCount, + dng_memory_stream &tileStream, + dng_abort_sniffer *sniffer) + { + + // This task must be executed serially and in sequential (ascending) order + // of tileIndex. + + dng_abort_sniffer::SniffForAbort (sniffer); + + // Remember this offset. + + uint32 tileOffset = (uint32) fStream.Position (); + + fBasic.SetTileOffset (tileIndex, tileOffset); + + // Copy tile stream for tile into main stream. + + tileStream.CopyToStream (fStream, tileByteCount); + + // Update tile count. + + fBasic.SetTileByteCount (tileIndex, tileByteCount); + + // Keep the tiles on even byte offsets. + + if (tileByteCount & 1) + { + fStream.Put_uint8 (0); + } + + } + +/*****************************************************************************/ + +void dng_image_writer::DoWriteTiles (dng_host &host, + const dng_ifd &ifd, + dng_basic_tag_set &basic, + dng_stream &stream, + const dng_image &image, + uint32 fakeChannels, + uint32 tilesDown, + uint32 tilesAcross, + uint32 compressedSize, + const dng_safe_uint32 &uncompressedSize) + { + + uint32 threadCount = Min_uint32 (tilesDown * tilesAcross, + host.PerformAreaTaskThreads ()); + + dng_write_tiles_task task (*this, + host, + ifd, + basic, + stream, + image, + fakeChannels, + tilesDown, + tilesAcross, + compressedSize, + uncompressedSize.Get ()); + + host.PerformAreaTask (task, + dng_rect (0, 0, 16, 16 * threadCount)); + + } + +/*****************************************************************************/ + +void dng_image_writer::WriteImage (dng_host &host, + const dng_ifd &ifd, + dng_basic_tag_set &basic, + dng_stream &stream, + const dng_image &image, + uint32 fakeChannels) + { + + // Deal with row interleaved images. + + if (ifd.fRowInterleaveFactor > 1 && + ifd.fRowInterleaveFactor < ifd.fImageLength) + { + + dng_ifd tempIFD (ifd); + + tempIFD.fRowInterleaveFactor = 1; + + dng_row_interleaved_image tempImage (*((dng_image *) &image), + ifd.fRowInterleaveFactor); + + WriteImage (host, + tempIFD, + basic, + stream, + tempImage, + fakeChannels); + + return; + + } + + // Compute basic information. + + dng_safe_uint32 bytesPerSample (TagTypeSize (image.PixelType ())); + + dng_safe_uint32 bytesPerPixel = bytesPerSample * ifd.fSamplesPerPixel; + + dng_safe_uint32 tileRowBytes = bytesPerPixel * ifd.fTileWidth; + + // If we can compute the number of bytes needed to store the + // data, we can split the write for each tile into sub-tiles. + + uint32 subTileLength = ifd.fTileLength; + + if (ifd.TileByteCount (ifd.TileArea (0, 0)) != 0) + { + + subTileLength = Pin_uint32 (ifd.fSubTileBlockRows, + kImageBufferSize / tileRowBytes.Get (), + ifd.fTileLength); + + // Don't split sub-tiles across subTileBlocks. + + subTileLength = subTileLength / ifd.fSubTileBlockRows + * ifd.fSubTileBlockRows; + + } + + // Find size of uncompressed buffer. + + dng_safe_uint32 uncompressedSize = tileRowBytes * subTileLength; + + // Find size of compressed buffer, if required. + + uint32 compressedSize = CompressedBufferSize (ifd, uncompressedSize.Get ()); + + // See if we can do this write using multiple threads. + + uint32 tilesAcross = ifd.TilesAcross (); + uint32 tilesDown = ifd.TilesDown (); + + bool useMultipleThreads = (tilesDown * tilesAcross >= 2) && + (host.PerformAreaTaskThreads () > 1) && + (subTileLength == ifd.fTileLength) && + (ifd.fCompression != ccUncompressed); + + if (useMultipleThreads) + { + + DoWriteTiles (host, + ifd, + basic, + stream, + image, + fakeChannels, + tilesDown, + tilesAcross, + compressedSize, + uncompressedSize.Get ()); + + } + + else + { + + AutoPtr compressedBuffer; + AutoPtr uncompressedBuffer; + AutoPtr subTileBlockBuffer; + AutoPtr tempBuffer; + + if (compressedSize) + { + compressedBuffer.Reset (host.Allocate (compressedSize)); + } + + if (uncompressedSize.Get ()) + { + uncompressedBuffer.Reset (host.Allocate (uncompressedSize.Get ())); + } + + if (ifd.fSubTileBlockRows > 1 && uncompressedSize.Get ()) + { + subTileBlockBuffer.Reset (host.Allocate (uncompressedSize.Get ())); + } + + // Write out each tile. + + uint32 tileIndex = 0; + + for (uint32 rowIndex = 0; rowIndex < tilesDown; rowIndex++) + { + + for (uint32 colIndex = 0; colIndex < tilesAcross; colIndex++) + { + + // Remember this offset. + + uint32 tileOffset = (uint32) stream.Position (); + + basic.SetTileOffset (tileIndex, tileOffset); + + // Split tile into sub-tiles if possible. + + dng_rect tileArea = ifd.TileArea (rowIndex, colIndex); + + uint32 subTileCount = (tileArea.H () + subTileLength - 1) / + subTileLength; + + for (uint32 subIndex = 0; subIndex < subTileCount; subIndex++) + { + + host.SniffForAbort (); + + dng_rect subArea (tileArea); + + subArea.t = tileArea.t + subIndex * subTileLength; + + subArea.b = Min_int32 (subArea.t + subTileLength, + tileArea.b); + + // Write the sub-tile. + + WriteTile (host, + ifd, + stream, + image, + subArea, + fakeChannels, + compressedBuffer, + uncompressedBuffer, + subTileBlockBuffer, + tempBuffer, + useMultipleThreads); + + } + + // Update tile count. + + uint32 tileByteCount = (uint32) stream.Position () - tileOffset; + + basic.SetTileByteCount (tileIndex, tileByteCount); + + tileIndex++; + + // Keep the tiles on even byte offsets. + + if (tileByteCount & 1) + { + stream.Put_uint8 (0); + } + + } + + } + + } + + } + +/*****************************************************************************/ + +static void CopyString (const dng_xmp &oldXMP, + dng_xmp &newXMP, + const char *ns, + const char *path, + dng_string *exif = NULL) + { + + dng_string s; + + if (oldXMP.GetString (ns, path, s)) + { + + if (s.NotEmpty ()) + { + + newXMP.SetString (ns, path, s); + + if (exif) + { + + *exif = s; + + } + + } + + } + + } + +/*****************************************************************************/ + +static void CopyStringList (const dng_xmp &oldXMP, + dng_xmp &newXMP, + const char *ns, + const char *path, + bool isBag) + { + + dng_string_list list; + + if (oldXMP.GetStringList (ns, path, list)) + { + + if (list.Count ()) + { + + newXMP.SetStringList (ns, path, list, isBag); + + } + + } + + } + +/*****************************************************************************/ + +static void CopyAltLangDefault (const dng_xmp &oldXMP, + dng_xmp &newXMP, + const char *ns, + const char *path, + dng_string *exif = NULL) + { + + dng_string s; + + if (oldXMP.GetAltLangDefault (ns, path, s)) + { + + if (s.NotEmpty ()) + { + + newXMP.SetAltLangDefault (ns, path, s); + + if (exif) + { + + *exif = s; + + } + + } + + } + + } + +/*****************************************************************************/ + +static void CopyStructField (const dng_xmp &oldXMP, + dng_xmp &newXMP, + const char *ns, + const char *path, + const char *field) + { + + dng_string s; + + if (oldXMP.GetStructField (ns, path, ns, field, s)) + { + + if (s.NotEmpty ()) + { + + newXMP.SetStructField (ns, path, ns, field, s); + + } + + } + + } + +/*****************************************************************************/ + +static void CopyBoolean (const dng_xmp &oldXMP, + dng_xmp &newXMP, + const char *ns, + const char *path) + { + + bool b; + + if (oldXMP.GetBoolean (ns, path, b)) + { + + newXMP.SetBoolean (ns, path, b); + + } + + } + +/*****************************************************************************/ + +void dng_image_writer::CleanUpMetadata (dng_host &host, + dng_metadata &metadata, + dng_metadata_subset metadataSubset, + const char *dstMIME, + const char *software) + { + + if (metadata.GetXMP () && metadata.GetExif ()) + { + + dng_xmp &newXMP (*metadata.GetXMP ()); + dng_exif &newEXIF (*metadata.GetExif ()); + + // Update software tag. + + if (software) + { + + newEXIF.fSoftware.Set (software); + + newXMP.Set (XMP_NS_XAP, + "CreatorTool", + software); + + } + + #if qDNGXMPDocOps + + newXMP.DocOpsPrepareForSave (metadata.SourceMIME ().Get (), + dstMIME); + + #else + + metadata.UpdateDateTimeToNow (); + + #endif + + // Update EXIF version to at least 2.3.1 so all the exif tags + // can be written. + + if (!newEXIF.AtLeastVersion0231 ()) + { + + newEXIF.SetVersion0231 (); + + newXMP.Set (XMP_NS_EXIF, "ExifVersion", "0231"); + + } + + // Resync EXIF, remove EXIF tags from XMP. + + newXMP.SyncExif (newEXIF, + metadata.GetOriginalExif (), + false, + true); + + // Deal with ImageIngesterPro bug. This program is adding lots of + // empty metadata strings into the XMP, which is screwing up Adobe CS4. + // We are saving a new file, so this is a chance to clean up this mess. + + newXMP.RemoveEmptyStringsAndArrays (XMP_NS_DC); + newXMP.RemoveEmptyStringsAndArrays (XMP_NS_XAP); + newXMP.RemoveEmptyStringsAndArrays (XMP_NS_PHOTOSHOP); + newXMP.RemoveEmptyStringsAndArrays (XMP_NS_IPTC); + newXMP.RemoveEmptyStringsAndArrays (XMP_NS_XAP_RIGHTS); + newXMP.RemoveEmptyStringsAndArrays ("http://ns.iview-multimedia.com/mediapro/1.0/"); + + // Process metadata subset. + + if (metadataSubset == kMetadataSubset_CopyrightOnly || + metadataSubset == kMetadataSubset_CopyrightAndContact) + { + + dng_xmp oldXMP (newXMP ); + dng_exif oldEXIF (newEXIF); + + // For these options, we start from nothing, and only fill in the + // fields that we absolutely need. + + newXMP.RemoveProperties (NULL); + + newEXIF.SetEmpty (); + + metadata.ClearMakerNote (); + + // Exif version is always required. + + newEXIF.fExifVersion = oldEXIF.fExifVersion; + + // Move copyright related fields over. + + CopyAltLangDefault (oldXMP, + newXMP, + XMP_NS_DC, + "rights", + &newEXIF.fCopyright); + + CopyAltLangDefault (oldXMP, + newXMP, + XMP_NS_XAP_RIGHTS, + "UsageTerms"); + + CopyString (oldXMP, + newXMP, + XMP_NS_XAP_RIGHTS, + "WebStatement"); + + CopyBoolean (oldXMP, + newXMP, + XMP_NS_XAP_RIGHTS, + "Marked"); + + #if qDNGXMPDocOps + + // Include basic DocOps fields, but not the full history. + + CopyString (oldXMP, + newXMP, + XMP_NS_MM, + "OriginalDocumentID"); + + CopyString (oldXMP, + newXMP, + XMP_NS_MM, + "DocumentID"); + + CopyString (oldXMP, + newXMP, + XMP_NS_MM, + "InstanceID"); + + CopyString (oldXMP, + newXMP, + XMP_NS_XAP, + "MetadataDate"); + + #endif + + // Copyright and Contact adds the contact info fields. + + if (metadataSubset == kMetadataSubset_CopyrightAndContact) + { + + // Note: Save for Web is not including the dc:creator list, but it + // is part of the IPTC contract info metadata panel, so I + // think it should be copied as part of the contact info. + + CopyStringList (oldXMP, + newXMP, + XMP_NS_DC, + "creator", + false); + + // The first string dc:creator list is mirrored to the + // the exif artist tag, so copy that also. + + newEXIF.fArtist = oldEXIF.fArtist; + + // Copy other contact fields. + + CopyString (oldXMP, + newXMP, + XMP_NS_PHOTOSHOP, + "AuthorsPosition"); + + CopyStructField (oldXMP, + newXMP, + XMP_NS_IPTC, + "CreatorContactInfo", + "CiEmailWork"); + + CopyStructField (oldXMP, + newXMP, + XMP_NS_IPTC, + "CreatorContactInfo", + "CiAdrExtadr"); + + CopyStructField (oldXMP, + newXMP, + XMP_NS_IPTC, + "CreatorContactInfo", + "CiAdrCity"); + + CopyStructField (oldXMP, + newXMP, + XMP_NS_IPTC, + "CreatorContactInfo", + "CiAdrRegion"); + + CopyStructField (oldXMP, + newXMP, + XMP_NS_IPTC, + "CreatorContactInfo", + "CiAdrPcode"); + + CopyStructField (oldXMP, + newXMP, + XMP_NS_IPTC, + "CreatorContactInfo", + "CiAdrCtry"); + + CopyStructField (oldXMP, + newXMP, + XMP_NS_IPTC, + "CreatorContactInfo", + "CiTelWork"); + + CopyStructField (oldXMP, + newXMP, + XMP_NS_IPTC, + "CreatorContactInfo", + "CiUrlWork"); + + CopyAltLangDefault (oldXMP, + newXMP, + XMP_NS_DC, + "title"); + + } + + } + + else if (metadataSubset == kMetadataSubset_AllExceptCameraInfo || + metadataSubset == kMetadataSubset_AllExceptCameraAndLocation || + metadataSubset == kMetadataSubset_AllExceptLocationInfo || + metadataSubset == KMetadataSubset_AllExceptCameraRawInfo || + metadataSubset == KMetadataSubset_AllExceptCameraRawInfoAndLocation) + { + + dng_xmp oldXMP (newXMP ); + dng_exif oldEXIF (newEXIF); + + if (metadataSubset == kMetadataSubset_AllExceptCameraInfo || + metadataSubset == kMetadataSubset_AllExceptCameraAndLocation || + metadataSubset == KMetadataSubset_AllExceptCameraRawInfo || + metadataSubset == KMetadataSubset_AllExceptCameraRawInfoAndLocation) + { + + // Remove Camera Raw info. + + newXMP.RemoveProperties(XMP_NS_CRS); + newXMP.RemoveProperties(XMP_NS_CRSS); + newXMP.RemoveProperties(XMP_NS_CRX); + + // Remove DocOps history, since it contains the original + // camera format and processing history. + + newXMP.Remove(XMP_NS_MM, "History"); + + // Remove Panorama info. + + newXMP.RemoveProperties(XMP_NS_PANO); + + } + + if (metadataSubset == kMetadataSubset_AllExceptCameraInfo || + metadataSubset == kMetadataSubset_AllExceptCameraAndLocation) + { + + // This removes most of the EXIF info, so just copy the fields + // we are not deleting. + + newEXIF.SetEmpty (); + + newEXIF.fImageDescription = oldEXIF.fImageDescription; // Note: Differs from SFW + newEXIF.fSoftware = oldEXIF.fSoftware; + newEXIF.fArtist = oldEXIF.fArtist; + newEXIF.fCopyright = oldEXIF.fCopyright; + newEXIF.fCopyright2 = oldEXIF.fCopyright2; + newEXIF.fDateTime = oldEXIF.fDateTime; + newEXIF.fDateTimeOriginal = oldEXIF.fDateTimeOriginal; + newEXIF.fDateTimeDigitized = oldEXIF.fDateTimeDigitized; + newEXIF.fExifVersion = oldEXIF.fExifVersion; + newEXIF.fImageUniqueID = oldEXIF.fImageUniqueID; + + newEXIF.CopyGPSFrom (oldEXIF); + + // Remove exif info from XMP. + + newXMP.RemoveProperties (XMP_NS_EXIF); + newXMP.RemoveProperties (XMP_NS_EXIFEX); + newXMP.RemoveProperties (XMP_NS_AUX); + + // MakerNote contains camera info. + + metadata.ClearMakerNote (); + + } + + if (metadataSubset == kMetadataSubset_AllExceptLocationInfo || + metadataSubset == kMetadataSubset_AllExceptCameraAndLocation || + metadataSubset == KMetadataSubset_AllExceptCameraRawInfoAndLocation) + { + + // Remove GPS fields. + + dng_exif blankExif; + + newEXIF.CopyGPSFrom (blankExif); + + // Remove MakerNote just in case, because we don't know + // all of what is in it. + + metadata.ClearMakerNote (); + + // Remove XMP & IPTC location fields. + + newXMP.Remove (XMP_NS_PHOTOSHOP, "City"); + newXMP.Remove (XMP_NS_PHOTOSHOP, "State"); + newXMP.Remove (XMP_NS_PHOTOSHOP, "Country"); + newXMP.Remove (XMP_NS_IPTC, "Location"); + newXMP.Remove (XMP_NS_IPTC, "CountryCode"); + newXMP.Remove (XMP_NS_IPTC_EXT, "LocationCreated"); + newXMP.Remove (XMP_NS_IPTC_EXT, "LocationShown"); + + } + + } + + // Rebuild the legacy IPTC block, if needed. + + bool isTIFF = (strcmp (dstMIME, "image/tiff") == 0); + bool isDNG = (strcmp (dstMIME, "image/dng" ) == 0); + + if (!isDNG) + { + + metadata.RebuildIPTC (host.Allocator (), + isTIFF); + + } + + else + { + + metadata.ClearIPTC (); + + } + + // Clear format related XMP. + + newXMP.ClearOrientation (); + + newXMP.ClearImageInfo (); + + newXMP.RemoveProperties (XMP_NS_DNG); + + // Clear default camera raw settings if not writing to DNG. + + if (!isDNG) + { + + newXMP.RemoveProperties (XMP_NS_CRD); + + } + + // All the formats we care about already keep the IPTC digest + // elsewhere, do we don't need to write it to the XMP. + + newXMP.ClearIPTCDigest (); + + // Make sure that sidecar specific tags never get written to files. + + newXMP.Remove (XMP_NS_PHOTOSHOP, "SidecarForExtension"); + newXMP.Remove (XMP_NS_PHOTOSHOP, "EmbeddedXMPDigest"); + + } + + } + +/*****************************************************************************/ + +void dng_image_writer::UpdateExifColorSpaceTag (dng_metadata &metadata, + const void *profileData, + const uint32 profileSize) + { + + if (!metadata.GetExif ()) + { + return; + } + + dng_exif &exif = *metadata.GetExif (); + + uint32 tagValue = 0xFFFF; + + if (profileData && profileSize) + { + + // Is the color profile sRGB IEC61966-2.1? + + uint32 sRGB_size = 0; + const uint8 *sRGB_data = 0; + + if (dng_space_sRGB::Get ().ICCProfile (sRGB_size, + sRGB_data)) + { + + if ((sRGB_size == profileSize) && + !memcmp (profileData, + (const void *) sRGB_data, + (size_t) sRGB_size)) + { + + // Yes. + + tagValue = 1; + + } + + } + +#if qLinux + exif.fColorSpace = tagValue; + } +#else + } + exif.fColorSpace = tagValue; +#endif + } + +/*****************************************************************************/ + +void dng_image_writer::WriteTIFF (dng_host &host, + dng_stream &stream, + const dng_image &image, + uint32 photometricInterpretation, + uint32 compression, + dng_negative *negative, + const dng_color_space *space, + const dng_resolution *resolution, + const dng_jpeg_preview *thumbnail, + const dng_memory_block *imageResources, + dng_metadata_subset metadataSubset, + bool hasTransparency) + { + + WriteTIFF (host, + stream, + image, + photometricInterpretation, + compression, + negative ? &(negative->Metadata ()) : NULL, + space, + resolution, + thumbnail, + imageResources, + metadataSubset, + hasTransparency); + + } + +/*****************************************************************************/ + +void dng_image_writer::WriteTIFF (dng_host &host, + dng_stream &stream, + const dng_image &image, + uint32 photometricInterpretation, + uint32 compression, + const dng_metadata *metadata, + const dng_color_space *space, + const dng_resolution *resolution, + const dng_jpeg_preview *thumbnail, + const dng_memory_block *imageResources, + dng_metadata_subset metadataSubset, + bool hasTransparency) + { + + const void *profileData = NULL; + uint32 profileSize = 0; + + const uint8 *data = NULL; + uint32 size = 0; + + if (space && space->ICCProfile (size, data)) + { + + profileData = data; + profileSize = size; + + } + + WriteTIFFWithProfile (host, + stream, + image, + photometricInterpretation, + compression, + metadata, + profileData, + profileSize, + resolution, + thumbnail, + imageResources, + metadataSubset, + hasTransparency); + + } + +/*****************************************************************************/ + +void dng_image_writer::WriteTIFFWithProfile (dng_host &host, + dng_stream &stream, + const dng_image &image, + uint32 photometricInterpretation, + uint32 compression, + dng_negative *negative, + const void *profileData, + uint32 profileSize, + const dng_resolution *resolution, + const dng_jpeg_preview *thumbnail, + const dng_memory_block *imageResources, + dng_metadata_subset metadataSubset, + bool hasTransparency) + { + + WriteTIFFWithProfile (host, + stream, + image, + photometricInterpretation, + compression, + negative ? &(negative->Metadata ()) : NULL, + profileData, + profileSize, + resolution, + thumbnail, + imageResources, + metadataSubset, + hasTransparency); + + } + +/*****************************************************************************/ + +void dng_image_writer::WriteTIFFWithProfile (dng_host &host, + dng_stream &stream, + const dng_image &image, + uint32 photometricInterpretation, + uint32 compression, + const dng_metadata *constMetadata, + const void *profileData, + uint32 profileSize, + const dng_resolution *resolution, + const dng_jpeg_preview *thumbnail, + const dng_memory_block *imageResources, + dng_metadata_subset metadataSubset, + bool hasTransparency) + { + + uint32 j; + + AutoPtr metadata; + + if (constMetadata) + { + + metadata.Reset (constMetadata->Clone (host.Allocator ())); + + CleanUpMetadata (host, + *metadata, + metadataSubset, + "image/tiff"); + + UpdateExifColorSpaceTag (*metadata, + profileData, + profileSize); + + } + + dng_ifd ifd; + + ifd.fNewSubFileType = sfMainImage; + + ifd.fImageWidth = image.Bounds ().W (); + ifd.fImageLength = image.Bounds ().H (); + + ifd.fSamplesPerPixel = image.Planes (); + + ifd.fBitsPerSample [0] = TagTypeSize (image.PixelType ()) * 8; + + for (j = 1; j < ifd.fSamplesPerPixel; j++) + { + ifd.fBitsPerSample [j] = ifd.fBitsPerSample [0]; + } + + ifd.fPhotometricInterpretation = photometricInterpretation; + + ifd.fCompression = compression; + + if (ifd.fCompression == ccUncompressed) + { + + ifd.SetSingleStrip (); + + } + + else + { + + ifd.FindStripSize (128 * 1024); + + ifd.fPredictor = cpHorizontalDifference; + + } + + uint32 extraSamples = 0; + + switch (photometricInterpretation) + { + + case piBlackIsZero: + { + extraSamples = image.Planes () - 1; + break; + } + + case piRGB: + case piCIELab: + case piICCLab: + { + extraSamples = image.Planes () - 3; + break; + } + + case piCMYK: + { + extraSamples = image.Planes () - 4; + break; + } + + default: + break; + + } + + ifd.fExtraSamplesCount = extraSamples; + + if (hasTransparency && extraSamples) + { + ifd.fExtraSamples [0] = esAssociatedAlpha; + } + + if (image.PixelType () == ttFloat) + { + + for (j = 0; j < ifd.fSamplesPerPixel; j++) + { + ifd.fSampleFormat [j] = sfFloatingPoint; + } + + } + + dng_tiff_directory mainIFD; + + dng_basic_tag_set basic (mainIFD, ifd); + + // Resolution. + + dng_resolution res; + + if (resolution) + { + res = *resolution; + } + + tag_urational tagXResolution (tcXResolution, res.fXResolution); + tag_urational tagYResolution (tcYResolution, res.fYResolution); + + tag_uint16 tagResolutionUnit (tcResolutionUnit, res.fResolutionUnit); + + if (resolution) + { + mainIFD.Add (&tagXResolution ); + mainIFD.Add (&tagYResolution ); + mainIFD.Add (&tagResolutionUnit); + } + + // ICC Profile. + + tag_icc_profile iccProfileTag (profileData, profileSize); + + if (iccProfileTag.Count ()) + { + mainIFD.Add (&iccProfileTag); + } + + // XMP metadata. + + tag_xmp tagXMP (metadata.Get () ? metadata->GetXMP () : NULL); + + if (tagXMP.Count ()) + { + mainIFD.Add (&tagXMP); + } + + // IPTC metadata. + + tag_iptc tagIPTC (metadata.Get () ? metadata->IPTCData () : NULL, + metadata.Get () ? metadata->IPTCLength () : 0); + + if (tagIPTC.Count ()) + { + mainIFD.Add (&tagIPTC); + } + + // Adobe data (thumbnail and IPTC digest) + + AutoPtr adobeData (BuildAdobeData (host, + metadata.Get (), + thumbnail, + imageResources)); + + tag_uint8_ptr tagAdobe (tcAdobeData, + adobeData->Buffer_uint8 (), + adobeData->LogicalSize ()); + + if (tagAdobe.Count ()) + { + mainIFD.Add (&tagAdobe); + } + + // Exif metadata. + + exif_tag_set exifSet (mainIFD, + metadata.Get () && metadata->GetExif () ? *metadata->GetExif () + : dng_exif (), + metadata.Get () ? metadata->IsMakerNoteSafe () : false, + metadata.Get () ? metadata->MakerNoteData () : NULL, + metadata.Get () ? metadata->MakerNoteLength () : 0, + false); + + // Find offset to main image data. + + uint32 offsetMainIFD = 8; + + uint32 offsetExifData = offsetMainIFD + mainIFD.Size (); + + exifSet.Locate (offsetExifData); + + uint32 offsetMainData = offsetExifData + exifSet.Size (); + + stream.SetWritePosition (offsetMainData); + + // Write the main image data. + + WriteImage (host, + ifd, + basic, + stream, + image); + + // Trim the file to this length. + + stream.SetLength (stream.Position ()); + + // TIFF has a 4G size limit. + + if (stream.Length () > 0x0FFFFFFFFL) + { + ThrowImageTooBigTIFF (); + } + + // Write TIFF Header. + + stream.SetWritePosition (0); + + stream.Put_uint16 (stream.BigEndian () ? byteOrderMM : byteOrderII); + + stream.Put_uint16 (42); + + stream.Put_uint32 (offsetMainIFD); + + // Write the IFDs. + + mainIFD.Put (stream); + + exifSet.Put (stream); + + stream.Flush (); + + } + +/*****************************************************************************/ + +void dng_image_writer::WriteDNG (dng_host &host, + dng_stream &stream, + dng_negative &negative, + const dng_preview_list *previewList, + uint32 maxBackwardVersion, + bool uncompressed) + { + + WriteDNGWithMetadata (host, + stream, + negative, + negative.Metadata (), + previewList, + maxBackwardVersion, + uncompressed); + + } + +/*****************************************************************************/ + +void dng_image_writer::WriteDNGWithMetadata (dng_host &host, + dng_stream &stream, + const dng_negative &negative, + const dng_metadata &constMetadata, + const dng_preview_list *previewList, + uint32 maxBackwardVersion, + bool uncompressed) + { + + uint32 j; + + // Clean up metadata per MWG recommendations. + + AutoPtr metadata (constMetadata.Clone (host.Allocator ())); + + CleanUpMetadata (host, + *metadata, + kMetadataSubset_All, + "image/dng"); + + // Figure out the compression to use. Most of the time this is lossless + // JPEG. + + uint32 compression = uncompressed ? ccUncompressed : ccJPEG; + + // Was the the original file lossy JPEG compressed? + + const dng_jpeg_image *rawJPEGImage = negative.RawJPEGImage (); + + // If so, can we save it using the requested compression and DNG version? + + if (uncompressed || maxBackwardVersion < dngVersion_1_4_0_0) + { + + if (rawJPEGImage || negative.RawJPEGImageDigest ().IsValid ()) + { + + rawJPEGImage = NULL; + + negative.ClearRawJPEGImageDigest (); + + negative.ClearRawImageDigest (); + + } + + } + + else if (rawJPEGImage) + { + + compression = ccLossyJPEG; + + } + + // Are we saving the original size tags? + + bool saveOriginalDefaultFinalSize = false; + bool saveOriginalBestQualityFinalSize = false; + bool saveOriginalDefaultCropSize = false; + + { + + // See if we are saving a proxy image. + + dng_point defaultFinalSize (negative.DefaultFinalHeight (), + negative.DefaultFinalWidth ()); + + saveOriginalDefaultFinalSize = (negative.OriginalDefaultFinalSize () != + defaultFinalSize); + + if (saveOriginalDefaultFinalSize) + { + + // If the save OriginalDefaultFinalSize tag, this changes the defaults + // for the OriginalBestQualityFinalSize and OriginalDefaultCropSize tags. + + saveOriginalBestQualityFinalSize = (negative.OriginalBestQualityFinalSize () != + negative.OriginalDefaultFinalSize ()); + + saveOriginalDefaultCropSize = (negative.OriginalDefaultCropSizeV () != + dng_urational (negative.OriginalDefaultFinalSize ().v, 1)) || + (negative.OriginalDefaultCropSizeH () != + dng_urational (negative.OriginalDefaultFinalSize ().h, 1)); + + } + + else + { + + // Else these two tags default to the normal non-proxy size image values. + + dng_point bestQualityFinalSize (negative.BestQualityFinalHeight (), + negative.BestQualityFinalWidth ()); + + saveOriginalBestQualityFinalSize = (negative.OriginalBestQualityFinalSize () != + bestQualityFinalSize); + + saveOriginalDefaultCropSize = (negative.OriginalDefaultCropSizeV () != + negative.DefaultCropSizeV ()) || + (negative.OriginalDefaultCropSizeH () != + negative.DefaultCropSizeH ()); + + } + + } + + // Is this a floating point image that we are saving? + + bool isFloatingPoint = (negative.RawImage ().PixelType () == ttFloat); + + // Does this image have a transparency mask? + + bool hasTransparencyMask = (negative.RawTransparencyMask () != NULL); + + // Does the image have depth mask? + + bool hasDepthMap = (negative.RawDepthMap () != NULL); + + // Should we save the enhanced stage 3 image? + + bool hasEnhancedImage = (&negative.RawImage () != negative.Stage3Image ()) && + negative.EnhanceParams ().NotEmpty (); + + // Should we save a compressed 32-bit integer file? + + bool isCompressed32BitInteger = (negative.RawImage ().PixelType () == ttLong) && + (maxBackwardVersion >= dngVersion_1_4_0_0) && + (!uncompressed); + + // Figure out what main version to use. + + uint32 dngVersion = dngVersion_Current; + + if (!hasDepthMap && !hasEnhancedImage) + { + + // Nothing in DNG 1.5 specification breaks backward compatiblity, + // so there is not really any reason to mark the file as being + // in DNG 1.5 format. So unless we are actually using an optional + // DNG 1.5 feature, leave the main version tag at 1.4. + + dngVersion = dngVersion_1_4_0_0; + + } + + // Figure out what backward version to use. + + uint32 dngBackwardVersion = dngVersion_1_1_0_0; + + #if defined(qTestRowInterleave) || defined(qTestSubTileBlockRows) || defined(qTestSubTileBlockCols) + dngBackwardVersion = Max_uint32 (dngBackwardVersion, dngVersion_1_2_0_0); + #endif + + dngBackwardVersion = Max_uint32 (dngBackwardVersion, + negative.OpcodeList1 ().MinVersion (false)); + + dngBackwardVersion = Max_uint32 (dngBackwardVersion, + negative.OpcodeList2 ().MinVersion (false)); + + dngBackwardVersion = Max_uint32 (dngBackwardVersion, + negative.OpcodeList3 ().MinVersion (false)); + + if (negative.GetMosaicInfo () && + negative.GetMosaicInfo ()->fCFALayout >= 6) + { + dngBackwardVersion = Max_uint32 (dngBackwardVersion, dngVersion_1_3_0_0); + } + + if (rawJPEGImage || isFloatingPoint || hasTransparencyMask || isCompressed32BitInteger) + { + dngBackwardVersion = Max_uint32 (dngBackwardVersion, dngVersion_1_4_0_0); + } + + if (dngBackwardVersion > dngVersion) + { + ThrowProgramError (); + } + + // Find best thumbnail from preview list, if any. + + const dng_preview *thumbnail = NULL; + + if (previewList) + { + + uint32 thumbArea = 0; + + for (j = 0; j < previewList->Count (); j++) + { + + const dng_image_preview *imagePreview = dynamic_cast(&previewList->Preview (j)); + + if (imagePreview) + { + + uint32 thisArea = imagePreview->fImage->Bounds ().W () * + imagePreview->fImage->Bounds ().H (); + + if (!thumbnail || thisArea < thumbArea) + { + + thumbnail = &previewList->Preview (j); + + thumbArea = thisArea; + + } + + } + + const dng_jpeg_preview *jpegPreview = dynamic_cast(&previewList->Preview (j)); + + if (jpegPreview) + { + + uint32 thisArea = jpegPreview->fPreviewSize.h * + jpegPreview->fPreviewSize.v; + + if (!thumbnail || thisArea < thumbArea) + { + + thumbnail = &previewList->Preview (j); + + thumbArea = thisArea; + + } + + } + + } + + } + + // Create the main IFD + + dng_tiff_directory mainIFD; + + // Create the IFD for the raw data. If there is no thumnail, this is + // just a reference the main IFD. Otherwise allocate a new one. + + AutoPtr rawIFD_IfNotMain; + + if (thumbnail) + { + rawIFD_IfNotMain.Reset (new dng_tiff_directory); + } + + dng_tiff_directory &rawIFD (thumbnail ? *rawIFD_IfNotMain : mainIFD); + + // Include DNG version tags. + + uint8 dngVersionData [4]; + + dngVersionData [0] = (uint8) (dngVersion >> 24); + dngVersionData [1] = (uint8) (dngVersion >> 16); + dngVersionData [2] = (uint8) (dngVersion >> 8); + dngVersionData [3] = (uint8) (dngVersion ); + + tag_uint8_ptr tagDNGVersion (tcDNGVersion, dngVersionData, 4); + + mainIFD.Add (&tagDNGVersion); + + uint8 dngBackwardVersionData [4]; + + dngBackwardVersionData [0] = (uint8) (dngBackwardVersion >> 24); + dngBackwardVersionData [1] = (uint8) (dngBackwardVersion >> 16); + dngBackwardVersionData [2] = (uint8) (dngBackwardVersion >> 8); + dngBackwardVersionData [3] = (uint8) (dngBackwardVersion ); + + tag_uint8_ptr tagDNGBackwardVersion (tcDNGBackwardVersion, dngBackwardVersionData, 4); + + mainIFD.Add (&tagDNGBackwardVersion); + + // The main IFD contains the thumbnail, if there is a thumbnail. + + AutoPtr thmBasic; + + if (thumbnail) + { + thmBasic.Reset (thumbnail->AddTagSet (mainIFD)); + } + + // Get the raw image we are writing. + + const dng_image &rawImage (negative.RawImage ()); + + // For floating point, we only support ZIP compression. + + if (isFloatingPoint && !uncompressed) + { + + compression = ccDeflate; + + } + + // For 32-bit integer images, we only support ZIP and uncompressed. + + if (rawImage.PixelType () == ttLong) + { + + if (isCompressed32BitInteger) + { + compression = ccDeflate; + } + + else + { + compression = ccUncompressed; + } + + } + + // Get a copy of the mosaic info. + + dng_mosaic_info mosaicInfo; + + if (negative.GetMosaicInfo ()) + { + mosaicInfo = *(negative.GetMosaicInfo ()); + } + + // Create a dng_ifd record for the raw image. + + dng_ifd info; + + info.fImageWidth = rawImage.Width (); + info.fImageLength = rawImage.Height (); + + info.fSamplesPerPixel = rawImage.Planes (); + + info.fPhotometricInterpretation = mosaicInfo.IsColorFilterArray () ? piCFA + : piLinearRaw; + + info.fCompression = compression; + + if (isFloatingPoint && compression == ccDeflate) + { + + info.fPredictor = cpFloatingPoint; + + if (mosaicInfo.IsColorFilterArray ()) + { + + if (mosaicInfo.fCFAPatternSize.h == 2) + { + info.fPredictor = cpFloatingPointX2; + } + + else if (mosaicInfo.fCFAPatternSize.h == 4) + { + info.fPredictor = cpFloatingPointX4; + } + + } + + } + + if (isCompressed32BitInteger) + { + + info.fPredictor = cpHorizontalDifference; + + if (mosaicInfo.IsColorFilterArray ()) + { + + if (mosaicInfo.fCFAPatternSize.h == 2) + { + info.fPredictor = cpHorizontalDifferenceX2; + } + + else if (mosaicInfo.fCFAPatternSize.h == 4) + { + info.fPredictor = cpHorizontalDifferenceX4; + } + + } + + } + + uint32 rawPixelType = rawImage.PixelType (); + + if (rawPixelType == ttShort) + { + + // See if we are using a linearization table with <= 256 entries, in which + // case the useful data will all fit within 8-bits. + + const dng_linearization_info *rangeInfo = negative.GetLinearizationInfo (); + + if (rangeInfo) + { + + if (rangeInfo->fLinearizationTable.Get ()) + { + + uint32 entries = rangeInfo->fLinearizationTable->LogicalSize () >> 1; + + if (entries <= 256) + { + + rawPixelType = ttByte; + + } + + } + + } + + } + + switch (rawPixelType) + { + + case ttByte: + { + info.fBitsPerSample [0] = 8; + break; + } + + case ttShort: + { + info.fBitsPerSample [0] = 16; + break; + } + + case ttLong: + { + info.fBitsPerSample [0] = 32; + break; + } + + case ttFloat: + { + + if (negative.RawFloatBitDepth () == 16) + { + info.fBitsPerSample [0] = 16; + } + + else if (negative.RawFloatBitDepth () == 24) + { + info.fBitsPerSample [0] = 24; + } + + else + { + info.fBitsPerSample [0] = 32; + } + + for (j = 0; j < info.fSamplesPerPixel; j++) + { + info.fSampleFormat [j] = sfFloatingPoint; + } + + break; + + } + + default: + { + ThrowProgramError (); + } + + } + + // For lossless JPEG compression, we often lie about the + // actual channel count to get the predictors to work across + // same color mosaic pixels. + + uint32 fakeChannels = 1; + + if (info.fCompression == ccJPEG) + { + + if (mosaicInfo.IsColorFilterArray ()) + { + + if (mosaicInfo.fCFAPatternSize.h == 4) + { + fakeChannels = 4; + } + + else if (mosaicInfo.fCFAPatternSize.h == 2) + { + fakeChannels = 2; + } + + // However, lossless JEPG is limited to four channels, + // so compromise might be required. + + while (fakeChannels * info.fSamplesPerPixel > 4 && + fakeChannels > 1) + { + + fakeChannels >>= 1; + + } + + } + + } + + // Figure out tile sizes. + + if (rawJPEGImage) + { + + DNG_ASSERT (rawPixelType == ttByte, + "Unexpected jpeg pixel type"); + + DNG_ASSERT (info.fImageWidth == (uint32) rawJPEGImage->fImageSize.h && + info.fImageLength == (uint32) rawJPEGImage->fImageSize.v, + "Unexpected jpeg image size"); + + info.fTileWidth = rawJPEGImage->fTileSize.h; + info.fTileLength = rawJPEGImage->fTileSize.v; + + info.fUsesStrips = rawJPEGImage->fUsesStrips; + + info.fUsesTiles = !info.fUsesStrips; + + } + + else if (info.fCompression == ccJPEG) + { + + info.FindTileSize (128 * 1024); + + } + + else if (info.fCompression == ccDeflate) + { + + info.FindTileSize (512 * 1024); + + } + + else if (info.fCompression == ccLossyJPEG) + { + + ThrowProgramError ("No JPEG compressed image"); + + } + + // Don't use tiles for uncompressed images. + + else + { + + info.SetSingleStrip (); + + } + + #ifdef qTestRowInterleave + + info.fRowInterleaveFactor = qTestRowInterleave; + + #endif + + #if defined(qTestSubTileBlockRows) && defined(qTestSubTileBlockCols) + + info.fSubTileBlockRows = qTestSubTileBlockRows; + info.fSubTileBlockCols = qTestSubTileBlockCols; + + if (fakeChannels == 2) + fakeChannels = 4; + + #endif + + // Basic information. + + dng_basic_tag_set rawBasic (rawIFD, info); + + // JPEG tables, if any. + + tag_data_ptr tagJPEGTables (tcJPEGTables, + ttUndefined, + 0, + NULL); + + if (rawJPEGImage && rawJPEGImage->fJPEGTables.Get ()) + { + + tagJPEGTables.SetData (rawJPEGImage->fJPEGTables->Buffer ()); + + tagJPEGTables.SetCount (rawJPEGImage->fJPEGTables->LogicalSize ()); + + rawIFD.Add (&tagJPEGTables); + + } + + // DefaultScale tag. + + dng_urational defaultScaleData [2]; + + defaultScaleData [0] = negative.DefaultScaleH (); + defaultScaleData [1] = negative.DefaultScaleV (); + + tag_urational_ptr tagDefaultScale (tcDefaultScale, + defaultScaleData, + 2); + + rawIFD.Add (&tagDefaultScale); + + // Best quality scale tag. + + tag_urational tagBestQualityScale (tcBestQualityScale, + negative.BestQualityScale ()); + + rawIFD.Add (&tagBestQualityScale); + + // DefaultCropOrigin tag. + + dng_urational defaultCropOriginData [2]; + + defaultCropOriginData [0] = negative.DefaultCropOriginH (); + defaultCropOriginData [1] = negative.DefaultCropOriginV (); + + tag_urational_ptr tagDefaultCropOrigin (tcDefaultCropOrigin, + defaultCropOriginData, + 2); + + rawIFD.Add (&tagDefaultCropOrigin); + + // DefaultCropSize tag. + + dng_urational defaultCropSizeData [2]; + + defaultCropSizeData [0] = negative.DefaultCropSizeH (); + defaultCropSizeData [1] = negative.DefaultCropSizeV (); + + tag_urational_ptr tagDefaultCropSize (tcDefaultCropSize, + defaultCropSizeData, + 2); + + rawIFD.Add (&tagDefaultCropSize); + + // DefaultUserCrop tag. + + dng_urational defaultUserCropData [4]; + + defaultUserCropData [0] = negative.DefaultUserCropT (); + defaultUserCropData [1] = negative.DefaultUserCropL (); + defaultUserCropData [2] = negative.DefaultUserCropB (); + defaultUserCropData [3] = negative.DefaultUserCropR (); + + tag_urational_ptr tagDefaultUserCrop (tcDefaultUserCrop, + defaultUserCropData, + 4); + + if (negative.HasDefaultUserCrop ()) + { + + rawIFD.Add (&tagDefaultUserCrop); + + } + + // Range mapping tag set. + + range_tag_set rangeSet (rawIFD, negative); + + // Mosaic pattern information. + + mosaic_tag_set mosaicSet (rawIFD, mosaicInfo); + + // Chroma blur radius. + + tag_urational tagChromaBlurRadius (tcChromaBlurRadius, + negative.ChromaBlurRadius ()); + + if (negative.ChromaBlurRadius ().IsValid ()) + { + + rawIFD.Add (&tagChromaBlurRadius); + + } + + // Anti-alias filter strength. + + tag_urational tagAntiAliasStrength (tcAntiAliasStrength, + negative.AntiAliasStrength ()); + + if (negative.AntiAliasStrength ().IsValid ()) + { + + rawIFD.Add (&tagAntiAliasStrength); + + } + + // Profile and other color related tags. + + AutoPtr profileSet; + + AutoPtr colorSet; + + std::vector extraProfileIndex; + + if (!negative.IsMonochrome ()) + { + + const dng_camera_profile &mainProfile (*negative.ComputeCameraProfileToEmbed (constMetadata)); + + profileSet.Reset (new profile_tag_set (mainIFD, + mainProfile)); + + colorSet.Reset (new color_tag_set (mainIFD, + negative)); + + // Build list of profile indices to include in extra profiles tag. + + uint32 profileCount = negative.ProfileCount (); + + for (uint32 index = 0; index < profileCount; index++) + { + + const dng_camera_profile &profile (negative.ProfileByIndex (index)); + + if (&profile != &mainProfile) + { + + if (profile.WasReadFromDNG ()) + { + + extraProfileIndex.push_back (index); + + } + + } + + } + + } + + // Extra camera profiles tag. + + uint32 extraProfileCount = (uint32) extraProfileIndex.size (); + + dng_memory_data extraProfileOffsets (extraProfileCount, sizeof (uint32)); + + tag_uint32_ptr extraProfileTag (tcExtraCameraProfiles, + extraProfileOffsets.Buffer_uint32 (), + extraProfileCount); + + if (extraProfileCount) + { + + mainIFD.Add (&extraProfileTag); + + } + + // Other tags. + + tag_uint16 tagOrientation (tcOrientation, + (uint16) negative.ComputeOrientation (constMetadata).GetTIFF ()); + + mainIFD.Add (&tagOrientation); + + tag_srational tagBaselineExposure (tcBaselineExposure, + negative.BaselineExposureR ()); + + mainIFD.Add (&tagBaselineExposure); + + tag_urational tagBaselineNoise (tcBaselineNoise, + negative.BaselineNoiseR ()); + + mainIFD.Add (&tagBaselineNoise); + + dng_urational rawNoiseReductionApplied = hasEnhancedImage ? negative.RawNoiseReductionApplied () + : negative.NoiseReductionApplied (); + + tag_urational tagNoiseReductionAppliedMainIFD (tcNoiseReductionApplied, + rawNoiseReductionApplied); + + tag_urational tagNoiseReductionAppliedRawIFD (tcNoiseReductionApplied, + rawNoiseReductionApplied); + + if (rawNoiseReductionApplied.IsValid ()) + { + + rawIFD.Add (&tagNoiseReductionAppliedRawIFD); + + // Kludge: DNG spec says that the NoiseReductionApplied tag should be + // in the Raw IFD, not main IFD. However, we also write a copy of this tag to + // main IFD to deal with legacy DNG readers that try to read the tag + // from the main IFD. + + if ((&rawIFD) != (&mainIFD)) + { + + mainIFD.Add (&tagNoiseReductionAppliedMainIFD); + + } + + } + + dng_noise_profile rawNoiseProfile = hasEnhancedImage ? negative.RawNoiseProfile () + : negative.NoiseProfile (); + + tag_dng_noise_profile tagNoiseProfileMainIFD (rawNoiseProfile); + tag_dng_noise_profile tagNoiseProfileRawIFD (rawNoiseProfile); + + if (rawNoiseProfile.IsValidForNegative (negative)) + { + + rawIFD.Add (&tagNoiseProfileRawIFD); + + // Kludge: DNG spec says that NoiseProfile tag should be in the Raw + // IFD, not main IFD. However, we also write a copy of this tag to + // main IFD to deal with legacy DNG readers that try to read the tag + // from the main IFD. + + if ((&rawIFD) != (&mainIFD)) + { + + mainIFD.Add (&tagNoiseProfileMainIFD); + + } + + } + + tag_urational tagBaselineSharpness (tcBaselineSharpness, + negative.RawBaselineSharpness ()); + + mainIFD.Add (&tagBaselineSharpness); + + tag_string tagUniqueName (tcUniqueCameraModel, + negative.ModelName (), + true); + + mainIFD.Add (&tagUniqueName); + + tag_string tagLocalName (tcLocalizedCameraModel, + negative.LocalName (), + false); + + if (negative.LocalName ().NotEmpty ()) + { + + mainIFD.Add (&tagLocalName); + + } + + tag_urational tagShadowScale (tcShadowScale, + negative.ShadowScaleR ()); + + mainIFD.Add (&tagShadowScale); + + tag_uint16 tagColorimetricReference (tcColorimetricReference, + (uint16) negative.ColorimetricReference ()); + + if (negative.ColorimetricReference () != crSceneReferred) + { + + mainIFD.Add (&tagColorimetricReference); + + } + + bool useNewDigest = (maxBackwardVersion >= dngVersion_1_4_0_0); + + if (compression == ccLossyJPEG) + { + + negative.FindRawJPEGImageDigest (host); + + } + + else + { + + if (useNewDigest) + { + negative.FindNewRawImageDigest (host); + } + else + { + negative.FindRawImageDigest (host); + } + + } + + tag_uint8_ptr tagRawImageDigest (useNewDigest ? tcNewRawImageDigest : tcRawImageDigest, + compression == ccLossyJPEG ? + negative.RawJPEGImageDigest ().data : + (useNewDigest ? negative.NewRawImageDigest ().data + : negative.RawImageDigest ().data), + 16); + + mainIFD.Add (&tagRawImageDigest); + + negative.FindRawDataUniqueID (host); + + // Make a local copy of the raw data unique ID. + + const auto rawDataUniqueID = negative.RawDataUniqueID (); + + tag_uint8_ptr tagRawDataUniqueID (tcRawDataUniqueID, + rawDataUniqueID.data, + 16); + + if (rawDataUniqueID.IsValid ()) + { + + mainIFD.Add (&tagRawDataUniqueID); + + } + + tag_string tagOriginalRawFileName (tcOriginalRawFileName, + negative.OriginalRawFileName (), + false); + + if (negative.HasOriginalRawFileName ()) + { + + mainIFD.Add (&tagOriginalRawFileName); + + } + + negative.FindOriginalRawFileDigest (); + + tag_data_ptr tagOriginalRawFileData (tcOriginalRawFileData, + ttUndefined, + negative.OriginalRawFileDataLength (), + negative.OriginalRawFileData ()); + + tag_uint8_ptr tagOriginalRawFileDigest (tcOriginalRawFileDigest, + negative.OriginalRawFileDigest ().data, + 16); + + if (negative.OriginalRawFileData ()) + { + + mainIFD.Add (&tagOriginalRawFileData); + + mainIFD.Add (&tagOriginalRawFileDigest); + + } + + // XMP metadata. + + tag_xmp tagXMP (metadata->GetXMP ()); + + if (tagXMP.Count ()) + { + + mainIFD.Add (&tagXMP); + + } + + // Exif tags. + + exif_tag_set exifSet (mainIFD, + *metadata->GetExif (), + metadata->IsMakerNoteSafe (), + metadata->MakerNoteData (), + metadata->MakerNoteLength (), + true); + + // Private data. + + tag_uint8_ptr tagPrivateData (tcDNGPrivateData, + negative.PrivateData (), + negative.PrivateLength ()); + + if (negative.PrivateLength ()) + { + + mainIFD.Add (&tagPrivateData); + + } + + // Proxy size tags. + + uint32 originalDefaultFinalSizeData [2]; + + originalDefaultFinalSizeData [0] = negative.OriginalDefaultFinalSize ().h; + originalDefaultFinalSizeData [1] = negative.OriginalDefaultFinalSize ().v; + + tag_uint32_ptr tagOriginalDefaultFinalSize (tcOriginalDefaultFinalSize, + originalDefaultFinalSizeData, + 2); + + if (saveOriginalDefaultFinalSize) + { + + mainIFD.Add (&tagOriginalDefaultFinalSize); + + } + + uint32 originalBestQualityFinalSizeData [2]; + + originalBestQualityFinalSizeData [0] = negative.OriginalBestQualityFinalSize ().h; + originalBestQualityFinalSizeData [1] = negative.OriginalBestQualityFinalSize ().v; + + tag_uint32_ptr tagOriginalBestQualityFinalSize (tcOriginalBestQualityFinalSize, + originalBestQualityFinalSizeData, + 2); + + if (saveOriginalBestQualityFinalSize) + { + + mainIFD.Add (&tagOriginalBestQualityFinalSize); + + } + + dng_urational originalDefaultCropSizeData [2]; + + originalDefaultCropSizeData [0] = negative.OriginalDefaultCropSizeH (); + originalDefaultCropSizeData [1] = negative.OriginalDefaultCropSizeV (); + + tag_urational_ptr tagOriginalDefaultCropSize (tcOriginalDefaultCropSize, + originalDefaultCropSizeData, + 2); + + if (saveOriginalDefaultCropSize) + { + + mainIFD.Add (&tagOriginalDefaultCropSize); + + } + + // Opcode list 1. + + AutoPtr opcodeList1Data (negative.OpcodeList1 ().Spool (host)); + + tag_data_ptr tagOpcodeList1 (tcOpcodeList1, + ttUndefined, + opcodeList1Data.Get () ? opcodeList1Data->LogicalSize () : 0, + opcodeList1Data.Get () ? opcodeList1Data->Buffer () : NULL); + + if (opcodeList1Data.Get ()) + { + + rawIFD.Add (&tagOpcodeList1); + + } + + // Opcode list 2. + + AutoPtr opcodeList2Data (negative.OpcodeList2 ().Spool (host)); + + tag_data_ptr tagOpcodeList2 (tcOpcodeList2, + ttUndefined, + opcodeList2Data.Get () ? opcodeList2Data->LogicalSize () : 0, + opcodeList2Data.Get () ? opcodeList2Data->Buffer () : NULL); + + if (opcodeList2Data.Get ()) + { + + rawIFD.Add (&tagOpcodeList2); + + } + + // Opcode list 3. + + AutoPtr opcodeList3Data (negative.OpcodeList3 ().Spool (host)); + + tag_data_ptr tagOpcodeList3 (tcOpcodeList3, + ttUndefined, + opcodeList3Data.Get () ? opcodeList3Data->LogicalSize () : 0, + opcodeList3Data.Get () ? opcodeList3Data->Buffer () : NULL); + + if (opcodeList3Data.Get ()) + { + + rawIFD.Add (&tagOpcodeList3); + + } + + // Transparency mask, if any. + + AutoPtr maskInfo; + + AutoPtr maskIFD; + + AutoPtr maskBasic; + + if (hasTransparencyMask) + { + + // Create mask IFD. + + maskInfo.Reset (new dng_ifd); + + maskInfo->fNewSubFileType = sfTransparencyMask; + + maskInfo->fImageWidth = negative.RawTransparencyMask ()->Bounds ().W (); + maskInfo->fImageLength = negative.RawTransparencyMask ()->Bounds ().H (); + + maskInfo->fSamplesPerPixel = 1; + + maskInfo->fBitsPerSample [0] = negative.RawTransparencyMaskBitDepth (); + + maskInfo->fPhotometricInterpretation = piTransparencyMask; + + maskInfo->fCompression = uncompressed ? ccUncompressed : ccDeflate; + maskInfo->fPredictor = uncompressed ? cpNullPredictor : cpHorizontalDifference; + + if (negative.RawTransparencyMask ()->PixelType () == ttFloat) + { + + maskInfo->fSampleFormat [0] = sfFloatingPoint; + + if (maskInfo->fCompression == ccDeflate) + { + maskInfo->fPredictor = cpFloatingPoint; + } + + } + + if (maskInfo->fCompression == ccDeflate) + { + maskInfo->FindTileSize (512 * 1024); + } + else + { + maskInfo->SetSingleStrip (); + } + + // Create mask tiff directory. + + maskIFD.Reset (new dng_tiff_directory); + + // Add mask basic tag set. + + maskBasic.Reset (new dng_basic_tag_set (*maskIFD, *maskInfo)); + + } + + AutoPtr depthInfo; + + AutoPtr depthIFD; + + AutoPtr depthBasic; + + tag_uint16 tagDepthFormat (tcDepthFormat, + (uint16) negative.DepthFormat ()); + + tag_urational tagDepthNear (tcDepthNear, + negative.DepthNear ()); + + tag_urational tagDepthFar (tcDepthFar, + negative.DepthFar ()); + + tag_uint16 tagDepthUnits (tcDepthUnits, + (uint16) negative.DepthUnits ()); + + tag_uint16 tagDepthMeasureType (tcDepthMeasureType, + (uint16) negative.DepthMeasureType ()); + + if (hasDepthMap) + { + + // Create depth IFD. + + depthInfo.Reset (new dng_ifd); + + depthInfo->fNewSubFileType = sfDepthMap; + + depthInfo->fImageWidth = negative.RawDepthMap ()->Bounds ().W (); + depthInfo->fImageLength = negative.RawDepthMap ()->Bounds ().H (); + + depthInfo->fSamplesPerPixel = 1; + + depthInfo->fBitsPerSample [0] = negative.RawDepthMap ()->PixelSize () * 8; + + depthInfo->fPhotometricInterpretation = piDepth; + + depthInfo->fCompression = uncompressed ? ccUncompressed : ccDeflate; + depthInfo->fPredictor = uncompressed ? cpNullPredictor : cpHorizontalDifference; + + if (depthInfo->fCompression == ccDeflate) + { + depthInfo->FindTileSize (512 * 1024); + } + else + { + depthInfo->SetSingleStrip (); + } + + // Create mask tiff directory. + + depthIFD.Reset (new dng_tiff_directory); + + // Add mask basic tag set. + + depthBasic.Reset (new dng_basic_tag_set (*depthIFD, *depthInfo)); + + // Depth metadata. + + mainIFD.Add (&tagDepthFormat); + mainIFD.Add (&tagDepthNear); + mainIFD.Add (&tagDepthFar); + mainIFD.Add (&tagDepthUnits); + mainIFD.Add (&tagDepthMeasureType); + + } + + // Enhanced stage 3 image. + + AutoPtr enhancedInfo; + + AutoPtr enhancedIFD; + + AutoPtr enhancedBasic; + + tag_string enhanceParams (tcEnhanceParams, + negative.EnhanceParams (), + false); + + tag_urational enhanceBaselineSharpness (tcBaselineSharpness, + negative.BaselineSharpnessR ()); + + tag_urational enhanceNoiseReductionApplied (tcNoiseReductionApplied, + negative.NoiseReductionApplied ()); + + tag_dng_noise_profile enhanceNoiseProfile (negative.NoiseProfile ()); + + uint16 enhanceBlackLevelData [kMaxColorPlanes]; + + tag_uint16_ptr enhanceBlackLevel (tcBlackLevel, + enhanceBlackLevelData); + + if (hasEnhancedImage) + { + + // Create enhanced IFD. + + enhancedInfo.Reset (new dng_ifd); + + enhancedInfo->fNewSubFileType = sfEnhancedImage; + + enhancedInfo->fImageWidth = negative.Stage3Image ()->Bounds ().W (); + enhancedInfo->fImageLength = negative.Stage3Image ()->Bounds ().H (); + + enhancedInfo->fSamplesPerPixel = negative.Stage3Image ()->Planes (); + + for (uint32 plane = 0; plane < enhancedInfo->fSamplesPerPixel; plane++) + { + + enhancedInfo->fBitsPerSample [plane] = negative.Stage3Image ()->PixelSize () * 8; + + if (negative.Stage3Image ()->PixelType () == ttFloat) + { + + enhancedInfo->fSampleFormat [plane] = sfFloatingPoint; + + } + + } + + enhancedInfo->fPhotometricInterpretation = piLinearRaw; + + if (uncompressed) + { + + enhancedInfo->fCompression = ccUncompressed; + + enhancedInfo->SetSingleStrip (); + + } + + else if (negative.Stage3Image ()->PixelType () == ttShort) + { + + enhancedInfo->fCompression = ccJPEG; + + enhancedInfo->FindTileSize (128 * 1024); + + } + + else + { + + enhancedInfo->fCompression = ccDeflate; + enhancedInfo->fPredictor = cpFloatingPoint; + + enhancedInfo->FindTileSize (512 * 1024); + + } + + // Create enhanced tiff directory. + + enhancedIFD.Reset (new dng_tiff_directory); + + // Add enhanced basic tag set. + + enhancedBasic.Reset (new dng_basic_tag_set (*enhancedIFD, *enhancedInfo)); + + // Add EnhanceParams tag. + + enhancedIFD->Add (&enhanceParams); + + // Record the enhanced baseline sharpness tag, if different. + + if (negative.RawBaselineSharpness () != negative.BaselineSharpnessR ()) + { + + enhancedIFD->Add (&enhanceBaselineSharpness); + + } + + // Record the enhanced noise reduction applied, if different. + + if (negative.RawNoiseReductionApplied () != negative.NoiseReductionApplied ()) + { + + enhancedIFD->Add (&enhanceNoiseReductionApplied); + + } + + // Record the enhanced noise profile, if different. + + if (negative.RawNoiseProfile () != negative.NoiseProfile ()) + { + + enhancedIFD->Add (&enhanceNoiseProfile); + + } + + // Record stage3 black level. + + if (negative.Stage3BlackLevel ()) + { + + for (uint32 plane = 0; plane < enhancedInfo->fSamplesPerPixel; plane++) + { + + enhanceBlackLevelData [plane] = negative.Stage3BlackLevel (); + + } + + enhanceBlackLevel.SetCount (enhancedInfo->fSamplesPerPixel); + + enhancedIFD->Add (&enhanceBlackLevel); + + } + + } + + // Add other subfiles. + + uint32 subFileCount = thumbnail ? 1 : 0; + + if (hasTransparencyMask) + { + subFileCount++; + } + + if (hasDepthMap) + { + subFileCount++; + } + + if (hasEnhancedImage) + { + subFileCount++; + } + + // Add previews. + + uint32 previewCount = previewList ? previewList->Count () : 0; + + AutoPtr previewIFD [kMaxDNGPreviews]; + + AutoPtr previewBasic [kMaxDNGPreviews]; + + for (j = 0; j < previewCount; j++) + { + + if (thumbnail != &previewList->Preview (j)) + { + + previewIFD [j] . Reset (new dng_tiff_directory); + + previewBasic [j] . Reset (previewList->Preview (j).AddTagSet (*previewIFD [j])); + + subFileCount++; + + } + + } + + // And a link to the raw and JPEG image IFDs. + + uint32 subFileData [kMaxDNGPreviews + 2]; + + tag_uint32_ptr tagSubFile (tcSubIFDs, + subFileData, + subFileCount); + + if (subFileCount) + { + + mainIFD.Add (&tagSubFile); + + } + + // Skip past the header and IFDs for now. + + uint32 currentOffset = 8; + + currentOffset += mainIFD.Size (); + + uint32 subFileIndex = 0; + + if (thumbnail) + { + + subFileData [subFileIndex++] = currentOffset; + + currentOffset += rawIFD.Size (); + + } + + if (hasTransparencyMask) + { + + subFileData [subFileIndex++] = currentOffset; + + currentOffset += maskIFD->Size (); + + } + + if (hasDepthMap) + { + + subFileData [subFileIndex++] = currentOffset; + + currentOffset += depthIFD->Size (); + + } + + if (hasEnhancedImage) + { + + subFileData [subFileIndex++] = currentOffset; + + currentOffset += enhancedIFD->Size (); + + } + + for (j = 0; j < previewCount; j++) + { + + if (thumbnail != &previewList->Preview (j)) + { + + subFileData [subFileIndex++] = currentOffset; + + currentOffset += previewIFD [j]->Size (); + + } + + } + + exifSet.Locate (currentOffset); + + currentOffset += exifSet.Size (); + + stream.SetWritePosition (currentOffset); + + // Write the extra profiles. + + if (extraProfileCount) + { + + for (j = 0; j < extraProfileCount; j++) + { + + extraProfileOffsets.Buffer_uint32 () [j] = (uint32) stream.Position (); + + uint32 index = extraProfileIndex [j]; + + const dng_camera_profile &profile (negative.ProfileByIndex (index)); + + tiff_dng_extended_color_profile extraWriter (profile); + + extraWriter.Put (stream, false); + + } + + } + + // Write the thumbnail data. + + if (thumbnail) + { + + thumbnail->WriteData (host, + *this, + *thmBasic, + stream); + + } + + // Write the preview data. + + for (j = 0; j < previewCount; j++) + { + + if (thumbnail != &previewList->Preview (j)) + { + + previewList->Preview (j).WriteData (host, + *this, + *previewBasic [j], + stream); + + } + + } + + // Write the raw data. + + if (rawJPEGImage) + { + + uint32 tileCount = info.TilesAcross () * + info.TilesDown (); + + for (uint32 tileIndex = 0; tileIndex < tileCount; tileIndex++) + { + + // Remember this offset. + + uint32 tileOffset = (uint32) stream.Position (); + + rawBasic.SetTileOffset (tileIndex, tileOffset); + + // Write JPEG data. + + stream.Put (rawJPEGImage->fJPEGData [tileIndex]->Buffer (), + rawJPEGImage->fJPEGData [tileIndex]->LogicalSize ()); + + // Update tile count. + + uint32 tileByteCount = (uint32) stream.Position () - tileOffset; + + rawBasic.SetTileByteCount (tileIndex, tileByteCount); + + // Keep the tiles on even byte offsets. + + if (tileByteCount & 1) + { + stream.Put_uint8 (0); + } + + } + + } + + else + { + + #if qDNGValidate + dng_timer timer ("Write raw image time"); + #endif + + WriteImage (host, + info, + rawBasic, + stream, + rawImage, + fakeChannels); + + } + + // Write transparency mask image. + + if (hasTransparencyMask) + { + + #if qDNGValidate + dng_timer timer ("Write transparency mask time"); + #endif + + WriteImage (host, + *maskInfo, + *maskBasic, + stream, + *negative.RawTransparencyMask ()); + + } + + // Write depth map image. + + if (hasDepthMap) + { + + #if qDNGValidate + dng_timer timer ("Write depth map time"); + #endif + + WriteImage (host, + *depthInfo, + *depthBasic, + stream, + *negative.RawDepthMap ()); + + } + + if (hasEnhancedImage) + { + + #if qDNGValidate + dng_timer timer ("Write enhanced image time"); + #endif + + WriteImage (host, + *enhancedInfo, + *enhancedBasic, + stream, + *negative.Stage3Image ()); + + } + + // Trim the file to this length. + + stream.SetLength (stream.Position ()); + + // DNG has a 4G size limit. + + if (stream.Length () > 0x0FFFFFFFFL) + { + ThrowImageTooBigDNG (); + } + + // Write TIFF Header. + + stream.SetWritePosition (0); + + stream.Put_uint16 (stream.BigEndian () ? byteOrderMM : byteOrderII); + + stream.Put_uint16 (42); + + stream.Put_uint32 (8); + + // Write the IFDs. + + mainIFD.Put (stream); + + if (thumbnail) + { + + rawIFD.Put (stream); + + } + + if (hasTransparencyMask) + { + + maskIFD->Put (stream); + + } + + if (hasDepthMap) + { + + depthIFD->Put (stream); + + } + + if (hasEnhancedImage) + { + + enhancedIFD->Put (stream); + + } + + for (j = 0; j < previewCount; j++) + { + + if (thumbnail != &previewList->Preview (j)) + { + + previewIFD [j]->Put (stream); + + } + + } + + exifSet.Put (stream); + + stream.Flush (); + + } + +/*****************************************************************************/ diff --git a/dng/dng_image_writer.h b/dng/dng_image_writer.h new file mode 100644 index 0000000..9eeaa59 --- /dev/null +++ b/dng/dng_image_writer.h @@ -0,0 +1,1296 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Support for writing DNG images to files. + */ + +/*****************************************************************************/ + +#ifndef __dng_image_writer__ +#define __dng_image_writer__ + +/*****************************************************************************/ + +#include "dng_area_task.h" +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_fingerprint.h" +#include "dng_memory.h" +#include "dng_mutex.h" +#include "dng_point.h" +#include "dng_rational.h" +#include "dng_safe_arithmetic.h" +#include "dng_sdk_limits.h" +#include "dng_string.h" +#include "dng_tag_types.h" +#include "dng_tag_values.h" +#include "dng_types.h" +#include "dng_uncopyable.h" + +#include + +/*****************************************************************************/ + +/// \brief Image resolution. + +class dng_resolution + { + + public: + + dng_urational fXResolution; + dng_urational fYResolution; + + uint16 fResolutionUnit; + + public: + + dng_resolution (); + + }; + +/*****************************************************************************/ + +class tiff_tag: private dng_uncopyable + { + + protected: + + uint16 fCode; + + uint16 fType; + + uint32 fCount; + + protected: + + tiff_tag (uint16 code, + uint16 type, + uint32 count) + + : fCode (code) + , fType (type) + , fCount (count) + + { + } + + public: + + virtual ~tiff_tag () + { + } + + uint16 Code () const + { + return fCode; + } + + uint16 Type () const + { + return fType; + } + + uint32 Count () const + { + return fCount; + } + + void SetCount (uint32 count) + { + fCount = count; + } + + uint32 Size () const + { + return TagTypeSize (Type ()) * Count (); + } + + virtual void Put (dng_stream &stream) const = 0; + + }; + +/******************************************************************************/ + +class tag_data_ptr: public tiff_tag + { + + protected: + + const void *fData; + + public: + + tag_data_ptr (uint16 code, + uint16 type, + uint32 count, + const void *data) + + : tiff_tag (code, type, count) + + , fData (data) + + { + } + + void SetData (const void *data) + { + fData = data; + } + + virtual void Put (dng_stream &stream) const; + + }; + +/******************************************************************************/ + +class tag_string: public tiff_tag + { + + protected: + + dng_string fString; + + public: + + tag_string (uint16 code, + const dng_string &s, + bool forceASCII = true); + + virtual void Put (dng_stream &stream) const; + + }; + +/******************************************************************************/ + +class tag_encoded_text: public tiff_tag + { + + private: + + dng_string fText; + + dng_memory_data fUTF16; + + public: + + tag_encoded_text (uint16 code, + const dng_string &text); + + virtual void Put (dng_stream &stream) const; + + }; + +/******************************************************************************/ + +class tag_uint8: public tag_data_ptr + { + + private: + + uint8 fValue; + + public: + + tag_uint8 (uint16 code, + uint8 value = 0) + + : tag_data_ptr (code, ttByte, 1, &fValue) + + , fValue (value) + + { + } + + void Set (uint8 value) + { + fValue = value; + } + + }; + +/******************************************************************************/ + +class tag_uint8_ptr: public tag_data_ptr + { + + public: + + tag_uint8_ptr (uint16 code, + const uint8 *data, + uint32 count = 1) + + : tag_data_ptr (code, ttByte, count, data) + + { + } + + }; + +/******************************************************************************/ + +class tag_uint16: public tag_data_ptr + { + + private: + + uint16 fValue; + + public: + + tag_uint16 (uint16 code, + uint16 value = 0) + + : tag_data_ptr (code, ttShort, 1, &fValue) + + , fValue (value) + + { + } + + void Set (uint16 value) + { + fValue = value; + } + + }; + +/******************************************************************************/ + +class tag_int16_ptr: public tag_data_ptr + { + + public: + + tag_int16_ptr (uint16 code, + const int16 *data, + uint32 count = 1) + + : tag_data_ptr (code, ttSShort, count, data) + + { + } + + }; + +/******************************************************************************/ + +class tag_uint16_ptr: public tag_data_ptr + { + + public: + + tag_uint16_ptr (uint16 code, + const uint16 *data, + uint32 count = 1) + + : tag_data_ptr (code, ttShort, count, data) + + { + } + + }; + +/******************************************************************************/ + +class tag_uint32: public tag_data_ptr + { + + private: + + uint32 fValue; + + public: + + tag_uint32 (uint16 code, + uint32 value = 0) + + : tag_data_ptr (code, ttLong, 1, &fValue) + + , fValue (value) + + { + } + + void Set (uint32 value) + { + fValue = value; + } + + }; + +/******************************************************************************/ + +class tag_uint32_ptr: public tag_data_ptr + { + + public: + + tag_uint32_ptr (uint16 code, + const uint32 *data, + uint32 count = 1) + + : tag_data_ptr (code, ttLong, count, data) + + { + } + + }; + +/******************************************************************************/ + +class tag_urational: public tag_data_ptr + { + + private: + + const dng_urational fValue; + + public: + + tag_urational (uint16 code, + const dng_urational &value) + + : tag_data_ptr (code, ttRational, 1, &fValue) + + , fValue (value) + + { + } + + }; + +/******************************************************************************/ + +class tag_urational_ptr: public tag_data_ptr + { + + public: + + tag_urational_ptr (uint16 code, + const dng_urational *data = NULL, + uint32 count = 1) + + : tag_data_ptr (code, ttRational, count, data) + + { + } + + }; + +/******************************************************************************/ + +class tag_srational: public tag_data_ptr + { + + private: + + const dng_srational fValue; + + public: + + tag_srational (uint16 code, + const dng_srational &value) + + : tag_data_ptr (code, ttSRational, 1, &fValue) + + , fValue (value) + + { + } + + }; + +/******************************************************************************/ + +class tag_srational_ptr: public tag_data_ptr + { + + public: + + tag_srational_ptr (uint16 code, + const dng_srational *data = NULL, + uint32 count = 1) + + : tag_data_ptr (code, ttSRational, count, data) + + { + } + + }; + +/******************************************************************************/ + +class tag_real64: public tag_data_ptr + { + + private: + + real64 fValue; + + public: + + tag_real64 (uint16 code, + real64 value = 0.0) + + : tag_data_ptr (code, ttDouble, 1, &fValue) + + , fValue (value) + + { + } + + void Set (real64 value) + { + fValue = value; + } + + }; + +/******************************************************************************/ + +class tag_matrix: public tag_srational_ptr + { + + private: + + dng_srational fEntry [kMaxColorPlanes * + kMaxColorPlanes]; + + public: + + tag_matrix (uint16 code, + const dng_matrix &m); + + }; + +/******************************************************************************/ + +class tag_icc_profile: public tag_data_ptr + { + + public: + + tag_icc_profile (const void *profileData, uint32 profileSize); + + }; + +/******************************************************************************/ + +class tag_cfa_pattern: public tiff_tag + { + + private: + + uint32 fRows; + uint32 fCols; + + const uint8 *fPattern; + + public: + + tag_cfa_pattern (uint16 code, + uint32 rows, + uint32 cols, + const uint8 *pattern) + + : tiff_tag (code, ttUndefined, 4 + rows * cols) + + , fRows (rows ) + , fCols (cols ) + , fPattern (pattern) + + { + } + + virtual void Put (dng_stream &stream) const; + + }; + +/******************************************************************************/ + +class tag_exif_date_time: public tag_data_ptr + { + + private: + + char fData [20]; + + public: + + tag_exif_date_time (uint16 code, + const dng_date_time &dt); + + }; + +/******************************************************************************/ + +class tag_iptc: public tiff_tag + { + + private: + + const void *fData; + + uint32 fLength; + + public: + + tag_iptc (const void *data, + uint32 length); + + virtual void Put (dng_stream &stream) const; + + }; + +/******************************************************************************/ + +class tag_xmp: public tag_uint8_ptr + { + + private: + + AutoPtr fBuffer; + + public: + + tag_xmp (const dng_xmp *xmp); + + }; + +/******************************************************************************/ + +class dng_tiff_directory: private dng_uncopyable + { + + private: + + enum + { + kMaxEntries = 100 + }; + + uint32 fEntries; + + const tiff_tag *fTag [kMaxEntries]; + + uint32 fChained; + + public: + + dng_tiff_directory () + + : fEntries (0) + , fChained (0) + + { + } + + virtual ~dng_tiff_directory () + { + } + + void Add (const tiff_tag *tag); + + void SetChained (uint32 offset) + { + fChained = offset; + } + + uint32 Size () const; + + enum OffsetsBase + { + offsetsRelativeToStream = 0, + offsetsRelativeToExplicitBase = 1, + offsetsRelativeToIFD = 2 + }; + + void Put (dng_stream &stream, + OffsetsBase offsetsBase = offsetsRelativeToStream, + uint32 explicitBase = 0) const; + + }; + +/******************************************************************************/ + +class dng_basic_tag_set: private dng_uncopyable + { + + private: + + tag_uint32 fNewSubFileType; + + tag_uint32 fImageWidth; + tag_uint32 fImageLength; + + tag_uint16 fPhotoInterpretation; + + tag_uint16 fFillOrder; + + tag_uint16 fSamplesPerPixel; + + uint16 fBitsPerSampleData [kMaxSamplesPerPixel]; + + tag_uint16_ptr fBitsPerSample; + + bool fStrips; + + tag_uint32 fTileWidth; + tag_uint32 fTileLength; + + dng_memory_data fTileInfoBuffer; + + uint32 *fTileOffsetData; + + tag_uint32_ptr fTileOffsets; + + uint32 *fTileByteCountData; + + tag_uint32_ptr fTileByteCounts; + + tag_uint16 fPlanarConfiguration; + + tag_uint16 fCompression; + + tag_uint16 fPredictor; + + uint16 fExtraSamplesData [kMaxSamplesPerPixel]; + + tag_uint16_ptr fExtraSamples; + + uint16 fSampleFormatData [kMaxSamplesPerPixel]; + + tag_uint16_ptr fSampleFormat; + + tag_uint16 fRowInterleaveFactor; + + uint16 fSubTileBlockSizeData [2]; + + tag_uint16_ptr fSubTileBlockSize; + + public: + + dng_basic_tag_set (dng_tiff_directory &directory, + const dng_ifd &info); + + virtual ~dng_basic_tag_set () + { + } + + void SetTileOffset (uint32 index, + uint32 offset) + { + fTileOffsetData [index] = offset; + } + + void SetTileByteCount (uint32 index, + uint32 count) + { + fTileByteCountData [index] = count; + } + + bool WritingStrips () const + { + return fStrips; + } + + }; + +/******************************************************************************/ + +class exif_tag_set: private dng_uncopyable + { + + protected: + + dng_tiff_directory fExifIFD; + dng_tiff_directory fGPSIFD; + + private: + + tag_uint32 fExifLink; + tag_uint32 fGPSLink; + + bool fAddedExifLink; + bool fAddedGPSLink; + + uint8 fExifVersionData [4]; + + tag_data_ptr fExifVersion; + + tag_urational fExposureTime; + tag_srational fShutterSpeedValue; + + tag_urational fFNumber; + tag_urational fApertureValue; + + tag_srational fBrightnessValue; + + tag_srational fExposureBiasValue; + + tag_urational fMaxApertureValue; + + tag_urational fSubjectDistance; + + tag_urational fFocalLength; + + tag_uint16 fISOSpeedRatings; + + tag_uint16 fSensitivityType; + tag_uint32 fStandardOutputSensitivity; + tag_uint32 fRecommendedExposureIndex; + tag_uint32 fISOSpeed; + tag_uint32 fISOSpeedLatitudeyyy; + tag_uint32 fISOSpeedLatitudezzz; + + tag_uint16 fFlash; + + tag_uint16 fExposureProgram; + + tag_uint16 fMeteringMode; + + tag_uint16 fLightSource; + + tag_uint16 fSensingMethod; + + tag_uint16 fFocalLength35mm; + + uint8 fFileSourceData; + tag_data_ptr fFileSource; + + uint8 fSceneTypeData; + tag_data_ptr fSceneType; + + tag_cfa_pattern fCFAPattern; + + tag_uint16 fCustomRendered; + tag_uint16 fExposureMode; + tag_uint16 fWhiteBalance; + tag_uint16 fSceneCaptureType; + tag_uint16 fGainControl; + tag_uint16 fContrast; + tag_uint16 fSaturation; + tag_uint16 fSharpness; + tag_uint16 fSubjectDistanceRange; + + tag_urational fDigitalZoomRatio; + + tag_urational fExposureIndex; + + tag_uint32 fImageNumber; + + tag_uint16 fSelfTimerMode; + + tag_string fBatteryLevelA; + tag_urational fBatteryLevelR; + + tag_uint16 fColorSpace; + + tag_urational fFocalPlaneXResolution; + tag_urational fFocalPlaneYResolution; + + tag_uint16 fFocalPlaneResolutionUnit; + + uint16 fSubjectAreaData [4]; + + tag_uint16_ptr fSubjectArea; + + dng_urational fLensInfoData [4]; + + tag_urational_ptr fLensInfo; + + tag_exif_date_time fDateTime; + tag_exif_date_time fDateTimeOriginal; + tag_exif_date_time fDateTimeDigitized; + + tag_string fSubsecTime; + tag_string fSubsecTimeOriginal; + tag_string fSubsecTimeDigitized; + + tag_string fOffsetTime; + tag_string fOffsetTimeOriginal; + tag_string fOffsetTimeDigitized; + + tag_string fMake; + tag_string fModel; + tag_string fArtist; + tag_string fSoftware; + tag_string fCopyright; + tag_string fImageDescription; + + tag_string fSerialNumber; + + tag_uint16 fMakerNoteSafety; + + tag_data_ptr fMakerNote; + + tag_encoded_text fUserComment; + + char fImageUniqueIDData [33]; + + tag_data_ptr fImageUniqueID; + + // EXIF 2.3 tags. + + tag_string fCameraOwnerName; + tag_string fBodySerialNumber; + tag_urational_ptr fLensSpecification; + tag_string fLensMake; + tag_string fLensModel; + tag_string fLensSerialNumber; + + // EXIF 2.3.1 tags. + + tag_srational fTemperature; + tag_urational fHumidity; + tag_urational fPressure; + tag_srational fWaterDepth; + tag_urational fAcceleration; + tag_srational fCameraElevationAngle; + + uint8 fGPSVersionData [4]; + + tag_uint8_ptr fGPSVersionID; + + tag_string fGPSLatitudeRef; + tag_urational_ptr fGPSLatitude; + + tag_string fGPSLongitudeRef; + tag_urational_ptr fGPSLongitude; + + tag_uint8 fGPSAltitudeRef; + tag_urational fGPSAltitude; + + tag_urational_ptr fGPSTimeStamp; + + tag_string fGPSSatellites; + tag_string fGPSStatus; + tag_string fGPSMeasureMode; + + tag_urational fGPSDOP; + + tag_string fGPSSpeedRef; + tag_urational fGPSSpeed; + + tag_string fGPSTrackRef; + tag_urational fGPSTrack; + + tag_string fGPSImgDirectionRef; + tag_urational fGPSImgDirection; + + tag_string fGPSMapDatum; + + tag_string fGPSDestLatitudeRef; + tag_urational_ptr fGPSDestLatitude; + + tag_string fGPSDestLongitudeRef; + tag_urational_ptr fGPSDestLongitude; + + tag_string fGPSDestBearingRef; + tag_urational fGPSDestBearing; + + tag_string fGPSDestDistanceRef; + tag_urational fGPSDestDistance; + + tag_encoded_text fGPSProcessingMethod; + tag_encoded_text fGPSAreaInformation; + + tag_string fGPSDateStamp; + + tag_uint16 fGPSDifferential; + + tag_urational fGPSHPositioningError; + + public: + + exif_tag_set (dng_tiff_directory &directory, + const dng_exif &exif, + bool makerNoteSafe = false, + const void *makerNoteData = NULL, + uint32 makerNoteLength = 0, + bool insideDNG = false); + + void Locate (uint32 offset) + { + fExifLink.Set (offset); + fGPSLink .Set (offset + fExifIFD.Size ()); + } + + uint32 Size () const + { + return fExifIFD.Size () + + fGPSIFD .Size (); + } + + void Put (dng_stream &stream) const + { + fExifIFD.Put (stream); + fGPSIFD .Put (stream); + } + + protected: + + void AddLinks (dng_tiff_directory &directory); + + }; + +/******************************************************************************/ + +class tiff_dng_extended_color_profile: private dng_tiff_directory + { + + protected: + + const dng_camera_profile &fProfile; + + public: + + tiff_dng_extended_color_profile (const dng_camera_profile &profile); + + void Put (dng_stream &stream, + bool includeModelRestriction = true); + + }; + +/*****************************************************************************/ + +class tag_dng_noise_profile: public tag_data_ptr + { + + protected: + + real64 fValues [2 * kMaxColorPlanes]; + + public: + + explicit tag_dng_noise_profile (const dng_noise_profile &profile); + + }; + +/*****************************************************************************/ + +// Enum to control the subset of metadata to save to a file. + +enum dng_metadata_subset + { + + kMetadataSubset_CopyrightOnly = 0, + kMetadataSubset_CopyrightAndContact, + kMetadataSubset_AllExceptCameraInfo, + kMetadataSubset_All, + kMetadataSubset_AllExceptLocationInfo, + kMetadataSubset_AllExceptCameraAndLocation, + KMetadataSubset_AllExceptCameraRawInfo, + KMetadataSubset_AllExceptCameraRawInfoAndLocation, + + kMetadataSubset_Last = KMetadataSubset_AllExceptCameraRawInfoAndLocation + + }; + +/*****************************************************************************/ + +/// \brief Support for writing dng_image or dng_negative instances to a +/// dng_stream in TIFF or DNG format. + +class dng_image_writer + { + + friend class dng_jpeg_image; + friend class dng_jpeg_image_encode_task; + friend class dng_write_tiles_task; + + protected: + + enum + { + + // Target size for buffer used to copy data to the image. + + kImageBufferSize = 128 * 1024 + + }; + + public: + + dng_image_writer (); + + virtual ~dng_image_writer (); + + virtual void EncodeJPEGPreview (dng_host &host, + const dng_image &image, + dng_jpeg_preview &preview, + int32 quality = -1); + + virtual void WriteImage (dng_host &host, + const dng_ifd &ifd, + dng_basic_tag_set &basic, + dng_stream &stream, + const dng_image &image, + uint32 fakeChannels = 1); + + /// Write a dng_image to a dng_stream in TIFF format. + /// \param host Host interface used for progress updates, abort testing, buffer allocation, etc. + /// \param stream The dng_stream on which to write the TIFF. + /// \param image The actual image data to be written. + /// \param photometricInterpretation Either piBlackIsZero for monochrome or piRGB for RGB images. + /// \param compression Must be ccUncompressed. + /// \param negative or metadata If non-NULL, EXIF, IPTC, and XMP metadata from this negative is written to TIFF. + /// \param space If non-null and color space has an ICC profile, TIFF will be tagged with this + /// profile. No color space conversion of image data occurs. + /// \param resolution If non-NULL, TIFF will be tagged with this resolution. + /// \param thumbnail If non-NULL, will be stored in TIFF as preview image. + /// \param imageResources If non-NULL, will image resources be stored in TIFF as well. + /// \param metadataSubset The subset of metadata (e.g., copyright only) to include in the TIFF. + + void WriteTIFF (dng_host &host, + dng_stream &stream, + const dng_image &image, + uint32 photometricInterpretation, + uint32 compression, + dng_negative *negative, + const dng_color_space *space = NULL, + const dng_resolution *resolution = NULL, + const dng_jpeg_preview *thumbnail = NULL, + const dng_memory_block *imageResources = NULL, + dng_metadata_subset metadataSubset = kMetadataSubset_All, + bool hasTransparency = false); + + void WriteTIFF (dng_host &host, + dng_stream &stream, + const dng_image &image, + uint32 photometricInterpretation = piBlackIsZero, + uint32 compression = ccUncompressed, + const dng_metadata *metadata = NULL, + const dng_color_space *space = NULL, + const dng_resolution *resolution = NULL, + const dng_jpeg_preview *thumbnail = NULL, + const dng_memory_block *imageResources = NULL, + dng_metadata_subset metadataSubset = kMetadataSubset_All, + bool hasTransparency = false); + + /// Write a dng_image to a dng_stream in TIFF format. + /// \param host Host interface used for progress updates, abort testing, buffer allocation, etc. + /// \param stream The dng_stream on which to write the TIFF. + /// \param image The actual image data to be written. + /// \param photometricInterpretation Either piBlackIsZero for monochrome or piRGB for RGB images. + /// \param compression Must be ccUncompressed. + /// \param negative or metadata If non-NULL, EXIF, IPTC, and XMP metadata from this negative is written to TIFF. + /// \param profileData If non-null, TIFF will be tagged with this profile. No color space conversion + /// of image data occurs. + /// \param profileSize The size for the profile data. + /// \param resolution If non-NULL, TIFF will be tagged with this resolution. + /// \param thumbnail If non-NULL, will be stored in TIFF as preview image. + /// \param imageResources If non-NULL, will image resources be stored in TIFF as well. + /// \param metadataSubset The subset of metadata (e.g., copyright only) to include in the TIFF. + + void WriteTIFFWithProfile (dng_host &host, + dng_stream &stream, + const dng_image &image, + uint32 photometricInterpretation, + uint32 compression, + dng_negative *negative, + const void *profileData = NULL, + uint32 profileSize = 0, + const dng_resolution *resolution = NULL, + const dng_jpeg_preview *thumbnail = NULL, + const dng_memory_block *imageResources = NULL, + dng_metadata_subset metadataSubset = kMetadataSubset_All, + bool hasTransparency = false); + + virtual void WriteTIFFWithProfile (dng_host &host, + dng_stream &stream, + const dng_image &image, + uint32 photometricInterpretation = piBlackIsZero, + uint32 compression = ccUncompressed, + const dng_metadata *metadata = NULL, + const void *profileData = NULL, + uint32 profileSize = 0, + const dng_resolution *resolution = NULL, + const dng_jpeg_preview *thumbnail = NULL, + const dng_memory_block *imageResources = NULL, + dng_metadata_subset metadataSubset = kMetadataSubset_All, + bool hasTransparency = false); + + /// Write a dng_image to a dng_stream in DNG format. + /// \param host Host interface used for progress updates, abort testing, buffer allocation, etc. + /// \param stream The dng_stream on which to write the TIFF. + /// \param negative The image data and metadata (EXIF, IPTC, XMP) to be written. + /// \param previewList List of previews (not counting thumbnail) to write to the file. Defaults to empty. + /// \param maxBackwardVersion The DNG file should be readable by readers at least back to this version. + /// \param uncompressed True to force uncompressed images. Otherwise use normal compression. + + void WriteDNG (dng_host &host, + dng_stream &stream, + dng_negative &negative, + const dng_preview_list *previewList = NULL, + uint32 maxBackwardVersion = dngVersion_SaveDefault, + bool uncompressed = false); + + /// Write a dng_image to a dng_stream in DNG format. + /// \param host Host interface used for progress updates, abort testing, buffer allocation, etc. + /// \param stream The dng_stream on which to write the TIFF. + /// \param negative The image data to be written. + /// \param metadata The metadata (EXIF, IPTC, XMP) to be written. + /// \param previewList List of previews (not counting thumbnail) to write to the file. Defaults to empty. + /// \param maxBackwardVersion The DNG file should be readable by readers at least back to this version. + /// \param uncompressed True to force uncompressed images. Otherwise use normal compression. + + virtual void WriteDNGWithMetadata (dng_host &host, + dng_stream &stream, + const dng_negative &negative, + const dng_metadata &metadata, + const dng_preview_list *previewList = NULL, + uint32 maxBackwardVersion = dngVersion_SaveDefault, + bool uncompressed = false); + + /// Resolve metadata conflicts and apply metadata policies in keeping + /// with Metadata Working Group (MWG) guidelines. + + virtual void CleanUpMetadata (dng_host &host, + dng_metadata &metadata, + dng_metadata_subset metadataSubset, + const char *dstMIME, + const char *software = NULL); + + protected: + + virtual void UpdateExifColorSpaceTag (dng_metadata &metadata, + const void *profileData, + const uint32 profileSize); + + virtual uint32 CompressedBufferSize (const dng_ifd &ifd, + uint32 uncompressedSize); + + virtual void EncodePredictor (dng_host &host, + const dng_ifd &ifd, + dng_pixel_buffer &buffer, + AutoPtr &tempBuffer); + + virtual void ByteSwapBuffer (dng_host &host, + dng_pixel_buffer &buffer); + + void ReorderSubTileBlocks (const dng_ifd &ifd, + dng_pixel_buffer &buffer, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer); + + virtual void WriteData (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_pixel_buffer &buffer, + AutoPtr &compressedBuffer, + bool usingMultipleThreads); + + virtual void WriteTile (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + const dng_image &image, + const dng_rect &tileArea, + uint32 fakeChannels, + AutoPtr &compressedBuffer, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer, + AutoPtr &tempBuffer, + bool usingMultipleThreads); + + virtual void DoWriteTiles (dng_host &host, + const dng_ifd &ifd, + dng_basic_tag_set &basic, + dng_stream &stream, + const dng_image &image, + uint32 fakeChannels, + uint32 tilesDown, + uint32 tilesAcross, + uint32 compressedSize, + const dng_safe_uint32 &uncompressedSize); + + }; + +/*****************************************************************************/ + +class dng_write_tiles_task : public dng_area_task, + private dng_uncopyable + { + + protected: + + dng_image_writer &fImageWriter; + + dng_host &fHost; + + const dng_ifd &fIFD; + + dng_basic_tag_set &fBasic; + + dng_stream &fStream; + + const dng_image &fImage; + + uint32 fFakeChannels; + + uint32 fTilesDown; + + uint32 fTilesAcross; + + uint32 fCompressedSize; + + uint32 fUncompressedSize; + + std::atomic_uint fNextTileIndex; + + dng_mutex fMutex; + + dng_condition fCondition; + + bool fTaskFailed; + + uint32 fWriteTileIndex; + + public: + + dng_write_tiles_task (dng_image_writer &imageWriter, + dng_host &host, + const dng_ifd &ifd, + dng_basic_tag_set &basic, + dng_stream &stream, + const dng_image &image, + uint32 fakeChannels, + uint32 tilesDown, + uint32 tilesAcross, + uint32 compressedSize, + uint32 uncompressedSize); + + void Process (uint32 threadIndex, + const dng_rect &tile, + dng_abort_sniffer *sniffer); + + protected: + + void ProcessTask (uint32 tileIndex, + AutoPtr &compressedBuffer, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer, + AutoPtr &tempBuffer, + uint32 &tileByteCount, // output + dng_memory_stream &tileStream, // output + dng_abort_sniffer *sniffer); + + void WriteTask (uint32 tileIndex, + uint32 tileByteCount, + dng_memory_stream &tileStream, + dng_abort_sniffer *sniffer); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_info.cpp b/dng/dng_info.cpp new file mode 100644 index 0000000..68e6cc9 --- /dev/null +++ b/dng/dng_info.cpp @@ -0,0 +1,2742 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_info.h" + +#include "dng_camera_profile.h" +#include "dng_exceptions.h" +#include "dng_globals.h" +#include "dng_host.h" +#include "dng_tag_codes.h" +#include "dng_parse_utils.h" +#include "dng_tag_types.h" +#include "dng_tag_values.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_info::dng_info () + + : fTIFFBlockOffset (0) + , fTIFFBlockOriginalOffset (kDNGStreamInvalidOffset) + , fBigEndian (false) + , fMagic (0) + , fExif () + , fShared () + , fMainIndex (-1) + , fMaskIndex (-1) + , fDepthIndex (-1) + , fEnhancedIndex (-1) + , fIFD () + , fChainedIFD () + , fChainedSubIFD () + , fMakerNoteNextIFD (0) + + { + + } + +/*****************************************************************************/ + +dng_info::~dng_info () + { + + for (size_t index = 0; index < fIFD.size (); index++) + { + + if (fIFD [index]) + { + delete fIFD [index]; + fIFD [index] = NULL; + } + + } + + for (size_t index2 = 0; index2 < fChainedIFD.size (); index2++) + { + + if (fChainedIFD [index2]) + { + delete fChainedIFD [index2]; + fChainedIFD [index2] = NULL; + } + + } + + for (size_t index3 = 0; index3 < fChainedSubIFD.size (); index3++) + { + + for (size_t index4 = 0; index4 < fChainedSubIFD [index3].size (); index4++) + { + + if (fChainedSubIFD [index3] [index4]) + { + delete fChainedSubIFD [index3] [index4]; + fChainedSubIFD [index3] [index4] = NULL; + } + + } + + } + + } + +/*****************************************************************************/ + +void dng_info::ValidateMagic () + { + + switch (fMagic) + { + + case magicTIFF: + case magicExtendedProfile: + case magicRawCache: + case magicPanasonic: + case magicOlympusA: + case magicOlympusB: + { + + return; + + } + + default: + { + + #if qDNGValidate + + ReportError ("Invalid TIFF magic number"); + + #endif + + ThrowBadFormat (); + + } + + } + + } + +/*****************************************************************************/ + +void dng_info::ParseTag (dng_host &host, + dng_stream &stream, + dng_exif *exif, + dng_shared *shared, + dng_ifd *ifd, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset, + int64 offsetDelta) + { + + bool isSubIFD = parentCode >= tcFirstSubIFD && + parentCode <= tcLastSubIFD; + + bool isMainIFD = (parentCode == 0 || isSubIFD) && + ifd && + ifd->fUsesNewSubFileType && + ifd->fNewSubFileType == sfMainImage; + + // Panasonic RAW format stores private tags using tag codes < 254 in + // IFD 0. Redirect the parsing of these tags into a logical + // "PanasonicRAW" IFD. + + // Panasonic is starting to use some higher numbers also (280..283). + + if (fMagic == 85 && parentCode == 0 && (tagCode < tcNewSubFileType || + (tagCode >= 280 && tagCode <= 283))) + { + + parentCode = tcPanasonicRAW; + + ifd = NULL; + + } + + stream.SetReadPosition (tagOffset); + + if (ifd && ifd->ParseTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + tagOffset)) + { + + return; + + } + + stream.SetReadPosition (tagOffset); + + if (exif && shared && exif->ParseTag (stream, + *shared, + parentCode, + isMainIFD, + tagCode, + tagType, + tagCount, + tagOffset)) + { + + return; + + } + + stream.SetReadPosition (tagOffset); + + if (shared && exif && shared->ParseTag (stream, + *exif, + parentCode, + isMainIFD, + tagCode, + tagType, + tagCount, + tagOffset, + offsetDelta)) + { + + return; + + } + + if (parentCode == tcLeicaMakerNote && + tagType == ttUndefined && + tagCount >= 14) + { + + if (ParseMakerNoteIFD (host, + stream, + tagCount, + tagOffset, + offsetDelta, + tagOffset, + stream.Length (), + tcLeicaMakerNote)) + { + + return; + + } + + } + + if (parentCode == tcOlympusMakerNote && + tagType == ttUndefined && + tagCount >= 14) + { + + uint32 olympusMakerParent = 0; + + switch (tagCode) + { + + case 8208: + olympusMakerParent = tcOlympusMakerNote8208; + break; + + case 8224: + olympusMakerParent = tcOlympusMakerNote8224; + break; + + case 8240: + olympusMakerParent = tcOlympusMakerNote8240; + break; + + case 8256: + olympusMakerParent = tcOlympusMakerNote8256; + break; + + case 8272: + olympusMakerParent = tcOlympusMakerNote8272; + break; + + case 12288: + olympusMakerParent = tcOlympusMakerNote12288; + break; + + default: + break; + + } + + if (olympusMakerParent) + { + + // Olympus made a mistake in some camera models in computing + // the size of these sub-tags, so we fudge the count. + + if (ParseMakerNoteIFD (host, + stream, + stream.Length () - tagOffset, + tagOffset, + offsetDelta, + tagOffset, + stream.Length (), + olympusMakerParent)) + { + + return; + + } + + } + + } + + if (parentCode == tcRicohMakerNote && + tagCode == 0x2001 && + tagType == ttUndefined && + tagCount > 22) + { + + char header [20]; + + stream.SetReadPosition (tagOffset); + + stream.Get (header, sizeof (header)); + + if (memcmp (header, "[Ricoh Camera Info]", 19) == 0) + { + + ParseMakerNoteIFD (host, + stream, + tagCount - 20, + tagOffset + 20, + offsetDelta, + tagOffset + 20, + tagOffset + tagCount, + tcRicohMakerNoteCameraInfo); + + return; + + } + + } + + #if qDNGValidate + + { + + stream.SetReadPosition (tagOffset); + + if (gVerbose) + { + + printf ("*"); + + DumpTagValues (stream, + LookupTagType (tagType), + parentCode, + tagCode, + tagType, + tagCount); + + } + + // If type is ASCII, then parse anyway so we report any ASCII + // NULL termination or character set errors. + + else if (tagType == ttAscii) + { + + dng_string s; + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + s, + false); + + } + + } + + #endif + + } + +/*****************************************************************************/ + +bool dng_info::ValidateIFD (dng_stream &stream, + uint64 ifdOffset, + int64 offsetDelta) + { + + // Make sure we have a count. + + if (ifdOffset + 2 > stream.Length ()) + { + return false; + } + + // Get entry count. + + stream.SetReadPosition (ifdOffset); + + uint32 ifdEntries = stream.Get_uint16 (); + + if (ifdEntries < 1) + { + return false; + } + + // Make sure we have room for all entries and next IFD link. + + if (ifdOffset + 2 + ifdEntries * 12 + 4 > stream.Length ()) + { + return false; + } + + // Check each entry. + + for (uint32 tag_index = 0; tag_index < ifdEntries; tag_index++) + { + + stream.SetReadPosition (ifdOffset + 2 + tag_index * 12); + + stream.Skip (2); // Ignore tag code. + + uint32 tagType = stream.Get_uint16 (); + uint32 tagCount = stream.Get_uint32 (); + + uint32 tag_type_size = TagTypeSize (tagType); + + if (tag_type_size == 0) + { + return false; + } + + uint32 tag_data_size = tagCount * tag_type_size; + + if (tag_data_size > 4) + { + + uint64 tagOffset = stream.Get_uint32 (); + + tagOffset += offsetDelta; + + if (tagOffset + tag_data_size > stream.Length ()) + { + return false; + } + + } + + } + + return true; + + } + +/*****************************************************************************/ + +void dng_info::ParseIFD (dng_host &host, + dng_stream &stream, + dng_exif *exif, + dng_shared *shared, + dng_ifd *ifd, + uint64 ifdOffset, + int64 offsetDelta, + uint32 parentCode) + { + + #if qDNGValidate + + bool isMakerNote = (parentCode >= tcFirstMakerNoteIFD && + parentCode <= tcLastMakerNoteIFD); + + #endif + + // TIFF IFDs often read from two very different places in the file, + // one for the IFD itself (and small tags), and elsewhere in the file + // for large tags. We can reduce the number of calls to the OS + // by double buffering reads for the two areas of the file. + + dng_stream_double_buffered ifdStream (stream); + + ifdStream.SetReadPosition (ifdOffset); + + if (ifd) + { + ifd->fThisIFD = ifdOffset; + } + + uint32 ifdEntries = ifdStream.Get_uint16 (); + + #if qDNGValidate + + // IC hits these warnings on JPG + bool generateOddOffsetWarnings = true; + if (gImagecore) + generateOddOffsetWarnings = false; + + if (gVerbose) + { + + printf ("%s: Offset = %u, Entries = %u\n\n", + LookupParentCode (parentCode), + (unsigned) ifdOffset, + (unsigned) ifdEntries); + + } + + if (generateOddOffsetWarnings && (ifdOffset & 1) && !isMakerNote) + { + + char message [256]; + + sprintf (message, + "%s has odd offset (%u)", + LookupParentCode (parentCode), + (unsigned) ifdOffset); + + ReportWarning (message); + + } + + #endif + + uint32 prev_tag_code = 0; + + for (uint32 tag_index = 0; tag_index < ifdEntries; tag_index++) + { + + ifdStream.SetReadPosition (ifdOffset + 2 + tag_index * 12); + + uint32 tagCode = ifdStream.Get_uint16 (); + uint32 tagType = ifdStream.Get_uint16 (); + + // Minolta 7D files have a bug in the EXIF block where the count + // is wrong, and we run off into next IFD link. So if abort parsing + // if we get a zero code/type combinations. + + if (tagCode == 0 && tagType == 0) + { + + #if qDNGValidate + + char message [256]; + + sprintf (message, + "%s had zero/zero tag code/type entry", + LookupParentCode (parentCode)); + + ReportWarning (message); + + #endif + + return; + + } + + uint32 tagCount = ifdStream.Get_uint32 (); + + #if qDNGValidate + + { + + if (tag_index > 0 && tagCode <= prev_tag_code && !isMakerNote) + { + + char message [256]; + + sprintf (message, + "%s tags are not sorted in ascending numerical order", + LookupParentCode (parentCode)); + + ReportWarning (message); + + } + + } + + #endif + + prev_tag_code = tagCode; + + uint32 tag_type_size = TagTypeSize (tagType); + + if (tag_type_size == 0) + { + + #if qDNGValidate + + { + + char message [256]; + + sprintf (message, + "%s %s has unknown type (%u)", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode), + (unsigned) tagType); + + ReportWarning (message); + + } + + #endif + + continue; + + } + + bool localTag = true; + + uint64 tagOffset = ifdOffset + 2 + tag_index * 12 + 8; + + if (tagCount * tag_type_size > 4) + { + + tagOffset = ifdStream.Get_uint32 (); + + #if qDNGValidate + + { + + if (generateOddOffsetWarnings && + !(ifdOffset & 1) && + (tagOffset & 1) && + !isMakerNote && + parentCode != tcKodakDCRPrivateIFD && + parentCode != tcKodakKDCPrivateIFD) + { + + char message [256]; + + sprintf (message, + "%s %s has odd data offset (%u)", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode), + (unsigned) tagOffset); + + ReportWarning (message); + + } + + } + + #endif + + tagOffset += offsetDelta; + + localTag = ifdStream.DataInBuffer (tagCount * tag_type_size, + tagOffset); + + if (localTag) + ifdStream.SetReadPosition (tagOffset); + else + stream.SetReadPosition (tagOffset); + + } + + ParseTag (host, + localTag ? ifdStream : stream, + exif, + shared, + ifd, + parentCode, + tagCode, + tagType, + tagCount, + tagOffset, + offsetDelta); + + } + + ifdStream.SetReadPosition (ifdOffset + 2 + ifdEntries * 12); + + uint32 nextIFD = ifdStream.Get_uint32 (); + + #if qDNGValidate + + if (gVerbose) + { + printf ("NextIFD = %u\n", (unsigned) nextIFD); + } + + #endif + + if (ifd) + { + ifd->fNextIFD = nextIFD; + } + + #if qDNGValidate + + if (nextIFD) + { + + if (parentCode != 0 && + (parentCode < tcFirstChainedIFD || + parentCode > tcLastChainedIFD )) + { + + char message [256]; + + sprintf (message, + "%s has an unexpected non-zero NextIFD (%u)", + LookupParentCode (parentCode), + (unsigned) nextIFD); + + ReportWarning (message); + + } + + } + + if (gVerbose) + { + printf ("\n"); + } + + stream.SetReadPosition (ifdStream.Position ()); + + #endif + + } + +/*****************************************************************************/ + +bool dng_info::ParseMakerNoteIFD (dng_host &host, + dng_stream &stream, + uint64 ifdSize, + uint64 ifdOffset, + int64 offsetDelta, + uint64 minOffset, + uint64 maxOffset, + uint32 parentCode) + { + + uint32 tagIndex; + uint32 tagCode; + uint32 tagType; + uint32 tagCount; + + // Assume there is no next IFD pointer. + + fMakerNoteNextIFD = 0; + + // If size is too small to hold a single entry IFD, abort. + + if (ifdSize < 14) + { + return false; + } + + // Get entry count. + + dng_stream_double_buffered ifdStream (stream); + + ifdStream.SetReadPosition (ifdOffset); + + uint32 ifdEntries = ifdStream.Get_uint16 (); + + // Make the entry count if reasonable for the MakerNote size. + + if (ifdEntries < 1 || 2 + ifdEntries * 12 > ifdSize) + { + return false; + } + + // Scan IFD to verify all the tag types are all valid. + + for (tagIndex = 0; tagIndex < ifdEntries; tagIndex++) + { + + ifdStream.SetReadPosition (ifdOffset + 2 + tagIndex * 12 + 2); + + tagType = ifdStream.Get_uint16 (); + + // Kludge: Some Canon MakerNotes contain tagType = 0 tags, so we + // need to ignore them. This was a "firmware 1.0.4" Canon 40D raw file. + + if (parentCode == tcCanonMakerNote && tagType == 0) + { + continue; + } + + if (TagTypeSize (tagType) == 0) + { + return false; + } + + } + + // OK, the IFD looks reasonable enough to parse. + + #if qDNGValidate + + if (gVerbose) + { + + printf ("%s: Offset = %u, Entries = %u\n\n", + LookupParentCode (parentCode), + (unsigned) ifdOffset, + (unsigned) ifdEntries); + + } + + #endif + + for (tagIndex = 0; tagIndex < ifdEntries; tagIndex++) + { + + ifdStream.SetReadPosition (ifdOffset + 2 + tagIndex * 12); + + tagCode = ifdStream.Get_uint16 (); + tagType = ifdStream.Get_uint16 (); + tagCount = ifdStream.Get_uint32 (); + + if (tagType == 0) + { + continue; + } + + uint32 tagSize = tagCount * TagTypeSize (tagType); + + uint64 tagOffset = ifdOffset + 2 + tagIndex * 12 + 8; + + bool localTag = true; + + if (tagSize > 4) + { + + tagOffset = ifdStream.Get_uint32 () + offsetDelta; + + if (tagOffset < minOffset || + tagOffset + tagSize > maxOffset) + { + + // Tag data is outside the valid offset range, + // so ignore this tag. + + continue; + + } + + localTag = ifdStream.DataInBuffer (tagSize, tagOffset); + + ifdStream.SetReadPosition (tagOffset); + + stream.SetReadPosition (tagOffset); + + } + + // Olympus switched to using IFDs in version 3 makernotes. + + if (parentCode == tcOlympusMakerNote && + tagType == ttIFD && + tagCount == 1) + { + + uint32 olympusMakerParent = 0; + + switch (tagCode) + { + + case 8208: + olympusMakerParent = tcOlympusMakerNote8208; + break; + + case 8224: + olympusMakerParent = tcOlympusMakerNote8224; + break; + + case 8240: + olympusMakerParent = tcOlympusMakerNote8240; + break; + + case 8256: + olympusMakerParent = tcOlympusMakerNote8256; + break; + + case 8272: + olympusMakerParent = tcOlympusMakerNote8272; + break; + + case 12288: + olympusMakerParent = tcOlympusMakerNote12288; + break; + + default: + break; + + } + + if (olympusMakerParent) + { + + stream.SetReadPosition (tagOffset); + + uint64 subMakerNoteOffset = stream.Get_uint32 () + offsetDelta; + + if (subMakerNoteOffset >= minOffset && + subMakerNoteOffset < maxOffset) + { + + if (ParseMakerNoteIFD (host, + stream, + maxOffset - subMakerNoteOffset, + subMakerNoteOffset, + offsetDelta, + minOffset, + maxOffset, + olympusMakerParent)) + { + + continue; + + } + + } + + } + + stream.SetReadPosition (tagOffset); + + } + + ParseTag (host, + localTag ? ifdStream : stream, + fExif.Get (), + fShared.Get (), + NULL, + parentCode, + tagCode, + tagType, + tagCount, + tagOffset, + offsetDelta); + + } + + // Grab next IFD pointer, for possible use. + + if (ifdSize >= 2 + ifdEntries * 12 + 4) + { + + ifdStream.SetReadPosition (ifdOffset + 2 + ifdEntries * 12); + + fMakerNoteNextIFD = ifdStream.Get_uint32 (); + + } + + #if qDNGValidate + + if (gVerbose) + { + printf ("\n"); + } + + #endif + + return true; + + } + +/*****************************************************************************/ + +void dng_info::ParseMakerNote (dng_host &host, + dng_stream &stream, + uint32 makerNoteCount, + uint64 makerNoteOffset, + int64 offsetDelta, + uint64 minOffset, + uint64 maxOffset) + { + + uint8 firstBytes [16]; + + memset (firstBytes, 0, sizeof (firstBytes)); + + stream.SetReadPosition (makerNoteOffset); + + stream.Get (firstBytes, (uint32) Min_uint64 (sizeof (firstBytes), + makerNoteCount)); + + // Epson MakerNote with header. + + if (memcmp (firstBytes, "EPSON\000\001\000", 8) == 0) + { + + if (makerNoteCount > 8) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount - 8, + makerNoteOffset + 8, + offsetDelta, + minOffset, + maxOffset, + tcEpsonMakerNote); + + } + + return; + + } + + // Fujifilm MakerNote. + + if (memcmp (firstBytes, "FUJIFILM", 8) == 0) + { + + stream.SetReadPosition (makerNoteOffset + 8); + + TempLittleEndian tempEndian (stream); + + uint32 ifd_offset = stream.Get_uint32 (); + + if (ifd_offset >= 12 && ifd_offset < makerNoteCount) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount - ifd_offset, + makerNoteOffset + ifd_offset, + makerNoteOffset, + minOffset, + maxOffset, + tcFujiMakerNote); + + } + + return; + + } + + // Leica MakerNote for models that store entry offsets relative to the start of + // the MakerNote (e.g., M9). + + if ((memcmp (firstBytes, "LEICA\000\000\000", 8) == 0) || + (memcmp (firstBytes, "LEICA0\003\000", 8) == 0) || + (memcmp (firstBytes, "LEICA\000\001\000", 8) == 0) || + (memcmp (firstBytes, "LEICA\000\004\000", 8) == 0) || + (memcmp (firstBytes, "LEICA\000\005\000", 8) == 0) || + (memcmp (firstBytes, "LEICA\000\006\000", 8) == 0)) + { + + if (makerNoteCount > 8) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount - 8, + makerNoteOffset + 8, + makerNoteOffset, + minOffset, + maxOffset, + tcLeicaMakerNote); + + } + + return; + + } + + // Leica MakerNote for models that store absolute entry offsets (i.e., relative + // to the start of the file, e.g., S2). + + if ((memcmp (firstBytes, "LEICA\000\002\377", 8) == 0) || + (memcmp (firstBytes, "LEICA\000\002\000", 8) == 0)) + { + + if (makerNoteCount > 8) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount - 8, + makerNoteOffset + 8, + offsetDelta, + minOffset, + maxOffset, + tcLeicaMakerNote); + + } + + return; + + } + + // Nikon version 2 MakerNote with header. + + if (memcmp (firstBytes, "Nikon\000\002", 7) == 0) + { + + stream.SetReadPosition (makerNoteOffset + 10); + + bool bigEndian = false; + + uint16 endianMark = stream.Get_uint16 (); + + if (endianMark == byteOrderMM) + { + bigEndian = true; + } + + else if (endianMark != byteOrderII) + { + return; + } + + TempBigEndian temp_endian (stream, bigEndian); + + uint16 magic = stream.Get_uint16 (); + + if (magic != 42) + { + return; + } + + uint32 ifd_offset = stream.Get_uint32 (); + + if (ifd_offset >= 8 && ifd_offset < makerNoteCount - 10) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount - 10 - ifd_offset, + makerNoteOffset + 10 + ifd_offset, + makerNoteOffset + 10, + minOffset, + maxOffset, + tcNikonMakerNote); + + } + + return; + + } + + // Newer version of Olympus MakerNote with byte order mark. + + if (memcmp (firstBytes, "OLYMPUS\000", 8) == 0) + { + + stream.SetReadPosition (makerNoteOffset + 8); + + bool bigEndian = false; + + uint16 endianMark = stream.Get_uint16 (); + + if (endianMark == byteOrderMM) + { + bigEndian = true; + } + + else if (endianMark != byteOrderII) + { + return; + } + + TempBigEndian temp_endian (stream, bigEndian); + + uint16 version = stream.Get_uint16 (); + + if (version != 3) + { + return; + } + + if (makerNoteCount > 12) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount - 12, + makerNoteOffset + 12, + makerNoteOffset, + minOffset, + maxOffset, + tcOlympusMakerNote); + + } + + return; + + } + + // Olympus MakerNote with header. + + if (memcmp (firstBytes, "OLYMP", 5) == 0) + { + + if (makerNoteCount > 8) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount - 8, + makerNoteOffset + 8, + offsetDelta, + minOffset, + maxOffset, + tcOlympusMakerNote); + + } + + return; + + } + + // Panasonic MakerNote. + + if (memcmp (firstBytes, "Panasonic\000\000\000", 12) == 0) + { + + if (makerNoteCount > 12) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount - 12, + makerNoteOffset + 12, + offsetDelta, + minOffset, + maxOffset, + tcPanasonicMakerNote); + + } + + return; + + } + + // Pentax MakerNote, absolute addresses. + + if (memcmp (firstBytes, "AOC", 4) == 0) + { + + if (makerNoteCount > 6) + { + + stream.SetReadPosition (makerNoteOffset + 4); + + bool bigEndian = stream.BigEndian (); + + uint16 endianMark = stream.Get_uint16 (); + + if (endianMark == byteOrderMM) + { + bigEndian = true; + } + + else if (endianMark == byteOrderII) + { + bigEndian = false; + } + + TempBigEndian temp_endian (stream, bigEndian); + + ParseMakerNoteIFD (host, + stream, + makerNoteCount - 6, + makerNoteOffset + 6, + offsetDelta, + minOffset, + maxOffset, + tcPentaxMakerNote); + + } + + return; + + } + + // Pentax MakerNote, relative addresses. + + if (memcmp (firstBytes, "PENTAX", 6) == 0) + { + + if (makerNoteCount > 8) + { + + stream.SetReadPosition (makerNoteOffset + 8); + + bool bigEndian = stream.BigEndian (); + + uint16 endianMark = stream.Get_uint16 (); + + if (endianMark == byteOrderMM) + { + bigEndian = true; + } + + else if (endianMark == byteOrderII) + { + bigEndian = false; + } + + TempBigEndian temp_endian (stream, bigEndian); + + ParseMakerNoteIFD (host, + stream, + makerNoteCount - 10, + makerNoteOffset + 10, + makerNoteOffset, // Relative to start of MakerNote. + minOffset, + maxOffset, + tcPentaxMakerNote); + + } + + return; + + } + + // Ricoh MakerNote. + + if (memcmp (firstBytes, "RICOH", 5) == 0 || + memcmp (firstBytes, "Ricoh", 5) == 0) + { + + if (makerNoteCount > 8) + { + + TempBigEndian tempEndian (stream); + + ParseMakerNoteIFD (host, + stream, + makerNoteCount - 8, + makerNoteOffset + 8, + offsetDelta, + minOffset, + maxOffset, + tcRicohMakerNote); + + } + + return; + + } + + // Nikon MakerNote without header. + + if (fExif->fMake.StartsWith ("NIKON")) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount, + makerNoteOffset, + offsetDelta, + minOffset, + maxOffset, + tcNikonMakerNote); + + return; + + } + + // Canon MakerNote. + + if (fExif->fMake.StartsWith ("CANON")) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount, + makerNoteOffset, + offsetDelta, + minOffset, + maxOffset, + tcCanonMakerNote); + + return; + + } + + // Minolta MakerNote. + + if (fExif->fMake.StartsWith ("MINOLTA" ) || + fExif->fMake.StartsWith ("KONICA MINOLTA")) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount, + makerNoteOffset, + offsetDelta, + minOffset, + maxOffset, + tcMinoltaMakerNote); + + return; + + } + + // Sony MakerNote. + + if (fExif->fMake.StartsWith ("SONY")) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount, + makerNoteOffset, + offsetDelta, + minOffset, + maxOffset, + tcSonyMakerNote); + + return; + + } + + // Kodak MakerNote. + + if (fExif->fMake.StartsWith ("EASTMAN KODAK")) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount, + makerNoteOffset, + offsetDelta, + minOffset, + maxOffset, + tcKodakMakerNote); + + return; + + } + + // Mamiya MakerNote. + + if (fExif->fMake.StartsWith ("Mamiya")) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount, + makerNoteOffset, + offsetDelta, + minOffset, + maxOffset, + tcMamiyaMakerNote); + + // Mamiya uses a MakerNote chain. + + while (fMakerNoteNextIFD) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount, + offsetDelta + fMakerNoteNextIFD, + offsetDelta, + minOffset, + maxOffset, + tcMamiyaMakerNote); + + } + + return; + + } + + // Nikon MakerNote without header. + + if (fExif->fMake.StartsWith ("Hasselblad")) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount, + makerNoteOffset, + offsetDelta, + minOffset, + maxOffset, + tcHasselbladMakerNote); + + return; + + } + + // Samsung MakerNote. + + if (fExif->fMake.StartsWith ("Samsung")) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount, + makerNoteOffset, + makerNoteOffset, + minOffset, + maxOffset, + tcSamsungMakerNote); + + return; + + } + + // Casio MakerNote. + + if (fExif->fMake.StartsWith ("CASIO COMPUTER") && + memcmp (firstBytes, "QVC\000\000\000", 6) == 0) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount - 6, + makerNoteOffset + 6, + makerNoteOffset, + minOffset, + maxOffset, + tcCasioMakerNote); + + return; + + } + + } + +/*****************************************************************************/ + +void dng_info::ParseSonyPrivateData (dng_host & /* host */, + dng_stream & /* stream */, + uint64 /* count */, + uint64 /* oldOffset */, + uint64 /* newOffset */) + { + + // Sony private data is encrypted, sorry. + + } + +/*****************************************************************************/ + +void dng_info::ParseDNGPrivateData (dng_host &host, + dng_stream &stream) + { + + if (fShared->fDNGPrivateDataCount < 2) + { + return; + } + + // DNG private data should always start with a null-terminated + // company name, to define the format of the private data. + + dng_string privateName; + + { + + char buffer [64]; + + stream.SetReadPosition (fShared->fDNGPrivateDataOffset); + + uint32 readLength = Min_uint32 (fShared->fDNGPrivateDataCount, + sizeof (buffer) - 1); + + stream.Get (buffer, readLength); + + buffer [readLength] = 0; + + privateName.Set (buffer); + + } + + // Pentax is storing their MakerNote in the DNGPrivateData data. + + if (privateName.StartsWith ("PENTAX" ) || + privateName.StartsWith ("SAMSUNG")) + { + + #if qDNGValidate + + if (gVerbose) + { + printf ("Parsing Pentax/Samsung DNGPrivateData\n\n"); + } + + #endif + + stream.SetReadPosition (fShared->fDNGPrivateDataOffset + 8); + + bool bigEndian = stream.BigEndian (); + + uint16 endianMark = stream.Get_uint16 (); + + if (endianMark == byteOrderMM) + { + bigEndian = true; + } + + else if (endianMark == byteOrderII) + { + bigEndian = false; + } + + TempBigEndian temp_endian (stream, bigEndian); + + ParseMakerNoteIFD (host, + stream, + fShared->fDNGPrivateDataCount - 10, + fShared->fDNGPrivateDataOffset + 10, + fShared->fDNGPrivateDataOffset, + fShared->fDNGPrivateDataOffset, + fShared->fDNGPrivateDataOffset + fShared->fDNGPrivateDataCount, + tcPentaxMakerNote); + + return; + + } + + // Stop parsing if this is not an Adobe format block. + + if (!privateName.Matches ("Adobe")) + { + return; + } + + TempBigEndian temp_order (stream); + + uint32 section_offset = 6; + + while (section_offset + 8 < fShared->fDNGPrivateDataCount) + { + + stream.SetReadPosition (fShared->fDNGPrivateDataOffset + section_offset); + + uint32 section_key = stream.Get_uint32 (); + uint32 section_count = stream.Get_uint32 (); + + if (section_key == DNG_CHAR4 ('M','a','k','N') && section_count > 6) + { + + #if qDNGValidate + + if (gVerbose) + { + printf ("Found MakerNote inside DNGPrivateData\n\n"); + } + + #endif + + uint16 order_mark = stream.Get_uint16 (); + uint64 old_offset = stream.Get_uint32 (); + + uint32 tempSize = section_count - 6; + + AutoPtr tempBlock (host.Allocate (tempSize)); + + uint64 positionInOriginalFile = stream.PositionInOriginalFile(); + + stream.Get (tempBlock->Buffer (), tempSize); + + dng_stream tempStream (tempBlock->Buffer (), + tempSize, + positionInOriginalFile); + + tempStream.SetBigEndian (order_mark == byteOrderMM); + + ParseMakerNote (host, + tempStream, + tempSize, + 0, + 0 - old_offset, + 0, + tempSize); + + } + + else if (section_key == DNG_CHAR4 ('S','R','2',' ') && section_count > 6) + { + + #if qDNGValidate + + if (gVerbose) + { + printf ("Found Sony private data inside DNGPrivateData\n\n"); + } + + #endif + + uint16 order_mark = stream.Get_uint16 (); + uint64 old_offset = stream.Get_uint32 (); + + uint64 new_offset = fShared->fDNGPrivateDataOffset + section_offset + 14; + + TempBigEndian sr2_order (stream, order_mark == byteOrderMM); + + ParseSonyPrivateData (host, + stream, + section_count - 6, + old_offset, + new_offset); + + } + + else if (section_key == DNG_CHAR4 ('R','A','F',' ') && section_count > 4) + { + + #if qDNGValidate + + if (gVerbose) + { + printf ("Found Fuji RAF tags inside DNGPrivateData\n\n"); + } + + #endif + + uint16 order_mark = stream.Get_uint16 (); + + uint32 tagCount = stream.Get_uint32 (); + + uint64 tagOffset = stream.Position (); + + if (tagCount) + { + + TempBigEndian raf_order (stream, order_mark == byteOrderMM); + + ParseTag (host, + stream, + fExif.Get (), + fShared.Get (), + NULL, + tcFujiRAF, + tcFujiHeader, + ttUndefined, + tagCount, + tagOffset, + 0); + + stream.SetReadPosition (tagOffset + tagCount); + + } + + tagCount = stream.Get_uint32 (); + + tagOffset = stream.Position (); + + if (tagCount) + { + + TempBigEndian raf_order (stream, order_mark == byteOrderMM); + + ParseTag (host, + stream, + fExif.Get (), + fShared.Get (), + NULL, + tcFujiRAF, + tcFujiRawInfo1, + ttUndefined, + tagCount, + tagOffset, + 0); + + stream.SetReadPosition (tagOffset + tagCount); + + } + + tagCount = stream.Get_uint32 (); + + tagOffset = stream.Position (); + + if (tagCount) + { + + TempBigEndian raf_order (stream, order_mark == byteOrderMM); + + ParseTag (host, + stream, + fExif.Get (), + fShared.Get (), + NULL, + tcFujiRAF, + tcFujiRawInfo2, + ttUndefined, + tagCount, + tagOffset, + 0); + + stream.SetReadPosition (tagOffset + tagCount); + + } + + } + + else if (section_key == DNG_CHAR4 ('C','n','t','x') && section_count > 4) + { + + #if qDNGValidate + + if (gVerbose) + { + printf ("Found Contax Raw header inside DNGPrivateData\n\n"); + } + + #endif + + uint16 order_mark = stream.Get_uint16 (); + + uint32 tagCount = stream.Get_uint32 (); + + uint64 tagOffset = stream.Position (); + + if (tagCount) + { + + TempBigEndian contax_order (stream, order_mark == byteOrderMM); + + ParseTag (host, + stream, + fExif.Get (), + fShared.Get (), + NULL, + tcContaxRAW, + tcContaxHeader, + ttUndefined, + tagCount, + tagOffset, + 0); + + } + + } + + else if (section_key == DNG_CHAR4 ('C','R','W',' ') && section_count > 4) + { + + #if qDNGValidate + + if (gVerbose) + { + printf ("Found Canon CRW tags inside DNGPrivateData\n\n"); + } + + #endif + + uint16 order_mark = stream.Get_uint16 (); + uint32 entries = stream.Get_uint16 (); + + uint64 crwTagStart = stream.Position (); + + for (uint32 parsePass = 1; parsePass <= 2; parsePass++) + { + + stream.SetReadPosition (crwTagStart); + + for (uint32 index = 0; index < entries; index++) + { + + uint32 tagCode = stream.Get_uint16 (); + + uint32 tagCount = stream.Get_uint32 (); + + uint64 tagOffset = stream.Position (); + + // We need to grab the model id tag first, and then all the + // other tags. + + if ((parsePass == 1) == (tagCode == 0x5834)) + { + + TempBigEndian tag_order (stream, order_mark == byteOrderMM); + + ParseTag (host, + stream, + fExif.Get (), + fShared.Get (), + NULL, + tcCanonCRW, + tagCode, + ttUndefined, + tagCount, + tagOffset, + 0); + + } + + stream.SetReadPosition (tagOffset + tagCount); + + } + + } + + } + + else if (section_count > 4) + { + + uint32 parentCode = 0; + + bool code32 = false; + bool hasType = true; + + switch (section_key) + { + + case DNG_CHAR4 ('M','R','W',' '): + { + parentCode = tcMinoltaMRW; + code32 = true; + hasType = false; + break; + } + + case DNG_CHAR4 ('P','a','n','o'): + { + parentCode = tcPanasonicRAW; + break; + } + + case DNG_CHAR4 ('L','e','a','f'): + { + parentCode = tcLeafMOS; + break; + } + + case DNG_CHAR4 ('K','o','d','a'): + { + parentCode = tcKodakDCRPrivateIFD; + break; + } + + case DNG_CHAR4 ('K','D','C',' '): + { + parentCode = tcKodakKDCPrivateIFD; + break; + } + + default: + break; + + } + + if (parentCode) + { + + #if qDNGValidate + + if (gVerbose) + { + printf ("Found %s tags inside DNGPrivateData\n\n", + LookupParentCode (parentCode)); + } + + #endif + + uint16 order_mark = stream.Get_uint16 (); + uint32 entries = stream.Get_uint16 (); + + for (uint32 index = 0; index < entries; index++) + { + + uint32 tagCode = code32 ? stream.Get_uint32 () + : stream.Get_uint16 (); + + uint32 tagType = hasType ? stream.Get_uint16 () + : ttUndefined; + + uint32 tagCount = stream.Get_uint32 (); + + uint32 tagSize = tagCount * TagTypeSize (tagType); + + uint64 tagOffset = stream.Position (); + + TempBigEndian tag_order (stream, order_mark == byteOrderMM); + + ParseTag (host, + stream, + fExif.Get (), + fShared.Get (), + NULL, + parentCode, + tagCode, + tagType, + tagCount, + tagOffset, + 0); + + stream.SetReadPosition (tagOffset + tagSize); + + } + + } + + } + + section_offset += 8 + section_count; + + if (section_offset & 1) + { + section_offset++; + } + + } + + } + +/*****************************************************************************/ + +void dng_info::Parse (dng_host &host, + dng_stream &stream) + { + + fTIFFBlockOffset = stream.Position (); + + fTIFFBlockOriginalOffset = stream.PositionInOriginalFile (); + + // Check byte order indicator. + + uint16 byteOrder = stream.Get_uint16 (); + + if (byteOrder == byteOrderII) + { + + fBigEndian = false; + + #if qDNGValidate + + if (gVerbose) + { + printf ("\nUses little-endian byte order\n"); + } + + #endif + + stream.SetLittleEndian (); + + } + + else if (byteOrder == byteOrderMM) + { + + fBigEndian = true; + + #if qDNGValidate + + if (gVerbose) + { + printf ("\nUses big-endian byte order\n"); + } + + #endif + + stream.SetBigEndian (); + + } + + else + { + + #if qDNGValidate + + ReportError ("Unknown byte order"); + + #endif + + ThrowBadFormat (); + + } + + // Check "magic number" indicator. + + fMagic = stream.Get_uint16 (); + + #if qDNGValidate + + if (gVerbose) + { + printf ("Magic number = %u\n\n", (unsigned) fMagic); + } + + #endif + + ValidateMagic (); + + // Parse IFD 0. + + uint64 next_offset = stream.Get_uint32 (); + + fExif.Reset (host.Make_dng_exif ()); + + fShared.Reset (host.Make_dng_shared ()); + + fIFD.push_back (host.Make_dng_ifd ()); + + ParseIFD (host, + stream, + fExif.Get (), + fShared.Get (), + fIFD [0], + fTIFFBlockOffset + next_offset, + fTIFFBlockOffset, + 0); + + next_offset = fIFD [0]->fNextIFD; + + // Parse chained IFDs. + + while (next_offset) + { + + if (next_offset >= stream.Length ()) + { + + #if qDNGValidate + + { + + ReportWarning ("Chained IFD offset past end of stream"); + + } + + #endif + + break; + + } + + // Some TIFF file writers forget about the next IFD offset, so + // validate the IFD at that offset before parsing it. + + if (!ValidateIFD (stream, + fTIFFBlockOffset + next_offset, + fTIFFBlockOffset)) + { + + #if qDNGValidate + + { + + ReportWarning ("Chained IFD is not valid"); + + } + + #endif + + break; + + } + + if (ChainedIFDCount () == kMaxChainedIFDs) + { + + #if qDNGValidate + + { + + ReportWarning ("Chained IFD count exceeds DNG SDK parsing limit"); + + } + + #endif + + break; + + } + + fChainedIFD.push_back (host.Make_dng_ifd ()); + + fChainedSubIFD.push_back (std::vector ()); + + ParseIFD (host, + stream, + NULL, + NULL, + fChainedIFD [ChainedIFDCount () - 1], + fTIFFBlockOffset + next_offset, + fTIFFBlockOffset, + tcFirstChainedIFD + ChainedIFDCount () - 1); + + next_offset = fChainedIFD [ChainedIFDCount () - 1]->fNextIFD; + + } + + // Parse SubIFDs. + + uint32 searchedIFDs = 0; + + bool tooManySubIFDs = false; + + while (searchedIFDs < IFDCount () && !tooManySubIFDs) + { + + uint32 searchLimit = IFDCount (); + + for (uint32 searchIndex = searchedIFDs; + searchIndex < searchLimit && !tooManySubIFDs; + searchIndex++) + { + + for (uint32 subIndex = 0; + subIndex < fIFD [searchIndex]->fSubIFDsCount; + subIndex++) + { + + if (IFDCount () == kMaxSubIFDs + 1) + { + + tooManySubIFDs = true; + + break; + + } + + stream.SetReadPosition (fIFD [searchIndex]->fSubIFDsOffset + + subIndex * 4); + + uint32 sub_ifd_offset = stream.Get_uint32 (); + + fIFD.push_back (host.Make_dng_ifd ()); + + ParseIFD (host, + stream, + fExif.Get (), + fShared.Get (), + fIFD [IFDCount () - 1], + fTIFFBlockOffset + sub_ifd_offset, + fTIFFBlockOffset, + tcFirstSubIFD + IFDCount () - 2); + + } + + searchedIFDs = searchLimit; + + } + + } + + #if qDNGValidate + + { + + if (tooManySubIFDs) + { + + ReportWarning ("SubIFD count exceeds DNG SDK parsing limit"); + + } + + } + + #endif + + // Parse SubIFDs in Chained IFDs. Don't currently need to make this a + // recursive search. + + for (uint32 chainedIndex = 0; + chainedIndex < ChainedIFDCount (); + chainedIndex++) + { + + for (uint32 subIndex = 0; + subIndex < fChainedIFD [chainedIndex]->fSubIFDsCount; + subIndex++) + { + + if (subIndex == kMaxSubIFDs) + { + + #if qDNGValidate + + ReportWarning ("Chained SubIFD count exceeds DNG SDK parsing limit"); + + #endif + + break; + + } + + stream.SetReadPosition (fChainedIFD [chainedIndex]->fSubIFDsOffset + + subIndex * 4); + + uint32 sub_ifd_offset = stream.Get_uint32 (); + + fChainedSubIFD [chainedIndex].push_back (host.Make_dng_ifd ()); + + ParseIFD (host, + stream, + fExif.Get (), + fShared.Get (), + fChainedSubIFD [chainedIndex] [subIndex], + fTIFFBlockOffset + sub_ifd_offset, + fTIFFBlockOffset, + tcFirstSubIFD + subIndex); + + } + + } + + // Parse EXIF IFD. + + if (fShared->fExifIFD) + { + + ParseIFD (host, + stream, + fExif.Get (), + fShared.Get (), + NULL, + fTIFFBlockOffset + fShared->fExifIFD, + fTIFFBlockOffset, + tcExifIFD); + + } + + // Parse GPS IFD. + + if (fShared->fGPSInfo) + { + + ParseIFD (host, + stream, + fExif.Get (), + fShared.Get (), + NULL, + fTIFFBlockOffset + fShared->fGPSInfo, + fTIFFBlockOffset, + tcGPSInfo); + + } + + // Parse Interoperability IFD. + + if (fShared->fInteroperabilityIFD) + { + + // Some Kodak KDC files have bogus Interoperability IFDs, so + // validate the IFD before trying to parse it. + + if (ValidateIFD (stream, + fTIFFBlockOffset + fShared->fInteroperabilityIFD, + fTIFFBlockOffset)) + { + + ParseIFD (host, + stream, + fExif.Get (), + fShared.Get (), + NULL, + fTIFFBlockOffset + fShared->fInteroperabilityIFD, + fTIFFBlockOffset, + tcInteroperabilityIFD); + + } + + #if qDNGValidate + + else + { + + ReportWarning ("The Interoperability IFD is not a valid IFD"); + + } + + #endif + + } + + // Parse Kodak DCR Private IFD. + + if (fShared->fKodakDCRPrivateIFD) + { + + ParseIFD (host, + stream, + fExif.Get (), + fShared.Get (), + NULL, + fTIFFBlockOffset + fShared->fKodakDCRPrivateIFD, + fTIFFBlockOffset, + tcKodakDCRPrivateIFD); + + } + + // Parse Kodak KDC Private IFD. + + if (fShared->fKodakKDCPrivateIFD) + { + + ParseIFD (host, + stream, + fExif.Get (), + fShared.Get (), + NULL, + fTIFFBlockOffset + fShared->fKodakKDCPrivateIFD, + fTIFFBlockOffset, + tcKodakKDCPrivateIFD); + + } + + // Parse MakerNote tag. + + if (fShared->fMakerNoteCount) + { + + ParseMakerNote (host, + stream, + (uint32) (fTIFFBlockOffset + fShared->fMakerNoteCount), + fShared->fMakerNoteOffset, + fTIFFBlockOffset, + 0, + stream.Length ()); + + } + + // Parse DNGPrivateData tag. + + if (fShared->fDNGPrivateDataCount && + fShared->fDNGVersion) + { + + ParseDNGPrivateData (host, stream); + + } + + #if qDNGValidate + + // If we are running dng_validate on stand-alone camera profile file, + // complete the validation of the profile. + + if (fMagic == magicExtendedProfile) + { + + dng_camera_profile_info &profileInfo = fShared->fCameraProfile; + + dng_camera_profile profile; + + profile.Parse (stream, profileInfo); + + if (profileInfo.fColorPlanes < 3 || !profile.IsValid (profileInfo.fColorPlanes)) + { + + ReportError ("Invalid camera profile file"); + + } + + } + + #endif + + } + +/*****************************************************************************/ + +void dng_info::PostParse (dng_host &host) + { + + uint32 index; + + fExif->PostParse (host, *fShared.Get ()); + + fShared->PostParse (host, *fExif.Get ()); + + for (index = 0; index < IFDCount (); index++) + { + + fIFD [index]->PostParse (); + + } + + for (index = 0; index < ChainedIFDCount (); index++) + { + + fChainedIFD [index]->PostParse (); + + } + + for (size_t i = 0; i < fChainedSubIFD.size (); i++) + { + + std::vector &chain = fChainedSubIFD [i]; + + for (size_t j = 0; j < chain.size (); j++) + { + + if (chain [j]) + { + chain [j]->PostParse (); + } + + } + + } + + if (fShared->fDNGVersion != 0) + { + + // Find main IFD. + + fMainIndex = -1; + + for (index = 0; index < IFDCount (); index++) + { + + if (fIFD [index]->fUsesNewSubFileType && + fIFD [index]->fNewSubFileType == sfMainImage) + { + + if (fMainIndex == -1) + { + + fMainIndex = index; + + } + + #if qDNGValidate + + else + { + + ReportError ("Multiple IFDs marked as main image"); + + } + + #endif + + } + + else if (fIFD [index]->fNewSubFileType == sfPreviewImage || + fIFD [index]->fNewSubFileType == sfAltPreviewImage) + { + + // Fill in default color space for DNG previews if not included. + + if (fIFD [index]->fPreviewInfo.fColorSpace == previewColorSpace_MaxEnum) + { + + if (fIFD [index]->fSamplesPerPixel == 1) + { + + fIFD [index]->fPreviewInfo.fColorSpace = previewColorSpace_GrayGamma22; + + } + + else + { + + fIFD [index]->fPreviewInfo.fColorSpace = previewColorSpace_sRGB; + + } + + } + + } + + } + + // Deal with lossless JPEG bug in early DNG versions. + + if (fShared->fDNGVersion < dngVersion_1_1_0_0) + { + + if (fMainIndex != -1) + { + + fIFD [fMainIndex]->fLosslessJPEGBug16 = true; + + } + + } + + // Find mask index. + + for (index = 0; index < IFDCount (); index++) + { + + if (fIFD [index]->fNewSubFileType == sfTransparencyMask) + { + + if (fMaskIndex == -1) + { + + fMaskIndex = index; + + } + + #if qDNGValidate + + else + { + + ReportError ("Multiple IFDs marked as transparency mask image"); + + } + + #endif + + } + + } + + // Find depth index. + + for (index = 0; index < IFDCount (); index++) + { + + if (fIFD [index]->fNewSubFileType == sfDepthMap) + { + + if (fDepthIndex == -1) + { + + fDepthIndex = index; + + } + + #if qDNGValidate + + else + { + + ReportError ("Multiple IFDs marked as depth map image"); + + } + + #endif + + } + + } + + // Find enhanced ifd index. + + for (index = 0; index < IFDCount (); index++) + { + + if (fIFD [index]->fNewSubFileType == sfEnhancedImage) + { + + if (fEnhancedIndex == -1) + { + + fEnhancedIndex = index; + + } + + #if qDNGValidate + + else + { + + ReportError ("Multiple IFDs marked as enhanced image"); + + } + + #endif + + } + + } + + // Warn about Chained IFDs. + + #if qDNGValidate + + if (ChainedIFDCount () > 0) + { + + ReportWarning ("This file has Chained IFDs, which will be ignored by DNG readers"); + + } + + #endif + + } + + } + +/*****************************************************************************/ + +bool dng_info::IsValidDNG () + { + + // Check shared info. + + if (!fShared->IsValidDNG ()) + { + + return false; + + } + + // Check TIFF magic number. + + if (fMagic != 42) + { + + #if qDNGValidate + + ReportError ("Invalid TIFF magic number"); + + #endif + + return false; + + } + + // Make sure we have a main image IFD. + + if (fMainIndex == -1) + { + + #if qDNGValidate + + ReportError ("Unable to find main image IFD"); + + #endif + + return false; + + } + + // Make sure is each IFD is valid. + + for (uint32 index = 0; index < IFDCount (); index++) + { + + uint32 parentCode = (index == 0 ? 0 : tcFirstSubIFD + index - 1); + + if (!fIFD [index]->IsValidDNG (*fShared.Get (), + parentCode)) + { + + // Only errors in the main and transparency mask IFDs are fatal to parsing. + + if (index == (uint32) fMainIndex || + index == (uint32) fMaskIndex) + { + + return false; + + } + + // Also errors to depth map... + + if (index == (uint32) fDepthIndex) + { + + return false; + + } + + } + + } + + return true; + + } + +/*****************************************************************************/ diff --git a/dng/dng_info.h b/dng/dng_info.h new file mode 100644 index 0000000..f2f596e --- /dev/null +++ b/dng/dng_info.h @@ -0,0 +1,179 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Class for holding top-level information about a DNG image. + */ + +/*****************************************************************************/ + +#ifndef __dng_info__ +#define __dng_info__ + +/*****************************************************************************/ + +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_errors.h" +#include "dng_exif.h" +#include "dng_ifd.h" +#include "dng_sdk_limits.h" +#include "dng_shared.h" +#include "dng_uncopyable.h" + +#include + +/*****************************************************************************/ + +/// \brief Top-level structure of DNG file with access to metadata. +/// +/// See \ref spec_dng "DNG 1.1.0 specification" for information on member fields of this class. + +class dng_info: private dng_uncopyable + { + + public: + + uint64 fTIFFBlockOffset; + + uint64 fTIFFBlockOriginalOffset; + + bool fBigEndian; + + uint32 fMagic; + + AutoPtr fExif; + + AutoPtr fShared; + + int32 fMainIndex; + + int32 fMaskIndex; + + int32 fDepthIndex; + + int32 fEnhancedIndex; + + std::vector fIFD; + + std::vector fChainedIFD; + + std::vector > fChainedSubIFD; + + protected: + + uint32 fMakerNoteNextIFD; + + public: + + dng_info (); + + virtual ~dng_info (); + + /// Returns the number of parsed SubIFDs (including the main IFD). + + uint32 IFDCount () const + { + return (uint32) fIFD.size (); + } + + /// Returns the number of chained IFDs. + + uint32 ChainedIFDCount () const + { + return (uint32) fChainedIFD.size (); + } + + /// Returns number SubIFDs for a chained IFD. + + uint32 ChainedSubIFDCount (uint32 chainIndex) const + { + if (chainIndex >= fChainedSubIFD.size ()) + return 0; + else + return (uint32) fChainedSubIFD [chainIndex].size (); + } + + /// Read dng_info from a dng_stream + /// \param host DNG host used for progress updating, abort testing, buffer allocation, etc. + /// \param stream Stream to read DNG data from. + + virtual void Parse (dng_host &host, + dng_stream &stream); + + /// Must be called immediately after a successful Parse operation. + + virtual void PostParse (dng_host &host); + + /// Test validity of DNG data. + /// \retval true if stream provided a valid DNG. + + virtual bool IsValidDNG (); + + protected: + + virtual void ValidateMagic (); + + virtual void ParseTag (dng_host &host, + dng_stream &stream, + dng_exif *exif, + dng_shared *shared, + dng_ifd *ifd, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset, + int64 offsetDelta); + + virtual bool ValidateIFD (dng_stream &stream, + uint64 ifdOffset, + int64 offsetDelta); + + virtual void ParseIFD (dng_host &host, + dng_stream &stream, + dng_exif *exif, + dng_shared *shared, + dng_ifd *ifd, + uint64 ifdOffset, + int64 offsetDelta, + uint32 parentCode); + + virtual bool ParseMakerNoteIFD (dng_host &host, + dng_stream &stream, + uint64 ifdSize, + uint64 ifdOffset, + int64 offsetDelta, + uint64 minOffset, + uint64 maxOffset, + uint32 parentCode); + + virtual void ParseMakerNote (dng_host &host, + dng_stream &stream, + uint32 makerNoteCount, + uint64 makerNoteOffset, + int64 offsetDelta, + uint64 minOffset, + uint64 maxOffset); + + virtual void ParseSonyPrivateData (dng_host &host, + dng_stream &stream, + uint64 count, + uint64 oldOffset, + uint64 newOffset); + + virtual void ParseDNGPrivateData (dng_host &host, + dng_stream &stream); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_iptc.cpp b/dng/dng_iptc.cpp new file mode 100644 index 0000000..7f982fb --- /dev/null +++ b/dng/dng_iptc.cpp @@ -0,0 +1,978 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_iptc.h" + +#include "dng_assertions.h" +#include "dng_auto_ptr.h" +#include "dng_memory_stream.h" +#include "dng_stream.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_iptc::dng_iptc () + + : fTitle () + + , fUrgency (-1) + + , fCategory () + + , fSupplementalCategories () + + , fKeywords () + + , fInstructions () + + , fDateTimeCreated () + + , fDigitalCreationDateTime () + + , fAuthors () + , fAuthorsPosition () + + , fCity () + , fState () + , fCountry () + , fCountryCode () + + , fLocation () + + , fTransmissionReference () + + , fHeadline () + + , fCredit () + + , fSource () + + , fCopyrightNotice () + + , fDescription () + , fDescriptionWriter () + + { + + } + +/*****************************************************************************/ + +dng_iptc::~dng_iptc () + { + + } + +/*****************************************************************************/ + +bool dng_iptc::IsEmpty () const + { + + if (fTitle.NotEmpty ()) + { + return false; + } + + if (fUrgency >= 0) + { + return false; + } + + if (fCategory.NotEmpty ()) + { + return false; + } + + if (fSupplementalCategories.Count () > 0) + { + return false; + } + + if (fKeywords.Count () > 0) + { + return false; + } + + if (fInstructions.NotEmpty ()) + { + return false; + } + + if (fDateTimeCreated.IsValid ()) + { + return false; + } + + if (fDigitalCreationDateTime.IsValid ()) + { + return false; + } + + if (fAuthors.Count () != 0 || + fAuthorsPosition.NotEmpty ()) + { + return false; + } + + if (fCity .NotEmpty () || + fState .NotEmpty () || + fCountry.NotEmpty ()) + { + return false; + } + + if (fCountryCode.NotEmpty ()) + { + return false; + } + + if (fLocation.NotEmpty ()) + { + return false; + } + + if (fTransmissionReference.NotEmpty ()) + { + return false; + } + + if (fHeadline.NotEmpty ()) + { + return false; + } + + if (fCredit.NotEmpty ()) + { + return false; + } + + if (fSource.NotEmpty ()) + { + return false; + } + + if (fCopyrightNotice.NotEmpty ()) + { + return false; + } + + if (fDescription .NotEmpty () || + fDescriptionWriter.NotEmpty ()) + { + return false; + } + + return true; + + } + +/*****************************************************************************/ + +void dng_iptc::ParseString (dng_stream &stream, + dng_string &s, + CharSet charSet) + { + + uint32 length = stream.Get_uint16 (); + + dng_memory_data buffer (length + 1); + + char *c = buffer.Buffer_char (); + + stream.Get (c, length); + + c [length] = 0; + + switch (charSet) + { + + case kCharSetUTF8: + { + s.Set_UTF8 (c); + break; + } + + default: + { + s.Set_SystemEncoding (c); + } + + } + + s.SetLineEndingsToNewLines (); + + s.StripLowASCII (); + + s.TrimTrailingBlanks (); + + } + +/*****************************************************************************/ + +void dng_iptc::Parse (const void *blockData, + uint32 blockSize, + uint64 offsetInOriginalFile) + { + + dng_stream stream (blockData, + blockSize, + offsetInOriginalFile); + + stream.SetBigEndian (); + + // Make a first pass though the data, trying to figure out the + // character set. + + CharSet charSet = kCharSetUnknown; + + bool isValidUTF8 = true; + + bool hasEncodingMarker = false; + + uint64 firstOffset = stream.Position (); + + uint64 nextOffset = firstOffset; + + while (nextOffset + 5 < stream.Length ()) + { + + stream.SetReadPosition (nextOffset); + + uint8 firstByte = stream.Get_uint8 (); + + if (firstByte != 0x1C) break; + + uint8 record = stream.Get_uint8 (); + uint8 dataSet = stream.Get_uint8 (); + uint32 dataSize = stream.Get_uint16 (); + + nextOffset = stream.Position () + dataSize; + + if (record == 1) + { + + switch (dataSet) + { + + case 90: + { + + hasEncodingMarker = true; + + if (dataSize == 3) + { + + uint32 byte1 = stream.Get_uint8 (); + uint32 byte2 = stream.Get_uint8 (); + uint32 byte3 = stream.Get_uint8 (); + + if (byte1 == 27 /* Escape */ && + byte2 == 0x25 && + byte3 == 0x47) + { + + charSet = kCharSetUTF8; + + } + + } + + break; + + } + + default: + break; + + } + + } + + else if (record == 2) + { + + dng_memory_data buffer (dataSize + 1); + + char *s = buffer.Buffer_char (); + + stream.Get (s, dataSize); + + s [dataSize] = 0; + + isValidUTF8 = isValidUTF8 && dng_string::IsUTF8 (s); + + } + + } + + // If we don't have an encoding marker, and the data is valid + // UTF-8, then assume that it is UTF-8 (rather than system encoding). + + if (!hasEncodingMarker && isValidUTF8) + { + + charSet = kCharSetUTF8; + + } + + // Make a second pass though the data, actually reading the data. + + nextOffset = firstOffset; + + while (nextOffset + 5 < stream.Length ()) + { + + stream.SetReadPosition (nextOffset); + + uint8 firstByte = stream.Get_uint8 (); + + if (firstByte != 0x1C) break; + + uint8 record = stream.Get_uint8 (); + uint8 dataSet = stream.Get_uint8 (); + uint32 dataSize = stream.Get_uint16 (); + + nextOffset = stream.Position () + dataSize; + + if (record == 2) + { + + stream.SetReadPosition (stream.Position () - 2); + + switch ((DataSet) dataSet) + { + + case kObjectNameSet: + { + ParseString (stream, fTitle, charSet); + break; + } + + case kUrgencySet: + { + + int32 size = stream.Get_uint16 (); + + if (size == 1) + { + + char c = stream.Get_int8 (); + + if (c >= '0' && c <= '9') + { + fUrgency = c - '0'; + } + + } + + break; + + } + + case kCategorySet: + { + ParseString (stream, fCategory, charSet); + break; + } + + case kSupplementalCategoriesSet: + { + + dng_string category; + + ParseString (stream, category, charSet); + + if (category.NotEmpty ()) + { + fSupplementalCategories.Append (category); + } + + break; + + } + + case kKeywordsSet: + { + + dng_string keyword; + + ParseString (stream, keyword, charSet); + + if (keyword.NotEmpty ()) + { + fKeywords.Append (keyword); + } + + break; + + } + + case kSpecialInstructionsSet: + { + ParseString (stream, fInstructions, charSet); + break; + } + + case kDateCreatedSet: + { + + uint32 length = stream.Get_uint16 (); + + if (length == 8) + { + + char date [9]; + + stream.Get (date, 8); + + date [8] = 0; + + fDateTimeCreated.Decode_IPTC_Date (date); + + } + + break; + + } + + case kTimeCreatedSet: + { + + uint32 length = stream.Get_uint16 (); + + if (length >= 4 && length <= 11) + { + + char time [12]; + + stream.Get (time, length); + + time [length] = 0; + + fDateTimeCreated.Decode_IPTC_Time (time); + + } + + break; + + } + + case kDigitalCreationDateSet: + { + + uint32 length = stream.Get_uint16 (); + + if (length == 8) + { + + char date [9]; + + stream.Get (date, 8); + + date [8] = 0; + + fDigitalCreationDateTime.Decode_IPTC_Date (date); + + } + + break; + + } + + case kDigitalCreationTimeSet: + { + + uint32 length = stream.Get_uint16 (); + + if (length >= 4 && length <= 11) + { + + char time [12]; + + stream.Get (time, length); + + time [length] = 0; + + fDigitalCreationDateTime.Decode_IPTC_Time (time); + + } + + break; + + } + + case kBylineSet: + { + + dng_string author; + + ParseString (stream, author, charSet); + + if (author.NotEmpty ()) + { + fAuthors.Append (author); + } + + break; + + } + + case kBylineTitleSet: + { + ParseString (stream, fAuthorsPosition, charSet); + break; + } + + case kCitySet: + { + ParseString (stream, fCity, charSet); + break; + } + + case kProvinceStateSet: + { + ParseString (stream, fState, charSet); + break; + } + + case kCountryNameSet: + { + ParseString (stream, fCountry, charSet); + break; + } + + case kCountryCodeSet: + { + ParseString (stream, fCountryCode, charSet); + break; + } + + case kSublocationSet: + { + ParseString (stream, fLocation, charSet); + break; + } + + case kOriginalTransmissionReferenceSet: + { + ParseString (stream, fTransmissionReference, charSet); + break; + } + + case kHeadlineSet: + { + ParseString (stream, fHeadline, charSet); + break; + } + + case kCreditSet: + { + ParseString (stream, fCredit, charSet); + break; + } + + case kSourceSet: + { + ParseString (stream, fSource, charSet); + break; + } + + case kCopyrightNoticeSet: + { + ParseString (stream, fCopyrightNotice, charSet); + break; + } + + case kCaptionSet: + { + ParseString (stream, fDescription, charSet); + break; + } + + case kCaptionWriterSet: + { + ParseString (stream, fDescriptionWriter, charSet); + break; + } + + // All other IPTC records are not part of the IPTC core + // and/or are not kept in sync with XMP tags, so we ignore + // them. + + default: + break; + + } + + } + + } + + } + +/*****************************************************************************/ + +void dng_iptc::SpoolString (dng_stream &stream, + const dng_string &s, + uint8 dataSet, + uint32 maxChars, + CharSet charSet) + { + + if (s.IsEmpty ()) + { + return; + } + + stream.Put_uint16 (0x1C02); + stream.Put_uint8 (dataSet); + + dng_string ss (s); + + ss.SetLineEndingsToReturns (); + + if (charSet == kCharSetUTF8) + { + + // UTF-8 encoding. + + if (ss.Length () > maxChars) + { + ss.Truncate (maxChars); + } + + uint32 len = ss.Length (); + + stream.Put_uint16 ((uint16) len); + + stream.Put (ss.Get (), len); + + } + + else + { + + // System character set encoding. + + dng_memory_data buffer; + + uint32 len = ss.Get_SystemEncoding (buffer); + + if (len > maxChars) + { + + uint32 lower = 0; + uint32 upper = ss.Length () - 1; + + while (upper > lower) + { + + uint32 middle = (upper + lower + 1) >> 1; + + dng_string sss (ss); + + sss.Truncate (middle); + + len = sss.Get_SystemEncoding (buffer); + + if (len <= maxChars) + { + + lower = middle; + + } + + else + { + + upper = middle - 1; + + } + + } + + ss.Truncate (lower); + + len = ss.Get_SystemEncoding (buffer); + + } + + stream.Put_uint16 ((uint16) len); + + stream.Put (buffer.Buffer_char (), len); + + } + + } +/*****************************************************************************/ + +dng_memory_block * dng_iptc::Spool (dng_memory_allocator &allocator, + bool padForTIFF) + { + + uint32 j; + + char s [64]; + + dng_memory_stream stream (allocator, NULL, 2048); + + stream.SetBigEndian (); + + // Medata working group - now we just always write UTF-8. + + CharSet charSet = kCharSetUTF8; + + // UTF-8 encoding marker. + + if (charSet == kCharSetUTF8) + { + + stream.Put_uint16 (0x1C01); + stream.Put_uint8 (90); + stream.Put_uint16 (3); + stream.Put_uint8 (27); + stream.Put_uint8 (0x25); + stream.Put_uint8 (0x47); + + } + + stream.Put_uint16 (0x1C02); + stream.Put_uint8 (kRecordVersionSet); + stream.Put_uint16 (2); + stream.Put_uint16 (4); + + SpoolString (stream, + fTitle, + kObjectNameSet, + 64, + charSet); + + if (fUrgency >= 0) + { + + sprintf (s, "%1u", (unsigned) fUrgency); + + stream.Put_uint16 (0x1C02); + stream.Put_uint8 (kUrgencySet); + + stream.Put_uint16 (1); + + stream.Put (s, 1); + + } + + SpoolString (stream, + fCategory, + kCategorySet, + 3, + charSet); + + for (j = 0; j < fSupplementalCategories.Count (); j++) + { + + SpoolString (stream, + fSupplementalCategories [j], + kSupplementalCategoriesSet, + 32, + charSet); + + } + + for (j = 0; j < fKeywords.Count (); j++) + { + + SpoolString (stream, + fKeywords [j], + kKeywordsSet, + 64, + charSet); + + } + + SpoolString (stream, + fInstructions, + kSpecialInstructionsSet, + 255, + charSet); + + if (fDateTimeCreated.IsValid ()) + { + + dng_string dateString = fDateTimeCreated.Encode_IPTC_Date (); + + if (dateString.NotEmpty ()) + { + + DNG_ASSERT (dateString.Length () == 8, "Wrong length IPTC date"); + + stream.Put_uint16 (0x1C02); + stream.Put_uint8 (kDateCreatedSet); + + stream.Put_uint16 (8); + + stream.Put (dateString.Get (), 8); + + } + + dng_string timeString = fDateTimeCreated.Encode_IPTC_Time (); + + if (timeString.NotEmpty ()) + { + + stream.Put_uint16 (0x1C02); + stream.Put_uint8 (kTimeCreatedSet); + + stream.Put_uint16 ((uint16)timeString.Length ()); + + stream.Put (timeString.Get (), timeString.Length ()); + + } + + } + + if (fDigitalCreationDateTime.IsValid ()) + { + + dng_string dateString = fDigitalCreationDateTime.Encode_IPTC_Date (); + + if (dateString.NotEmpty ()) + { + + DNG_ASSERT (dateString.Length () == 8, "Wrong length IPTC date"); + + stream.Put_uint16 (0x1C02); + stream.Put_uint8 (kDigitalCreationDateSet); + + stream.Put_uint16 (8); + + stream.Put (dateString.Get (), 8); + + } + + dng_string timeString = fDigitalCreationDateTime.Encode_IPTC_Time (); + + if (timeString.NotEmpty ()) + { + + stream.Put_uint16 (0x1C02); + stream.Put_uint8 (kDigitalCreationTimeSet); + + stream.Put_uint16 ((uint16)timeString.Length ()); + + stream.Put (timeString.Get (), timeString.Length ()); + + } + + } + + for (j = 0; j < fAuthors.Count (); j++) + { + + SpoolString (stream, + fAuthors [j], + kBylineSet, + 32, + charSet); + + } + + SpoolString (stream, + fAuthorsPosition, + kBylineTitleSet, + 32, + charSet); + + SpoolString (stream, + fCity, + kCitySet, + 32, + charSet); + + SpoolString (stream, + fLocation, + kSublocationSet, + 32, + charSet); + + SpoolString (stream, + fState, + kProvinceStateSet, + 32, + charSet); + + SpoolString (stream, + fCountryCode, + kCountryCodeSet, + 3, + charSet); + + SpoolString (stream, + fCountry, + kCountryNameSet, + 64, + charSet); + + SpoolString (stream, + fTransmissionReference, + kOriginalTransmissionReferenceSet, + 32, + charSet); + + SpoolString (stream, + fHeadline, + kHeadlineSet, + 255, + charSet); + + SpoolString (stream, + fCredit, + kCreditSet, + 32, + charSet); + + SpoolString (stream, + fSource, + kSourceSet, + 32, + charSet); + + SpoolString (stream, + fCopyrightNotice, + kCopyrightNoticeSet, + 128, + charSet); + + SpoolString (stream, + fDescription, + kCaptionSet, + 2000, + charSet); + + SpoolString (stream, + fDescriptionWriter, + kCaptionWriterSet, + 32, + charSet); + + if (padForTIFF) + { + + while (stream.Length () & 3) + { + stream.Put_uint8 (0); + } + + } + + stream.Flush (); + + return stream.AsMemoryBlock (allocator); + + } + +/*****************************************************************************/ diff --git a/dng/dng_iptc.h b/dng/dng_iptc.h new file mode 100644 index 0000000..70d5e4e --- /dev/null +++ b/dng/dng_iptc.h @@ -0,0 +1,167 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Support for IPTC metadata within DNG files. + */ + +/*****************************************************************************/ + +#ifndef __dng_iptc__ +#define __dng_iptc__ + +/*****************************************************************************/ + +#include "dng_date_time.h" +#include "dng_string.h" +#include "dng_string_list.h" + +/*****************************************************************************/ + +/// \brief Class for reading and holding IPTC metadata associated with a DNG file. +/// +/// See the \ref spec_iptc "IPTC specification" +/// for information on member fields of this class. + +class dng_iptc + { + + public: + + dng_string fTitle; + + int32 fUrgency; + + dng_string fCategory; + + dng_string_list fSupplementalCategories; + + dng_string_list fKeywords; + + dng_string fInstructions; + + dng_date_time_info fDateTimeCreated; + + dng_date_time_info fDigitalCreationDateTime; + + dng_string_list fAuthors; + + dng_string fAuthorsPosition; + + dng_string fCity; + dng_string fState; + dng_string fCountry; + dng_string fCountryCode; + + dng_string fLocation; + + dng_string fTransmissionReference; + + dng_string fHeadline; + + dng_string fCredit; + + dng_string fSource; + + dng_string fCopyrightNotice; + + dng_string fDescription; + dng_string fDescriptionWriter; + + protected: + + enum DataSet + { + kRecordVersionSet = 0, + kObjectNameSet = 5, + kUrgencySet = 10, + kCategorySet = 15, + kSupplementalCategoriesSet = 20, + kKeywordsSet = 25, + kSpecialInstructionsSet = 40, + kDateCreatedSet = 55, + kTimeCreatedSet = 60, + kDigitalCreationDateSet = 62, + kDigitalCreationTimeSet = 63, + kBylineSet = 80, + kBylineTitleSet = 85, + kCitySet = 90, + kSublocationSet = 92, + kProvinceStateSet = 95, + kCountryCodeSet = 100, + kCountryNameSet = 101, + kOriginalTransmissionReferenceSet = 103, + kHeadlineSet = 105, + kCreditSet = 110, + kSourceSet = 115, + kCopyrightNoticeSet = 116, + kCaptionSet = 120, + kCaptionWriterSet = 122 + }; + + enum CharSet + { + kCharSetUnknown = 0, + kCharSetUTF8 = 1 + }; + + public: + + dng_iptc (); + + virtual ~dng_iptc (); + + /// Test if IPTC metadata exists. + /// \retval true if no IPTC metadata exists for this DNG. + + bool IsEmpty () const; + + /// Test if IPTC metadata exists. + /// \retval true if IPTC metadata exists for this DNG. + + bool NotEmpty () const + { + return !IsEmpty (); + } + + /// Parse a complete block of IPTC data. + /// \param blockData The block of IPTC data. + /// \param blockSize Size in bytes of data block. + /// \param offsetInOriginalFile Used to enable certain file patching operations such as updating date/time in place. + + void Parse (const void *blockData, + uint32 blockSize, + uint64 offsetInOriginalFile); + + /// Serialize IPTC data to a memory block. + /// \param allocator Memory allocator used to acquire memory block. + /// \param padForTIFF Forces length of block to be a multiple of four bytes in accordance with TIFF standard. + /// \retval Memory block + + dng_memory_block * Spool (dng_memory_allocator &allocator, + bool padForTIFF); + + protected: + + void ParseString (dng_stream &stream, + dng_string &s, + CharSet charSet); + + void SpoolString (dng_stream &stream, + const dng_string &s, + uint8 dataSet, + uint32 maxChars, + CharSet charSet); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_jpeg_image.cpp b/dng/dng_jpeg_image.cpp new file mode 100644 index 0000000..9428497 --- /dev/null +++ b/dng/dng_jpeg_image.cpp @@ -0,0 +1,354 @@ +/*****************************************************************************/ +// Copyright 2011-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_jpeg_image.h" + +#include "dng_abort_sniffer.h" +#include "dng_area_task.h" +#include "dng_assertions.h" +#include "dng_host.h" +#include "dng_ifd.h" +#include "dng_image.h" +#include "dng_image_writer.h" +#include "dng_memory_stream.h" +#include "dng_safe_arithmetic.h" +#include "dng_uncopyable.h" + +#include + +/*****************************************************************************/ + +dng_jpeg_image::dng_jpeg_image () + + : fImageSize () + , fTileSize () + , fUsesStrips (false) + , fJPEGTables () + , fJPEGData () + + { + + } + +/*****************************************************************************/ + +class dng_jpeg_image_encode_task : public dng_area_task, + private dng_uncopyable + { + + private: + + dng_host &fHost; + + dng_image_writer &fWriter; + + const dng_image &fImage; + + dng_jpeg_image &fJPEGImage; + + uint32 fTileCount; + + const dng_ifd &fIFD; + + std::atomic_uint fNextTileIndex; + + public: + + dng_jpeg_image_encode_task (dng_host &host, + dng_image_writer &writer, + const dng_image &image, + dng_jpeg_image &jpegImage, + uint32 tileCount, + const dng_ifd &ifd) + + : dng_area_task ("dng_jpeg_image_encode_task") + + , fHost (host) + , fWriter (writer) + , fImage (image) + , fJPEGImage (jpegImage) + , fTileCount (tileCount) + , fIFD (ifd) + , fNextTileIndex (0) + + { + + fMinTaskArea = 16 * 16; + fUnitCell = dng_point (16, 16); + fMaxTileSize = dng_point (16, 16); + + } + + void Process (uint32 /* threadIndex */, + const dng_rect & /* tile */, + dng_abort_sniffer *sniffer) + { + + AutoPtr compressedBuffer; + AutoPtr uncompressedBuffer; + AutoPtr subTileBlockBuffer; + AutoPtr tempBuffer; + + uint32 uncompressedSize = SafeUint32Mult (fIFD.fTileLength, + fIFD.fTileWidth, + fIFD.fSamplesPerPixel); + + uncompressedBuffer.Reset (fHost.Allocate (uncompressedSize)); + + uint32 tilesAcross = fIFD.TilesAcross (); + + while (true) + { + + // Note: fNextTileIndex is atomic + + uint32 tileIndex = fNextTileIndex++; + + if (tileIndex >= fTileCount) + { + return; + } + + dng_abort_sniffer::SniffForAbort (sniffer); + + uint32 rowIndex = tileIndex / tilesAcross; + uint32 colIndex = tileIndex % tilesAcross; + + dng_rect tileArea = fIFD.TileArea (rowIndex, colIndex); + + dng_memory_stream stream (fHost.Allocator ()); + + fWriter.WriteTile (fHost, + fIFD, + stream, + fImage, + tileArea, + 1, + compressedBuffer, + uncompressedBuffer, + subTileBlockBuffer, + tempBuffer, + true); + + fJPEGImage.fJPEGData [tileIndex].Reset (stream.AsMemoryBlock (fHost.Allocator ())); + + } + + } + + }; + +/*****************************************************************************/ + +void dng_jpeg_image::Encode (dng_host &host, + const dng_negative &negative, + dng_image_writer &writer, + const dng_image &image) + { + + #if qDNGValidate + dng_timer timer ("Encode JPEG Proxy time"); + #endif + + DNG_ASSERT (image.PixelType () == ttByte, "Cannot JPEG encode non-byte image"); + + fImageSize = image.Bounds ().Size (); + + dng_ifd ifd; + + ifd.fImageWidth = fImageSize.h; + ifd.fImageLength = fImageSize.v; + + ifd.fSamplesPerPixel = image.Planes (); + + ifd.fBitsPerSample [0] = 8; + ifd.fBitsPerSample [1] = 8; + ifd.fBitsPerSample [2] = 8; + ifd.fBitsPerSample [3] = 8; + + ifd.fPhotometricInterpretation = piLinearRaw; + + ifd.fCompression = ccLossyJPEG; + + ifd.FindTileSize (512 * 512 * ifd.fSamplesPerPixel); + + fTileSize.h = ifd.fTileWidth; + fTileSize.v = ifd.fTileLength; + + // Need a higher quality for raw proxies than non-raw proxies, since users + // often perform much greater color changes. Also, if we are targeting a + // "large" size proxy (larger than 5 MP), or this is a full size proxy, + // then use a higher quality. + + bool useHigherQuality = (uint64) ifd.fImageWidth * + (uint64) ifd.fImageLength > 5000000 || + image.Bounds ().Size () == negative.OriginalDefaultFinalSize (); + + if (negative.ColorimetricReference () == crSceneReferred) + { + ifd.fCompressionQuality = useHigherQuality ? 11 : 10; + } + else + { + ifd.fCompressionQuality = useHigherQuality ? 10 : 8; + } + + uint32 tilesAcross = ifd.TilesAcross (); + uint32 tilesDown = ifd.TilesDown (); + + uint32 tileCount = tilesAcross * tilesDown; + + fJPEGData.Reset (new dng_jpeg_image_tile_ptr [tileCount]); + + uint32 threadCount = Min_uint32 (tileCount, + host.PerformAreaTaskThreads ()); + + dng_jpeg_image_encode_task task (host, + writer, + image, + *this, + tileCount, + ifd); + + host.PerformAreaTask (task, + dng_rect (0, 0, 16, 16 * threadCount)); + + } + +/*****************************************************************************/ + +class dng_jpeg_image_find_digest_task : public dng_area_task, + private dng_uncopyable + { + + private: + + const dng_jpeg_image &fJPEGImage; + + uint32 fTileCount; + + dng_fingerprint *fDigests; + + std::atomic_uint fNextTileIndex; + + public: + + dng_jpeg_image_find_digest_task (const dng_jpeg_image &jpegImage, + uint32 tileCount, + dng_fingerprint *digests) + + : dng_area_task ("dng_jpeg_image_find_digest_task") + + , fJPEGImage (jpegImage) + , fTileCount (tileCount) + , fDigests (digests) + , fNextTileIndex (0) + + { + + fMinTaskArea = 16 * 16; + fUnitCell = dng_point (16, 16); + fMaxTileSize = dng_point (16, 16); + + } + + void Process (uint32 /* threadIndex */, + const dng_rect & /* tile */, + dng_abort_sniffer *sniffer) + { + + while (true) + { + + // Note: fNextTileIndex is atomic + + uint32 tileIndex = fNextTileIndex++; + + if (tileIndex >= fTileCount) + { + return; + } + + dng_abort_sniffer::SniffForAbort (sniffer); + + dng_md5_printer printer; + + printer.Process (fJPEGImage.fJPEGData [tileIndex]->Buffer (), + fJPEGImage.fJPEGData [tileIndex]->LogicalSize ()); + + fDigests [tileIndex] = printer.Result (); + + } + + } + + }; + +/*****************************************************************************/ + +dng_fingerprint dng_jpeg_image::FindDigest (dng_host &host) const + { + + uint32 tileCount = TileCount (); + + uint32 arrayCount = tileCount + (fJPEGTables.Get () ? 1 : 0); + + AutoArray digests (new dng_fingerprint [arrayCount]); + + // Compute digest of each compressed tile. + + { + + uint32 threadCount = Min_uint32 (tileCount, + host.PerformAreaTaskThreads ()); + + dng_jpeg_image_find_digest_task task (*this, + tileCount, + digests.Get ()); + + host.PerformAreaTask (task, + dng_rect (0, 0, 16, 16 * threadCount)); + + } + + // Compute digest of JPEG tables, if any. + + if (fJPEGTables.Get ()) + { + + dng_md5_printer printer; + + printer.Process (fJPEGTables->Buffer (), + fJPEGTables->LogicalSize ()); + + digests [tileCount] = printer.Result (); + + } + + // Combine digests into a single digest. + + { + + dng_md5_printer printer; + + for (uint32 k = 0; k < arrayCount; k++) + { + + printer.Process (digests [k].data, + dng_fingerprint::kDNGFingerprintSize); + + } + + return printer.Result (); + + } + + } + +/*****************************************************************************/ + diff --git a/dng/dng_jpeg_image.h b/dng/dng_jpeg_image.h new file mode 100644 index 0000000..c9c2ca3 --- /dev/null +++ b/dng/dng_jpeg_image.h @@ -0,0 +1,85 @@ +/*****************************************************************************/ +// Copyright 2011-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef __dng_jpeg_image__ +#define __dng_jpeg_image__ + +/*****************************************************************************/ + +#include "dng_auto_ptr.h" +#include "dng_memory.h" +#include "dng_point.h" + +/*****************************************************************************/ + +typedef AutoPtr dng_jpeg_image_tile_ptr; + +/*****************************************************************************/ + +class dng_jpeg_image + { + + public: + + dng_point fImageSize; + + dng_point fTileSize; + + bool fUsesStrips; + + AutoPtr fJPEGTables; + + AutoArray fJPEGData; + + public: + + dng_jpeg_image (); + + uint32 TilesAcross () const + { + if (fTileSize.h) + { + return (fImageSize.h + fTileSize.h - 1) / fTileSize.h; + } + else + { + return 0; + } + } + + uint32 TilesDown () const + { + if (fTileSize.v) + { + return (fImageSize.v + fTileSize.v - 1) / fTileSize.v; + } + else + { + return 0; + } + } + + uint32 TileCount () const + { + return TilesAcross () * TilesDown (); + } + + void Encode (dng_host &host, + const dng_negative &negative, + dng_image_writer &writer, + const dng_image &image); + + dng_fingerprint FindDigest (dng_host &host) const; + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_lens_correction.cpp b/dng/dng_lens_correction.cpp new file mode 100644 index 0000000..79ca79e --- /dev/null +++ b/dng/dng_lens_correction.cpp @@ -0,0 +1,2509 @@ +/*****************************************************************************/ +// Copyright 2008-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include +#include + +#include "dng_1d_table.h" +#include "dng_assertions.h" +#include "dng_bottlenecks.h" +#include "dng_exceptions.h" +#include "dng_filter_task.h" +#include "dng_globals.h" +#include "dng_host.h" +#include "dng_image.h" +#include "dng_lens_correction.h" +#include "dng_negative.h" +#include "dng_safe_arithmetic.h" +#include "dng_sdk_limits.h" +#include "dng_tag_values.h" + +/*****************************************************************************/ + +dng_warp_params::dng_warp_params () + + : fPlanes (1) + , fCenter (0.5, 0.5) + + { + + } + +/*****************************************************************************/ + +dng_warp_params::dng_warp_params (uint32 planes, + const dng_point_real64 ¢er) + + : fPlanes (planes) + , fCenter (center) + + { + + DNG_ASSERT (planes >= 1, "Too few planes." ); + DNG_ASSERT (planes <= kMaxColorPlanes, "Too many planes."); + + DNG_ASSERT (fCenter.h >= 0.0 && fCenter.h <= 1.0, + "Center (horizontal) out of range."); + + DNG_ASSERT (fCenter.v >= 0.0 && fCenter.v <= 1.0, + "Center (vertical) out of range."); + + } + +/*****************************************************************************/ + +dng_warp_params::~dng_warp_params () + { + + } + +/*****************************************************************************/ + +bool dng_warp_params::IsNOPAll () const + { + + return IsRadNOPAll () && + IsTanNOPAll (); + + } + +/*****************************************************************************/ + +bool dng_warp_params::IsNOP (uint32 plane) const + { + + return IsRadNOP (plane) && + IsTanNOP (plane); + + } + +/*****************************************************************************/ + +bool dng_warp_params::IsRadNOPAll () const + { + + for (uint32 plane = 0; plane < fPlanes; plane++) + { + + if (!IsRadNOP (plane)) + { + return false; + } + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_warp_params::IsRadNOP (uint32 /* plane */) const + { + + return false; + + } + +/*****************************************************************************/ + +bool dng_warp_params::IsTanNOPAll () const + { + + for (uint32 plane = 0; plane < fPlanes; plane++) + { + + if (!IsTanNOP (plane)) + { + return false; + } + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_warp_params::IsTanNOP (uint32 /* plane */) const + { + + return false; + + } + +/*****************************************************************************/ + +bool dng_warp_params::IsValid () const + { + + if (fPlanes < 1 || fPlanes > kMaxColorPlanes) + { + + return false; + + } + + if (fCenter.h < 0.0 || + fCenter.h > 1.0 || + fCenter.v < 0.0 || + fCenter.v > 1.0) + { + + return false; + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_warp_params::IsValidForNegative (const dng_negative &negative) const + { + + if (!IsValid ()) + { + return false; + } + + if ((fPlanes != 1) && + (fPlanes != negative.ColorChannels ())) + { + return false; + } + + return true; + + } + +/*****************************************************************************/ + +real64 dng_warp_params::EvaluateInverse (uint32 plane, + real64 y) const + { + + const uint32 kMaxIterations = 30; + const real64 kNearZero = 1.0e-10; + + real64 x0 = 0.0; + real64 y0 = Evaluate (plane, + x0); + + real64 x1 = 1.0; + real64 y1 = Evaluate (plane, + x1); + + for (uint32 iteration = 0; iteration < kMaxIterations; iteration++) + { + + if (Abs_real64 (y1 - y0) < kNearZero) + { + break; + } + + const real64 x2 = Pin_real64 (0.0, + x1 + (y - y1) * (x1 - x0) / (y1 - y0), + 1.0); + + const real64 y2 = Evaluate (plane, + x2); + + x0 = x1; + y0 = y1; + + x1 = x2; + y1 = y2; + + } + + return x1; + + } + +/*****************************************************************************/ + +dng_point_real64 dng_warp_params::EvaluateTangential2 (uint32 plane, + const dng_point_real64 &diff) const + { + + const real64 dvdv = diff.v * diff.v; + const real64 dhdh = diff.h * diff.h; + + const real64 rr = dvdv + dhdh; + + dng_point_real64 diffSqr (dvdv, + dhdh); + + return EvaluateTangential (plane, + rr, + diff, + diffSqr); + + } + +/*****************************************************************************/ + +dng_point_real64 dng_warp_params::EvaluateTangential3 (uint32 plane, + real64 r2, + const dng_point_real64 &diff) const + { + + dng_point_real64 diffSqr (diff.v * diff.v, + diff.h * diff.h); + + return EvaluateTangential (plane, + r2, + diff, + diffSqr); + + } + +/*****************************************************************************/ + +void dng_warp_params::Dump () const + { + + #if qDNGValidate + + printf ("Planes: %u\n", (unsigned) fPlanes); + + printf (" Optical center:\n" + " h = %.6lf\n" + " v = %.6lf\n", + fCenter.h, + fCenter.v); + + #endif + + } + +/*****************************************************************************/ + +dng_warp_params_rectilinear::dng_warp_params_rectilinear () + + : dng_warp_params () + + { + + for (uint32 plane = 0; plane < kMaxColorPlanes; plane++) + { + + fRadParams [plane] = dng_vector (4); + fTanParams [plane] = dng_vector (2); + + fRadParams [plane][0] = 1.0; + + } + + } + +/*****************************************************************************/ + +dng_warp_params_rectilinear::dng_warp_params_rectilinear (uint32 planes, + const dng_vector radParams [], + const dng_vector tanParams [], + const dng_point_real64 ¢er) + + : dng_warp_params (planes, + center) + + { + + for (uint32 i = 0; i < fPlanes; i++) + { + fRadParams [i] = radParams [i]; + fTanParams [i] = tanParams [i]; + } + + } + +/*****************************************************************************/ + +dng_warp_params_rectilinear::~dng_warp_params_rectilinear () + { + + } + +/*****************************************************************************/ + +bool dng_warp_params_rectilinear::IsRadNOP (uint32 plane) const + { + + DNG_ASSERT (plane < fPlanes, "plane out of range."); + + const dng_vector &r = fRadParams [plane]; + + return (r [0] == 1.0 && + r [1] == 0.0 && + r [2] == 0.0 && + r [3] == 0.0); + + } + +/*****************************************************************************/ + +bool dng_warp_params_rectilinear::IsTanNOP (uint32 plane) const + { + + DNG_ASSERT (plane < fPlanes, "plane out of range."); + + const dng_vector &t = fTanParams [plane]; + + return (t [0] == 0.0 && + t [1] == 0.0); + + } + +/*****************************************************************************/ + +bool dng_warp_params_rectilinear::IsValid () const + { + + for (uint32 plane = 0; plane < fPlanes; plane++) + { + + if (fRadParams [plane].Count () != 4) + { + return false; + } + + if (fTanParams [plane].Count () < 2) + { + return false; + } + + } + + return dng_warp_params::IsValid (); + + } + +/*****************************************************************************/ + +void dng_warp_params_rectilinear::PropagateToAllPlanes (uint32 totalPlanes) + { + + for (uint32 plane = fPlanes; plane < totalPlanes; plane++) + { + + fRadParams [plane] = fRadParams [0]; + fTanParams [plane] = fTanParams [0]; + + } + + fPlanes = totalPlanes; + + } + +/*****************************************************************************/ + +real64 dng_warp_params_rectilinear::Evaluate (uint32 plane, + real64 x) const + { + + const dng_vector &K = fRadParams [plane]; // Coefficients. + + const real64 x2 = x * x; + + return x * (K [0] + x2 * (K [1] + x2 * (K [2] + x2 * K [3]))); + + } + +/*****************************************************************************/ + +real64 dng_warp_params_rectilinear::EvaluateRatio (uint32 plane, + real64 r2) const + { + + const dng_vector &K = fRadParams [plane]; // Coefficients. + + return K [0] + r2 * (K [1] + r2 * (K [2] + r2 * K [3])); + + } + +/*****************************************************************************/ + +dng_point_real64 dng_warp_params_rectilinear::EvaluateTangential (uint32 plane, + real64 r2, + const dng_point_real64 &diff, + const dng_point_real64 &diff2) const + { + + const real64 kt0 = fTanParams [plane][0]; + const real64 kt1 = fTanParams [plane][1]; + + const real64 dh = diff.h; + const real64 dv = diff.v; + + const real64 dhdh = diff2.h; + const real64 dvdv = diff2.v; + + return dng_point_real64 (kt0 * (r2 + 2.0 * dvdv) + (2.0 * kt1 * dh * dv), // v + kt1 * (r2 + 2.0 * dhdh) + (2.0 * kt0 * dh * dv)); // h + + } + +/*****************************************************************************/ + +real64 dng_warp_params_rectilinear::MaxSrcRadiusGap (real64 maxDstGap) const + { + + real64 maxSrcGap = 0.0; + + for (uint32 plane = 0; plane < fPlanes; plane++) + { + + const dng_vector &coefs = fRadParams [plane]; + + const real64 k3 = coefs [1]; + const real64 k5 = coefs [2]; + const real64 k7 = coefs [3]; + + // + // Let f (r) be the radius warp function. Consider the function + // + // gap (r) = f (r + maxDstGap) - f (r) + // + // We wish to maximize gap (r) over the domain [0, 1 - maxDstGap]. This will + // give us a reasonable upper bound on the src tile size, independent of + // dstBounds. + // + // As usual, we maximize gap (r) by examining its critical points, i.e., by + // considering the roots of its derivative which lie in the domain [0, 1 - + // maxDstGap]. gap' (r) is a 5th-order polynomial. One of its roots is + // -maxDstGap / 2, which is negative and hence lies outside the domain of + // interest. This leaves 4 other possible roots. We solve for these + // analytically below. + // + + real64 roots [4]; + uint32 numRoots = 0; + + if (k7 == 0.0) + { + + if (k5 == 0.0) + { + + // No roots in [0,1]. + + } + + else + { + + // k7 is zero, but k5 is non-zero. At most two real roots. + + const real64 discrim = 25.0 * (-6.0 * k3 * k5 - 5.0 * k5 * maxDstGap * maxDstGap); + + if (discrim >= 0.0) + { + + // Two real roots. + + const real64 scale = 0.1 * k5; + const real64 offset = -5.0 * maxDstGap * k5; + const real64 sDiscrim = sqrt (discrim); + + roots [numRoots++] = scale * (offset + sDiscrim); + roots [numRoots++] = scale * (offset - sDiscrim); + + } + + } + + } + + else + { + + // k7 is non-zero. Up to 4 real roots. + + const real64 d = maxDstGap; + const real64 d2 = d * d; + const real64 d4 = d2 * d2; + + const real64 discrim = 25.0 * k5 * k5 + - 63.0 * k3 * k7 + + 35.0 * d2 * k5 * k7 + + 49.0 * d4 * k7 * k7; + + if (discrim >= 0.0) + { + + const real64 sDiscrim = 4.0 * k7 * sqrt (discrim); + + const real64 offset = -20.0 * k5 * k7 - 35.0 * d2 * k7 * k7; + + const real64 discrim1 = offset - sDiscrim; + const real64 discrim2 = offset + sDiscrim; + + const real64 scale = sqrt (21.0) / (42.0 * k7); + + if (discrim1 >= 0.0) + { + + const real64 offset1 = -d * 0.5; + const real64 sDiscrim1 = scale * sqrt (discrim1); + + roots [numRoots++] = offset1 + sDiscrim1; + roots [numRoots++] = offset1 - sDiscrim1; + + } + + if (discrim2 >= 0.0) + { + + const real64 offset2 = -d * 0.5; + const real64 sDiscrim2 = scale * sqrt (discrim2); + + roots [numRoots++] = offset2 + sDiscrim2; + roots [numRoots++] = offset2 - sDiscrim2; + + } + + } + + } + + real64 planeMaxSrcGap = 0.0; + + // Examine the endpoints. + + { + + // Check left endpoint: f (maxDstGap) - f (0). Remember that f (0) == 0. + + const real64 gap1 = Evaluate (plane, maxDstGap); + + planeMaxSrcGap = Max_real64 (planeMaxSrcGap, gap1); + + // Check right endpoint: f (1) - f (1 - maxDstGap). + + const real64 gap2 = Evaluate (plane, 1.0) + - Evaluate (plane, 1.0 - maxDstGap); + + planeMaxSrcGap = Max_real64 (planeMaxSrcGap, gap2); + + } + + // Examine the roots we found, if any. + + for (uint32 i = 0; i < numRoots; i++) + { + + const real64 r = roots [i]; + + if (r > 0.0 && r < 1.0 - maxDstGap) + { + + const real64 gap = Evaluate (plane, r + maxDstGap) + - Evaluate (plane, r); + + planeMaxSrcGap = Max_real64 (planeMaxSrcGap, gap); + + } + + } + + maxSrcGap = Max_real64 (maxSrcGap, + planeMaxSrcGap); + + } + + return maxSrcGap; + + } + +/*****************************************************************************/ + +dng_point_real64 dng_warp_params_rectilinear::MaxSrcTanGap (dng_point_real64 minDst, + dng_point_real64 maxDst) const + { + + const real64 v [] = { minDst.v, maxDst.v, 0.0 }; + const real64 h [] = { minDst.h, maxDst.h, 0.0 }; + + dng_point_real64 maxGap; + + for (uint32 plane = 0; plane < fPlanes; plane++) + { + + real64 hMin = +FLT_MAX; + real64 hMax = -FLT_MAX; + + real64 vMin = +FLT_MAX; + real64 vMax = -FLT_MAX; + + for (uint32 i = 0; i < 3; i++) + { + + for (uint32 j = 0; j < 3; j++) + { + + dng_point_real64 dstDiff (v [i], + h [j]); + + dng_point_real64 srcDiff = EvaluateTangential2 (plane, + dstDiff); + + hMin = Min_real64 (hMin, srcDiff.h); + hMax = Max_real64 (hMax, srcDiff.h); + + vMin = Min_real64 (vMin, srcDiff.v); + vMax = Max_real64 (vMax, srcDiff.v); + + } + + } + + const real64 hGap = hMax - hMin; + const real64 vGap = vMax - vMin; + + maxGap.h = Max_real64 (maxGap.h, hGap); + maxGap.v = Max_real64 (maxGap.v, vGap); + + } + + return maxGap; + + } + +/*****************************************************************************/ + +real64 dng_warp_params_rectilinear::SafeMinRatio () const + { + + return 0.5; + + } + +/*****************************************************************************/ + +real64 dng_warp_params_rectilinear::SafeMaxRatio () const + { + + return 2.0; + + } + +/*****************************************************************************/ + +void dng_warp_params_rectilinear::Dump () const + { + + #if qDNGValidate + + dng_warp_params::Dump (); + + for (uint32 plane = 0; plane < fPlanes; plane++) + { + + printf (" Plane %u:\n", (unsigned) plane); + + printf (" Radial params: %.6lf, %.6lf, %.6lf, %.6lf\n", + fRadParams [plane][0], + fRadParams [plane][1], + fRadParams [plane][2], + fRadParams [plane][3]); + + printf (" Tangential params: %.6lf, %.6lf\n", + fTanParams [plane][0], + fTanParams [plane][1]); + + } + + #endif + + } + +/*****************************************************************************/ + +dng_warp_params_fisheye::dng_warp_params_fisheye () + + : dng_warp_params () + + { + + for (uint32 plane = 0; plane < kMaxColorPlanes; plane++) + { + + fRadParams [plane] = dng_vector (4); + + } + + } + +/*****************************************************************************/ + +dng_warp_params_fisheye::dng_warp_params_fisheye (uint32 planes, + const dng_vector radParams [], + const dng_point_real64 ¢er) + + : dng_warp_params (planes, center) + + { + + for (uint32 i = 0; i < fPlanes; i++) + { + + fRadParams [i] = radParams [i]; + + } + + } + +/*****************************************************************************/ + +dng_warp_params_fisheye::~dng_warp_params_fisheye () + { + + } + +/*****************************************************************************/ + +bool dng_warp_params_fisheye::IsRadNOP (uint32 /* plane */) const + { + + return false; + + } + +/*****************************************************************************/ + +bool dng_warp_params_fisheye::IsTanNOP (uint32 /* plane */) const + { + + return true; + + } + +/*****************************************************************************/ + +bool dng_warp_params_fisheye::IsValid () const + { + + for (uint32 plane = 0; plane < fPlanes; plane++) + { + + if (fRadParams [plane].Count () != 4) + { + return false; + } + + } + + return dng_warp_params::IsValid (); + + } + +/*****************************************************************************/ + +void dng_warp_params_fisheye::PropagateToAllPlanes (uint32 totalPlanes) + { + + for (uint32 plane = fPlanes; plane < totalPlanes; plane++) + { + + fRadParams [plane] = fRadParams [0]; + + } + + fPlanes = totalPlanes; + + } + +/*****************************************************************************/ + +real64 dng_warp_params_fisheye::Evaluate (uint32 plane, + real64 r) const + { + + const real64 t = atan (r); + + const dng_vector &K = fRadParams [plane]; + + const real64 t2 = t * t; + + return t * (K [0] + t2 * (K [1] + t2 * (K [2] + t2 * K [3]))); + + } + +/*****************************************************************************/ + +real64 dng_warp_params_fisheye::EvaluateRatio (uint32 plane, + real64 rSqr) const + { + + const real64 eps = 1.0e-12; + + if (rSqr < eps) + { + + // r is very close to zero. + + return 1.0; + + } + + const real64 r = sqrt (rSqr); + + return Evaluate (plane, r) / r; + + } + +/*****************************************************************************/ + +dng_point_real64 dng_warp_params_fisheye::EvaluateTangential (uint32 /* plane */, + real64 /* r2 */, + const dng_point_real64 & /* diff */, + const dng_point_real64 & /* diff2 */) const + { + + // This fisheye model does not support tangential warping. + + ThrowProgramError (); + + return dng_point_real64 (0.0, 0.0); + + } + +/*****************************************************************************/ + +real64 dng_warp_params_fisheye::MaxSrcRadiusGap (real64 maxDstGap) const + { + + // + // Let f (r) be the radius warp function. Consider the function + // + // gap (r) = f (r + maxDstGap) - f (r) + // + // We wish to maximize gap (r) over the domain [0, 1 - maxDstGap]. This will + // give us a reasonable upper bound on the src tile size, independent of + // dstBounds. + // + // Ideally, we'd like to maximize gap (r) by examining its critical points, + // i.e., by considering the roots of its derivative which lie in the domain [0, + // 1 - maxDstGap]. However, gap' (r) is too complex to find its roots + // analytically. + // + + real64 maxSrcGap = 0.0; + + DNG_REQUIRE (maxDstGap > 0.0, "maxDstGap must be positive."); + + const real64 kMaxValue = 1.0 - maxDstGap; + + const uint32 kSteps = 128; + + const real64 kNorm = kMaxValue / real64 (kSteps - 1); + + for (uint32 plane = 0; plane < fPlanes; plane++) + { + + for (uint32 i = 0; i < kSteps; i++) + { + + const real64 tt = i * kNorm; + + const real64 gap = Evaluate (plane, tt + maxDstGap) + - Evaluate (plane, tt); + + maxSrcGap = Max_real64 (maxSrcGap, + gap); + + } + + } + + return maxSrcGap; + + } + +/*****************************************************************************/ + +dng_point_real64 dng_warp_params_fisheye::MaxSrcTanGap (dng_point_real64 /* minDst */, + dng_point_real64 /* maxDst */) const + { + + // This fisheye model does not support tangential distortion. + + return dng_point_real64 (0.0, 0.0); + + } + +/*****************************************************************************/ + +real64 dng_warp_params_fisheye::SafeMinRatio () const + { + + return 0.2; + + } + +/*****************************************************************************/ + +real64 dng_warp_params_fisheye::SafeMaxRatio () const + { + + return 5.0; + + } + +/*****************************************************************************/ + +void dng_warp_params_fisheye::Dump () const + { + + #if qDNGValidate + + dng_warp_params::Dump (); + + for (uint32 plane = 0; plane < fPlanes; plane++) + { + + printf (" Plane %u:\n", (unsigned) plane); + + printf (" Radial params: %.6lf, %.6lf, %.6lf, %.6lf\n", + fRadParams [plane][0], + fRadParams [plane][1], + fRadParams [plane][2], + fRadParams [plane][3]); + + } + + #endif + + } + +/*****************************************************************************/ + +class dng_filter_warp: public dng_filter_task + { + + protected: + + AutoPtr fParams; + + dng_point_real64 fCenter; + + dng_resample_weights_2d fWeights; + + real64 fNormRadius; + real64 fInvNormRadius; + + bool fIsRadNOP; + bool fIsTanNOP; + + const real64 fPixelScaleV; + const real64 fPixelScaleVInv; + + public: + + dng_filter_warp (const dng_image &srcImage, + dng_image &dstImage, + const dng_negative &negative, + AutoPtr ¶ms); + + virtual void Initialize (dng_host &host); + + virtual dng_rect SrcArea (const dng_rect &dstArea); + + virtual dng_point SrcTileSize (const dng_point &dstTileSize); + + virtual void ProcessArea (uint32 threadIndex, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer); + + virtual dng_point_real64 GetSrcPixelPosition (const dng_point_real64 &dst, + uint32 plane); + + }; + +/*****************************************************************************/ + +dng_filter_warp::dng_filter_warp (const dng_image &srcImage, + dng_image &dstImage, + const dng_negative &negative, + AutoPtr ¶ms) + + : dng_filter_task ("dng_filter_warp", + srcImage, + dstImage) + + , fParams (params.Release ()) + + , fCenter () + + , fWeights () + + , fNormRadius (1.0) + , fInvNormRadius (1.0) + + , fIsRadNOP (false) + , fIsTanNOP (false) + + , fPixelScaleV (1.0 / negative.PixelAspectRatio ()) + , fPixelScaleVInv (1.0 / fPixelScaleV) + + { + + // Force float processing. + + fSrcPixelType = ttFloat; + fDstPixelType = ttFloat; + + fIsRadNOP = fParams->IsRadNOPAll (); + fIsTanNOP = fParams->IsTanNOPAll (); + + const uint32 negPlanes = negative.ColorChannels (); + + DNG_ASSERT (negPlanes >= 1, "Too few planes." ); + DNG_ASSERT (negPlanes <= kMaxColorPlanes, "Too many planes."); + + (void) negPlanes; + + // At least one set of params must do something interesting. + + if (fIsRadNOP && fIsTanNOP) + { + ThrowProgramError (); + } + + // Make sure the warp params are valid for this negative. + + if (!fParams->IsValidForNegative (negative)) + { + ThrowBadFormat (); + } + + // Compute center. + + const dng_rect bounds = srcImage.Bounds (); + + fCenter.h = Lerp_real64 ((real64) bounds.l, + (real64) bounds.r, + fParams->fCenter.h); + + fCenter.v = Lerp_real64 ((real64) bounds.t, + (real64) bounds.b, + fParams->fCenter.v); + + // Compute max pixel radius and derive various normalized radius values. Note + // that when computing the max pixel radius, we must take into account the pixel + // aspect ratio. + + { + + dng_rect squareBounds (bounds); + + squareBounds.b = squareBounds.t + + Round_int32 (fPixelScaleV * (real64) squareBounds.H ()); + + const dng_point_real64 squareCenter (Lerp_real64 ((real64) squareBounds.t, + (real64) squareBounds.b, + fParams->fCenter.v), + + Lerp_real64 ((real64) squareBounds.l, + (real64) squareBounds.r, + fParams->fCenter.h)); + + fNormRadius = MaxDistancePointToRect (squareCenter, + squareBounds); + + fInvNormRadius = 1.0 / fNormRadius; + + } + + // Propagate warp params to other planes. + + fParams->PropagateToAllPlanes (fDstPlanes); + + } + +/*****************************************************************************/ + +void dng_filter_warp::Initialize (dng_host &host) + { + + // Make resample weights. + + const dng_resample_function &kernel = dng_resample_bicubic::Get (); + + fWeights.Initialize (kernel, + host.Allocator ()); + + } + +/*****************************************************************************/ + +dng_rect dng_filter_warp::SrcArea (const dng_rect &dstArea) + { + + // Walk each pixel of the boundary of dstArea, map it to the uncorrected src + // pixel position, and return the rectangle that contains all such src pixels. + + int32 xMin = INT_MAX; + int32 xMax = INT_MIN; + int32 yMin = INT_MAX; + int32 yMax = INT_MIN; + + for (uint32 plane = 0; plane < fDstPlanes; plane++) + { + + // Top and bottom edges. + + for (int32 c = dstArea.l; c < dstArea.r; c++) + { + + // Top edge. + + { + + const dng_point_real64 dst (dstArea.t, c); + + const dng_point_real64 src = GetSrcPixelPosition (dst, plane); + + const int32 y = (int32) floor (src.v); + + yMin = Min_int32 (yMin, y); + + } + + // Bottom edge. + + { + + const dng_point_real64 dst (dstArea.b - 1, c); + + const dng_point_real64 src = GetSrcPixelPosition (dst, plane); + + const int32 y = (int32) ceil (src.v); + + yMax = Max_int32 (yMax, y); + + } + + } + + // Left and right edges. + + for (int32 r = dstArea.t; r < dstArea.b; r++) + { + + // Left edge. + + { + + const dng_point_real64 dst (r, dstArea.l); + + const dng_point_real64 src = GetSrcPixelPosition (dst, plane); + + const int32 x = (int32) floor (src.h); + + xMin = Min_int32 (xMin, x); + + } + + // Right edge. + + { + + const dng_point_real64 dst (r, dstArea.r - 1); + + const dng_point_real64 src = GetSrcPixelPosition (dst, plane); + + const int32 x = (int32) ceil (src.h); + + xMax = Max_int32 (xMax, x); + + } + + } + + } + + // Pad each side by filter radius. + + const int32 pad = (int32) fWeights.Radius (); + + xMin -= pad; + yMin -= pad; + xMax += pad; + yMax += pad; + + xMax += 1; + yMax += 1; + + dng_rect srcArea (yMin, + xMin, + yMax, + xMax); + + // Limit to src image area. + + srcArea = srcArea & fSrcImage.Bounds (); + + return srcArea; + + } + +/*****************************************************************************/ + +dng_point dng_filter_warp::SrcTileSize (const dng_point &dstTileSize) + { + + // Obtain an upper bound on the source tile size. We'll do this by considering + // upper bounds on the radial and tangential warp components separately, then add + // them together. This is not a tight bound but is good enough because the + // tangential terms are usually quite small. + + // Get upper bound on src tile size from radial warp. + + DNG_REQUIRE (dstTileSize.v > 0, "Invalid tile height."); + DNG_REQUIRE (dstTileSize.h > 0, "Invalid tile width."); + + const real64 maxDstGap = fInvNormRadius * hypot ((real64) dstTileSize.h, + (real64) dstTileSize.v); + + dng_point srcTileSize; + + if (maxDstGap >= 1.0) + { + + // The proposed tile size is unusually large, i.e., its hypotenuse is larger + // than the maximum radius. Bite the bullet and just return a tile size big + // enough to process the whole image. + + srcTileSize = SrcArea (fDstImage.Bounds ()).Size (); + + } + + else + { + + // maxDstGap < 1.0. + + const real64 maxSrcGap = fParams->MaxSrcRadiusGap (maxDstGap); + + const int32 dim = (int32) ceil (maxSrcGap * fNormRadius); + + srcTileSize = dng_point (dim, dim); + + } + + srcTileSize.h += (int32) (fWeights.Width ()); + srcTileSize.v += (int32) (fWeights.Width ()); + + // Get upper bound on src tile size from tangential warp. + + const dng_rect_real64 bounds (fSrcImage.Bounds ()); + + const dng_point_real64 minDst ((bounds.t - fCenter.v) * fInvNormRadius, + (bounds.l - fCenter.h) * fInvNormRadius); + + const dng_point_real64 maxDst ((bounds.b - 1.0 - fCenter.v) * fInvNormRadius, + (bounds.r - 1.0 - fCenter.h) * fInvNormRadius); + + const dng_point_real64 srcTanGap = fParams->MaxSrcTanGap (minDst, + maxDst); + + // Add the two bounds together. + + srcTileSize.v += (int32) ceil (srcTanGap.v * fNormRadius); + srcTileSize.h += (int32) ceil (srcTanGap.h * fNormRadius); + + DNG_REQUIRE (srcTileSize.v > 0, "Bad srcTileSize.v in dng_filter_warp::SrcTileSize"); + DNG_REQUIRE (srcTileSize.h > 0, "Bad srcTileSize.h in dng_filter_warp::SrcTileSize"); + + return srcTileSize; + + } + +/*****************************************************************************/ + +void dng_filter_warp::ProcessArea (uint32 /* threadIndex */, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer) + { + + // Prepare resample constants. + + const int32 wCount = fWeights.Width (); + + const dng_point srcOffset (fWeights.Offset (), + fWeights.Offset ()); + + const real64 numSubsamples = (real64) kResampleSubsampleCount2D; + + // Prepare area and step constants. + + const dng_rect srcArea = srcBuffer.fArea; + const dng_rect dstArea = dstBuffer.fArea; + + const int32 srcRowStep = (int32) srcBuffer.RowStep (); + + const int32 hMin = srcArea.l; + const int32 hMax = SafeInt32Sub (SafeInt32Sub (srcArea.r, wCount), 1); + + const int32 vMin = srcArea.t; + const int32 vMax = SafeInt32Sub (SafeInt32Sub (srcArea.b, wCount), 1); + + if (hMax < hMin || + vMax < vMin) + { + + ThrowBadFormat ("Empty source area in dng_filter_warp."); + + } + + // Warp each plane. + + const dng_rect_real64 srcImageArea (fSrcImage.Bounds ()); + + for (uint32 plane = 0; plane < dstBuffer.fPlanes; plane++) + { + + real32 *dPtr = dstBuffer.DirtyPixel_real32 (dstArea.t, + dstArea.l, + plane); + + for (int32 dstRow = dstArea.t; dstRow < dstArea.b; dstRow++) + { + + uint32 dstIndex = 0; + + for (int32 dstCol = dstArea.l; dstCol < dstArea.r; dstCol++, dstIndex++) + { + + // Get destination (corrected) pixel position. + + const dng_point_real64 dPos ((real64) dstRow, + (real64) dstCol); + + // Warp to source (uncorrected) pixel position. + + dng_point_real64 sPos = GetSrcPixelPosition (dPos, + plane); + + // Limit to source image area. + + sPos.h = Max_real64 (sPos.h, srcImageArea.l); + sPos.h = Min_real64 (sPos.h, srcImageArea.r - 1.0); + sPos.v = Max_real64 (sPos.v, srcImageArea.t); + sPos.v = Min_real64 (sPos.v, srcImageArea.b - 1.0); + + // Decompose into integer and fractional parts. + + dng_point sInt ((int32) floor (sPos.v), + (int32) floor (sPos.h)); + + dng_point sFct ((int32) ((sPos.v - (real64) sInt.v) * numSubsamples), + (int32) ((sPos.h - (real64) sInt.h) * numSubsamples)); + + // Add resample offset. + + sInt = sInt + srcOffset; + + // Clip. + + if (sInt.h < hMin) + { + sInt.h = hMin; + sFct.h = 0; + } + + else if (sInt.h > hMax) + { + sInt.h = hMax; + sFct.h = 0; + } + + if (sInt.v < vMin) + { + sInt.v = vMin; + sFct.v = 0; + } + + else if (sInt.v > vMax) + { + sInt.v = vMax; + sFct.v = 0; + } + + // Perform 2D resample. + + const real32 *w = fWeights.Weights32 (sFct); + + const real32 *s = srcBuffer.ConstPixel_real32 (sInt.v, + sInt.h, + plane); + + real32 total = 0.0f; + + for (int32 i = 0; i < wCount; i++) + { + + for (int32 j = 0; j < wCount; j++) + { + + total += w [j] * s [j]; + + } + + w += wCount; + s += srcRowStep; + + } + + // Store final pixel value. + + dPtr [dstIndex] = Pin_real32 (total); + + } + + // Advance to next row. + + dPtr += dstBuffer.RowStep (); + + } + + } + + } + +/*****************************************************************************/ + +dng_point_real64 dng_filter_warp::GetSrcPixelPosition (const dng_point_real64 &dst, + uint32 plane) + { + + const dng_point_real64 diff = dst - fCenter; + + const dng_point_real64 diffNorm (diff.v * fInvNormRadius, + diff.h * fInvNormRadius); + + const dng_point_real64 diffNormScaled (diffNorm.v * fPixelScaleV, + diffNorm.h); + + const dng_point_real64 diffNormSqr (diffNormScaled.v * diffNormScaled.v, + diffNormScaled.h * diffNormScaled.h); + + const real64 rr = Min_real64 (diffNormSqr.v + diffNormSqr.h, + 1.0); + + dng_point_real64 dSrc; + + if (fIsTanNOP) + { + + // Radial only. + + const real64 ratio = fParams->EvaluateRatio (plane, + rr); + + dSrc.h = diff.h * ratio; + dSrc.v = diff.v * ratio; + + } + + else if (fIsRadNOP) + { + + // Tangential only. + + const dng_point_real64 tan = fParams->EvaluateTangential (plane, + rr, + diffNormScaled, + diffNormSqr); + + dSrc.h = diff.h + (fNormRadius * tan.h); + dSrc.v = diff.v + (fNormRadius * tan.v * fPixelScaleVInv); + + } + + else + { + + // Radial and tangential. + + const real64 ratio = fParams->EvaluateRatio (plane, + rr); + + const dng_point_real64 tan = fParams->EvaluateTangential (plane, + rr, + diffNormScaled, + diffNormSqr); + + dSrc.h = fNormRadius * (diffNorm.h * ratio + tan.h); + dSrc.v = fNormRadius * (diffNorm.v * ratio + tan.v * fPixelScaleVInv); + + } + + return fCenter + dSrc; + + } + +/*****************************************************************************/ + +dng_opcode_WarpRectilinear::dng_opcode_WarpRectilinear (const dng_warp_params_rectilinear ¶ms, + uint32 flags) + + : dng_opcode (dngOpcode_WarpRectilinear, + dngVersion_1_3_0_0, + flags) + + , fWarpParams (params) + + { + + if (!params.IsValid ()) + { + ThrowBadFormat (); + } + + } + +/*****************************************************************************/ + +dng_opcode_WarpRectilinear::dng_opcode_WarpRectilinear (dng_stream &stream) + + : dng_opcode (dngOpcode_WarpRectilinear, + stream, + "WarpRectilinear") + + , fWarpParams () + + { + + // Grab the size in bytes. + + const uint32 bytes = stream.Get_uint32 (); + + // Grab the number of planes to warp. + + fWarpParams.fPlanes = stream.Get_uint32 (); + + // Verify number of planes. + + if (fWarpParams.fPlanes == 0 || + fWarpParams.fPlanes > kMaxColorPlanes) + { + ThrowBadFormat (); + } + + // Verify the size. + + if (bytes != ParamBytes (fWarpParams.fPlanes)) + { + ThrowBadFormat (); + } + + // Read warp parameters for each plane. + + for (uint32 plane = 0; plane < fWarpParams.fPlanes; plane++) + { + + fWarpParams.fRadParams [plane][0] = stream.Get_real64 (); + fWarpParams.fRadParams [plane][1] = stream.Get_real64 (); + fWarpParams.fRadParams [plane][2] = stream.Get_real64 (); + fWarpParams.fRadParams [plane][3] = stream.Get_real64 (); + + fWarpParams.fTanParams [plane][0] = stream.Get_real64 (); + fWarpParams.fTanParams [plane][1] = stream.Get_real64 (); + + } + + // Read the image center. + + fWarpParams.fCenter.h = stream.Get_real64 (); + fWarpParams.fCenter.v = stream.Get_real64 (); + + #if qDNGValidate + + if (gVerbose) + { + + fWarpParams.Dump (); + + } + + #endif + + if (!fWarpParams.IsValid ()) + { + ThrowBadFormat (); + } + + } + +/*****************************************************************************/ + +bool dng_opcode_WarpRectilinear::IsNOP () const + { + + return fWarpParams.IsNOPAll (); + + } + +/*****************************************************************************/ + +bool dng_opcode_WarpRectilinear::IsValidForNegative (const dng_negative &negative) const + { + + return fWarpParams.IsValidForNegative (negative); + + } + +/*****************************************************************************/ + +void dng_opcode_WarpRectilinear::PutData (dng_stream &stream) const + { + + const uint32 bytes = ParamBytes (fWarpParams.fPlanes); + + stream.Put_uint32 (bytes); + + stream.Put_uint32 (fWarpParams.fPlanes); + + for (uint32 plane = 0; plane < fWarpParams.fPlanes; plane++) + { + + stream.Put_real64 (fWarpParams.fRadParams [plane][0]); + stream.Put_real64 (fWarpParams.fRadParams [plane][1]); + stream.Put_real64 (fWarpParams.fRadParams [plane][2]); + stream.Put_real64 (fWarpParams.fRadParams [plane][3]); + + stream.Put_real64 (fWarpParams.fTanParams [plane][0]); + stream.Put_real64 (fWarpParams.fTanParams [plane][1]); + + } + + stream.Put_real64 (fWarpParams.fCenter.h); + stream.Put_real64 (fWarpParams.fCenter.v); + + } + +/*****************************************************************************/ + +void dng_opcode_WarpRectilinear::Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image) + { + + #if qDNGValidate + + dng_timer timer ("WarpRectilinear time"); + + #endif + + // Allocate destination image. + + AutoPtr dstImage (host.Make_dng_image (image->Bounds (), + image->Planes (), + image->PixelType ())); + + // Warp the image. + + AutoPtr params (new dng_warp_params_rectilinear (fWarpParams)); + + dng_filter_warp filter (*image, + *dstImage, + negative, + params); + + filter.Initialize (host); + + host.PerformAreaTask (filter, + image->Bounds ()); + + // Return the new image. + + image.Reset (dstImage.Release ()); + + } + +/*****************************************************************************/ + +bool dng_opcode_WarpRectilinear::HasDistort () const + { + + for (uint32 plane = 0; plane < fWarpParams.fPlanes; plane++) + { + + if (fWarpParams.IsNOP (plane)) + { + return false; + } + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_opcode_WarpRectilinear::HasLateralCA () const + { + + if (fWarpParams.fPlanes <= 1) + { + return false; + } + + for (uint32 plane = 0; plane < fWarpParams.fPlanes; plane++) + { + + if (!fWarpParams.IsNOP (plane)) + { + return true; + } + + } + + return false; + + } + +/*****************************************************************************/ + +uint32 dng_opcode_WarpRectilinear::ParamBytes (uint32 planes) + { + + return (1 * (uint32) sizeof (uint32) ) + // Number of planes. + (6 * (uint32) sizeof (real64) * planes) + // Warp coefficients. + (2 * (uint32) sizeof (real64) ); // Optical center. + + } + +/*****************************************************************************/ + +dng_opcode_WarpFisheye::dng_opcode_WarpFisheye (const dng_warp_params_fisheye ¶ms, + uint32 flags) + + : dng_opcode (dngOpcode_WarpFisheye, + dngVersion_1_3_0_0, + flags) + + , fWarpParams (params) + + { + + if (!params.IsValid ()) + { + ThrowBadFormat (); + } + + } + +/*****************************************************************************/ + +dng_opcode_WarpFisheye::dng_opcode_WarpFisheye (dng_stream &stream) + + : dng_opcode (dngOpcode_WarpFisheye, + stream, + "WarpFisheye") + + , fWarpParams () + + { + + // Grab the size in bytes. + + const uint32 bytes = stream.Get_uint32 (); + + // Grab the number of planes to warp. + + fWarpParams.fPlanes = stream.Get_uint32 (); + + // Verify number of planes. + + if (fWarpParams.fPlanes == 0 || + fWarpParams.fPlanes > kMaxColorPlanes) + { + ThrowBadFormat (); + } + + // Verify the size. + + if (bytes != ParamBytes (fWarpParams.fPlanes)) + { + ThrowBadFormat (); + } + + // Read warp parameters for each plane. + + for (uint32 plane = 0; plane < fWarpParams.fPlanes; plane++) + { + + fWarpParams.fRadParams [plane][0] = stream.Get_real64 (); + fWarpParams.fRadParams [plane][1] = stream.Get_real64 (); + fWarpParams.fRadParams [plane][2] = stream.Get_real64 (); + fWarpParams.fRadParams [plane][3] = stream.Get_real64 (); + + } + + // Read the image center. + + fWarpParams.fCenter.h = stream.Get_real64 (); + fWarpParams.fCenter.v = stream.Get_real64 (); + + #if qDNGValidate + + if (gVerbose) + { + + fWarpParams.Dump (); + + } + + #endif + + if (!fWarpParams.IsValid ()) + { + ThrowBadFormat (); + } + + } + +/*****************************************************************************/ + +bool dng_opcode_WarpFisheye::IsNOP () const + { + + return fWarpParams.IsNOPAll (); + + } + +/*****************************************************************************/ + +bool dng_opcode_WarpFisheye::IsValidForNegative (const dng_negative &negative) const + { + + return fWarpParams.IsValidForNegative (negative); + + } + +/*****************************************************************************/ + +void dng_opcode_WarpFisheye::PutData (dng_stream &stream) const + { + + const uint32 bytes = ParamBytes (fWarpParams.fPlanes); + + stream.Put_uint32 (bytes); + + // Write the number of planes. + + stream.Put_uint32 (fWarpParams.fPlanes); + + // Write the warp coefficients. + + for (uint32 plane = 0; plane < fWarpParams.fPlanes; plane++) + { + + stream.Put_real64 (fWarpParams.fRadParams [plane][0]); + stream.Put_real64 (fWarpParams.fRadParams [plane][1]); + stream.Put_real64 (fWarpParams.fRadParams [plane][2]); + stream.Put_real64 (fWarpParams.fRadParams [plane][3]); + + } + + // Write the optical center. + + stream.Put_real64 (fWarpParams.fCenter.h); + stream.Put_real64 (fWarpParams.fCenter.v); + + } + +/*****************************************************************************/ + +void dng_opcode_WarpFisheye::Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image) + { + + #if qDNGValidate + + dng_timer timer ("WarpFisheye time"); + + #endif + + // Allocate destination image. + + AutoPtr dstImage (host.Make_dng_image (image->Bounds (), + image->Planes (), + image->PixelType ())); + + // Warp the image. + + AutoPtr params (new dng_warp_params_fisheye (fWarpParams)); + + dng_filter_warp filter (*image, + *dstImage, + negative, + params); + + filter.Initialize (host); + + host.PerformAreaTask (filter, + image->Bounds ()); + + // Return the new image. + + image.Reset (dstImage.Release ()); + + } + +/*****************************************************************************/ + +uint32 dng_opcode_WarpFisheye::ParamBytes (uint32 planes) + { + + return (1 * (uint32) sizeof (uint32) ) + // Number of planes. + (4 * (uint32) sizeof (real64) * planes) + // Warp coefficients. + (2 * (uint32) sizeof (real64) ); // Optical center. + + } + +/*****************************************************************************/ + +dng_vignette_radial_params::dng_vignette_radial_params () + + : fParams (kNumTerms) + , fCenter (0.5, 0.5) + + { + + } + +/*****************************************************************************/ + +dng_vignette_radial_params::dng_vignette_radial_params (const dng_std_vector ¶ms, + const dng_point_real64 ¢er) + + : fParams (params) + , fCenter (center) + + { + + } + +/*****************************************************************************/ + +dng_vignette_radial_params::dng_vignette_radial_params + (const dng_vignette_radial_params ¶ms) + { + + fParams = params.fParams; + + fCenter = params.fCenter; + + } + +/*****************************************************************************/ + +bool dng_vignette_radial_params::IsNOP () const + { + + for (uint32 i = 0; i < fParams.size (); i++) + { + + if (fParams [i] != 0.0) + { + return false; + } + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_vignette_radial_params::IsValid () const + { + + if (fParams.size () != kNumTerms) + { + return false; + } + + if (fCenter.h < 0.0 || + fCenter.h > 1.0 || + fCenter.v < 0.0 || + fCenter.v > 1.0) + { + return false; + } + + return true; + + } + +/*****************************************************************************/ + +void dng_vignette_radial_params::Dump () const + { + + #if qDNGValidate + + printf (" Radial vignette params: "); + + for (uint32 i = 0; i < fParams.size (); i++) + { + + printf ("%s%.6lf", + (i == 0) ? "" : ", ", + fParams [i]); + + } + + printf ("\n"); + + printf (" Optical center:\n" + " h = %.6lf\n" + " v = %.6lf\n", + fCenter.h, + fCenter.v); + + #endif + + } + +/*****************************************************************************/ + +class dng_vignette_radial_function: public dng_1d_function + { + + protected: + + const dng_vignette_radial_params fParams; + + public: + + explicit dng_vignette_radial_function (const dng_vignette_radial_params ¶ms) + + : fParams (params) + + { + + } + + // x represents r^2, where r is the normalized Euclidean distance from the + // optical center to a pixel. r lies in [0,1], so r^2 (and hence x) also lies + // in [0,1]. + + virtual real64 Evaluate (real64 x) const + { + + DNG_REQUIRE (fParams.fParams.size () == dng_vignette_radial_params::kNumTerms, + "Bad number of vignette opcode coefficients."); + + real64 sum = 0.0; + + const dng_std_vector &v = fParams.fParams; + + for (dng_std_vector::const_reverse_iterator i = v.rbegin (); i != v.rend (); i++) + { + sum = x * ((*i) + sum); + } + + sum += 1.0; + + return sum; + + } + + }; + +/*****************************************************************************/ + +dng_opcode_FixVignetteRadial::dng_opcode_FixVignetteRadial (const dng_vignette_radial_params ¶ms, + uint32 flags) + + : dng_inplace_opcode (dngOpcode_FixVignetteRadial, + dngVersion_1_3_0_0, + flags) + + , fParams (params) + + , fImagePlanes (1) + + , fSrcOriginH (0) + , fSrcOriginV (0) + + , fSrcStepH (0) + , fSrcStepV (0) + + , fTableInputBits (0) + , fTableOutputBits (0) + + , fGainTable () + + { + + if (!params.IsValid ()) + { + ThrowBadFormat (); + } + + } + +/*****************************************************************************/ + +dng_opcode_FixVignetteRadial::dng_opcode_FixVignetteRadial (dng_stream &stream) + + : dng_inplace_opcode (dngOpcode_FixVignetteRadial, + stream, + "FixVignetteRadial") + + , fParams () + + , fImagePlanes (1) + + , fSrcOriginH (0) + , fSrcOriginV (0) + + , fSrcStepH (0) + , fSrcStepV (0) + + , fTableInputBits (0) + , fTableOutputBits (0) + + , fGainTable () + + { + + // Grab the size in bytes. + + const uint32 bytes = stream.Get_uint32 (); + + // Verify the size. + + if (bytes != ParamBytes ()) + { + ThrowBadFormat (); + } + + // Read vignette coefficients. + + fParams.fParams = dng_std_vector (dng_vignette_radial_params::kNumTerms); + + for (uint32 i = 0; i < dng_vignette_radial_params::kNumTerms; i++) + { + fParams.fParams [i] = stream.Get_real64 (); + } + + // Read the image center. + + fParams.fCenter.h = stream.Get_real64 (); + fParams.fCenter.v = stream.Get_real64 (); + + // Debug. + + #if qDNGValidate + + if (gVerbose) + { + + fParams.Dump (); + + } + + #endif + + if (!fParams.IsValid ()) + { + ThrowBadFormat (); + } + + } + +/*****************************************************************************/ + +bool dng_opcode_FixVignetteRadial::IsNOP () const + { + + return fParams.IsNOP (); + + } + +/*****************************************************************************/ + +bool dng_opcode_FixVignetteRadial::IsValidForNegative (const dng_negative & /* negative */) const + { + + return fParams.IsValid (); + + } + +/*****************************************************************************/ + +void dng_opcode_FixVignetteRadial::PutData (dng_stream &stream) const + { + + const uint32 bytes = ParamBytes (); + + stream.Put_uint32 (bytes); + + DNG_REQUIRE (fParams.fParams.size () == dng_vignette_radial_params::kNumTerms, + "Bad number of vignette opcode coefficients."); + + for (uint32 i = 0; i < dng_vignette_radial_params::kNumTerms; i++) + { + stream.Put_real64 (fParams.fParams [i]); + } + + stream.Put_real64 (fParams.fCenter.h); + stream.Put_real64 (fParams.fCenter.v); + + } + +/*****************************************************************************/ + +void dng_opcode_FixVignetteRadial::Prepare (dng_negative &negative, + uint32 threadCount, + const dng_point &tileSize, + const dng_rect &imageBounds, + uint32 imagePlanes, + uint32 bufferPixelType, + dng_memory_allocator &allocator) + { + + // This opcode is restricted to 32-bit images. + + if (bufferPixelType != ttFloat) + { + ThrowBadFormat (); + } + + // Sanity check number of planes. + + DNG_ASSERT (imagePlanes >= 1 && imagePlanes <= kMaxColorPlanes, + "Bad number of planes."); + + if (imagePlanes < 1 || imagePlanes > kMaxColorPlanes) + { + ThrowProgramError (); + } + + fImagePlanes = imagePlanes; + + // Get the vignette radial params. + + dng_vignette_radial_params params = MakeParamsForRender (negative); + + // Grab the destination image area. + + const dng_rect_real64 bounds (imageBounds); + + // Determine the optical center and maximum radius in pixel coordinates. + + const dng_point_real64 centerPixel (Lerp_real64 (bounds.t, + bounds.b, + params.fCenter.v), + + Lerp_real64 (bounds.l, + bounds.r, + params.fCenter.h)); + + const real64 pixelScaleV = 1.0 / negative.PixelAspectRatio (); + + const real64 maxRadius = hypot (Max_real64 (Abs_real64 (centerPixel.v - bounds.t), + Abs_real64 (centerPixel.v - bounds.b)) * pixelScaleV, + + Max_real64 (Abs_real64 (centerPixel.h - bounds.l), + Abs_real64 (centerPixel.h - bounds.r))); + + const dng_point_real64 radius (maxRadius, + maxRadius); + + // Find origin and scale. + + const real64 pixelScaleH = 1.0; + + fSrcOriginH = Real64ToFixed64 (-centerPixel.h * pixelScaleH / radius.h); + fSrcOriginV = Real64ToFixed64 (-centerPixel.v * pixelScaleV / radius.v); + + fSrcStepH = Real64ToFixed64 (pixelScaleH / radius.h); + fSrcStepV = Real64ToFixed64 (pixelScaleV / radius.v); + + // Adjust for pixel centers. + + fSrcOriginH += fSrcStepH >> 1; + fSrcOriginV += fSrcStepV >> 1; + + if (!fGainTable.Get ()) + { + + // Set the vignette correction curve. + + const dng_vignette_radial_function curve (params); + + // Evaluate 32-bit vignette correction table. + + dng_1d_table table32; + + table32.Initialize (allocator, + curve, + false); + + // Find maximum scale factor. + + const real64 maxScale = Max_real32 (table32.Interpolate (0.0f), + table32.Interpolate (1.0f)); + + // Find table input bits. + + fTableInputBits = 16; + + // Find table output bits. + + fTableOutputBits = 15; + + while ((1 << fTableOutputBits) * maxScale > 65535.0) + { + fTableOutputBits--; + } + + // Allocate 16-bit scale table. + + const uint32 tableEntries = (1 << fTableInputBits) + 1; + + fGainTable.Reset (allocator.Allocate (tableEntries * (uint32) sizeof (uint16))); + + uint16 *table16 = fGainTable->Buffer_uint16 (); + + // Interpolate 32-bit table into 16-bit table. + + const real32 scale0 = 1.0f / (1 << fTableInputBits ); + const real32 scale1 = 1.0f * (1 << fTableOutputBits); + + for (uint32 index = 0; index < tableEntries; index++) + { + + real32 x = index * scale0; + + real32 y = table32.Interpolate (x) * scale1; + + table16 [index] = (uint16) Round_uint32 (y); + + } + + } + + // Prepare vignette mask buffers. + + { + + const uint32 pixelType = ttShort; + + const uint32 bufferSize = ComputeBufferSize (pixelType, + tileSize, + imagePlanes, + padSIMDBytes); + + // Support repeated Prepare() calls by ensuring all buffers are reset. + + for (uint32 threadIndex = 0; threadIndex < kMaxMPThreads; threadIndex++) + { + + fMaskBuffers [threadIndex] . Reset (); + + } + + for (uint32 threadIndex = 0; threadIndex < threadCount; threadIndex++) + { + + fMaskBuffers [threadIndex] . Reset (allocator.Allocate (bufferSize)); + + } + + } + + } + +/*****************************************************************************/ + +void dng_opcode_FixVignetteRadial::ProcessArea (dng_negative &negative, + uint32 threadIndex, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect & /* imageBounds */) + { + + // Setup mask pixel buffer. + + dng_pixel_buffer maskPixelBuffer (dstArea, + 0, + fImagePlanes, + ttShort, + pcRowInterleavedAlignSIMD, + fMaskBuffers [threadIndex]->Buffer ()); + + // Compute mask. + + DoVignetteMask16 (maskPixelBuffer.DirtyPixel_uint16 (dstArea.t, dstArea.l), + dstArea.H (), + dstArea.W (), + maskPixelBuffer.RowStep (), + fSrcOriginH + fSrcStepH * dstArea.l, + fSrcOriginV + fSrcStepV * dstArea.t, + fSrcStepH, + fSrcStepV, + fTableInputBits, + fGainTable->Buffer_uint16 ()); + + // Apply mask. + + uint16 blackLevel = (Stage () >= 2) ? negative.Stage3BlackLevel () : 0; + + DoVignette32 (buffer.DirtyPixel_real32 (dstArea.t, dstArea.l), + maskPixelBuffer.ConstPixel_uint16 (dstArea.t, dstArea.l), + dstArea.H (), + dstArea.W (), + fImagePlanes, + buffer.RowStep (), + buffer.PlaneStep (), + maskPixelBuffer.RowStep (), + fTableOutputBits, + blackLevel); + + } + +/*****************************************************************************/ + +uint32 dng_opcode_FixVignetteRadial::ParamBytes () + { + + const uint32 N = dng_vignette_radial_params::kNumTerms; + + return ((N * sizeof (real64)) + // Vignette coefficients. + (2 * sizeof (real64))); // Optical center. + + } + +/*****************************************************************************/ + +dng_vignette_radial_params dng_opcode_FixVignetteRadial::MakeParamsForRender + (const dng_negative &negative) + { + + (void) negative; + + return fParams; + + } + +/*****************************************************************************/ diff --git a/dng/dng_lens_correction.h b/dng/dng_lens_correction.h new file mode 100644 index 0000000..31ef98b --- /dev/null +++ b/dng/dng_lens_correction.h @@ -0,0 +1,670 @@ +/*****************************************************************************/ +// Copyright 2008-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Opcodes to fix lens aberrations such as geometric distortion, lateral chromatic + * aberration, and vignetting (peripheral illumination falloff). + */ + +/*****************************************************************************/ + +#ifndef __dng_lens_correction__ +#define __dng_lens_correction__ + +/*****************************************************************************/ + +#include "dng_1d_function.h" +#include "dng_matrix.h" +#include "dng_memory.h" +#include "dng_opcodes.h" +#include "dng_pixel_buffer.h" +#include "dng_point.h" +#include "dng_resample.h" +#include "dng_sdk_limits.h" + +/*****************************************************************************/ + +/// \brief Abstract base class holding common warp opcode parameters (e.g., +/// number of planes, optical center) and common warp routines. + +class dng_warp_params + { + + public: + + // Number of planes to be warped. Must be either 1 or equal to the + // number of planes of the image to be processed. If set to 1, then a + // single set of warp parameters applies to all planes of the image. + // fPlanes must be at least 1 and no greater than kMaxColorPlanes (see + // dng_sdk_limits.h). + + uint32 fPlanes; + + // The optical center of the lens in normalized [0,1] coordinates with + // respect to the image's active area. For example, a value of (0.5, + // 0.5) indicates that the optical center of the lens is at the center + // of the image's active area. A normalized radius of 1.0 corresponds to + // the distance from fCenter to the farthest corner of the image's + // active area. Each component of fCenter must lie in the range [0,1]. + + dng_point_real64 fCenter; + + public: + + /// Create empty (invalid) warp parameters. + + dng_warp_params (); + + /// Create warp parameters with specified number of planes and image + /// center. + /// + /// \param planes The number of planes of parameters specified: It must + /// be either 1 or equal to the number of planes of the image to be + /// processed. + /// + /// \param fCenter The image center in relative coordinates. + + dng_warp_params (uint32 planes, + const dng_point_real64 &fCenter); + + virtual ~dng_warp_params (); + + /// Is the entire correction a NOP for all planes? + + virtual bool IsNOPAll () const; + + /// Is the entire correction a NOP for the specified plane? + + virtual bool IsNOP (uint32 plane) const; + + /// Is the radial correction a NOP for all planes? + + virtual bool IsRadNOPAll () const; + + /// Is the radial correction a NOP for the specified plane? + + virtual bool IsRadNOP (uint32 plane) const; + + /// Is the tangential correction a NOP for all planes? + + virtual bool IsTanNOPAll () const; + + /// Is the tangential correction a NOP for the specified plane? + + virtual bool IsTanNOP (uint32 plane) const; + + /// Do these warp params appear valid? + + virtual bool IsValid () const; + + /// Are these warp params valid for the specified negative? + + virtual bool IsValidForNegative (const dng_negative &negative) const; + + /// Propagate warp parameters from first plane to all other planes. + + virtual void PropagateToAllPlanes (uint32 totalPlanes) = 0; + + /// Evaluate the 1D radial warp function for the specified plane. + /// Parameter r is the destination (i.e., corrected) normalized radius, + /// i.e., the normalized Euclidean distance between a corrected pixel + /// position and the optical center in the image. r lies in the range + /// [0,1]. The returned result is non-negative. + + virtual real64 Evaluate (uint32 plane, + real64 r) const = 0; + + /// Compute and return the inverse of Evaluate () above. The base + /// implementation uses Newton's method to perform the inversion. + /// Parameter r is the source (i.e., uncorrected) normalized radius, + /// i.e., normalized Euclidean distance between a corrected pixel + /// position and the optical center in the image. Both r and the + /// computed result are non-negative. + + virtual real64 EvaluateInverse (uint32 plane, + real64 r) const; + + /// Evaluate the 1D radial warp ratio function for the specified plane. + /// Parameter r2 is the square of the destination (i.e., corrected) + /// normalized radius, i.e., the square of the normalized Euclidean + /// distance between a corrected pixel position and the optical center + /// in the image. r2 must lie in the range [0,1]. Note that this is + /// different than the Evaluate () function, above, in that the argument + /// to EvaluateRatio () is the square of the radius, not the radius + /// itself. The returned result is non-negative. Mathematically, + /// EvaluateRatio (r * r) is the same as Evaluate (r) / r. + + virtual real64 EvaluateRatio (uint32 plane, + real64 r2) const = 0; + + /// Evaluate the 2D tangential warp for the specified plane. Parameter + /// r2 is the square of the destination (i.e., corrected) normalized + /// radius, i.e., the square of the normalized Euclidean distance + /// between a corrected pixel position P and the optical center in the + /// image. r2 must lie in the range [0,1]. diff contains the vertical + /// and horizontal Euclidean distances (in pixels) between P and the + /// optical center. diff2 contains the squares of the vertical and + /// horizontal Euclidean distances (in pixels) between P and the optical + /// center. The returned result is the tangential warp offset, measured + /// in pixels. + + virtual dng_point_real64 EvaluateTangential (uint32 plane, + real64 r2, + const dng_point_real64 &diff, + const dng_point_real64 &diff2) const = 0; + + /// Evaluate the 2D tangential warp for the specified plane. diff + /// contains the vertical and horizontal Euclidean distances (in pixels) + /// between the destination (i.e., corrected) pixel position and the + /// optical center in the image. The returned result is the tangential + /// warp offset, measured in pixels. + + dng_point_real64 EvaluateTangential2 (uint32 plane, + const dng_point_real64 &diff) const; + + /// Evaluate the 2D tangential warp for the specified plane. Parameter + /// r2 is the square of the destination (i.e., corrected) normalized + /// radius, i.e., the square of the normalized Euclidean distance + /// between a corrected pixel position P and the optical center in the + /// image. r2 must lie in the range [0,1]. diff contains the vertical + /// and horizontal Euclidean distances (in pixels) between P and the + /// optical center. The returned result is the tangential warp offset, + /// measured in pixels. + + dng_point_real64 EvaluateTangential3 (uint32 plane, + real64 r2, + const dng_point_real64 &diff) const; + + /// Compute and return the maximum warped radius gap. Let D be a + /// rectangle in a destination (corrected) image. Let rDstFar and + /// rDstNear be the farthest and nearest points to the image center, + /// respectively. Then the specified parameter maxDstGap is the + /// Euclidean distance between rDstFar and rDstNear. Warp D through this + /// warp function to a closed and bounded (generally not rectangular) + /// region S. Let rSrcfar and rSrcNear be the farthest and nearest + /// points to the image center, respectively. This routine returns a + /// value that is at least (rSrcFar - rSrcNear). + + virtual real64 MaxSrcRadiusGap (real64 maxDstGap) const = 0; + + /// Compute and return the maximum warped tangential gap. minDst is the + /// top-left pixel of the image in normalized pixel coordinates. maxDst + /// is the bottom-right pixel of the image in normalized pixel + /// coordinates. MaxSrcTanGap () computes the maximum absolute shift in + /// normalized pixels in the horizontal and vertical directions that can + /// occur as a result of the tangential warp. + + virtual dng_point_real64 MaxSrcTanGap (dng_point_real64 minDst, + dng_point_real64 maxDst) const = 0; + + /// Compute and return the minimum src/dst ratio that should be used + /// for this warp. + + virtual real64 SafeMinRatio () const = 0; + + /// Compute and return the maximum src/dst ratio that should be used + /// for this warp. + + virtual real64 SafeMaxRatio () const = 0; + + /// Debug parameters. + + virtual void Dump () const; + + }; + +/*****************************************************************************/ + +/// \brief Warp parameters for pinhole perspective rectilinear (not fisheye) +/// camera model. Supports radial and tangential (decentering) distortion +/// correction parameters. +/// +/// Note the restrictions described below. + +class dng_warp_params_rectilinear: public dng_warp_params + { + + public: + + // Radial and tangential polynomial coefficients. These define a warp + // from corrected pixel coordinates (xDst, yDst) to uncorrected pixel + // coordinates (xSrc, ySrc) for each plane P as follows: + // + // Let kr0 = fRadParams [P][0] + // kr1 = fRadParams [P][1] + // kr2 = fRadParams [P][2] + // kr3 = fRadParams [P][3] + // + // kt0 = fTanParams [P][0] + // kt1 = fTanParams [P][1] + // + // Let (xCenter, yCenter) be the optical image center (see fCenter, + // below) expressed in pixel coordinates. Let maxDist be the Euclidean + // distance (in pixels) from (xCenter, yCenter) to the farthest image + // corner. + // + // First, compute the normalized distance of the corrected pixel + // position (xDst, yDst) from the image center: + // + // dx = (xDst - xCenter) / maxDist + // dy = (yDst - yCenter) / maxDist + // + // r^2 = dx^2 + dy^2 + // + // Compute the radial correction term: + // + // ratio = kr0 + (kr1 * r^2) + (kr2 * r^4) + (kr3 * r^6) + // + // dxRad = dx * ratio + // dyRad = dy * ratio + // + // Compute the tangential correction term: + // + // dxTan = (2 * kt0 * dx * dy) + kt1 * (r^2 + 2 * dx^2) + // dyTan = (2 * kt1 * dx * dy) + kt0 * (r^2 + 2 * dy^2) + // + // Compute the uncorrected pixel position (xSrc, ySrc): + // + // xSrc = xCenter + (dxRad + dxTan) * maxDist + // ySrc = yCenter + (dyRad + dyTan) * maxDist + // + // Mathematical definitions and restrictions: + // + // Let { xSrc, ySrc } = f (xDst, yDst) be the warp function defined + // above. + // + // Let xSrc = fx (xDst, yDst) be the x-component of the warp function. + // Let ySrc = fy (xDst, yDst) be the y-component of the warp function. + // + // f (x, y) must be an invertible function. + // + // fx (x, y) must be an increasing function of x. + // fy (x, y) must be an increasing function of x. + // + // The parameters kr0, kr1, kr2, and kr3 must define an increasing + // radial warp function. Specifically, let w (r) be the radial warp + // function: + // + // w (r) = (kr0 * r) + (kr1 * r^3) + (kr2 * r^5) + (kr3 * r^7). + // + // w (r) must be an increasing function. + + dng_vector fRadParams [kMaxColorPlanes]; + dng_vector fTanParams [kMaxColorPlanes]; + + public: + + /// Create empty (invalid) rectilinear warp parameters. + + dng_warp_params_rectilinear (); + + /// Create rectilinear warp parameters with the specified number of + /// planes, radial component terms, tangential component terms, and + /// image center in relative coordinates. + + dng_warp_params_rectilinear (uint32 planes, + const dng_vector radParams [], + const dng_vector tanParams [], + const dng_point_real64 &fCenter); + + virtual ~dng_warp_params_rectilinear (); + + // Overridden methods. + + virtual bool IsRadNOP (uint32 plane) const; + + virtual bool IsTanNOP (uint32 plane) const; + + virtual bool IsValid () const; + + virtual void PropagateToAllPlanes (uint32 totalPlanes); + + virtual real64 Evaluate (uint32 plane, + real64 r) const; + + virtual real64 EvaluateRatio (uint32 plane, + real64 r2) const; + + virtual dng_point_real64 EvaluateTangential (uint32 plane, + real64 r2, + const dng_point_real64 &diff, + const dng_point_real64 &diff2) const; + + virtual real64 MaxSrcRadiusGap (real64 maxDstGap) const; + + virtual dng_point_real64 MaxSrcTanGap (dng_point_real64 minDst, + dng_point_real64 maxDst) const; + + virtual real64 SafeMinRatio () const; + + virtual real64 SafeMaxRatio () const; + + virtual void Dump () const; + + }; + +/*****************************************************************************/ + +/// \brief Warp parameters for fisheye camera model (radial component only). +/// Note the restrictions described below. + +class dng_warp_params_fisheye: public dng_warp_params + { + + public: + + // Radial warp coefficients. These define a warp from corrected pixel + // coordinates (xDst, yDst) to uncorrected pixel coordinates (xSrc, + // ySrc) for each plane P as follows: + // + // Let kr0 = fRadParams [P][0] + // kr1 = fRadParams [P][1] + // kr2 = fRadParams [P][2] + // kr3 = fRadParams [P][3] + // + // Let (xCenter, yCenter) be the optical image center (see fCenter, + // below) expressed in pixel coordinates. Let maxDist be the Euclidean + // distance (in pixels) from (xCenter, yCenter) to the farthest image + // corner. + // + // First, compute the normalized distance of the corrected pixel + // position (xDst, yDst) from the image center: + // + // dx = (xDst - xCenter) / maxDist + // dy = (yDst - yCenter) / maxDist + // + // r = sqrt (dx^2 + dy^2) + // + // Compute the radial correction term: + // + // t = atan (r) + // + // rWarp = (kr0 * t) + (kr1 * t^3) + (kr2 * t^5) + (kr3 * t^7) + // + // ratio = rWarp / r + // + // dxRad = dx * ratio + // dyRad = dy * ratio + // + // Compute the uncorrected pixel position (xSrc, ySrc): + // + // xSrc = xCenter + (dxRad * maxDist) + // ySrc = yCenter + (dyRad * maxDist) + // + // The parameters kr0, kr1, kr2, and kr3 must define an increasing + // radial warp function. Specifically, let w (r) be the radial warp + // function: + // + // t = atan (r) + // + // w (r) = (kr0 * t) + (kr1 * t^3) + (kr2 * t^5) + (kr3 * t^7). + // + // w (r) must be an increasing function. + + dng_vector fRadParams [kMaxColorPlanes]; + + public: + + /// Create empty (invalid) fisheye warp parameters. + + dng_warp_params_fisheye (); + + /// Create rectilinear warp parameters with the specified number of + /// planes, radial component terms, and image center in relative + /// coordinates. + + dng_warp_params_fisheye (uint32 planes, + const dng_vector radParams [], + const dng_point_real64 &fCenter); + + virtual ~dng_warp_params_fisheye (); + + // Overridden methods. + + virtual bool IsRadNOP (uint32 plane) const; + + virtual bool IsTanNOP (uint32 plane) const; + + virtual bool IsValid () const; + + virtual void PropagateToAllPlanes (uint32 totalPlanes); + + virtual real64 Evaluate (uint32 plane, + real64 r) const; + + virtual real64 EvaluateRatio (uint32 plane, + real64 r2) const; + + virtual dng_point_real64 EvaluateTangential (uint32 plane, + real64 r2, + const dng_point_real64 &diff, + const dng_point_real64 &diff2) const; + + virtual real64 MaxSrcRadiusGap (real64 maxDstGap) const; + + virtual dng_point_real64 MaxSrcTanGap (dng_point_real64 minDst, + dng_point_real64 maxDst) const; + + virtual real64 SafeMinRatio () const; + + virtual real64 SafeMaxRatio () const; + + virtual void Dump () const; + + }; + +/*****************************************************************************/ + +/// \brief Warp opcode for pinhole perspective (rectilinear) camera model. + +class dng_opcode_WarpRectilinear: public dng_opcode + { + + protected: + + dng_warp_params_rectilinear fWarpParams; + + public: + + dng_opcode_WarpRectilinear (const dng_warp_params_rectilinear ¶ms, + uint32 flags); + + explicit dng_opcode_WarpRectilinear (dng_stream &stream); + + // Overridden methods. + + virtual bool IsNOP () const; + + virtual bool IsValidForNegative (const dng_negative &negative) const; + + virtual void PutData (dng_stream &stream) const; + + virtual void Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image); + + // Other methods. + + bool HasDistort () const; + + bool HasLateralCA () const; + + const dng_warp_params_rectilinear & Params () const + { + return fWarpParams; + } + + protected: + + static uint32 ParamBytes (uint32 planes); + + }; + +/*****************************************************************************/ + +/// \brief Warp opcode for fisheye camera model. + +class dng_opcode_WarpFisheye: public dng_opcode + { + + protected: + + dng_warp_params_fisheye fWarpParams; + + public: + + dng_opcode_WarpFisheye (const dng_warp_params_fisheye ¶ms, + uint32 flags); + + explicit dng_opcode_WarpFisheye (dng_stream &stream); + + // Overridden methods. + + virtual bool IsNOP () const; + + virtual bool IsValidForNegative (const dng_negative &negative) const; + + virtual void PutData (dng_stream &stream) const; + + virtual void Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image); + + protected: + + static uint32 ParamBytes (uint32 planes); + + }; + +/*****************************************************************************/ + +/// \brief Radially-symmetric vignette (peripheral illuminational falloff) +/// correction parameters. + +class dng_vignette_radial_params + { + + public: + + static const uint32 kNumTerms = 5; + + public: + + // Let v be an uncorrected pixel value of a pixel p in linear space. + // + // Let r be the Euclidean distance between p and the optical center. + // + // Compute corrected pixel value v' = v * g, where g is the gain. + // + // Let k0 = fParams [0] + // Let k1 = fParams [1] + // Let k2 = fParams [2] + // Let k3 = fParams [3] + // Let k4 = fParams [4] + // + // Gain g = 1 + (k0 * r^2) + (k1 * r^4) + (k2 * r^6) + (k3 * r^8) + (k4 * r^10) + + dng_std_vector fParams; + + dng_point_real64 fCenter; + + public: + + dng_vignette_radial_params (); + + dng_vignette_radial_params (const dng_std_vector ¶ms, + const dng_point_real64 ¢er); + + dng_vignette_radial_params (const dng_vignette_radial_params ¶ms); + + bool IsNOP () const; + + bool IsValid () const; + + // For debugging. + + void Dump () const; + + }; + +/*****************************************************************************/ + +/// \brief Radially-symmetric lens vignette correction opcode. + +class dng_opcode_FixVignetteRadial: public dng_inplace_opcode + { + + protected: + + dng_vignette_radial_params fParams; + + uint32 fImagePlanes; + + int64 fSrcOriginH; + int64 fSrcOriginV; + + int64 fSrcStepH; + int64 fSrcStepV; + + uint32 fTableInputBits; + uint32 fTableOutputBits; + + AutoPtr fGainTable; + + AutoPtr fMaskBuffers [kMaxMPThreads]; + + public: + + dng_opcode_FixVignetteRadial (const dng_vignette_radial_params ¶ms, + uint32 flags); + + explicit dng_opcode_FixVignetteRadial (dng_stream &stream); + + const dng_vignette_radial_params & Params () const + { + return fParams; + } + + virtual bool IsNOP () const; + + virtual bool IsValidForNegative (const dng_negative &) const; + + virtual void PutData (dng_stream &stream) const; + + virtual uint32 BufferPixelType (uint32 /* imagePixelType */) + { + return ttFloat; + } + + virtual void Prepare (dng_negative &negative, + uint32 threadCount, + const dng_point &tileSize, + const dng_rect &imageBounds, + uint32 imagePlanes, + uint32 bufferPixelType, + dng_memory_allocator &allocator); + + virtual void ProcessArea (dng_negative &negative, + uint32 threadIndex, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect &imageBounds); + + protected: + + static uint32 ParamBytes (); + + virtual dng_vignette_radial_params MakeParamsForRender (const dng_negative &negative); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_linearization_info.cpp b/dng/dng_linearization_info.cpp new file mode 100644 index 0000000..c534e94 --- /dev/null +++ b/dng/dng_linearization_info.cpp @@ -0,0 +1,1600 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_linearization_info.h" + +#include "dng_area_task.h" +#include "dng_exceptions.h" +#include "dng_host.h" +#include "dng_image.h" +#include "dng_info.h" +#include "dng_negative.h" +#include "dng_pixel_buffer.h" +#include "dng_safe_arithmetic.h" +#include "dng_sdk_limits.h" +#include "dng_tag_types.h" +#include "dng_tile_iterator.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +class dng_linearize_plane + { + + private: + + const dng_image & fSrcImage; + dng_image & fDstImage; + + uint32 fPlane; + + dng_rect fActiveArea; + + uint32 fSrcPixelType; + uint32 fDstPixelType; + + bool fReal32; + + real32 fScale; + + AutoPtr fScale_buffer; + + uint32 fBlack_2D_rows; + uint32 fBlack_2D_cols; + + AutoPtr fBlack_2D_buffer; + + uint32 fBlack_1D_rows; + + AutoPtr fBlack_1D_buffer; + + public: + + dng_linearize_plane (dng_host &host, + dng_linearization_info &info, + uint16 dstBlackLevel, + bool forceClipBlackLevel, + const dng_image &srcImage, + dng_image &dstImage, + uint32 plane); + + ~dng_linearize_plane (); + + void Process (const dng_rect &tile); + + }; + +/*****************************************************************************/ + +dng_linearize_plane::dng_linearize_plane (dng_host &host, + dng_linearization_info &info, + uint16 dstBlackLevel, + bool forceClipBlackLevel, + const dng_image &srcImage, + dng_image &dstImage, + uint32 plane) + + : fSrcImage (srcImage) + , fDstImage (dstImage) + , fPlane (plane) + , fActiveArea (info.fActiveArea) + , fSrcPixelType (srcImage.PixelType ()) + , fDstPixelType (dstImage.PixelType ()) + , fReal32 (false) + , fScale (0.0f) + , fScale_buffer () + , fBlack_2D_rows (0) + , fBlack_2D_cols (0) + , fBlack_2D_buffer () + , fBlack_1D_rows (0) + , fBlack_1D_buffer () + + { + + uint32 j; + uint32 k; + + // Make sure the source pixel type is supported. + + if (fSrcPixelType != ttByte && + fSrcPixelType != ttShort && + fSrcPixelType != ttLong && + fSrcPixelType != ttFloat) + { + + DNG_REPORT ("Unsupported source pixel type"); + + ThrowProgramError (); + + } + + if (fDstPixelType != ttShort && + fDstPixelType != ttFloat) + { + + DNG_REPORT ("Unsupported destination pixel type"); + + ThrowProgramError (); + + } + + if (fSrcPixelType == ttFloat && + fDstPixelType != ttFloat) + { + + DNG_REPORT ("Cannot convert floating point stage1 to non-floating stage2"); + + ThrowProgramError (); + + } + + // Are we using floating point math? + + fReal32 = (fSrcPixelType == ttLong || + fDstPixelType == ttFloat); + + // Find the scale for this plane. + + real64 maxBlack = info.MaxBlackLevel (plane); + + real64 minRange = info.fWhiteLevel [plane] - maxBlack; + + if (minRange <= 0.0) + { + ThrowBadFormat (); + } + + real64 scale = 1.0 / minRange; + + fScale = (real32) scale; + + // Calculate two-dimensional black pattern, if any. + + if (info.fBlackDeltaH.Get ()) + { + + fBlack_2D_rows = info.fBlackLevelRepeatRows; + fBlack_2D_cols = info.fActiveArea.W (); + + } + + else if (info.fBlackLevelRepeatCols > 1) + { + + fBlack_2D_rows = info.fBlackLevelRepeatRows; + fBlack_2D_cols = info.fBlackLevelRepeatCols; + + } + + if (fBlack_2D_rows) + { + + fBlack_2D_buffer.Reset (host.Allocate (SafeUint32Mult (fBlack_2D_rows, + fBlack_2D_cols, + 4))); + + for (j = 0; j < fBlack_2D_rows; j++) + { + + for (k = 0; k < fBlack_2D_cols; k++) + { + + real64 x = info.fBlackLevel [j] + [k % info.fBlackLevelRepeatCols] + [plane]; + + if (info.fBlackDeltaH.Get ()) + { + + x += info.fBlackDeltaH->Buffer_real64 () [k]; + + } + + x *= scale; + + uint32 index = j * fBlack_2D_cols + k; + + if (fReal32) + { + + fBlack_2D_buffer->Buffer_real32 () [index] = (real32) x; + + } + + else + { + + x *= (0x0FFFF - dstBlackLevel) * 256; + + int32 y = Round_int32 (x); + + fBlack_2D_buffer->Buffer_int32 () [index] = y; + + } + + } + + } + + } + + // Calculate one-dimensional (per row) black pattern, if any. + + if (info.fBlackDeltaV.Get ()) + { + + fBlack_1D_rows = info.fActiveArea.H (); + + } + + else if (fBlack_2D_rows == 0 && + (info.fBlackLevelRepeatRows > 1 || (fSrcPixelType != ttShort && + fSrcPixelType != ttByte))) + { + + fBlack_1D_rows = info.fBlackLevelRepeatRows; + + } + + if (fBlack_1D_rows) + { + + fBlack_1D_buffer.Reset + (host.Allocate (SafeUint32Mult (fBlack_1D_rows, 4))); + + bool allZero = true; + + for (j = 0; j < fBlack_1D_rows; j++) + { + + real64 x = 0.0; + + if (fBlack_2D_rows == 0) + { + + x = info.fBlackLevel [j % info.fBlackLevelRepeatRows] + [0] + [plane]; + + } + + if (info.fBlackDeltaV.Get ()) + { + + x += info.fBlackDeltaV->Buffer_real64 () [j]; + + } + + allZero = allZero && (x == 0.0); + + x *= scale; + + if (fReal32) + { + + fBlack_1D_buffer->Buffer_real32 () [j] = (real32) x; + + } + + else + { + + x *= (0x0FFFF - dstBlackLevel) * 256; + + int32 y = Round_int32 (x); + + fBlack_1D_buffer->Buffer_int32 () [j] = y; + + } + + } + + if (allZero) + { + + fBlack_1D_rows = 0; + + fBlack_1D_buffer.Reset (); + + } + + } + + // Calculate scale table, if any. + + if (fSrcPixelType != ttLong && + fSrcPixelType != ttFloat) + { + + // Find linearization table, if any. + + uint16 *lut = NULL; + + uint32 lutEntries = 0; + + if (info.fLinearizationTable.Get ()) + { + + lut = info.fLinearizationTable->Buffer_uint16 (); + + lutEntries = info.fLinearizationTable->LogicalSize () >> 1; + + } + + // If the black level does not vary from pixel to pixel, then + // the entire process can be a single LUT. + + if (fBlack_1D_rows == 0 && + fBlack_2D_rows == 0) + { + + uint32 scaleLUTEntries = (fSrcPixelType == ttByte ? 0x100 : 0x10000); + + fScale_buffer.Reset (host.Allocate (scaleLUTEntries * + TagTypeSize (fDstPixelType))); + + for (j = 0; j < scaleLUTEntries; j++) + { + + uint32 x = j; + + // Apply linearization table, if any. + + if (lut) + { + + x = Min_uint32 (x, lutEntries - 1); + + x = lut [x]; + + } + + // Subtract constant black level. + + real64 y = x - info.fBlackLevel [0] [0] [plane]; + + // Apply scale. + + y *= scale; + + // Burn in the clipping if requested. + + if (forceClipBlackLevel) + { + y = Pin_real64 (0.0, y, 1.0); + } + + // Store output value in table. + + if (fDstPixelType == ttShort) + { + + uint16 z = Pin_uint16 (Round_int32 (y * (0x0FFFF - dstBlackLevel) + dstBlackLevel)); + + fScale_buffer->Buffer_uint16 () [j] = z; + + } + + else + { + + fScale_buffer->Buffer_real32 () [j] = (real32) y; + + } + + } + + } + + // Else we only do the scaling operation in the scale table. + + else + { + + fScale_buffer.Reset (host.Allocate (0x10000 * 4)); + + for (j = 0; j < 0x10000; j++) + { + + uint32 x = j; + + // Apply linearization table, if any. + + if (lut) + { + + x = Min_uint32 (x, lutEntries - 1); + + x = lut [x]; + + } + + // Apply scale. + + real64 y = x * scale; + + // Store output value in table. + + if (fReal32) + { + + fScale_buffer->Buffer_real32 () [j] = (real32) y; + + } + + else + { + + int32 z = Round_int32 ((y * (0x0FFFF - dstBlackLevel) + dstBlackLevel) * 256.0); + + fScale_buffer->Buffer_int32 () [j] = z; + + } + + } + + } + + } + + } + +/*****************************************************************************/ + +dng_linearize_plane::~dng_linearize_plane () + { + + } + +/*****************************************************************************/ + +void dng_linearize_plane::Process (const dng_rect &srcTile) + { + + // Process tile. + + dng_rect dstTile = srcTile - fActiveArea.TL (); + + dng_const_tile_buffer srcBuffer (fSrcImage, srcTile); + dng_dirty_tile_buffer dstBuffer (fDstImage, dstTile); + + int32 sStep = srcBuffer.fColStep; + int32 dStep = dstBuffer.fColStep; + + uint32 count = srcTile.W (); + + uint32 dstCol = dstTile.l; + + uint32 rows = srcTile.H (); + + for (uint32 row = 0; row < rows; row++) + { + + uint32 dstRow = dstTile.t + row; + + const void *sPtr = srcBuffer.ConstPixel (srcTile.t + row, + srcTile.l, + fPlane); + + void *dPtr = dstBuffer.DirtyPixel (dstRow, + dstCol, + fPlane); + + // Floating point source case. + + if (fSrcPixelType == ttFloat) + { + + real32 scale = fScale; + + const real32 *srcPtr = (const real32 *) sPtr; + + real32 *dstPtr = (real32 *) dPtr; + + // Optimize scale only case, which is the most common. + + if (fBlack_1D_rows == 0 && + fBlack_2D_cols == 0) + { + + for (uint32 j = 0; j < count; j++) + { + + *dstPtr = (*srcPtr) * scale; + + srcPtr += sStep; + dstPtr += dStep; + + } + + } + + else + { + + real32 b1 = 0.0f; + + if (fBlack_1D_rows) + { + b1 = fBlack_1D_buffer->Buffer_real32 () [dstRow % fBlack_1D_rows]; + } + + const real32 *b2 = NULL; + + uint32 b2_count = fBlack_2D_cols; + uint32 b2_phase = 0; + + if (b2_count) + { + + b2 = fBlack_2D_buffer->Buffer_real32 () + + b2_count * (dstRow % fBlack_2D_rows); + + b2_phase = dstCol % b2_count; + + } + + for (uint32 j = 0; j < count; j++) + { + + real32 x = (*srcPtr) * scale - b1; + + if (b2_count) + { + + x -= b2 [b2_phase]; + + if (++b2_phase == b2_count) + { + b2_phase = 0; + } + + } + + *dstPtr = x; + + srcPtr += sStep; + dstPtr += dStep; + + } + + } + + } + + // Simple LUT case. + + else if (fBlack_1D_rows == 0 && + fBlack_2D_rows == 0 && fSrcPixelType != ttLong) + { + + if (fDstPixelType == ttShort) + { + + const uint16 *lut = fScale_buffer->Buffer_uint16 (); + + uint16 *dstPtr = (uint16 *) dPtr; + + if (fSrcPixelType == ttByte) + { + + const uint8 *srcPtr = (const uint8 *) sPtr; + + for (uint32 j = 0; j < count; j++) + { + + *dstPtr = lut [*srcPtr]; + + srcPtr += sStep; + dstPtr += dStep; + + } + + } + + else + { + + const uint16 *srcPtr = (const uint16 *) sPtr; + + for (uint32 j = 0; j < count; j++) + { + + *dstPtr = lut [*srcPtr]; + + srcPtr += sStep; + dstPtr += dStep; + + } + + } + + } + + else + { + + const real32 *lut = fScale_buffer->Buffer_real32 (); + + real32 *dstPtr = (real32 *) dPtr; + + if (fSrcPixelType == ttByte) + { + + const uint8 *srcPtr = (const uint8 *) sPtr; + + for (uint32 j = 0; j < count; j++) + { + + *dstPtr = lut [*srcPtr]; + + srcPtr += sStep; + dstPtr += dStep; + + } + + } + + else + { + + const uint16 *srcPtr = (const uint16 *) sPtr; + + for (uint32 j = 0; j < count; j++) + { + + *dstPtr = lut [*srcPtr]; + + srcPtr += sStep; + dstPtr += dStep; + + } + + } + + } + + } + + // Integer math case. + + else if (!fReal32) + { + + const int32 *lut = fScale_buffer->Buffer_int32 (); + + int32 b1 = 0; + + if (fBlack_1D_rows) + { + b1 = fBlack_1D_buffer->Buffer_int32 () [dstRow % fBlack_1D_rows]; + } + + const int32 *b2 = NULL; + + uint32 b2_count = fBlack_2D_cols; + uint32 b2_phase = 0; + + if (b2_count) + { + + DNG_REQUIRE (fBlack_2D_rows > 0, + "Bad fBlack_2D_rows in dng_linearize_plane::Process"); + + b2 = fBlack_2D_buffer->Buffer_int32 () + + b2_count * (dstRow % fBlack_2D_rows); + + b2_phase = dstCol % b2_count; + + } + + uint16 *dstPtr = (uint16 *) dPtr; + + b1 -= 128; // Rounding for 8 bit shift + + if (fSrcPixelType == ttByte) + { + + const uint8 *srcPtr = (const uint8 *) sPtr; + + for (uint32 j = 0; j < count; j++) + { + + int32 x = lut [*srcPtr] - b1; + + if (b2_count) + { + + x -= b2 [b2_phase]; + + if (++b2_phase == b2_count) + { + b2_phase = 0; + } + + } + + x >>= 8; + + *dstPtr = Pin_uint16 (x); + + srcPtr += sStep; + dstPtr += dStep; + + } + + } + + else + { + + const uint16 *srcPtr = (const uint16 *) sPtr; + + for (uint32 j = 0; j < count; j++) + { + + int32 x = lut [*srcPtr] - b1; + + if (b2_count) + { + + x -= b2 [b2_phase]; + + if (++b2_phase == b2_count) + { + b2_phase = 0; + } + + } + + x >>= 8; + + *dstPtr = Pin_uint16 (x); + + srcPtr += sStep; + dstPtr += dStep; + + } + + } + + } + + // Floating point math cases. + + else + { + + real32 b1 = 0.0f; + + if (fBlack_1D_rows) + { + b1 = fBlack_1D_buffer->Buffer_real32 () [dstRow % fBlack_1D_rows]; + } + + const real32 *b2 = NULL; + + uint32 b2_count = fBlack_2D_cols; + uint32 b2_phase = 0; + + if (b2_count) + { + + DNG_REQUIRE (fBlack_2D_rows > 0, + "Bad fBlack_2D_rows in dng_linearize_plane::Process"); + + b2 = fBlack_2D_buffer->Buffer_real32 () + + b2_count * (dstRow % fBlack_2D_rows); + + b2_phase = dstCol % b2_count; + + } + + // Case 1: uint8/uint16 -> real32 + + if (fSrcPixelType != ttLong) + { + + const real32 *lut = fScale_buffer->Buffer_real32 (); + + real32 *dstPtr = (real32 *) dPtr; + + if (fSrcPixelType == ttByte) + { + + const uint8 *srcPtr = (const uint8 *) sPtr; + + for (uint32 j = 0; j < count; j++) + { + + real32 x = lut [*srcPtr] - b1; + + if (b2_count) + { + + x -= b2 [b2_phase]; + + if (++b2_phase == b2_count) + { + b2_phase = 0; + } + + } + + x = Pin_real32 (0.0f, x, 1.0f); + + *dstPtr = x; + + srcPtr += sStep; + dstPtr += dStep; + + } + + } + + else + { + + const uint16 *srcPtr = (const uint16 *) sPtr; + + for (uint32 j = 0; j < count; j++) + { + + real32 x = lut [*srcPtr] - b1; + + if (b2_count) + { + + x -= b2 [b2_phase]; + + if (++b2_phase == b2_count) + { + b2_phase = 0; + } + + } + + x = Pin_real32 (0.0f, x, 1.0f); + + *dstPtr = x; + + srcPtr += sStep; + dstPtr += dStep; + + } + + } + + } + + // Otherwise source is uint32 + + else + { + + real32 scale = fScale; + + const uint32 *srcPtr = (const uint32 *) sPtr; + + // Case 2: uint32 -> real32 + + if (fDstPixelType == ttFloat) + { + + real32 *dstPtr = (real32 *) dPtr; + + for (uint32 j = 0; j < count; j++) + { + + real32 x = ((real32) *srcPtr) * scale - b1; + + if (b2_count) + { + + x -= b2 [b2_phase]; + + if (++b2_phase == b2_count) + { + b2_phase = 0; + } + + } + + x = Pin_real32 (0.0f, x, 1.0f); + + *dstPtr = x; + + srcPtr += sStep; + dstPtr += dStep; + + } + + } + + // Case 3: uint32 -> uint16 + + else + { + + uint16 *dstPtr = (uint16 *) dPtr; + + real32 dstScale = (real32) 0x0FFFF; + + for (uint32 j = 0; j < count; j++) + { + + real32 x = ((real32) *srcPtr) * scale - b1; + + if (b2_count) + { + + x -= b2 [b2_phase]; + + if (++b2_phase == b2_count) + { + b2_phase = 0; + } + + } + + x = Pin_real32 (0.0f, x, 1.0f); + + *dstPtr = (uint16) (x * dstScale + 0.5f); + + srcPtr += sStep; + dstPtr += dStep; + + } + + } + + } + + } + + } + + } + +/*****************************************************************************/ + +class dng_linearize_image: public dng_area_task + { + + private: + + const dng_image & fSrcImage; + dng_image & fDstImage; + + dng_rect fActiveArea; + + AutoPtr fPlaneTask [kMaxColorPlanes]; + + public: + + dng_linearize_image (dng_host &host, + dng_linearization_info &info, + uint16 dstBlackLevel, + bool forceClipBlackLevel, + const dng_image &srcImage, + dng_image &dstImage); + + virtual ~dng_linearize_image (); + + virtual dng_rect RepeatingTile1 () const; + + virtual dng_rect RepeatingTile2 () const; + + virtual void Process (uint32 threadIndex, + const dng_rect &tile, + dng_abort_sniffer *sniffer); + + }; + +/*****************************************************************************/ + +dng_linearize_image::dng_linearize_image (dng_host &host, + dng_linearization_info &info, + uint16 dstBlackLevel, + bool forceClipBlackLevel, + const dng_image &srcImage, + dng_image &dstImage) + + : dng_area_task ("dng_linearization_image") + + , fSrcImage (srcImage) + , fDstImage (dstImage) + , fActiveArea (info.fActiveArea) + + { + + // Build linearization table for each plane. + + for (uint32 plane = 0; plane < srcImage.Planes (); plane++) + { + + fPlaneTask [plane].Reset (new dng_linearize_plane (host, + info, + dstBlackLevel, + forceClipBlackLevel, + srcImage, + dstImage, + plane)); + + } + + // Adjust maximum tile size. + + fMaxTileSize = dng_point (1024, 1024); + + } + +/*****************************************************************************/ + +dng_linearize_image::~dng_linearize_image () + { + + } + +/*****************************************************************************/ + +dng_rect dng_linearize_image::RepeatingTile1 () const + { + + return fSrcImage.RepeatingTile (); + + } + +/*****************************************************************************/ + +dng_rect dng_linearize_image::RepeatingTile2 () const + { + + return fDstImage.RepeatingTile () + fActiveArea.TL (); + + } + +/*****************************************************************************/ + +void dng_linearize_image::Process (uint32 /* threadIndex */, + const dng_rect &srcTile, + dng_abort_sniffer * /* sniffer */) + { + + // Process each plane. + + for (uint32 plane = 0; plane < fSrcImage.Planes (); plane++) + { + + fPlaneTask [plane]->Process (srcTile); + + } + + } + +/*****************************************************************************/ + +dng_linearization_info::dng_linearization_info () + + : fActiveArea () + , fMaskedAreaCount (0) + , fLinearizationTable () + , fBlackLevelRepeatRows (1) + , fBlackLevelRepeatCols (1) + , fBlackDeltaH () + , fBlackDeltaV () + , fBlackDenom (256) + + { + + uint32 j; + uint32 k; + uint32 n; + + for (j = 0; j < kMaxBlackPattern; j++) + for (k = 0; k < kMaxBlackPattern; k++) + for (n = 0; n < kMaxSamplesPerPixel; n++) + { + fBlackLevel [j] [k] [n] = 0.0; + } + + for (n = 0; n < kMaxSamplesPerPixel; n++) + { + fWhiteLevel [n] = 65535.0; + } + + } + +/*****************************************************************************/ + +dng_linearization_info::~dng_linearization_info () + { + + } + +/*****************************************************************************/ + +void dng_linearization_info::RoundBlacks () + { + + uint32 j; + uint32 k; + uint32 n; + + real64 maxAbs = 0.0; + + for (j = 0; j < fBlackLevelRepeatRows; j++) + for (k = 0; k < fBlackLevelRepeatCols; k++) + for (n = 0; n < kMaxSamplesPerPixel; n++) + { + + maxAbs = Max_real64 (maxAbs, + Abs_real64 (fBlackLevel [j] [k] [n])); + + } + + uint32 count = RowBlackCount (); + + for (j = 0; j < count; j++) + { + + maxAbs = Max_real64 (maxAbs, + Abs_real64 (fBlackDeltaV->Buffer_real64 () [j])); + + } + + count = ColumnBlackCount (); + + for (j = 0; j < count; j++) + { + + maxAbs = Max_real64 (maxAbs, + Abs_real64 (fBlackDeltaH->Buffer_real64 () [j])); + + + } + + fBlackDenom = 256; + + while (fBlackDenom > 1 && (maxAbs * fBlackDenom) >= 30000.0 * 65536.0) + { + fBlackDenom >>= 1; + } + + for (j = 0; j < fBlackLevelRepeatRows; j++) + for (k = 0; k < fBlackLevelRepeatCols; k++) + for (n = 0; n < kMaxSamplesPerPixel; n++) + { + + fBlackLevel [j] [k] [n] = BlackLevel (j, k, n).As_real64 (); + + } + + count = RowBlackCount (); + + for (j = 0; j < count; j++) + { + + fBlackDeltaV->Buffer_real64 () [j] = RowBlack (j).As_real64 (); + + } + + count = ColumnBlackCount (); + + for (j = 0; j < count; j++) + { + + fBlackDeltaH->Buffer_real64 () [j] = ColumnBlack (j).As_real64 (); + + } + + } + +/*****************************************************************************/ + +void dng_linearization_info::Parse (dng_host &host, + dng_stream &stream, + dng_info &info) + { + + uint32 j; + uint32 k; + uint32 n; + + // Find main image IFD. + + dng_ifd &rawIFD = *info.fIFD [info.fMainIndex]; + + // Copy active area. + + fActiveArea = rawIFD.fActiveArea; + + // Copy masked areas. + + fMaskedAreaCount = rawIFD.fMaskedAreaCount; + + for (j = 0; j < fMaskedAreaCount; j++) + { + fMaskedArea [j] = rawIFD.fMaskedArea [j]; + } + + // Read linearization LUT. + + if (rawIFD.fLinearizationTableCount) + { + + uint32 size = SafeUint32Mult (rawIFD.fLinearizationTableCount, + static_cast (sizeof (uint16))); + + fLinearizationTable.Reset (host.Allocate (size)); + + uint16 *table = fLinearizationTable->Buffer_uint16 (); + + stream.SetReadPosition (rawIFD.fLinearizationTableOffset); + + for (j = 0; j < rawIFD.fLinearizationTableCount; j++) + { + table [j] = stream.Get_uint16 (); + } + + } + + // Copy black level pattern. + + fBlackLevelRepeatRows = rawIFD.fBlackLevelRepeatRows; + fBlackLevelRepeatCols = rawIFD.fBlackLevelRepeatCols; + + for (j = 0; j < kMaxBlackPattern; j++) + for (k = 0; k < kMaxBlackPattern; k++) + for (n = 0; n < kMaxSamplesPerPixel; n++) + { + fBlackLevel [j] [k] [n] = rawIFD.fBlackLevel [j] [k] [n]; + } + + // Read BlackDeltaH. + + if (rawIFD.fBlackLevelDeltaHCount) + { + + uint32 size = SafeUint32Mult (rawIFD.fBlackLevelDeltaHCount, + static_cast (sizeof (real64))); + + fBlackDeltaH.Reset (host.Allocate (size)); + + real64 *blacks = fBlackDeltaH->Buffer_real64 (); + + stream.SetReadPosition (rawIFD.fBlackLevelDeltaHOffset); + + for (j = 0; j < rawIFD.fBlackLevelDeltaHCount; j++) + { + blacks [j] = stream.TagValue_real64 (rawIFD.fBlackLevelDeltaHType); + } + + } + + // Read BlackDeltaV. + + if (rawIFD.fBlackLevelDeltaVCount) + { + + uint32 size = SafeUint32Mult (rawIFD.fBlackLevelDeltaVCount, + static_cast (sizeof (real64))); + + fBlackDeltaV.Reset (host.Allocate (size)); + + real64 *blacks = fBlackDeltaV->Buffer_real64 (); + + stream.SetReadPosition (rawIFD.fBlackLevelDeltaVOffset); + + for (j = 0; j < rawIFD.fBlackLevelDeltaVCount; j++) + { + blacks [j] = stream.TagValue_real64 (rawIFD.fBlackLevelDeltaVType); + } + + } + + // Copy white level. + + for (n = 0; n < kMaxSamplesPerPixel; n++) + { + fWhiteLevel [n] = rawIFD.fWhiteLevel [n]; + } + + // Round off black levels. + + RoundBlacks (); + + } + +/*****************************************************************************/ + +void dng_linearization_info::PostParse (dng_host & /* host */, + dng_negative &negative) + { + + if (fActiveArea.IsEmpty ()) + { + + fActiveArea = negative.Stage1Image ()->Bounds (); + + } + + } + +/*****************************************************************************/ + +real64 dng_linearization_info::MaxBlackLevel (uint32 plane) const + { + + uint32 j; + uint32 k; + + // Find maximum value of fBlackDeltaH for each phase of black pattern. + + real64 maxDeltaH [kMaxBlackPattern]; + + memset (maxDeltaH, 0, sizeof (maxDeltaH)); + + for (j = 0; j < fBlackLevelRepeatCols; j++) + { + maxDeltaH [j] = 0.0; + } + + if (fBlackDeltaH.Get ()) + { + + real64 *table = fBlackDeltaH->Buffer_real64 (); + + uint32 entries = fBlackDeltaH->LogicalSize () / (uint32) sizeof (table [0]); + + for (j = 0; j < entries; j++) + { + + DNG_REQUIRE (fBlackLevelRepeatCols > 0, + "Bad fBlackLevelRepeatCols in dng_linearization_info::MaxBlackLevel"); + + real64 &entry = maxDeltaH [j % fBlackLevelRepeatCols]; + + if (j < fBlackLevelRepeatCols) + { + entry = table [j]; + } + else + { + entry = Max_real64 (entry, table [j]); + } + + } + + } + + // Find maximum value of fBlackDeltaV for each phase of black pattern. + + real64 maxDeltaV [kMaxBlackPattern]; + + memset (maxDeltaV, 0, sizeof (maxDeltaV)); + + for (j = 0; j < fBlackLevelRepeatRows; j++) + { + maxDeltaV [j] = 0.0; + } + + if (fBlackDeltaV.Get ()) + { + + real64 *table = fBlackDeltaV->Buffer_real64 (); + + uint32 entries = fBlackDeltaV->LogicalSize () / (uint32) sizeof (table [0]); + + for (j = 0; j < entries; j++) + { + + DNG_REQUIRE (fBlackLevelRepeatRows > 0, + "Bad fBlackLevelRepeatRows in dng_linearization_info::MaxBlackLevel"); + + real64 &entry = maxDeltaV [j % fBlackLevelRepeatRows]; + + if (j < fBlackLevelRepeatRows) + { + entry = table [j]; + } + else + { + entry = Max_real64 (entry, table [j]); + } + + } + + } + + // Now scan the pattern and find the maximum value after row and column + // deltas. + + real64 maxBlack = 0.0; + + for (j = 0; j < fBlackLevelRepeatRows; j++) + { + + for (k = 0; k < fBlackLevelRepeatCols; k++) + { + + real64 black = fBlackLevel [j] [k] [plane]; + + black += maxDeltaH [k]; + black += maxDeltaV [j]; + + if (j == 0 && k == 0) + { + maxBlack = black; + } + else + { + maxBlack = Max_real64 (maxBlack, black); + } + + } + + } + + return maxBlack; + + } + +/*****************************************************************************/ + +void dng_linearization_info::Linearize (dng_host &host, + dng_negative &negative, + const dng_image &srcImage, + dng_image &dstImage) + { + + bool allowPreserveBlackLevels = negative.SupportsPreservedBlackLevels (host); + + if (allowPreserveBlackLevels && + negative.ColorimetricReference () == crSceneReferred && + dstImage.PixelType () == ttShort) + { + + real64 zeroFract = 0.0; + + for (uint32 plane = 0; plane < srcImage.Planes (); plane++) + { + + real64 maxBlackLevel = MaxBlackLevel (plane); + real64 whiteLevel = fWhiteLevel [plane]; + + if (maxBlackLevel > 0.0 && maxBlackLevel < whiteLevel) + { + + zeroFract = Max_real64 (zeroFract, maxBlackLevel / whiteLevel); + + } + + } + + zeroFract = Min_real64 (zeroFract, kMaxStage3BlackLevelNormalized); + + uint16 dstBlackLevel = (uint16) Round_uint32 (65535.0 * zeroFract); + + if (negative.GetMosaicInfo ()) + { + + // If we have a mosaic image that supports non-zero black levels, + // enforce a minimum black level to give the demosaic algorithms + // some "footroom". + + dstBlackLevel = (uint16) Max_uint32 (dstBlackLevel, 0x0404); + + } + + negative.SetStage3BlackLevel (dstBlackLevel); + + } + + bool forceClipBlackLevel = !allowPreserveBlackLevels; + + dng_linearize_image processor (host, + *this, + negative.Stage3BlackLevel (), + forceClipBlackLevel, + srcImage, + dstImage); + + host.PerformAreaTask (processor, + fActiveArea); + + } + +/*****************************************************************************/ + +dng_urational dng_linearization_info::BlackLevel (uint32 row, + uint32 col, + uint32 plane) const + { + + dng_urational r; + + r.Set_real64 (fBlackLevel [row] [col] [plane], fBlackDenom); + + return r; + + } + +/*****************************************************************************/ + +uint32 dng_linearization_info::RowBlackCount () const + { + + if (fBlackDeltaV.Get ()) + { + + return fBlackDeltaV->LogicalSize () >> 3; + + } + + return 0; + + } + +/*****************************************************************************/ + +dng_srational dng_linearization_info::RowBlack (uint32 row) const + { + + if (fBlackDeltaV.Get ()) + { + + dng_srational r; + + r.Set_real64 (fBlackDeltaV->Buffer_real64 () [row], fBlackDenom); + + return r; + + } + + return dng_srational (0, 1); + + } + +/*****************************************************************************/ + +uint32 dng_linearization_info::ColumnBlackCount () const + { + + if (fBlackDeltaH.Get ()) + { + + return fBlackDeltaH->LogicalSize () >> 3; + + } + + return 0; + + } + +/*****************************************************************************/ + +dng_srational dng_linearization_info::ColumnBlack (uint32 col) const + { + + if (fBlackDeltaH.Get ()) + { + + dng_srational r; + + r.Set_real64 (fBlackDeltaH->Buffer_real64 () [col], fBlackDenom); + + return r; + + } + + return dng_srational (0, 1); + + } + +/*****************************************************************************/ diff --git a/dng/dng_linearization_info.h b/dng/dng_linearization_info.h new file mode 100644 index 0000000..a309fc8 --- /dev/null +++ b/dng/dng_linearization_info.h @@ -0,0 +1,161 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Support for linearization table and black level tags. + */ + +/*****************************************************************************/ + +#ifndef __dng_linearization_info__ +#define __dng_linearization_info__ + +/*****************************************************************************/ + +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_memory.h" +#include "dng_rational.h" +#include "dng_rect.h" +#include "dng_sdk_limits.h" + +/*****************************************************************************/ + +/// \brief Class for managing data values related to DNG linearization. +/// +/// See LinearizationTable, BlackLevel, BlackLevelRepeatDim, BlackLevelDeltaH, +/// BlackLevelDeltaV and WhiteLevel tags in the \ref spec_dng "DNG 1.1.0 specification". + +class dng_linearization_info + { + + public: + + /// This rectangle defines the active (non-masked) pixels of the sensor. + /// The order of the rectangle coordinates is: top, left, bottom, right. + + dng_rect fActiveArea; + + /// Number of rectangles in fMaskedArea + + uint32 fMaskedAreaCount; + + /// List of non-overlapping rectangle coordinates of fully masked pixels. + /// Can be optionally used by DNG readers to measure the black encoding level. + /// The order of each rectangle's coordinates is: top, left, bottom, right. + /// If the raw image data has already had its black encoding level subtracted, then this tag should + /// not be used, since the masked pixels are no longer useful. + /// Note that DNG writers are still required to include an estimate and store the black encoding level + /// using the black level DNG tags. Support for the MaskedAreas tag is not required of DNG + /// readers. + + dng_rect fMaskedArea [kMaxMaskedAreas]; + + /// A lookup table that maps stored values into linear values. + /// This tag is typically used to increase compression ratios by storing the raw data in a non-linear, more + /// visually uniform space with fewer total encoding levels. + /// If SamplesPerPixel is not equal to one, e.g. Fuji S3 type sensor, this single table applies to all the samples for each + /// pixel. + + AutoPtr fLinearizationTable; + + /// Actual number of rows in fBlackLevel pattern + + uint32 fBlackLevelRepeatRows; + + /// Actual number of columns in fBlackLevel pattern + + uint32 fBlackLevelRepeatCols; + + /// Repeating pattern of black level deltas fBlackLevelRepeatRows by fBlackLevelRepeatCols in size. + + real64 fBlackLevel [kMaxBlackPattern] [kMaxBlackPattern] [kMaxSamplesPerPixel]; + + /// Memory block of double-precision floating point deltas between baseline black level and a given column's black level + + AutoPtr fBlackDeltaH; + + /// Memory block of double-precision floating point deltas between baseline black level and a given row's black level + + AutoPtr fBlackDeltaV; + + /// Single white level (maximum sensor value) for each sample plane. + + real64 fWhiteLevel [kMaxSamplesPerPixel]; + + protected: + + int32 fBlackDenom; + + public: + + dng_linearization_info (); + + virtual ~dng_linearization_info (); + + void RoundBlacks (); + + virtual void Parse (dng_host &host, + dng_stream &stream, + dng_info &info); + + virtual void PostParse (dng_host &host, + dng_negative &negative); + + /// Compute the maximum black level for a given sample plane taking into account base + /// black level, repeated black level patter, and row/column delta maps. + + real64 MaxBlackLevel (uint32 plane) const; + + /// Convert raw data from in-file format to a true linear image using linearization data from DNG. + /// \param host Used to allocate buffers, check for aborts, and post progress updates. + /// \param negative Used to remember preserved black point. + /// \param srcImage Input pre-linearization RAW samples. + /// \param dstImage Output linearized image. + + virtual void Linearize (dng_host &host, + dng_negative &negative, + const dng_image &srcImage, + dng_image &dstImage); + + /// Compute black level for one coordinate and sample plane in the image. + /// \param row Row to compute black level for. + /// \param col Column to compute black level for. + /// \param plane Sample plane to compute black level for. + + dng_urational BlackLevel (uint32 row, + uint32 col, + uint32 plane) const; + + /// Number of per-row black level deltas in fBlackDeltaV. + + uint32 RowBlackCount () const; + + /// Lookup black level delta for a given row. + /// \param row Row to get black level for. + /// \retval black level for indicated row. + + dng_srational RowBlack (uint32 row) const; + + /// Number of per-column black level deltas in fBlackDeltaV. + + uint32 ColumnBlackCount () const; + + /// Lookup black level delta for a given column. + /// \param col Column to get black level for. + /// \retval black level for indicated column. + + dng_srational ColumnBlack (uint32 col) const; + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_local_string.cpp b/dng/dng_local_string.cpp new file mode 100644 index 0000000..15083a8 --- /dev/null +++ b/dng/dng_local_string.cpp @@ -0,0 +1,191 @@ +/*****************************************************************************/ +// Copyright 2015-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_local_string.h" + +/*****************************************************************************/ + +dng_local_string::dng_local_string () + + : fDefaultText () + , fDictionary () + + { + + } + +/*****************************************************************************/ + +dng_local_string::dng_local_string (const dng_string &s) + + : fDefaultText (s) + , fDictionary () + + { + + } + +/*****************************************************************************/ + +dng_local_string::~dng_local_string () + { + + } + +/*****************************************************************************/ + +void dng_local_string::Clear () + { + + fDefaultText.Clear (); + + fDictionary.clear (); + + } + +/*****************************************************************************/ + +void dng_local_string::SetDefaultText (const dng_string &s) + { + + fDefaultText = s; + + } + +/*****************************************************************************/ + +void dng_local_string::AddTranslation (const dng_string &language, + const dng_string &translation) + { + + dng_string safeLanguage (language); + + safeLanguage.Truncate (255); + + fDictionary.push_back (dictionary_entry (safeLanguage, + translation)); + + } + +/*****************************************************************************/ + +void dng_local_string::Set (const char *s) + { + + dng_string defaultText; + + defaultText.Set (s); + + *this = dng_local_string (defaultText); + + } + +/*****************************************************************************/ + +const dng_string & dng_local_string::LocalText (const dng_string &locale) const + { + + // Pass 1 - try for a match starting with the entire locale string. + + if (locale.Length () >= 5) + { + + for (uint32 index = 0; index < TranslationCount (); index++) + { + + if (Language (index).StartsWith (locale.Get (), false)) + { + + return Translation (index); + + } + + } + + } + + // Pass 2 - try for a language only match. + + if (locale.Length () >= 2) + { + + dng_string languageOnly (locale); + + languageOnly.Truncate (2); + + for (uint32 index = 0; index < TranslationCount (); index++) + { + + if (Language (index).StartsWith (languageOnly.Get (), false)) + { + + return Translation (index); + + } + + } + + } + + // Otherwise use default text. + + return DefaultText (); + + } + +/*****************************************************************************/ + +bool dng_local_string::operator== (const dng_local_string &s) const + { + + if (DefaultText () != s.DefaultText ()) + { + return false; + } + + if (TranslationCount () != s.TranslationCount ()) + { + return false; + } + + for (uint32 index = 0; index < TranslationCount (); index++) + { + + if (Language (index) != s.Language (index)) + { + return false; + } + + if (Translation (index) != s.Translation (index)) + { + return false; + } + + } + + return true; + + } + +/*****************************************************************************/ + +void dng_local_string::Truncate (uint32 maxBytes) + { + + fDefaultText.Truncate (maxBytes); + + for (uint32 index = 0; index < TranslationCount (); index++) + { + + fDictionary [index] . fTranslation . Truncate (maxBytes); + + } + + } + +/*****************************************************************************/ diff --git a/dng/dng_local_string.h b/dng/dng_local_string.h new file mode 100644 index 0000000..b7f0fc6 --- /dev/null +++ b/dng/dng_local_string.h @@ -0,0 +1,119 @@ +/*****************************************************************************/ +// Copyright 2015-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef __dng_local_string__ +#define __dng_local_string__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_string.h" +#include "dng_types.h" + +#include + +/*****************************************************************************/ + +class dng_local_string + { + + private: + + dng_string fDefaultText; + + struct dictionary_entry + { + + dng_string fLanguage; + + dng_string fTranslation; + + dictionary_entry (const dng_string &language, + const dng_string &translation) + + : fLanguage (language) + , fTranslation (translation) + + { + + } + + }; + + std::vector fDictionary; + + public: + + dng_local_string (); + + dng_local_string (const dng_string &s); + + ~dng_local_string (); + + void Clear (); + + void SetDefaultText (const dng_string &s); + + void AddTranslation (const dng_string &language, + const dng_string &translation); + + void Set (const char *s); + + const dng_string & DefaultText () const + { + return fDefaultText; + } + + dng_string & DefaultText () + { + return fDefaultText; + } + + uint32 TranslationCount () const + { + return (uint32) fDictionary.size (); + } + + const dng_string & Language (uint32 index) const + { + return fDictionary [index] . fLanguage; + } + + const dng_string & Translation (uint32 index) const + { + return fDictionary [index] . fTranslation; + } + + const dng_string & LocalText (const dng_string &locale) const; + + bool IsEmpty () const + { + return DefaultText ().IsEmpty (); + } + + bool NotEmpty () const + { + return !IsEmpty (); + } + + bool operator== (const dng_local_string &s) const; + + bool operator!= (const dng_local_string &s) const + { + return !(*this == s); + } + + void Truncate (uint32 maxBytes); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_lossless_jpeg.cpp b/dng/dng_lossless_jpeg.cpp new file mode 100644 index 0000000..fe5a824 --- /dev/null +++ b/dng/dng_lossless_jpeg.cpp @@ -0,0 +1,3844 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +// Lossless JPEG code adapted from: + +/* Copyright (C) 1991, 1992, Thomas G. Lane. + * Part of the Independent JPEG Group's software. + * See the file Copyright for more details. + * + * Copyright (c) 1993 Brian C. Smith, The Regents of the University + * of California + * All rights reserved. + * + * Copyright (c) 1994 Kongji Huang and Brian C. Smith. + * Cornell University + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL CORNELL UNIVERSITY BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF CORNELL + * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * CORNELL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND CORNELL UNIVERSITY HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/*****************************************************************************/ + +#include "dng_lossless_jpeg.h" + +#include "dng_assertions.h" +#include "dng_exceptions.h" +#include "dng_memory.h" +#include "dng_stream.h" +#include "dng_tag_codes.h" + +/*****************************************************************************/ + +// This module contains routines that should be as fast as possible, even +// at the expense of slight code size increases. + +#include "dng_fast_module.h" + +/*****************************************************************************/ + +// The qSupportCanon_sRAW stuff not actually required for DNG support, but +// only included to allow this code to be used on Canon sRAW files. + +#ifndef qSupportCanon_sRAW +#define qSupportCanon_sRAW 1 +#endif + +// The qSupportHasselblad_3FR stuff not actually required for DNG support, but +// only included to allow this code to be used on Hasselblad 3FR files. + +#ifndef qSupportHasselblad_3FR +#define qSupportHasselblad_3FR 1 +#endif + +/*****************************************************************************/ + +/* + * One of the following structures is created for each huffman coding + * table. We use the same structure for encoding and decoding, so there + * may be some extra fields for encoding that aren't used in the decoding + * and vice-versa. + */ + +struct HuffmanTable + { + + /* + * These two fields directly represent the contents of a JPEG DHT + * marker + */ + uint8 bits[17]; + uint8 huffval[256]; + + /* + * The remaining fields are computed from the above to allow more + * efficient coding and decoding. These fields should be considered + * private to the Huffman compression & decompression modules. + */ + + uint16 mincode[17]; + int32 maxcode[18]; + int16 valptr[17]; + int32 numbits[256]; + int32 value[256]; + + uint16 ehufco[256]; + int8 ehufsi[256]; + + }; + +/*****************************************************************************/ + +// Computes the derived fields in the Huffman table structure. + +static void FixHuffTbl (HuffmanTable *htbl) + { + + int32 l; + int32 i; + + const uint32 bitMask [] = + { + 0xffffffff, 0x7fffffff, 0x3fffffff, 0x1fffffff, + 0x0fffffff, 0x07ffffff, 0x03ffffff, 0x01ffffff, + 0x00ffffff, 0x007fffff, 0x003fffff, 0x001fffff, + 0x000fffff, 0x0007ffff, 0x0003ffff, 0x0001ffff, + 0x0000ffff, 0x00007fff, 0x00003fff, 0x00001fff, + 0x00000fff, 0x000007ff, 0x000003ff, 0x000001ff, + 0x000000ff, 0x0000007f, 0x0000003f, 0x0000001f, + 0x0000000f, 0x00000007, 0x00000003, 0x00000001 + }; + + // Figure C.1: make table of Huffman code length for each symbol + // Note that this is in code-length order. + + int8 huffsize [257]; + + int32 p = 0; + + for (l = 1; l <= 16; l++) + { + + for (i = 1; i <= (int32) htbl->bits [l]; i++) + huffsize [p++] = (int8) l; + + } + + huffsize [p] = 0; + + int32 lastp = p; + + // Figure C.2: generate the codes themselves + // Note that this is in code-length order. + + uint16 huffcode [257]; + + uint16 code = 0; + + int32 si = huffsize [0]; + + p = 0; + + while (huffsize [p]) + { + + while (((int32) huffsize [p]) == si) + { + huffcode [p++] = code; + code++; + } + + code <<= 1; + + si++; + + } + + // Figure C.3: generate encoding tables + // These are code and size indexed by symbol value + // Set any codeless symbols to have code length 0; this allows + // EmitBits to detect any attempt to emit such symbols. + + memset (htbl->ehufsi, 0, sizeof (htbl->ehufsi)); + + for (p = 0; p < lastp; p++) + { + + htbl->ehufco [htbl->huffval [p]] = huffcode [p]; + htbl->ehufsi [htbl->huffval [p]] = huffsize [p]; + + } + + // Figure F.15: generate decoding tables + + p = 0; + + for (l = 1; l <= 16; l++) + { + + if (htbl->bits [l]) + { + + htbl->valptr [l] = (int16) p; + htbl->mincode [l] = huffcode [p]; + + p += htbl->bits [l]; + + htbl->maxcode [l] = huffcode [p - 1]; + + } + + else + { + htbl->maxcode [l] = -1; + } + + } + + // We put in this value to ensure HuffDecode terminates. + + htbl->maxcode[17] = 0xFFFFFL; + + // Build the numbits, value lookup tables. + // These table allow us to gather 8 bits from the bits stream, + // and immediately lookup the size and value of the huffman codes. + // If size is zero, it means that more than 8 bits are in the huffman + // code (this happens about 3-4% of the time). + + memset (htbl->numbits, 0, sizeof (htbl->numbits)); + + for (p = 0; p < lastp; p++) + { + + int32 size = huffsize [p]; + + if (size <= 8) + { + + int32 value = htbl->huffval [p]; + + code = huffcode [p]; + + int32 ll = code << (8 -size); + + int32 ul = (size < 8 ? ll | bitMask [24 + size] + : ll); + + if (ul >= static_cast (sizeof(htbl->numbits) / sizeof (htbl->numbits [0])) || + ul >= static_cast (sizeof(htbl->value ) / sizeof (htbl->value [0]))) + { + ThrowBadFormat (); + } + + for (i = ll; i <= ul; i++) + { + htbl->numbits [i] = size; + htbl->value [i] = value; + } + + } + + } + + } + +/*****************************************************************************/ + +/* + * The following structure stores basic information about one component. + */ + +struct JpegComponentInfo + { + + /* + * These values are fixed over the whole image. + * They are read from the SOF marker. + */ + int16 componentId; /* identifier for this component (0..255) */ + int16 componentIndex; /* its index in SOF or cPtr->compInfo[] */ + + /* + * Downsampling is not normally used in lossless JPEG, although + * it is permitted by the JPEG standard (DIS). We set all sampling + * factors to 1 in this program. + */ + int16 hSampFactor; /* horizontal sampling factor */ + int16 vSampFactor; /* vertical sampling factor */ + + /* + * Huffman table selector (0..3). The value may vary + * between scans. It is read from the SOS marker. + */ + int16 dcTblNo; + + }; + +/* + * One of the following structures is used to pass around the + * decompression information. + */ + +struct DecompressInfo + { + + /* + * Image width, height, and image data precision (bits/sample) + * These fields are set by ReadFileHeader or ReadScanHeader + */ + int32 imageWidth; + int32 imageHeight; + int32 dataPrecision; + + /* + * compInfo[i] describes component that appears i'th in SOF + * numComponents is the # of color components in JPEG image. + */ + JpegComponentInfo *compInfo; + int16 numComponents; + + /* + * *curCompInfo[i] describes component that appears i'th in SOS. + * compsInScan is the # of color components in current scan. + */ + JpegComponentInfo *curCompInfo[4]; + int16 compsInScan; + + /* + * MCUmembership[i] indexes the i'th component of MCU into the + * curCompInfo array. + */ + int16 MCUmembership[10]; + + /* + * ptrs to Huffman coding tables, or NULL if not defined + */ + HuffmanTable *dcHuffTblPtrs[4]; + + /* + * prediction selection value (PSV) and point transform parameter (Pt) + */ + int32 Ss; + int32 Pt; + + /* + * In lossless JPEG, restart interval shall be an integer + * multiple of the number of MCU in a MCU row. + */ + int32 restartInterval;/* MCUs per restart interval, 0 = no restart */ + int32 restartInRows; /*if > 0, MCU rows per restart interval; 0 = no restart*/ + + /* + * these fields are private data for the entropy decoder + */ + int32 restartRowsToGo; /* MCUs rows left in this restart interval */ + int16 nextRestartNum; /* # of next RSTn marker (0..7) */ + + }; + +/*****************************************************************************/ + +// An MCU (minimum coding unit) is an array of samples. + +typedef uint16 ComponentType; // the type of image components + +typedef ComponentType *MCU; // MCU - array of samples + +/*****************************************************************************/ + +class dng_lossless_decoder: private dng_uncopyable + { + + private: + + dng_stream *fStream; // Input data. + + dng_spooler *fSpooler; // Output data. + + bool fBug16; // Decode data with the "16-bit" bug. + + dng_memory_data huffmanBuffer [4]; + + dng_memory_data compInfoBuffer; + + DecompressInfo info; + + dng_memory_data mcuBuffer1; + dng_memory_data mcuBuffer2; + dng_memory_data mcuBuffer3; + dng_memory_data mcuBuffer4; + + MCU *mcuROW1; + MCU *mcuROW2; + + uint64 getBuffer; // current bit-extraction buffer + int32 bitsLeft; // # of unused bits in it + + #if qSupportHasselblad_3FR + bool fHasselblad3FR; + #endif + + public: + + dng_lossless_decoder (dng_stream *stream, + dng_spooler *spooler, + bool bug16); + + void StartRead (uint32 &imageWidth, + uint32 &imageHeight, + uint32 &imageChannels); + + void FinishRead (); + + #if qSupportHasselblad_3FR + + bool IsHasselblad3FR () + { + return fHasselblad3FR; + } + + #endif + + private: + + uint8 GetJpegChar () + { + return fStream->Get_uint8 (); + } + + void UnGetJpegChar () + { + fStream->SetReadPosition (fStream->Position () - 1); + } + + uint16 Get2bytes (); + + void SkipVariable (); + + void GetDht (); + + void GetDri (); + + void GetApp0 (); + + void GetSof (int32 code); + + void GetSos (); + + void GetSoi (); + + int32 NextMarker (); + + JpegMarker ProcessTables (); + + void ReadFileHeader (); + + int32 ReadScanHeader (); + + void DecoderStructInit (); + + void HuffDecoderInit (); + + void ProcessRestart (); + + int32 QuickPredict (int32 col, + int32 curComp, + MCU *curRowBuf, + MCU *prevRowBuf); + + void FillBitBuffer (int32 nbits); + + int32 show_bits8 (); + + void flush_bits (int32 nbits); + + int32 get_bits (int32 nbits); + + int32 get_bit (); + + int32 HuffDecode (HuffmanTable *htbl); + + void HuffExtend (int32 &x, int32 s); + + void PmPutRow (MCU *buf, + int32 numComp, + int32 numCol, + int32 row); + + void DecodeFirstRow (MCU *curRowBuf); + + void DecodeImage (); + + }; + +/*****************************************************************************/ + +dng_lossless_decoder::dng_lossless_decoder (dng_stream *stream, + dng_spooler *spooler, + bool bug16) + + : fStream (stream ) + , fSpooler (spooler) + , fBug16 (bug16 ) + + , compInfoBuffer () + , info () + , mcuBuffer1 () + , mcuBuffer2 () + , mcuBuffer3 () + , mcuBuffer4 () + , mcuROW1 (NULL) + , mcuROW2 (NULL) + , getBuffer (0) + , bitsLeft (0) + + #if qSupportHasselblad_3FR + , fHasselblad3FR (false) + #endif + + { + + memset (&info, 0, sizeof (info)); + + } + +/*****************************************************************************/ + +uint16 dng_lossless_decoder::Get2bytes () + { + + uint16 a = GetJpegChar (); + + return (uint16) ((a << 8) + GetJpegChar ()); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * SkipVariable -- + * + * Skip over an unknown or uninteresting variable-length marker + * + * Results: + * None. + * + * Side effects: + * Bitstream is parsed over marker. + * + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::SkipVariable () + { + + uint32 length = Get2bytes () - 2; + + fStream->Skip (length); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * GetDht -- + * + * Process a DHT marker + * + * Results: + * None + * + * Side effects: + * A huffman table is read. + * Exits on error. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::GetDht () + { + + int32 length = Get2bytes () - 2; + + while (length > 0) + { + + int32 index = GetJpegChar (); + + if (index < 0 || index >= 4) + { + ThrowBadFormat (); + } + + HuffmanTable *&htblptr = info.dcHuffTblPtrs [index]; + + if (htblptr == NULL) + { + + huffmanBuffer [index] . Allocate (sizeof (HuffmanTable)); + + htblptr = (HuffmanTable *) huffmanBuffer [index] . Buffer (); + + } + + htblptr->bits [0] = 0; + + int32 count = 0; + + for (int32 i = 1; i <= 16; i++) + { + + htblptr->bits [i] = GetJpegChar (); + + count += htblptr->bits [i]; + + } + + if (count > 256) + { + ThrowBadFormat (); + } + + for (int32 j = 0; j < count; j++) + { + + htblptr->huffval [j] = GetJpegChar (); + + } + + length -= 1 + 16 + count; + + } + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * GetDri -- + * + * Process a DRI marker + * + * Results: + * None + * + * Side effects: + * Exits on error. + * Bitstream is parsed. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::GetDri () + { + + if (Get2bytes () != 4) + { + ThrowBadFormat (); + } + + info.restartInterval = Get2bytes (); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * GetApp0 -- + * + * Process an APP0 marker. + * + * Results: + * None + * + * Side effects: + * Bitstream is parsed + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::GetApp0 () + { + + SkipVariable (); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * GetSof -- + * + * Process a SOFn marker + * + * Results: + * None. + * + * Side effects: + * Bitstream is parsed + * Exits on error + * info structure is filled in + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::GetSof (int32 /*code*/) + { + + int32 length = Get2bytes (); + + info.dataPrecision = GetJpegChar (); + info.imageHeight = Get2bytes (); + info.imageWidth = Get2bytes (); + info.numComponents = GetJpegChar (); + + // We don't support files in which the image height is initially + // specified as 0 and is later redefined by DNL. As long as we + // have to check that, might as well have a general sanity check. + + if ((info.imageHeight <= 0) || + (info.imageWidth <= 0) || + (info.numComponents <= 0)) + { + ThrowBadFormat (); + } + + // Lossless JPEG specifies data precision to be from 2 to 16 bits/sample. + + const int32 MinPrecisionBits = 2; + const int32 MaxPrecisionBits = 16; + + if ((info.dataPrecision < MinPrecisionBits) || + (info.dataPrecision > MaxPrecisionBits)) + { + ThrowBadFormat (); + } + + // Check length of tag. + + if (length != (info.numComponents * 3 + 8)) + { + ThrowBadFormat (); + } + + // Allocate per component info. + + // We can cast info.numComponents to a uint32 because the check above + // guarantees that it cannot be negative. + + compInfoBuffer.Allocate (static_cast (info.numComponents), + sizeof (JpegComponentInfo)); + + info.compInfo = (JpegComponentInfo *) compInfoBuffer.Buffer (); + + // Read in the per compent info. + + for (int32 ci = 0; ci < info.numComponents; ci++) + { + + JpegComponentInfo *compptr = &info.compInfo [ci]; + + compptr->componentIndex = (int16) ci; + + compptr->componentId = GetJpegChar (); + + int32 c = GetJpegChar (); + + compptr->hSampFactor = (int16) ((c >> 4) & 15); + compptr->vSampFactor = (int16) ((c ) & 15); + + (void) GetJpegChar (); /* skip Tq */ + + } + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * GetSos -- + * + * Process a SOS marker + * + * Results: + * None. + * + * Side effects: + * Bitstream is parsed. + * Exits on error. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::GetSos () + { + + int32 length = Get2bytes (); + + // Get the number of image components. + + int32 n = GetJpegChar (); + info.compsInScan = (int16) n; + + // Check length. + + length -= 3; + + if (length != (n * 2 + 3) || n < 1 || n > 4) + { + ThrowBadFormat (); + } + + // Find index and huffman table for each component. + + for (int32 i = 0; i < n; i++) + { + + int32 cc = GetJpegChar (); + int32 c = GetJpegChar (); + + int32 ci; + + for (ci = 0; ci < info.numComponents; ci++) + { + + if (cc == info.compInfo[ci].componentId) + { + break; + } + + } + + if (ci >= info.numComponents) + { + ThrowBadFormat (); + } + + JpegComponentInfo *compptr = &info.compInfo [ci]; + + info.curCompInfo [i] = compptr; + + compptr->dcTblNo = (int16) ((c >> 4) & 15); + + } + + // Get the PSV, skip Se, and get the point transform parameter. + + info.Ss = GetJpegChar (); + + (void) GetJpegChar (); + + info.Pt = GetJpegChar () & 0x0F; + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * GetSoi -- + * + * Process an SOI marker + * + * Results: + * None. + * + * Side effects: + * Bitstream is parsed. + * Exits on error. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::GetSoi () + { + + // Reset all parameters that are defined to be reset by SOI + + info.restartInterval = 0; + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * NextMarker -- + * + * Find the next JPEG marker Note that the output might not + * be a valid marker code but it will never be 0 or FF + * + * Results: + * The marker found. + * + * Side effects: + * Bitstream is parsed. + * + *-------------------------------------------------------------- + */ + +int32 dng_lossless_decoder::NextMarker () + { + + int32 c; + + do + { + + // skip any non-FF bytes + + do + { + c = GetJpegChar (); + } + while (c != 0xFF); + + // skip any duplicate FFs, since extra FFs are legal + + do + { + c = GetJpegChar(); + } + while (c == 0xFF); + + } + while (c == 0); // repeat if it was a stuffed FF/00 + + return c; + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * ProcessTables -- + * + * Scan and process JPEG markers that can appear in any order + * Return when an SOI, EOI, SOFn, or SOS is found + * + * Results: + * The marker found. + * + * Side effects: + * Bitstream is parsed. + * + *-------------------------------------------------------------- + */ + +JpegMarker dng_lossless_decoder::ProcessTables () + { + + while (true) + { + + int32 c = NextMarker (); + + switch (c) + { + + case M_SOF0: + case M_SOF1: + case M_SOF2: + case M_SOF3: + case M_SOF5: + case M_SOF6: + case M_SOF7: + case M_JPG: + case M_SOF9: + case M_SOF10: + case M_SOF11: + case M_SOF13: + case M_SOF14: + case M_SOF15: + case M_SOI: + case M_EOI: + case M_SOS: + return (JpegMarker) c; + + case M_DHT: + GetDht (); + break; + + case M_DQT: + break; + + case M_DRI: + GetDri (); + break; + + case M_APP0: + GetApp0 (); + break; + + case M_RST0: // these are all parameterless + case M_RST1: + case M_RST2: + case M_RST3: + case M_RST4: + case M_RST5: + case M_RST6: + case M_RST7: + case M_TEM: + break; + + default: // must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn + SkipVariable (); + break; + + } + + } + + return M_ERROR; + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * ReadFileHeader -- + * + * Initialize and read the stream header (everything through + * the SOF marker). + * + * Results: + * None + * + * Side effects: + * Exit on error. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::ReadFileHeader () + { + + // Demand an SOI marker at the start of the stream --- otherwise it's + // probably not a JPEG stream at all. + + int32 c = GetJpegChar (); + int32 c2 = GetJpegChar (); + + if ((c != 0xFF) || (c2 != M_SOI)) + { + ThrowBadFormat (); + } + + // OK, process SOI + + GetSoi (); + + // Process markers until SOF + + c = ProcessTables (); + + switch (c) + { + + case M_SOF0: + case M_SOF1: + case M_SOF3: + GetSof (c); + break; + + default: + ThrowBadFormat (); + break; + + } + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * ReadScanHeader -- + * + * Read the start of a scan (everything through the SOS marker). + * + * Results: + * 1 if find SOS, 0 if find EOI + * + * Side effects: + * Bitstream is parsed, may exit on errors. + * + *-------------------------------------------------------------- + */ + +int32 dng_lossless_decoder::ReadScanHeader () + { + + // Process markers until SOS or EOI + + int32 c = ProcessTables (); + + switch (c) + { + + case M_SOS: + GetSos (); + return 1; + + case M_EOI: + return 0; + + default: + ThrowBadFormat (); + break; + + } + + return 0; + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * DecoderStructInit -- + * + * Initalize the rest of the fields in the decompression + * structure. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::DecoderStructInit () + { + + int32 ci; + + #if qSupportCanon_sRAW + + bool canon_sRAW = (info.numComponents == 3) && + (info.compInfo [0].hSampFactor == 2) && + (info.compInfo [1].hSampFactor == 1) && + (info.compInfo [2].hSampFactor == 1) && + (info.compInfo [0].vSampFactor == 1) && + (info.compInfo [1].vSampFactor == 1) && + (info.compInfo [2].vSampFactor == 1) && + (info.dataPrecision == 15) && + (info.Ss == 1) && + ((info.imageWidth & 1) == 0); + + bool canon_sRAW2 = (info.numComponents == 3) && + (info.compInfo [0].hSampFactor == 2) && + (info.compInfo [1].hSampFactor == 1) && + (info.compInfo [2].hSampFactor == 1) && + (info.compInfo [0].vSampFactor == 2) && + (info.compInfo [1].vSampFactor == 1) && + (info.compInfo [2].vSampFactor == 1) && + (info.dataPrecision == 15) && + (info.Ss == 1) && + ((info.imageWidth & 1) == 0) && + ((info.imageHeight & 1) == 0); + + if (!canon_sRAW && !canon_sRAW2) + + #endif + + { + + // Check sampling factor validity. + + for (ci = 0; ci < info.numComponents; ci++) + { + + JpegComponentInfo *compPtr = &info.compInfo [ci]; + + if (compPtr->hSampFactor != 1 || + compPtr->vSampFactor != 1) + { + ThrowBadFormat (); + } + + } + + } + + // Prepare array describing MCU composition. + + if (info.compsInScan < 0 || + info.compsInScan > 4) + { + ThrowBadFormat (); + } + + for (ci = 0; ci < info.compsInScan; ci++) + { + info.MCUmembership [ci] = (int16) ci; + } + + // Initialize mucROW1 and mcuROW2 which buffer two rows of + // pixels for predictor calculation. + + // This multiplication cannot overflow because info.compsInScan is + // guaranteed to be between 0 and 4 inclusive (see checks above). + + int32 mcuSize = info.compsInScan * (uint32) sizeof (ComponentType); + + mcuBuffer1.Allocate (info.imageWidth, sizeof (MCU)); + mcuBuffer2.Allocate (info.imageWidth, sizeof (MCU)); + + mcuROW1 = (MCU *) mcuBuffer1.Buffer (); + mcuROW2 = (MCU *) mcuBuffer2.Buffer (); + + mcuBuffer3.Allocate (info.imageWidth, mcuSize); + mcuBuffer4.Allocate (info.imageWidth, mcuSize); + + mcuROW1 [0] = (ComponentType *) mcuBuffer3.Buffer (); + mcuROW2 [0] = (ComponentType *) mcuBuffer4.Buffer (); + + for (int32 j = 1; j < info.imageWidth; j++) + { + + mcuROW1 [j] = mcuROW1 [j - 1] + info.compsInScan; + mcuROW2 [j] = mcuROW2 [j - 1] + info.compsInScan; + + } + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * HuffDecoderInit -- + * + * Initialize for a Huffman-compressed scan. + * This is invoked after reading the SOS marker. + * + * Results: + * None + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::HuffDecoderInit () + { + + // Initialize bit parser state + + getBuffer = 0; + bitsLeft = 0; + + // Prepare Huffman tables. + + for (int16 ci = 0; ci < info.compsInScan; ci++) + { + + JpegComponentInfo *compptr = info.curCompInfo [ci]; + + // Make sure requested tables are present + + if (compptr->dcTblNo < 0 || compptr->dcTblNo > 3) + { + ThrowBadFormat (); + } + + if (info.dcHuffTblPtrs [compptr->dcTblNo] == NULL) + { + ThrowBadFormat (); + } + + // Compute derived values for Huffman tables. + // We may do this more than once for same table, but it's not a + // big deal + + FixHuffTbl (info.dcHuffTblPtrs [compptr->dcTblNo]); + + } + + // Initialize restart stuff + + info.restartInRows = info.restartInterval / info.imageWidth; + info.restartRowsToGo = info.restartInRows; + info.nextRestartNum = 0; + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * ProcessRestart -- + * + * Check for a restart marker & resynchronize decoder. + * + * Results: + * None. + * + * Side effects: + * BitStream is parsed, bit buffer is reset, etc. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::ProcessRestart () + { + + // Throw away and unused odd bits in the bit buffer. + + fStream->SetReadPosition (fStream->Position () - bitsLeft / 8); + + bitsLeft = 0; + getBuffer = 0; + + // Scan for next JPEG marker + + int32 c; + + do + { + + // skip any non-FF bytes + + do + { + c = GetJpegChar (); + } + while (c != 0xFF); + + // skip any duplicate FFs + + do + { + c = GetJpegChar (); + } + while (c == 0xFF); + + } + while (c == 0); // repeat if it was a stuffed FF/00 + + // Verify correct restart code. + + if (c != (M_RST0 + info.nextRestartNum)) + { + ThrowBadFormat (); + } + + // Update restart state. + + info.restartRowsToGo = info.restartInRows; + info.nextRestartNum = (info.nextRestartNum + 1) & 7; + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * QuickPredict -- + * + * Calculate the predictor for sample curRowBuf[col][curComp]. + * It does not handle the special cases at image edges, such + * as first row and first column of a scan. We put the special + * case checkings outside so that the computations in main + * loop can be simpler. This has enhenced the performance + * significantly. + * + * Results: + * predictor is passed out. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +inline int32 dng_lossless_decoder::QuickPredict (int32 col, + int32 curComp, + MCU *curRowBuf, + MCU *prevRowBuf) + { + + int32 diag = prevRowBuf [col - 1] [curComp]; + int32 upper = prevRowBuf [col ] [curComp]; + int32 left = curRowBuf [col - 1] [curComp]; + + switch (info.Ss) + { + + case 0: + return 0; + + case 1: + return left; + + case 2: + return upper; + + case 3: + return diag; + + case 4: + return left + upper - diag; + + case 5: + return left + ((upper - diag) >> 1); + + case 6: + return upper + ((left - diag) >> 1); + + case 7: + return (left + upper) >> 1; + + default: + { + ThrowBadFormat (); + return 0; + } + + } + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * FillBitBuffer -- + * + * Load up the bit buffer with at least nbits + * Process any stuffed bytes at this time. + * + * Results: + * None + * + * Side effects: + * The bitwise global variables are updated. + * + *-------------------------------------------------------------- + */ + +inline void dng_lossless_decoder::FillBitBuffer (int32 nbits) + { + + const int32 kMinGetBits = sizeof (uint32) * 8 - 7; + + #if qSupportHasselblad_3FR + + if (fHasselblad3FR) + { + + while (bitsLeft < kMinGetBits) + { + + int32 c0 = 0; + int32 c1 = 0; + int32 c2 = 0; + int32 c3 = 0; + + try + { + c0 = GetJpegChar (); + c1 = GetJpegChar (); + c2 = GetJpegChar (); + c3 = GetJpegChar (); + } + + catch (dng_exception &except) + { + + // If we got any exception other than EOF, rethrow. + + if (except.ErrorCode () != dng_error_end_of_file) + { + throw except; + } + + // Some Hasselblad files now use the JPEG end of image marker. + // If we DIDN'T hit that, rethrow. + // This sequence also sometimes occurs in the image data, so + // we can't simply check for it and exit - we need to wait until + // we throw the EOF and then look to see if we had it. + + // Look for the marker in c1 and c2 as well. + // (if we get it in c2 and c3, we won't throw.) + + if (!((c0 == 0xFF && c1 == 0xD9) || + (c1 == 0xFF && c2 == 0xD9))) + { + throw except; + } + + // Swallow the case where we hit EOF with the JPEG EOI marker. + + } + + getBuffer = (getBuffer << 8) | c3; + getBuffer = (getBuffer << 8) | c2; + getBuffer = (getBuffer << 8) | c1; + getBuffer = (getBuffer << 8) | c0; + + bitsLeft += 32; + + } + + return; + + } + + #endif + + while (bitsLeft < kMinGetBits) + { + + int32 c = GetJpegChar (); + + // If it's 0xFF, check and discard stuffed zero byte + + if (c == 0xFF) + { + + int32 c2 = GetJpegChar (); + + if (c2 != 0) + { + + // Oops, it's actually a marker indicating end of + // compressed data. Better put it back for use later. + + UnGetJpegChar (); + UnGetJpegChar (); + + // There should be enough bits still left in the data + // segment; if so, just break out of the while loop. + + if (bitsLeft >= nbits) + break; + + // Uh-oh. Corrupted data: stuff zeroes into the data + // stream, since this sometimes occurs when we are on the + // last show_bits8 during decoding of the Huffman + // segment. + + c = 0; + + } + + } + + getBuffer = (getBuffer << 8) | c; + + bitsLeft += 8; + + } + + } + +/*****************************************************************************/ + +inline int32 dng_lossless_decoder::show_bits8 () + { + + if (bitsLeft < 8) + FillBitBuffer (8); + + return (int32) ((getBuffer >> (bitsLeft - 8)) & 0xff); + + } + +/*****************************************************************************/ + +inline void dng_lossless_decoder::flush_bits (int32 nbits) + { + + bitsLeft -= nbits; + + } + +/*****************************************************************************/ + +inline int32 dng_lossless_decoder::get_bits (int32 nbits) + { + + if (nbits > 16) + { + ThrowBadFormat (); + } + + if (bitsLeft < nbits) + FillBitBuffer (nbits); + + return (int32) ((getBuffer >> (bitsLeft -= nbits)) & (0x0FFFF >> (16 - nbits))); + + } + +/*****************************************************************************/ + +inline int32 dng_lossless_decoder::get_bit () + { + + if (!bitsLeft) + FillBitBuffer (1); + + return (int32) ((getBuffer >> (--bitsLeft)) & 1); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * HuffDecode -- + * + * Taken from Figure F.16: extract next coded symbol from + * input stream. This should becode a macro. + * + * Results: + * Next coded symbol + * + * Side effects: + * Bitstream is parsed. + * + *-------------------------------------------------------------- + */ + +inline int32 dng_lossless_decoder::HuffDecode (HuffmanTable *htbl) + { + + // If the huffman code is less than 8 bits, we can use the fast + // table lookup to get its value. It's more than 8 bits about + // 3-4% of the time. + + int32 code = show_bits8 (); + + if (htbl->numbits [code]) + { + + flush_bits (htbl->numbits [code]); + + return htbl->value [code]; + + } + + else + { + + flush_bits (8); + + int32 l = 8; + + while (code > htbl->maxcode [l]) + { + code = (code << 1) | get_bit (); + l++; + } + + // With garbage input we may reach the sentinel value l = 17. + + if (l > 16) + { + return 0; // fake a zero as the safest result + } + else + { + return htbl->huffval [htbl->valptr [l] + + ((int32) (code - htbl->mincode [l]))]; + } + + } + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * HuffExtend -- + * + * Code and table for Figure F.12: extend sign bit + * + * Results: + * The extended value. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +DNG_ATTRIB_NO_SANITIZE("undefined") +inline void dng_lossless_decoder::HuffExtend (int32 &x, int32 s) + { + + if (x < (0x08000 >> (16 - s))) + { + x += (-1 << s) + 1; + } + + } + +/*****************************************************************************/ + +// Called from DecodeImage () to write one row. + +void dng_lossless_decoder::PmPutRow (MCU *buf, + int32 numComp, + int32 numCol, + int32 /* row */) + { + + uint16 *sPtr = &buf [0] [0]; + + uint32 pixels = numCol * numComp; + + fSpooler->Spool (sPtr, pixels * (uint32) sizeof (uint16)); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * DecodeFirstRow -- + * + * Decode the first raster line of samples at the start of + * the scan and at the beginning of each restart interval. + * This includes modifying the component value so the real + * value, not the difference is returned. + * + * Results: + * None. + * + * Side effects: + * Bitstream is parsed. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::DecodeFirstRow (MCU *curRowBuf) + { + + int32 compsInScan = info.compsInScan; + + // Process the first column in the row. + + for (int32 curComp = 0; curComp < compsInScan; curComp++) + { + + int32 ci = info.MCUmembership [curComp]; + + JpegComponentInfo *compptr = info.curCompInfo [ci]; + + HuffmanTable *dctbl = info.dcHuffTblPtrs [compptr->dcTblNo]; + + // Section F.2.2.1: decode the difference + + int32 d = 0; + + int32 s = HuffDecode (dctbl); + + if (s) + { + + if (s == 16 && !fBug16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + // Add the predictor to the difference. + + int32 Pr = info.dataPrecision; + int32 Pt = info.Pt; + + curRowBuf [0] [curComp] = (ComponentType) (d + (1 << (Pr-Pt-1))); + + } + + // Process the rest of the row. + + int32 numCOL = info.imageWidth; + + for (int32 col = 1; col < numCOL; col++) + { + + for (int32 curComp = 0; curComp < compsInScan; curComp++) + { + + int32 ci = info.MCUmembership [curComp]; + + JpegComponentInfo *compptr = info.curCompInfo [ci]; + + HuffmanTable *dctbl = info.dcHuffTblPtrs [compptr->dcTblNo]; + + // Section F.2.2.1: decode the difference + + int32 d = 0; + + int32 s = HuffDecode (dctbl); + + if (s) + { + + if (s == 16 && !fBug16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + // Add the predictor to the difference. + + curRowBuf [col] [curComp] = (ComponentType) (d + curRowBuf [col-1] [curComp]); + + } + + } + + // Update the restart counter + + if (info.restartInRows) + { + info.restartRowsToGo--; + } + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * DecodeImage -- + * + * Decode the input stream. This includes modifying + * the component value so the real value, not the + * difference is returned. + * + * Results: + * None. + * + * Side effects: + * Bitstream is parsed. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::DecodeImage () + { + + #define swap(type,a,b) {type c; c=(a); (a)=(b); (b)=c;} + + int32 numCOL = info.imageWidth; + int32 numROW = info.imageHeight; + int32 compsInScan = info.compsInScan; + + // Precompute the decoding table for each table. + + HuffmanTable *ht [4]; + + memset (ht, 0, sizeof (ht)); + + for (int32 curComp = 0; curComp < compsInScan; curComp++) + { + + int32 ci = info.MCUmembership [curComp]; + + JpegComponentInfo *compptr = info.curCompInfo [ci]; + + ht [curComp] = info.dcHuffTblPtrs [compptr->dcTblNo]; + + } + + MCU *prevRowBuf = mcuROW1; + MCU *curRowBuf = mcuROW2; + + #if qSupportCanon_sRAW + + // Canon sRAW support + + if (info.compInfo [0].hSampFactor == 2 && + info.compInfo [0].vSampFactor == 1) + { + + for (int32 row = 0; row < numROW; row++) + { + + // Initialize predictors. + + int32 p0; + int32 p1; + int32 p2; + + if (row == 0) + { + p0 = 1 << 14; + p1 = 1 << 14; + p2 = 1 << 14; + } + + else + { + p0 = prevRowBuf [0] [0]; + p1 = prevRowBuf [0] [1]; + p2 = prevRowBuf [0] [2]; + } + + for (int32 col = 0; col < numCOL; col += 2) + { + + // Read first luminance component. + + { + + int32 d = 0; + + int32 s = HuffDecode (ht [0]); + + if (s) + { + + if (s == 16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + p0 += d; + + curRowBuf [col] [0] = (ComponentType) p0; + + } + + // Read second luminance component. + + { + + int32 d = 0; + + int32 s = HuffDecode (ht [0]); + + if (s) + { + + if (s == 16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + p0 += d; + + curRowBuf [col + 1] [0] = (ComponentType) p0; + + } + + // Read first chroma component. + + { + + int32 d = 0; + + int32 s = HuffDecode (ht [1]); + + if (s) + { + + if (s == 16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + p1 += d; + + curRowBuf [col ] [1] = (ComponentType) p1; + curRowBuf [col + 1] [1] = (ComponentType) p1; + + } + + // Read second chroma component. + + { + + int32 d = 0; + + int32 s = HuffDecode (ht [2]); + + if (s) + { + + if (s == 16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + p2 += d; + + curRowBuf [col ] [2] = (ComponentType) p2; + curRowBuf [col + 1] [2] = (ComponentType) p2; + + } + + } + + PmPutRow (curRowBuf, compsInScan, numCOL, row); + + swap (MCU *, prevRowBuf, curRowBuf); + + } + + return; + + } + + if (info.compInfo [0].hSampFactor == 2 && + info.compInfo [0].vSampFactor == 2) + { + + for (int32 row = 0; row < numROW; row += 2) + { + + // Initialize predictors. + + int32 p0; + int32 p1; + int32 p2; + + if (row == 0) + { + p0 = 1 << 14; + p1 = 1 << 14; + p2 = 1 << 14; + } + + else + { + p0 = prevRowBuf [0] [0]; + p1 = prevRowBuf [0] [1]; + p2 = prevRowBuf [0] [2]; + } + + for (int32 col = 0; col < numCOL; col += 2) + { + + // Read first luminance component. + + { + + int32 d = 0; + + int32 s = HuffDecode (ht [0]); + + if (s) + { + + if (s == 16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + p0 += d; + + prevRowBuf [col] [0] = (ComponentType) p0; + + } + + // Read second luminance component. + + { + + int32 d = 0; + + int32 s = HuffDecode (ht [0]); + + if (s) + { + + if (s == 16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + p0 += d; + + prevRowBuf [col + 1] [0] = (ComponentType) p0; + + } + + // Read third luminance component. + + { + + int32 d = 0; + + int32 s = HuffDecode (ht [0]); + + if (s) + { + + if (s == 16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + p0 += d; + + curRowBuf [col] [0] = (ComponentType) p0; + + } + + // Read fourth luminance component. + + { + + int32 d = 0; + + int32 s = HuffDecode (ht [0]); + + if (s) + { + + if (s == 16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + p0 += d; + + curRowBuf [col + 1] [0] = (ComponentType) p0; + + } + + // Read first chroma component. + + { + + int32 d = 0; + + int32 s = HuffDecode (ht [1]); + + if (s) + { + + if (s == 16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + p1 += d; + + prevRowBuf [col ] [1] = (ComponentType) p1; + prevRowBuf [col + 1] [1] = (ComponentType) p1; + + curRowBuf [col ] [1] = (ComponentType) p1; + curRowBuf [col + 1] [1] = (ComponentType) p1; + + } + + // Read second chroma component. + + { + + int32 d = 0; + + int32 s = HuffDecode (ht [2]); + + if (s) + { + + if (s == 16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + p2 += d; + + prevRowBuf [col ] [2] = (ComponentType) p2; + prevRowBuf [col + 1] [2] = (ComponentType) p2; + + curRowBuf [col ] [2] = (ComponentType) p2; + curRowBuf [col + 1] [2] = (ComponentType) p2; + + } + + } + + PmPutRow (prevRowBuf, compsInScan, numCOL, row); + PmPutRow (curRowBuf, compsInScan, numCOL, row); + + } + + return; + + } + + #endif + + #if qSupportHasselblad_3FR + + if (info.Ss == 8) + { + + fHasselblad3FR = true; + + for (int32 row = 0; row < numROW; row++) + { + + int32 p0 = 32768; + int32 p1 = 32768; + + for (int32 col = 0; col < numCOL; col += 2) + { + + int32 s0 = HuffDecode (ht [0]); + int32 s1 = HuffDecode (ht [0]); + + if (s0) + { + int32 d = get_bits (s0); + if (s0 == 16) + { + d = -32768; + } + else + { + HuffExtend (d, s0); + } + p0 += d; + } + + if (s1) + { + int32 d = get_bits (s1); + if (s1 == 16) + { + d = -32768; + } + else + { + HuffExtend (d, s1); + } + p1 += d; + } + + curRowBuf [col ] [0] = (ComponentType) p0; + curRowBuf [col + 1] [0] = (ComponentType) p1; + + } + + PmPutRow (curRowBuf, compsInScan, numCOL, row); + + } + + return; + + } + + #endif + + // Decode the first row of image. Output the row and + // turn this row into a previous row for later predictor + // calculation. + + DecodeFirstRow (mcuROW1); + + PmPutRow (mcuROW1, compsInScan, numCOL, 0); + + // Process each row. + + for (int32 row = 1; row < numROW; row++) + { + + // Account for restart interval, process restart marker if needed. + + if (info.restartInRows) + { + + if (info.restartRowsToGo == 0) + { + + ProcessRestart (); + + // Reset predictors at restart. + + DecodeFirstRow (curRowBuf); + + PmPutRow (curRowBuf, compsInScan, numCOL, row); + + swap (MCU *, prevRowBuf, curRowBuf); + + continue; + + } + + info.restartRowsToGo--; + + } + + // The upper neighbors are predictors for the first column. + + for (int32 curComp = 0; curComp < compsInScan; curComp++) + { + + // Section F.2.2.1: decode the difference + + int32 d = 0; + + int32 s = HuffDecode (ht [curComp]); + + if (s) + { + + if (s == 16 && !fBug16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + // First column of row above is predictor for first column. + + curRowBuf [0] [curComp] = (ComponentType) (d + prevRowBuf [0] [curComp]); + + } + + // For the rest of the column on this row, predictor + // calculations are based on PSV. + + if (compsInScan == 2 && info.Ss == 1) + { + + // This is the combination used by both the Canon and Kodak raw formats. + // Unrolling the general case logic results in a significant speed increase. + + uint16 *dPtr = &curRowBuf [1] [0]; + + int32 prev0 = dPtr [-2]; + int32 prev1 = dPtr [-1]; + + for (int32 col = 1; col < numCOL; col++) + { + + int32 s = HuffDecode (ht [0]); + + if (s) + { + + int32 d; + + if (s == 16 && !fBug16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + prev0 += d; + + } + + s = HuffDecode (ht [1]); + + if (s) + { + + int32 d; + + if (s == 16 && !fBug16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + prev1 += d; + + } + + dPtr [0] = (uint16) prev0; + dPtr [1] = (uint16) prev1; + + dPtr += 2; + + } + + } + + else + { + + for (int32 col = 1; col < numCOL; col++) + { + + for (int32 curComp = 0; curComp < compsInScan; curComp++) + { + + // Section F.2.2.1: decode the difference + + int32 d = 0; + + int32 s = HuffDecode (ht [curComp]); + + if (s) + { + + if (s == 16 && !fBug16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + // Predict the pixel value. + + int32 predictor = QuickPredict (col, + curComp, + curRowBuf, + prevRowBuf); + + // Save the difference. + + curRowBuf [col] [curComp] = (ComponentType) (d + predictor); + + } + + } + + } + + PmPutRow (curRowBuf, compsInScan, numCOL, row); + + swap (MCU *, prevRowBuf, curRowBuf); + + } + + #undef swap + + } + +/*****************************************************************************/ + +void dng_lossless_decoder::StartRead (uint32 &imageWidth, + uint32 &imageHeight, + uint32 &imageChannels) + { + + ReadFileHeader (); + ReadScanHeader (); + DecoderStructInit (); + HuffDecoderInit (); + + imageWidth = info.imageWidth; + imageHeight = info.imageHeight; + imageChannels = info.compsInScan; + + } + +/*****************************************************************************/ + +void dng_lossless_decoder::FinishRead () + { + + DecodeImage (); + + } + +/*****************************************************************************/ + +void DecodeLosslessJPEG (dng_stream &stream, + dng_spooler &spooler, + uint32 minDecodedSize, + uint32 maxDecodedSize, + bool bug16, + uint64 endOfData) + { + + dng_lossless_decoder decoder (&stream, + &spooler, + bug16); + + uint32 imageWidth; + uint32 imageHeight; + uint32 imageChannels; + + decoder.StartRead (imageWidth, + imageHeight, + imageChannels); + + uint32 decodedSize = imageWidth * + imageHeight * + imageChannels * + (uint32) sizeof (uint16); + + if (decodedSize < minDecodedSize || + decodedSize > maxDecodedSize) + { + ThrowBadFormat (); + } + + decoder.FinishRead (); + + uint64 streamPos = stream.Position (); + + if (streamPos > endOfData) + { + + bool throwBadFormat = true; + + // Per Hasselblad's request: + // If we have a Hassy file with exactly four extra bytes, + // let it through; the file is likely still valid. + + #if qSupportHasselblad_3FR + + if (decoder.IsHasselblad3FR () && + streamPos - endOfData == 4) + { + throwBadFormat = false; + } + + #endif + + if (throwBadFormat) + { + ThrowBadFormat (); + } + } + + } + +/*****************************************************************************/ + +class dng_lossless_encoder + { + + private: + + const uint16 *fSrcData; + + uint32 fSrcRows; + uint32 fSrcCols; + uint32 fSrcChannels; + uint32 fSrcBitDepth; + + int32 fSrcRowStep; + int32 fSrcColStep; + + dng_stream &fStream; + + HuffmanTable huffTable [4]; + + uint32 freqCount [4] [257]; + + // Current bit-accumulation buffer + + int32 huffPutBuffer; + int32 huffPutBits; + + // Lookup table for number of bits in an 8 bit value. + + int numBitsTable [256]; + + public: + + dng_lossless_encoder (const uint16 *srcData, + uint32 srcRows, + uint32 srcCols, + uint32 srcChannels, + uint32 srcBitDepth, + int32 srcRowStep, + int32 srcColStep, + dng_stream &stream); + + void Encode (); + + private: + + void EmitByte (uint8 value); + + void EmitBits (int code, int size); + + void FlushBits (); + + void CountOneDiff (int diff, uint32 *countTable); + + void EncodeOneDiff (int diff, HuffmanTable *dctbl); + + void FreqCountSet (); + + void HuffEncode (); + + void GenHuffCoding (HuffmanTable *htbl, uint32 *freq); + + void HuffOptimize (); + + void EmitMarker (JpegMarker mark); + + void Emit2bytes (int value); + + void EmitDht (int index); + + void EmitSof (JpegMarker code); + + void EmitSos (); + + void WriteFileHeader (); + + void WriteScanHeader (); + + void WriteFileTrailer (); + + }; + +/*****************************************************************************/ + +dng_lossless_encoder::dng_lossless_encoder (const uint16 *srcData, + uint32 srcRows, + uint32 srcCols, + uint32 srcChannels, + uint32 srcBitDepth, + int32 srcRowStep, + int32 srcColStep, + dng_stream &stream) + + : fSrcData (srcData ) + , fSrcRows (srcRows ) + , fSrcCols (srcCols ) + , fSrcChannels (srcChannels) + , fSrcBitDepth (srcBitDepth) + , fSrcRowStep (srcRowStep ) + , fSrcColStep (srcColStep ) + , fStream (stream ) + + , huffPutBuffer (0) + , huffPutBits (0) + + { + + // Initialize number of bits lookup table. + + numBitsTable [0] = 0; + + for (int i = 1; i < 256; i++) + { + + int temp = i; + int nbits = 1; + + while (temp >>= 1) + { + nbits++; + } + + numBitsTable [i] = nbits; + + } + + } + +/*****************************************************************************/ + +inline void dng_lossless_encoder::EmitByte (uint8 value) + { + + fStream.Put_uint8 (value); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * EmitBits -- + * + * Code for outputting bits to the file + * + * Only the right 24 bits of huffPutBuffer are used; the valid + * bits are left-justified in this part. At most 16 bits can be + * passed to EmitBits in one call, and we never retain more than 7 + * bits in huffPutBuffer between calls, so 24 bits are + * sufficient. + * + * Results: + * None. + * + * Side effects: + * huffPutBuffer and huffPutBits are updated. + * + *-------------------------------------------------------------- + */ + +inline void dng_lossless_encoder::EmitBits (int code, int size) + { + + DNG_ASSERT (size != 0, "Bad Huffman table entry"); + + int putBits = size; + int putBuffer = code; + + putBits += huffPutBits; + + putBuffer <<= 24 - putBits; + putBuffer |= huffPutBuffer; + + while (putBits >= 8) + { + + uint8 c = (uint8) (putBuffer >> 16); + + // Output whole bytes we've accumulated with byte stuffing + + EmitByte (c); + + if (c == 0xFF) + { + EmitByte (0); + } + + putBuffer <<= 8; + putBits -= 8; + + } + + huffPutBuffer = putBuffer; + huffPutBits = putBits; + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * FlushBits -- + * + * Flush any remaining bits in the bit buffer. Used before emitting + * a marker. + * + * Results: + * None. + * + * Side effects: + * huffPutBuffer and huffPutBits are reset + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::FlushBits () + { + + // The first call forces output of any partial bytes. + + EmitBits (0x007F, 7); + + // We can then zero the buffer. + + huffPutBuffer = 0; + huffPutBits = 0; + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * CountOneDiff -- + * + * Count the difference value in countTable. + * + * Results: + * diff is counted in countTable. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +inline void dng_lossless_encoder::CountOneDiff (int diff, uint32 *countTable) + { + + // Encode the DC coefficient difference per section F.1.2.1 + + int temp = diff; + + if (temp < 0) + { + + temp = -temp; + + } + + // Find the number of bits needed for the magnitude of the coefficient + + int nbits = temp >= 256 ? numBitsTable [temp >> 8 ] + 8 + : numBitsTable [temp & 0xFF]; + + // Update count for this bit length + + countTable [nbits] ++; + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * EncodeOneDiff -- + * + * Encode a single difference value. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +inline void dng_lossless_encoder::EncodeOneDiff (int diff, HuffmanTable *dctbl) + { + + // Encode the DC coefficient difference per section F.1.2.1 + + int temp = diff; + int temp2 = diff; + + if (temp < 0) + { + + temp = -temp; + + // For a negative input, want temp2 = bitwise complement of + // abs (input). This code assumes we are on a two's complement + // machine. + + temp2--; + + } + + // Find the number of bits needed for the magnitude of the coefficient + + int nbits = temp >= 256 ? numBitsTable [temp >> 8 ] + 8 + : numBitsTable [temp & 0xFF]; + + // Emit the Huffman-coded symbol for the number of bits + + EmitBits (dctbl->ehufco [nbits], + dctbl->ehufsi [nbits]); + + // Emit that number of bits of the value, if positive, + // or the complement of its magnitude, if negative. + + // If the number of bits is 16, there is only one possible difference + // value (-32786), so the lossless JPEG spec says not to output anything + // in that case. So we only need to output the diference value if + // the number of bits is between 1 and 15. + + if (nbits & 15) + { + + EmitBits (temp2 & (0x0FFFF >> (16 - nbits)), + nbits); + + } + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * FreqCountSet -- + * + * Count the times each category symbol occurs in this image. + * + * Results: + * None. + * + * Side effects: + * The freqCount has counted all category + * symbols appeared in the image. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::FreqCountSet () + { + + memset (freqCount, 0, sizeof (freqCount)); + + DNG_ASSERT ((int32)fSrcRows >= 0, "dng_lossless_encoder::FreqCountSet: fSrcRpws too large."); + + for (int32 row = 0; row < (int32)fSrcRows; row++) + { + + const uint16 *sPtr = fSrcData + row * fSrcRowStep; + + // Initialize predictors for this row. + + int32 predictor [4] = { 0, 0, 0, 0 }; + + for (int32 channel = 0; channel < (int32)fSrcChannels; channel++) + { + + if (row == 0) + predictor [channel] = 1 << (fSrcBitDepth - 1); + + else + predictor [channel] = sPtr [channel - fSrcRowStep]; + + } + + // Unroll most common case of two channels + + if (fSrcChannels == 2) + { + + int32 pred0 = predictor [0]; + int32 pred1 = predictor [1]; + + uint32 srcCols = fSrcCols; + int32 srcColStep = fSrcColStep; + + for (uint32 col = 0; col < srcCols; col++) + { + + int32 pixel0 = sPtr [0]; + int32 pixel1 = sPtr [1]; + + int16 diff0 = (int16) (pixel0 - pred0); + int16 diff1 = (int16) (pixel1 - pred1); + + CountOneDiff (diff0, freqCount [0]); + CountOneDiff (diff1, freqCount [1]); + + pred0 = pixel0; + pred1 = pixel1; + + sPtr += srcColStep; + + } + + } + + // General case. + + else + { + + for (uint32 col = 0; col < fSrcCols; col++) + { + + for (uint32 channel = 0; channel < fSrcChannels; channel++) + { + + int32 pixel = sPtr [channel]; + + int16 diff = (int16) (pixel - predictor [channel]); + + CountOneDiff (diff, freqCount [channel]); + + predictor [channel] = pixel; + + } + + sPtr += fSrcColStep; + + } + + } + + } + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * HuffEncode -- + * + * Encode and output Huffman-compressed image data. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::HuffEncode () + { + + DNG_ASSERT ((int32)fSrcRows >= 0, "dng_lossless_encoder::HuffEncode: fSrcRows too large."); + + for (int32 row = 0; row < (int32)fSrcRows; row++) + { + + const uint16 *sPtr = fSrcData + row * fSrcRowStep; + + // Initialize predictors for this row. + + int32 predictor [4] = { 0, 0, 0, 0 }; + + for (int32 channel = 0; channel < (int32)fSrcChannels; channel++) + { + + if (row == 0) + predictor [channel] = 1 << (fSrcBitDepth - 1); + + else + predictor [channel] = sPtr [channel - fSrcRowStep]; + + } + + // Unroll most common case of two channels + + if (fSrcChannels == 2) + { + + int32 pred0 = predictor [0]; + int32 pred1 = predictor [1]; + + uint32 srcCols = fSrcCols; + int32 srcColStep = fSrcColStep; + + for (uint32 col = 0; col < srcCols; col++) + { + + int32 pixel0 = sPtr [0]; + int32 pixel1 = sPtr [1]; + + int16 diff0 = (int16) (pixel0 - pred0); + int16 diff1 = (int16) (pixel1 - pred1); + + EncodeOneDiff (diff0, &huffTable [0]); + EncodeOneDiff (diff1, &huffTable [1]); + + pred0 = pixel0; + pred1 = pixel1; + + sPtr += srcColStep; + + } + + } + + // General case. + + else + { + + for (uint32 col = 0; col < fSrcCols; col++) + { + + for (uint32 channel = 0; channel < fSrcChannels; channel++) + { + + int32 pixel = sPtr [channel]; + + int16 diff = (int16) (pixel - predictor [channel]); + + EncodeOneDiff (diff, &huffTable [channel]); + + predictor [channel] = pixel; + + } + + sPtr += fSrcColStep; + + } + + } + + } + + FlushBits (); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * GenHuffCoding -- + * + * Generate the optimal coding for the given counts. + * This algorithm is explained in section K.2 of the + * JPEG standard. + * + * Results: + * htbl->bits and htbl->huffval are constructed. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::GenHuffCoding (HuffmanTable *htbl, uint32 *freq) + { + + int i; + int j; + + const int MAX_CLEN = 32; // assumed maximum initial code length + + uint8 bits [MAX_CLEN + 1]; // bits [k] = # of symbols with code length k + short codesize [257]; // codesize [k] = code length of symbol k + short others [257]; // next symbol in current branch of tree + + memset (bits , 0, sizeof (bits )); + memset (codesize, 0, sizeof (codesize)); + + for (i = 0; i < 257; i++) + others [i] = -1; // init links to empty + + // Including the pseudo-symbol 256 in the Huffman procedure guarantees + // that no real symbol is given code-value of all ones, because 256 + // will be placed in the largest codeword category. + + freq [256] = 1; // make sure there is a nonzero count + + // Huffman's basic algorithm to assign optimal code lengths to symbols + + while (true) + { + + // Find the smallest nonzero frequency, set c1 = its symbol. + // In case of ties, take the larger symbol number. + + int c1 = -1; + + uint32 v = 0xFFFFFFFF; + + for (i = 0; i <= 256; i++) + { + + if (freq [i] && freq [i] <= v) + { + v = freq [i]; + c1 = i; + } + + } + + // Find the next smallest nonzero frequency, set c2 = its symbol. + // In case of ties, take the larger symbol number. + + int c2 = -1; + + v = 0xFFFFFFFF; + + for (i = 0; i <= 256; i++) + { + + if (freq [i] && freq [i] <= v && i != c1) + { + v = freq [i]; + c2 = i; + } + + } + + // Done if we've merged everything into one frequency. + + if (c2 < 0) + break; + + // Else merge the two counts/trees. + + freq [c1] += freq [c2]; + freq [c2] = 0; + + // Increment the codesize of everything in c1's tree branch. + + codesize [c1] ++; + + while (others [c1] >= 0) + { + c1 = others [c1]; + codesize [c1] ++; + } + + // chain c2 onto c1's tree branch + + others [c1] = (short) c2; + + // Increment the codesize of everything in c2's tree branch. + + codesize [c2] ++; + + while (others [c2] >= 0) + { + c2 = others [c2]; + codesize [c2] ++; + } + + } + + // Now count the number of symbols of each code length. + + for (i = 0; i <= 256; i++) + { + + if (codesize [i]) + { + + // The JPEG standard seems to think that this can't happen, + // but I'm paranoid... + + if (codesize [i] > MAX_CLEN) + { + + ThrowOverflow ("Huffman code size table overflow"); + + } + + bits [codesize [i]]++; + + } + + } + + // JPEG doesn't allow symbols with code lengths over 16 bits, so if the pure + // Huffman procedure assigned any such lengths, we must adjust the coding. + // Here is what the JPEG spec says about how this next bit works: + // Since symbols are paired for the longest Huffman code, the symbols are + // removed from this length category two at a time. The prefix for the pair + // (which is one bit shorter) is allocated to one of the pair; then, + // skipping the BITS entry for that prefix length, a code word from the next + // shortest nonzero BITS entry is converted into a prefix for two code words + // one bit longer. + + for (i = MAX_CLEN; i > 16; i--) + { + + while (bits [i] > 0) + { + + // Kludge: I have never been able to test this logic, and there + // are comments on the web that this encoder has bugs with 16-bit + // data, so just throw an error if we get here and revert to a + // default table. - tknoll 12/1/03. + + DNG_REPORT ("Info: Optimal huffman table bigger than 16 bits"); + + ThrowProgramError (); + + // Original logic: + + j = i - 2; // find length of new prefix to be used + + while (bits [j] == 0) + j--; + + bits [i ] -= 2; // remove two symbols + bits [i - 1] ++; // one goes in this length + bits [j + 1] += 2; // two new symbols in this length + bits [j ] --; // symbol of this length is now a prefix + + } + + } + + // Remove the count for the pseudo-symbol 256 from + // the largest codelength. + + while (bits [i] == 0) // find largest codelength still in use + i--; + + bits [i] --; + + // Return final symbol counts (only for lengths 0..16). + + memcpy (htbl->bits, bits, sizeof (htbl->bits)); + + // Return a list of the symbols sorted by code length. + // It's not real clear to me why we don't need to consider the codelength + // changes made above, but the JPEG spec seems to think this works. + + int p = 0; + + for (i = 1; i <= MAX_CLEN; i++) + { + + for (j = 0; j <= 255; j++) + { + + if (codesize [j] == i) + { + htbl->huffval [p] = (uint8) j; + p++; + } + + } + + } + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * HuffOptimize -- + * + * Find the best coding parameters for a Huffman-coded scan. + * When called, the scan data has already been converted to + * a sequence of MCU groups of source image samples, which + * are stored in a "big" array, mcuTable. + * + * It counts the times each category symbol occurs. Based on + * this counting, optimal Huffman tables are built. Then it + * uses this optimal Huffman table and counting table to find + * the best PSV. + * + * Results: + * Optimal Huffman tables are retured in cPtr->dcHuffTblPtrs[tbl]. + * Best PSV is retured in cPtr->Ss. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::HuffOptimize () + { + + // Collect the frequency counts. + + FreqCountSet (); + + // Generate Huffman encoding tables. + + for (uint32 channel = 0; channel < fSrcChannels; channel++) + { + + try + { + + GenHuffCoding (&huffTable [channel], freqCount [channel]); + + } + + catch (...) + { + + DNG_REPORT ("Info: Reverting to default huffman table"); + + for (uint32 j = 0; j <= 256; j++) + { + + freqCount [channel] [j] = (j <= 16 ? 1 : 0); + + } + + GenHuffCoding (&huffTable [channel], freqCount [channel]); + + } + + FixHuffTbl (&huffTable [channel]); + + } + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * EmitMarker -- + * + * Emit a marker code into the output stream. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::EmitMarker (JpegMarker mark) + { + + EmitByte (0xFF); + EmitByte ((uint8) mark); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * Emit2bytes -- + * + * Emit a 2-byte integer; these are always MSB first in JPEG + * files + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::Emit2bytes (int value) + { + + EmitByte ((value >> 8) & 0xFF); + EmitByte (value & 0xFF); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * EmitDht -- + * + * Emit a DHT marker, follwed by the huffman data. + * + * Results: + * None + * + * Side effects: + * None + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::EmitDht (int index) + { + + int i; + + HuffmanTable *htbl = &huffTable [index]; + + EmitMarker (M_DHT); + + int length = 0; + + for (i = 1; i <= 16; i++) + length += htbl->bits [i]; + + Emit2bytes (length + 2 + 1 + 16); + + EmitByte ((uint8) index); + + for (i = 1; i <= 16; i++) + EmitByte (htbl->bits [i]); + + for (i = 0; i < length; i++) + EmitByte (htbl->huffval [i]); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * EmitSof -- + * + * Emit a SOF marker plus data. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::EmitSof (JpegMarker code) + { + + EmitMarker (code); + + Emit2bytes (3 * fSrcChannels + 2 + 5 + 1); // length + + EmitByte ((uint8) fSrcBitDepth); + + Emit2bytes (fSrcRows); + Emit2bytes (fSrcCols); + + EmitByte ((uint8) fSrcChannels); + + for (uint32 i = 0; i < fSrcChannels; i++) + { + + EmitByte ((uint8) i); + + EmitByte ((uint8) ((1 << 4) + 1)); // Not subsampled. + + EmitByte (0); // Tq shall be 0 for lossless. + + } + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * EmitSos -- + * + * Emit a SOS marker plus data. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::EmitSos () + { + + EmitMarker (M_SOS); + + Emit2bytes (2 * fSrcChannels + 2 + 1 + 3); // length + + EmitByte ((uint8) fSrcChannels); // Ns + + for (uint32 i = 0; i < fSrcChannels; i++) + { + + // Cs,Td,Ta + + EmitByte ((uint8) i); + EmitByte ((uint8) (i << 4)); + + } + + EmitByte (1); // PSV - hardcoded - tknoll + EmitByte (0); // Spectral selection end - Se + EmitByte (0); // The point transform parameter + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * WriteFileHeader -- + * + * Write the file header. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::WriteFileHeader () + { + + EmitMarker (M_SOI); // first the SOI + + EmitSof (M_SOF3); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * WriteScanHeader -- + * + * Write the start of a scan (everything through the SOS marker). + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::WriteScanHeader () + { + + // Emit Huffman tables. + + for (uint32 i = 0; i < fSrcChannels; i++) + { + + EmitDht (i); + + } + + EmitSos (); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * WriteFileTrailer -- + * + * Write the End of image marker at the end of a JPEG file. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::WriteFileTrailer () + { + + EmitMarker (M_EOI); + + } + +/*****************************************************************************/ + +void dng_lossless_encoder::Encode () + { + + DNG_ASSERT (fSrcChannels <= 4, "Too many components in scan"); + + // Count the times each difference category occurs. + // Construct the optimal Huffman table. + + HuffOptimize (); + + // Write the frame and scan headers. + + WriteFileHeader (); + + WriteScanHeader (); + + // Encode the image. + + HuffEncode (); + + // Clean up everything. + + WriteFileTrailer (); + + } + +/*****************************************************************************/ + +void EncodeLosslessJPEG (const uint16 *srcData, + uint32 srcRows, + uint32 srcCols, + uint32 srcChannels, + uint32 srcBitDepth, + int32 srcRowStep, + int32 srcColStep, + dng_stream &stream) + { + + dng_lossless_encoder encoder (srcData, + srcRows, + srcCols, + srcChannels, + srcBitDepth, + srcRowStep, + srcColStep, + stream); + + encoder.Encode (); + + } + +/*****************************************************************************/ diff --git a/dng/dng_lossless_jpeg.h b/dng/dng_lossless_jpeg.h new file mode 100644 index 0000000..6939c53 --- /dev/null +++ b/dng/dng_lossless_jpeg.h @@ -0,0 +1,65 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Functions for encoding and decoding lossless JPEG format. + */ + +/*****************************************************************************/ + +#ifndef __dng_lossless_jpeg__ +#define __dng_lossless_jpeg__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_types.h" + +/*****************************************************************************/ + +class dng_spooler + { + + protected: + + virtual ~dng_spooler () + { + } + + public: + + virtual void Spool (const void *data, + uint32 count) = 0; + + }; + +/*****************************************************************************/ + +void DecodeLosslessJPEG (dng_stream &stream, + dng_spooler &spooler, + uint32 minDecodedSize, + uint32 maxDecodedSize, + bool bug16, + uint64 endOfData); + +/*****************************************************************************/ + +void EncodeLosslessJPEG (const uint16 *srcData, + uint32 srcRows, + uint32 srcCols, + uint32 srcChannels, + uint32 srcBitDepth, + int32 srcRowStep, + int32 srcColStep, + dng_stream &stream); + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_matrix.cpp b/dng/dng_matrix.cpp new file mode 100644 index 0000000..315616c --- /dev/null +++ b/dng/dng_matrix.cpp @@ -0,0 +1,1353 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_matrix.h" + +#include "dng_assertions.h" +#include "dng_exceptions.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_matrix::dng_matrix () + + : fRows (0) + , fCols (0) + + { + + } + +/*****************************************************************************/ + +dng_matrix::dng_matrix (uint32 rows, + uint32 cols) + + : fRows (0) + , fCols (0) + + { + + if (rows < 1 || rows > kMaxColorPlanes || + cols < 1 || cols > kMaxColorPlanes) + { + + ThrowProgramError (); + + } + + fRows = rows; + fCols = cols; + + for (uint32 row = 0; row < fRows; row++) + for (uint32 col = 0; col < fCols; col++) + { + + fData [row] [col] = 0.0; + + } + + } + +/*****************************************************************************/ + +dng_matrix::dng_matrix (const dng_matrix &m) + + : fRows (m.fRows) + , fCols (m.fCols) + + { + + for (uint32 row = 0; row < fRows; row++) + for (uint32 col = 0; col < fCols; col++) + { + + fData [row] [col] = m.fData [row] [col]; + + } + + } + +/*****************************************************************************/ + +void dng_matrix::Clear () + { + + fRows = 0; + fCols = 0; + + } + +/*****************************************************************************/ + +void dng_matrix::SetIdentity (uint32 count) + { + + *this = dng_matrix (count, count); + + for (uint32 j = 0; j < count; j++) + { + + fData [j] [j] = 1.0; + + } + + } + +/******************************************************************************/ + +bool dng_matrix::operator== (const dng_matrix &m) const + { + + if (Rows () != m.Rows () || + Cols () != m.Cols ()) + { + + return false; + + } + + for (uint32 j = 0; j < Rows (); j++) + for (uint32 k = 0; k < Cols (); k++) + { + + if (fData [j] [k] != m.fData [j] [k]) + { + + return false; + + } + + } + + return true; + + } + +/******************************************************************************/ + +bool dng_matrix::IsDiagonal () const + { + + if (IsEmpty ()) + { + return false; + } + + if (Rows () != Cols ()) + { + return false; + } + + for (uint32 j = 0; j < Rows (); j++) + for (uint32 k = 0; k < Cols (); k++) + { + + if (j != k) + { + + if (fData [j] [k] != 0.0) + { + return false; + } + + } + + } + + return true; + + } + +/******************************************************************************/ + +bool dng_matrix::IsIdentity () const + { + + if (IsDiagonal ()) + { + + for (uint32 j = 0; j < Rows (); j++) + { + + if (fData [j] [j] != 1.0) + { + return false; + } + + } + + return true; + + } + + return false; + + } + +/******************************************************************************/ + +real64 dng_matrix::MaxEntry () const + { + + if (IsEmpty ()) + { + + return 0.0; + + } + + real64 m = fData [0] [0]; + + for (uint32 j = 0; j < Rows (); j++) + for (uint32 k = 0; k < Cols (); k++) + { + + m = Max_real64 (m, fData [j] [k]); + + } + + return m; + + } + +/******************************************************************************/ + +real64 dng_matrix::MinEntry () const + { + + if (IsEmpty ()) + { + + return 0.0; + + } + + real64 m = fData [0] [0]; + + for (uint32 j = 0; j < Rows (); j++) + for (uint32 k = 0; k < Cols (); k++) + { + + m = Min_real64 (m, fData [j] [k]); + + } + + return m; + + } + +/*****************************************************************************/ + +void dng_matrix::Scale (real64 factor) + { + + for (uint32 j = 0; j < Rows (); j++) + for (uint32 k = 0; k < Cols (); k++) + { + + fData [j] [k] *= factor; + + } + + } + +/*****************************************************************************/ + +void dng_matrix::Round (real64 factor) + { + + real64 invFactor = 1.0 / factor; + + for (uint32 j = 0; j < Rows (); j++) + for (uint32 k = 0; k < Cols (); k++) + { + + fData [j] [k] = Round_int32 (fData [j] [k] * factor) * invFactor; + + } + + } + +/*****************************************************************************/ + +void dng_matrix::SafeRound (real64 factor) + { + + real64 invFactor = 1.0 / factor; + + for (uint32 j = 0; j < Rows (); j++) + { + + // Round each row to the specified accuracy, but make sure the + // a rounding does not affect the total of the elements in a row + // more than necessary. + + real64 error = 0.0; + + for (uint32 k = 0; k < Cols (); k++) + { + + fData [j] [k] += error; + + real64 rounded = Round_int32 (fData [j] [k] * factor) * invFactor; + + error = fData [j] [k] - rounded; + + fData [j] [k] = rounded; + + } + + } + + } + +/*****************************************************************************/ + +bool dng_matrix::AlmostEqual (const dng_matrix &m, + real64 slop) const + { + + if (Rows () != m.Rows () || + Cols () != m.Cols ()) + { + return false; + } + + for (uint32 j = 0; j < Rows (); j++) + { + + for (uint32 k = 0; k < Cols (); k++) + { + + if (Abs_real64 (fData [j] [k] - m [j] [k]) > slop) + { + return false; + } + + } + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_matrix::AlmostIdentity (real64 slop) const + { + + dng_matrix m; + + m.SetIdentity (Rows ()); + + return AlmostEqual (m, slop); + + } + +/*****************************************************************************/ + +dng_matrix_3by3::dng_matrix_3by3 () + + : dng_matrix (3, 3) + + { + } + +/*****************************************************************************/ + +dng_matrix_3by3::dng_matrix_3by3 (const dng_matrix &m) + + : dng_matrix (m) + + { + + if (Rows () != 3 || + Cols () != 3) + { + + ThrowMatrixMath (); + + } + + } + +/*****************************************************************************/ + +dng_matrix_3by3::dng_matrix_3by3 (real64 a00, real64 a01, real64 a02, + real64 a10, real64 a11, real64 a12, + real64 a20, real64 a21, real64 a22) + + + : dng_matrix (3, 3) + + { + + fData [0] [0] = a00; + fData [0] [1] = a01; + fData [0] [2] = a02; + + fData [1] [0] = a10; + fData [1] [1] = a11; + fData [1] [2] = a12; + + fData [2] [0] = a20; + fData [2] [1] = a21; + fData [2] [2] = a22; + + } + +/*****************************************************************************/ + +dng_matrix_3by3::dng_matrix_3by3 (real64 a00, real64 a11, real64 a22) + + : dng_matrix (3, 3) + + { + + fData [0] [0] = a00; + fData [1] [1] = a11; + fData [2] [2] = a22; + + } + +/*****************************************************************************/ + +dng_matrix_4by3::dng_matrix_4by3 () + + : dng_matrix (4, 3) + + { + } + +/*****************************************************************************/ + +dng_matrix_4by3::dng_matrix_4by3 (const dng_matrix &m) + + : dng_matrix (m) + + { + + if (Rows () != 4 || + Cols () != 3) + { + + ThrowMatrixMath (); + + } + + } + +/*****************************************************************************/ + +dng_matrix_4by3::dng_matrix_4by3 (real64 a00, real64 a01, real64 a02, + real64 a10, real64 a11, real64 a12, + real64 a20, real64 a21, real64 a22, + real64 a30, real64 a31, real64 a32) + + + : dng_matrix (4, 3) + + { + + fData [0] [0] = a00; + fData [0] [1] = a01; + fData [0] [2] = a02; + + fData [1] [0] = a10; + fData [1] [1] = a11; + fData [1] [2] = a12; + + fData [2] [0] = a20; + fData [2] [1] = a21; + fData [2] [2] = a22; + + fData [3] [0] = a30; + fData [3] [1] = a31; + fData [3] [2] = a32; + + } + +/*****************************************************************************/ + +dng_matrix_4by4::dng_matrix_4by4 () + + : dng_matrix (4, 4) + + { + + } + +/*****************************************************************************/ + +dng_matrix_4by4::dng_matrix_4by4 (const dng_matrix &m) + + : dng_matrix (m) + + { + + // Input must be either 3x3 or 4x4. + + const bool is3by3 = (m.Rows () == 3 && + m.Cols () == 3); + + const bool is4by4 = (m.Rows () == 4 && + m.Cols () == 4); + + if (!is3by3 && !is4by4) + { + + ThrowMatrixMath (); + + } + + // For 3x3 case, pad to 4x4 (equivalent 4x4 matrix). + + if (is3by3) + { + + fRows = 4; + fCols = 4; + + fData [0] [3] = 0.0; + fData [1] [3] = 0.0; + fData [2] [3] = 0.0; + + fData [3] [0] = 0.0; + fData [3] [1] = 0.0; + fData [3] [2] = 0.0; + + fData [3] [3] = 1.0; + + } + + } + +/*****************************************************************************/ + +dng_matrix_4by4::dng_matrix_4by4 (real64 a00, real64 a01, real64 a02, real64 a03, + real64 a10, real64 a11, real64 a12, real64 a13, + real64 a20, real64 a21, real64 a22, real64 a23, + real64 a30, real64 a31, real64 a32, real64 a33) + + : dng_matrix (4, 4) + + { + + fData [0] [0] = a00; + fData [0] [1] = a01; + fData [0] [2] = a02; + fData [0] [3] = a03; + + fData [1] [0] = a10; + fData [1] [1] = a11; + fData [1] [2] = a12; + fData [1] [3] = a13; + + fData [2] [0] = a20; + fData [2] [1] = a21; + fData [2] [2] = a22; + fData [2] [3] = a23; + + fData [3] [0] = a30; + fData [3] [1] = a31; + fData [3] [2] = a32; + fData [3] [3] = a33; + + } + +/*****************************************************************************/ + +dng_matrix_4by4::dng_matrix_4by4 (real64 a00, + real64 a11, + real64 a22, + real64 a33) + + : dng_matrix (4, 4) + + { + + fData [0] [0] = a00; + fData [1] [1] = a11; + fData [2] [2] = a22; + fData [3] [3] = a33; + + } + +/*****************************************************************************/ + +dng_vector::dng_vector () + + : fCount (0) + + { + + } + +/*****************************************************************************/ + +dng_vector::dng_vector (uint32 count) + + : fCount (0) + + { + + if (count < 1 || count > kMaxColorPlanes) + { + + ThrowProgramError (); + + } + + fCount = count; + + for (uint32 index = 0; index < fCount; index++) + { + + fData [index] = 0.0; + + } + + } + +/*****************************************************************************/ + +dng_vector::dng_vector (const dng_vector &v) + + : fCount (v.fCount) + + { + + for (uint32 index = 0; index < fCount; index++) + { + + fData [index] = v.fData [index]; + + } + + } + +/*****************************************************************************/ + +void dng_vector::Clear () + { + + fCount = 0; + + } + +/*****************************************************************************/ + +void dng_vector::SetIdentity (uint32 count) + { + + *this = dng_vector (count); + + for (uint32 j = 0; j < count; j++) + { + + fData [j] = 1.0; + + } + + } + +/******************************************************************************/ + +bool dng_vector::operator== (const dng_vector &v) const + { + + if (Count () != v.Count ()) + { + + return false; + + } + + for (uint32 j = 0; j < Count (); j++) + { + + if (fData [j] != v.fData [j]) + { + + return false; + + } + + } + + return true; + + } + +/******************************************************************************/ + +real64 dng_vector::MaxEntry () const + { + + if (IsEmpty ()) + { + + return 0.0; + + } + + real64 m = fData [0]; + + for (uint32 j = 0; j < Count (); j++) + { + + m = Max_real64 (m, fData [j]); + + } + + return m; + + } + +/******************************************************************************/ + +real64 dng_vector::MinEntry () const + { + + if (IsEmpty ()) + { + + return 0.0; + + } + + real64 m = fData [0]; + + for (uint32 j = 0; j < Count (); j++) + { + + m = Min_real64 (m, fData [j]); + + } + + return m; + + } + +/*****************************************************************************/ + +void dng_vector::Scale (real64 factor) + { + + for (uint32 j = 0; j < Count (); j++) + { + + fData [j] *= factor; + + } + + } + +/*****************************************************************************/ + +void dng_vector::Round (real64 factor) + { + + real64 invFactor = 1.0 / factor; + + for (uint32 j = 0; j < Count (); j++) + { + + fData [j] = Round_int32 (fData [j] * factor) * invFactor; + + } + + } + +/*****************************************************************************/ + +dng_matrix dng_vector::AsDiagonal () const + { + + dng_matrix M (Count (), Count ()); + + for (uint32 j = 0; j < Count (); j++) + { + + M [j] [j] = fData [j]; + + } + + return M; + + } + +/*****************************************************************************/ + +dng_matrix dng_vector::AsColumn () const + { + + dng_matrix M (Count (), 1); + + for (uint32 j = 0; j < Count (); j++) + { + + M [j] [0] = fData [j]; + + } + + return M; + + } + +/******************************************************************************/ + +dng_vector_3::dng_vector_3 () + + : dng_vector (3) + + { + } + +/******************************************************************************/ + +dng_vector_3::dng_vector_3 (const dng_vector &v) + + : dng_vector (v) + + { + + if (Count () != 3) + { + + ThrowMatrixMath (); + + } + + } + +/******************************************************************************/ + +dng_vector_3::dng_vector_3 (real64 a0, + real64 a1, + real64 a2) + + : dng_vector (3) + + { + + fData [0] = a0; + fData [1] = a1; + fData [2] = a2; + + } + +/******************************************************************************/ + +dng_vector_4::dng_vector_4 () + + : dng_vector (4) + + { + } + +/******************************************************************************/ + +dng_vector_4::dng_vector_4 (const dng_vector &v) + + : dng_vector (v) + + { + + if (Count () != 4) + { + + ThrowMatrixMath (); + + } + + } + +/******************************************************************************/ + +dng_vector_4::dng_vector_4 (real64 a0, + real64 a1, + real64 a2, + real64 a3) + + : dng_vector (4) + + { + + fData [0] = a0; + fData [1] = a1; + fData [2] = a2; + fData [3] = a3; + + } + +/******************************************************************************/ + +dng_matrix operator* (const dng_matrix &A, + const dng_matrix &B) + { + + if (A.Cols () != B.Rows ()) + { + + ThrowMatrixMath (); + + } + + dng_matrix C (A.Rows (), B.Cols ()); + + for (uint32 j = 0; j < C.Rows (); j++) + for (uint32 k = 0; k < C.Cols (); k++) + { + + C [j] [k] = 0.0; + + for (uint32 m = 0; m < A.Cols (); m++) + { + + real64 aa = A [j] [m]; + + real64 bb = B [m] [k]; + + C [j] [k] += aa * bb; + + } + + } + + return C; + + } + +/******************************************************************************/ + +dng_vector operator* (const dng_matrix &A, + const dng_vector &B) + { + + if (A.Cols () != B.Count ()) + { + + ThrowMatrixMath (); + + } + + dng_vector C (A.Rows ()); + + for (uint32 j = 0; j < C.Count (); j++) + { + + C [j] = 0.0; + + for (uint32 m = 0; m < A.Cols (); m++) + { + + real64 aa = A [j] [m]; + + real64 bb = B [m]; + + C [j] += aa * bb; + + } + + } + + return C; + + } + +/******************************************************************************/ + +dng_matrix operator* (real64 scale, + const dng_matrix &A) + { + + dng_matrix B (A); + + B.Scale (scale); + + return B; + + } + +/******************************************************************************/ + +dng_vector operator* (real64 scale, + const dng_vector &A) + { + + dng_vector B (A); + + B.Scale (scale); + + return B; + + } + +/******************************************************************************/ + +dng_matrix operator+ (const dng_matrix &A, + const dng_matrix &B) + { + + if (A.Cols () != B.Cols () || + A.Rows () != B.Rows ()) + { + + ThrowMatrixMath (); + + } + + dng_matrix C (A); + + for (uint32 j = 0; j < C.Rows (); j++) + for (uint32 k = 0; k < C.Cols (); k++) + { + + C [j] [k] += B [j] [k]; + + } + + return C; + + } + +/******************************************************************************/ + +dng_vector operator- (const dng_vector &a, + const dng_vector &b) + { + + uint32 count = a.Count (); + + DNG_REQUIRE (count == b.Count (), + "Mismatch count in Dot"); + + if (!count) + { + return dng_vector (); + } + + dng_vector result (count); + + for (uint32 i = 0; i < count; i++) + { + + result [i] = a [i] - b [i]; + + } + + return result; + + } + +/******************************************************************************/ + +const real64 kNearZero = 1.0E-10; + +/******************************************************************************/ + +// Work around bug #1294195, which may be a hardware problem on a specific machine. +// This pragma turns on "improved" floating-point consistency. +#ifdef _MSC_VER +#pragma optimize ("p", on) +#endif + +static dng_matrix Invert3by3 (const dng_matrix &A) + { + + real64 a00 = A [0] [0]; + real64 a01 = A [0] [1]; + real64 a02 = A [0] [2]; + real64 a10 = A [1] [0]; + real64 a11 = A [1] [1]; + real64 a12 = A [1] [2]; + real64 a20 = A [2] [0]; + real64 a21 = A [2] [1]; + real64 a22 = A [2] [2]; + + real64 temp [3] [3]; + + temp [0] [0] = a11 * a22 - a21 * a12; + temp [0] [1] = a21 * a02 - a01 * a22; + temp [0] [2] = a01 * a12 - a11 * a02; + temp [1] [0] = a20 * a12 - a10 * a22; + temp [1] [1] = a00 * a22 - a20 * a02; + temp [1] [2] = a10 * a02 - a00 * a12; + temp [2] [0] = a10 * a21 - a20 * a11; + temp [2] [1] = a20 * a01 - a00 * a21; + temp [2] [2] = a00 * a11 - a10 * a01; + + real64 det = (a00 * temp [0] [0] + + a01 * temp [1] [0] + + a02 * temp [2] [0]); + + if (Abs_real64 (det) < kNearZero) + { + + ThrowMatrixMath (); + + } + + dng_matrix B (3, 3); + + for (uint32 j = 0; j < 3; j++) + for (uint32 k = 0; k < 3; k++) + { + + B [j] [k] = temp [j] [k] / det; + + } + + return B; + + } + +// Reset floating-point optimization. See comment above. +#ifdef _MSC_VER +#pragma optimize ("p", off) +#endif + +/******************************************************************************/ + +static dng_matrix InvertNbyN (const dng_matrix &A) + { + + uint32 i; + uint32 j; + uint32 k; + + const uint32 n = A.Rows (); + + const uint32 augmented_cols = 2 * n; + + real64 temp [kMaxColorPlanes] [kMaxColorPlanes * 2]; + + memset (temp, 0, sizeof (temp)); + + for (i = 0; i < n; i++) + for (j = 0; j < n; j++) + { + + temp [i] [j ] = A [i] [j]; + + temp [i] [j + n] = (i == j ? 1.0 : 0.0); + + } + + for (i = 0; i < n; i++) + { + + // Find row iMax with largest absolute entry in column i. + + uint32 iMax = i; + real64 vMax = -1.0; + + for (k = i; k < n; k++) + { + + real64 v = Abs_real64 (A [k] [i]); + + if (v > vMax) + { + vMax = v; + iMax = k; + } + + } + + real64 alpha = temp [iMax] [i]; + + if (Abs_real64 (alpha) < kNearZero) + { + + ThrowMatrixMath (); + + } + + // Swap rows i and iMax, column by column. + + if (i != iMax) + { + + for (j = 0; j < augmented_cols; j++) + { + + std::swap (temp [i ] [j], + temp [iMax] [j]); + + } + + } + + for (j = 0; j < augmented_cols; j++) + { + + temp [i] [j] /= alpha; + + } + + for (k = 0; k < n; k++) + { + + if (i != k) + { + + real64 beta = temp [k] [i]; + + for (j = 0; j < augmented_cols; j++) + { + + temp [k] [j] -= beta * temp [i] [j]; + + } + + } + + } + + } + + dng_matrix B (n, n); + + for (i = 0; i < n; i++) + for (j = 0; j < n; j++) + { + + B [i] [j] = temp [i] [j + n]; + + } + + return B; + + } + +/******************************************************************************/ + +dng_matrix Transpose (const dng_matrix &A) + { + + dng_matrix B (A.Cols (), A.Rows ()); + + for (uint32 j = 0; j < B.Rows (); j++) + for (uint32 k = 0; k < B.Cols (); k++) + { + + B [j] [k] = A [k] [j]; + + } + + return B; + + } + +/******************************************************************************/ + +dng_matrix Invert (const dng_matrix &A) + { + + if (A.Rows () < 2 || A.Cols () < 2) + { + + ThrowMatrixMath (); + + } + + if (A.Rows () == A.Cols ()) + { + + if (A.Rows () == 3) + { + + return Invert3by3 (A); + + } + + return InvertNbyN (A); + + } + + else + { + + // Compute the pseudo inverse. + + dng_matrix B = Transpose (A); + + return Invert (B * A) * B; + + } + + } + +/******************************************************************************/ + +dng_matrix Invert (const dng_matrix &A, + const dng_matrix &hint) + { + + if (A.Rows () == A .Cols () || + A.Rows () != hint.Cols () || + A.Cols () != hint.Rows ()) + { + + return Invert (A); + + } + + else + { + + // Use the specified hint matrix. + + return Invert (hint * A) * hint; + + } + + } + +/*****************************************************************************/ + +real64 Dot (const dng_vector &a, + const dng_vector &b) + { + + DNG_REQUIRE (a.Count () == b.Count (), + "Cannot take dot product between vectors of different size."); + + // DNG_REQUIRE (a.Count () > 0, + // "Cannot take dot product with an empty vector."); + + real64 sum = 0.0; + + for (uint32 j = 0; j < a.Count (); j++) + { + + sum += (a [j] * b [j]); + + } + + return sum; + + } + +/*****************************************************************************/ + +real64 Distance (const dng_vector &a, + const dng_vector &b) + { + + dng_vector c = a - b; + + return sqrt (Dot (c, c)); + + } + +/*****************************************************************************/ diff --git a/dng/dng_matrix.h b/dng/dng_matrix.h new file mode 100644 index 0000000..b3f96a2 --- /dev/null +++ b/dng/dng_matrix.h @@ -0,0 +1,365 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Matrix and vector classes, including specialized 3x3 and 4x3 versions as + * well as length 3 vectors. + */ + +/*****************************************************************************/ + +#ifndef __dng_matrix__ +#define __dng_matrix__ + +/*****************************************************************************/ + +#include "dng_sdk_limits.h" +#include "dng_types.h" + +/*****************************************************************************/ + +/// \brief Class to represent 2D matrix up to kMaxColorPlanes x kMaxColorPlanes +/// in size. + +class dng_matrix + { + + protected: + + uint32 fRows; + uint32 fCols; + + real64 fData [kMaxColorPlanes] [kMaxColorPlanes]; + + public: + + dng_matrix (); + + dng_matrix (uint32 rows, + uint32 cols); + + dng_matrix (const dng_matrix &m); + + virtual ~dng_matrix () + { + } + + void Clear (); + + void SetIdentity (uint32 count); + + uint32 Rows () const + { + return fRows; + } + + uint32 Cols () const + { + return fCols; + } + + real64 * operator [] (uint32 row) + { + return fData [row]; + } + + const real64 * operator [] (uint32 row) const + { + return fData [row]; + } + + bool operator== (const dng_matrix &m) const; + + bool operator!= (const dng_matrix &m) const + { + return !(*this == m); + } + + bool IsEmpty () const + { + return fRows == 0 || fCols == 0; + } + + bool NotEmpty () const + { + return !IsEmpty (); + } + + bool IsDiagonal () const; + + bool IsIdentity () const; + + real64 MaxEntry () const; + + real64 MinEntry () const; + + void Scale (real64 factor); + + void Round (real64 factor); + + void SafeRound (real64 factor); + + bool AlmostEqual (const dng_matrix &m, + real64 slop = 1.0e-8) const; + + bool AlmostIdentity (real64 slop = 1.0e-8) const; + + }; + +/*****************************************************************************/ + +/// \brief A 3x3 matrix. + +class dng_matrix_3by3: public dng_matrix + { + + public: + + dng_matrix_3by3 (); + + dng_matrix_3by3 (const dng_matrix &m); + + dng_matrix_3by3 (real64 a00, real64 a01, real64 a02, + real64 a10, real64 a11, real64 a12, + real64 a20, real64 a21, real64 a22); + + dng_matrix_3by3 (real64 a00, real64 a11, real64 a22); + + }; + +/*****************************************************************************/ + +/// \brief A 4x3 matrix. Handy for working with 4-color cameras. + +class dng_matrix_4by3: public dng_matrix + { + + public: + + dng_matrix_4by3 (); + + dng_matrix_4by3 (const dng_matrix &m); + + dng_matrix_4by3 (real64 a00, real64 a01, real64 a02, + real64 a10, real64 a11, real64 a12, + real64 a20, real64 a21, real64 a22, + real64 a30, real64 a31, real64 a32); + + }; + +/*****************************************************************************/ + +/// \brief A 4x4 matrix. Handy for GPU APIs. + +class dng_matrix_4by4: public dng_matrix + { + + public: + + dng_matrix_4by4 (); + + dng_matrix_4by4 (const dng_matrix &m); + + dng_matrix_4by4 (real64 a00, real64 a01, real64 a02, real64 a03, + real64 a10, real64 a11, real64 a12, real64 a13, + real64 a20, real64 a21, real64 a22, real64 a23, + real64 a30, real64 a31, real64 a32, real64 a33); + + dng_matrix_4by4 (real64 a00, real64 a11, real64 a22, real64 a33); + + }; + +/*****************************************************************************/ + +/// \brief Class to represent 1-dimensional vector with up to kMaxColorPlanes +/// components. + +class dng_vector + { + + protected: + + uint32 fCount; + + real64 fData [kMaxColorPlanes]; + + public: + + dng_vector (); + + dng_vector (uint32 count); + + dng_vector (const dng_vector &v); + + virtual ~dng_vector () + { + } + + void Clear (); + + void SetIdentity (uint32 count); + + uint32 Count () const + { + return fCount; + } + + real64 & operator [] (uint32 index) + { + return fData [index]; + } + + const real64 & operator [] (uint32 index) const + { + return fData [index]; + } + + bool operator== (const dng_vector &v) const; + + bool operator!= (const dng_vector &v) const + { + return !(*this == v); + } + + bool IsEmpty () const + { + return fCount == 0; + } + + bool NotEmpty () const + { + return !IsEmpty (); + } + + real64 MaxEntry () const; + + real64 MinEntry () const; + + void Scale (real64 factor); + + void Round (real64 factor); + + dng_matrix AsDiagonal () const; + + dng_matrix AsColumn () const; + + }; + +/*****************************************************************************/ + +/// \brief A 3-element vector. + +class dng_vector_3: public dng_vector + { + + public: + + dng_vector_3 (); + + dng_vector_3 (const dng_vector &v); + + dng_vector_3 (real64 a0, + real64 a1, + real64 a2); + + }; + +/*****************************************************************************/ + +/// \brief A 4-element vector. + +class dng_vector_4: public dng_vector + { + + public: + + dng_vector_4 (); + + dng_vector_4 (const dng_vector &v); + + dng_vector_4 (real64 a0, + real64 a1, + real64 a2, + real64 a3); + + }; + +/*****************************************************************************/ + +dng_matrix operator* (const dng_matrix &A, + const dng_matrix &B); + +dng_vector operator* (const dng_matrix &A, + const dng_vector &B); + +dng_matrix operator* (real64 scale, + const dng_matrix &A); + +dng_vector operator* (real64 scale, + const dng_vector &A); + +/*****************************************************************************/ + +dng_matrix operator+ (const dng_matrix &A, + const dng_matrix &B); + +/*****************************************************************************/ + +dng_vector operator- (const dng_vector &a, + const dng_vector &b); + +/*****************************************************************************/ + +dng_matrix Transpose (const dng_matrix &A); + +/*****************************************************************************/ + +dng_matrix Invert (const dng_matrix &A); + +dng_matrix Invert (const dng_matrix &A, + const dng_matrix &hint); + +/*****************************************************************************/ + +inline real64 MaxEntry (const dng_matrix &A) + { + return A.MaxEntry (); + } + +inline real64 MaxEntry (const dng_vector &A) + { + return A.MaxEntry (); + } + +/*****************************************************************************/ + +inline real64 MinEntry (const dng_matrix &A) + { + return A.MinEntry (); + } + +inline real64 MinEntry (const dng_vector &A) + { + return A.MinEntry (); + } + +/*****************************************************************************/ + +real64 Dot (const dng_vector &a, + const dng_vector &b); + +/*****************************************************************************/ + +real64 Distance (const dng_vector &a, + const dng_vector &b); + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_memory.cpp b/dng/dng_memory.cpp new file mode 100644 index 0000000..48e6bdd --- /dev/null +++ b/dng/dng_memory.cpp @@ -0,0 +1,284 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_memory.h" + +#include "dng_bottlenecks.h" +#include "dng_exceptions.h" + +#ifdef _MSC_VER +#include +#endif + +/*****************************************************************************/ + +dng_memory_data::dng_memory_data () + + : fBuffer (NULL) + + { + + } + +/*****************************************************************************/ + +dng_memory_data::dng_memory_data (uint32 size) + + : fBuffer (NULL) + + { + + Allocate (size); + + } + +/*****************************************************************************/ + +dng_memory_data::dng_memory_data (const dng_safe_uint32 &size) + + : fBuffer (NULL) + + { + + Allocate (size.Get ()); + + } + +/*****************************************************************************/ + +dng_memory_data::dng_memory_data (uint32 count, + std::size_t elementSize) + + : fBuffer (NULL) + + { + + Allocate (count, elementSize); + + } + +/*****************************************************************************/ + +dng_memory_data::~dng_memory_data () + { + + Clear (); + + } + +/*****************************************************************************/ + +void dng_memory_data::Allocate (uint32 size) + { + + Clear (); + + if (size) + { + //printf("Calling malloc from %s\n", __FUNCTION__); + fBuffer = (char *) malloc (size); + + if (!fBuffer) + { + + ThrowMemoryFull (); + + } + + } + + } + +/*****************************************************************************/ + +void dng_memory_data::Allocate (const dng_safe_uint32 &size) + { + + Allocate (size.Get ()); + + } + +/*****************************************************************************/ + +void dng_memory_data::Allocate (uint32 count, + std::size_t elementSize) + { + + // Convert elementSize to a uint32. + + const uint32 elementSizeAsUint32 = static_cast (elementSize); + + if (static_cast (elementSizeAsUint32) != elementSize) + { + ThrowOverflow ("elementSize overflow"); + } + + // Compute required number of bytes and allocate memory. + + dng_safe_uint32 numBytes = dng_safe_uint32 (count) * elementSizeAsUint32; + + Allocate (numBytes.Get ()); + + } + +/*****************************************************************************/ + +void dng_memory_data::Allocate (const dng_safe_uint32 &count, + std::size_t elementSize) + { + + Allocate (count.Get (), elementSize); + + } + +/*****************************************************************************/ + +void dng_memory_data::Clear () + { + + if (fBuffer) + { + + free (fBuffer); + + fBuffer = NULL; + + } + + } + +/*****************************************************************************/ + +dng_memory_block * dng_memory_block::Clone (dng_memory_allocator &allocator) const + { + + uint32 size = LogicalSize (); + + dng_memory_block * result = allocator.Allocate (size); + + DoCopyBytes (Buffer (), result->Buffer (), size); + + return result; + + } + +/*****************************************************************************/ + +dng_malloc_block::dng_malloc_block (uint32 logicalSize) + + : dng_memory_block (logicalSize) + + , fMalloc (NULL) + + { + + #if qLinux + + // TO DO: Need to change this alignment for AVX support? + + int err = ::posix_memalign ((void **) &fMalloc, + 16, + (size_t) PhysicalSize ()); + + if (err) + { + + ThrowMemoryFull (); + + } + + #elif qAndroid + + fMalloc = memalign (16, (size_t) PhysicalSize ()); + + if (!fMalloc) + { + + ThrowMemoryFull (); + + } + + #else + + //fMalloc = (char *) VirtualAlloc (NULL, PhysicalSize (), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + //printf("Calling malloc from %s\n", __FUNCTION__); + fMalloc = (char *)malloc(PhysicalSize()); + + if (!fMalloc) + { + + ThrowMemoryFull (); + + } + + //*(size_t*)(fMalloc) = size_t(PhysicalSize() + 16); + //fMalloc = (char*)fMalloc+16; + + #endif + + SetBuffer (fMalloc); + + } + +/*****************************************************************************/ + +dng_malloc_block::~dng_malloc_block () + { + + if (fMalloc) + { + + //size_t size = *(size_t*)((char*)fMalloc - 16); + //VirtualFree(fMalloc, 0, MEM_RELEASE); + free (fMalloc); + + } + + } + +/*****************************************************************************/ + +dng_memory_block * dng_memory_allocator::Allocate (uint32 size) + { + + dng_memory_block *result = new dng_malloc_block (size); + + if (!result) + { + + ThrowMemoryFull (); + + } + + return result; + + } + +/*****************************************************************************/ + +void * dng_memory_allocator::Malloc (size_t size) + { + + return malloc (size); + + } + +/*****************************************************************************/ + +void dng_memory_allocator::Free (void *ptr) + { + + free (ptr); + + } + +/*****************************************************************************/ + +dng_memory_allocator gDefaultDNGMemoryAllocator; + +/*****************************************************************************/ diff --git a/dng/dng_memory.h b/dng/dng_memory.h new file mode 100644 index 0000000..c9b10f3 --- /dev/null +++ b/dng/dng_memory.h @@ -0,0 +1,652 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** Support for memory allocation. + */ + +/*****************************************************************************/ + +#ifndef __dng_memory__ +#define __dng_memory__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_exceptions.h" +#include "dng_flags.h" +#include "dng_safe_arithmetic.h" +#include "dng_types.h" +#include "dng_uncopyable.h" + +#include +#include + +/*****************************************************************************/ + +#if qDNGAVXSupport + #define DNG_ALIGN_SIMD(x) ((((uintptr) (x)) + 31) & ~((uintptr) 31)) +#else + #define DNG_ALIGN_SIMD(x) ((((uintptr) (x)) + 15) & ~((uintptr) 15)) +#endif + +/*****************************************************************************/ + +/// \brief Class to provide resource acquisition is instantiation discipline +/// for small memory allocations. +/// +/// This class does not use dng_memory_allocator for memory allocation. + +class dng_memory_data: private dng_uncopyable + { + + private: + + char *fBuffer; + + public: + + /// Construct an empty memory buffer using malloc. + /// \exception dng_memory_full with fErrorCode equal to dng_error_memory. + + dng_memory_data (); + + /// Construct memory buffer of size bytes using malloc. + /// \param size Number of bytes of memory needed. + /// \exception dng_memory_full with fErrorCode equal to dng_error_memory. + + dng_memory_data (uint32 size); + + dng_memory_data (const dng_safe_uint32 &size); + + /// Note: This constructor is for internal use only and should not be + /// considered part of the DNG SDK API. + /// + /// Construct memory buffer of count elements of elementSize bytes each. + /// \param count Number of elements. + /// \param elementSize Size of each element. + /// \exception dng_memory_full with fErrorCode equal to dng_error_memory. + + dng_memory_data (uint32 count, + std::size_t elementSize); + + /// Release memory buffer using free. + + ~dng_memory_data (); + + /// Clear existing memory buffer and allocate new memory of size bytes. + /// \param size Number of bytes of memory needed. + /// \exception dng_memory_full with fErrorCode equal to dng_error_memory. + + void Allocate (uint32 size); + + void Allocate (const dng_safe_uint32 &size); + + /// Note: This method is for internal use only and should not be + /// considered part of the DNG SDK API. + /// + /// Clear existing memory buffer and allocate new memory of count + /// elements of elementSize bytes each. + /// \param count Number of elements. + /// \param elementSize Size of each element. + /// \exception dng_memory_full with fErrorCode equal to dng_error_memory. + + void Allocate (uint32 count, + std::size_t elementSize); + + void Allocate (const dng_safe_uint32 &count, + std::size_t elementSize); + + /// Release any allocated memory using free. Object is still valid and + /// Allocate can be called again. + + void Clear (); + + /// Return pointer to allocated memory as a void *.. + /// \retval void * valid for as many bytes as were allocated. + + void * Buffer () + { + return fBuffer; + } + + /// Return pointer to allocated memory as a const void *. + /// \retval const void * valid for as many bytes as were allocated. + + const void * Buffer () const + { + return fBuffer; + } + + /// Return pointer to allocated memory as a char *. + /// \retval char * valid for as many bytes as were allocated. + + char * Buffer_char () + { + return (char *) Buffer (); + } + + /// Return pointer to allocated memory as a const char *. + /// \retval const char * valid for as many bytes as were allocated. + + const char * Buffer_char () const + { + return (const char *) Buffer (); + } + + /// Return pointer to allocated memory as a uint8 *. + /// \retval uint8 * valid for as many bytes as were allocated. + + uint8 * Buffer_uint8 () + { + return (uint8 *) Buffer (); + } + + /// Return pointer to allocated memory as a const uint8 *. + /// \retval const uint8 * valid for as many bytes as were allocated. + + const uint8 * Buffer_uint8 () const + { + return (const uint8 *) Buffer (); + } + + /// Return pointer to allocated memory as a uint16 *. + /// \retval uint16 * valid for as many bytes as were allocated. + + uint16 * Buffer_uint16 () + { + return (uint16 *) Buffer (); + } + + /// Return pointer to allocated memory as a const uint16 *. + /// \retval const uint16 * valid for as many bytes as were allocated. + + const uint16 * Buffer_uint16 () const + { + return (const uint16 *) Buffer (); + } + + /// Return pointer to allocated memory as a int16 *. + /// \retval int16 * valid for as many bytes as were allocated. + + int16 * Buffer_int16 () + { + return (int16 *) Buffer (); + } + + /// Return pointer to allocated memory as a const int16 *. + /// \retval const int16 * valid for as many bytes as were allocated. + + const int16 * Buffer_int16 () const + { + return (const int16 *) Buffer (); + } + + /// Return pointer to allocated memory as a uint32 *. + /// \retval uint32 * valid for as many bytes as were allocated. + + uint32 * Buffer_uint32 () + { + return (uint32 *) Buffer (); + } + + /// Return pointer to allocated memory as a uint32 *. + /// \retval uint32 * valid for as many bytes as were allocated. + + const uint32 * Buffer_uint32 () const + { + return (const uint32 *) Buffer (); + } + + /// Return pointer to allocated memory as a const int32 *. + /// \retval const int32 * valid for as many bytes as were allocated. + + int32 * Buffer_int32 () + { + return (int32 *) Buffer (); + } + + /// Return pointer to allocated memory as a const int32 *. + /// \retval const int32 * valid for as many bytes as were allocated. + + const int32 * Buffer_int32 () const + { + return (const int32 *) Buffer (); + } + + /// Return pointer to allocated memory as a uint64 *. + /// \retval uint64 * valid for as many bytes as were allocated. + + uint64 * Buffer_uint64 () + { + return (uint64 *) Buffer (); + } + + /// Return pointer to allocated memory as a uint64 *. + /// \retval uint64 * valid for as many bytes as were allocated. + + const uint64 * Buffer_uint64 () const + { + return (const uint64 *) Buffer (); + } + + /// Return pointer to allocated memory as a const int64 *. + /// \retval const int64 * valid for as many bytes as were allocated. + + int64 * Buffer_int64 () + { + return (int64 *) Buffer (); + } + + /// Return pointer to allocated memory as a const int64 *. + /// \retval const int64 * valid for as many bytes as were allocated. + + const int64 * Buffer_int64 () const + { + return (const int64 *) Buffer (); + } + + /// Return pointer to allocated memory as a real32 *. + /// \retval real32 * valid for as many bytes as were allocated. + + real32 * Buffer_real32 () + { + return (real32 *) Buffer (); + } + + /// Return pointer to allocated memory as a const real32 *. + /// \retval const real32 * valid for as many bytes as were allocated. + + const real32 * Buffer_real32 () const + { + return (const real32 *) Buffer (); + } + + /// Return pointer to allocated memory as a real64 *. + /// \retval real64 * valid for as many bytes as were allocated. + + real64 * Buffer_real64 () + { + return (real64 *) Buffer (); + } + + /// Return pointer to allocated memory as a const real64 *. + /// \retval const real64 * valid for as many bytes as were allocated. + + const real64 * Buffer_real64 () const + { + return (const real64 *) Buffer (); + } + + }; + +/*****************************************************************************/ + +/// \brief Class to provide resource acquisition is instantiation discipline for +/// image buffers and other larger memory allocations. +/// +/// This class requires a dng_memory_allocator for allocation. + +class dng_memory_block: private dng_uncopyable + { + + private: + + uint32 fLogicalSize; + + char *fBuffer; + + protected: + + dng_memory_block (uint32 logicalSize) + : fLogicalSize (logicalSize) + , fBuffer (NULL) + { + } + + uint32 PhysicalSize () + { + + // This size is padded for TWO reasons! The first is allow + // alignment to 16-byte boundaries if the allocator does not do + // that already. The second, which is very important, so to + // provide safe overread areas for SSE2-type bottlenecks, which + // can often be written faster by allowing them to reading + // slightly block. Someone on the image core them did not + // understand this and removed this padding. I'm undoing this + // removal and restoring this padding, since removing it might + // lead to memory access crashes in some cases. + // + // Please do NOT change the following padding unless you are very + // sure what you are doing. + + dng_safe_uint32 safeLogicalSize (fLogicalSize); + + #if qDNGAVXSupport + + // Allow 32-byte alignment + 64-byte overread in both directions: + // 32 + 64 + 64 = 160. + + return (safeLogicalSize + 160u).Get (); + + #else + + // Allow 16-byte alignment + overread. + + return (safeLogicalSize + 64u).Get (); + + #endif // qDNGAVXSupport + + } + + void SetBuffer (void *p) + { + fBuffer = (char *) DNG_ALIGN_SIMD (p); + } + + public: + + virtual ~dng_memory_block () + { + } + + dng_memory_block * Clone (dng_memory_allocator &allocator) const; + + /// Getter for available size, in bytes, of memory block. + /// \retval size in bytes of available memory in memory block. + + uint32 LogicalSize () const + { + return fLogicalSize; + } + + /// Return pointer to allocated memory as a void *.. + /// \retval void * valid for as many bytes as were allocated. + + void * Buffer () + { + return fBuffer; + } + + /// Return pointer to allocated memory as a const void *. + /// \retval const void * valid for as many bytes as were allocated. + + const void * Buffer () const + { + return fBuffer; + } + + /// Return pointer to allocated memory as a char *. + /// \retval char * valid for as many bytes as were allocated. + + char * Buffer_char () + { + return (char *) Buffer (); + } + + /// Return pointer to allocated memory as a const char *. + /// \retval const char * valid for as many bytes as were allocated. + + const char * Buffer_char () const + { + return (const char *) Buffer (); + } + + /// Return pointer to allocated memory as a uint8 *. + /// \retval uint8 * valid for as many bytes as were allocated. + + uint8 * Buffer_uint8 () + { + return (uint8 *) Buffer (); + } + + /// Return pointer to allocated memory as a const uint8 *. + /// \retval const uint8 * valid for as many bytes as were allocated. + + const uint8 * Buffer_uint8 () const + { + return (const uint8 *) Buffer (); + } + + /// Return pointer to allocated memory as a uint16 *. + /// \retval uint16 * valid for as many bytes as were allocated. + + uint16 * Buffer_uint16 () + { + return (uint16 *) Buffer (); + } + + /// Return pointer to allocated memory as a const uint16 *. + /// \retval const uint16 * valid for as many bytes as were allocated. + + const uint16 * Buffer_uint16 () const + { + return (const uint16 *) Buffer (); + } + + /// Return pointer to allocated memory as a int16 *. + /// \retval int16 * valid for as many bytes as were allocated. + + int16 * Buffer_int16 () + { + return (int16 *) Buffer (); + } + + /// Return pointer to allocated memory as a const int16 *. + /// \retval const int16 * valid for as many bytes as were allocated. + + const int16 * Buffer_int16 () const + { + return (const int16 *) Buffer (); + } + + /// Return pointer to allocated memory as a uint32 *. + /// \retval uint32 * valid for as many bytes as were allocated. + + uint32 * Buffer_uint32 () + { + return (uint32 *) Buffer (); + } + + /// Return pointer to allocated memory as a const uint32 *. + /// \retval const uint32 * valid for as many bytes as were allocated. + + const uint32 * Buffer_uint32 () const + { + return (const uint32 *) Buffer (); + } + + /// Return pointer to allocated memory as a int32 *. + /// \retval int32 * valid for as many bytes as were allocated. + + int32 * Buffer_int32 () + { + return (int32 *) Buffer (); + } + + /// Return pointer to allocated memory as a const int32 *. + /// \retval const int32 * valid for as many bytes as were allocated. + + const int32 * Buffer_int32 () const + { + return (const int32 *) Buffer (); + } + + /// Return pointer to allocated memory as a real32 *. + /// \retval real32 * valid for as many bytes as were allocated. + + real32 * Buffer_real32 () + { + return (real32 *) Buffer (); + } + + /// Return pointer to allocated memory as a const real32 *. + /// \retval const real32 * valid for as many bytes as were allocated. + + const real32 * Buffer_real32 () const + { + return (const real32 *) Buffer (); + } + + /// Return pointer to allocated memory as a real64 *. + /// \retval real64 * valid for as many bytes as were allocated. + + real64 * Buffer_real64 () + { + return (real64 *) Buffer (); + } + + /// Return pointer to allocated memory as a const real64 *. + /// \retval const real64 * valid for as many bytes as were allocated. + + const real64 * Buffer_real64 () const + { + return (const real64 *) Buffer (); + } + + }; + +/*****************************************************************************/ + +/// \brief Interface for dng_memory_block allocator. + +class dng_memory_allocator + { + + public: + + virtual ~dng_memory_allocator () + { + } + + /// Allocate a dng_memory block. + /// \param size Number of bytes in memory block. + /// \retval A dng_memory_block with at least size bytes of valid storage. + /// \exception dng_exception with fErrorCode equal to dng_error_memory. + + virtual dng_memory_block * Allocate (uint32 size); + + /// Directly allocate a block of at least 'size' bytes. + /// \param size Number of bytes in memory block. + /// \retval A pointer to a contiguous block of memory with at least + /// size bytes of valid storage. + /// Caller is responsible for freeing the memory with Free. + /// Default implementation uses standard library 'malloc' routine. + + virtual void * Malloc (size_t size); + + /// Free the specified block of memory previously allocated with Malloc. + /// Default implementation uses standard library 'free' routine. + + virtual void Free (void *ptr); + + }; + +/*****************************************************************************/ + +class dng_malloc_block : public dng_memory_block + { + + private: + + void *fMalloc; + + public: + + dng_malloc_block (uint32 logicalSize); + + virtual ~dng_malloc_block (); + + }; + +/*****************************************************************************/ + +/// \brief Default memory allocator used if NULL is passed in for allocator +/// when constructing a dng_host. +/// +/// Uses new and delete for memory block object and malloc/free for underlying +/// buffer. + +extern dng_memory_allocator gDefaultDNGMemoryAllocator; + +/*****************************************************************************/ + +/// \brief C++ allocator (i.e. an implementation of the Allocator concept) +/// that throws a dng_exception with error code dng_error_memory if it cannot +/// allocate memory. + +template +class dng_std_allocator + { + + public: + + typedef T value_type; + + #if defined(_MSC_VER) && _MSC_VER >= 1900 + + // Default implementations of default constructor and copy + // constructor. + + dng_std_allocator () = default; + + // dng_std_allocator (const dng_std_allocator &) = default; + + template dng_std_allocator (const dng_std_allocator &) {} + + #endif + + T * allocate (size_t n) + { + const size_t size = SafeSizetMult (n, sizeof (T)); + T *retval = static_cast (malloc (size)); + if (!retval) + { + ThrowMemoryFull (); + } + return retval; + } + + void deallocate (T *ptr, + size_t /* n */) + { + free (ptr); + } + + }; + +template +bool operator== (const dng_std_allocator & /* a1 */, + const dng_std_allocator & /* a2 */) + { + return true; + } + +template +bool operator!= (const dng_std_allocator & /* a1 */, + const dng_std_allocator & /* a2 */) + { + return false; + } + +/*****************************************************************************/ + +// std::vector specialized to use dng_std_allocator for allocation. + +#if 0 +// original implementation without using custom allocator +#define dng_std_vector std::vector +#else +// preferred implementation using custom allocator, requires C++11 +template using dng_std_vector = std::vector >; +#endif + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_memory_stream.cpp b/dng/dng_memory_stream.cpp new file mode 100644 index 0000000..25cbb7a --- /dev/null +++ b/dng/dng_memory_stream.cpp @@ -0,0 +1,282 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_memory_stream.h" + +#include "dng_bottlenecks.h" +#include "dng_exceptions.h" +#include "dng_safe_arithmetic.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_memory_stream::dng_memory_stream (dng_memory_allocator &allocator, + dng_abort_sniffer *sniffer, + uint32 pageSize) + + : dng_stream (sniffer, + kDefaultBufferSize, + kDNGStreamInvalidOffset) + + , fAllocator (allocator) + , fPageSize (pageSize ) + + , fPageCount (0) + , fPagesAllocated (0) + , fPageList (NULL) + + , fMemoryStreamLength (0) + + , fLengthLimit (0) + + { + + } + +/*****************************************************************************/ + +dng_memory_stream::~dng_memory_stream () + { + + if (fPageList) + { + + for (uint32 index = 0; index < fPageCount; index++) + { + + delete fPageList [index]; + + } + + free (fPageList); + + } + + } + +/*****************************************************************************/ + +uint64 dng_memory_stream::DoGetLength () + { + + return fMemoryStreamLength; + + } + +/*****************************************************************************/ + +void dng_memory_stream::DoRead (void *data, + uint32 count, + uint64 offset) + { + + if (offset + count > fMemoryStreamLength) + { + + ThrowEndOfFile (); + + } + + uint64 baseOffset = offset; + + while (count) + { + + uint32 pageIndex = (uint32) (offset / fPageSize); + uint32 pageOffset = (uint32) (offset % fPageSize); + + uint32 blockCount = Min_uint32 (fPageSize - pageOffset, count); + + const uint8 *sPtr = fPageList [pageIndex]->Buffer_uint8 () + + pageOffset; + + uint8 *dPtr = ((uint8 *) data) + (uint32) (offset - baseOffset); + + DoCopyBytes (sPtr, dPtr, blockCount); + + offset += blockCount; + count -= blockCount; + + } + + } + +/*****************************************************************************/ + +void dng_memory_stream::DoSetLength (uint64 length) + { + + if (fLengthLimit && length > fLengthLimit) + { + + Throw_dng_error (dng_error_end_of_file, + "dng_memory_stream::fLengthLimit", + NULL, + true); + + } + + while (length > fPageCount * (uint64) fPageSize) + { + + if (fPageCount == fPagesAllocated) + { + + uint32 newSizeTemp1 = 0; + uint32 newSizeTemp2 = 0; + + if (!SafeUint32Add (fPagesAllocated, 32u, &newSizeTemp1) || + !SafeUint32Mult (fPagesAllocated, 2u, &newSizeTemp2)) + { + ThrowOverflow ("Arithmetic overflow in DoSetLength"); + } + + uint32 newSize = Max_uint32 (newSizeTemp1, + newSizeTemp2); + + uint32 numBytes; + + if (!SafeUint32Mult (newSize, + sizeof (dng_memory_block *), + &numBytes)) + { + ThrowOverflow ("Arithmetic overflow in DoSetLength"); + } + + dng_memory_block **list = (dng_memory_block **) malloc (numBytes); + + if (!list) + { + + ThrowMemoryFull (); + + } + + if (fPageCount) + { + + // The multiplication here is safe against overflow. + // fPageCount can never reach a value that is large enough to + // cause overflow because the computation of numBytes above + // would fail before a list of that size could be allocated. + + DoCopyBytes (fPageList, + list, + fPageCount * (uint32) sizeof (dng_memory_block *)); + + } + + if (fPageList) + { + + free (fPageList); + + } + + fPageList = list; + + fPagesAllocated = newSize; + + } + + fPageList [fPageCount] = fAllocator.Allocate (fPageSize); + + fPageCount++; + + } + + fMemoryStreamLength = length; + + } + +/*****************************************************************************/ + +void dng_memory_stream::DoWrite (const void *data, + uint32 count, + uint64 offset) + { + + DoSetLength (Max_uint64 (fMemoryStreamLength, + offset + count)); + + uint64 baseOffset = offset; + + while (count) + { + + uint32 pageIndex = (uint32) (offset / fPageSize); + uint32 pageOffset = (uint32) (offset % fPageSize); + + uint32 blockCount = Min_uint32 (fPageSize - pageOffset, count); + + const uint8 *sPtr = ((const uint8 *) data) + (uint32) (offset - baseOffset); + + uint8 *dPtr = fPageList [pageIndex]->Buffer_uint8 () + + pageOffset; + + DoCopyBytes (sPtr, dPtr, blockCount); + + offset += blockCount; + count -= blockCount; + + } + + } + +/*****************************************************************************/ + +void dng_memory_stream::CopyToStream (dng_stream &dstStream, + uint64 count) + { + + if (count < kBigBufferSize) + { + + dng_stream::CopyToStream (dstStream, count); + + } + + else + { + + Flush (); + + uint64 offset = Position (); + + if (offset + count > Length ()) + { + + ThrowEndOfFile (); + + } + + while (count) + { + + uint32 pageIndex = (uint32) (offset / fPageSize); + uint32 pageOffset = (uint32) (offset % fPageSize); + + uint32 blockCount = (uint32) Min_uint64 (fPageSize - pageOffset, count); + + const uint8 *sPtr = fPageList [pageIndex]->Buffer_uint8 () + + pageOffset; + + dstStream.Put (sPtr, blockCount); + + offset += blockCount; + count -= blockCount; + + } + + SetReadPosition (offset); + + } + + } + +/*****************************************************************************/ diff --git a/dng/dng_memory_stream.h b/dng/dng_memory_stream.h new file mode 100644 index 0000000..e0e83a2 --- /dev/null +++ b/dng/dng_memory_stream.h @@ -0,0 +1,93 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Stream abstraction to/from in-memory data. + */ + +/*****************************************************************************/ + +#ifndef __dng_memory_stream__ +#define __dng_memory_stream__ + +/*****************************************************************************/ + +#include "dng_stream.h" + +/*****************************************************************************/ + +/// \brief A dng_stream which can be read from or written to memory. +/// +/// Stream is populated via writing and either read or accessed by asking for contents as a pointer. + +class dng_memory_stream: public dng_stream + { + + protected: + + dng_memory_allocator &fAllocator; + + uint32 fPageSize; + + uint32 fPageCount; + uint32 fPagesAllocated; + + dng_memory_block **fPageList; + + uint64 fMemoryStreamLength; + + uint64 fLengthLimit; + + public: + + /// Construct a new memory-based stream. + /// \param allocator Allocator to use to allocate memory in stream as needed. + /// \param sniffer If non-NULL used to check for user cancellation. + /// \param pageSize Unit of allocation for data stored in stream. + + dng_memory_stream (dng_memory_allocator &allocator, + dng_abort_sniffer *sniffer = NULL, + uint32 pageSize = 64 * 1024); + + virtual ~dng_memory_stream (); + + /// Sets a maximum length limit. + + void SetLengthLimit (uint64 limit) + { + fLengthLimit = limit; + } + + /// Copy a specified number of bytes to a target stream. + /// \param dstStream The target stream. + /// \param count The number of bytes to copy. + + virtual void CopyToStream (dng_stream &dstStream, + uint64 count); + + protected: + + virtual uint64 DoGetLength (); + + virtual void DoRead (void *data, + uint32 count, + uint64 offset); + + virtual void DoSetLength (uint64 length); + + virtual void DoWrite (const void *data, + uint32 count, + uint64 offset); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_misc_opcodes.cpp b/dng/dng_misc_opcodes.cpp new file mode 100644 index 0000000..2ac072d --- /dev/null +++ b/dng/dng_misc_opcodes.cpp @@ -0,0 +1,1552 @@ +/*****************************************************************************/ +// Copyright 2008-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_misc_opcodes.h" + +#include "dng_bottlenecks.h" +#include "dng_exceptions.h" +#include "dng_globals.h" +#include "dng_host.h" +#include "dng_image.h" +#include "dng_negative.h" +#include "dng_rect.h" +#include "dng_safe_arithmetic.h" +#include "dng_stream.h" +#include "dng_tag_values.h" + +/*****************************************************************************/ + +dng_opcode_TrimBounds::dng_opcode_TrimBounds (const dng_rect &bounds) + + : dng_opcode (dngOpcode_TrimBounds, + dngVersion_1_3_0_0, + kFlag_None) + + , fBounds (bounds) + + { + + } + +/*****************************************************************************/ + +dng_opcode_TrimBounds::dng_opcode_TrimBounds (dng_stream &stream) + + : dng_opcode (dngOpcode_TrimBounds, + stream, + "TrimBounds") + + , fBounds () + + { + + if (stream.Get_uint32 () != 16) + { + ThrowBadFormat (); + } + + fBounds.t = stream.Get_int32 (); + fBounds.l = stream.Get_int32 (); + fBounds.b = stream.Get_int32 (); + fBounds.r = stream.Get_int32 (); + + if (fBounds.IsEmpty ()) + { + ThrowBadFormat (); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Bounds: t=%d, l=%d, b=%d, r=%d\n", + (int) fBounds.t, + (int) fBounds.l, + (int) fBounds.b, + (int) fBounds.r); + + } + + #endif + + } + +/*****************************************************************************/ + +void dng_opcode_TrimBounds::PutData (dng_stream &stream) const + { + + stream.Put_uint32 (16); + + stream.Put_int32 (fBounds.t); + stream.Put_int32 (fBounds.l); + stream.Put_int32 (fBounds.b); + stream.Put_int32 (fBounds.r); + + } + +/*****************************************************************************/ + +void dng_opcode_TrimBounds::Apply (dng_host & /* host */, + dng_negative & /* negative */, + AutoPtr &image) + { + + if (fBounds.IsEmpty () || (fBounds & image->Bounds ()) != fBounds) + { + ThrowBadFormat (); + } + + image->Trim (fBounds); + + } + +/*****************************************************************************/ + +void dng_area_spec::GetData (dng_stream &stream) + { + + fArea.t = stream.Get_int32 (); + fArea.l = stream.Get_int32 (); + fArea.b = stream.Get_int32 (); + fArea.r = stream.Get_int32 (); + + fPlane = stream.Get_uint32 (); + fPlanes = stream.Get_uint32 (); + + fRowPitch = stream.Get_uint32 (); + fColPitch = stream.Get_uint32 (); + + if (fPlanes < 1) + { + ThrowBadFormat (); + } + + if (fRowPitch < 1 || fColPitch < 1) + { + ThrowBadFormat (); + } + + if (fRowPitch >= fArea.H () || fColPitch >= fArea.W ()) + { + + DNG_REPORT ("Bad rowPitch or colPitch in dng_area_spec::GetData"); + + fRowPitch = Min_uint32 (fRowPitch, fArea.H ()); + fColPitch = Min_uint32 (fColPitch, fArea.W ()); + + } + + if (fArea.IsEmpty ()) + { + + if (fRowPitch != 1 || fColPitch != 1) + { + ThrowBadFormat (); + } + + } + + else + { + + int32 width = 0; + int32 height = 0; + + if (!SafeInt32Sub (fArea.b, fArea.t, &height) || + !SafeInt32Sub (fArea.r, fArea.l, &width) || + fRowPitch > static_cast (height) || + fColPitch > static_cast (width)) + { + ThrowBadFormat (); + } + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("AreaSpec: t=%d, l=%d, b=%d, r=%d, p=%u:%u, rp=%u, cp=%u\n", + (int) fArea.t, + (int) fArea.l, + (int) fArea.b, + (int) fArea.r, + (unsigned) fPlane, + (unsigned) fPlanes, + (unsigned) fRowPitch, + (unsigned) fColPitch); + + } + + #endif + + } + +/*****************************************************************************/ + +void dng_area_spec::PutData (dng_stream &stream) const + { + + stream.Put_int32 (fArea.t); + stream.Put_int32 (fArea.l); + stream.Put_int32 (fArea.b); + stream.Put_int32 (fArea.r); + + stream.Put_uint32 (fPlane); + stream.Put_uint32 (fPlanes); + + stream.Put_uint32 (fRowPitch); + stream.Put_uint32 (fColPitch); + + } + +/*****************************************************************************/ + +dng_rect dng_area_spec::Overlap (const dng_rect &tile) const + { + + // Special case - if the fArea is empty, then dng_area_spec covers + // the entire image, no matter how large it is. + + if (fArea.IsEmpty ()) + { + return tile; + } + + dng_rect overlap = fArea & tile; + + if (overlap.NotEmpty ()) + { + + overlap.t = fArea.t + ((overlap.t - fArea.t + fRowPitch - 1) / fRowPitch) * fRowPitch; + overlap.l = fArea.l + ((overlap.l - fArea.l + fColPitch - 1) / fColPitch) * fColPitch; + + if (overlap.NotEmpty ()) + { + + overlap.b = overlap.t + ((overlap.H () - 1) / fRowPitch) * fRowPitch + 1; + overlap.r = overlap.l + ((overlap.W () - 1) / fColPitch) * fColPitch + 1; + + return overlap; + + } + + } + + return dng_rect (); + + } + +/*****************************************************************************/ + +dng_opcode_MapTable::dng_opcode_MapTable (dng_host &host, + const dng_area_spec &areaSpec, + const uint16 *table, + uint32 count) + + : dng_inplace_opcode (dngOpcode_MapTable, + dngVersion_1_3_0_0, + kFlag_None) + + , fAreaSpec (areaSpec) + , fTable () + , fCount (count) + + , fBlackAdjustedTable () + + { + + if (count == 0 || count > 0x10000) + { + ThrowProgramError (); + } + + fTable.Reset (host.Allocate (0x10000 * sizeof (uint16))); + + DoCopyBytes (table, + fTable->Buffer (), + count * (uint32) sizeof (uint16)); + + ReplicateLastEntry (); + + } + +/*****************************************************************************/ + +dng_opcode_MapTable::dng_opcode_MapTable (dng_host &host, + dng_stream &stream) + + : dng_inplace_opcode (dngOpcode_MapTable, + stream, + "MapTable") + + , fAreaSpec () + , fTable () + , fCount (0) + + , fBlackAdjustedTable () + + { + + uint32 dataSize = stream.Get_uint32 (); + + fAreaSpec.GetData (stream); + + fCount = stream.Get_uint32 (); + + if (dataSize != dng_area_spec::kDataSize + 4 + fCount * 2) + { + ThrowBadFormat (); + } + + if (fCount == 0 || fCount > 0x10000) + { + ThrowBadFormat (); + } + + fTable.Reset (host.Allocate (0x10000 * sizeof (uint16))); + + uint16 *table = fTable->Buffer_uint16 (); + + for (uint32 index = 0; index < fCount; index++) + { + table [index] = stream.Get_uint16 (); + } + + ReplicateLastEntry (); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Count: %u\n", (unsigned) fCount); + + for (uint32 j = 0; j < fCount && j < gDumpLineLimit; j++) + { + printf (" Table [%5u] = %5u\n", (unsigned) j, (unsigned) table [j]); + } + + if (fCount > gDumpLineLimit) + { + printf (" ... %u table entries skipped\n", (unsigned) (fCount - gDumpLineLimit)); + } + + } + + #endif + + } + +/*****************************************************************************/ + +void dng_opcode_MapTable::ReplicateLastEntry () + { + + uint16 *table = fTable->Buffer_uint16 (); + + uint16 lastEntry = table [fCount]; + + for (uint32 index = fCount; index < 0x10000; index++) + { + table [index] = lastEntry; + } + + } + +/*****************************************************************************/ + +void dng_opcode_MapTable::PutData (dng_stream &stream) const + { + + stream.Put_uint32 (dng_area_spec::kDataSize + 4 + fCount * 2); + + fAreaSpec.PutData (stream); + + stream.Put_uint32 (fCount); + + uint16 *table = fTable->Buffer_uint16 (); + + for (uint32 index = 0; index < fCount; index++) + { + stream.Put_uint16 (table [index]); + } + + } + +/*****************************************************************************/ + +uint32 dng_opcode_MapTable::BufferPixelType (uint32 /* imagePixelType */) + { + + return ttShort; + + } + +/*****************************************************************************/ + +dng_rect dng_opcode_MapTable::ModifiedBounds (const dng_rect &imageBounds) + { + + return fAreaSpec.Overlap (imageBounds); + + } + +/*****************************************************************************/ + +void dng_opcode_MapTable::Prepare (dng_negative &negative, + uint32 /* threadCount */, + const dng_point & /* tileSize */, + const dng_rect & /* imageBounds */, + uint32 /* imagePlanes */, + uint32 /* bufferPixelType */, + dng_memory_allocator &allocator) + { + + fBlackAdjustedTable.Reset (); + + int32 blackLevel = negative.Stage3BlackLevel (); + + if (Stage () >= 2 && blackLevel != 0) + { + + fBlackAdjustedTable.Reset (allocator.Allocate (0x10000 * sizeof (uint16))); + + const uint16 *srcTable = fTable->Buffer_uint16 (); + + uint16 *dstTable = fBlackAdjustedTable->Buffer_uint16 (); + + real64 srcScale = 65535.0 / (65535.0 - blackLevel); + + real64 dstScale = (65535.0 - blackLevel) / 65535.0; + + for (int32 index = 0; index < 0x10000; index++) + { + + real64 x = (index - blackLevel) * srcScale; + + real64 y; + + if (x < 0.0) + { + + y = srcTable [0] * 2.0 - (real64) srcTable [Round_uint32 (-x)]; + + } + + else + { + + y = srcTable [Round_uint32 (x)]; + + } + + dstTable [index] = Pin_uint16 (Round_int32 (y * dstScale) + blackLevel); + + } + + } + + } + +/*****************************************************************************/ + +void dng_opcode_MapTable::ProcessArea (dng_negative & /* negative */, + uint32 /* threadIndex */, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect & /* imageBounds */) + { + + dng_rect overlap = fAreaSpec.Overlap (dstArea); + + if (overlap.NotEmpty ()) + { + + const uint16 *table = fBlackAdjustedTable.Get () ? fBlackAdjustedTable->Buffer_uint16 () + : fTable ->Buffer_uint16 (); + + for (uint32 plane = fAreaSpec.Plane (); + plane < fAreaSpec.Plane () + fAreaSpec.Planes () && + plane < buffer.Planes (); + plane++) + { + + DoMapArea16 (buffer.DirtyPixel_uint16 (overlap.t, overlap.l, plane), + 1, + (overlap.H () + fAreaSpec.RowPitch () - 1) / fAreaSpec.RowPitch (), + (overlap.W () + fAreaSpec.ColPitch () - 1) / fAreaSpec.ColPitch (), + 0, + fAreaSpec.RowPitch () * buffer.RowStep (), + fAreaSpec.ColPitch (), + table); + + } + + } + + } + +/*****************************************************************************/ + +dng_opcode_MapPolynomial::dng_opcode_MapPolynomial (const dng_area_spec &areaSpec, + uint32 degree, + const real64 *coefficient) + + : dng_inplace_opcode (dngOpcode_MapPolynomial, + dngVersion_1_3_0_0, + kFlag_None) + + , fAreaSpec (areaSpec) + , fDegree (degree) + + { + + for (uint32 j = 0; j <= kMaxDegree; j++) + { + + if (j <= fDegree) + { + fCoefficient [j] = coefficient [j]; + } + + else + { + fCoefficient [j] = 0.0; + } + + } + + // Reduce degree if possible. + + while (fDegree > 0 && fCoefficient [fDegree] == 0.0) + { + fDegree--; + } + + } + +/*****************************************************************************/ + +dng_opcode_MapPolynomial::dng_opcode_MapPolynomial (dng_stream &stream) + + : dng_inplace_opcode (dngOpcode_MapPolynomial, + stream, + "MapPolynomial") + + , fAreaSpec () + , fDegree (0) + + { + + uint32 dataSize = stream.Get_uint32 (); + + fAreaSpec.GetData (stream); + + fDegree = stream.Get_uint32 (); + + if (dataSize != dng_area_spec::kDataSize + 4 + (fDegree + 1) * 8) + { + ThrowBadFormat (); + } + + if (fDegree > kMaxDegree) + { + ThrowBadFormat (); + } + + for (uint32 j = 0; j <= kMaxDegree; j++) + { + + if (j <= fDegree) + { + fCoefficient [j] = stream.Get_real64 (); + } + else + { + fCoefficient [j] = 0.0; + } + + } + + #if qDNGValidate + + if (gVerbose) + { + + for (uint32 k = 0; k <= fDegree; k++) + { + printf (" Coefficient [%u] = %f\n", (unsigned) k, fCoefficient [k]); + } + + } + + #endif + + } + +/*****************************************************************************/ + +void dng_opcode_MapPolynomial::PutData (dng_stream &stream) const + { + + stream.Put_uint32 (dng_area_spec::kDataSize + 4 + (fDegree + 1) * 8); + + fAreaSpec.PutData (stream); + + stream.Put_uint32 (fDegree); + + for (uint32 j = 0; j <= fDegree; j++) + { + stream.Put_real64 (fCoefficient [j]); + } + + } + +/*****************************************************************************/ + +uint32 dng_opcode_MapPolynomial::BufferPixelType (uint32 imagePixelType) + { + + // If we are operating on the stage 1 image, then we need + // to adjust the coefficients to convert from the image + // values to the 32-bit floating point values that this + // opcode operates on. + + // If we are operating on the stage 2 or 3 image, the logical + // range of the image is already 0.0 to 1.0, so we don't + // need to adjust the values. + + real64 scale32 = 1.0; + + if (Stage () == 1) + { + + switch (imagePixelType) + { + + case ttFloat: + break; + + case ttShort: + { + scale32 = (real64) 0xFFFF; + break; + } + + case ttLong: + { + scale32 = (real64) 0xFFFFFFFF; + break; + } + + default: + ThrowBadFormat (); + + } + + } + + real64 factor32 = 1.0 / scale32; + + for (uint32 j = 0; j <= kMaxDegree; j++) + { + + fCoefficient32 [j] = (real32) (fCoefficient [j] * factor32); + + factor32 *= scale32; + + } + + return ttFloat; + + } + +/*****************************************************************************/ + +dng_rect dng_opcode_MapPolynomial::ModifiedBounds (const dng_rect &imageBounds) + { + + return fAreaSpec.Overlap (imageBounds); + + } + +/*****************************************************************************/ + +void dng_opcode_MapPolynomial::ProcessArea (dng_negative &negative, + uint32 /* threadIndex */, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect & /* imageBounds */) + { + + dng_rect overlap = fAreaSpec.Overlap (dstArea); + + if (overlap.NotEmpty ()) + { + + uint16 blackLevel = Stage () >= 2 ? negative.Stage3BlackLevel () : 0; + + uint32 rowPitch = fAreaSpec.RowPitch (); + uint32 colPitch = fAreaSpec.ColPitch (); + + for (uint32 plane = fAreaSpec.Plane (); + plane < fAreaSpec.Plane () + fAreaSpec.Planes () && + plane < buffer.Planes (); + plane++) + { + + DoProcess (buffer, + overlap, + plane, + rowPitch, + colPitch, + fCoefficient32, + fDegree, + blackLevel); + + } + + } + + } + +/*****************************************************************************/ + +void dng_opcode_MapPolynomial::DoProcess (dng_pixel_buffer &buffer, + const dng_rect &area, + const uint32 plane, + const uint32 rowPitch, + const uint32 colPitch, + const real32 *coefficients, + const uint32 degree, + uint16 blackLevel) const + { + + DoBaselineMapPoly32 (buffer.DirtyPixel_real32 (area.t, + area.l, + plane), + buffer.RowStep () * (int32) rowPitch, + area.H (), + area.W (), + rowPitch, + colPitch, + coefficients, + degree, + blackLevel); + + } + +/*****************************************************************************/ + +dng_opcode_DeltaPerRow::dng_opcode_DeltaPerRow (const dng_area_spec &areaSpec, + AutoPtr &table) + + : dng_inplace_opcode (dngOpcode_DeltaPerRow, + dngVersion_1_3_0_0, + kFlag_None) + + , fAreaSpec (areaSpec) + , fTable () + , fScale (1.0f) + + { + + fTable.Reset (table.Release ()); + + } + +/*****************************************************************************/ + +dng_opcode_DeltaPerRow::dng_opcode_DeltaPerRow (dng_host &host, + dng_stream &stream) + + : dng_inplace_opcode (dngOpcode_DeltaPerRow, + stream, + "DeltaPerRow") + + , fAreaSpec () + , fTable () + , fScale (1.0f) + + { + + uint32 dataSize = stream.Get_uint32 (); + + fAreaSpec.GetData (stream); + + uint32 deltas = SafeUint32DivideUp (fAreaSpec.Area ().H (), + fAreaSpec.RowPitch ()); + + if (deltas != stream.Get_uint32 ()) + { + ThrowBadFormat (); + } + + if (dataSize != dng_area_spec::kDataSize + 4 + deltas * 4) + { + ThrowBadFormat (); + } + + fTable.Reset (host.Allocate + (SafeUint32Mult (deltas, + static_cast (sizeof (real32))))); + + real32 *table = fTable->Buffer_real32 (); + + for (uint32 j = 0; j < deltas; j++) + { + table [j] = stream.Get_real32 (); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Count: %u\n", (unsigned) deltas); + + for (uint32 k = 0; k < deltas && k < gDumpLineLimit; k++) + { + printf (" Delta [%u] = %f\n", (unsigned) k, table [k]); + } + + if (deltas > gDumpLineLimit) + { + printf (" ... %u deltas skipped\n", (unsigned) (deltas - gDumpLineLimit)); + } + + } + + #endif + + } + +/*****************************************************************************/ + +void dng_opcode_DeltaPerRow::PutData (dng_stream &stream) const + { + + uint32 deltas = SafeUint32DivideUp (fAreaSpec.Area ().H (), + fAreaSpec.RowPitch ()); + + stream.Put_uint32 (dng_area_spec::kDataSize + 4 + deltas * 4); + + fAreaSpec.PutData (stream); + + stream.Put_uint32 (deltas); + + real32 *table = fTable->Buffer_real32 (); + + for (uint32 j = 0; j < deltas; j++) + { + stream.Put_real32 (table [j]); + } + + } + +/*****************************************************************************/ + +uint32 dng_opcode_DeltaPerRow::BufferPixelType (uint32 imagePixelType) + { + + real64 scale32 = 1.0; + + switch (imagePixelType) + { + + case ttFloat: + break; + + case ttShort: + { + scale32 = (real64) 0xFFFF; + break; + } + + case ttLong: + { + scale32 = (real64) 0xFFFFFFFF; + break; + } + + default: + ThrowBadFormat (); + + } + + fScale = (real32) (1.0 / scale32); + + return ttFloat; + + } + +/*****************************************************************************/ + +dng_rect dng_opcode_DeltaPerRow::ModifiedBounds (const dng_rect &imageBounds) + { + + return fAreaSpec.Overlap (imageBounds); + + } + +/*****************************************************************************/ + +void dng_opcode_DeltaPerRow::ProcessArea (dng_negative &negative, + uint32 /* threadIndex */, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect & /* imageBounds */) + { + + dng_rect overlap = fAreaSpec.Overlap (dstArea); + + if (overlap.NotEmpty ()) + { + + uint32 cols = overlap.W (); + + uint32 colPitch = fAreaSpec.ColPitch (); + + real32 scale = fScale; + + if (Stage () >= 2 && negative.Stage3BlackLevel () != 0) + { + scale *= (real32) (1.0 - negative.Stage3BlackLevelNormalized ()); + } + + for (uint32 plane = fAreaSpec.Plane (); + plane < fAreaSpec.Plane () + fAreaSpec.Planes () && + plane < buffer.Planes (); + plane++) + { + + const real32 *table = fTable->Buffer_real32 () + + ((overlap.t - fAreaSpec.Area ().t) / + fAreaSpec.RowPitch ()); + + for (int32 row = overlap.t; row < overlap.b; row += fAreaSpec.RowPitch ()) + { + + real32 rowDelta = *(table++) * scale; + + real32 *dPtr = buffer.DirtyPixel_real32 (row, overlap.l, plane); + + for (uint32 col = 0; col < cols; col += colPitch) + { + + real32 x = dPtr [col]; + + real32 y = x + rowDelta; + + dPtr [col] = Pin_real32 (-1.0f, y, 1.0f); + + } + + } + + } + + } + + } + +/*****************************************************************************/ + +dng_opcode_DeltaPerColumn::dng_opcode_DeltaPerColumn (const dng_area_spec &areaSpec, + AutoPtr &table) + + : dng_inplace_opcode (dngOpcode_DeltaPerColumn, + dngVersion_1_3_0_0, + kFlag_None) + + , fAreaSpec (areaSpec) + , fTable () + , fScale (1.0f) + + { + + fTable.Reset (table.Release ()); + + } + +/*****************************************************************************/ + +dng_opcode_DeltaPerColumn::dng_opcode_DeltaPerColumn (dng_host &host, + dng_stream &stream) + + : dng_inplace_opcode (dngOpcode_DeltaPerColumn, + stream, + "DeltaPerColumn") + + , fAreaSpec () + , fTable () + , fScale (1.0f) + + { + + uint32 dataSize = stream.Get_uint32 (); + + fAreaSpec.GetData (stream); + + uint32 deltas = SafeUint32DivideUp (fAreaSpec.Area ().W (), + fAreaSpec.ColPitch ()); + + if (deltas != stream.Get_uint32 ()) + { + ThrowBadFormat (); + } + + if (dataSize != dng_area_spec::kDataSize + 4 + deltas * 4) + { + ThrowBadFormat (); + } + + fTable.Reset (host.Allocate + (SafeUint32Mult (deltas, + static_cast (sizeof (real32))))); + + real32 *table = fTable->Buffer_real32 (); + + for (uint32 j = 0; j < deltas; j++) + { + table [j] = stream.Get_real32 (); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Count: %u\n", (unsigned) deltas); + + for (uint32 k = 0; k < deltas && k < gDumpLineLimit; k++) + { + printf (" Delta [%u] = %f\n", (unsigned) k, table [k]); + } + + if (deltas > gDumpLineLimit) + { + printf (" ... %u deltas skipped\n", (unsigned) (deltas - gDumpLineLimit)); + } + + } + + #endif + + } + +/*****************************************************************************/ + +void dng_opcode_DeltaPerColumn::PutData (dng_stream &stream) const + { + + uint32 deltas = SafeUint32DivideUp (fAreaSpec.Area ().W (), + fAreaSpec.ColPitch ()); + + stream.Put_uint32 (dng_area_spec::kDataSize + 4 + deltas * 4); + + fAreaSpec.PutData (stream); + + stream.Put_uint32 (deltas); + + real32 *table = fTable->Buffer_real32 (); + + for (uint32 j = 0; j < deltas; j++) + { + stream.Put_real32 (table [j]); + } + + } + +/*****************************************************************************/ + +uint32 dng_opcode_DeltaPerColumn::BufferPixelType (uint32 imagePixelType) + { + + real64 scale32 = 1.0; + + switch (imagePixelType) + { + + case ttFloat: + break; + + case ttShort: + { + scale32 = (real64) 0xFFFF; + break; + } + + case ttLong: + { + scale32 = (real64) 0xFFFFFFFF; + break; + } + + default: + ThrowBadFormat (); + + } + + fScale = (real32) (1.0 / scale32); + + return ttFloat; + + } + +/*****************************************************************************/ + +dng_rect dng_opcode_DeltaPerColumn::ModifiedBounds (const dng_rect &imageBounds) + { + + return fAreaSpec.Overlap (imageBounds); + + } + +/*****************************************************************************/ + +void dng_opcode_DeltaPerColumn::ProcessArea (dng_negative &negative, + uint32 /* threadIndex */, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect & /* imageBounds */) + { + + dng_rect overlap = fAreaSpec.Overlap (dstArea); + + if (overlap.NotEmpty ()) + { + + uint32 rows = (overlap.H () + fAreaSpec.RowPitch () - 1) / + fAreaSpec.RowPitch (); + + int32 rowStep = buffer.RowStep () * fAreaSpec.RowPitch (); + + real32 scale = fScale; + + if (Stage () >= 2 && negative.Stage3BlackLevel () != 0) + { + scale *= (real32) (1.0 - negative.Stage3BlackLevelNormalized ()); + } + + for (uint32 plane = fAreaSpec.Plane (); + plane < fAreaSpec.Plane () + fAreaSpec.Planes () && + plane < buffer.Planes (); + plane++) + { + + const real32 *table = fTable->Buffer_real32 () + + ((overlap.l - fAreaSpec.Area ().l) / + fAreaSpec.ColPitch ()); + + for (int32 col = overlap.l; col < overlap.r; col += fAreaSpec.ColPitch ()) + { + + real32 colDelta = *(table++) * scale; + + real32 *dPtr = buffer.DirtyPixel_real32 (overlap.t, col, plane); + + for (uint32 row = 0; row < rows; row++) + { + + real32 x = dPtr [0]; + + real32 y = x + colDelta; + + dPtr [0] = Pin_real32 (-1.0f, y, 1.0f); + + dPtr += rowStep; + + } + + } + + } + + } + + } + +/*****************************************************************************/ + +dng_opcode_ScalePerRow::dng_opcode_ScalePerRow (const dng_area_spec &areaSpec, + AutoPtr &table) + + : dng_inplace_opcode (dngOpcode_ScalePerRow, + dngVersion_1_3_0_0, + kFlag_None) + + , fAreaSpec (areaSpec) + , fTable () + + { + + fTable.Reset (table.Release ()); + + } + +/*****************************************************************************/ + +dng_opcode_ScalePerRow::dng_opcode_ScalePerRow (dng_host &host, + dng_stream &stream) + + : dng_inplace_opcode (dngOpcode_ScalePerRow, + stream, + "ScalePerRow") + + , fAreaSpec () + , fTable () + + { + + uint32 dataSize = stream.Get_uint32 (); + + fAreaSpec.GetData (stream); + + uint32 scales = SafeUint32DivideUp (fAreaSpec.Area ().H (), + fAreaSpec.RowPitch ()); + + if (scales != stream.Get_uint32 ()) + { + ThrowBadFormat (); + } + + if (dataSize != dng_area_spec::kDataSize + 4 + scales * 4) + { + ThrowBadFormat (); + } + + fTable.Reset (host.Allocate + (SafeUint32Mult (scales, + static_cast (sizeof (real32))))); + + real32 *table = fTable->Buffer_real32 (); + + for (uint32 j = 0; j < scales; j++) + { + table [j] = stream.Get_real32 (); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Count: %u\n", (unsigned) scales); + + for (uint32 k = 0; k < scales && k < gDumpLineLimit; k++) + { + printf (" Scale [%u] = %f\n", (unsigned) k, table [k]); + } + + if (scales > gDumpLineLimit) + { + printf (" ... %u scales skipped\n", (unsigned) (scales - gDumpLineLimit)); + } + + } + + #endif + + } + +/*****************************************************************************/ + +void dng_opcode_ScalePerRow::PutData (dng_stream &stream) const + { + + uint32 scales = SafeUint32DivideUp (fAreaSpec.Area ().H (), + fAreaSpec.RowPitch ()); + + stream.Put_uint32 (dng_area_spec::kDataSize + 4 + scales * 4); + + fAreaSpec.PutData (stream); + + stream.Put_uint32 (scales); + + real32 *table = fTable->Buffer_real32 (); + + for (uint32 j = 0; j < scales; j++) + { + stream.Put_real32 (table [j]); + } + + } + +/*****************************************************************************/ + +uint32 dng_opcode_ScalePerRow::BufferPixelType (uint32 /* imagePixelType */) + { + + return ttFloat; + + } + +/*****************************************************************************/ + +dng_rect dng_opcode_ScalePerRow::ModifiedBounds (const dng_rect &imageBounds) + { + + return fAreaSpec.Overlap (imageBounds); + + } + +/*****************************************************************************/ + +void dng_opcode_ScalePerRow::ProcessArea (dng_negative &negative, + uint32 /* threadIndex */, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect & /* imageBounds */) + { + + dng_rect overlap = fAreaSpec.Overlap (dstArea); + + if (overlap.NotEmpty ()) + { + + uint32 cols = overlap.W (); + + uint32 colPitch = fAreaSpec.ColPitch (); + + real32 blackOffset = 0.0f; + + if (Stage () >= 2 && negative.Stage3BlackLevel () != 0) + { + blackOffset = (real32) negative.Stage3BlackLevelNormalized (); + } + + for (uint32 plane = fAreaSpec.Plane (); + plane < fAreaSpec.Plane () + fAreaSpec.Planes () && + plane < buffer.Planes (); + plane++) + { + + const real32 *table = fTable->Buffer_real32 () + + ((overlap.t - fAreaSpec.Area ().t) / + fAreaSpec.RowPitch ()); + + for (int32 row = overlap.t; row < overlap.b; row += fAreaSpec.RowPitch ()) + { + + real32 rowScale = *(table++); + + real32 *dPtr = buffer.DirtyPixel_real32 (row, overlap.l, plane); + + for (uint32 col = 0; col < cols; col += colPitch) + { + + real32 x = dPtr [col]; + + real32 y = (x - blackOffset) * rowScale + blackOffset; + + dPtr [col] = Pin_real32 (-1.0f, y, 1.0f); + + } + + } + + } + + } + + } + +/*****************************************************************************/ + +dng_opcode_ScalePerColumn::dng_opcode_ScalePerColumn (const dng_area_spec &areaSpec, + AutoPtr &table) + + : dng_inplace_opcode (dngOpcode_ScalePerColumn, + dngVersion_1_3_0_0, + kFlag_None) + + , fAreaSpec (areaSpec) + , fTable () + + { + + fTable.Reset (table.Release ()); + + } + +/*****************************************************************************/ + +dng_opcode_ScalePerColumn::dng_opcode_ScalePerColumn (dng_host &host, + dng_stream &stream) + + : dng_inplace_opcode (dngOpcode_ScalePerColumn, + stream, + "ScalePerColumn") + + , fAreaSpec () + , fTable () + + { + + uint32 dataSize = stream.Get_uint32 (); + + fAreaSpec.GetData (stream); + + uint32 scales = SafeUint32DivideUp (fAreaSpec.Area ().W (), + fAreaSpec.ColPitch()); + + if (scales != stream.Get_uint32 ()) + { + ThrowBadFormat (); + } + + if (dataSize != dng_area_spec::kDataSize + 4 + scales * 4) + { + ThrowBadFormat (); + } + + fTable.Reset (host.Allocate + (SafeUint32Mult (scales, + static_cast (sizeof (real32))))); + + real32 *table = fTable->Buffer_real32 (); + + for (uint32 j = 0; j < scales; j++) + { + table [j] = stream.Get_real32 (); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Count: %u\n", (unsigned) scales); + + for (uint32 k = 0; k < scales && k < gDumpLineLimit; k++) + { + printf (" Scale [%u] = %f\n", (unsigned) k, table [k]); + } + + if (scales > gDumpLineLimit) + { + printf (" ... %u deltas skipped\n", (unsigned) (scales - gDumpLineLimit)); + } + + } + + #endif + + } + +/*****************************************************************************/ + +void dng_opcode_ScalePerColumn::PutData (dng_stream &stream) const + { + + uint32 scales = SafeUint32DivideUp (fAreaSpec.Area ().W (), + fAreaSpec.ColPitch ()); + + stream.Put_uint32 (dng_area_spec::kDataSize + 4 + scales * 4); + + fAreaSpec.PutData (stream); + + stream.Put_uint32 (scales); + + real32 *table = fTable->Buffer_real32 (); + + for (uint32 j = 0; j < scales; j++) + { + stream.Put_real32 (table [j]); + } + + } + +/*****************************************************************************/ + +uint32 dng_opcode_ScalePerColumn::BufferPixelType (uint32 /* imagePixelType */) + { + + return ttFloat; + + } + +/*****************************************************************************/ + +dng_rect dng_opcode_ScalePerColumn::ModifiedBounds (const dng_rect &imageBounds) + { + + return fAreaSpec.Overlap (imageBounds); + + } + +/*****************************************************************************/ + +void dng_opcode_ScalePerColumn::ProcessArea (dng_negative &negative, + uint32 /* threadIndex */, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect & /* imageBounds */) + { + + dng_rect overlap = fAreaSpec.Overlap (dstArea); + + if (overlap.NotEmpty ()) + { + + uint32 rows = (overlap.H () + fAreaSpec.RowPitch () - 1) / + fAreaSpec.RowPitch (); + + int32 rowStep = buffer.RowStep () * fAreaSpec.RowPitch (); + + real32 blackOffset = 0.0f; + + if (Stage () >= 2 && negative.Stage3BlackLevel () != 0) + { + blackOffset = (real32) negative.Stage3BlackLevelNormalized (); + } + + for (uint32 plane = fAreaSpec.Plane (); + plane < fAreaSpec.Plane () + fAreaSpec.Planes () && + plane < buffer.Planes (); + plane++) + { + + const real32 *table = fTable->Buffer_real32 () + + ((overlap.l - fAreaSpec.Area ().l) / + fAreaSpec.ColPitch ()); + + for (int32 col = overlap.l; col < overlap.r; col += fAreaSpec.ColPitch ()) + { + + real32 colScale = *(table++); + + real32 *dPtr = buffer.DirtyPixel_real32 (overlap.t, col, plane); + + for (uint32 row = 0; row < rows; row++) + { + + real32 x = dPtr [0]; + + real32 y = (x - blackOffset) * colScale + blackOffset; + + dPtr [0] = Pin_real32 (-1.0f, y, 1.0f); + + dPtr += rowStep; + + } + + } + + } + + } + + } + +/*****************************************************************************/ diff --git a/dng/dng_misc_opcodes.h b/dng/dng_misc_opcodes.h new file mode 100644 index 0000000..447d78b --- /dev/null +++ b/dng/dng_misc_opcodes.h @@ -0,0 +1,445 @@ +/*****************************************************************************/ +// Copyright 2008-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Miscellaneous DNG opcodes. + */ + +/*****************************************************************************/ + +#ifndef __dng_misc_opcodes__ +#define __dng_misc_opcodes__ + +/*****************************************************************************/ + +#include "dng_classes.h" + +#include "dng_opcodes.h" + +/*****************************************************************************/ + +/// \brief Opcode to trim image to a specified rectangle. + +class dng_opcode_TrimBounds: public dng_opcode + { + + private: + + dng_rect fBounds; + + public: + + /// Create opcode to trim image to the specified bounds. + + dng_opcode_TrimBounds (const dng_rect &bounds); + + dng_opcode_TrimBounds (dng_stream &stream); + + virtual void PutData (dng_stream &stream) const; + + virtual void Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image); + + }; + +/*****************************************************************************/ + +/// \brief A class to describe an area of an image, including a pixel subrectangle, +/// plane range, and row/column pitch (e.g., for mosaic images). Useful for +/// specifying opcodes that only apply to specific color planes or pixel types (e.g., +/// only one of the two green Bayer pixels). + +class dng_area_spec + { + + public: + + enum + { + kDataSize = 32 + }; + + private: + + dng_rect fArea; + + uint32 fPlane; + uint32 fPlanes; + + uint32 fRowPitch; + uint32 fColPitch; + + public: + + /// Create an empty area. + + dng_area_spec (const dng_rect &area = dng_rect (), + uint32 plane = 0, + uint32 planes = 1, + uint32 rowPitch = 1, + uint32 colPitch = 1) + + : fArea (area) + , fPlane (plane) + , fPlanes (planes) + , fRowPitch (rowPitch) + , fColPitch (colPitch) + + { + } + + /// The pixel area. + + const dng_rect & Area () const + { + return fArea; + } + + /// The first plane. + + uint32 Plane () const + { + return fPlane; + } + + /// The total number of planes. + + uint32 Planes () const + { + return fPlanes; + } + + /// The row pitch (i.e., stride). A pitch of 1 means all rows. + + uint32 RowPitch () const + { + return fRowPitch; + } + + /// The column pitch (i.e., stride). A pitch of 1 means all columns. + + uint32 ColPitch () const + { + return fColPitch; + } + + /// Read area data from the specified stream. + + void GetData (dng_stream &stream); + + /// Write area data to the specified stream. + + void PutData (dng_stream &stream) const; + + /// Compute and return pixel area overlap (i.e., intersection) between this + /// area and the specified tile. + + dng_rect Overlap (const dng_rect &tile) const; + + }; + +/*****************************************************************************/ + +/// \brief An opcode to apply a 1D function (represented as a 16-bit table) to an +/// image area. + +class dng_opcode_MapTable: public dng_inplace_opcode + { + + private: + + dng_area_spec fAreaSpec; + + AutoPtr fTable; + + uint32 fCount; + + AutoPtr fBlackAdjustedTable; + + public: + + /// Create a MapTable opcode with the specified area, table, and number of + /// table entries. + + dng_opcode_MapTable (dng_host &host, + const dng_area_spec &areaSpec, + const uint16 *table, + uint32 count = 0x10000); + + dng_opcode_MapTable (dng_host &host, + dng_stream &stream); + + virtual void PutData (dng_stream &stream) const; + + virtual uint32 BufferPixelType (uint32 imagePixelType); + + virtual dng_rect ModifiedBounds (const dng_rect &imageBounds); + + virtual void Prepare (dng_negative &negative, + uint32 threadCount, + const dng_point &tileSize, + const dng_rect &imageBounds, + uint32 imagePlanes, + uint32 bufferPixelType, + dng_memory_allocator &allocator); + + virtual void ProcessArea (dng_negative &negative, + uint32 threadIndex, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect &imageBounds); + + private: + + void ReplicateLastEntry (); + + }; + +/*****************************************************************************/ + +/// \brief An opcode to apply a 1D function (represented as a polynomial) to an +/// image area. + +class dng_opcode_MapPolynomial: public dng_inplace_opcode + { + + public: + + enum + { + kMaxDegree = 8 + }; + + protected: + + dng_area_spec fAreaSpec; + + uint32 fDegree; + + real64 fCoefficient [kMaxDegree + 1]; + + real32 fCoefficient32 [kMaxDegree + 1]; + + public: + + /// Create a MapPolynomial opcode with the specified area, polynomial + /// degree, and polynomial coefficients. The function that will be + /// applied to each pixel x is: + /// + /// f (x) = coefficient [0] + ((x * coefficient [1]) + + /// (x^2 * coefficient [2]) + + /// (x^3 * coefficient [3]) + + /// (x^4 * coefficient [4]) ... + + dng_opcode_MapPolynomial (const dng_area_spec &areaSpec, + uint32 degree, + const real64 *coefficient); + + dng_opcode_MapPolynomial (dng_stream &stream); + + virtual void PutData (dng_stream &stream) const; + + virtual uint32 BufferPixelType (uint32 imagePixelType); + + virtual dng_rect ModifiedBounds (const dng_rect &imageBounds); + + virtual void ProcessArea (dng_negative &negative, + uint32 threadIndex, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect &imageBounds); + + uint32 Degree () const + { + return fDegree; + } + + const real64 * Coefficients () const + { + return fCoefficient; + } + + protected: + + virtual void DoProcess (dng_pixel_buffer &buffer, + const dng_rect &area, + const uint32 plane, + const uint32 rowPitch, + const uint32 colPitch, + const real32 *coefficients, + const uint32 degree, + uint16 blackLevel) const; + + }; + +/*****************************************************************************/ + +/// \brief An opcode to apply a delta (i.e., offset) that varies per row. Within +/// a row, the same delta value is applied to all specified pixels. + +class dng_opcode_DeltaPerRow: public dng_inplace_opcode + { + + private: + + dng_area_spec fAreaSpec; + + AutoPtr fTable; + + real32 fScale; + + public: + + /// Create a DeltaPerRow opcode with the specified area and row deltas + /// (specified as a table of 32-bit floats). + + dng_opcode_DeltaPerRow (const dng_area_spec &areaSpec, + AutoPtr &table); + + dng_opcode_DeltaPerRow (dng_host &host, + dng_stream &stream); + + virtual void PutData (dng_stream &stream) const; + + virtual uint32 BufferPixelType (uint32 imagePixelType); + + virtual dng_rect ModifiedBounds (const dng_rect &imageBounds); + + virtual void ProcessArea (dng_negative &negative, + uint32 threadIndex, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect &imageBounds); + + }; + +/*****************************************************************************/ + +/// \brief An opcode to apply a delta (i.e., offset) that varies per column. +/// Within a column, the same delta value is applied to all specified pixels. + +class dng_opcode_DeltaPerColumn: public dng_inplace_opcode + { + + private: + + dng_area_spec fAreaSpec; + + AutoPtr fTable; + + real32 fScale; + + public: + + /// Create a DeltaPerColumn opcode with the specified area and column + /// deltas (specified as a table of 32-bit floats). + + dng_opcode_DeltaPerColumn (const dng_area_spec &areaSpec, + AutoPtr &table); + + dng_opcode_DeltaPerColumn (dng_host &host, + dng_stream &stream); + + virtual void PutData (dng_stream &stream) const; + + virtual uint32 BufferPixelType (uint32 imagePixelType); + + virtual dng_rect ModifiedBounds (const dng_rect &imageBounds); + + virtual void ProcessArea (dng_negative &negative, + uint32 threadIndex, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect &imageBounds); + + }; + +/*****************************************************************************/ + +/// \brief An opcode to apply a scale factor that varies per row. Within a row, +/// the same scale factor is applied to all specified pixels. + +class dng_opcode_ScalePerRow: public dng_inplace_opcode + { + + private: + + dng_area_spec fAreaSpec; + + AutoPtr fTable; + + public: + + /// Create a ScalePerRow opcode with the specified area and row scale + /// factors (specified as a table of 32-bit floats). + + dng_opcode_ScalePerRow (const dng_area_spec &areaSpec, + AutoPtr &table); + + dng_opcode_ScalePerRow (dng_host &host, + dng_stream &stream); + + virtual void PutData (dng_stream &stream) const; + + virtual uint32 BufferPixelType (uint32 imagePixelType); + + virtual dng_rect ModifiedBounds (const dng_rect &imageBounds); + + virtual void ProcessArea (dng_negative &negative, + uint32 threadIndex, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect &imageBounds); + + }; + +/*****************************************************************************/ + +/// \brief An opcode to apply a scale factor that varies per column. Within a +/// column, the same scale factor is applied to all specified pixels. + +class dng_opcode_ScalePerColumn: public dng_inplace_opcode + { + + private: + + dng_area_spec fAreaSpec; + + AutoPtr fTable; + + public: + + /// Create a ScalePerColumn opcode with the specified area and column + /// scale factors (specified as a table of 32-bit floats). + + dng_opcode_ScalePerColumn (const dng_area_spec &areaSpec, + AutoPtr &table); + + dng_opcode_ScalePerColumn (dng_host &host, + dng_stream &stream); + + virtual void PutData (dng_stream &stream) const; + + virtual uint32 BufferPixelType (uint32 imagePixelType); + + virtual dng_rect ModifiedBounds (const dng_rect &imageBounds); + + virtual void ProcessArea (dng_negative &negative, + uint32 threadIndex, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect &imageBounds); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_mosaic_info.cpp b/dng/dng_mosaic_info.cpp new file mode 100644 index 0000000..84b961a --- /dev/null +++ b/dng/dng_mosaic_info.cpp @@ -0,0 +1,2008 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_mosaic_info.h" + +#include "dng_area_task.h" +#include "dng_assertions.h" +#include "dng_bottlenecks.h" +#include "dng_exceptions.h" +#include "dng_filter_task.h" +#include "dng_host.h" +#include "dng_ifd.h" +#include "dng_image.h" +#include "dng_info.h" +#include "dng_negative.h" +#include "dng_pixel_buffer.h" +#include "dng_tag_types.h" +#include "dng_tag_values.h" +#include "dng_tile_iterator.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +// A interpolation kernel for a single pixel of a single plane. + +class dng_bilinear_kernel + { + + public: + + enum + { + kMaxCount = 8 + }; + + uint32 fCount; + + dng_point fDelta [kMaxCount]; + + real32 fWeight32 [kMaxCount]; + uint16 fWeight16 [kMaxCount]; + + int32 fOffset [kMaxCount]; + + public: + + dng_bilinear_kernel () + : fCount (0) + { + } + + void Add (const dng_point &delta, + real32 weight); + + void Finalize (const dng_point &scale, + uint32 patRow, + uint32 patCol, + int32 rowStep, + int32 colStep); + + }; + +/*****************************************************************************/ + +void dng_bilinear_kernel::Add (const dng_point &delta, + real32 weight) + { + + // Don't add zero weight elements. + + if (weight <= 0.0f) + { + return; + } + + // If the delta already matches an existing element, just combine the + // weights. + + for (uint32 j = 0; j < fCount; j++) + { + + if (fDelta [j] == delta) + { + + fWeight32 [j] += weight; + + return; + + } + + } + + // Add element to list. + + DNG_ASSERT (fCount < kMaxCount, "Too many kernel entries"); + + fDelta [fCount] = delta; + fWeight32 [fCount] = weight; + + fCount++; + + } + +/*****************************************************************************/ + +void dng_bilinear_kernel::Finalize (const dng_point &scale, + uint32 patRow, + uint32 patCol, + int32 rowStep, + int32 colStep) + { + + uint32 j; + + // Adjust deltas to compensate for interpolation upscaling. + + for (j = 0; j < fCount; j++) + { + + dng_point &delta = fDelta [j]; + + if (scale.v == 2) + { + + delta.v = (delta.v + (int32) (patRow & 1)) >> 1; + + } + + if (scale.h == 2) + { + + delta.h = (delta.h + (int32) (patCol & 1)) >> 1; + + } + + } + + // Sort entries into row-column scan order. + + while (true) + { + + bool didSwap = false; + + for (j = 1; j < fCount; j++) + { + + dng_point &delta0 = fDelta [j - 1]; + dng_point &delta1 = fDelta [j ]; + + if (delta0.v > delta1.v || + (delta0.v == delta1.v && + delta0.h > delta1.h)) + { + + didSwap = true; + + dng_point tempDelta = delta0; + + delta0 = delta1; + delta1 = tempDelta; + + real32 tempWeight = fWeight32 [j - 1]; + + fWeight32 [j - 1] = fWeight32 [j]; + fWeight32 [j ] = tempWeight; + + } + + } + + if (!didSwap) + { + break; + } + + } + + // Calculate offsets. + + for (j = 0; j < fCount; j++) + { + + fOffset [j] = rowStep * fDelta [j].v + + colStep * fDelta [j].h; + + } + + // Calculate 16-bit weights. + + uint16 total = 0; + uint32 biggest = 0; + + for (j = 0; j < fCount; j++) + { + + // Round weights to 8 fractional bits. + + fWeight16 [j] = (uint16) Round_uint32 (fWeight32 [j] * 256.0); + + // Keep track of total of weights. + + total += fWeight16 [j]; + + // Keep track of which weight is biggest. + + if (fWeight16 [biggest] < fWeight16 [j]) + { + + biggest = j; + + } + + } + + // Adjust largest entry so total of weights is exactly 256. + + fWeight16 [biggest] += (256 - total); + + // Recompute the floating point weights from the rounded integer weights + // so results match more closely. + + for (j = 0; j < fCount; j++) + { + + fWeight32 [j] = fWeight16 [j] * (1.0f / 256.0f); + + } + + } + +/*****************************************************************************/ + +class dng_bilinear_pattern + { + + public: + + enum + { + kMaxPattern = kMaxCFAPattern * 2 + }; + + dng_point fScale; + + uint32 fPatRows; + uint32 fPatCols; + + dng_bilinear_kernel fKernel [kMaxPattern] + [kMaxPattern]; + + uint32 fCounts [kMaxPattern] + [kMaxPattern]; + + int32 *fOffsets [kMaxPattern] + [kMaxPattern]; + + uint16 *fWeights16 [kMaxPattern] + [kMaxPattern]; + + real32 *fWeights32 [kMaxPattern] + [kMaxPattern]; + + public: + + dng_bilinear_pattern () + + : fScale () + , fPatRows (0) + , fPatCols (0) + + { + } + + private: + + DNG_ATTRIB_NO_SANITIZE("unsigned-integer-overflow") + uint32 DeltaRow (uint32 row, int32 delta) + { + return (row + fPatRows + (uint32) delta) % fPatRows; + } + + DNG_ATTRIB_NO_SANITIZE("unsigned-integer-overflow") + uint32 DeltaCol (uint32 col, int32 delta) + { + return (col + fPatCols + (uint32) delta) % fPatCols; + } + + real32 LinearWeight1 (int32 d1, int32 d2) + { + if (d1 == d2) + return 1.0f; + else + return d2 / (real32) (d2 - d1); + } + + real32 LinearWeight2 (int32 d1, int32 d2) + { + if (d1 == d2) + return 0.0f; + else + return -d1 / (real32) (d2 - d1); + } + + public: + + void Calculate (const dng_mosaic_info &info, + uint32 dstPlane, + int32 rowStep, + int32 colStep); + + }; + +/*****************************************************************************/ + +void dng_bilinear_pattern::Calculate (const dng_mosaic_info &info, + uint32 dstPlane, + int32 rowStep, + int32 colStep) + { + + uint32 j; + uint32 k; + uint32 patRow; + uint32 patCol; + + // Find destination pattern size. + + fScale = info.FullScale (); + + fPatRows = info.fCFAPatternSize.v * fScale.v; + fPatCols = info.fCFAPatternSize.h * fScale.h; + + // See if we need to scale up just while computing the kernels. + + dng_point tempScale (1, 1); + + if (info.fCFALayout >= 6) + { + + tempScale = dng_point (2, 2); + + fPatRows *= tempScale.v; + fPatCols *= tempScale.h; + + } + + // Find a boolean map for this plane color and layout. + + bool map [kMaxPattern] + [kMaxPattern]; + + uint8 planeColor = info.fCFAPlaneColor [dstPlane]; + + switch (info.fCFALayout) + { + + case 1: // Rectangular (or square) layout + { + + for (j = 0; j < fPatRows; j++) + { + + for (k = 0; k < fPatCols; k++) + { + + map [j] [k] = (info.fCFAPattern [j] [k] == planeColor); + + } + + } + + break; + + } + + // Note that when the descriptions of the staggered patterns refer to even rows or + // columns, this mean the second, fourth, etc. (i.e. using one-based numbering). + // This needs to be clarified in the DNG specification. + + case 2: // Staggered layout A: even (1-based) columns are offset down by 1/2 row + { + + for (j = 0; j < fPatRows; j++) + { + + for (k = 0; k < fPatCols; k++) + { + + if ((j & 1) != (k & 1)) + { + + map [j] [k] = false; + + } + + else + { + + map [j] [k] = (info.fCFAPattern [j >> 1] [k] == planeColor); + + } + + } + + } + + break; + + } + + case 3: // Staggered layout B: even (1-based) columns are offset up by 1/2 row + { + + for (j = 0; j < fPatRows; j++) + { + + for (k = 0; k < fPatCols; k++) + { + + if ((j & 1) == (k & 1)) + { + + map [j] [k] = false; + + } + + else + { + + map [j] [k] = (info.fCFAPattern [j >> 1] [k] == planeColor); + + } + + } + + } + + break; + + } + + case 4: // Staggered layout C: even (1-based) rows are offset right by 1/2 column + { + + for (j = 0; j < fPatRows; j++) + { + + for (k = 0; k < fPatCols; k++) + { + + if ((j & 1) != (k & 1)) + { + + map [j] [k] = false; + + } + + else + { + + map [j] [k] = (info.fCFAPattern [j] [k >> 1] == planeColor); + + } + + } + + } + + break; + + } + + case 5: // Staggered layout D: even (1-based) rows are offset left by 1/2 column + { + + for (j = 0; j < fPatRows; j++) + { + + for (k = 0; k < fPatCols; k++) + { + + if ((j & 1) == (k & 1)) + { + + map [j] [k] = false; + + } + + else + { + + map [j] [k] = (info.fCFAPattern [j] [k >> 1] == planeColor); + + } + + } + + } + + break; + + } + + case 6: // Staggered layout E: even rows are offset up by 1/2 row, even columns are offset left by 1/2 column + case 7: // Staggered layout F: even rows are offset up by 1/2 row, even columns are offset right by 1/2 column + case 8: // Staggered layout G: even rows are offset down by 1/2 row, even columns are offset left by 1/2 column + case 9: // Staggered layout H: even rows are offset down by 1/2 row, even columns are offset right by 1/2 column + { + + uint32 eRow = (info.fCFALayout == 6 || + info.fCFALayout == 7) ? 1 : 3; + + uint32 eCol = (info.fCFALayout == 6 || + info.fCFALayout == 8) ? 1 : 3; + + for (j = 0; j < fPatRows; j++) + { + + for (k = 0; k < fPatCols; k++) + { + + uint32 jj = j & 3; + uint32 kk = k & 3; + + if ((jj != 0 && jj != eRow) || + (kk != 0 && kk != eCol)) + { + + map [j] [k] = false; + + } + + else + { + + map [j] [k] = (info.fCFAPattern [((j >> 1) & ~1) + Min_uint32 (jj, 1)] + [((k >> 1) & ~1) + Min_uint32 (kk, 1)] == planeColor); + + } + + } + + } + + break; + + } + + default: + ThrowProgramError (); + + } + + // Find projections of maps. + + bool mapH [kMaxPattern]; + bool mapV [kMaxPattern]; + + for (j = 0; j < kMaxPattern; j++) + { + + mapH [j] = false; + mapV [j] = false; + + } + + for (j = 0; j < fPatRows; j++) + { + + for (k = 0; k < fPatCols; k++) + { + + if (map [j] [k]) + { + + mapV [j] = true; + mapH [k] = true; + + } + + } + + } + + // Find kernel for each patten entry. + + for (patRow = 0; patRow < fPatRows; patRow += tempScale.v) + { + + for (patCol = 0; patCol < fPatCols; patCol += tempScale.h) + { + + dng_bilinear_kernel &kernel = fKernel [patRow] [patCol]; + + // Special case no interpolation case. + + if (map [patRow] [patCol]) + { + + kernel.Add (dng_point (0, 0), 1.0f); + + continue; + + } + + // Special case common patterns in 3 by 3 neighborhood. + + uint32 n = DeltaRow (patRow, -1); + uint32 s = DeltaRow (patRow, 1); + uint32 w = DeltaCol (patCol, -1); + uint32 e = DeltaCol (patCol, 1); + + bool mapNW = map [n] [w]; + bool mapN = map [n] [patCol]; + bool mapNE = map [n] [e]; + + bool mapW = map [patRow] [w]; + bool mapE = map [patRow] [e]; + + bool mapSW = map [s] [w]; + bool mapS = map [s] [patCol]; + bool mapSE = map [s] [e]; + + // All sides. + + if (mapN && mapS && mapW && mapW) + { + + kernel.Add (dng_point (-1, 0), 0.25f); + kernel.Add (dng_point ( 0, -1), 0.25f); + kernel.Add (dng_point ( 0, 1), 0.25f); + kernel.Add (dng_point ( 1, 0), 0.25f); + + continue; + + } + + // N & S. + + if (mapN && mapS) + { + + kernel.Add (dng_point (-1, 0), 0.5f); + kernel.Add (dng_point ( 1, 0), 0.5f); + + continue; + + } + + // E & W. + + if (mapW && mapE) + { + + kernel.Add (dng_point ( 0, -1), 0.5f); + kernel.Add (dng_point ( 0, 1), 0.5f); + + continue; + + } + + // N & SW & SE. + + if (mapN && mapSW && mapSE) + { + + kernel.Add (dng_point (-1, 0), 0.50f); + kernel.Add (dng_point ( 1, -1), 0.25f); + kernel.Add (dng_point ( 1, 1), 0.25f); + + continue; + + } + + // S & NW & NE. + + if (mapS && mapNW && mapNE) + { + + kernel.Add (dng_point (-1, -1), 0.25f); + kernel.Add (dng_point (-1, 1), 0.25f); + kernel.Add (dng_point ( 1, 0), 0.50f); + + continue; + + } + + // W & NE & SE. + + if (mapW && mapNE && mapSE) + { + + kernel.Add (dng_point (-1, 1), 0.25f); + kernel.Add (dng_point ( 0, -1), 0.50f); + kernel.Add (dng_point ( 1, 1), 0.25f); + + continue; + + } + + // E & NW & SW. + + if (mapE && mapNW && mapSW) + { + + kernel.Add (dng_point (-1, -1), 0.25f); + kernel.Add (dng_point ( 0, 1), 0.50f); + kernel.Add (dng_point ( 1, -1), 0.25f); + + continue; + + } + + // Four corners. + + if (mapNW && mapNE && mapSE && mapSW) + { + + kernel.Add (dng_point (-1, -1), 0.25f); + kernel.Add (dng_point (-1, 1), 0.25f); + kernel.Add (dng_point ( 1, -1), 0.25f); + kernel.Add (dng_point ( 1, 1), 0.25f); + + continue; + + } + + // NW & SE + + if (mapNW && mapSE) + { + + kernel.Add (dng_point (-1, -1), 0.50f); + kernel.Add (dng_point ( 1, 1), 0.50f); + + continue; + + } + + // NE & SW + + if (mapNE && mapSW) + { + + kernel.Add (dng_point (-1, 1), 0.50f); + kernel.Add (dng_point ( 1, -1), 0.50f); + + continue; + + } + + // Else use double-bilinear kernel. + + int32 dv1 = 0; + int32 dv2 = 0; + + while (!mapV [DeltaRow (patRow, dv1)]) + { + dv1--; + } + + while (!mapV [DeltaRow (patRow, dv2)]) + { + dv2++; + } + + real32 w1 = LinearWeight1 (dv1, dv2) * 0.5f; + real32 w2 = LinearWeight2 (dv1, dv2) * 0.5f; + + int32 v1 = DeltaRow (patRow, dv1); + int32 v2 = DeltaRow (patRow, dv2); + + int32 dh1 = 0; + int32 dh2 = 0; + + while (!map [v1] [DeltaCol (patCol, dh1)]) + { + dh1--; + } + + while (!map [v1] [DeltaCol (patCol, dh2)]) + { + dh2++; + } + + kernel.Add (dng_point (dv1, dh1), + LinearWeight1 (dh1, dh2) * w1); + + kernel.Add (dng_point (dv1, dh2), + LinearWeight2 (dh1, dh2) * w1); + + dh1 = 0; + dh2 = 0; + + while (!map [v2] [DeltaCol (patCol, dh1)]) + { + dh1--; + } + + while (!map [v2] [DeltaCol (patCol, dh2)]) + { + dh2++; + } + + kernel.Add (dng_point (dv2, dh1), + LinearWeight1 (dh1, dh2) * w2); + + kernel.Add (dng_point (dv2, dh2), + LinearWeight2 (dh1, dh2) * w2); + + dh1 = 0; + dh2 = 0; + + while (!mapH [DeltaCol (patCol, dh1)]) + { + dh1--; + } + + while (!mapH [DeltaCol (patCol, dh2)]) + { + dh2++; + } + + w1 = LinearWeight1 (dh1, dh2) * 0.5f; + w2 = LinearWeight2 (dh1, dh2) * 0.5f; + + int32 h1 = DeltaCol (patCol, dh1); + int32 h2 = DeltaCol (patCol, dh2); + + dv1 = 0; + dv2 = 0; + + while (!map [DeltaRow (patRow, dv1)] [h1]) + { + dv1--; + } + + while (!map [DeltaRow (patRow, dv2)] [h1]) + { + dv2++; + } + + kernel.Add (dng_point (dv1, dh1), + LinearWeight1 (dv1, dv2) * w1); + + kernel.Add (dng_point (dv2, dh1), + LinearWeight2 (dv1, dv2) * w1); + + dv1 = 0; + dv2 = 0; + + while (!map [DeltaRow (patRow, dv1)] [h2]) + { + dv1--; + } + + while (!map [DeltaRow (patRow, dv2)] [h2]) + { + dv2++; + } + + kernel.Add (dng_point (dv1, dh2), + LinearWeight1 (dv1, dv2) * w2); + + kernel.Add (dng_point (dv2, dh2), + LinearWeight2 (dv1, dv2) * w2); + + } + + } + + // Deal with temp scale case. + + if (tempScale == dng_point (2, 2)) + { + + fPatRows /= tempScale.v; + fPatCols /= tempScale.h; + + for (patRow = 0; patRow < fPatRows; patRow++) + { + + for (patCol = 0; patCol < fPatCols; patCol++) + { + + int32 patRow2 = patRow << 1; + int32 patCol2 = patCol << 1; + + dng_bilinear_kernel &kernel = fKernel [patRow2] [patCol2]; + + for (j = 0; j < kernel.fCount; j++) + { + + int32 x = patRow2 + kernel.fDelta [j].v; + + if ((x & 3) != 0) + { + x = (x & ~3) + 2; + } + + kernel.fDelta [j].v = ((x - patRow2) >> 1); + + x = patCol2 + kernel.fDelta [j].h; + + if ((x & 3) != 0) + { + x = (x & ~3) + 2; + } + + kernel.fDelta [j].h = ((x - patCol2) >> 1); + + } + + kernel.Finalize (fScale, + patRow, + patCol, + rowStep, + colStep); + + fCounts [patRow] [patCol] = kernel.fCount; + fOffsets [patRow] [patCol] = kernel.fOffset; + fWeights16 [patRow] [patCol] = kernel.fWeight16; + fWeights32 [patRow] [patCol] = kernel.fWeight32; + + } + + } + + } + + // Non-temp scale case. + + else + { + + for (patRow = 0; patRow < fPatRows; patRow++) + { + + for (patCol = 0; patCol < fPatCols; patCol++) + { + + dng_bilinear_kernel &kernel = fKernel [patRow] [patCol]; + + kernel.Finalize (fScale, + patRow, + patCol, + rowStep, + colStep); + + fCounts [patRow] [patCol] = kernel.fCount; + fOffsets [patRow] [patCol] = kernel.fOffset; + fWeights16 [patRow] [patCol] = kernel.fWeight16; + fWeights32 [patRow] [patCol] = kernel.fWeight32; + + } + + } + + } + + } + +/*****************************************************************************/ + +class dng_bilinear_interpolator + { + + private: + + dng_bilinear_pattern fPattern [kMaxColorPlanes]; + + public: + + dng_bilinear_interpolator (const dng_mosaic_info &info, + int32 rowStep, + int32 colStep); + + void Interpolate (dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer); + + }; + +/*****************************************************************************/ + +dng_bilinear_interpolator::dng_bilinear_interpolator (const dng_mosaic_info &info, + int32 rowStep, + int32 colStep) + { + + for (uint32 dstPlane = 0; dstPlane < info.fColorPlanes; dstPlane++) + { + + fPattern [dstPlane] . Calculate (info, + dstPlane, + rowStep, + colStep); + + } + + } + +/*****************************************************************************/ + +void dng_bilinear_interpolator::Interpolate (dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer) + { + + uint32 patCols = fPattern [0] . fPatCols; + uint32 patRows = fPattern [0] . fPatRows; + + dng_point scale = fPattern [0] . fScale; + + uint32 sRowShift = scale.v - 1; + uint32 sColShift = scale.h - 1; + + int32 dstCol = dstBuffer.fArea.l; + + int32 srcCol = dstCol >> sColShift; + + uint32 patPhase = dstCol % patCols; + + for (int32 dstRow = dstBuffer.fArea.t; + dstRow < dstBuffer.fArea.b; + dstRow++) + { + + int32 srcRow = dstRow >> sRowShift; + + uint32 patRow = dstRow % patRows; + + for (uint32 dstPlane = 0; + dstPlane < dstBuffer.fPlanes; + dstPlane++) + { + + const void *sPtr = srcBuffer.ConstPixel (srcRow, + srcCol, + srcBuffer.fPlane); + + void *dPtr = dstBuffer.DirtyPixel (dstRow, + dstCol, + dstPlane); + + if (dstBuffer.fPixelType == ttShort) + { + + DoBilinearRow16 ((const uint16 *) sPtr, + (uint16 *) dPtr, + dstBuffer.fArea.W (), + patPhase, + patCols, + fPattern [dstPlane].fCounts [patRow], + fPattern [dstPlane].fOffsets [patRow], + fPattern [dstPlane].fWeights16 [patRow], + sColShift); + + } + + else + { + + DoBilinearRow32 ((const real32 *) sPtr, + (real32 *) dPtr, + dstBuffer.fArea.W (), + patPhase, + patCols, + fPattern [dstPlane].fCounts [patRow], + fPattern [dstPlane].fOffsets [patRow], + fPattern [dstPlane].fWeights32 [patRow], + sColShift); + + } + + } + + } + + } + +/*****************************************************************************/ + +class dng_fast_interpolator: public dng_filter_task + { + + protected: + + const dng_mosaic_info &fInfo; + + dng_point fDownScale; + + uint32 fFilterColor [kMaxCFAPattern] [kMaxCFAPattern]; + + public: + + dng_fast_interpolator (const dng_mosaic_info &info, + const dng_image &srcImage, + dng_image &dstImage, + const dng_point &downScale, + uint32 srcPlane); + + virtual dng_rect SrcArea (const dng_rect &dstArea); + + virtual void ProcessArea (uint32 threadIndex, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer); + + }; + +/*****************************************************************************/ + +dng_fast_interpolator::dng_fast_interpolator (const dng_mosaic_info &info, + const dng_image &srcImage, + dng_image &dstImage, + const dng_point &downScale, + uint32 srcPlane) + + : dng_filter_task ("dng_fast_interpolator", + srcImage, + dstImage) + + , fInfo (info ) + , fDownScale (downScale) + + { + + fSrcPlane = srcPlane; + fSrcPlanes = 1; + + fSrcPixelType = ttShort; + fDstPixelType = ttShort; + + fSrcRepeat = fInfo.fCFAPatternSize; + + fUnitCell = fInfo.fCFAPatternSize; + + fMaxTileSize = dng_point (256 / fDownScale.v, + 256 / fDownScale.h); + + fMaxTileSize.h = Max_int32 (fMaxTileSize.h, fUnitCell.h); + fMaxTileSize.v = Max_int32 (fMaxTileSize.v, fUnitCell.v); + + // Find color map. + + { + + for (int32 r = 0; r < fInfo.fCFAPatternSize.v; r++) + { + + for (int32 c = 0; c < fInfo.fCFAPatternSize.h; c++) + { + + uint8 key = fInfo.fCFAPattern [r] [c]; + + for (uint32 index = 0; index < fInfo.fColorPlanes; index++) + { + + if (key == fInfo.fCFAPlaneColor [index]) + { + + fFilterColor [r] [c] = index; + + break; + + } + + } + + } + + } + + } + + } + +/*****************************************************************************/ + +dng_rect dng_fast_interpolator::SrcArea (const dng_rect &dstArea) + { + + return dng_rect (dstArea.t * fDownScale.v, + dstArea.l * fDownScale.h, + dstArea.b * fDownScale.v, + dstArea.r * fDownScale.h); + + } + +/*****************************************************************************/ + +void dng_fast_interpolator::ProcessArea (uint32 /* threadIndex */, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer) + { + + dng_rect srcArea = srcBuffer.fArea; + dng_rect dstArea = dstBuffer.fArea; + + // Downsample buffer. + + int32 srcRow = srcArea.t; + + uint32 srcRowPhase1 = 0; + uint32 srcRowPhase2 = 0; + + uint32 patRows = fInfo.fCFAPatternSize.v; + uint32 patCols = fInfo.fCFAPatternSize.h; + + uint32 cellRows = fDownScale.v; + uint32 cellCols = fDownScale.h; + + uint32 plane; + uint32 planes = fInfo.fColorPlanes; + + int32 dstPlaneStep = dstBuffer.fPlaneStep; + + uint32 total [kMaxColorPlanes]; + uint32 count [kMaxColorPlanes]; + + for (plane = 0; plane < planes; plane++) + { + total [plane] = 0; + count [plane] = 0; + } + + for (int32 dstRow = dstArea.t; dstRow < dstArea.b; dstRow++) + { + + const uint16 *sPtr = srcBuffer.ConstPixel_uint16 (srcRow, + srcArea.l, + fSrcPlane); + + uint16 *dPtr = dstBuffer.DirtyPixel_uint16 (dstRow, + dstArea.l, + 0); + + uint32 srcColPhase1 = 0; + uint32 srcColPhase2 = 0; + + for (int32 dstCol = dstArea.l; dstCol < dstArea.r; dstCol++) + { + + const uint16 *ssPtr = sPtr; + + srcRowPhase2 = srcRowPhase1; + + for (uint32 cellRow = 0; cellRow < cellRows; cellRow++) + { + + const uint32 *filterRow = fFilterColor [srcRowPhase2]; + + if (++srcRowPhase2 == patRows) + { + srcRowPhase2 = 0; + } + + srcColPhase2 = srcColPhase1; + + for (uint32 cellCol = 0; cellCol < cellCols; cellCol++) + { + + uint32 color = filterRow [srcColPhase2]; + + if (++srcColPhase2 == patCols) + { + srcColPhase2 = 0; + } + + total [color] += (uint32) ssPtr [cellCol]; + count [color] ++; + + } + + ssPtr += srcBuffer.fRowStep; + + } + + for (plane = 0; plane < planes; plane++) + { + + uint32 t = total [plane]; + uint32 c = Max_uint32 (count [plane], 1); + + dPtr [plane * dstPlaneStep] = (uint16) ((t + (c >> 1)) / c); + + total [plane] = 0; + count [plane] = 0; + + } + + srcColPhase1 = srcColPhase2; + + sPtr += cellCols; + + dPtr ++; + + } + + srcRowPhase1 = srcRowPhase2; + + srcRow += cellRows; + + } + + } + +/*****************************************************************************/ + +dng_mosaic_info::dng_mosaic_info () + + : fCFAPatternSize () + , fColorPlanes (0) + , fCFALayout (1) + , fBayerGreenSplit (0) + , fSrcSize () + , fCroppedSize () + , fAspectRatio (1.0) + + { + + } + +/*****************************************************************************/ + +dng_mosaic_info::~dng_mosaic_info () + { + + } + +/*****************************************************************************/ + +void dng_mosaic_info::Parse (dng_host & /* host */, + dng_stream & /* stream */, + dng_info &info) + { + + // Find main image IFD. + + dng_ifd &rawIFD = *info.fIFD [info.fMainIndex]; + + // This information only applies to CFA images. + + if (rawIFD.fPhotometricInterpretation != piCFA) + { + return; + } + + // Copy CFA pattern. + + fCFAPatternSize.v = rawIFD.fCFARepeatPatternRows; + fCFAPatternSize.h = rawIFD.fCFARepeatPatternCols; + + for (int32 j = 0; j < fCFAPatternSize.v; j++) + { + for (int32 k = 0; k < fCFAPatternSize.h; k++) + { + fCFAPattern [j] [k] = rawIFD.fCFAPattern [j] [k]; + } + } + + // Copy CFA plane information. + + fColorPlanes = info.fShared->fCameraProfile.fColorPlanes; + + for (uint32 n = 0; n < fColorPlanes; n++) + { + fCFAPlaneColor [n] = rawIFD.fCFAPlaneColor [n]; + } + + // Copy CFA layout information. + + fCFALayout = rawIFD.fCFALayout; + + // Green split value for Bayer patterns. + + fBayerGreenSplit = rawIFD.fBayerGreenSplit; + + } + +/*****************************************************************************/ + +void dng_mosaic_info::PostParse (dng_host & /* host */, + dng_negative &negative) + { + + // Keep track of source image size. + + fSrcSize = negative.Stage2Image ()->Size (); + + // Default cropped size. + + fCroppedSize.v = Round_int32 (negative.DefaultCropSizeV ().As_real64 ()); + fCroppedSize.h = Round_int32 (negative.DefaultCropSizeH ().As_real64 ()); + + // Pixel aspect ratio. + + fAspectRatio = negative.DefaultScaleH ().As_real64 () / + negative.DefaultScaleV ().As_real64 (); + + } + +/*****************************************************************************/ + +bool dng_mosaic_info::SetFourColorBayer () + { + + if (fCFAPatternSize != dng_point (2, 2)) + { + return false; + } + + if (fColorPlanes != 3) + { + return false; + } + + uint8 color0 = fCFAPlaneColor [0]; + uint8 color1 = fCFAPlaneColor [1]; + uint8 color2 = fCFAPlaneColor [2]; + + // Look for color 1 repeated twice in a diagonal. + + if ((fCFAPattern [0] [0] == color1 && fCFAPattern [1] [1] == color1) || + (fCFAPattern [0] [1] == color1 && fCFAPattern [1] [0] == color1)) + { + + // OK, this looks like a Bayer pattern. + + // Find unused color code. + + uint8 color3 = 0; + + while (color3 == color0 || + color3 == color1 || + color3 == color2) + { + color3++; + } + + // Switch the four color mosaic. + + fColorPlanes = 4; + + fCFAPlaneColor [3] = color3; + + // Replace the "green" in the "blue" rows with the new color. + + if (fCFAPattern [0] [0] == color0) + { + fCFAPattern [1] [0] = color3; + } + + else if (fCFAPattern [0] [1] == color0) + { + fCFAPattern [1] [1] = color3; + } + + else if (fCFAPattern [1] [0] == color0) + { + fCFAPattern [0] [0] = color3; + } + + else + { + fCFAPattern [0] [1] = color3; + } + + return true; + + } + + return false; + + } + +/*****************************************************************************/ + +dng_point dng_mosaic_info::FullScale () const + { + + switch (fCFALayout) + { + + // Staggered layouts with offset columns double the row count + // during interpolation. + + case 2: + case 3: + return dng_point (2, 1); + + // Staggered layouts with offset rows double the column count + // during interpolation. + + case 4: + case 5: + return dng_point (1, 2); + + // Otherwise there is no size change during interpolation. + + default: + break; + + } + + return dng_point (1, 1); + + } + +/*****************************************************************************/ + +bool dng_mosaic_info::IsSafeDownScale (const dng_point &downScale) const + { + + if (downScale.v >= fCFAPatternSize.v && + downScale.h >= fCFAPatternSize.h) + { + + return true; + + } + + dng_point test; + + test.v = Min_int32 (downScale.v, fCFAPatternSize.v); + test.h = Min_int32 (downScale.h, fCFAPatternSize.h); + + for (int32 phaseV = 0; phaseV < fCFAPatternSize.v; phaseV++) + { + + for (int32 phaseH = 0; phaseH < fCFAPatternSize.h; phaseH++) + { + + uint32 plane; + + bool contains [kMaxColorPlanes]; + + for (plane = 0; plane < fColorPlanes; plane++) + { + + contains [plane] = false; + + } + + for (int32 srcRow = 0; srcRow < test.v; srcRow++) + { + + for (int32 srcCol = 0; srcCol < test.h; srcCol++) + { + + uint8 srcKey = fCFAPattern [(srcRow + phaseV) % fCFAPatternSize.v] + [(srcCol + phaseH) % fCFAPatternSize.h]; + + for (plane = 0; plane < fColorPlanes; plane++) + { + + if (srcKey == fCFAPlaneColor [plane]) + { + + contains [plane] = true; + + } + + } + + + } + + } + + for (plane = 0; plane < fColorPlanes; plane++) + { + + if (!contains [plane]) + { + + return false; + + } + + } + + } + + } + + return true; + + } + +/*****************************************************************************/ + +uint32 dng_mosaic_info::SizeForDownScale (const dng_point &downScale) const + { + + uint32 sizeV = Max_uint32 (1, (fCroppedSize.v + (downScale.v >> 1)) / downScale.v); + uint32 sizeH = Max_uint32 (1, (fCroppedSize.h + (downScale.h >> 1)) / downScale.h); + + return Max_int32 (sizeV, sizeH); + + } + +/*****************************************************************************/ + +bool dng_mosaic_info::ValidSizeDownScale (const dng_point &downScale, + uint32 minSize) const + { + + const int32 kMaxDownScale = 64; + + if (downScale.h > kMaxDownScale || + downScale.v > kMaxDownScale) + { + + return false; + + } + + return SizeForDownScale (downScale) >= minSize; + + } + +/*****************************************************************************/ + +dng_point dng_mosaic_info::DownScale (uint32 minSize, + uint32 prefSize, + real64 cropFactor) const + { + + dng_point bestScale (1, 1); + + if (prefSize && IsColorFilterArray ()) + { + + // Adjust sizes for crop factor. + + minSize = Round_uint32 (minSize / cropFactor); + prefSize = Round_uint32 (prefSize / cropFactor); + + prefSize = Max_uint32 (prefSize, minSize); + + // Start by assuming we need the full size image. + + int32 bestSize = SizeForDownScale (bestScale); + + // Find size of nearly square cell. + + dng_point squareCell (1, 1); + + if (fAspectRatio < 1.0 / 1.8) + { + + squareCell.h = Min_int32 (4, Round_int32 (1.0 / fAspectRatio)); + + } + + if (fAspectRatio > 1.8) + { + + squareCell.v = Min_int32 (4, Round_int32 (fAspectRatio)); + + } + + // Find minimum safe cell size. + + dng_point testScale = squareCell; + + while (!IsSafeDownScale (testScale)) + { + + testScale.v += squareCell.v; + testScale.h += squareCell.h; + + } + + // See if this scale is usable. + + if (!ValidSizeDownScale (testScale, minSize)) + { + + // We cannot downsample at all... + + return bestScale; + + } + + // See if this is closer to the preferred size. + + int32 testSize = SizeForDownScale (testScale); + + if (Abs_int32 (testSize - (int32) prefSize) <= + Abs_int32 (bestSize - (int32) prefSize)) + { + bestScale = testScale; + bestSize = testSize; + } + + else + { + return bestScale; + } + + // Now keep adding square cells as long as possible. + + while (true) + { + + testScale.v += squareCell.v; + testScale.h += squareCell.h; + + if (IsSafeDownScale (testScale)) + { + + if (!ValidSizeDownScale (testScale, minSize)) + { + return bestScale; + } + + // See if this is closer to the preferred size. + + testSize = SizeForDownScale (testScale); + + if (Abs_int32 (testSize - (int32) prefSize) <= + Abs_int32 (bestSize - (int32) prefSize)) + { + bestScale = testScale; + bestSize = testSize; + } + + else + { + return bestScale; + } + + } + + } + + } + + return bestScale; + + } + +/*****************************************************************************/ + +dng_point dng_mosaic_info::DstSize (const dng_point &downScale) const + { + + if (downScale == dng_point (1, 1)) + { + + dng_point scale = FullScale (); + + return dng_point (fSrcSize.v * scale.v, + fSrcSize.h * scale.h); + + } + + const int32 kMaxDownScale = 64; + + if (downScale.h > kMaxDownScale || + downScale.v > kMaxDownScale) + { + + return dng_point (0, 0); + + } + + dng_point size; + + size.v = Max_int32 (1, (fSrcSize.v + (downScale.v >> 1)) / downScale.v); + size.h = Max_int32 (1, (fSrcSize.h + (downScale.h >> 1)) / downScale.h); + + return size; + + } + +/*****************************************************************************/ + +void dng_mosaic_info::InterpolateGeneric (dng_host &host, + dng_negative & /* negative */, + const dng_image &srcImage, + dng_image &dstImage, + uint32 srcPlane) const + { + + // Find destination to source bit shifts. + + dng_point scale = FullScale (); + + uint32 srcShiftV = scale.v - 1; + uint32 srcShiftH = scale.h - 1; + + // Find tile sizes. + + const uint32 kMaxDstTileRows = 128; + const uint32 kMaxDstTileCols = 128; + + dng_point dstTileSize = dstImage.RepeatingTile ().Size (); + + dstTileSize.v = Min_int32 (dstTileSize.v, kMaxDstTileRows); + dstTileSize.h = Min_int32 (dstTileSize.h, kMaxDstTileCols); + + dng_point srcTileSize = dstTileSize; + + srcTileSize.v >>= srcShiftV; + srcTileSize.h >>= srcShiftH; + + srcTileSize.v += fCFAPatternSize.v * 2; + srcTileSize.h += fCFAPatternSize.h * 2; + + // Allocate source buffer. + + dng_pixel_buffer srcBuffer (dng_rect (srcTileSize), + srcPlane, + 1, + srcImage.PixelType (), + pcInterleaved, + NULL); + + uint32 srcBufferSize = ComputeBufferSize (srcBuffer.fPixelType, + srcTileSize, + srcBuffer.fPlanes, + padNone); + + AutoPtr srcData (host.Allocate (srcBufferSize)); + + srcBuffer.fData = srcData->Buffer (); + + // Allocate destination buffer. + + dng_pixel_buffer dstBuffer (dng_rect (dstTileSize), + 0, + fColorPlanes, + dstImage.PixelType (), + pcRowInterleaved, + NULL); + + uint32 dstBufferSize = ComputeBufferSize (dstBuffer.fPixelType, + dstTileSize, + dstBuffer.fPlanes, + padNone); + + AutoPtr dstData (host.Allocate (dstBufferSize)); + + dstBuffer.fData = dstData->Buffer (); + + // Create interpolator. + + AutoPtr interpolator (new dng_bilinear_interpolator (*this, + srcBuffer.fRowStep, + srcBuffer.fColStep)); + + // Iterate over destination tiles. + + dng_rect dstArea; + + dng_tile_iterator iter1 (dstImage, dstImage.Bounds ()); + + while (iter1.GetOneTile (dstArea)) + { + + // Break into buffer sized tiles. + + dng_rect dstTile; + + dng_tile_iterator iter2 (dstTileSize, dstArea); + + while (iter2.GetOneTile (dstTile)) + { + + host.SniffForAbort (); + + // Setup buffers for this tile. + + dng_rect srcTile (dstTile); + + srcTile.t >>= srcShiftV; + srcTile.b >>= srcShiftV; + + srcTile.l >>= srcShiftH; + srcTile.r >>= srcShiftH; + + srcTile.t -= fCFAPatternSize.v; + srcTile.b += fCFAPatternSize.v; + + srcTile.l -= fCFAPatternSize.h; + srcTile.r += fCFAPatternSize.h; + + srcBuffer.fArea = srcTile; + dstBuffer.fArea = dstTile; + + // Get source data. + + srcImage.Get (srcBuffer, + dng_image::edge_repeat, + fCFAPatternSize.v, + fCFAPatternSize.h); + + // Process data. + + interpolator->Interpolate (srcBuffer, + dstBuffer); + + // Save results. + + dstImage.Put (dstBuffer); + + } + + } + + } + +/*****************************************************************************/ + +void dng_mosaic_info::InterpolateFast (dng_host &host, + dng_negative & /* negative */, + const dng_image &srcImage, + dng_image &dstImage, + const dng_point &downScale, + uint32 srcPlane) const + { + + // Create fast interpolator task. + + dng_fast_interpolator interpolator (*this, + srcImage, + dstImage, + downScale, + srcPlane); + + // Find area to process. + + dng_rect bounds = dstImage.Bounds (); + + // Do the interpolation. + + host.PerformAreaTask (interpolator, + bounds); + + } + +/*****************************************************************************/ + +void dng_mosaic_info::Interpolate (dng_host &host, + dng_negative &negative, + const dng_image &srcImage, + dng_image &dstImage, + const dng_point &downScale, + uint32 srcPlane, + dng_matrix *scaleTransforms) const + { + + if (scaleTransforms && downScale != dng_point (1, 1)) + { + + for (uint32 plane = 0; plane < dstImage.Planes (); plane++) + { + + scaleTransforms [plane] = dng_matrix_3by3 (1.0 / downScale.v, 0.0, 0.0, + 0.0, 1.0 / downScale.h, 0.0, + 0.0, 0.0, 1.0); + + } + + } + + if (downScale == dng_point (1, 1)) + { + + InterpolateGeneric (host, + negative, + srcImage, + dstImage, + srcPlane); + + } + + else + { + + InterpolateFast (host, + negative, + srcImage, + dstImage, + downScale, + srcPlane); + + } + + } + +/*****************************************************************************/ + +bool dng_mosaic_info::SupportsPreservedBlackLevels () const + { + + return false; + + } + +/*****************************************************************************/ diff --git a/dng/dng_mosaic_info.h b/dng/dng_mosaic_info.h new file mode 100644 index 0000000..988aaef --- /dev/null +++ b/dng/dng_mosaic_info.h @@ -0,0 +1,198 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Support for descriptive information about color filter array patterns. + */ + +/*****************************************************************************/ + +#ifndef __dng_mosaic_info__ +#define __dng_mosaic_info__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_rect.h" +#include "dng_sdk_limits.h" +#include "dng_types.h" + +/*****************************************************************************/ + +/// \brief Support for describing color filter array patterns and manipulating mosaic sample data. +/// +/// See CFAPattern tag in \ref spec_tiff_ep "TIFF/EP specification" and CFAPlaneColor, CFALayout, and BayerGreenSplit +/// tags in the \ref spec_dng "DNG 1.1.0 specification". + +class dng_mosaic_info + { + + public: + + /// Size of fCFAPattern. + + dng_point fCFAPatternSize; + + /// CFA pattern from CFAPattern tag in the \ref spec_tiff_ep "TIFF/EP specification." + + uint8 fCFAPattern [kMaxCFAPattern] [kMaxCFAPattern]; + + /// Number of color planes in DNG input. + + uint32 fColorPlanes; + + uint8 fCFAPlaneColor [kMaxColorPlanes]; + + /// Value of CFALayout tag in the \ref spec_dng "DNG 1.3 specification." + /// CFALayout describes the spatial layout of the CFA. The currently defined values are: + /// - 1 = Rectangular (or square) layout. + /// - 2 = Staggered layout A: even columns are offset down by 1/2 row. + /// - 3 = Staggered layout B: even columns are offset up by 1/2 row. + /// - 4 = Staggered layout C: even rows are offset right by 1/2 column. + /// - 5 = Staggered layout D: even rows are offset left by 1/2 column. + /// - 6 = Staggered layout E: even rows are offset up by 1/2 row, even columns are offset left by 1/2 column. + /// - 7 = Staggered layout F: even rows are offset up by 1/2 row, even columns are offset right by 1/2 column. + /// - 8 = Staggered layout G: even rows are offset down by 1/2 row, even columns are offset left by 1/2 column. + /// - 9 = Staggered layout H: even rows are offset down by 1/2 row, even columns are offset right by 1/2 column. + + uint32 fCFALayout; + + /// Value of BayerGreeSplit tag in DNG file. + /// BayerGreenSplit only applies to CFA images using a Bayer pattern filter array. This tag + /// specifies, in arbitrary units, how closely the values of the green pixels in the blue/green rows + /// track the values of the green pixels in the red/green rows. + /// + /// A value of zero means the two kinds of green pixels track closely, while a non-zero value + /// means they sometimes diverge. The useful range for this tag is from 0 (no divergence) to about + /// 5000 (large divergence). + + uint32 fBayerGreenSplit; + + protected: + + dng_point fSrcSize; + + dng_point fCroppedSize; + + real64 fAspectRatio; + + public: + + dng_mosaic_info (); + + virtual ~dng_mosaic_info (); + + virtual void Parse (dng_host &host, + dng_stream &stream, + dng_info &info); + + virtual void PostParse (dng_host &host, + dng_negative &negative); + + /// Returns whether the RAW data in this DNG file from a color filter array (mosaiced) source. + /// \retval true if this DNG file is from a color filter array (mosiaced) source. + + bool IsColorFilterArray () const + { + return fCFAPatternSize != dng_point (0, 0); + } + + /// Enable generating four-plane output from three-plane Bayer input. + /// Extra plane is a second version of the green channel. First green is produced + /// using green mosaic samples from one set of rows/columns (even/odd) and the second + /// green channel is produced using the other set of rows/columns. One can compare the + /// two versions to judge whether BayerGreenSplit needs to be set for a given input source. + + virtual bool SetFourColorBayer (); + + /// Returns scaling factor relative to input size needed to capture output data. + /// Staggered (or rotated) sensing arrays are produced to a larger output than the number of input samples. + /// This method indicates how much larger. + /// \retval a point with integer scaling factors for the horizotal and vertical dimensions. + + virtual dng_point FullScale () const; + + /// Returns integer factors by which mosaic data must be downsampled to produce an image which is as close + /// to prefSize as possible in longer dimension, but no smaller than minSize. + /// \param minSize Number of pixels as minium for longer dimension of downsampled image. + /// \param prefSize Number of pixels as target for longer dimension of downsampled image. + /// \param cropFactor Faction of the image to be used after cropping. + /// \retval Point containing integer factors by which image must be downsampled. + + virtual dng_point DownScale (uint32 minSize, + uint32 prefSize, + real64 cropFactor) const; + + /// Return size of demosaiced image for passed in downscaling factor. + /// \param downScale Integer downsampling factor obtained from DownScale method. + /// \retval Size of resulting demosaiced image. + + virtual dng_point DstSize (const dng_point &downScale) const; + + /// Demosaic interpolation of a single plane for non-downsampled case. + /// \param host dng_host to use for buffer allocation requests, user cancellation testing, and progress updates. + /// \param negative DNG negative of mosaiced data. + /// \param srcImage Source image for mosaiced data. + /// \param dstImage Destination image for resulting interpolated data. + /// \param srcPlane Which plane to interpolate. + + virtual void InterpolateGeneric (dng_host &host, + dng_negative &negative, + const dng_image &srcImage, + dng_image &dstImage, + uint32 srcPlane = 0) const; + + /// Demosaic interpolation of a single plane for downsampled case. + /// \param host dng_host to use for buffer allocation requests, user cancellation testing, and progress updates. + /// \param negative DNG negative of mosaiced data. + /// \param srcImage Source image for mosaiced data. + /// \param dstImage Destination image for resulting interpolated data. + /// \param downScale Amount (in horizontal and vertical) by which to subsample image. + /// \param srcPlane Which plane to interpolate. + + virtual void InterpolateFast (dng_host &host, + dng_negative &negative, + const dng_image &srcImage, + dng_image &dstImage, + const dng_point &downScale, + uint32 srcPlane = 0) const; + + /// Demosaic interpolation of a single plane. Chooses between generic and fast interpolators based on parameters. + /// \param host dng_host to use for buffer allocation requests, user cancellation testing, and progress updates. + /// \param negative DNG negative of mosaiced data. + /// \param srcImage Source image for mosaiced data. + /// \param dstImage Destination image for resulting interpolated data. + /// \param downScale Amount (in horizontal and vertical) by which to subsample image. + /// \param srcPlane Which plane to interpolate. + + virtual void Interpolate (dng_host &host, + dng_negative &negative, + const dng_image &srcImage, + dng_image &dstImage, + const dng_point &downScale, + uint32 srcPlane = 0, + dng_matrix *scaleTransforms = NULL) const; + + virtual bool SupportsPreservedBlackLevels () const; + + protected: + + virtual bool IsSafeDownScale (const dng_point &downScale) const; + + uint32 SizeForDownScale (const dng_point &downScale) const; + + virtual bool ValidSizeDownScale (const dng_point &downScale, + uint32 minSize) const; + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_mutex.cpp b/dng/dng_mutex.cpp new file mode 100644 index 0000000..61f32ae --- /dev/null +++ b/dng/dng_mutex.cpp @@ -0,0 +1,516 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_abort_sniffer.h" +#include "dng_mutex.h" + +#include "dng_assertions.h" +#include "dng_exceptions.h" + +#include + +/*****************************************************************************/ + +// do mutex lock level tracking, asserts stripped in non-debug so don't track there +#ifndef qDNGThreadTestMutexLevels +#define qDNGThreadTestMutexLevels (qDNGThreadSafe && qDNGDebug) +#endif + +#if qDNGThreadTestMutexLevels +namespace + { + + class InnermostMutexHolder + { + + private: + + pthread_key_t fInnermostMutexKey; + + public: + + InnermostMutexHolder () + + : fInnermostMutexKey () + + { + + int result = pthread_key_create (&fInnermostMutexKey, NULL); + + DNG_ASSERT (result == 0, "pthread_key_create failed."); + + if (result != 0) + ThrowProgramError (); + + } + + ~InnermostMutexHolder () + { + + pthread_key_delete (fInnermostMutexKey); + + } + + void SetInnermostMutex (dng_mutex *mutex) + { + + int result; + + result = pthread_setspecific (fInnermostMutexKey, (void *)mutex); + + DNG_ASSERT (result == 0, "pthread_setspecific failed."); + + (void) result; + + #if 0 // Hard failure here was causing crash on quit. + + if (result != 0) + ThrowProgramError (); + + #endif + + } + + dng_mutex *GetInnermostMutex () + { + + void *result = pthread_getspecific (fInnermostMutexKey); + + return reinterpret_cast (result); + + } + + }; + + InnermostMutexHolder gInnermostMutexHolder; + + } + +#endif + +/*****************************************************************************/ + +dng_mutex::dng_mutex (const char *mutexName, uint32 mutexLevel) + + #if qDNGThreadSafe + + : fPthreadMutex () + , fMutexLevel (mutexLevel) + , fRecursiveLockCount (0) + , fPrevHeldMutex (NULL) + , fMutexName (mutexName) + + #endif + + { + + #if qDNGThreadSafe + + #if qWinOS + + // Win is already a recursive mutex by default + if (pthread_mutex_init (&fPthreadMutex, NULL) != 0) + { + ThrowMemoryFull (); + } + + #else + + // make recursive mutex, can lock within itself + pthread_mutexattr_t mta; + pthread_mutexattr_init(&mta); + pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_RECURSIVE); + + if (pthread_mutex_init (&fPthreadMutex, &mta) != 0) + { + ThrowMemoryFull (); + } + #endif + + #endif + + } + +/*****************************************************************************/ + +dng_mutex::~dng_mutex () + { + + #if qDNGThreadSafe + + pthread_mutex_destroy (&fPthreadMutex); + + #endif + + } + +/*****************************************************************************/ + +void dng_mutex::Lock () + { + + #if qDNGThreadSafe + #if qDNGThreadTestMutexLevels + + dng_mutex *innermostMutex = gInnermostMutexHolder.GetInnermostMutex (); + + if (innermostMutex != NULL) + { + + if (innermostMutex == this) + { + + int result = pthread_mutex_lock (&fPthreadMutex); + + if (result != 0) + { + + DNG_ASSERT (result == 0, "pthread_mutex_lock failed."); + + ThrowProgramError (); + + } + + fRecursiveLockCount++; + + return; + + } + + bool lockOrderPreserved = fMutexLevel > innermostMutex->fMutexLevel; + + // to allow cloning of class internals both with a dng_mutex and get closer to the C++ mutex, + // test for MutexLevelIgnore and don't generate level violations + if (!lockOrderPreserved) + { + + if ((fMutexLevel == kDNGMutexLevelIgnore) || (innermostMutex->fMutexLevel == kDNGMutexLevelIgnore)) + lockOrderPreserved = true; + + } + + if (!lockOrderPreserved) + { + + char msg[1024]; + + sprintf(msg, + "Lock order violation: This mutex: %s v Innermost mutex: %s", + this->MutexName (), + innermostMutex->MutexName ()); + + DNG_REPORT(msg); // asserts inside of mutex lock, any locks within that must be lower + + } + + } + + int result = pthread_mutex_lock (&fPthreadMutex); + + if (result != 0) + { + + DNG_ASSERT (result == 0, "pthread_mutex_lock failed."); + + ThrowProgramError (); + + } + + fPrevHeldMutex = innermostMutex; + + gInnermostMutexHolder.SetInnermostMutex (this); + + #else + + // Register the fact that we're trying to lock this mutex. + + int result = pthread_mutex_lock (&fPthreadMutex); + + if (result != 0) + { + + DNG_REPORT ("pthread_mutex_lock failed"); + + ThrowProgramError (); + + } + + // Register the fact that we've now successfully acquired the mutex. + + #endif + #endif + + } + +/*****************************************************************************/ + +void dng_mutex::Unlock () + { + + #if qDNGThreadSafe + #if qDNGThreadTestMutexLevels + + DNG_ASSERT (gInnermostMutexHolder.GetInnermostMutex () == this, "Mutexes unlocked out of order!!!"); + + if (fRecursiveLockCount > 0) + { + + fRecursiveLockCount--; + + pthread_mutex_unlock (&fPthreadMutex); + + return; + + } + + gInnermostMutexHolder.SetInnermostMutex (fPrevHeldMutex); + + fPrevHeldMutex = NULL; + + #endif + + pthread_mutex_unlock (&fPthreadMutex); + + #endif + + } + +/*****************************************************************************/ + +const char *dng_mutex::MutexName () const + { + + #if qDNGThreadSafe + + if (fMutexName) + return fMutexName; + + #endif + + return "< unknown >"; + + } + +/*****************************************************************************/ + +dng_lock_mutex::dng_lock_mutex (dng_mutex *mutex) + + : fMutex (mutex) + + { + + if (fMutex) + fMutex->Lock (); + + } + +/*****************************************************************************/ + +dng_lock_mutex::dng_lock_mutex (dng_mutex &mutex) + + : fMutex (&mutex) + + { + + if (fMutex) + fMutex->Lock (); + + } + +/*****************************************************************************/ + +dng_lock_mutex::~dng_lock_mutex () + { + + if (fMutex) + fMutex->Unlock (); + + } + +/*****************************************************************************/ + +dng_unlock_mutex::dng_unlock_mutex (dng_mutex *mutex) + + : fMutex (mutex) + + { + + if (fMutex) + fMutex->Unlock (); + + } + +/*****************************************************************************/ + +dng_unlock_mutex::dng_unlock_mutex (dng_mutex &mutex) + + : fMutex (&mutex) + + { + + if (fMutex) + fMutex->Unlock (); + + } + +/*****************************************************************************/ + +dng_unlock_mutex::~dng_unlock_mutex () + { + + if (fMutex) + fMutex->Lock (); + + } + +/*****************************************************************************/ + +dng_condition::dng_condition () + +#if qDNGThreadSafe + : fPthreadCondition () +#endif + + { + +#if qDNGThreadSafe + int result; + + result = pthread_cond_init (&fPthreadCondition, NULL); + + DNG_ASSERT (result == 0, "pthread_cond_init failed."); + + if (result != 0) + { + ThrowProgramError (); + } +#endif + + } + +/*****************************************************************************/ + +dng_condition::~dng_condition () + { +#if qDNGThreadSafe + pthread_cond_destroy (&fPthreadCondition); +#endif + } + +/*****************************************************************************/ + +bool dng_condition::Wait (dng_mutex &mutex, double timeoutSecs) + { + +#if qDNGThreadSafe + bool timedOut = false; + + #if qDNGThreadTestMutexLevels + + dng_mutex *innermostMutex = gInnermostMutexHolder.GetInnermostMutex (); + + DNG_ASSERT (innermostMutex == &mutex, "Attempt to wait on non-innermost mutex."); + + (void) innermostMutex; + + innermostMutex = mutex.fPrevHeldMutex; + + gInnermostMutexHolder.SetInnermostMutex (innermostMutex); + + mutex.fPrevHeldMutex = NULL; + + #endif + + if (timeoutSecs < 0) + { + + pthread_cond_wait (&fPthreadCondition, &mutex.fPthreadMutex); + + } + + else + { + + struct timespec now; + + dng_pthread_now (&now); + + timeoutSecs += now.tv_sec; + timeoutSecs += now.tv_nsec / 1000000000.0; + + now.tv_sec = (long) timeoutSecs; + now.tv_nsec = (long) ((timeoutSecs - now.tv_sec) * 1000000000); + + #if defined(_MSC_VER) && _MSC_VER >= 1900 + + struct dng_timespec tempNow; + + tempNow.tv_sec = (long) now.tv_sec; + tempNow.tv_nsec = now.tv_nsec; + + timedOut = (pthread_cond_timedwait (&fPthreadCondition, &mutex.fPthreadMutex, &tempNow) == ETIMEDOUT); + + #else + + timedOut = (pthread_cond_timedwait (&fPthreadCondition, &mutex.fPthreadMutex, &now) == ETIMEDOUT); + + #endif + + } + + #if qDNGThreadTestMutexLevels + + mutex.fPrevHeldMutex = innermostMutex; + + gInnermostMutexHolder.SetInnermostMutex (&mutex); + + #endif + + return !timedOut; +#else + return true; +#endif + + } + +/*****************************************************************************/ + +void dng_condition::Signal () + { + +#if qDNGThreadSafe + int result; + + result = pthread_cond_signal (&fPthreadCondition); + + DNG_ASSERT (result == 0, "pthread_cond_signal failed."); + + if (result != 0) + ThrowProgramError (); +#endif + + } + +/*****************************************************************************/ + +void dng_condition::Broadcast () + { +#if qDNGThreadSafe + + int result; + + result = pthread_cond_broadcast (&fPthreadCondition); + + DNG_ASSERT (result == 0, "pthread_cond_broadcast failed."); + + if (result != 0) + ThrowProgramError (); +#endif + } + +/*****************************************************************************/ diff --git a/dng/dng_mutex.h b/dng/dng_mutex.h new file mode 100644 index 0000000..9964741 --- /dev/null +++ b/dng/dng_mutex.h @@ -0,0 +1,145 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef DNG_MUTEX +#define DNG_MUTEX + +/******************************************************************************/ + +#include "dng_flags.h" +#include "dng_types.h" +#include "dng_uncopyable.h" + +#if qDNGThreadSafe +#include "dng_pthread.h" +#endif + +#include + +typedef std::mutex dng_std_mutex; +typedef std::lock_guard dng_lock_std_mutex; +typedef std::unique_lock dng_unique_lock; + +// We should try to phase out use of dng_mutex over time. +// +// Note that dng_mutex differs from dng_std_mutex (std::mutex) in that +// dng_mutex supports recursive locking (hierarchical mutex). + +/******************************************************************************/ + +class dng_mutex: private dng_uncopyable + { + + public: + + enum + { + kDNGMutexLevelLeaf = 0x70000000u, + kDNGMutexLevelIgnore = 0x7FFFFFFFu + }; + + dng_mutex (const char *mutexName, + uint32 mutexLevel = kDNGMutexLevelLeaf); + + virtual ~dng_mutex (); + + void Lock (); + + void Unlock (); + + const char *MutexName () const; + + protected: + + #if qDNGThreadSafe + + pthread_mutex_t fPthreadMutex; + + const uint32 fMutexLevel; + + uint32 fRecursiveLockCount; + + dng_mutex *fPrevHeldMutex; + + const char * const fMutexName; + + friend class dng_condition; + + #endif + + }; + +/*****************************************************************************/ + +class dng_lock_mutex: private dng_uncopyable + { + + private: + + dng_mutex *fMutex; + + public: + + dng_lock_mutex (dng_mutex *mutex); + + dng_lock_mutex (dng_mutex &mutex); + + ~dng_lock_mutex (); + + }; + +/*****************************************************************************/ + +class dng_unlock_mutex: private dng_uncopyable + { + + private: + + dng_mutex *fMutex; + + public: + + dng_unlock_mutex (dng_mutex *mutex); + + dng_unlock_mutex (dng_mutex &mutex); + + ~dng_unlock_mutex (); + + }; + +/*****************************************************************************/ + +class dng_condition: private dng_uncopyable + { + + public: + + dng_condition (); + + ~dng_condition (); + + bool Wait (dng_mutex &mutex, double timeoutSecs = -1.0); + + void Signal (); + + void Broadcast (); + + protected: + + +#if qDNGThreadSafe + pthread_cond_t fPthreadCondition; +#endif // qDNGThreadSafe + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_negative.cpp b/dng/dng_negative.cpp new file mode 100644 index 0000000..bcd6f54 --- /dev/null +++ b/dng/dng_negative.cpp @@ -0,0 +1,5941 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_negative.h" + +#include "dng_1d_table.h" +#include "dng_abort_sniffer.h" +#include "dng_area_task.h" +#include "dng_assertions.h" +#include "dng_bottlenecks.h" +#include "dng_camera_profile.h" +#include "dng_color_space.h" +#include "dng_color_spec.h" +#include "dng_exceptions.h" +#include "dng_globals.h" +#include "dng_host.h" +#include "dng_image.h" +#include "dng_image_writer.h" +#include "dng_info.h" +#include "dng_jpeg_image.h" +#include "dng_linearization_info.h" +#include "dng_memory.h" +#include "dng_memory_stream.h" +#include "dng_misc_opcodes.h" +#include "dng_mosaic_info.h" +#include "dng_preview.h" +#include "dng_resample.h" +#include "dng_safe_arithmetic.h" +#include "dng_simple_image.h" +#include "dng_tag_codes.h" +#include "dng_tag_values.h" +#include "dng_tile_iterator.h" +#include "dng_uncopyable.h" +#include "dng_utils.h" +#include "dng_xmp.h" + +/*****************************************************************************/ + +dng_noise_profile::dng_noise_profile () + + : fNoiseFunctions () + + { + + } + +/*****************************************************************************/ + +dng_noise_profile::dng_noise_profile (const dng_std_vector &functions) + + : fNoiseFunctions (functions) + + { + + } + +/*****************************************************************************/ + +bool dng_noise_profile::IsValid () const + { + + if (NumFunctions () == 0 || NumFunctions () > kMaxColorPlanes) + { + return false; + } + + for (uint32 plane = 0; plane < NumFunctions (); plane++) + { + + if (!NoiseFunction (plane).IsValid ()) + { + return false; + } + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_noise_profile::IsValidForNegative (const dng_negative &negative) const + { + + if (!(NumFunctions () == 1 || NumFunctions () == negative.ColorChannels ())) + { + return false; + } + + return IsValid (); + + } + +/*****************************************************************************/ + +const dng_noise_function & dng_noise_profile::NoiseFunction (uint32 plane) const + { + + if (NumFunctions () == 1) + { + return fNoiseFunctions.front (); + } + + DNG_REQUIRE (plane < NumFunctions (), + "Bad plane index argument for NoiseFunction ()."); + + return fNoiseFunctions [plane]; + + } + +/*****************************************************************************/ + +uint32 dng_noise_profile::NumFunctions () const + { + return (uint32) fNoiseFunctions.size (); + } + +/*****************************************************************************/ + +bool dng_noise_profile::operator== (const dng_noise_profile &profile) const + { + + if (IsValid ()) + { + + if (!profile.IsValid ()) + { + return false; + } + + if (NumFunctions () != profile.NumFunctions ()) + { + return false; + } + + for (uint32 plane = 0; plane < NumFunctions (); plane++) + { + + if (NoiseFunction (plane).Scale () != profile.NoiseFunction (plane).Scale () || + NoiseFunction (plane).Offset () != profile.NoiseFunction (plane).Offset ()) + { + return false; + } + + } + + return true; + + } + + else + return !profile.IsValid (); + + } + +/*****************************************************************************/ + +dng_metadata::dng_metadata (dng_host &host) + + : fHasBaseOrientation (false) + , fBaseOrientation () + , fIsMakerNoteSafe (false) + , fMakerNote () + , fExif (host.Make_dng_exif ()) + , fOriginalExif () + , fIPTCBlock () + , fIPTCOffset (kDNGStreamInvalidOffset) + , fXMP (host.Make_dng_xmp ()) + , fEmbeddedXMPDigest () + , fXMPinSidecar (false) + , fXMPisNewer (false) + , fSourceMIME () + + { + } + +/*****************************************************************************/ + +dng_metadata::~dng_metadata () + { + } + +/******************************************************************************/ + +template< class T > +T * CloneAutoPtr (const AutoPtr< T > &ptr) + { + + return ptr.Get () ? ptr->Clone () : NULL; + + } + +/******************************************************************************/ + +template< class T, typename U > +T * CloneAutoPtr (const AutoPtr< T > &ptr, U &u) + { + + return ptr.Get () ? ptr->Clone (u) : NULL; + + } + +/******************************************************************************/ + +dng_metadata::dng_metadata (const dng_metadata &rhs, + dng_memory_allocator &allocator) + + : fHasBaseOrientation (rhs.fHasBaseOrientation) + , fBaseOrientation (rhs.fBaseOrientation) + , fIsMakerNoteSafe (rhs.fIsMakerNoteSafe) + , fMakerNote (CloneAutoPtr (rhs.fMakerNote, allocator)) + , fExif (CloneAutoPtr (rhs.fExif)) + , fOriginalExif (CloneAutoPtr (rhs.fOriginalExif)) + , fIPTCBlock (CloneAutoPtr (rhs.fIPTCBlock, allocator)) + , fIPTCOffset (rhs.fIPTCOffset) + , fXMP (CloneAutoPtr (rhs.fXMP)) + , fEmbeddedXMPDigest (rhs.fEmbeddedXMPDigest) + , fXMPinSidecar (rhs.fXMPinSidecar) + , fXMPisNewer (rhs.fXMPisNewer) + , fSourceMIME (rhs.fSourceMIME) + + { + + } + +/******************************************************************************/ + +dng_metadata * dng_metadata::Clone (dng_memory_allocator &allocator) const + { + + return new dng_metadata (*this, allocator); + + } + +/******************************************************************************/ + +void dng_metadata::SetBaseOrientation (const dng_orientation &orientation) + { + + fHasBaseOrientation = true; + + fBaseOrientation = orientation; + + } + +/******************************************************************************/ + +void dng_metadata::ApplyOrientation (const dng_orientation &orientation) + { + + fBaseOrientation += orientation; + + fXMP->SetOrientation (fBaseOrientation); + + } + +/*****************************************************************************/ + +void dng_metadata::ResetExif (dng_exif * newExif) + { + + fExif.Reset (newExif); + + } + +/******************************************************************************/ + +dng_memory_block * dng_metadata::BuildExifBlock (dng_memory_allocator &allocator, + const dng_resolution *resolution, + bool includeIPTC, + const dng_jpeg_preview *thumbnail) const + { + + dng_memory_stream stream (allocator); + + { + + // Create the main IFD + + dng_tiff_directory mainIFD; + + // Optionally include the resolution tags. + + dng_resolution res; + + if (resolution) + { + res = *resolution; + } + + tag_urational tagXResolution (tcXResolution, res.fXResolution); + tag_urational tagYResolution (tcYResolution, res.fYResolution); + + tag_uint16 tagResolutionUnit (tcResolutionUnit, res.fResolutionUnit); + + if (resolution) + { + mainIFD.Add (&tagXResolution ); + mainIFD.Add (&tagYResolution ); + mainIFD.Add (&tagResolutionUnit); + } + + // Optionally include IPTC block. + + tag_iptc tagIPTC (IPTCData (), + IPTCLength ()); + + if (includeIPTC && tagIPTC.Count ()) + { + mainIFD.Add (&tagIPTC); + } + + // Exif tags. + + exif_tag_set exifSet (mainIFD, + *GetExif (), + IsMakerNoteSafe (), + MakerNoteData (), + MakerNoteLength (), + false); + + // Figure out the Exif IFD offset. + + uint32 exifOffset = 8 + mainIFD.Size (); + + exifSet.Locate (exifOffset); + + // Thumbnail IFD (if any). + + dng_tiff_directory thumbIFD; + + tag_uint16 thumbCompression (tcCompression, ccOldJPEG); + + tag_urational thumbXResolution (tcXResolution, dng_urational (72, 1)); + tag_urational thumbYResolution (tcYResolution, dng_urational (72, 1)); + + tag_uint16 thumbResolutionUnit (tcResolutionUnit, ruInch); + + tag_uint32 thumbDataOffset (tcJPEGInterchangeFormat , 0); + tag_uint32 thumbDataLength (tcJPEGInterchangeFormatLength, 0); + + if (thumbnail) + { + + thumbIFD.Add (&thumbCompression); + + thumbIFD.Add (&thumbXResolution); + thumbIFD.Add (&thumbYResolution); + thumbIFD.Add (&thumbResolutionUnit); + + thumbIFD.Add (&thumbDataOffset); + thumbIFD.Add (&thumbDataLength); + + thumbDataLength.Set (thumbnail->fCompressedData->LogicalSize ()); + + uint32 thumbOffset = exifOffset + exifSet.Size (); + + mainIFD.SetChained (thumbOffset); + + thumbDataOffset.Set (thumbOffset + thumbIFD.Size ()); + + } + + // Don't write anything unless the main IFD has some tags. + + if (mainIFD.Size () != 0) + { + + // Write TIFF Header. + + stream.SetWritePosition (0); + + stream.Put_uint16 (stream.BigEndian () ? byteOrderMM : byteOrderII); + + stream.Put_uint16 (42); + + stream.Put_uint32 (8); + + // Write the IFDs. + + mainIFD.Put (stream); + + exifSet.Put (stream); + + if (thumbnail) + { + + thumbIFD.Put (stream); + + stream.Put (thumbnail->fCompressedData->Buffer (), + thumbnail->fCompressedData->LogicalSize ()); + + } + + // Trim the file to this length. + + stream.Flush (); + + stream.SetLength (stream.Position ()); + + } + + } + + return stream.AsMemoryBlock (allocator); + + } + +/******************************************************************************/ + +void dng_metadata::SetIPTC (AutoPtr &block, uint64 offset) + { + + fIPTCBlock.Reset (block.Release ()); + + fIPTCOffset = offset; + + } + +/******************************************************************************/ + +void dng_metadata::SetIPTC (AutoPtr &block) + { + + SetIPTC (block, kDNGStreamInvalidOffset); + + } + +/******************************************************************************/ + +void dng_metadata::ClearIPTC () + { + + fIPTCBlock.Reset (); + + fIPTCOffset = kDNGStreamInvalidOffset; + + } + +/*****************************************************************************/ + +const void * dng_metadata::IPTCData () const + { + + if (fIPTCBlock.Get ()) + { + + return fIPTCBlock->Buffer (); + + } + + return NULL; + + } + +/*****************************************************************************/ + +uint32 dng_metadata::IPTCLength () const + { + + if (fIPTCBlock.Get ()) + { + + return fIPTCBlock->LogicalSize (); + + } + + return 0; + + } + +/*****************************************************************************/ + +uint64 dng_metadata::IPTCOffset () const + { + + if (fIPTCBlock.Get ()) + { + + return fIPTCOffset; + + } + + return kDNGStreamInvalidOffset; + + } + +/*****************************************************************************/ + +dng_fingerprint dng_metadata::IPTCDigest (bool includePadding) const + { + + if (IPTCLength ()) + { + + dng_md5_printer printer; + + const uint8 *data = (const uint8 *) IPTCData (); + + uint32 count = IPTCLength (); + + // Because of some stupid ways of storing the IPTC data, the IPTC + // data might be padded with up to three zeros. The official Adobe + // logic is to include these zeros in the digest. However, older + // versions of the Camera Raw code did not include the padding zeros + // in the digest, so we support both methods and allow either to + // match. + + if (!includePadding) + { + + uint32 removed = 0; + + while ((removed < 3) && (count > 0) && (data [count - 1] == 0)) + { + removed++; + count--; + } + + } + + printer.Process (data, count); + + return printer.Result (); + + } + + return dng_fingerprint (); + + } + +/******************************************************************************/ + +void dng_metadata::RebuildIPTC (dng_memory_allocator &allocator, + bool padForTIFF) + { + + ClearIPTC (); + + fXMP->RebuildIPTC (*this, allocator, padForTIFF); + + dng_fingerprint digest = IPTCDigest (); + + fXMP->SetIPTCDigest (digest); + + } + +/*****************************************************************************/ + +void dng_metadata::ResetXMP (dng_xmp * newXMP) + { + + fXMP.Reset (newXMP); + + } + +/*****************************************************************************/ + +void dng_metadata::ResetXMPSidecarNewer (dng_xmp * newXMP, + bool inSidecar, + bool isNewer ) + { + + fXMP.Reset (newXMP); + + fXMPinSidecar = inSidecar; + + fXMPisNewer = isNewer; + + } + +/*****************************************************************************/ + +bool dng_metadata::SetXMP (dng_host &host, + const void *buffer, + uint32 count, + bool xmpInSidecar, + bool xmpIsNewer) + { + + bool result = false; + + try + { + + AutoPtr tempXMP (host.Make_dng_xmp ()); + + tempXMP->Parse (host, buffer, count); + + ResetXMPSidecarNewer (tempXMP.Release (), xmpInSidecar, xmpIsNewer); + + result = true; + + } + + catch (dng_exception &except) + { + + // Don't ignore transient errors. + + if (host.IsTransientError (except.ErrorCode ())) + { + + throw; + + } + + // Eat other parsing errors. + + } + + catch (...) + { + + // Eat unknown parsing exceptions. + + } + + return result; + + } + +/*****************************************************************************/ + +void dng_metadata::SetEmbeddedXMP (dng_host &host, + const void *buffer, + uint32 count) + { + + if (SetXMP (host, buffer, count)) + { + + dng_md5_printer printer; + + printer.Process (buffer, count); + + fEmbeddedXMPDigest = printer.Result (); + + // Remove any sidecar specific tags from embedded XMP. + + if (fXMP.Get ()) + { + + fXMP->Remove (XMP_NS_PHOTOSHOP, "SidecarForExtension"); + fXMP->Remove (XMP_NS_PHOTOSHOP, "EmbeddedXMPDigest"); + + } + + } + + else + { + + fEmbeddedXMPDigest.Clear (); + + } + + } + +/*****************************************************************************/ + +void dng_metadata::SynchronizeMetadata () + { + + DNG_REQUIRE (fExif.Get (), + "Expected valid fExif field in " + "dng_metadata::SynchronizeMetadata"); + + if (!fOriginalExif.Get ()) + { + + fOriginalExif.Reset (fExif->Clone ()); + + } + + fXMP->ValidateMetadata (); + + fXMP->IngestIPTC (*this, fXMPisNewer); + + fXMP->SyncExif (*fExif.Get ()); + + fXMP->SyncOrientation (*this, fXMPinSidecar); + + } + +/*****************************************************************************/ + +void dng_metadata::UpdateDateTime (const dng_date_time_info &dt) + { + + fExif->UpdateDateTime (dt); + + fXMP->UpdateDateTime (dt); + + } + +/*****************************************************************************/ + +void dng_metadata::UpdateDateTimeToNow () + { + + dng_date_time_info dt; + + CurrentDateTimeAndZone (dt); + + UpdateDateTime (dt); + + fXMP->UpdateMetadataDate (dt); + + } + +/*****************************************************************************/ + +void dng_metadata::UpdateMetadataDateTimeToNow () + { + + dng_date_time_info dt; + + CurrentDateTimeAndZone (dt); + + fXMP->UpdateMetadataDate (dt); + + } + +/*****************************************************************************/ + +dng_negative::dng_negative (dng_host &host) + + : fAllocator (host.Allocator ()) + + , fModelName () + , fLocalName () + , fDefaultCropSizeH () + , fDefaultCropSizeV () + , fDefaultCropOriginH (0, 1) + , fDefaultCropOriginV (0, 1) + , fDefaultUserCropT (0, 1) + , fDefaultUserCropL (0, 1) + , fDefaultUserCropB (1, 1) + , fDefaultUserCropR (1, 1) + , fDefaultScaleH (1, 1) + , fDefaultScaleV (1, 1) + , fBestQualityScale (1, 1) + , fOriginalDefaultFinalSize () + , fOriginalBestQualityFinalSize () + , fOriginalDefaultCropSizeH () + , fOriginalDefaultCropSizeV () + , fRawToFullScaleH (1.0) + , fRawToFullScaleV (1.0) + , fBaselineNoise (100, 100) + , fNoiseReductionApplied (0, 0) + , fRawNoiseReductionApplied (0, 0) + , fNoiseProfile () + , fRawNoiseProfile () + , fBaselineExposure ( 0, 100) + , fBaselineSharpness (100, 100) + , fRawBaselineSharpness (0, 0) + , fChromaBlurRadius () + , fAntiAliasStrength (100, 100) + , fLinearResponseLimit (100, 100) + , fShadowScale (1, 1) + , fColorimetricReference (crSceneReferred) + , fFloatingPoint (false) + , fColorChannels (0) + , fAnalogBalance () + , fCameraNeutral () + , fCameraWhiteXY () + , fCameraCalibration1 () + , fCameraCalibration2 () + , fCameraCalibrationSignature () + , fCameraProfile () + , fAsShotProfileName () + , fRawImageDigest () + , fNewRawImageDigest () + , fRawDataUniqueID () + , fOriginalRawFileName () + , fHasOriginalRawFileData (false) + , fOriginalRawFileData () + , fOriginalRawFileDigest () + , fDNGPrivateData () + , fMetadata (host) + , fLinearizationInfo () + , fMosaicInfo () + , fOpcodeList1 (1) + , fOpcodeList2 (2) + , fOpcodeList3 (3) + , fStage1Image () + , fStage2Image () + , fStage3Image () + , fStage3Gain (1.0) + , fStage3BlackLevel (0) + , fIsPreview (false) + , fIsDamaged (false) + , fRawImageStage (rawImageStageNone) + , fRawImage () + , fRawImageBlackLevel (0) + , fRawFloatBitDepth (0) + , fRawJPEGImage () + , fRawJPEGImageDigest () + , fTransparencyMask () + , fRawTransparencyMask () + , fRawTransparencyMaskBitDepth (0) + , fUnflattenedStage3Image () + , fHasDepthMap (false) + , fDepthMap () + , fRawDepthMap () + , fDepthFormat (depthFormatUnknown) + , fDepthNear (0, 0) + , fDepthFar (0, 0) + , fDepthUnits (depthUnitsUnknown) + , fDepthMeasureType (depthMeasureUnknown) + , fEnhanceParams () + + { + + } + +/*****************************************************************************/ + +dng_negative::~dng_negative () + { + + // Delete any camera profiles owned by this negative. + + ClearProfiles (); + + } + +/******************************************************************************/ + +void dng_negative::Initialize () + { + + } + +/******************************************************************************/ + +dng_negative * dng_negative::Make (dng_host &host) + { + + AutoPtr result (new dng_negative (host)); + + if (!result.Get ()) + { + ThrowMemoryFull (); + } + + result->Initialize (); + + return result.Release (); + + } + +/******************************************************************************/ + +dng_metadata * dng_negative::CloneInternalMetadata () const + { + + return InternalMetadata ().Clone (Allocator ()); + + } + +/******************************************************************************/ + +dng_orientation dng_negative::ComputeOrientation (const dng_metadata &metadata) const + { + + return metadata.BaseOrientation (); + + } + +/******************************************************************************/ + +void dng_negative::SetAnalogBalance (const dng_vector &b) + { + + real64 minEntry = b.MinEntry (); + + if (b.NotEmpty () && minEntry > 0.0) + { + + fAnalogBalance = b; + + fAnalogBalance.Scale (1.0 / minEntry); + + fAnalogBalance.Round (1000000.0); + + } + + else + { + + fAnalogBalance.Clear (); + + } + + } + +/*****************************************************************************/ + +real64 dng_negative::AnalogBalance (uint32 channel) const + { + + DNG_ASSERT (channel < ColorChannels (), "Channel out of bounds"); + + if (channel < fAnalogBalance.Count ()) + { + + return fAnalogBalance [channel]; + + } + + return 1.0; + + } + +/*****************************************************************************/ + +dng_urational dng_negative::AnalogBalanceR (uint32 channel) const + { + + dng_urational result; + + result.Set_real64 (AnalogBalance (channel), 1000000); + + return result; + + } + +/******************************************************************************/ + +void dng_negative::SetCameraNeutral (const dng_vector &n) + { + + real64 maxEntry = n.MaxEntry (); + + if (n.NotEmpty () && maxEntry > 0.0) + { + + fCameraNeutral = n; + + fCameraNeutral.Scale (1.0 / maxEntry); + + fCameraNeutral.Round (1000000.0); + + } + + else + { + + fCameraNeutral.Clear (); + + } + + } + +/*****************************************************************************/ + +dng_urational dng_negative::CameraNeutralR (uint32 channel) const + { + + dng_urational result; + + result.Set_real64 (CameraNeutral () [channel], 1000000); + + return result; + + } + +/******************************************************************************/ + +void dng_negative::SetCameraWhiteXY (const dng_xy_coord &coord) + { + + if (coord.IsValid ()) + { + + fCameraWhiteXY.x = Round_int32 (coord.x * 1000000.0) / 1000000.0; + fCameraWhiteXY.y = Round_int32 (coord.y * 1000000.0) / 1000000.0; + + } + + else + { + + fCameraWhiteXY.Clear (); + + } + + } + +/*****************************************************************************/ + +const dng_xy_coord & dng_negative::CameraWhiteXY () const + { + + DNG_ASSERT (HasCameraWhiteXY (), "Using undefined CameraWhiteXY"); + + return fCameraWhiteXY; + + } + +/*****************************************************************************/ + +void dng_negative::GetCameraWhiteXY (dng_urational &x, + dng_urational &y) const + { + + dng_xy_coord coord = CameraWhiteXY (); + + x.Set_real64 (coord.x, 1000000); + y.Set_real64 (coord.y, 1000000); + + } + +/*****************************************************************************/ + +void dng_negative::SetCameraCalibration1 (const dng_matrix &m) + { + + fCameraCalibration1 = m; + + fCameraCalibration1.Round (10000); + + } + +/******************************************************************************/ + +void dng_negative::SetCameraCalibration2 (const dng_matrix &m) + { + + fCameraCalibration2 = m; + + fCameraCalibration2.Round (10000); + + } + +/******************************************************************************/ + +void dng_negative::AddProfile (AutoPtr &profile) + { + + // Make sure we have a profile to add. + + if (!profile.Get ()) + { + + return; + + } + + // We must have some profile name. Use "embedded" if nothing else. + + if (profile->Name ().IsEmpty ()) + { + + profile->SetName (kProfileName_Embedded); + + } + + // Special case support for reading older DNG files which did not store + // the profile name in the main IFD profile. + + if (fCameraProfile.size ()) + { + + // See the first profile has a default "embedded" name, and has + // the same data as the profile we are adding. + + if (fCameraProfile [0]->NameIsEmbedded () && + fCameraProfile [0]->EqualData (*profile.Get ())) + { + + // If the profile we are deleting was read from DNG + // then the new profile should be marked as such also. + + if (fCameraProfile [0]->WasReadFromDNG ()) + { + + profile->SetWasReadFromDNG (); + + } + + // If the profile we are deleting wasn't read from disk then the new + // profile should be marked as such also. + + if (!fCameraProfile [0]->WasReadFromDisk ()) + { + + profile->SetWasReadFromDisk (false); + + } + + // Delete the profile with default name. + + delete fCameraProfile [0]; + + fCameraProfile [0] = NULL; + + fCameraProfile.erase (fCameraProfile.begin ()); + + } + + } + + // Duplicate detection logic. We give a preference to last added profile + // so the profiles end up in a more consistent order no matter what profiles + // happen to be embedded in the DNG. + + for (uint32 index = 0; index < (uint32) fCameraProfile.size (); index++) + { + + // Instead of checking for matching fingerprints, we check that the two + // profiles have the same color and have the same name. This allows two + // profiles that are identical except for copyright string and embed policy + // to be considered duplicates. + + const bool equalColorAndSameName = (fCameraProfile [index]->EqualData (*profile.Get ()) && + fCameraProfile [index]->Name () == profile->Name ()); + + if (equalColorAndSameName) + { + + // If the profile we are deleting was read from DNG + // then the new profile should be marked as such also. + + if (fCameraProfile [index]->WasReadFromDNG ()) + { + + profile->SetWasReadFromDNG (); + + } + + // If the profile we are deleting wasn't read from disk then the new + // profile should be marked as such also. + + if (!fCameraProfile [index]->WasReadFromDisk ()) + { + + profile->SetWasReadFromDisk (false); + + } + + // Delete the duplicate profile. + + delete fCameraProfile [index]; + + fCameraProfile [index] = NULL; + + fCameraProfile.erase (fCameraProfile.begin () + index); + + break; + + } + + } + + // Now add to profile list. + + fCameraProfile.push_back (NULL); + + fCameraProfile [fCameraProfile.size () - 1] = profile.Release (); + + } + +/******************************************************************************/ + +void dng_negative::ClearProfiles () + { + + // Delete any camera profiles owned by this negative. + + for (uint32 index = 0; index < (uint32) fCameraProfile.size (); index++) + { + + if (fCameraProfile [index]) + { + + delete fCameraProfile [index]; + + fCameraProfile [index] = NULL; + + } + + } + + // Now empty list. + + fCameraProfile.clear (); + + } + +/*****************************************************************************/ + +void dng_negative::ClearProfiles (bool clearBuiltinMatrixProfiles, + bool clearReadFromDisk) + { + + // If neither flag is set, then there's nothing to do. + + if (!clearBuiltinMatrixProfiles && + !clearReadFromDisk) + { + return; + } + + // Delete any camera profiles in this negative that match the specified criteria. + + dng_std_vector::iterator iter = fCameraProfile.begin (); + dng_std_vector::iterator next; + + for (; iter != fCameraProfile.end (); iter = next) + { + + dng_camera_profile *profile = *iter; + + // If the profile is invalid (i.e., NULL pointer), or meets one of the + // specified criteria, then axe it. + + if (!profile || + (clearBuiltinMatrixProfiles && profile->WasBuiltinMatrix ()) || + (clearReadFromDisk && profile->WasReadFromDisk ())) + { + + delete profile; + + next = fCameraProfile.erase (iter); + + } + + // Otherwise, just advance to the next element. + + else + { + + next = iter + 1; + + } + + } + + } + +/******************************************************************************/ + +uint32 dng_negative::ProfileCount () const + { + + return (uint32) fCameraProfile.size (); + + } + +/******************************************************************************/ + +const dng_camera_profile & dng_negative::ProfileByIndex (uint32 index) const + { + + DNG_ASSERT (index < ProfileCount (), + "Invalid index for ProfileByIndex"); + + return *fCameraProfile [index]; + + } + +/*****************************************************************************/ + +const dng_camera_profile * dng_negative::ProfileByID (const dng_camera_profile_id &id, + bool useDefaultIfNoMatch) const + { + + uint32 index; + + // If this negative does not have any profiles, we are not going to + // find a match. + + uint32 profileCount = ProfileCount (); + + if (profileCount == 0) + { + return NULL; + } + + // If we have both a profile name and fingerprint, try matching both. + + if (id.Name ().NotEmpty () && id.Fingerprint ().IsValid ()) + { + + for (index = 0; index < profileCount; index++) + { + + const dng_camera_profile &profile = ProfileByIndex (index); + + if (id.Name () == profile.Name () && + id.Fingerprint () == profile.Fingerprint ()) + { + + return &profile; + + } + + } + + } + + // If we have a name, try matching that. + + if (id.Name ().NotEmpty ()) + { + + for (index = 0; index < profileCount; index++) + { + + const dng_camera_profile &profile = ProfileByIndex (index); + + if (id.Name () == profile.Name ()) + { + + return &profile; + + } + + } + + } + + // If we have a valid fingerprint, try matching that. + + if (id.Fingerprint ().IsValid ()) + { + + for (index = 0; index < profileCount; index++) + { + + const dng_camera_profile &profile = ProfileByIndex (index); + + if (id.Fingerprint () == profile.Fingerprint ()) + { + + return &profile; + + } + + } + + } + + // Try "upgrading" profile name versions. + + if (id.Name ().NotEmpty ()) + { + + dng_string baseName; + int32 version; + + SplitCameraProfileName (id.Name (), + baseName, + version); + + int32 bestIndex = -1; + int32 bestVersion = 0; + + for (index = 0; index < profileCount; index++) + { + + const dng_camera_profile &profile = ProfileByIndex (index); + + if (profile.Name ().StartsWith (baseName.Get ())) + { + + dng_string testBaseName; + int32 testVersion; + + SplitCameraProfileName (profile.Name (), + testBaseName, + testVersion); + + if (bestIndex == -1 || testVersion > bestVersion) + { + + bestIndex = index; + bestVersion = testVersion; + + } + + } + + } + + if (bestIndex != -1) + { + + return &ProfileByIndex (bestIndex); + + } + + } + + // Did not find a match any way. See if we should return a default value. + + if (useDefaultIfNoMatch) + { + + return &ProfileByIndex (0); + + } + + // Found nothing. + + return NULL; + + } + +/*****************************************************************************/ + +const dng_camera_profile * dng_negative::ComputeCameraProfileToEmbed + (const dng_metadata & /* metadata */) const + { + + uint32 index; + + uint32 count = ProfileCount (); + + if (count == 0) + { + + return NULL; + + } + + // First try to look for the first profile that was already in the DNG + // when we read it. + + for (index = 0; index < count; index++) + { + + const dng_camera_profile &profile (ProfileByIndex (index)); + + if (profile.WasReadFromDNG ()) + { + + return &profile; + + } + + } + + // Next we look for the first profile that is legal to embed. + + for (index = 0; index < count; index++) + { + + const dng_camera_profile &profile (ProfileByIndex (index)); + + if (profile.IsLegalToEmbed ()) + { + + return &profile; + + } + + } + + // Else just return the first profile. + + return fCameraProfile [0]; + + } + +/*****************************************************************************/ + +dng_color_spec * dng_negative::MakeColorSpec (const dng_camera_profile_id &id) const + { + + dng_color_spec *spec = new dng_color_spec (*this, ProfileByID (id)); + + if (!spec) + { + ThrowMemoryFull (); + } + + return spec; + + } + +/*****************************************************************************/ + +dng_fingerprint dng_negative::FindImageDigest (dng_host &host, + const dng_image &image) + { + + dng_md5_printer printer; + + dng_pixel_buffer buffer (image.Bounds (), + 0, + image.Planes (), + image.PixelType (), + pcInterleaved, + NULL); + + // Sometimes we expand 8-bit data to 16-bit data while reading or + // writing, so always compute the digest of 8-bit data as 16-bits. + + if (buffer.fPixelType == ttByte) + { + buffer.fPixelType = ttShort; + buffer.fPixelSize = 2; + } + + const uint32 kBufferRows = 16; + + uint32 bufferBytes = 0; + + if (!SafeUint32Mult (kBufferRows, buffer.fRowStep, &bufferBytes) || + !SafeUint32Mult (bufferBytes, buffer.fPixelSize, &bufferBytes)) + { + + ThrowOverflow ("Arithmetic overflow computing buffer size."); + + } + + AutoPtr bufferData (host.Allocate (bufferBytes)); + + buffer.fData = bufferData->Buffer (); + + dng_rect area; + + dng_tile_iterator iter (dng_point (kBufferRows, + image.Width ()), + image.Bounds ()); + + while (iter.GetOneTile (area)) + { + + host.SniffForAbort (); + + buffer.fArea = area; + + image.Get (buffer); + + uint32 count = buffer.fArea.H () * + buffer.fRowStep * + buffer.fPixelSize; + + #if qDNGBigEndian + + // We need to use the same byte order to compute + // the digest, no matter the native order. Little-endian + // is more common now, so use that. + + switch (buffer.fPixelSize) + { + + case 1: + break; + + case 2: + { + DoSwapBytes16 ((uint16 *) buffer.fData, count >> 1); + break; + } + + case 4: + { + DoSwapBytes32 ((uint32 *) buffer.fData, count >> 2); + break; + } + + default: + { + DNG_REPORT ("Unexpected pixel size"); + break; + } + + } + + #endif + + printer.Process (buffer.fData, + count); + + } + + return printer.Result (); + + } + +/*****************************************************************************/ + +void dng_negative::FindRawImageDigest (dng_host &host) const + { + + if (fRawImageDigest.IsNull ()) + { + + // Since we are adding the floating point and transparency support + // in DNG 1.4, and there are no legacy floating point or transparent + // DNGs, switch to using the more MP friendly algorithm to compute + // the digest for these images. + + if (RawImage ().PixelType () == ttFloat || RawTransparencyMask ()) + { + + FindNewRawImageDigest (host); + + fRawImageDigest = fNewRawImageDigest; + + } + + else + { + + #if qDNGValidate + + dng_timer timeScope ("FindRawImageDigest time"); + + #endif + + fRawImageDigest = FindImageDigest (host, RawImage ()); + + } + + } + + } + +/*****************************************************************************/ + +class dng_find_new_raw_image_digest_task : public dng_area_task + { + + private: + + enum + { + kTileSize = 256 + }; + + const dng_image &fImage; + + uint32 fPixelType; + uint32 fPixelSize; + + uint32 fTilesAcross; + uint32 fTilesDown; + + uint32 fTileCount; + + AutoArray fTileHash; + + AutoPtr fBufferData [kMaxMPThreads]; + + public: + + dng_find_new_raw_image_digest_task (const dng_image &image, + uint32 pixelType) + + : dng_area_task ("dng_find_new_raw_image_digest_task") + + , fImage (image) + , fPixelType (pixelType) + , fPixelSize (TagTypeSize (pixelType)) + , fTilesAcross (0) + , fTilesDown (0) + , fTileCount (0) + , fTileHash (NULL) + + { + + fMinTaskArea = 1; + + fUnitCell = dng_point (Min_int32 (kTileSize, fImage.Bounds ().H ()), + Min_int32 (kTileSize, fImage.Bounds ().W ())); + + fMaxTileSize = fUnitCell; + + } + + virtual void Start (uint32 threadCount, + const dng_rect & /* dstArea */, + const dng_point &tileSize, + dng_memory_allocator *allocator, + dng_abort_sniffer * /* sniffer */) + { + + if (tileSize != fUnitCell) + { + ThrowProgramError (); + } + + fTilesAcross = (fImage.Bounds ().W () + fUnitCell.h - 1) / fUnitCell.h; + fTilesDown = (fImage.Bounds ().H () + fUnitCell.v - 1) / fUnitCell.v; + + fTileCount = fTilesAcross * fTilesDown; + + fTileHash.Reset (new dng_fingerprint [fTileCount]); + + const uint32 bufferSize = + ComputeBufferSize (fPixelType, + tileSize, + fImage.Planes (), + padNone); + + for (uint32 index = 0; index < threadCount; index++) + { + + fBufferData [index].Reset (allocator->Allocate (bufferSize)); + + } + + } + + virtual void Process (uint32 threadIndex, + const dng_rect &tile, + dng_abort_sniffer * /* sniffer */) + { + + int32 colIndex = (tile.l - fImage.Bounds ().l) / fUnitCell.h; + int32 rowIndex = (tile.t - fImage.Bounds ().t) / fUnitCell.v; + + DNG_ASSERT (tile.l == fImage.Bounds ().l + colIndex * fUnitCell.h && + tile.t == fImage.Bounds ().t + rowIndex * fUnitCell.v, + "Bad tile origin"); + + uint32 tileIndex = rowIndex * fTilesAcross + colIndex; + + dng_pixel_buffer buffer (tile, + 0, + fImage.Planes (), + fPixelType, + pcPlanar, + fBufferData [threadIndex]->Buffer ()); + + fImage.Get (buffer); + + uint32 count = buffer.fPlaneStep * + buffer.fPlanes * + buffer.fPixelSize; + + #if qDNGBigEndian + + // We need to use the same byte order to compute + // the digest, no matter the native order. Little-endian + // is more common now, so use that. + + switch (buffer.fPixelSize) + { + + case 1: + break; + + case 2: + { + DoSwapBytes16 ((uint16 *) buffer.fData, count >> 1); + break; + } + + case 4: + { + DoSwapBytes32 ((uint32 *) buffer.fData, count >> 2); + break; + } + + default: + { + DNG_REPORT ("Unexpected pixel size"); + break; + } + + } + + #endif + + dng_md5_printer printer; + + printer.Process (buffer.fData, count); + + fTileHash [tileIndex] = printer.Result (); + + } + + dng_fingerprint Result () + { + + dng_md5_printer printer; + + for (uint32 tileIndex = 0; tileIndex < fTileCount; tileIndex++) + { + + printer.Process (fTileHash [tileIndex] . data, 16); + + } + + return printer.Result (); + + } + + }; + +/*****************************************************************************/ + +void dng_negative::FindNewRawImageDigest (dng_host &host) const + { + + if (fNewRawImageDigest.IsNull ()) + { + + #if qDNGValidate + + dng_timer timeScope ("FindNewRawImageDigest time"); + + #endif + + // Find fast digest of the raw image. + + { + + const dng_image &rawImage = RawImage (); + + // Find pixel type that will be saved in the file. When saving DNGs, we convert + // some 16-bit data to 8-bit data, so we need to do the matching logic here. + + uint32 rawPixelType = rawImage.PixelType (); + + if (rawPixelType == ttShort) + { + + // See if we are using a linearization table with <= 256 entries, in which + // case the useful data will all fit within 8-bits. + + const dng_linearization_info *rangeInfo = GetLinearizationInfo (); + + if (rangeInfo) + { + + if (rangeInfo->fLinearizationTable.Get ()) + { + + uint32 entries = rangeInfo->fLinearizationTable->LogicalSize () >> 1; + + if (entries <= 256) + { + + rawPixelType = ttByte; + + } + + } + + } + + } + + // Find the fast digest on the raw image. + + dng_find_new_raw_image_digest_task task (rawImage, rawPixelType); + + host.PerformAreaTask (task, rawImage.Bounds ()); + + fNewRawImageDigest = task.Result (); + + } + + // If there is a transparancy mask, we need to include that in the + // digest also. + + if (RawTransparencyMask () != NULL) + { + + // Find the fast digest on the raw mask. + + dng_fingerprint maskDigest; + + { + + dng_find_new_raw_image_digest_task task (*RawTransparencyMask (), + RawTransparencyMask ()->PixelType ()); + + host.PerformAreaTask (task, RawTransparencyMask ()->Bounds ()); + + maskDigest = task.Result (); + + } + + // Combine the two digests into a single digest. + + dng_md5_printer printer; + + printer.Process (fNewRawImageDigest.data, 16); + + printer.Process (maskDigest.data, 16); + + fNewRawImageDigest = printer.Result (); + + } + + } + + } + +/*****************************************************************************/ + +void dng_negative::ValidateRawImageDigest (dng_host &host) + { + + if (Stage1Image () && !IsPreview () && (fRawImageDigest .IsValid () || + fNewRawImageDigest.IsValid ())) + { + + bool isNewDigest = fNewRawImageDigest.IsValid (); + + dng_fingerprint &rawDigest = isNewDigest ? fNewRawImageDigest + : fRawImageDigest; + + // For lossy compressed JPEG images, we need to compare the stored + // digest to the digest computed from the compressed data, since + // decompressing lossy JPEG data is itself a lossy process. + + if (RawJPEGImageDigest ().IsValid () || RawJPEGImage ()) + { + + // Compute the raw JPEG image digest if we have not done so + // already. + + FindRawJPEGImageDigest (host); + + if (rawDigest != RawJPEGImageDigest ()) + { + + #if qDNGValidate + + ReportError ("RawImageDigest does not match raw jpeg image"); + + #else + + SetIsDamaged (true); + + #endif + + } + + } + + // Else we can compare the stored digest to the image in memory. + + else + { + + dng_fingerprint oldDigest = rawDigest; + + try + { + + rawDigest.Clear (); + + if (isNewDigest) + { + + FindNewRawImageDigest (host); + + } + + else + { + + FindRawImageDigest (host); + + } + + } + + catch (...) + { + + rawDigest = oldDigest; + + throw; + + } + + if (oldDigest != rawDigest) + { + + #if qDNGValidate + + if (isNewDigest) + { + ReportError ("NewRawImageDigest does not match raw image"); + } + else + { + ReportError ("RawImageDigest does not match raw image"); + } + + SetIsDamaged (true); + + #else + + if (!isNewDigest) + { + + // Note that Lightroom 1.4 Windows had a bug that corrupts the + // first four bytes of the RawImageDigest tag. So if the last + // twelve bytes match, this is very likely the result of the + // bug, and not an actual corrupt file. So don't report this + // to the user--just fix it. + + { + + bool matchLast12 = true; + + for (uint32 j = 4; j < 16; j++) + { + matchLast12 = matchLast12 && (oldDigest.data [j] == fRawImageDigest.data [j]); + } + + if (matchLast12) + { + return; + } + + } + + // Sometimes Lightroom 1.4 would corrupt more than the first four + // bytes, but for all those files that I have seen so far the + // resulting first four bytes are 0x08 0x00 0x00 0x00. + + if (oldDigest.data [0] == 0x08 && + oldDigest.data [1] == 0x00 && + oldDigest.data [2] == 0x00 && + oldDigest.data [3] == 0x00) + { + return; + } + + } + + SetIsDamaged (true); + + #endif + + } + + } + + } + + } + +/*****************************************************************************/ + +dng_fingerprint dng_negative::RawDataUniqueID () const + { + + dng_lock_std_mutex lock (fRawDataUniqueIDMutex); + + if (fRawDataUniqueID.IsValid () && fEnhanceParams.NotEmpty ()) + { + + dng_md5_printer printer; + + printer.Process (fRawDataUniqueID.data, 16); + + printer.Process (fEnhanceParams.Get (), + fEnhanceParams.Length ()); + + return printer.Result (); + + } + + return fRawDataUniqueID; + + } + +/*****************************************************************************/ + +// If the raw data unique ID is missing, compute one based on a MD5 hash of +// the raw image hash and the model name, plus other commonly changed +// data that can affect rendering. + +void dng_negative::FindRawDataUniqueID (dng_host &host) const + { + + if (RawDataUniqueID ().IsNull ()) + { + + dng_md5_printer_stream printer; + + // If we have a raw jpeg image, it is much faster to + // use its digest as part of the unique ID since + // the data size is much smaller. We cannot use it + // if there a transparency mask, since that is not + // included in the RawJPEGImageDigest. + + if (RawJPEGImage () && !RawTransparencyMask ()) + { + + FindRawJPEGImageDigest (host); + + printer.Put (fRawJPEGImageDigest.data, 16); + + } + + // Include the new raw image digest in the unique ID. + + else + { + + FindNewRawImageDigest (host); + + printer.Put (fNewRawImageDigest.data, 16); + + } + + // Include model name. + + printer.Put (ModelName ().Get (), + ModelName ().Length ()); + + // Include default crop area, since DNG Recover Edges can modify + // these values and they affect rendering. + + printer.Put_uint32 (fDefaultCropSizeH.n); + printer.Put_uint32 (fDefaultCropSizeH.d); + + printer.Put_uint32 (fDefaultCropSizeV.n); + printer.Put_uint32 (fDefaultCropSizeV.d); + + printer.Put_uint32 (fDefaultCropOriginH.n); + printer.Put_uint32 (fDefaultCropOriginH.d); + + printer.Put_uint32 (fDefaultCropOriginV.n); + printer.Put_uint32 (fDefaultCropOriginV.d); + + // Include default user crop. + + printer.Put_uint32 (fDefaultUserCropT.n); + printer.Put_uint32 (fDefaultUserCropT.d); + + printer.Put_uint32 (fDefaultUserCropL.n); + printer.Put_uint32 (fDefaultUserCropL.d); + + printer.Put_uint32 (fDefaultUserCropB.n); + printer.Put_uint32 (fDefaultUserCropB.d); + + printer.Put_uint32 (fDefaultUserCropR.n); + printer.Put_uint32 (fDefaultUserCropR.d); + + // Include opcode lists, since lens correction utilities can modify + // these values and they affect rendering. + + fOpcodeList1.FingerprintToStream (printer); + fOpcodeList2.FingerprintToStream (printer); + fOpcodeList3.FingerprintToStream (printer); + + dng_lock_std_mutex lock (fRawDataUniqueIDMutex); + + fRawDataUniqueID = printer.Result (); + + } + + } + +/******************************************************************************/ + +// Forces recomputation of RawDataUniqueID, useful to call +// after modifying the opcode lists, etc. + +void dng_negative::RecomputeRawDataUniqueID (dng_host &host) + { + + fRawDataUniqueID.Clear (); + + FindRawDataUniqueID (host); + + } + +/******************************************************************************/ + +void dng_negative::FindOriginalRawFileDigest () const + { + + if (fOriginalRawFileDigest.IsNull () && fOriginalRawFileData.Get ()) + { + + dng_md5_printer printer; + + printer.Process (fOriginalRawFileData->Buffer (), + fOriginalRawFileData->LogicalSize ()); + + fOriginalRawFileDigest = printer.Result (); + + } + + } + +/*****************************************************************************/ + +void dng_negative::ValidateOriginalRawFileDigest () + { + + if (fOriginalRawFileDigest.IsValid () && fOriginalRawFileData.Get ()) + { + + dng_fingerprint oldDigest = fOriginalRawFileDigest; + + try + { + + fOriginalRawFileDigest.Clear (); + + FindOriginalRawFileDigest (); + + } + + catch (...) + { + + fOriginalRawFileDigest = oldDigest; + + throw; + + } + + if (oldDigest != fOriginalRawFileDigest) + { + + #if qDNGValidate + + ReportError ("OriginalRawFileDigest does not match OriginalRawFileData"); + + #else + + SetIsDamaged (true); + + #endif + + // Don't "repair" the original image data digest. Once it is + // bad, it stays bad. The user cannot tell by looking at the image + // whether the damage is acceptable and can be ignored in the + // future. + + fOriginalRawFileDigest = oldDigest; + + } + + } + + } + +/******************************************************************************/ + +dng_rect dng_negative::DefaultCropArea () const + { + + // First compute the area using simple rounding. + + dng_rect result; + + result.l = Round_int32 (fDefaultCropOriginH.As_real64 () * fRawToFullScaleH); + result.t = Round_int32 (fDefaultCropOriginV.As_real64 () * fRawToFullScaleV); + + result.r = result.l + Round_int32 (fDefaultCropSizeH.As_real64 () * fRawToFullScaleH); + result.b = result.t + Round_int32 (fDefaultCropSizeV.As_real64 () * fRawToFullScaleV); + + // Sometimes the simple rounding causes the resulting default crop + // area to slide off the scaled image area. So we force this not + // to happen. We only do this if the image is not stubbed. + + const dng_image *image = Stage3Image (); + + if (image) + { + + dng_point imageSize = image->Size (); + + if (result.r > imageSize.h) + { + result.l -= result.r - imageSize.h; + result.r = imageSize.h; + } + + if (result.b > imageSize.v) + { + result.t -= result.b - imageSize.v; + result.b = imageSize.v; + } + + } + + return result; + + } + +/*****************************************************************************/ + +real64 dng_negative::TotalBaselineExposure (const dng_camera_profile_id &profileID) const + { + + real64 total = BaselineExposure (); + + const dng_camera_profile *profile = ProfileByID (profileID); + + if (profile) + { + + real64 offset = profile->BaselineExposureOffset ().As_real64 (); + + total += offset; + + } + + return total; + + } + +/******************************************************************************/ + +void dng_negative::SetShadowScale (const dng_urational &scale) + { + + if (scale.d > 0) + { + + real64 s = scale.As_real64 (); + + if (s > 0.0 && s <= 1.0) + { + + fShadowScale = scale; + + } + + } + + } + +/******************************************************************************/ + +void dng_negative::SetActiveArea (const dng_rect &area) + { + + NeedLinearizationInfo (); + + dng_linearization_info &info = *fLinearizationInfo.Get (); + + info.fActiveArea = area; + + } + +/******************************************************************************/ + +void dng_negative::SetMaskedAreas (uint32 count, + const dng_rect *area) + { + + DNG_ASSERT (count <= kMaxMaskedAreas, "Too many masked areas"); + + NeedLinearizationInfo (); + + dng_linearization_info &info = *fLinearizationInfo.Get (); + + info.fMaskedAreaCount = Min_uint32 (count, kMaxMaskedAreas); + + for (uint32 index = 0; index < info.fMaskedAreaCount; index++) + { + + info.fMaskedArea [index] = area [index]; + + } + + } + +/*****************************************************************************/ + +void dng_negative::SetLinearization (AutoPtr &curve) + { + + NeedLinearizationInfo (); + + dng_linearization_info &info = *fLinearizationInfo.Get (); + + info.fLinearizationTable.Reset (curve.Release ()); + + } + +/*****************************************************************************/ + +void dng_negative::SetBlackLevel (real64 black, + int32 plane) + { + + NeedLinearizationInfo (); + + dng_linearization_info &info = *fLinearizationInfo.Get (); + + info.fBlackLevelRepeatRows = 1; + info.fBlackLevelRepeatCols = 1; + + if (plane < 0) + { + + for (uint32 j = 0; j < kMaxSamplesPerPixel; j++) + { + + info.fBlackLevel [0] [0] [j] = black; + + } + + } + + else + { + + info.fBlackLevel [0] [0] [plane] = black; + + } + + info.RoundBlacks (); + + } + +/*****************************************************************************/ + +void dng_negative::SetQuadBlacks (real64 black0, + real64 black1, + real64 black2, + real64 black3, + int32 plane) + { + + NeedLinearizationInfo (); + + dng_linearization_info &info = *fLinearizationInfo.Get (); + + info.fBlackLevelRepeatRows = 2; + info.fBlackLevelRepeatCols = 2; + + if (plane < 0) + { + + for (uint32 j = 0; j < kMaxSamplesPerPixel; j++) + { + + info.fBlackLevel [0] [0] [j] = black0; + info.fBlackLevel [0] [1] [j] = black1; + info.fBlackLevel [1] [0] [j] = black2; + info.fBlackLevel [1] [1] [j] = black3; + + } + + } + + else + { + + info.fBlackLevel [0] [0] [plane] = black0; + info.fBlackLevel [0] [1] [plane] = black1; + info.fBlackLevel [1] [0] [plane] = black2; + info.fBlackLevel [1] [1] [plane] = black3; + + } + + info.RoundBlacks (); + + } + +/*****************************************************************************/ + +void dng_negative::Set6x6Blacks (real64 blacks6x6 [36], + int32 plane) + { + + NeedLinearizationInfo (); + + dng_linearization_info &info = *fLinearizationInfo.Get (); + + info.fBlackLevelRepeatRows = 6; + info.fBlackLevelRepeatCols = 6; + + if (plane < 0) + { + + // Apply the black levels to each image plane up to kMaxSamplesPerPixel. + + for (uint32 p = 0; p < kMaxSamplesPerPixel; p++) + { + + uint32 m = 0; + + for (uint32 r = 0; r < info.fBlackLevelRepeatRows; r++) + for (uint32 c = 0; c < info.fBlackLevelRepeatCols; c++) + { + + info.fBlackLevel [r] [c] [p] = blacks6x6 [m]; + + m++; + + } + } + } + + else + { + + uint32 m = 0; + + // Apply the black levels to a single plane. + + for (uint32 r = 0; r < info.fBlackLevelRepeatRows; r++) + for (uint32 c = 0; c < info.fBlackLevelRepeatCols; c++) + { + + info.fBlackLevel [r] [c] [plane] = blacks6x6 [m]; + + m++; + + } + + } + + info.RoundBlacks (); + + } + +/*****************************************************************************/ + +void dng_negative::SetRowBlacks (const real64 *blacks, + uint32 count) + { + + if (count) + { + + NeedLinearizationInfo (); + + dng_linearization_info &info = *fLinearizationInfo.Get (); + + dng_safe_uint32 byteCount = + dng_safe_uint32 (count) * (uint32) sizeof (real64); + + info.fBlackDeltaV.Reset (Allocator ().Allocate (byteCount.Get ())); + + DoCopyBytes (blacks, + info.fBlackDeltaV->Buffer (), + byteCount.Get ()); + + info.RoundBlacks (); + + } + + else if (fLinearizationInfo.Get ()) + { + + dng_linearization_info &info = *fLinearizationInfo.Get (); + + info.fBlackDeltaV.Reset (); + + } + + } + +/*****************************************************************************/ + +void dng_negative::SetColumnBlacks (const real64 *blacks, + uint32 count) + { + + if (count) + { + + NeedLinearizationInfo (); + + dng_linearization_info &info = *fLinearizationInfo.Get (); + + dng_safe_uint32 byteCount = + dng_safe_uint32 (count) * (uint32) sizeof (real64); + + info.fBlackDeltaH.Reset (Allocator ().Allocate (byteCount.Get ())); + + DoCopyBytes (blacks, + info.fBlackDeltaH->Buffer (), + byteCount.Get ()); + + info.RoundBlacks (); + + } + + else if (fLinearizationInfo.Get ()) + { + + dng_linearization_info &info = *fLinearizationInfo.Get (); + + info.fBlackDeltaH.Reset (); + + } + + } + +/*****************************************************************************/ + +uint32 dng_negative::WhiteLevel (uint32 plane) const + { + + if (fLinearizationInfo.Get ()) + { + + const dng_linearization_info &info = *fLinearizationInfo.Get (); + + return Round_uint32 (info.fWhiteLevel [plane]); + + } + + if (RawImage ().PixelType () == ttFloat) + { + + return 1; + + } + + return 0x0FFFF; + + } + +/*****************************************************************************/ + +void dng_negative::SetWhiteLevel (uint32 white, + int32 plane) + { + + NeedLinearizationInfo (); + + dng_linearization_info &info = *fLinearizationInfo.Get (); + + if (plane < 0) + { + + for (uint32 j = 0; j < kMaxSamplesPerPixel; j++) + { + + info.fWhiteLevel [j] = (real64) white; + + } + + } + + else + { + + info.fWhiteLevel [plane] = (real64) white; + + } + + } + +/******************************************************************************/ + +void dng_negative::SetColorKeys (ColorKeyCode color0, + ColorKeyCode color1, + ColorKeyCode color2, + ColorKeyCode color3) + { + + NeedMosaicInfo (); + + dng_mosaic_info &info = *fMosaicInfo.Get (); + + info.fCFAPlaneColor [0] = color0; + info.fCFAPlaneColor [1] = color1; + info.fCFAPlaneColor [2] = color2; + info.fCFAPlaneColor [3] = color3; + + } + +/******************************************************************************/ + +void dng_negative::SetBayerMosaic (uint32 phase) + { + + NeedMosaicInfo (); + + dng_mosaic_info &info = *fMosaicInfo.Get (); + + ColorKeyCode color0 = (ColorKeyCode) info.fCFAPlaneColor [0]; + ColorKeyCode color1 = (ColorKeyCode) info.fCFAPlaneColor [1]; + ColorKeyCode color2 = (ColorKeyCode) info.fCFAPlaneColor [2]; + + info.fCFAPatternSize = dng_point (2, 2); + + switch (phase) + { + + case 0: + { + info.fCFAPattern [0] [0] = color1; + info.fCFAPattern [0] [1] = color0; + info.fCFAPattern [1] [0] = color2; + info.fCFAPattern [1] [1] = color1; + break; + } + + case 1: + { + info.fCFAPattern [0] [0] = color0; + info.fCFAPattern [0] [1] = color1; + info.fCFAPattern [1] [0] = color1; + info.fCFAPattern [1] [1] = color2; + break; + } + + case 2: + { + info.fCFAPattern [0] [0] = color2; + info.fCFAPattern [0] [1] = color1; + info.fCFAPattern [1] [0] = color1; + info.fCFAPattern [1] [1] = color0; + break; + } + + case 3: + { + info.fCFAPattern [0] [0] = color1; + info.fCFAPattern [0] [1] = color2; + info.fCFAPattern [1] [0] = color0; + info.fCFAPattern [1] [1] = color1; + break; + } + + } + + info.fColorPlanes = 3; + + info.fCFALayout = 1; + + } + +/******************************************************************************/ + +void dng_negative::SetFujiMosaic (uint32 phase) + { + + NeedMosaicInfo (); + + dng_mosaic_info &info = *fMosaicInfo.Get (); + + ColorKeyCode color0 = (ColorKeyCode) info.fCFAPlaneColor [0]; + ColorKeyCode color1 = (ColorKeyCode) info.fCFAPlaneColor [1]; + ColorKeyCode color2 = (ColorKeyCode) info.fCFAPlaneColor [2]; + + info.fCFAPatternSize = dng_point (2, 4); + + switch (phase) + { + + case 0: + { + info.fCFAPattern [0] [0] = color0; + info.fCFAPattern [0] [1] = color1; + info.fCFAPattern [0] [2] = color2; + info.fCFAPattern [0] [3] = color1; + info.fCFAPattern [1] [0] = color2; + info.fCFAPattern [1] [1] = color1; + info.fCFAPattern [1] [2] = color0; + info.fCFAPattern [1] [3] = color1; + break; + } + + case 1: + { + info.fCFAPattern [0] [0] = color2; + info.fCFAPattern [0] [1] = color1; + info.fCFAPattern [0] [2] = color0; + info.fCFAPattern [0] [3] = color1; + info.fCFAPattern [1] [0] = color0; + info.fCFAPattern [1] [1] = color1; + info.fCFAPattern [1] [2] = color2; + info.fCFAPattern [1] [3] = color1; + break; + } + + } + + info.fColorPlanes = 3; + + info.fCFALayout = 2; + + } + +/*****************************************************************************/ + +void dng_negative::SetFujiMosaic6x6 (uint32 phase) + { + + NeedMosaicInfo (); + + dng_mosaic_info &info = *fMosaicInfo.Get (); + + ColorKeyCode color0 = (ColorKeyCode) info.fCFAPlaneColor [0]; + ColorKeyCode color1 = (ColorKeyCode) info.fCFAPlaneColor [1]; + ColorKeyCode color2 = (ColorKeyCode) info.fCFAPlaneColor [2]; + + const uint32 patSize = 6; + + info.fCFAPatternSize = dng_point (patSize, patSize); + + info.fCFAPattern [0] [0] = color1; + info.fCFAPattern [0] [1] = color2; + info.fCFAPattern [0] [2] = color1; + info.fCFAPattern [0] [3] = color1; + info.fCFAPattern [0] [4] = color0; + info.fCFAPattern [0] [5] = color1; + + info.fCFAPattern [1] [0] = color0; + info.fCFAPattern [1] [1] = color1; + info.fCFAPattern [1] [2] = color0; + info.fCFAPattern [1] [3] = color2; + info.fCFAPattern [1] [4] = color1; + info.fCFAPattern [1] [5] = color2; + + info.fCFAPattern [2] [0] = color1; + info.fCFAPattern [2] [1] = color2; + info.fCFAPattern [2] [2] = color1; + info.fCFAPattern [2] [3] = color1; + info.fCFAPattern [2] [4] = color0; + info.fCFAPattern [2] [5] = color1; + + info.fCFAPattern [3] [0] = color1; + info.fCFAPattern [3] [1] = color0; + info.fCFAPattern [3] [2] = color1; + info.fCFAPattern [3] [3] = color1; + info.fCFAPattern [3] [4] = color2; + info.fCFAPattern [3] [5] = color1; + + info.fCFAPattern [4] [0] = color2; + info.fCFAPattern [4] [1] = color1; + info.fCFAPattern [4] [2] = color2; + info.fCFAPattern [4] [3] = color0; + info.fCFAPattern [4] [4] = color1; + info.fCFAPattern [4] [5] = color0; + + info.fCFAPattern [5] [0] = color1; + info.fCFAPattern [5] [1] = color0; + info.fCFAPattern [5] [2] = color1; + info.fCFAPattern [5] [3] = color1; + info.fCFAPattern [5] [4] = color2; + info.fCFAPattern [5] [5] = color1; + + DNG_REQUIRE (phase >= 0 && phase < patSize * patSize, + "Bad phase in SetFujiMosaic6x6."); + + if (phase > 0) + { + + dng_mosaic_info temp = info; + + uint32 phaseRow = phase / patSize; + + uint32 phaseCol = phase - (phaseRow * patSize); + + for (uint32 dstRow = 0; dstRow < patSize; dstRow++) + { + + uint32 srcRow = (dstRow + phaseRow) % patSize; + + for (uint32 dstCol = 0; dstCol < patSize; dstCol++) + { + + uint32 srcCol = (dstCol + phaseCol) % patSize; + + temp.fCFAPattern [dstRow] [dstCol] = info.fCFAPattern [srcRow] [srcCol]; + + } + + } + + info = temp; + + } + + info.fColorPlanes = 3; + + info.fCFALayout = 1; + + } + +/******************************************************************************/ + +void dng_negative::SetQuadMosaic (uint32 pattern) + { + + // The pattern of the four colors is assumed to be repeat at least every two + // columns and eight rows. The pattern is encoded as a 32-bit integer, + // with every two bits encoding a color, in scan order for two columns and + // eight rows (lsb is first). The usual color coding is: + // + // 0 = Green + // 1 = Magenta + // 2 = Cyan + // 3 = Yellow + // + // Examples: + // + // PowerShot 600 uses 0xe1e4e1e4: + // + // 0 1 2 3 4 5 + // 0 G M G M G M + // 1 C Y C Y C Y + // 2 M G M G M G + // 3 C Y C Y C Y + // + // PowerShot A5 uses 0x1e4e1e4e: + // + // 0 1 2 3 4 5 + // 0 C Y C Y C Y + // 1 G M G M G M + // 2 C Y C Y C Y + // 3 M G M G M G + // + // PowerShot A50 uses 0x1b4e4b1e: + // + // 0 1 2 3 4 5 + // 0 C Y C Y C Y + // 1 M G M G M G + // 2 Y C Y C Y C + // 3 G M G M G M + // 4 C Y C Y C Y + // 5 G M G M G M + // 6 Y C Y C Y C + // 7 M G M G M G + // + // PowerShot Pro70 uses 0x1e4b4e1b: + // + // 0 1 2 3 4 5 + // 0 Y C Y C Y C + // 1 M G M G M G + // 2 C Y C Y C Y + // 3 G M G M G M + // 4 Y C Y C Y C + // 5 G M G M G M + // 6 C Y C Y C Y + // 7 M G M G M G + // + // PowerShots Pro90 and G1 use 0xb4b4b4b4: + // + // 0 1 2 3 4 5 + // 0 G M G M G M + // 1 Y C Y C Y C + + NeedMosaicInfo (); + + dng_mosaic_info &info = *fMosaicInfo.Get (); + + if (((pattern >> 16) & 0x0FFFF) != (pattern & 0x0FFFF)) + { + info.fCFAPatternSize = dng_point (8, 2); + } + + else if (((pattern >> 8) & 0x0FF) != (pattern & 0x0FF)) + { + info.fCFAPatternSize = dng_point (4, 2); + } + + else + { + info.fCFAPatternSize = dng_point (2, 2); + } + + for (int32 row = 0; row < info.fCFAPatternSize.v; row++) + { + + for (int32 col = 0; col < info.fCFAPatternSize.h; col++) + { + + uint32 index = (pattern >> ((((row << 1) & 14) + (col & 1)) << 1)) & 3; + + info.fCFAPattern [row] [col] = info.fCFAPlaneColor [index]; + + } + + } + + info.fColorPlanes = 4; + + info.fCFALayout = 1; + + } + +/******************************************************************************/ + +void dng_negative::SetGreenSplit (uint32 split) + { + + NeedMosaicInfo (); + + dng_mosaic_info &info = *fMosaicInfo.Get (); + + info.fBayerGreenSplit = split; + + } + +/*****************************************************************************/ + +void dng_negative::Parse (dng_host &host, + dng_stream &stream, + dng_info &info) + { + + // Shared info. + + dng_shared &shared = *(info.fShared.Get ()); + + // Find IFD holding the main raw information. + + dng_ifd &rawIFD = *info.fIFD [info.fMainIndex]; + + // Model name. + + SetModelName (shared.fUniqueCameraModel.Get ()); + + // Localized model name. + + SetLocalName (shared.fLocalizedCameraModel.Get ()); + + // Base orientation. + + { + + uint32 orientation = info.fIFD [0]->fOrientation; + + if (orientation >= 1 && orientation <= 8) + { + + SetBaseOrientation (dng_orientation::TIFFtoDNG (orientation)); + + } + + } + + // Default crop rectangle. + + SetDefaultCropSize (rawIFD.fDefaultCropSizeH, + rawIFD.fDefaultCropSizeV); + + SetDefaultCropOrigin (rawIFD.fDefaultCropOriginH, + rawIFD.fDefaultCropOriginV); + + // Default user crop rectangle. + + SetDefaultUserCrop (rawIFD.fDefaultUserCropT, + rawIFD.fDefaultUserCropL, + rawIFD.fDefaultUserCropB, + rawIFD.fDefaultUserCropR); + + // Default scale. + + SetDefaultScale (rawIFD.fDefaultScaleH, + rawIFD.fDefaultScaleV); + + // Best quality scale. + + SetBestQualityScale (rawIFD.fBestQualityScale); + + // Baseline noise. + + SetBaselineNoise (shared.fBaselineNoise.As_real64 ()); + + // NoiseReductionApplied. + + // Kludge: DNG spec says that NoiseReductionApplied tag should be in the + // Raw IFD, not main IFD. However, our original DNG SDK implementation + // read/wrote this tag from/to the main IFD. We now support reading the + // NoiseReductionApplied tag from both locations, but prefer the raw IFD + // (correct location). + + if (rawIFD.fNoiseReductionApplied.IsValid ()) + { + + SetNoiseReductionApplied (rawIFD.fNoiseReductionApplied); + + } + + else + { + + const dng_ifd &ifd0 = *info.fIFD [0]; + + SetNoiseReductionApplied (ifd0.fNoiseReductionApplied); + + } + + // NoiseProfile. + + // Kludge: DNG spec says that NoiseProfile tag should be in the Raw IFD, + // not main IFD. However, our original DNG SDK implementation read/wrote + // this tag from/to the main IFD. We now support reading the NoiseProfile + // tag from both locations, but prefer the raw IFD (correct location). + + if (rawIFD.fNoiseProfile.IsValid ()) + { + + SetNoiseProfile (rawIFD.fNoiseProfile); + + } + + else + { + + const dng_ifd &ifd0 = *info.fIFD [0]; + + SetNoiseProfile (ifd0.fNoiseProfile); + + } + + // Baseline exposure. + + SetBaselineExposure (shared.fBaselineExposure.As_real64 ()); + + // Baseline sharpness. + + SetBaselineSharpness (shared.fBaselineSharpness.As_real64 ()); + + // Chroma blur radius. + + SetChromaBlurRadius (rawIFD.fChromaBlurRadius); + + // Anti-alias filter strength. + + SetAntiAliasStrength (rawIFD.fAntiAliasStrength); + + // Linear response limit. + + SetLinearResponseLimit (shared.fLinearResponseLimit.As_real64 ()); + + // Shadow scale. + + SetShadowScale (shared.fShadowScale); + + // Colorimetric reference. + + SetColorimetricReference (shared.fColorimetricReference); + + // Floating point flag. + + SetFloatingPoint (rawIFD.fSampleFormat [0] == sfFloatingPoint); + + // Color channels. + + SetColorChannels (shared.fCameraProfile.fColorPlanes); + + // Analog balance. + + if (shared.fAnalogBalance.NotEmpty ()) + { + + SetAnalogBalance (shared.fAnalogBalance); + + } + + // Camera calibration matrices + + if (shared.fCameraCalibration1.NotEmpty ()) + { + + SetCameraCalibration1 (shared.fCameraCalibration1); + + } + + if (shared.fCameraCalibration2.NotEmpty ()) + { + + SetCameraCalibration2 (shared.fCameraCalibration2); + + } + + if (shared.fCameraCalibration1.NotEmpty () || + shared.fCameraCalibration2.NotEmpty ()) + { + + SetCameraCalibrationSignature (shared.fCameraCalibrationSignature.Get ()); + + } + + // Embedded camera profiles. + + if (shared.fCameraProfile.fColorPlanes > 1) + { + + if (qDNGValidate || host.NeedsMeta () || host.NeedsImage ()) + { + + // Add profile from main IFD. + + { + + AutoPtr profile (new dng_camera_profile ()); + + dng_camera_profile_info &profileInfo = shared.fCameraProfile; + + profile->Parse (stream, profileInfo); + + // The main embedded profile must be valid. + + if (!profile->IsValid (shared.fCameraProfile.fColorPlanes)) + { + + ThrowBadFormat (); + + } + + profile->SetWasReadFromDNG (); + + AddProfile (profile); + + } + + // Extra profiles. + + for (uint32 index = 0; index < (uint32) shared.fExtraCameraProfiles.size (); index++) + { + + try + { + + AutoPtr profile (new dng_camera_profile ()); + + dng_camera_profile_info &profileInfo = shared.fExtraCameraProfiles [index]; + + profile->Parse (stream, profileInfo); + + if (!profile->IsValid (shared.fCameraProfile.fColorPlanes)) + { + + ThrowBadFormat (); + + } + + profile->SetWasReadFromDNG (); + + AddProfile (profile); + + } + + catch (dng_exception &except) + { + + // Don't ignore transient errors. + + if (host.IsTransientError (except.ErrorCode ())) + { + + throw; + + } + + // Eat other parsing errors. + + #if qDNGValidate + + ReportWarning ("Unable to parse extra profile"); + + #endif + + } + + } + + } + + // As shot profile name. + + if (shared.fAsShotProfileName.NotEmpty ()) + { + + SetAsShotProfileName (shared.fAsShotProfileName.Get ()); + + } + + } + + // Raw image data digest. + + if (shared.fRawImageDigest.IsValid ()) + { + + SetRawImageDigest (shared.fRawImageDigest); + + } + + // New raw image data digest. + + if (shared.fNewRawImageDigest.IsValid ()) + { + + SetNewRawImageDigest (shared.fNewRawImageDigest); + + } + + // Raw data unique ID. + + if (shared.fRawDataUniqueID.IsValid ()) + { + + SetRawDataUniqueID (shared.fRawDataUniqueID); + + } + + // Original raw file name. + + if (shared.fOriginalRawFileName.NotEmpty ()) + { + + SetOriginalRawFileName (shared.fOriginalRawFileName.Get ()); + + } + + // Original raw file data. + + if (shared.fOriginalRawFileDataCount) + { + + SetHasOriginalRawFileData (true); + + if (host.KeepOriginalFile ()) + { + + uint32 count = shared.fOriginalRawFileDataCount; + + AutoPtr block (host.Allocate (count)); + + stream.SetReadPosition (shared.fOriginalRawFileDataOffset); + + stream.Get (block->Buffer (), count); + + SetOriginalRawFileData (block); + + SetOriginalRawFileDigest (shared.fOriginalRawFileDigest); + + ValidateOriginalRawFileDigest (); + + } + + } + + // DNG private data. + + if (shared.fDNGPrivateDataCount && (host.SaveDNGVersion () != dngVersion_None)) + { + + uint32 length = shared.fDNGPrivateDataCount; + + AutoPtr block (host.Allocate (length)); + + stream.SetReadPosition (shared.fDNGPrivateDataOffset); + + stream.Get (block->Buffer (), length); + + SetPrivateData (block); + + } + + // Hand off EXIF metadata to negative. + + ResetExif (info.fExif.Release ()); + + // Parse linearization info. + + NeedLinearizationInfo (); + + fLinearizationInfo.Get ()->Parse (host, + stream, + info); + + // Parse mosaic info. + + if (rawIFD.fPhotometricInterpretation == piCFA) + { + + NeedMosaicInfo (); + + fMosaicInfo.Get ()->Parse (host, + stream, + info); + + } + + // Fill in original sizes. + + if (shared.fOriginalDefaultFinalSize.h > 0 && + shared.fOriginalDefaultFinalSize.v > 0) + { + + SetOriginalDefaultFinalSize (shared.fOriginalDefaultFinalSize); + + SetOriginalBestQualityFinalSize (shared.fOriginalDefaultFinalSize); + + SetOriginalDefaultCropSize (dng_urational (shared.fOriginalDefaultFinalSize.h, 1), + dng_urational (shared.fOriginalDefaultFinalSize.v, 1)); + + } + + if (shared.fOriginalBestQualityFinalSize.h > 0 && + shared.fOriginalBestQualityFinalSize.v > 0) + { + + SetOriginalBestQualityFinalSize (shared.fOriginalBestQualityFinalSize); + + } + + if (shared.fOriginalDefaultCropSizeH.As_real64 () >= 1.0 && + shared.fOriginalDefaultCropSizeV.As_real64 () >= 1.0) + { + + SetOriginalDefaultCropSize (shared.fOriginalDefaultCropSizeH, + shared.fOriginalDefaultCropSizeV); + + } + + if (shared.fDepthFormat <= depthFormatUnknown) + { + + SetDepthFormat (shared.fDepthFormat); + + } + + if (shared.fDepthNear != dng_urational (0, 0)) + { + + SetDepthNear (shared.fDepthNear); + + } + + if (shared.fDepthFar != dng_urational (0, 0)) + { + + SetDepthFar (shared.fDepthFar); + + } + + if (shared.fDepthUnits != depthUnitsUnknown) + { + + SetDepthUnits (shared.fDepthUnits); + + } + + if (shared.fDepthMeasureType != depthMeasureUnknown) + { + + SetDepthMeasureType (shared.fDepthMeasureType); + + } + + } + +/*****************************************************************************/ + +void dng_negative::SetDefaultOriginalSizes () + { + + // Fill in original sizes if we don't have them already. + + if (OriginalDefaultFinalSize () == dng_point ()) + { + + SetOriginalDefaultFinalSize (dng_point (DefaultFinalHeight (), + DefaultFinalWidth ())); + + } + + if (OriginalBestQualityFinalSize () == dng_point ()) + { + + SetOriginalBestQualityFinalSize (dng_point (BestQualityFinalHeight (), + BestQualityFinalWidth ())); + + } + + if (OriginalDefaultCropSizeH ().NotValid () || + OriginalDefaultCropSizeV ().NotValid ()) + { + + SetOriginalDefaultCropSize (DefaultCropSizeH (), + DefaultCropSizeV ()); + + } + + } + +/*****************************************************************************/ + +void dng_negative::SetOriginalSizes (const dng_point &size) + { + + SetOriginalDefaultFinalSize (size); + + SetOriginalBestQualityFinalSize (size); + + SetOriginalDefaultCropSize (dng_urational (size.h, 1), + dng_urational (size.v, 1)); + + } + +/*****************************************************************************/ + +void dng_negative::PostParse (dng_host &host, + dng_stream &stream, + dng_info &info) + { + + // Shared info. + + dng_shared &shared = *(info.fShared.Get ()); + + if (host.NeedsMeta ()) + { + + // Fill in original sizes if we don't have them already. + + SetDefaultOriginalSizes (); + + // MakerNote. + + if (shared.fMakerNoteCount) + { + + // See if we know if the MakerNote is safe or not. + + SetMakerNoteSafety (shared.fMakerNoteSafety == 1); + + // If the MakerNote is safe, preserve it as a MakerNote. + + if (IsMakerNoteSafe ()) + { + + AutoPtr block (host.Allocate (shared.fMakerNoteCount)); + + stream.SetReadPosition (shared.fMakerNoteOffset); + + stream.Get (block->Buffer (), shared.fMakerNoteCount); + + SetMakerNote (block); + + } + + } + + // IPTC metadata. + + if (shared.fIPTC_NAA_Count) + { + + AutoPtr block (host.Allocate (shared.fIPTC_NAA_Count)); + + stream.SetReadPosition (shared.fIPTC_NAA_Offset); + + uint64 iptcOffset = stream.PositionInOriginalFile(); + + stream.Get (block->Buffer (), + block->LogicalSize ()); + + SetIPTC (block, iptcOffset); + + } + + // XMP metadata. + + if (shared.fXMPCount) + { + + AutoPtr block (host.Allocate (shared.fXMPCount)); + + stream.SetReadPosition (shared.fXMPOffset); + + stream.Get (block->Buffer (), + block->LogicalSize ()); + + Metadata ().SetEmbeddedXMP (host, + block->Buffer (), + block->LogicalSize ()); + + #if qDNGValidate + + if (!Metadata ().HaveValidEmbeddedXMP ()) + { + ReportError ("The embedded XMP is invalid"); + } + + #endif + + } + + // Color info. + + if (!IsMonochrome ()) + { + + // If the ColorimetricReference is the ICC profile PCS, + // then the data must be already be white balanced to the + // ICC profile PCS white point. + + if (ColorimetricReference () == crICCProfilePCS) + { + + ClearCameraNeutral (); + + SetCameraWhiteXY (PCStoXY ()); + + } + + else + { + + // AsShotNeutral. + + if (shared.fAsShotNeutral.Count () == ColorChannels ()) + { + + SetCameraNeutral (shared.fAsShotNeutral); + + } + + // AsShotWhiteXY. + + if (shared.fAsShotWhiteXY.IsValid () && !HasCameraNeutral ()) + { + + SetCameraWhiteXY (shared.fAsShotWhiteXY); + + } + + } + + } + + } + + } + +/*****************************************************************************/ + +bool dng_negative::SetFourColorBayer () + { + + if (ColorChannels () != 3) + { + return false; + } + + if (!fMosaicInfo.Get ()) + { + return false; + } + + if (!fMosaicInfo.Get ()->SetFourColorBayer ()) + { + return false; + } + + SetColorChannels (4); + + if (fCameraNeutral.Count () == 3) + { + + dng_vector n (4); + + n [0] = fCameraNeutral [0]; + n [1] = fCameraNeutral [1]; + n [2] = fCameraNeutral [2]; + n [3] = fCameraNeutral [1]; + + fCameraNeutral = n; + + } + + fCameraCalibration1.Clear (); + fCameraCalibration2.Clear (); + + fCameraCalibrationSignature.Clear (); + + for (uint32 index = 0; index < (uint32) fCameraProfile.size (); index++) + { + + fCameraProfile [index]->SetFourColorBayer (); + + } + + return true; + + } + +/*****************************************************************************/ + +const dng_image & dng_negative::RawImage () const + { + + if (fRawImage.Get ()) + { + return *fRawImage.Get (); + } + + if (fStage1Image.Get ()) + { + return *fStage1Image.Get (); + } + + if (fUnflattenedStage3Image.Get ()) + { + return *fUnflattenedStage3Image.Get (); + } + + DNG_REQUIRE (fStage3Image.Get (), + "dng_negative::RawImage with no raw image"); + + return *fStage3Image.Get (); + + } + +/*****************************************************************************/ + +uint16 dng_negative::RawImageBlackLevel () const + { + + if (fRawImage.Get ()) + { + return fRawImageBlackLevel; + } + + if (fStage1Image.Get ()) + { + return 0; + } + + return fStage3BlackLevel; + + } + +/*****************************************************************************/ + +const dng_jpeg_image * dng_negative::RawJPEGImage () const + { + + return fRawJPEGImage.Get (); + + } + +/*****************************************************************************/ + +void dng_negative::SetRawJPEGImage (AutoPtr &jpegImage) + { + + fRawJPEGImage.Reset (jpegImage.Release ()); + + } + +/*****************************************************************************/ + +void dng_negative::ClearRawJPEGImage () + { + + fRawJPEGImage.Reset (); + + } + +/*****************************************************************************/ + +void dng_negative::FindRawJPEGImageDigest (dng_host &host) const + { + + if (fRawJPEGImageDigest.IsNull ()) + { + + if (fRawJPEGImage.Get ()) + { + + #if qDNGValidate + + dng_timer timer ("FindRawJPEGImageDigest time"); + + #endif + + fRawJPEGImageDigest = fRawJPEGImage->FindDigest (host); + + } + + else + { + + ThrowProgramError ("No raw JPEG image"); + + } + + } + + } + +/*****************************************************************************/ + +void dng_negative::ReadOpcodeLists (dng_host &host, + dng_stream &stream, + dng_info &info) + { + + dng_ifd &rawIFD = *info.fIFD [info.fMainIndex]; + + if (rawIFD.fOpcodeList1Count) + { + + #if qDNGValidate + + if (gVerbose) + { + printf ("\nParsing OpcodeList1: "); + } + + #endif + + fOpcodeList1.Parse (host, + stream, + rawIFD.fOpcodeList1Count, + rawIFD.fOpcodeList1Offset); + + } + + if (rawIFD.fOpcodeList2Count) + { + + #if qDNGValidate + + if (gVerbose) + { + printf ("\nParsing OpcodeList2: "); + } + + #endif + + fOpcodeList2.Parse (host, + stream, + rawIFD.fOpcodeList2Count, + rawIFD.fOpcodeList2Offset); + + } + + if (rawIFD.fOpcodeList3Count) + { + + #if qDNGValidate + + if (gVerbose) + { + printf ("\nParsing OpcodeList3: "); + } + + #endif + + fOpcodeList3.Parse (host, + stream, + rawIFD.fOpcodeList3Count, + rawIFD.fOpcodeList3Offset); + + } + + } + +/*****************************************************************************/ + +void dng_negative::ReadStage1Image (dng_host &host, + dng_stream &stream, + dng_info &info) + { + + // Allocate image we are reading. + + dng_ifd &rawIFD = *info.fIFD [info.fMainIndex]; + + fStage1Image.Reset (host.Make_dng_image (rawIFD.Bounds (), + rawIFD.fSamplesPerPixel, + rawIFD.PixelType ())); + + // See if we should grab the compressed JPEG data. + + AutoPtr jpegImage; + + if (host.SaveDNGVersion () >= dngVersion_1_4_0_0 && + !host.PreferredSize () && + !host.ForPreview () && + rawIFD.fCompression == ccLossyJPEG) + { + + jpegImage.Reset (new dng_jpeg_image); + + } + + // See if we need to compute the digest of the compressed JPEG data + // while reading. + + bool needJPEGDigest = (RawImageDigest ().IsValid () || + NewRawImageDigest ().IsValid ()) && + rawIFD.fCompression == ccLossyJPEG && + jpegImage.Get () == NULL; + + dng_fingerprint jpegDigest; + + // Read the image. + + rawIFD.ReadImage (host, + stream, + *fStage1Image.Get (), + jpegImage.Get (), + needJPEGDigest ? &jpegDigest : NULL); + + // Remember the raw floating point bit depth, if reading from + // a floating point image. + + if (fStage1Image->PixelType () == ttFloat) + { + + SetRawFloatBitDepth (rawIFD.fBitsPerSample [0]); + + } + + // Remember the compressed JPEG data if we read it. + + if (jpegImage.Get ()) + { + + SetRawJPEGImage (jpegImage); + + } + + // Remember the compressed JPEG digest if we computed it. + + if (jpegDigest.IsValid ()) + { + + SetRawJPEGImageDigest (jpegDigest); + + } + + // We are are reading the main image, we should read the opcode lists + // also. + + ReadOpcodeLists (host, + stream, + info); + + } + +/*****************************************************************************/ + +void dng_negative::ReadEnhancedImage (dng_host &host, + dng_stream &stream, + dng_info &info) + { + + // Allocate image we are reading. + + dng_ifd &enhancedIFD = *info.fIFD [info.fEnhancedIndex]; + + fStage3Image.Reset (host.Make_dng_image (enhancedIFD.Bounds (), + enhancedIFD.fSamplesPerPixel, + enhancedIFD.PixelType ())); + + // Read the image. + + enhancedIFD.ReadImage (host, + stream, + *fStage3Image.Get ()); + + // Remember the enhance parameters. + + SetEnhanceParams (enhancedIFD.fEnhanceParams); + + // Stage 3 black level. + + SetStage3BlackLevel ((uint16) Round_uint32 (enhancedIFD.fBlackLevel [0] [0] [0])); + + // We are are reading the enhanced image, we should read the opcode lists + // also, since we at least need to know about lens opcodes. + + ReadOpcodeLists (host, + stream, + info); + + // Should we read the raw image also? + + bool needRawImage = host.SaveDNGVersion () != 0 && + !host.SaveLinearDNG (*this); + + // Read in raw image data if required. + + if (needRawImage) + { + + dng_ifd &mainIFD = *info.fIFD [info.fMainIndex]; + + fRawImage.Reset (host.Make_dng_image (mainIFD.Bounds (), + mainIFD.fSamplesPerPixel, + mainIFD.PixelType ())); + + mainIFD.ReadImage (host, + stream, + *fRawImage.Get ()); + + } + + // Baseline sharpness. + + if (enhancedIFD.fBaselineSharpness.IsValid ()) + { + + SetRawBaselineSharpness (); + + fBaselineSharpness = enhancedIFD.fBaselineSharpness; + + } + + // Noise reduction applied. + + if (enhancedIFD.fNoiseReductionApplied.IsValid ()) + { + + SetRawNoiseReductionApplied (); + + fNoiseReductionApplied = enhancedIFD.fNoiseReductionApplied; + + } + + // Noise profile. + + if (enhancedIFD.fNoiseProfile.IsValidForNegative (*this)) + { + + SetRawNoiseProfile (); + + fNoiseProfile = enhancedIFD.fNoiseProfile; + + } + + // Raw to full scale. + + if (fLinearizationInfo.Get ()) + { + + if (fLinearizationInfo->fActiveArea.W ()) + { + fRawToFullScaleH = (real64) fStage3Image->Bounds ().W () / + (real64) fLinearizationInfo->fActiveArea.W (); + } + + if (fLinearizationInfo->fActiveArea.H ()) + { + fRawToFullScaleV = (real64) fStage3Image->Bounds ().H () / + (real64) fLinearizationInfo->fActiveArea.H (); + } + + } + + if (!needRawImage) + { + + ClearLinearizationInfo (); + + ClearMosaicInfo (); + + fOpcodeList1.Clear (); + fOpcodeList2.Clear (); + fOpcodeList3.Clear (); + + fRawImageDigest .Clear (); + fNewRawImageDigest.Clear (); + + fRawBaselineSharpness .Clear (); + fRawNoiseReductionApplied.Clear (); + + fRawNoiseProfile = dng_noise_profile (); + + if (fRawDataUniqueID.IsValid ()) + { + fRawDataUniqueID = RawDataUniqueID (); + } + + fEnhanceParams.Clear (); + + } + + } + +/*****************************************************************************/ + +void dng_negative::SetStage1Image (AutoPtr &image) + { + + fStage1Image.Reset (image.Release ()); + + } + +/*****************************************************************************/ + +void dng_negative::SetStage2Image (AutoPtr &image) + { + + fStage2Image.Reset (image.Release ()); + + } + +/*****************************************************************************/ + +void dng_negative::SetStage3Image (AutoPtr &image) + { + + fStage3Image.Reset (image.Release ()); + + SetFloatingPoint (fStage3Image.Get () && + (fStage3Image->PixelType () == ttFloat)); + + } + +/*****************************************************************************/ + +void dng_negative::DoBuildStage2 (dng_host &host) + { + + dng_image &stage1 = *fStage1Image.Get (); + + dng_linearization_info &info = *fLinearizationInfo.Get (); + + uint32 pixelType = ttShort; + + if (stage1.PixelType () == ttLong || + stage1.PixelType () == ttFloat) + { + + pixelType = ttFloat; + + } + + fStage2Image.Reset (host.Make_dng_image (info.fActiveArea.Size (), + stage1.Planes (), + pixelType)); + + info.Linearize (host, + *this, + stage1, + *fStage2Image.Get ()); + + } + +/*****************************************************************************/ + +void dng_negative::DoPostOpcodeList2 (dng_host & /* host */) + { + + // Nothing by default. + + } + +/*****************************************************************************/ + +bool dng_negative::NeedDefloatStage2 (dng_host &host) + { + + if (fStage2Image->PixelType () == ttFloat) + { + + if (fRawImageStage >= rawImageStagePostOpcode2 && + host.SaveDNGVersion () != dngVersion_None && + host.SaveDNGVersion () < dngVersion_1_4_0_0) + { + + return true; + + } + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_negative::DefloatStage2 (dng_host & /* host */) + { + + ThrowNotYetImplemented ("dng_negative::DefloatStage2"); + + } + +/*****************************************************************************/ + +void dng_negative::BuildStage2Image (dng_host &host) + { + + // If reading the negative to save in DNG format, figure out + // when to grab a copy of the raw data. + + if (host.SaveDNGVersion () != dngVersion_None) + { + + // Transparency masks are only supported in DNG version 1.4 and + // later. In this case, the flattening of the transparency mask happens + // on the the stage3 image. + + if (TransparencyMask () && host.SaveDNGVersion () < dngVersion_1_4_0_0) + { + fRawImageStage = rawImageStagePostOpcode3; + } + + else if (fOpcodeList3.MinVersion (false) > host.SaveDNGVersion () || + fOpcodeList3.AlwaysApply ()) + { + fRawImageStage = rawImageStagePostOpcode3; + } + + // If we are not doing a full resolution read, then always save the DNG + // from the processed stage 3 image. + + else if (host.PreferredSize ()) + { + fRawImageStage = rawImageStagePostOpcode3; + } + + else if (host.SaveLinearDNG (*this)) + { + + // If the opcode list 3 has optional tags that are beyond the + // the minimum version, and we are saving a linear DNG anyway, + // then go ahead and apply them. + + if (fOpcodeList3.MinVersion (true) > host.SaveDNGVersion ()) + { + fRawImageStage = rawImageStagePostOpcode3; + } + + else + { + fRawImageStage = rawImageStagePreOpcode3; + } + + } + + else if (fOpcodeList2.MinVersion (false) > host.SaveDNGVersion () || + fOpcodeList2.AlwaysApply ()) + { + fRawImageStage = rawImageStagePostOpcode2; + } + + else if (fOpcodeList1.MinVersion (false) > host.SaveDNGVersion () || + fOpcodeList1.AlwaysApply ()) + { + fRawImageStage = rawImageStagePostOpcode1; + } + + else + { + fRawImageStage = rawImageStagePreOpcode1; + } + + // We should not save floating point stage1 images unless the target + // DNG version is high enough to understand floating point images. + // We handle this by converting from floating point to integer if + // required after building stage2 image. + + if (fStage1Image->PixelType () == ttFloat) + { + + if (fRawImageStage < rawImageStagePostOpcode2) + { + + if (host.SaveDNGVersion () < dngVersion_1_4_0_0) + { + + fRawImageStage = rawImageStagePostOpcode2; + + } + + } + + } + + // If the host is requesting a negative read for fast conversion to + // DNG, then check whether we can actually do a fast interpolation or + // not. For now, keep the logic simple. If the raw image stage is the + // pre-opcode stage 1 image (original), then proceed with trying a + // fast/downsampled interpolation when building the stage 3 image. + // Otherwise, turn off the attempted optimization. + + if (host.ForFastSaveToDNG () && + (fRawImageStage > rawImageStagePreOpcode1)) + { + + // Disable/revert the optimization attempt, and do a normal + // interpolation when building the stage 3 image. + + host.SetForFastSaveToDNG (false, 0); + + } + + } + + // Grab clone of raw image if required. + + if (fRawImageStage == rawImageStagePreOpcode1) + { + + fRawImage.Reset (fStage1Image->Clone ()); + + if (fTransparencyMask.Get ()) + { + fRawTransparencyMask.Reset (fTransparencyMask->Clone ()); + } + + if (fDepthMap.Get ()) + { + fRawDepthMap.Reset (fDepthMap->Clone ()); + } + + } + + else + { + + // If we are not keeping the most raw image, we need + // to recompute the raw image digest. + + ClearRawImageDigest (); + + // If we don't grab the unprocessed stage 1 image, then + // the raw JPEG image is no longer valid. + + ClearRawJPEGImage (); + + // Nor is the digest of the raw JPEG data. + + ClearRawJPEGImageDigest (); + + // We also don't know the raw floating point bit depth. + + SetRawFloatBitDepth (0); + + } + + // Process opcode list 1. + + host.ApplyOpcodeList (fOpcodeList1, *this, fStage1Image); + + // See if we are done with the opcode list 1. + + if (fRawImageStage > rawImageStagePreOpcode1) + { + + fOpcodeList1.Clear (); + + } + + // Grab clone of raw image if required. + + if (fRawImageStage == rawImageStagePostOpcode1) + { + + fRawImage.Reset (fStage1Image->Clone ()); + + if (fTransparencyMask.Get ()) + { + fRawTransparencyMask.Reset (fTransparencyMask->Clone ()); + } + + if (fDepthMap.Get ()) + { + fRawDepthMap.Reset (fDepthMap->Clone ()); + } + + } + + // Finalize linearization info. + + { + + NeedLinearizationInfo (); + + dng_linearization_info &info = *fLinearizationInfo.Get (); + + info.PostParse (host, *this); + + } + + // Perform the linearization. + + DoBuildStage2 (host); + + // Delete the stage1 image now that we have computed the stage 2 image. + + fStage1Image.Reset (); + + // Are we done with the linearization info. + + if (fRawImageStage > rawImageStagePostOpcode1) + { + + ClearLinearizationInfo (); + + } + + // Process opcode list 2. + + host.ApplyOpcodeList (fOpcodeList2, *this, fStage2Image); + + // See if we are done with the opcode list 2. + + if (fRawImageStage > rawImageStagePostOpcode1) + { + + fOpcodeList2.Clear (); + + } + + // Hook for any required processing just after opcode list 2. + + DoPostOpcodeList2 (host); + + // Convert from floating point to integer if required. + + if (NeedDefloatStage2 (host)) + { + + DefloatStage2 (host); + + } + + // Grab clone of raw image if required. + + if (fRawImageStage == rawImageStagePostOpcode2) + { + + fRawImage.Reset (fStage2Image->Clone ()); + + fRawImageBlackLevel = fStage3BlackLevel; + + if (fTransparencyMask.Get ()) + { + fRawTransparencyMask.Reset (fTransparencyMask->Clone ()); + } + + if (fDepthMap.Get ()) + { + fRawDepthMap.Reset (fDepthMap->Clone ()); + } + + } + + } + +/*****************************************************************************/ + +void dng_negative::DoInterpolateStage3 (dng_host &host, + int32 srcPlane, + dng_matrix *scaleTransforms) + { + + dng_image &stage2 = *fStage2Image.Get (); + + dng_mosaic_info &info = *fMosaicInfo.Get (); + + dng_point downScale; + + const bool fastSaveToDNG = host.ForFastSaveToDNG (); + + const uint32 fastSaveSize = host.FastSaveToDNGSize (); + + if (fastSaveToDNG && (fastSaveSize > 0)) + { + + downScale = info.DownScale (host.MinimumSize (), + host.FastSaveToDNGSize (), + host.CropFactor ()); + + } + + else + { + + downScale = info.DownScale (host.MinimumSize (), + host.PreferredSize (), + host.CropFactor ()); + + } + + if (downScale != dng_point (1, 1)) + { + SetIsPreview (true); + } + + dng_point dstSize = info.DstSize (downScale); + + fStage3Image.Reset (host.Make_dng_image (dng_rect (dstSize), + info.fColorPlanes, + stage2.PixelType ())); + + if (srcPlane < 0 || srcPlane >= (int32) stage2.Planes ()) + { + srcPlane = 0; + } + + info.Interpolate (host, + *this, + stage2, + *fStage3Image.Get (), + downScale, + srcPlane, + scaleTransforms); + + } + +/*****************************************************************************/ + +// Interpolate and merge a multi-channel CFA image. + +void dng_negative::DoMergeStage3 (dng_host &host, + dng_matrix *scaleTransforms) + { + + // The DNG SDK does not provide multi-channel CFA image merging code. + // It just grabs the first channel and uses that. + + DoInterpolateStage3 (host, 0, scaleTransforms); + + // Just grabbing the first channel would often result in the very + // bright image using the baseline exposure value. + + fStage3Gain = pow (2.0, BaselineExposure ()); + + } + +/*****************************************************************************/ + +void dng_negative::DoBuildStage3 (dng_host &host, + int32 srcPlane, + dng_matrix *scaleTransforms) + { + + // If we don't have a mosaic pattern, then just move the stage 2 + // image on to stage 3. + + dng_mosaic_info *info = fMosaicInfo.Get (); + + if (!info || !info->IsColorFilterArray ()) + { + + fStage3Image.Reset (fStage2Image.Release ()); + + } + + else + { + + // Remember the size of the stage 2 image. + + dng_point stage2_size = fStage2Image->Size (); + + // Special case multi-channel CFA interpolation. + + if ((fStage2Image->Planes () > 1) && (srcPlane < 0)) + { + + DoMergeStage3 (host, + scaleTransforms); + + } + + // Else do a single channel interpolation. + + else + { + + DoInterpolateStage3 (host, + srcPlane, + scaleTransforms); + + } + + // Calculate the ratio of the stage 3 image size to stage 2 image size. + + dng_point stage3_size = fStage3Image->Size (); + + fRawToFullScaleH = (real64) stage3_size.h / (real64) stage2_size.h; + fRawToFullScaleV = (real64) stage3_size.v / (real64) stage2_size.v; + + } + + } + +/*****************************************************************************/ + +void dng_negative::BuildStage3Image (dng_host &host, + int32 srcPlane) + { + + // Finalize the mosaic information. + + dng_mosaic_info *info = fMosaicInfo.Get (); + + if (info) + { + + info->PostParse (host, *this); + + } + + // Do the interpolation as required. + + DoBuildStage3 (host, srcPlane, NULL); + + // Delete the stage2 image now that we have computed the stage 3 image, + // unless the host wants to preserve it. + + if (!host.WantsPreserveStage2 ()) + { + + fStage2Image.Reset (); + + } + + // Are we done with the mosaic info? + + if (fRawImageStage >= rawImageStagePreOpcode3) + { + + // If we're preserving the stage 2 image, also preserve the mosaic + // info. + + if (!host.WantsPreserveStage2 ()) + { + + ClearMosaicInfo (); + + } + + // To support saving linear DNG files, to need to account for + // and upscaling during interpolation. + + if (fRawToFullScaleH > 1.0) + { + + uint32 adjust = Round_uint32 (fRawToFullScaleH); + + fDefaultCropSizeH .n *= adjust; + fDefaultCropOriginH.n *= adjust; + fDefaultScaleH .d *= adjust; + + fRawToFullScaleH /= (real64) adjust; + + } + + if (fRawToFullScaleV > 1.0) + { + + uint32 adjust = Round_uint32 (fRawToFullScaleV); + + fDefaultCropSizeV .n *= adjust; + fDefaultCropOriginV.n *= adjust; + fDefaultScaleV .d *= adjust; + + fRawToFullScaleV /= (real64) adjust; + + } + + } + + // Resample the transparency mask if required. + + ResizeTransparencyToMatchStage3 (host); + + // Grab clone of raw image if required. + + if (fRawImageStage == rawImageStagePreOpcode3) + { + + fRawImage.Reset (fStage3Image->Clone ()); + + fRawImageBlackLevel = fStage3BlackLevel; + + if (fTransparencyMask.Get ()) + { + fRawTransparencyMask.Reset (fTransparencyMask->Clone ()); + } + + if (fDepthMap.Get ()) + { + fRawDepthMap.Reset (fDepthMap->Clone ()); + } + + } + + // Process opcode list 3. + + host.ApplyOpcodeList (fOpcodeList3, *this, fStage3Image); + + // See if we are done with the opcode list 3. + + if (fRawImageStage > rawImageStagePreOpcode3) + { + + // Currently re-use the same flag for preserving the opcode list. + + if (!host.WantsPreserveStage2 ()) + { + + fOpcodeList3.Clear (); + + } + + } + + // Just in case the opcode list 3 changed the image size, resample the + // transparency mask again if required. This is nearly always going + // to be a fast NOP operation. + + ResizeTransparencyToMatchStage3 (host); + + // Depth maps are often lower resolution than the main image, + // so make sure we upsample if required. + + ResizeDepthToMatchStage3 (host); + + // Update Floating Point flag. + + SetFloatingPoint (fStage3Image->PixelType () == ttFloat); + + // Don't need to grab a copy of raw data at this stage since + // it is kept around as the stage 3 image. + + } + +/******************************************************************************/ + +// RESEARCH: Instead of using a constant slope, consider using a family +// of slopes ranging from the original one (1/16) to a limit of 1/128, +// depending on the histogram distribution. + +static const real64 kSceneProxyCurveSlope = 1.0 / 128.0; + +static inline real64 SceneProxyCurve (real64 x) + { + + // The following code evaluates the inverse of: + // + // f (x) = (s * x) + ((1 - s) * x^3) + // + // where s is the slope of the function at the origin (x==0). + + static const real64 s = kSceneProxyCurveSlope; + + static const real64 k0 = pow (2.0, 1.0 / 3.0); + + static const real64 k1 = 108.0 * s * s * s * (1.0 - s) * (1.0 - s) * (1.0 - s); + + real64 k2 = (27.0 * x) - (54.0 * s * x) + (27.0 * x * s * s); + + real64 k3 = pow (k2 + sqrt (k1 + k2 * k2), 1.0 / 3.0); + + real64 y = (k3 / (3.0 * k0 * (1.0 - s))) - (k0 * s / k3); + + y = Pin_real64 (0.0, y, 1.0); + + DNG_ASSERT (Abs_real64 (x - (kSceneProxyCurveSlope * y + + (1.0 - kSceneProxyCurveSlope) * y * y * y)) < 0.0000001, + "SceneProxyCurve round trip error"); + + return y; + + } + +/*****************************************************************************/ + +static const real64 kOutputProxyCurveSlope = 1.0 / 16.0; + +static inline real64 OutputProxyCurve (real64 x) + { + + DNG_ASSERT (kOutputProxyCurveSlope == 1.0 / 16.0, + "OutputProxyCurve unexpected slope"); + + real64 y = (sqrt (960.0 * x + 1.0) - 1.0) / 30.0; + + DNG_ASSERT (Abs_real64 (x - (kOutputProxyCurveSlope * y + + (1.0 - kOutputProxyCurveSlope) * y * y)) < 0.0000001, + "OutputProxyCurve round trip error"); + + return y; + + } + +/*****************************************************************************/ + +class dng_gamma_encode_proxy : public dng_1d_function + { + + private: + + real64 fLower; + real64 fUpper; + + bool fIsSceneReferred; + + real64 fStage3BlackLevel; + + real64 fBlackLevel; + + public: + + dng_gamma_encode_proxy (real64 lower, + real64 upper, + bool isSceneReferred, + real64 stage3BlackLevel, + real64 blackLevel) + + : fLower (lower) + , fUpper (upper) + , fIsSceneReferred (isSceneReferred) + , fStage3BlackLevel (stage3BlackLevel) + , fBlackLevel (blackLevel / 255.0) + + { + + } + + virtual real64 Evaluate (real64 x) const + { + + real64 y; + + if (fIsSceneReferred) + { + + if (fLower < fStage3BlackLevel) + { + + x = Pin_real64 (-1.0, + (x - fStage3BlackLevel) / (fUpper - fStage3BlackLevel), + 1.0); + + if (x >= 0.0) + { + + y = SceneProxyCurve (x); + + } + + else + { + + y = -SceneProxyCurve (-x); + + } + + y = Pin_real64 (0.0, y * (1.0 - fBlackLevel) + fBlackLevel, 1.0); + + } + + else + { + + x = Pin_real64 (0.0, (x - fLower) / (fUpper - fLower), 1.0); + + y = SceneProxyCurve (x); + + } + + } + + else + { + + x = Pin_real64 (0.0, (x - fLower) / (fUpper - fLower), 1.0); + + y = OutputProxyCurve (x); + + } + + return y; + + } + + }; + +/*****************************************************************************/ + +class dng_encode_proxy_task: public dng_area_task, + private dng_uncopyable + { + + private: + + const dng_image &fSrcImage; + + dng_image &fDstImage; + + AutoPtr fTable16 [kMaxColorPlanes]; + + public: + + dng_encode_proxy_task (dng_host &host, + const dng_image &srcImage, + dng_image &dstImage, + const real64 *lower, + const real64 *upper, + bool isSceneReferred, + real64 stage3BlackLevel, + real64 *blackLevel); + + virtual dng_rect RepeatingTile1 () const + { + return fSrcImage.RepeatingTile (); + } + + virtual dng_rect RepeatingTile2 () const + { + return fDstImage.RepeatingTile (); + } + + virtual void Process (uint32 threadIndex, + const dng_rect &tile, + dng_abort_sniffer *sniffer); + + }; + +/*****************************************************************************/ + +dng_encode_proxy_task::dng_encode_proxy_task (dng_host &host, + const dng_image &srcImage, + dng_image &dstImage, + const real64 *lower, + const real64 *upper, + bool isSceneReferred, + real64 stage3BlackLevel, + real64 *blackLevel) + + : dng_area_task ("dng_encode_proxy_task") + + , fSrcImage (srcImage) + , fDstImage (dstImage) + + { + + for (uint32 plane = 0; plane < fSrcImage.Planes (); plane++) + { + + fTable16 [plane] . Reset (host.Allocate (0x10000 * sizeof (uint16))); + + dng_gamma_encode_proxy gamma (lower [plane], + upper [plane], + isSceneReferred, + stage3BlackLevel, + blackLevel [plane]); + + // Compute fast approximation of encoding table. + + dng_1d_table table32; + + table32.Initialize (host.Allocator (), gamma); + + table32.Expand16 (fTable16 [plane]->Buffer_uint16 ()); + + // The gamma curve has some fairly high curvature near + // the black point, and the above approximation can actually + // change results. So use exact math near the black point. + // Still very fast, since we are only computing a small + // faction of the range exactly. + + { + + const int32 kHighResRadius = 1024; + + uint32 zeroPt = Round_uint32 (stage3BlackLevel * 65535.0); + + uint32 highResLower = Max_int32 (0 , zeroPt - kHighResRadius); + uint32 highResUpper = Min_int32 (0x10000, zeroPt + kHighResRadius); + + for (uint32 j = highResLower; j < highResUpper; j++) + { + + real64 x = j * (1.0 / 65535.0); + + real64 y = gamma.Evaluate (x); + + uint16 z = Pin_uint16 (Round_int32 (y * 65535.0)); + + fTable16 [plane]->Buffer_uint16 () [j] = z; + + } + + } + + } + + } + +/*****************************************************************************/ + +void dng_encode_proxy_task::Process (uint32 /* threadIndex */, + const dng_rect &tile, + dng_abort_sniffer * /* sniffer */) + { + + dng_const_tile_buffer srcBuffer (fSrcImage, tile); + dng_dirty_tile_buffer dstBuffer (fDstImage, tile); + + int32 sColStep = srcBuffer.fColStep; + int32 dColStep = dstBuffer.fColStep; + + const uint16 *noise = dng_dither::Get ().NoiseBuffer16 (); + + for (uint32 plane = 0; plane < fSrcImage.Planes (); plane++) + { + + const uint16 *map = fTable16 [plane]->Buffer_uint16 (); + + for (int32 row = tile.t; row < tile.b; row++) + { + + const uint16 *sPtr = srcBuffer.ConstPixel_uint16 (row, tile.l, plane); + + uint8 *dPtr = dstBuffer.DirtyPixel_uint8 (row, tile.l, plane); + + const uint16 *rPtr = &noise [(row & dng_dither::kRNGMask) * dng_dither::kRNGSize]; + + for (int32 col = tile.l; col < tile.r; col++) + { + + uint32 x = *sPtr; + + uint32 r = rPtr [col & dng_dither::kRNGMask]; + + x = map [x]; + + x = (((x << 8) - x) + r) >> 16; + + *dPtr = (uint8) x; + + sPtr += sColStep; + dPtr += dColStep; + + } + + } + + } + + } + +/******************************************************************************/ + +bool dng_negative::SupportsPreservedBlackLevels (dng_host & /* host */) + { + + return false; + + } + +/******************************************************************************/ + +dng_image * dng_negative::EncodeRawProxy (dng_host &host, + const dng_image &srcImage, + dng_opcode_list &opcodeList, + real64 *blackLevel) const + { + + if (srcImage.PixelType () != ttShort) + { + return NULL; + } + + real64 lower [kMaxColorPlanes]; + real64 upper [kMaxColorPlanes]; + + { + + const real64 kClipFraction = 0.00001; + + uint64 pixels = (uint64) srcImage.Bounds ().H () * + (uint64) srcImage.Bounds ().W (); + + uint32 limit = Round_int32 ((real64) pixels * kClipFraction); + + AutoPtr histData (host.Allocate (65536 * sizeof (uint32))); + + uint32 *hist = histData->Buffer_uint32 (); + + for (uint32 plane = 0; plane < srcImage.Planes (); plane++) + { + + HistogramArea (host, + srcImage, + srcImage.Bounds (), + hist, + 65535, + plane); + + uint32 total = 0; + + uint32 upperIndex = 65535; + + while (total + hist [upperIndex] <= limit && upperIndex > 255) + { + + total += hist [upperIndex]; + + upperIndex--; + + } + + total = 0; + + uint32 lowerIndex = 0; + + while (total + hist [lowerIndex] <= limit && lowerIndex < upperIndex - 255) + { + + total += hist [lowerIndex]; + + lowerIndex++; + + } + + lower [plane] = lowerIndex / 65535.0; + upper [plane] = upperIndex / 65535.0; + + } + + } + + bool isSceneReferred = (ColorimetricReference () == crSceneReferred); + + real64 stage3BlackLevel = Stage3BlackLevelNormalized (); + + for (uint32 n = 0; n < kMaxSamplesPerPixel; n++) + { + blackLevel [n] = 0.0; + } + + if (isSceneReferred && stage3BlackLevel > 0.0) + { + + for (uint32 plane = 0; plane < srcImage.Planes (); plane++) + { + + if (lower [plane] < stage3BlackLevel) + { + + upper [plane] = Max_real64 (upper [plane], + stage3BlackLevel + + (stage3BlackLevel - lower [plane]) * + (1.0 / kMaxStage3BlackLevelNormalized - 1.0)); + + upper [plane] = Min_real64 (upper [plane], 1.0); + + real64 negRange = SceneProxyCurve ((stage3BlackLevel - lower [plane]) / + (upper [plane] - stage3BlackLevel)); + + real64 outBlack = negRange / (1.0 + negRange); + + blackLevel [plane] = Min_real64 (kMaxStage3BlackLevelNormalized * 255.0, + ceil (outBlack * 255.0)); + + } + + } + + } + + // Apply the gamma encoding, using dither when downsampling to 8-bit. + + AutoPtr dstImage (host.Make_dng_image (srcImage.Bounds (), + srcImage.Planes (), + ttByte)); + + { + + dng_encode_proxy_task task (host, + srcImage, + *dstImage, + lower, + upper, + isSceneReferred, + stage3BlackLevel, + blackLevel); + + host.PerformAreaTask (task, + srcImage.Bounds ()); + + } + + // Add opcodes to undo the gamma encoding. + + { + + for (uint32 plane = 0; plane < srcImage.Planes (); plane++) + { + + dng_area_spec areaSpec (srcImage.Bounds (), + plane); + + real64 coefficient [4]; + + coefficient [0] = 0.0; + + if (isSceneReferred) + { + coefficient [1] = kSceneProxyCurveSlope; + coefficient [2] = 0.0; + coefficient [3] = 1.0 - coefficient [1]; + } + else + { + coefficient [1] = kOutputProxyCurveSlope; + coefficient [2] = 1.0 - coefficient [1]; + coefficient [3] = 0.0; + } + + if (lower [plane] < stage3BlackLevel) + { + + real64 rescale = (upper [plane] - stage3BlackLevel) / (1.0 - stage3BlackLevel); + + coefficient [0] *= rescale; + coefficient [1] *= rescale; + coefficient [2] *= rescale; + coefficient [3] *= rescale; + + } + + else + { + + real64 rescale = (upper [plane] - lower [plane]) / (1.0 - stage3BlackLevel); + + coefficient [0] *= rescale; + coefficient [1] *= rescale; + coefficient [2] *= rescale; + coefficient [3] *= rescale; + + coefficient [0] += (lower [plane] - stage3BlackLevel) / (1.0 - stage3BlackLevel); + + } + + AutoPtr opcode (new dng_opcode_MapPolynomial (areaSpec, + isSceneReferred ? 3 : 2, + coefficient)); + + opcodeList.Append (opcode); + + } + + } + + return dstImage.Release (); + + } + +/******************************************************************************/ + +void dng_negative::AdjustProfileForStage3 () + { + + // For dng_sdk, the stage3 image's color space is always the same as the + // raw image's color space. + + } + +/******************************************************************************/ + +void dng_negative::ConvertToProxy (dng_host &host, + dng_image_writer &writer, + uint32 proxySize, + uint64 proxyCount) + { + + if (!proxySize) + { + proxySize = kMaxImageSide; + } + + if (!proxyCount) + { + proxyCount = (uint64) proxySize * proxySize; + } + + // Don't need to keep private data around in non-full size proxies. + + if (proxySize < kMaxImageSide || + proxyCount < kMaxImageSide * kMaxImageSide) + { + + ClearMakerNote (); + + ClearPrivateData (); + + } + + // See if we already have an acceptable proxy image. + + if (fRawImage.Get () && + fRawImage->PixelType () == ttByte && + fRawImage->Bounds () == DefaultCropArea () && + fRawImage->Bounds ().H () <= proxySize && + fRawImage->Bounds ().W () <= proxySize && + (uint64) fRawImage->Bounds ().H () * + (uint64) fRawImage->Bounds ().W () <= proxyCount && + fRawToFullScaleH == 1.0 && + fRawToFullScaleV == 1.0 && + (!GetMosaicInfo () || !GetMosaicInfo ()->IsColorFilterArray ()) && + fRawJPEGImage.Get () && + (!RawTransparencyMask () || RawTransparencyMask ()->PixelType () == ttByte)) + { + + return; + + } + + if (fRawImage.Get () && + fRawImage->PixelType () == ttFloat && + fRawImage->Bounds ().H () <= proxySize && + fRawImage->Bounds ().W () <= proxySize && + (uint64) fRawImage->Bounds ().H () * + (uint64) fRawImage->Bounds ().W () <= proxyCount && + fRawToFullScaleH == 1.0 && + fRawToFullScaleV == 1.0 && + RawFloatBitDepth () == 16 && + (!RawTransparencyMask () || RawTransparencyMask ()->PixelType () == ttByte)) + { + + return; + + } + + // Clear any grabbed raw image, since we are going to start + // building the proxy with the stage3 image. + + fRawImage.Reset (); + + fRawImageBlackLevel = 0; + + ClearRawJPEGImage (); + + SetRawFloatBitDepth (0); + + ClearLinearizationInfo (); + + ClearMosaicInfo (); + + fOpcodeList1.Clear (); + fOpcodeList2.Clear (); + fOpcodeList3.Clear (); + + // Adjust the profile to match the stage 3 image, if required. + + AdjustProfileForStage3 (); + + // Not saving the raw-most image, do the old raw digest is no + // longer valid. + + ClearRawImageDigest (); + + ClearRawJPEGImageDigest (); + + // Trim off extra pixels outside the default crop area. + + dng_rect defaultCropArea = DefaultCropArea (); + + if (Stage3Image ()->Bounds () != defaultCropArea) + { + + fStage3Image->Trim (defaultCropArea); + + if (fTransparencyMask.Get ()) + { + fTransparencyMask->Trim (defaultCropArea); + } + + if (fDepthMap.Get ()) + { + fDepthMap->Trim (defaultCropArea); + fRawDepthMap.Reset (); + } + + fDefaultCropOriginH = dng_urational (0, 1); + fDefaultCropOriginV = dng_urational (0, 1); + + } + + // Figure out the requested proxy pixel size. + + real64 aspectRatio = AspectRatio (); + + dng_point newSize (proxySize, proxySize); + + if (aspectRatio >= 1.0) + { + newSize.v = Max_int32 (1, Round_int32 (proxySize / aspectRatio)); + } + else + { + newSize.h = Max_int32 (1, Round_int32 (proxySize * aspectRatio)); + } + + newSize.v = Min_int32 (newSize.v, DefaultFinalHeight ()); + newSize.h = Min_int32 (newSize.h, DefaultFinalWidth ()); + + if ((uint64) newSize.v * + (uint64) newSize.h > proxyCount) + { + + if (aspectRatio >= 1.0) + { + + newSize.h = (uint32) sqrt (proxyCount * aspectRatio); + + newSize.v = Max_int32 (1, Round_int32 (newSize.h / aspectRatio)); + + } + + else + { + + newSize.v = (uint32) sqrt (proxyCount / aspectRatio); + + newSize.h = Max_int32 (1, Round_int32 (newSize.v * aspectRatio)); + + } + + } + + // If this is fewer pixels, downsample the stage 3 image to that size. + + dng_point oldSize = defaultCropArea.Size (); + + real64 pixelAspect = PixelAspectRatio (); + + if ((uint64) newSize.v * (uint64) newSize.h < + (uint64) oldSize.v * (uint64) oldSize.h || + pixelAspect < 0.99 || + pixelAspect > 1.01) + { + + const dng_image &srcImage (*Stage3Image ()); + + AutoPtr dstImage (host.Make_dng_image (newSize, + srcImage.Planes (), + srcImage.PixelType ())); + + host.ResampleImage (srcImage, + *dstImage); + + fStage3Image.Reset (dstImage.Release ()); + + fDefaultCropSizeH = dng_urational (newSize.h, 1); + fDefaultCropSizeV = dng_urational (newSize.v, 1); + + fDefaultScaleH = dng_urational (1, 1); + fDefaultScaleV = dng_urational (1, 1); + + fBestQualityScale = dng_urational (1, 1); + + fRawToFullScaleH = 1.0; + fRawToFullScaleV = 1.0; + + } + + // If there is still a raw to full scale factor, we need to + // remove it and adjust the crop coordinates. + + else if (fRawToFullScaleH != 1.0 || + fRawToFullScaleV != 1.0) + { + + fDefaultCropSizeH = dng_urational (oldSize.h, 1); + fDefaultCropSizeV = dng_urational (oldSize.v, 1); + + fDefaultScaleH = dng_urational (1, 1); + fDefaultScaleV = dng_urational (1, 1); + + fBestQualityScale = dng_urational (1, 1); + + fRawToFullScaleH = 1.0; + fRawToFullScaleV = 1.0; + + } + + // Convert 32-bit floating point images to 16-bit floating point to + // save space. + + if (Stage3Image ()->PixelType () == ttFloat) + { + + fRawImage.Reset (host.Make_dng_image (Stage3Image ()->Bounds (), + Stage3Image ()->Planes (), + ttFloat)); + + fRawImageBlackLevel = 0; + + LimitFloatBitDepth (host, + *Stage3Image (), + *fRawImage, + 16, + 32768.0f); + + SetRawFloatBitDepth (16); + + SetWhiteLevel (32768); + + } + + else + { + + // Convert 16-bit deep images to 8-bit deep image for saving. + + real64 blackLevel [kMaxSamplesPerPixel]; + + fRawImage.Reset (EncodeRawProxy (host, + *Stage3Image (), + fOpcodeList2, + blackLevel)); + + fRawImageBlackLevel = 0; + + if (fRawImage.Get ()) + { + + SetWhiteLevel (255); + + for (uint32 plane = 0; plane < fRawImage->Planes (); plane++) + { + SetBlackLevel (blackLevel [plane], plane); + } + + // Compute JPEG compressed version. + + if (fRawImage->PixelType () == ttByte && + host.SaveDNGVersion () >= dngVersion_1_4_0_0) + { + + AutoPtr jpegImage (new dng_jpeg_image); + + jpegImage->Encode (host, + *this, + writer, + *fRawImage); + + SetRawJPEGImage (jpegImage); + + } + + } + + } + + // Deal with transparency mask. + + if (TransparencyMask ()) + { + + const bool convertTo8Bit = true; + + ResizeTransparencyToMatchStage3 (host, convertTo8Bit); + + fRawTransparencyMask.Reset (fTransparencyMask->Clone ()); + + } + + // Deal with depth map. + + if (DepthMap ()) + { + + ResizeDepthToMatchStage3 (host); + + if (fRawDepthMap.Get ()) + { + + if (fRawDepthMap->Bounds ().W () > fDepthMap->Bounds ().W () || + fRawDepthMap->Bounds ().H () > fDepthMap->Bounds ().H ()) + { + fRawDepthMap.Reset (); + } + + } + + } + + // Recompute the raw data unique ID, since we changed the image data. + + RecomputeRawDataUniqueID (host); + + } + +/*****************************************************************************/ + +bool dng_negative::IsProxy () const + { + + return (DefaultCropSizeH () != OriginalDefaultCropSizeH ()) && + (DefaultCropSizeV () != OriginalDefaultCropSizeV ()); + + } + +/*****************************************************************************/ + +dng_linearization_info * dng_negative::MakeLinearizationInfo () + { + + dng_linearization_info *info = new dng_linearization_info (); + + if (!info) + { + ThrowMemoryFull (); + } + + return info; + + } + +/*****************************************************************************/ + +void dng_negative::NeedLinearizationInfo () + { + + if (!fLinearizationInfo.Get ()) + { + + fLinearizationInfo.Reset (MakeLinearizationInfo ()); + + } + + } + +/*****************************************************************************/ + +dng_mosaic_info * dng_negative::MakeMosaicInfo () + { + + dng_mosaic_info *info = new dng_mosaic_info (); + + if (!info) + { + ThrowMemoryFull (); + } + + return info; + + } + +/*****************************************************************************/ + +void dng_negative::NeedMosaicInfo () + { + + if (!fMosaicInfo.Get ()) + { + + fMosaicInfo.Reset (MakeMosaicInfo ()); + + } + + } + +/*****************************************************************************/ + +void dng_negative::SetTransparencyMask (AutoPtr &image, + uint32 bitDepth) + { + + fTransparencyMask.Reset (image.Release ()); + + fRawTransparencyMaskBitDepth = bitDepth; + + } + +/*****************************************************************************/ + +const dng_image * dng_negative::TransparencyMask () const + { + + return fTransparencyMask.Get (); + + } + +/*****************************************************************************/ + +const dng_image * dng_negative::RawTransparencyMask () const + { + + if (fRawTransparencyMask.Get ()) + { + + return fRawTransparencyMask.Get (); + + } + + return TransparencyMask (); + + } + +/*****************************************************************************/ + +uint32 dng_negative::RawTransparencyMaskBitDepth () const + { + + if (fRawTransparencyMaskBitDepth) + { + + return fRawTransparencyMaskBitDepth; + + } + + const dng_image *mask = RawTransparencyMask (); + + if (mask) + { + + switch (mask->PixelType ()) + { + + case ttByte: + return 8; + + case ttShort: + return 16; + + case ttFloat: + return 32; + + default: + ThrowProgramError (); + + } + + } + + return 0; + + } + +/*****************************************************************************/ + +void dng_negative::ReadTransparencyMask (dng_host &host, + dng_stream &stream, + dng_info &info) + { + + if (info.fMaskIndex != -1) + { + + // Allocate image we are reading. + + dng_ifd &maskIFD = *info.fIFD [info.fMaskIndex]; + + fTransparencyMask.Reset (host.Make_dng_image (maskIFD.Bounds (), + 1, + maskIFD.PixelType ())); + + // Read the image. + + maskIFD.ReadImage (host, + stream, + *fTransparencyMask.Get ()); + + // Remember the pixel depth. + + fRawTransparencyMaskBitDepth = maskIFD.fBitsPerSample [0]; + + } + + } + +/*****************************************************************************/ + +void dng_negative::ResizeTransparencyToMatchStage3 (dng_host &host, + bool convertTo8Bit) + { + + if (TransparencyMask ()) + { + + if ((TransparencyMask ()->Bounds () != fStage3Image->Bounds ()) || + (TransparencyMask ()->PixelType () != ttByte && convertTo8Bit)) + { + + AutoPtr newMask (host.Make_dng_image (fStage3Image->Bounds (), + 1, + convertTo8Bit ? + ttByte : + TransparencyMask ()->PixelType ())); + + host.ResampleImage (*TransparencyMask (), + *newMask); + + fTransparencyMask.Reset (newMask.Release ()); + + if (!fRawTransparencyMask.Get ()) + { + fRawTransparencyMaskBitDepth = 0; + } + + else if (convertTo8Bit) + { + fRawTransparencyMaskBitDepth = 8; + } + + } + + } + + } + +/*****************************************************************************/ + +bool dng_negative::NeedFlattenTransparency (dng_host & /* host */) + { + + return false; + + } + +/*****************************************************************************/ + +void dng_negative::FlattenTransparency (dng_host & /* host */) + { + + ThrowNotYetImplemented (); + + } + +/*****************************************************************************/ + +const dng_image * dng_negative::UnflattenedStage3Image () const + { + + if (fUnflattenedStage3Image.Get ()) + { + + return fUnflattenedStage3Image.Get (); + + } + + return fStage3Image.Get (); + + } + +/*****************************************************************************/ + +void dng_negative::SetDepthMap (AutoPtr &depthMap) + { + + fDepthMap.Reset (depthMap.Release ()); + + SetHasDepthMap (fDepthMap.Get () != NULL); + + } + +/*****************************************************************************/ + +void dng_negative::ReadDepthMap (dng_host &host, + dng_stream &stream, + dng_info &info) + { + + if (info.fDepthIndex != -1) + { + + // Allocate image we are reading. + + dng_ifd &depthIFD = *info.fIFD [info.fDepthIndex]; + + fDepthMap.Reset (host.Make_dng_image (depthIFD.Bounds (), + 1, + depthIFD.PixelType ())); + + // Read the image. + + depthIFD.ReadImage (host, + stream, + *fDepthMap.Get ()); + + SetHasDepthMap (fDepthMap.Get () != NULL); + + } + + } + +/*****************************************************************************/ + +void dng_negative::ResizeDepthToMatchStage3 (dng_host &host) + { + + if (DepthMap ()) + { + + if (DepthMap ()->Bounds () != fStage3Image->Bounds ()) + { + + // If we are upsampling, and have not grabbed the raw depth map + // yet, do so now. + + if (!fRawDepthMap.Get ()) + { + + uint64 imagePixels = fStage3Image->Bounds ().H () * (uint64) + fStage3Image->Bounds ().W (); + + uint64 depthPixels = DepthMap ()->Bounds ().H () * (uint64) + DepthMap ()->Bounds ().W (); + + if (depthPixels < imagePixels) + { + fRawDepthMap.Reset (fDepthMap->Clone ()); + } + + } + + AutoPtr newMap (host.Make_dng_image (fStage3Image->Bounds (), + 1, + DepthMap ()->PixelType ())); + + host.ResampleImage (*DepthMap (), + *newMap); + + fDepthMap.Reset (newMap.Release ()); + + } + + } + + } + +/*****************************************************************************/ diff --git a/dng/dng_negative.h b/dng/dng_negative.h new file mode 100644 index 0000000..98b558f --- /dev/null +++ b/dng/dng_negative.h @@ -0,0 +1,2713 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Functions and classes for working with a digital negative (image data and + * corresponding metadata). + */ + +/*****************************************************************************/ + +#ifndef __dng_negative__ +#define __dng_negative__ + +/*****************************************************************************/ + +#include "dng_1d_function.h" +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_fingerprint.h" +#include "dng_image.h" +#include "dng_linearization_info.h" +#include "dng_matrix.h" +#include "dng_mosaic_info.h" +#include "dng_mutex.h" +#include "dng_opcode_list.h" +#include "dng_orientation.h" +#include "dng_rational.h" +#include "dng_sdk_limits.h" +#include "dng_string.h" +#include "dng_tag_types.h" +#include "dng_tag_values.h" +#include "dng_types.h" +#include "dng_utils.h" +#include "dng_xy_coord.h" + +#include + +/*****************************************************************************/ + +// To prevent using the internal metadata when we meant to use override +// metadata, the following definitions allow us to only allow access to +// the internal metadata on non-const negatives. This allows the old API +// to keep working essentially unchanged provided one does not use const +// negatives, but will prevent access to the embedded data on const +// negatives. + +#if 1 + +#define qMetadataOnConst 0 +#define METACONST + +#else + +#define qMetadataOnConst 1 +#define METACONST const + +#endif + +/*****************************************************************************/ + +/// \brief Noise model for photon and sensor read noise, assuming that they are +/// independent random variables and spatially invariant. +/// +/// The noise model is N (x) = sqrt (scale*x + offset), where x represents a linear +/// signal value in the range [0,1], and N (x) is the standard deviation (i.e., +/// noise). The parameters scale and offset are both sensor-dependent and +/// ISO-dependent. scale must be positive, and offset must be non-negative. + +class dng_noise_function: public dng_1d_function + { + + protected: + + real64 fScale; + real64 fOffset; + + public: + + /// Create empty and invalid noise function. + + dng_noise_function () + + : fScale (0.0) + , fOffset (0.0) + + { + + } + + /// Create noise function with the specified scale and offset. + + dng_noise_function (real64 scale, + real64 offset) + + : fScale (scale) + , fOffset (offset) + + { + + } + + /// Compute noise (standard deviation) at the specified average signal level + /// x. + + virtual real64 Evaluate (real64 x) const + { + return sqrt (fScale * x + fOffset); + } + + /// The scale (slope, gain) of the noise function. + + real64 Scale () const + { + return fScale; + } + + /// The offset (square of the noise floor) of the noise function. + + real64 Offset () const + { + return fOffset; + } + + /// Set the scale (slope, gain) of the noise function. + + void SetScale (real64 scale) + { + fScale = scale; + } + + /// Set the offset (square of the noise floor) of the noise function. + + void SetOffset (real64 offset) + { + fOffset = offset; + } + + /// Is the noise function valid? + + bool IsValid () const + { + return (fScale > 0.0 && fOffset >= 0.0); + } + + }; + +/*****************************************************************************/ + +/// \brief Noise profile for a negative. +/// +/// For mosaiced negatives, the noise profile describes the approximate noise +/// characteristics of a mosaic negative after linearization, but prior to +/// demosaicing. For demosaiced negatives (i.e., linear DNGs), the noise profile +/// describes the approximate noise characteristics of the image data immediately +/// following the demosaic step, prior to the processing of opcode list 3. +/// +/// A noise profile may contain 1 or N noise functions, where N is the number of +/// color planes for the negative. Otherwise the noise profile is considered to be +/// invalid for that negative. If the noise profile contains 1 noise function, then +/// it is assumed that this single noise function applies to all color planes of the +/// negative. Otherwise, the N noise functions map to the N planes of the negative in +/// order specified in the CFAPlaneColor tag. + +class dng_noise_profile + { + + protected: + + dng_std_vector fNoiseFunctions; + + public: + + /// Create empty (invalid) noise profile. + + dng_noise_profile (); + + /// Create noise profile with the specified noise functions (1 per plane). + + explicit dng_noise_profile (const dng_std_vector &functions); + + /// Is the noise profile valid? + + bool IsValid () const; + + /// Is the noise profile valid for the specified negative? + + bool IsValidForNegative (const dng_negative &negative) const; + + /// The noise function for the specified plane. + + const dng_noise_function & NoiseFunction (uint32 plane) const; + + /// The number of noise functions in this profile. + + uint32 NumFunctions () const; + + /// Equality test. + + bool operator== (const dng_noise_profile &profile) const; + + bool operator!= (const dng_noise_profile &profile) const + { + return !(*this == profile); + } + + }; + +/*****************************************************************************/ + +/// \brief Main class for holding metadata. + +class dng_metadata + { + + private: + + // Base orientation of both the thumbnail and raw data. This is + // generally based on the EXIF values. + + bool fHasBaseOrientation; + + dng_orientation fBaseOrientation; + + // Is the maker note safe to copy from file to file? Defaults to false + // because many maker notes are not safe. + + bool fIsMakerNoteSafe; + + // MakerNote binary data block. + + AutoPtr fMakerNote; + + // EXIF data. + + AutoPtr fExif; + + // A copy of the EXIF data before is was synchronized with other metadata sources. + + AutoPtr fOriginalExif; + + // IPTC binary data block and offset in original file. + + AutoPtr fIPTCBlock; + + uint64 fIPTCOffset; + + // XMP data. + + AutoPtr fXMP; + + // If there a valid embedded XMP block, has is its digest? NULL if no valid + // embedded XMP. + + dng_fingerprint fEmbeddedXMPDigest; + + // Is the XMP data from a sidecar file? + + bool fXMPinSidecar; + + // If the XMP data is from a sidecar file, is the sidecar file newer + // than the raw file? + + bool fXMPisNewer; + + // Source file mimi-type, if known. + + dng_string fSourceMIME; + + public: + + dng_metadata (dng_host &host); + + dng_metadata (const dng_metadata &rhs, + dng_memory_allocator &allocator); + + virtual ~dng_metadata (); + + /// Copy this metadata. + + virtual dng_metadata * Clone (dng_memory_allocator &allocator) const; + + /// Setter for BaseOrientation. + + void SetBaseOrientation (const dng_orientation &orientation); + + /// Has BaseOrientation been set? + + bool HasBaseOrientation () const + { + return fHasBaseOrientation; + } + + /// Getter for BaseOrientation. + + const dng_orientation & BaseOrientation () const + { + return fBaseOrientation; + } + + /// Logically rotates the image by changing the orientation values. + /// This will also update the XMP data. + + void ApplyOrientation (const dng_orientation &orientation); + + // API for IPTC metadata: + + void SetIPTC (AutoPtr &block, + uint64 offset); + + void SetIPTC (AutoPtr &block); + + void ClearIPTC (); + + const void * IPTCData () const; + + uint32 IPTCLength () const; + + uint64 IPTCOffset () const; + + dng_fingerprint IPTCDigest (bool includePadding = true) const; + + void RebuildIPTC (dng_memory_allocator &allocator, + bool padForTIFF); + + // API for MakerNote data: + + void SetMakerNoteSafety (bool safe) + { + fIsMakerNoteSafe = safe; + } + + bool IsMakerNoteSafe () const + { + return fIsMakerNoteSafe; + } + + void SetMakerNote (AutoPtr &block) + { + fMakerNote.Reset (block.Release ()); + } + + void ClearMakerNote () + { + fIsMakerNoteSafe = false; + fMakerNote.Reset (); + } + + const void * MakerNoteData () const + { + return fMakerNote.Get () ? fMakerNote->Buffer () + : NULL; + } + + uint32 MakerNoteLength () const + { + return fMakerNote.Get () ? fMakerNote->LogicalSize () + : 0; + } + + // API for EXIF metadata: + + dng_exif * GetExif () + { + return fExif.Get (); + } + + const dng_exif * GetExif () const + { + return fExif.Get (); + } + + template< class E > + E & Exif (); + + template< class E > + const E & Exif () const; + + void ResetExif (dng_exif * newExif); + + dng_memory_block * BuildExifBlock (dng_memory_allocator &allocator, + const dng_resolution *resolution = NULL, + bool includeIPTC = false, + const dng_jpeg_preview *thumbnail = NULL) const; + + // API for original EXIF metadata. + + dng_exif * GetOriginalExif () + { + return fOriginalExif.Get (); + } + + const dng_exif * GetOriginalExif () const + { + return fOriginalExif.Get (); + } + + // API for XMP metadata: + + bool SetXMP (dng_host &host, + const void *buffer, + uint32 count, + bool xmpInSidecar = false, + bool xmpIsNewer = false); + + void SetEmbeddedXMP (dng_host &host, + const void *buffer, + uint32 count); + + dng_xmp * GetXMP () + { + return fXMP.Get (); + } + + const dng_xmp * GetXMP () const + { + return fXMP.Get (); + } + + template< class X > + X & XMP (); + + template< class X > + const X & XMP () const; + + bool XMPinSidecar () const + { + return fXMPinSidecar; + } + + const dng_fingerprint & EmbeddedXMPDigest () const + { + return fEmbeddedXMPDigest; + } + + bool HaveValidEmbeddedXMP () const + { + return fEmbeddedXMPDigest.IsValid (); + } + + void ResetXMP (dng_xmp * newXMP); + + void ResetXMPSidecarNewer (dng_xmp * newXMP, bool inSidecar, bool isNewer ); + + // Synchronize metadata sources. + + void SynchronizeMetadata (); + + // Routines to update the date/time field in the EXIF and XMP + // metadata. + + void UpdateDateTime (const dng_date_time_info &dt); + + void UpdateDateTimeToNow (); + + void UpdateMetadataDateTimeToNow (); + + // Routines to set and get the source file MIME type. + + void SetSourceMIME (const char *s) + { + fSourceMIME.Set (s); + } + + const dng_string & SourceMIME () const + { + return fSourceMIME; + } + + }; + +/*****************************************************************************/ + +template< class E > +E & dng_metadata::Exif () + { + dng_exif * exif = GetExif (); + if (!exif) ThrowProgramError ("EXIF object is NULL."); + return dynamic_cast< E & > (*exif); + } + +/*****************************************************************************/ + +template< class E > +const E & dng_metadata::Exif () const + { + const dng_exif * exif = GetExif (); + if (!exif) ThrowProgramError ("EXIF object is NULL."); + return dynamic_cast< const E & > (*exif); + } + +/*****************************************************************************/ + +template< class X > +X & dng_metadata::XMP () + { + dng_xmp * xmp = GetXMP (); + if (!xmp) ThrowProgramError ("XMP object is NULL."); + return dynamic_cast< X & > (*xmp); + } + +/*****************************************************************************/ + +template< class X > +const X & dng_metadata::XMP () const + { + const dng_xmp * xmp = GetXMP (); + if (!xmp) ThrowProgramError ("XMP object is NULL."); + return dynamic_cast< const X & > (*xmp); + } + +/*****************************************************************************/ + +/// \brief Main class for holding DNG image data and associated metadata. + +class dng_negative + { + + public: + + enum RawImageStageEnum + { + rawImageStagePreOpcode1, + rawImageStagePostOpcode1, + rawImageStagePostOpcode2, + rawImageStagePreOpcode3, + rawImageStagePostOpcode3, + rawImageStageNone + }; + + protected: + + // The negative stores an associated allocator. It does not do + // anything to keep it alive or to release it when the object destructs. + // Hence, clients will need to make sure that the allocator's lifespan + // encompasses that of the dng_factory object which is generally + // directly bound to the dng_negative object. + + dng_memory_allocator &fAllocator; + + // Non-localized ASCII model name. + + dng_string fModelName; + + // Localized UTF-8 model name. + + dng_string fLocalName; + + // The area of raw image that should be included in the final converted + // image. This stems from extra pixels around the edges of the sensor + // including both the black mask and some additional padding. + + // The default crop can be smaller than the "active" area which includes + // the padding but not the black masked pixels. + + dng_urational fDefaultCropSizeH; + dng_urational fDefaultCropSizeV; + + dng_urational fDefaultCropOriginH; + dng_urational fDefaultCropOriginV; + + // Default user crop, in relative coordinates. + + dng_urational fDefaultUserCropT; + dng_urational fDefaultUserCropL; + dng_urational fDefaultUserCropB; + dng_urational fDefaultUserCropR; + + // Default scale factors. Generally, 1.0 for square pixel cameras. They + // can compensate for non-square pixels. The choice of exact values will + // generally depend on what the camera does. These are particularly + // interesting for the Nikon D1X and the Fuji diamond mosaic. + + dng_urational fDefaultScaleH; + dng_urational fDefaultScaleV; + + // Best quality scale factor. Used for the Nikon D1X and Fuji cameras + // to force everything to be a scale up rather than scale down. So, + // generally this is 1.0 / min (fDefaultScaleH, fDefaultScaleV) but + // this isn't used if the scale factors are only slightly different + // from 1.0. + + dng_urational fBestQualityScale; + + // Proxy image support. Remember certain sizes for the original image + // this proxy was derived from. + + dng_point fOriginalDefaultFinalSize; + dng_point fOriginalBestQualityFinalSize; + + dng_urational fOriginalDefaultCropSizeH; + dng_urational fOriginalDefaultCropSizeV; + + // Scale factors used in demosaic algorithm (calculated). + // Maps raw image coordinates to full image coordinates -- i.e., + // original image coordinates on raw sensor data to coordinates + // in fStage3Image which is the output of the interpolation step. + // So, if we downsample when interpolating, these numbers get + // smaller. + + real64 fRawToFullScaleH; + real64 fRawToFullScaleV; + + // Relative amount of noise at ISO 100. This is measured per camera model + // based on looking at flat areas of color. + + dng_urational fBaselineNoise; + + // How much noise reduction has already been applied (0.0 to 1.0) to the + // the raw image data? 0.0 = none, 1.0 = "ideal" amount--i.e. don't apply any + // more by default. 0/0 for unknown. + + dng_urational fNoiseReductionApplied; + + // Enhanced images can change the applied noise reduction, so we + // need to keep around the original value. + + dng_urational fRawNoiseReductionApplied; + + // Amount of noise for this negative (see dng_noise_profile for details). + + dng_noise_profile fNoiseProfile; + + // Enhanced images can change the noise profile, so we + // need to keep around the original value. + + dng_noise_profile fRawNoiseProfile; + + // Zero point for the exposure compensation slider. This reflects how + // the manufacturer sets up the camera and its conversions. + + dng_srational fBaselineExposure; + + // Relative amount of sharpening required. This is chosen per camera + // model based on how strong the anti-alias filter is on the camera + // and the quality of the lenses. This scales the sharpness slider + // value. + + dng_urational fBaselineSharpness; + + // Enhanced images can change the baseline sharpness, so we + // need to keep around the original value. + + dng_urational fRawBaselineSharpness; + + // Chroma blur radius (or 0/0 for auto). Set to 0/1 to disable + // chroma blurring. + + dng_urational fChromaBlurRadius; + + // Anti-alias filter strength (0.0 to 1.0). Used as a hint + // to the demosaic algorithms. + + dng_urational fAntiAliasStrength; + + // Linear response limit. The point at which the sensor goes + // non-linear and color information becomes unreliable. Used in + // the highlight-recovery logic. + + dng_urational fLinearResponseLimit; + + // Scale factor for shadows slider. The Fuji HDR cameras, for example, + // need a more sensitive shadow slider. + + dng_urational fShadowScale; + + // Colormetric reference. + + uint32 fColorimetricReference; + + // Is the stage 3 image floating point? + + bool fFloatingPoint; + + // Number of color channels for this image (e.g. 1, 3, or 4). + + uint32 fColorChannels; + + // Amount by which each channel has already been scaled. Some cameras + // have analog amplifiers on the color channels and these can result + // in different scalings per channel. This provides some level of + // analog white balancing. The Nikon D1 also did digital scaling but + // this caused problems with highlight recovery. + + dng_vector fAnalogBalance; + + // The "As Shot" neutral color coordinates in native camera space. + // This overrides fCameraWhiteXY if both are specified. This + // specifies the values per channel that would result in a neutral + // color for the "As Shot" case. This is generally supplied by + // the camera. + + dng_vector fCameraNeutral; + + // The "As Shot" white balance xy coordinates. Sometimes this is + // supplied by the camera. Sometimes the camera just supplies a name + // for the white balance. + + dng_xy_coord fCameraWhiteXY; + + // Individual camera calibrations. + + // Camera data --> camera calibration --> "inverse" of color matrix + + // This will be a 4x4 matrix for a 4-color camera. The defaults are + // almost always the identity matrix and for the cases where they + // aren't, they are diagonal matrices. + + dng_matrix fCameraCalibration1; + dng_matrix fCameraCalibration2; + + // Signature which allows a profile to announce that it is compatible + // with these calibration matrices. + + dng_string fCameraCalibrationSignature; + + // List of camera profiles. + + dng_std_vector fCameraProfile; + + // "As shot" camera profile name. + + dng_string fAsShotProfileName; + + // Raw image data digests. These are MD5 fingerprints of the raw image data + // in the file, computed using a specific algorithms. They can be used + // verify the raw data has not been corrupted. The new version is faster + // to compute on MP machines, and is used starting with DNG version 1.4. + + mutable dng_fingerprint fRawImageDigest; + + mutable dng_fingerprint fNewRawImageDigest; + + // Raw data unique ID. This is an unique identifer for the actual + // raw image data in the file. It can be used to index into caches + // for this data. + + mutable dng_fingerprint fRawDataUniqueID; + + mutable dng_std_mutex fRawDataUniqueIDMutex; + + // Original raw file name. Just the file name, not the full path. + + dng_string fOriginalRawFileName; + + // Is the original raw file data availaible? + + bool fHasOriginalRawFileData; + + // The compressed original raw file data. + + AutoPtr fOriginalRawFileData; + + // MD5 digest of original raw file data block. + + mutable dng_fingerprint fOriginalRawFileDigest; + + // DNG private data block. + + AutoPtr fDNGPrivateData; + + // Metadata information (XMP, IPTC, EXIF, orientation) + + dng_metadata fMetadata; + + // Information required to linearize and range map the raw data. + + AutoPtr fLinearizationInfo; + + // Information required to demoasic the raw data. + + AutoPtr fMosaicInfo; + + // Opcode list 1. (Applied to stored data) + + dng_opcode_list fOpcodeList1; + + // Opcode list 2. (Applied to range mapped data) + + dng_opcode_list fOpcodeList2; + + // Opcode list 3. (Post demosaic) + + dng_opcode_list fOpcodeList3; + + // Stage 1 image, which is image data stored in a DNG file. + + AutoPtr fStage1Image; + + // Stage 2 image, which is the stage 1 image after it has been + // linearized and range mapped. + + AutoPtr fStage2Image; + + // Stage 3 image, which is the stage 2 image after it has been + // demosaiced. + + AutoPtr fStage3Image; + + // Additional gain applied when building the stage 3 image. + + real64 fStage3Gain; + + // Optical black level of stage 3 image (in [0,65535]). + + uint16 fStage3BlackLevel; + + // Were any approximations (e.g. downsampling, etc.) applied + // file reading this image? + + bool fIsPreview; + + // Does the file appear to be damaged? + + bool fIsDamaged; + + // At what processing stage did we grab a copy of raw image data? + + RawImageStageEnum fRawImageStage; + + // The raw image data that we grabbed, if any. + + AutoPtr fRawImage; + + // The black level of the raw image (if not encoded by linearization info). + + uint16 fRawImageBlackLevel; + + // The floating point bit depth of the raw file, if any. + + uint32 fRawFloatBitDepth; + + // The raw image JPEG data that we grabbed, if any. + + AutoPtr fRawJPEGImage; + + // Keep a separate digest for the compressed JPEG data, if any. + + mutable dng_fingerprint fRawJPEGImageDigest; + + // Transparency mask image, if any. + + AutoPtr fTransparencyMask; + + // Grabbed transparency mask, if we are not saving the current mask. + + AutoPtr fRawTransparencyMask; + + // The bit depth for the raw transparancy mask, if known. + + uint32 fRawTransparencyMaskBitDepth; + + // We sometimes need to keep of copy of the stage3 image before + // flattening the transparency. + + AutoPtr fUnflattenedStage3Image; + + // Depth map. + + bool fHasDepthMap; + + AutoPtr fDepthMap; + + // Grabbed depth map, if we are not saving the current map. + + AutoPtr fRawDepthMap; + + // Depth metadata. + + uint32 fDepthFormat; + dng_urational fDepthNear; + dng_urational fDepthFar; + uint32 fDepthUnits; + uint32 fDepthMeasureType; + + // Enhance metadata. + + dng_string fEnhanceParams; + + public: + + virtual ~dng_negative (); + + static dng_negative * Make (dng_host &host); + + /// Provide access to the memory allocator used for this object. + + dng_memory_allocator & Allocator () const + { + return fAllocator; + } + + /// Getter for ModelName. + + void SetModelName (const char *name) + { + fModelName.Set_ASCII (name); + } + + /// Setter for ModelName. + + const dng_string & ModelName () const + { + return fModelName; + } + + /// Setter for LocalName. + + void SetLocalName (const char *name) + { + fLocalName.Set (name); + } + + /// Getter for LocalName. + + const dng_string & LocalName () const + { + return fLocalName; + } + + /// Getter for metadata + + dng_metadata &Metadata () + { + return fMetadata; + } + + #if qMetadataOnConst + + const dng_metadata &Metadata () const + { + return fMetadata; + } + + #endif // qMetadataOnConst + + /// Make a copy of the internal metadata generally as a basis for further + /// changes. + + dng_metadata * CloneInternalMetadata () const; + + protected: + + /// An accessor for the internal metadata that works even when we + /// have general access turned off. This is needed to provide + /// access to EXIF ISO information. + + const dng_metadata &InternalMetadata () const + { + return fMetadata; + } + + public: + + /// Setter for BaseOrientation. + + void SetBaseOrientation (const dng_orientation &orientation) + { + Metadata ().SetBaseOrientation (orientation); + } + + /// Has BaseOrientation been set? + + bool HasBaseOrientation () METACONST + { + return Metadata ().HasBaseOrientation (); + } + + /// Getter for BaseOrientation. + + const dng_orientation & BaseOrientation () METACONST + { + return Metadata ().BaseOrientation (); + } + + /// Hook to allow SDK host code to add additional rotations. + + virtual dng_orientation ComputeOrientation (const dng_metadata &metadata) const; + + /// For non-const negatives, we simply default to using the metadata attached to the negative. + + dng_orientation Orientation () + { + return ComputeOrientation (Metadata ()); + } + + /// Logically rotates the image by changing the orientation values. + /// This will also update the XMP data. + + void ApplyOrientation (const dng_orientation &orientation) + { + Metadata ().ApplyOrientation (orientation); + } + + /// Setter for DefaultCropSize. + + void SetDefaultCropSize (const dng_urational &sizeH, + const dng_urational &sizeV) + { + fDefaultCropSizeH = sizeH; + fDefaultCropSizeV = sizeV; + } + + /// Setter for DefaultCropSize. + + void SetDefaultCropSize (uint32 sizeH, + uint32 sizeV) + { + SetDefaultCropSize (dng_urational (sizeH, 1), + dng_urational (sizeV, 1)); + } + + /// Getter for DefaultCropSize horizontal. + + const dng_urational & DefaultCropSizeH () const + { + return fDefaultCropSizeH; + } + + /// Getter for DefaultCropSize vertical. + + const dng_urational & DefaultCropSizeV () const + { + return fDefaultCropSizeV; + } + + /// Setter for DefaultCropOrigin. + + void SetDefaultCropOrigin (const dng_urational &originH, + const dng_urational &originV) + { + fDefaultCropOriginH = originH; + fDefaultCropOriginV = originV; + } + + /// Setter for DefaultCropOrigin. + + void SetDefaultCropOrigin (uint32 originH, + uint32 originV) + { + SetDefaultCropOrigin (dng_urational (originH, 1), + dng_urational (originV, 1)); + } + + /// Set default crop around center of image. + + void SetDefaultCropCentered (const dng_point &rawSize) + { + + uint32 sizeH = Round_uint32 (fDefaultCropSizeH.As_real64 ()); + uint32 sizeV = Round_uint32 (fDefaultCropSizeV.As_real64 ()); + + SetDefaultCropOrigin ((rawSize.h - sizeH) >> 1, + (rawSize.v - sizeV) >> 1); + + } + + /// Get default crop origin horizontal value. + + const dng_urational & DefaultCropOriginH () const + { + return fDefaultCropOriginH; + } + + /// Get default crop origin vertical value. + + const dng_urational & DefaultCropOriginV () const + { + return fDefaultCropOriginV; + } + + /// Is there a default user crop? + + bool HasDefaultUserCrop () const + { + return (fDefaultUserCropT.As_real64 () != 0.0 || + fDefaultUserCropL.As_real64 () != 0.0 || + fDefaultUserCropB.As_real64 () != 1.0 || + fDefaultUserCropR.As_real64 () != 1.0); + } + + /// Getter for top coordinate of default user crop. + + const dng_urational & DefaultUserCropT () const + { + return fDefaultUserCropT; + } + + /// Getter for left coordinate of default user crop. + + const dng_urational & DefaultUserCropL () const + { + return fDefaultUserCropL; + } + + /// Getter for bottom coordinate of default user crop. + + const dng_urational & DefaultUserCropB () const + { + return fDefaultUserCropB; + } + + /// Getter for right coordinate of default user crop. + + const dng_urational & DefaultUserCropR () const + { + return fDefaultUserCropR; + } + + /// Reset default user crop to default crop area. + + void ResetDefaultUserCrop () + { + fDefaultUserCropT = dng_urational (0, 1); + fDefaultUserCropL = dng_urational (0, 1); + fDefaultUserCropB = dng_urational (1, 1); + fDefaultUserCropR = dng_urational (1, 1); + } + + /// Setter for all 4 coordinates of default user crop. + + void SetDefaultUserCrop (const dng_urational &t, + const dng_urational &l, + const dng_urational &b, + const dng_urational &r) + { + fDefaultUserCropT = t; + fDefaultUserCropL = l; + fDefaultUserCropB = b; + fDefaultUserCropR = r; + } + + /// Setter for top coordinate of default user crop. + + void SetDefaultUserCropT (const dng_urational &value) + { + fDefaultUserCropT = value; + } + + /// Setter for left coordinate of default user crop. + + void SetDefaultUserCropL (const dng_urational &value) + { + fDefaultUserCropL = value; + } + + /// Setter for bottom coordinate of default user crop. + + void SetDefaultUserCropB (const dng_urational &value) + { + fDefaultUserCropB = value; + } + + /// Setter for right coordinate of default user crop. + + void SetDefaultUserCropR (const dng_urational &value) + { + fDefaultUserCropR = value; + } + + /// Setter for DefaultScale. + + void SetDefaultScale (const dng_urational &scaleH, + const dng_urational &scaleV) + { + fDefaultScaleH = scaleH; + fDefaultScaleV = scaleV; + } + + /// Get default scale horizontal value. + + const dng_urational & DefaultScaleH () const + { + return fDefaultScaleH; + } + + /// Get default scale vertical value. + + const dng_urational & DefaultScaleV () const + { + return fDefaultScaleV; + } + + /// Setter for BestQualityScale. + + void SetBestQualityScale (const dng_urational &scale) + { + fBestQualityScale = scale; + } + + /// Getter for BestQualityScale. + + const dng_urational & BestQualityScale () const + { + return fBestQualityScale; + } + + /// Is the best quality scale different than the default scale? + + bool HasBestQualityScale () const + { + return fBestQualityScale.As_real64 () != 1.0; + } + + /// API for raw to full image scaling factors horizontal. + + real64 RawToFullScaleH () const + { + return fRawToFullScaleH; + } + + /// API for raw to full image scaling factors vertical. + + real64 RawToFullScaleV () const + { + return fRawToFullScaleV; + } + + /// Setter for raw to full scales. + + void SetRawToFullScale (real64 scaleH, + real64 scaleV) + { + fRawToFullScaleH = scaleH; + fRawToFullScaleV = scaleV; + } + + /// Get default scale factor. + /// When specifing a single scale factor, we use the horizontal + /// scale factor, and let the vertical scale factor be calculated + /// based on the pixel aspect ratio. + + real64 DefaultScale () const + { + return DefaultScaleH ().As_real64 (); + } + + /// Default cropped image size (at scale == 1.0) width. + + real64 SquareWidth () const + { + return DefaultCropSizeH ().As_real64 (); + } + + /// Default cropped image size (at scale == 1.0) height. + + real64 SquareHeight () const + { + return DefaultCropSizeV ().As_real64 () * + DefaultScaleV ().As_real64 () / + DefaultScaleH ().As_real64 (); + } + + /// Default cropped image aspect ratio. + + real64 AspectRatio () const + { + return SquareWidth () / + SquareHeight (); + } + + /// Pixel aspect ratio of stage 3 image. + + real64 PixelAspectRatio () const + { + return (DefaultScaleH ().As_real64 () / RawToFullScaleH ()) / + (DefaultScaleV ().As_real64 () / RawToFullScaleV ()); + } + + /// Default cropped image size at given scale factor width. + + uint32 FinalWidth (real64 scale) const + { + return Round_uint32 (SquareWidth () * scale); + } + + /// Default cropped image size at given scale factor height. + + uint32 FinalHeight (real64 scale) const + { + return Round_uint32 (SquareHeight () * scale); + } + + /// Default cropped image size at default scale factor width. + + uint32 DefaultFinalWidth () const + { + return FinalWidth (DefaultScale ()); + } + + /// Default cropped image size at default scale factor height. + + uint32 DefaultFinalHeight () const + { + return FinalHeight (DefaultScale ()); + } + + /// Get best quality width. + /// For a naive conversion, one could use either the default size, + /// or the best quality size. + + uint32 BestQualityFinalWidth () const + { + return FinalWidth (DefaultScale () * BestQualityScale ().As_real64 ()); + } + + /// Get best quality height. + /// For a naive conversion, one could use either the default size, + /// or the best quality size. + + uint32 BestQualityFinalHeight () const + { + return FinalHeight (DefaultScale () * BestQualityScale ().As_real64 ()); + } + + /// Default size of original (non-proxy) image. For non-proxy images, this + /// is equal to DefaultFinalWidth/DefaultFinalHight. For proxy images, this + /// is equal to the DefaultFinalWidth/DefaultFinalHeight of the image this + /// proxy was derived from. + + const dng_point & OriginalDefaultFinalSize () const + { + return fOriginalDefaultFinalSize; + } + + /// Setter for OriginalDefaultFinalSize. + + void SetOriginalDefaultFinalSize (const dng_point &size) + { + fOriginalDefaultFinalSize = size; + } + + /// Best quality size of original (non-proxy) image. For non-proxy images, this + /// is equal to BestQualityFinalWidth/BestQualityFinalHeight. For proxy images, this + /// is equal to the BestQualityFinalWidth/BestQualityFinalHeight of the image this + /// proxy was derived from. + + const dng_point & OriginalBestQualityFinalSize () const + { + return fOriginalBestQualityFinalSize; + } + + /// Setter for OriginalBestQualityFinalSize. + + void SetOriginalBestQualityFinalSize (const dng_point &size) + { + fOriginalBestQualityFinalSize = size; + } + + /// DefaultCropSize for original (non-proxy) image. For non-proxy images, + /// this is equal to the DefaultCropSize. for proxy images, this is + /// equal size of the DefaultCropSize of the image this proxy was derived from. + + const dng_urational & OriginalDefaultCropSizeH () const + { + return fOriginalDefaultCropSizeH; + } + + const dng_urational & OriginalDefaultCropSizeV () const + { + return fOriginalDefaultCropSizeV; + } + + /// Setter for OriginalDefaultCropSize. + + void SetOriginalDefaultCropSize (const dng_urational &sizeH, + const dng_urational &sizeV) + { + fOriginalDefaultCropSizeH = sizeH; + fOriginalDefaultCropSizeV = sizeV; + } + + /// If the original size fields are undefined, set them to the + /// current sizes. + + void SetDefaultOriginalSizes (); + + /// Set all the original size fields to a specific size. + + void SetOriginalSizes (const dng_point &size); + + /// The default crop area in the stage 3 image coordinates. + + dng_rect DefaultCropArea () const; + + /// Setter for BaselineNoise. + + void SetBaselineNoise (real64 noise) + { + fBaselineNoise.Set_real64 (noise, 100); + } + + /// Getter for BaselineNoise as dng_urational. + + const dng_urational & BaselineNoiseR () const + { + return fBaselineNoise; + } + + /// Getter for BaselineNoise as real64. + + real64 BaselineNoise () const + { + return fBaselineNoise.As_real64 (); + } + + /// Setter for NoiseReductionApplied. + + void SetNoiseReductionApplied (const dng_urational &value) + { + fNoiseReductionApplied = value; + } + + /// Getter for NoiseReductionApplied. + + const dng_urational & NoiseReductionApplied () const + { + return fNoiseReductionApplied; + } + + // Sets the raw noise reduction applied to be a copy of the unenhanced + // noise reduction applied, if not set yet. + + void SetRawNoiseReductionApplied () + { + if (fRawNoiseReductionApplied.NotValid ()) + { + fRawNoiseReductionApplied = fNoiseReductionApplied; + } + } + + // Gets the raw NoiseReductionApplied value. + + const dng_urational & RawNoiseReductionApplied () const + { + return fRawNoiseReductionApplied; + } + + /// Setter for noise profile. + + void SetNoiseProfile (const dng_noise_profile &noiseProfile) + { + fNoiseProfile = noiseProfile; + } + + /// Does this negative have a valid noise profile? + + bool HasNoiseProfile () const + { + return fNoiseProfile.IsValidForNegative (*this); + } + + /// Getter for noise profile. + + const dng_noise_profile & NoiseProfile () const + { + return fNoiseProfile; + } + + // Does this negative have a valid raw noise profile? + + bool HasRawNoiseProfile () const + { + return fRawNoiseProfile.IsValidForNegative (*this); + } + + // Sets the raw noise profile to be a copy of the unenhanced + // noise profile, if not set yet. + + void SetRawNoiseProfile () + { + if (!HasRawNoiseProfile ()) + { + fRawNoiseProfile = fNoiseProfile; + } + } + + // Getter for raw noise profile. + + const dng_noise_profile & RawNoiseProfile () const + { + return fRawNoiseProfile; + } + + /// Setter for BaselineExposure. + + void SetBaselineExposure (real64 exposure) + { + fBaselineExposure.Set_real64 (exposure, 100); + } + + /// Getter for BaselineExposure as dng_urational. + + const dng_srational & BaselineExposureR () const + { + return fBaselineExposure; + } + + /// Getter for BaselineExposure as real64. + + real64 BaselineExposure () const + { + return BaselineExposureR ().As_real64 (); + } + + /// Compute total baseline exposure (sum of negative's BaselineExposure and + /// profile's BaselineExposureOffset). + + real64 TotalBaselineExposure (const dng_camera_profile_id &profileID) const; + + /// Setter for BaselineSharpness. + + void SetBaselineSharpness (real64 sharpness) + { + fBaselineSharpness.Set_real64 (sharpness, 100); + } + + /// Getter for BaselineSharpness as dng_urational. + + const dng_urational & BaselineSharpnessR () const + { + return fBaselineSharpness; + } + + /// Getter for BaselineSharpness as real64. + + real64 BaselineSharpness () const + { + return BaselineSharpnessR ().As_real64 (); + } + + // Sets the raw baseline sharpness to be a copy of the baseline + // sharpness, if not set yet. + + void SetRawBaselineSharpness () + { + if (fRawBaselineSharpness.d == 0) + { + fRawBaselineSharpness = fBaselineSharpness; + } + } + + // Gets the raw baseline sharpness value. + + const dng_urational & RawBaselineSharpness () const + { + if (fRawBaselineSharpness.d != 0) + { + return fRawBaselineSharpness; + } + return fBaselineSharpness; + } + + /// Setter for ChromaBlurRadius. + + void SetChromaBlurRadius (const dng_urational &radius) + { + fChromaBlurRadius = radius; + } + + /// Getter for ChromaBlurRadius as dng_urational. + + const dng_urational & ChromaBlurRadius () const + { + return fChromaBlurRadius; + } + + /// Setter for AntiAliasStrength. + + void SetAntiAliasStrength (const dng_urational &strength) + { + fAntiAliasStrength = strength; + } + + /// Getter for AntiAliasStrength as dng_urational. + + const dng_urational & AntiAliasStrength () const + { + return fAntiAliasStrength; + } + + /// Setter for LinearResponseLimit. + + void SetLinearResponseLimit (real64 limit) + { + fLinearResponseLimit.Set_real64 (limit, 100); + } + + /// Getter for LinearResponseLimit as dng_urational. + + const dng_urational & LinearResponseLimitR () const + { + return fLinearResponseLimit; + } + + /// Getter for LinearResponseLimit as real64. + + real64 LinearResponseLimit () const + { + return LinearResponseLimitR ().As_real64 (); + } + + /// Setter for ShadowScale. + + void SetShadowScale (const dng_urational &scale); + + /// Getter for ShadowScale as dng_urational. + + const dng_urational & ShadowScaleR () const + { + return fShadowScale; + } + + /// Getter for ShadowScale as real64. + + real64 ShadowScale () const + { + return ShadowScaleR ().As_real64 (); + } + + // API for ColorimetricReference. + + void SetColorimetricReference (uint32 ref) + { + fColorimetricReference = ref; + } + + uint32 ColorimetricReference () const + { + return fColorimetricReference; + } + + // Floating point flag. + + void SetFloatingPoint (bool isFloatingPoint) + { + fFloatingPoint = isFloatingPoint; + } + + bool IsFloatingPoint () const + { + return fFloatingPoint; + } + + // HDR/NDR. + + bool IsHighDynamicRange () const + { + return IsFloatingPoint () && + ColorimetricReference () == crSceneReferred; + } + + bool IsNormalDynamicRange () const + { + return !IsHighDynamicRange (); + } + + /// Setter for ColorChannels. + + void SetColorChannels (uint32 channels) + { + fColorChannels = channels; + } + + /// Getter for ColorChannels. + + uint32 ColorChannels () const + { + return fColorChannels; + } + + /// Setter for Monochrome. + + void SetMonochrome () + { + SetColorChannels (1); + } + + /// Getter for Monochrome. + + bool IsMonochrome () const + { + return ColorChannels () == 1; + } + + /// Setter for AnalogBalance. + + void SetAnalogBalance (const dng_vector &b); + + /// Getter for AnalogBalance as dng_urational. + + dng_urational AnalogBalanceR (uint32 channel) const; + + /// Getter for AnalogBalance as real64. + + real64 AnalogBalance (uint32 channel) const; + + /// Setter for CameraNeutral. + + void SetCameraNeutral (const dng_vector &n); + + /// Clear CameraNeutral. + + void ClearCameraNeutral () + { + fCameraNeutral.Clear (); + } + + /// Determine if CameraNeutral has been set but not cleared. + + bool HasCameraNeutral () const + { + return fCameraNeutral.NotEmpty (); + } + + /// Getter for CameraNeutral. + + const dng_vector & CameraNeutral () const + { + return fCameraNeutral; + } + + dng_urational CameraNeutralR (uint32 channel) const; + + /// Setter for CameraWhiteXY. + + void SetCameraWhiteXY (const dng_xy_coord &coord); + + bool HasCameraWhiteXY () const + { + return fCameraWhiteXY.IsValid (); + } + + const dng_xy_coord & CameraWhiteXY () const; + + void GetCameraWhiteXY (dng_urational &x, + dng_urational &y) const; + + // API for camera calibration: + + /// Setter for first of up to two color matrices used for individual camera calibrations. + /// + /// The sequence of matrix transforms is: + /// Camera data --> camera calibration --> "inverse" of color matrix + /// + /// This will be a 4x4 matrix for a four-color camera. The defaults are + /// almost always the identity matrix, and for the cases where they + /// aren't, they are diagonal matrices. + + void SetCameraCalibration1 (const dng_matrix &m); + + /// Setter for second of up to two color matrices used for individual camera calibrations. + /// + /// The sequence of matrix transforms is: + /// Camera data --> camera calibration --> "inverse" of color matrix + /// + /// This will be a 4x4 matrix for a four-color camera. The defaults are + /// almost always the identity matrix, and for the cases where they + /// aren't, they are diagonal matrices. + + void SetCameraCalibration2 (const dng_matrix &m); + + /// Getter for first of up to two color matrices used for individual camera calibrations. + + const dng_matrix & CameraCalibration1 () const + { + return fCameraCalibration1; + } + + /// Getter for second of up to two color matrices used for individual camera calibrations. + + const dng_matrix & CameraCalibration2 () const + { + return fCameraCalibration2; + } + + void SetCameraCalibrationSignature (const char *signature) + { + fCameraCalibrationSignature.Set (signature); + } + + const dng_string & CameraCalibrationSignature () const + { + return fCameraCalibrationSignature; + } + + // Camera Profile API: + + void AddProfile (AutoPtr &profile); + + void ClearProfiles (); + + void ClearProfiles (bool clearBuiltinMatrixProfiles, + bool clearReadFromDisk); + + uint32 ProfileCount () const; + + const dng_camera_profile & ProfileByIndex (uint32 index) const; + + virtual const dng_camera_profile * ProfileByID (const dng_camera_profile_id &id, + bool useDefaultIfNoMatch = true) const; + + bool HasProfileID (const dng_camera_profile_id &id) const + { + return ProfileByID (id, false) != NULL; + } + + // Returns the camera profile to embed when saving to DNG: + + virtual const dng_camera_profile * ComputeCameraProfileToEmbed + (const dng_metadata &metadata) const; + + // For non-const negatives, we can use the embedded metadata. + + const dng_camera_profile * CameraProfileToEmbed () + { + return ComputeCameraProfileToEmbed (Metadata ()); + } + + // API for AsShotProfileName. + + void SetAsShotProfileName (const char *name) + { + fAsShotProfileName.Set (name); + } + + const dng_string & AsShotProfileName () const + { + return fAsShotProfileName; + } + + // Makes a dng_color_spec object for this negative. + + virtual dng_color_spec * MakeColorSpec (const dng_camera_profile_id &id) const; + + // Compute a MD5 hash on an image, using a fixed algorithm. + // The results must be stable across different hardware, OSes, + // and software versions. + + static dng_fingerprint FindImageDigest (dng_host &host, + const dng_image &image); + + // API for RawImageDigest and NewRawImageDigest: + + void SetRawImageDigest (const dng_fingerprint &digest) + { + fRawImageDigest = digest; + } + + void SetNewRawImageDigest (const dng_fingerprint &digest) + { + fNewRawImageDigest = digest; + } + + void ClearRawImageDigest () const + { + fRawImageDigest .Clear (); + fNewRawImageDigest.Clear (); + } + + const dng_fingerprint & RawImageDigest () const + { + return fRawImageDigest; + } + + const dng_fingerprint & NewRawImageDigest () const + { + return fNewRawImageDigest; + } + + void FindRawImageDigest (dng_host &host) const; + + void FindNewRawImageDigest (dng_host &host) const; + + void ValidateRawImageDigest (dng_host &host); + + // API for RawDataUniqueID: + + void SetRawDataUniqueID (const dng_fingerprint &id) + { + fRawDataUniqueID = id; + } + + dng_fingerprint RawDataUniqueID () const; + + void FindRawDataUniqueID (dng_host &host) const; + + virtual void RecomputeRawDataUniqueID (dng_host &host); + + // API for original raw file name: + + void SetOriginalRawFileName (const char *name) + { + fOriginalRawFileName.Set (name); + } + + bool HasOriginalRawFileName () const + { + return fOriginalRawFileName.NotEmpty (); + } + + const dng_string & OriginalRawFileName () const + { + return fOriginalRawFileName; + } + + // API for original raw file data: + + void SetHasOriginalRawFileData (bool hasData) + { + fHasOriginalRawFileData = hasData; + } + + bool CanEmbedOriginalRaw () const + { + return fHasOriginalRawFileData && HasOriginalRawFileName (); + } + + void SetOriginalRawFileData (AutoPtr &data) + { + fOriginalRawFileData.Reset (data.Release ()); + } + + const void * OriginalRawFileData () const + { + return fOriginalRawFileData.Get () ? fOriginalRawFileData->Buffer () + : NULL; + } + + uint32 OriginalRawFileDataLength () const + { + return fOriginalRawFileData.Get () ? fOriginalRawFileData->LogicalSize () + : 0; + } + + // API for original raw file data digest. + + void SetOriginalRawFileDigest (const dng_fingerprint &digest) + { + fOriginalRawFileDigest = digest; + } + + const dng_fingerprint & OriginalRawFileDigest () const + { + return fOriginalRawFileDigest; + } + + void FindOriginalRawFileDigest () const; + + void ValidateOriginalRawFileDigest (); + + // API for DNG private data: + + void SetPrivateData (AutoPtr &block) + { + fDNGPrivateData.Reset (block.Release ()); + } + + void ClearPrivateData () + { + fDNGPrivateData.Reset (); + } + + const uint8 * PrivateData () const + { + return fDNGPrivateData.Get () ? fDNGPrivateData->Buffer_uint8 () + : NULL; + } + + uint32 PrivateLength () const + { + return fDNGPrivateData.Get () ? fDNGPrivateData->LogicalSize () + : 0; + } + + // API for MakerNote data: + + void SetMakerNoteSafety (bool safe) + { + Metadata ().SetMakerNoteSafety (safe); + } + + bool IsMakerNoteSafe () METACONST + { + return Metadata ().IsMakerNoteSafe (); + } + + void SetMakerNote (AutoPtr &block) + { + Metadata ().SetMakerNote (block); + } + + void ClearMakerNote () + { + Metadata ().ClearMakerNote (); + } + + const void * MakerNoteData () METACONST + { + return Metadata ().MakerNoteData (); + } + + uint32 MakerNoteLength () METACONST + { + return Metadata ().MakerNoteLength (); + } + + // API for EXIF metadata: + + dng_exif * GetExif () + { + return Metadata ().GetExif (); + } + + #if qMetadataOnConst + + const dng_exif * GetExif () const + { + return Metadata ().GetExif (); + } + + #endif // qMetadataOnConst + + void ResetExif (dng_exif * newExif) + { + Metadata ().ResetExif (newExif); + } + + // API for original EXIF metadata. + + dng_exif * GetOriginalExif () + { + return Metadata ().GetOriginalExif (); + } + + #if qMetadataOnConst + + const dng_exif * GetOriginalExif () const + { + return Metadata ().GetOriginalExif (); + } + + #endif // qMetadataOnConst + + // API for IPTC metadata: + + void SetIPTC (AutoPtr &block, + uint64 offset) + { + Metadata ().SetIPTC (block, offset); + } + + void SetIPTC (AutoPtr &block) + { + Metadata ().SetIPTC (block); + } + + void ClearIPTC () + { + Metadata ().ClearIPTC (); + } + + const void * IPTCData () METACONST + { + return Metadata ().IPTCData (); + } + + uint32 IPTCLength () METACONST + { + return Metadata ().IPTCLength (); + } + + uint64 IPTCOffset () METACONST + { + return Metadata ().IPTCOffset (); + } + + dng_fingerprint IPTCDigest (bool includePadding = true) METACONST + { + return Metadata ().IPTCDigest (includePadding); + } + + void RebuildIPTC (bool padForTIFF) + { + Metadata ().RebuildIPTC (Allocator (), padForTIFF); + } + + // API for XMP metadata: + + bool SetXMP (dng_host &host, + const void *buffer, + uint32 count, + bool xmpInSidecar = false, + bool xmpIsNewer = false) + { + return Metadata ().SetXMP (host, + buffer, + count, + xmpInSidecar, + xmpIsNewer); + } + + dng_xmp * GetXMP () + { + return Metadata ().GetXMP (); + } + + #if qMetadataOnConst + + const dng_xmp * GetXMP () const + { + return Metadata ().GetXMP (); + } + + #endif // qMetadataOnConst + + bool XMPinSidecar () METACONST + { + return Metadata ().XMPinSidecar (); + } + + void ResetXMP (dng_xmp * newXMP) + { + Metadata ().ResetXMP (newXMP); + } + + void ResetXMPSidecarNewer (dng_xmp * newXMP, bool inSidecar, bool isNewer ) + { + Metadata ().ResetXMPSidecarNewer (newXMP, inSidecar, isNewer); + } + + bool HaveValidEmbeddedXMP () METACONST + { + return Metadata ().HaveValidEmbeddedXMP (); + } + + // API for source MIME type. + + void SetSourceMIME (const char *s) + { + Metadata ().SetSourceMIME (s); + } + + // API for linearization information: + + const dng_linearization_info * GetLinearizationInfo () const + { + return fLinearizationInfo.Get (); + } + + void ClearLinearizationInfo () + { + fLinearizationInfo.Reset (); + } + + // Linearization curve. Usually used to increase compression ratios + // by storing the compressed data in a more visually uniform space. + // This is a 16-bit LUT that maps the stored data back to linear. + + void SetLinearization (AutoPtr &curve); + + // Active area (non-black masked pixels). These pixels are trimmed + // during linearization step. + + void SetActiveArea (const dng_rect &area); + + // Areas that are known to contain black masked pixels that can + // be used to estimate black levels. + + void SetMaskedAreas (uint32 count, + const dng_rect *area); + + void SetMaskedArea (const dng_rect &area) + { + SetMaskedAreas (1, &area); + } + + // Sensor black level information. + + void SetBlackLevel (real64 black, + int32 plane = -1); + + void SetQuadBlacks (real64 black0, + real64 black1, + real64 black2, + real64 black3, + int32 plane = -1); + + void Set6x6Blacks (real64 blacks6x6 [36], + int32 plane = -1); + + void SetRowBlacks (const real64 *blacks, + uint32 count); + + void SetColumnBlacks (const real64 *blacks, + uint32 count); + + // Sensor white level information. + + uint32 WhiteLevel (uint32 plane = 0) const; + + void SetWhiteLevel (uint32 white, + int32 plane = -1); + + // API for mosaic information: + + const dng_mosaic_info * GetMosaicInfo () const + { + return fMosaicInfo.Get (); + } + + void ClearMosaicInfo () + { + fMosaicInfo.Reset (); + } + + // ColorKeys APIs: + + void SetColorKeys (ColorKeyCode color0, + ColorKeyCode color1, + ColorKeyCode color2, + ColorKeyCode color3 = colorKeyMaxEnum); + + void SetRGB () + { + + SetColorChannels (3); + + SetColorKeys (colorKeyRed, + colorKeyGreen, + colorKeyBlue); + + } + + void SetCMY () + { + + SetColorChannels (3); + + SetColorKeys (colorKeyCyan, + colorKeyMagenta, + colorKeyYellow); + + } + + void SetGMCY () + { + + SetColorChannels (4); + + SetColorKeys (colorKeyGreen, + colorKeyMagenta, + colorKeyCyan, + colorKeyYellow); + + } + + // APIs to set mosaic patterns. + + void SetBayerMosaic (uint32 phase); + + void SetFujiMosaic (uint32 phase); + + void SetFujiMosaic6x6 (uint32 phase); + + void SetQuadMosaic (uint32 pattern); + + // BayerGreenSplit. + + void SetGreenSplit (uint32 split); + + // APIs for opcode lists. + + const dng_opcode_list & OpcodeList1 () const + { + return fOpcodeList1; + } + + dng_opcode_list & OpcodeList1 () + { + return fOpcodeList1; + } + + const dng_opcode_list & OpcodeList2 () const + { + return fOpcodeList2; + } + + dng_opcode_list & OpcodeList2 () + { + return fOpcodeList2; + } + + const dng_opcode_list & OpcodeList3 () const + { + return fOpcodeList3; + } + + dng_opcode_list & OpcodeList3 () + { + return fOpcodeList3; + } + + // First part of parsing logic. + + virtual void Parse (dng_host &host, + dng_stream &stream, + dng_info &info); + + // Second part of parsing logic. This is split off from the + // first part because these operations are useful when extending + // this sdk to support non-DNG raw formats. + + virtual void PostParse (dng_host &host, + dng_stream &stream, + dng_info &info); + + // Synchronize metadata sources. + + void SynchronizeMetadata () + { + Metadata ().SynchronizeMetadata (); + } + + // Routines to update the date/time field in the EXIF and XMP + // metadata. + + void UpdateDateTime (const dng_date_time_info &dt) + { + Metadata ().UpdateDateTime (dt); + } + + void UpdateDateTimeToNow () + { + Metadata ().UpdateDateTimeToNow (); + } + + // Developer's utility function to switch to four color Bayer + // interpolation. This is useful for evaluating how much green + // split a Bayer pattern sensor has. + + virtual bool SetFourColorBayer (); + + // Access routines for the image stages. + + const dng_image * Stage1Image () const + { + return fStage1Image.Get (); + } + + const dng_image * Stage2Image () const + { + return fStage2Image.Get (); + } + + const dng_image * Stage3Image () const + { + return fStage3Image.Get (); + } + + // Returns the processing stage of the raw image data. + + RawImageStageEnum RawImageStage () const + { + return fRawImageStage; + } + + // Returns the raw image data. + + const dng_image & RawImage () const; + + // Returns the raw image black level in 16-bit space. + + uint16 RawImageBlackLevel () const; + + // API for raw floating point bit depth. + + uint32 RawFloatBitDepth () const + { + return fRawFloatBitDepth; + } + + void SetRawFloatBitDepth (uint32 bitDepth) + { + fRawFloatBitDepth = bitDepth; + } + + // API for raw jpeg image. + + const dng_jpeg_image * RawJPEGImage () const; + + void SetRawJPEGImage (AutoPtr &jpegImage); + + void ClearRawJPEGImage (); + + // API for RawJPEGImageDigest: + + void SetRawJPEGImageDigest (const dng_fingerprint &digest) + { + fRawJPEGImageDigest = digest; + } + + void ClearRawJPEGImageDigest () const + { + fRawJPEGImageDigest.Clear (); + } + + const dng_fingerprint & RawJPEGImageDigest () const + { + return fRawJPEGImageDigest; + } + + void FindRawJPEGImageDigest (dng_host &host) const; + + // Read the opcode lists. + + virtual void ReadOpcodeLists (dng_host &host, + dng_stream &stream, + dng_info &info); + + // Read the stage 1 image. + + virtual void ReadStage1Image (dng_host &host, + dng_stream &stream, + dng_info &info); + + // Read the enhanced image directly into the stage 3 image. + + virtual void ReadEnhancedImage (dng_host &host, + dng_stream &stream, + dng_info &info); + + // Assign the stage 1 image. + + void SetStage1Image (AutoPtr &image); + + // Assign the stage 2 image. + + void SetStage2Image (AutoPtr &image); + + // Assign the stage 3 image. + + void SetStage3Image (AutoPtr &image); + + // Build the stage 2 (linearized and range mapped) image. + + void BuildStage2Image (dng_host &host); + + // Build the stage 3 (demosaiced) image. + + void BuildStage3Image (dng_host &host, + int32 srcPlane = -1); + + // Additional gain applied when building the stage 3 image. + + void SetStage3Gain (real64 gain) + { + fStage3Gain = gain; + } + + real64 Stage3Gain () const + { + return fStage3Gain; + } + + // Optical black level of stage 3 image (in [0,65535]). + + void SetStage3BlackLevel (uint16 level) + { + fStage3BlackLevel = level; + } + + uint16 Stage3BlackLevel () const + { + return fStage3BlackLevel; + } + + // Optical black level of stage 3 image (in [0,1]). + + real64 Stage3BlackLevelNormalized () const + { + return fStage3BlackLevel * (1.0 / 65535.0); + } + + // Is this negative permitted to support deferred black subtraction + // (by preserving offset or negative black values in the stage 3 + // image)? + // + // If false, then fStage3BlackLevel must be zero. + // If true, then fStage3BlackLevel may or may not be zero. + // + // Default implementation return false. + + virtual bool SupportsPreservedBlackLevels (dng_host &host); + + // Adaptively encode a proxy image down to 8-bits/channel. + + dng_image * EncodeRawProxy (dng_host &host, + const dng_image &srcImage, + dng_opcode_list &opcodeList, + real64 *blackLevel) const; + + // Convert to a proxy negative. + + void ConvertToProxy (dng_host &host, + dng_image_writer &writer, + uint32 proxySize = 0, + uint64 proxyCount = 0); + + // IsProxy API: + + bool IsProxy () const; + + // IsPreview API: + + void SetIsPreview (bool preview) + { + fIsPreview = preview; + } + + bool IsPreview () const + { + return fIsPreview; + } + + // IsDamaged API: + + void SetIsDamaged (bool damaged) + { + fIsDamaged = damaged; + } + + bool IsDamaged () const + { + return fIsDamaged; + } + + // Transparancy Mask API: + + void SetTransparencyMask (AutoPtr &image, + uint32 bitDepth = 0); + + const dng_image * TransparencyMask () const; + + const dng_image * RawTransparencyMask () const; + + uint32 RawTransparencyMaskBitDepth () const; + + void ReadTransparencyMask (dng_host &host, + dng_stream &stream, + dng_info &info); + + virtual void ResizeTransparencyToMatchStage3 (dng_host &host, + bool convertTo8Bit = false); + + virtual bool NeedFlattenTransparency (dng_host &host); + + virtual void FlattenTransparency (dng_host &host); + + const dng_image * UnflattenedStage3Image () const; + + // Depth map API: + + bool HasDepthMap () const + { + return fHasDepthMap; + } + + void SetHasDepthMap (bool hasDepthMap) + { + fHasDepthMap = hasDepthMap; + } + + const dng_image * DepthMap () const + { + return fDepthMap.Get (); + } + + void SetDepthMap (AutoPtr &depthMap); + + bool HasDepthMapImage () const + { + return (fDepthMap.Get () != NULL); + } + + const dng_image * RawDepthMap () const + { + if (fRawDepthMap.Get ()) + { + return fRawDepthMap.Get (); + } + return DepthMap (); + } + + void ReadDepthMap (dng_host &host, + dng_stream &stream, + dng_info &info); + + virtual void ResizeDepthToMatchStage3 (dng_host &host); + + uint32 DepthFormat () const + { + return fDepthFormat; + } + + void SetDepthFormat (uint32 format) + { + fDepthFormat = format; + } + + const dng_urational & DepthNear () const + { + return fDepthNear; + } + + void SetDepthNear (const dng_urational &dist) + { + fDepthNear = dist; + } + + const dng_urational & DepthFar () const + { + return fDepthFar; + } + + void SetDepthFar (const dng_urational &dist) + { + fDepthFar = dist; + } + + uint32 DepthUnits () const + { + return fDepthUnits; + } + + void SetDepthUnits (uint32 units) + { + fDepthUnits = units; + } + + uint32 DepthMeasureType () const + { + return fDepthMeasureType; + } + + void SetDepthMeasureType (uint32 measure) + { + fDepthMeasureType = measure; + } + + // EnhanceParams API: + + const dng_string & EnhanceParams () const + { + return fEnhanceParams; + } + + void SetEnhanceParams (const dng_string &s) + { + fEnhanceParams = s; + } + + void SetEnhanceParams (const char *s) + { + fEnhanceParams.Set (s); + } + + protected: + + dng_negative (dng_host &host); + + virtual void Initialize (); + + virtual dng_linearization_info * MakeLinearizationInfo (); + + void NeedLinearizationInfo (); + + virtual dng_mosaic_info * MakeMosaicInfo (); + + void NeedMosaicInfo (); + + virtual void DoBuildStage2 (dng_host &host); + + virtual void DoPostOpcodeList2 (dng_host &host); + + virtual bool NeedDefloatStage2 (dng_host &host); + + virtual void DefloatStage2 (dng_host &host); + + virtual void DoInterpolateStage3 (dng_host &host, + int32 srcPlane, + dng_matrix *scaleTransforms); + + virtual void DoMergeStage3 (dng_host &host, + dng_matrix *scaleTransforms); + + virtual void DoBuildStage3 (dng_host &host, + int32 srcPlane, + dng_matrix *scaleTransforms); + + virtual void AdjustProfileForStage3 (); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_opcode_list.cpp b/dng/dng_opcode_list.cpp new file mode 100644 index 0000000..98ad4f3 --- /dev/null +++ b/dng/dng_opcode_list.cpp @@ -0,0 +1,272 @@ +/*****************************************************************************/ +// Copyright 2008-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_opcode_list.h" + +#include "dng_globals.h" +#include "dng_host.h" +#include "dng_memory_stream.h" +#include "dng_negative.h" +#include "dng_tag_values.h" +#include "dng_utils.h" + +#include + +/*****************************************************************************/ + +dng_opcode_list::dng_opcode_list (uint32 stage) + + : fList () + , fAlwaysApply (false) + , fStage (stage) + + { + + } + +/******************************************************************************/ + +dng_opcode_list::~dng_opcode_list () + { + + Clear (); + + } + +/******************************************************************************/ + +void dng_opcode_list::Clear () + { + + for (size_t index = 0; index < fList.size (); index++) + { + + if (fList [index]) + { + + delete fList [index]; + + fList [index] = NULL; + + } + + } + + fList.clear (); + + fAlwaysApply = false; + + } + +/******************************************************************************/ + +void dng_opcode_list::Swap (dng_opcode_list &otherList) + { + + fList.swap (otherList.fList); + + std::swap (fAlwaysApply, otherList.fAlwaysApply); + + std::swap (fStage, otherList.fStage); + + } + +/******************************************************************************/ + +uint32 dng_opcode_list::MinVersion (bool includeOptional) const + { + + uint32 result = dngVersion_None; + + for (size_t index = 0; index < fList.size (); index++) + { + + if (includeOptional || !fList [index]->Optional ()) + { + + result = Max_uint32 (result, fList [index]->MinVersion ()); + + } + + } + + return result; + + } + +/*****************************************************************************/ + +void dng_opcode_list::Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image) + { + + DNG_REQUIRE (image.Get (), "Bad image in dng_opcode_list::Apply"); + + for (uint32 index = 0; index < Count (); index++) + { + + dng_opcode &opcode (Entry (index)); + + if (opcode.AboutToApply (host, + negative, + image->Bounds (), + image->Planes ())) + { + + opcode.Apply (host, + negative, + image); + + } + + } + + } + +/*****************************************************************************/ + +void dng_opcode_list::Append (AutoPtr &opcode) + { + + if (opcode->OpcodeID () == dngOpcode_Private) + { + SetAlwaysApply (); + } + + opcode->SetStage (fStage); + + fList.push_back (NULL); + + fList [fList.size () - 1] = opcode.Release (); + + } + +/*****************************************************************************/ + +dng_memory_block * dng_opcode_list::Spool (dng_host &host) const + { + + if (IsEmpty ()) + { + return NULL; + } + + if (AlwaysApply ()) + { + ThrowProgramError (); + } + + dng_memory_stream stream (host.Allocator ()); + + stream.SetBigEndian (); + + stream.Put_uint32 ((uint32) fList.size ()); + + for (size_t index = 0; index < fList.size (); index++) + { + + stream.Put_uint32 (fList [index]->OpcodeID ()); + stream.Put_uint32 (fList [index]->MinVersion ()); + stream.Put_uint32 (fList [index]->Flags ()); + + fList [index]->PutData (stream); + + } + + return stream.AsMemoryBlock (host.Allocator ()); + + } + +/*****************************************************************************/ + +void dng_opcode_list::FingerprintToStream (dng_stream &stream) const + { + + if (IsEmpty ()) + { + return; + } + + stream.Put_uint32 ((uint32) fList.size ()); + + for (size_t index = 0; index < fList.size (); index++) + { + + stream.Put_uint32 (fList [index]->OpcodeID ()); + stream.Put_uint32 (fList [index]->MinVersion ()); + stream.Put_uint32 (fList [index]->Flags ()); + + if (fList [index]->OpcodeID () != dngOpcode_Private) + { + + fList [index]->PutData (stream); + + } + + } + + } + +/*****************************************************************************/ + +void dng_opcode_list::Parse (dng_host &host, + dng_stream &stream, + uint32 byteCount, + uint64 streamOffset) + { + + Clear (); + + TempBigEndian tempBigEndian (stream); + + stream.SetReadPosition (streamOffset); + + uint32 count = stream.Get_uint32 (); + + #if qDNGValidate + + if (gVerbose) + { + + if (count == 1) + { + printf ("1 opcode\n"); + } + + else + { + printf ("%u opcodes\n", (unsigned) count); + } + + } + + #endif + + for (uint32 index = 0; index < count; index++) + { + + uint32 opcodeID = stream.Get_uint32 (); + + AutoPtr opcode (host.Make_dng_opcode (opcodeID, + stream)); + + Append (opcode); + + } + + if (stream.Position () != streamOffset + byteCount) + { + + ThrowBadFormat ("Error parsing opcode list"); + + } + + } + +/*****************************************************************************/ diff --git a/dng/dng_opcode_list.h b/dng/dng_opcode_list.h new file mode 100644 index 0000000..9a4d192 --- /dev/null +++ b/dng/dng_opcode_list.h @@ -0,0 +1,153 @@ +/*****************************************************************************/ +// Copyright 2008-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * List of opcodes. + */ + +/*****************************************************************************/ + +#ifndef __dng_opcode_list__ +#define __dng_opcode_list__ + +/*****************************************************************************/ + +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_opcodes.h" +#include "dng_uncopyable.h" + +#include + +/*****************************************************************************/ + +/// A list of opcodes. + +class dng_opcode_list: private dng_uncopyable + { + + private: + + dng_std_vector fList; + + bool fAlwaysApply; + + uint32 fStage; + + public: + + /// Create an empty opcode list for the specific image stage (1, 2, or 3). + + dng_opcode_list (uint32 stage); + + ~dng_opcode_list (); + + /// Is the opcode list empty? + + bool IsEmpty () const + { + return fList.size () == 0; + } + + /// Does the list contain at least 1 opcode? + + bool NotEmpty () const + { + return !IsEmpty (); + } + + /// Should the opcode list always be applied to the image? + + bool AlwaysApply () const + { + return fAlwaysApply && NotEmpty (); + } + + /// Set internal flag to indicate this opcode list should always be + /// applied. + + void SetAlwaysApply () + { + fAlwaysApply = true; + } + + /// The number of opcodes in this list. + + uint32 Count () const + { + return (uint32) fList.size (); + } + + /// Retrieve read/write opcode by index (must be in the range 0 to Count + /// () - 1). + + dng_opcode & Entry (uint32 index) + { + return *fList [index]; + } + + /// Retrieve read-only opcode by index (must be in the range 0 to Count + /// () - 1). + + const dng_opcode & Entry (uint32 index) const + { + return *fList [index]; + } + + /// Remove all opcodes from the list. + + void Clear (); + + /// Swap two opcode lists. + + void Swap (dng_opcode_list &otherList); + + /// Return minimum DNG version required to support all opcodes in this + /// list. If includeOptional is set to true, then this calculation will + /// include optional opcodes. + + uint32 MinVersion (bool includeOptional) const; + + /// Apply this opcode list to the specified image with corresponding + /// negative. + + void Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image); + + /// Append the specified opcode to this list. + + void Append (AutoPtr &opcode); + + /// Serialize this opcode list to a block of memory. The caller is + /// responsible for deleting this block. + + dng_memory_block * Spool (dng_host &host) const; + + /// Write a fingerprint of this opcode list to the specified stream. + + void FingerprintToStream (dng_stream &stream) const; + + /// Read an opcode list from the specified stream, starting at the + /// specified offset (streamOffset, in bytes). byteCount is provided for + /// error checking purposes. A bad format exception + /// will be thrown if the length of the opcode stream does not exactly + /// match byteCount. + + void Parse (dng_host &host, + dng_stream &stream, + uint32 byteCount, + uint64 streamOffset); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_opcodes.cpp b/dng/dng_opcodes.cpp new file mode 100644 index 0000000..e25949e --- /dev/null +++ b/dng/dng_opcodes.cpp @@ -0,0 +1,563 @@ +/*****************************************************************************/ +// Copyright 2008-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_opcodes.h" + +#include "dng_bottlenecks.h" +#include "dng_exceptions.h" +#include "dng_filter_task.h" +#include "dng_globals.h" +#include "dng_host.h" +#include "dng_image.h" +#include "dng_negative.h" +#include "dng_parse_utils.h" +#include "dng_stream.h" +#include "dng_tag_values.h" + +/*****************************************************************************/ + +dng_opcode::dng_opcode (uint32 opcodeID, + uint32 minVersion, + uint32 flags) + + : fOpcodeID (opcodeID) + , fMinVersion (minVersion) + , fFlags (flags) + , fWasReadFromStream (false) + , fStage (0) + + { + + } + +/*****************************************************************************/ + +dng_opcode::dng_opcode (uint32 opcodeID, + dng_stream &stream, + const char *name) + + : fOpcodeID (opcodeID) + , fMinVersion (0) + , fFlags (0) + , fWasReadFromStream (true) + , fStage (0) + + { + + fMinVersion = stream.Get_uint32 (); + fFlags = stream.Get_uint32 (); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("\nOpcode: "); + + if (name) + { + printf ("%s", name); + } + else + { + printf ("Unknown (%u)", (unsigned) opcodeID); + } + + printf (", minVersion = %u.%u.%u.%u", + (unsigned) ((fMinVersion >> 24) & 0x0FF), + (unsigned) ((fMinVersion >> 16) & 0x0FF), + (unsigned) ((fMinVersion >> 8) & 0x0FF), + (unsigned) ((fMinVersion ) & 0x0FF)); + + printf (", flags = %u\n", (unsigned) fFlags); + + } + + #else + + (void) name; + + #endif + + } + +/*****************************************************************************/ + +dng_opcode::~dng_opcode () + { + + } + +/*****************************************************************************/ + +void dng_opcode::PutData (dng_stream &stream) const + { + + // No data by default + + stream.Put_uint32 (0); + + } + +/*****************************************************************************/ + +bool dng_opcode::AboutToApply (dng_host &host, + dng_negative &negative, + const dng_rect &imageBounds, + uint32 imagePlanes) + { + + if (SkipIfPreview () && host.ForPreview ()) + { + + negative.SetIsPreview (true); + + } + + else if (MinVersion () > dngVersion_Current && + WasReadFromStream ()) + { + + if (!Optional ()) + { + + // Somebody screwed up computing the DNGBackwardVersion... + + ThrowBadFormat (); + + } + + } + + else if (!IsValidForNegative (negative)) + { + + ThrowBadFormat (); + + } + + else if (!IsNOP ()) + { + + DoAboutToApply (host, + negative, + imageBounds, + imagePlanes); + + return true; + + } + + return false; + + } + +/*****************************************************************************/ + +dng_opcode_Unknown::dng_opcode_Unknown (dng_host &host, + uint32 opcodeID, + dng_stream &stream) + + : dng_opcode (opcodeID, + stream, + NULL) + + , fData () + + { + + uint32 size = stream.Get_uint32 (); + + if (size) + { + + fData.Reset (host.Allocate (size)); + + stream.Get (fData->Buffer (), + fData->LogicalSize ()); + + #if qDNGValidate + + if (gVerbose) + { + + DumpHexAscii (fData->Buffer_uint8 (), + fData->LogicalSize ()); + + } + + #endif + + } + + } + +/*****************************************************************************/ + +void dng_opcode_Unknown::PutData (dng_stream &stream) const + { + + if (fData.Get ()) + { + + stream.Put_uint32 (fData->LogicalSize ()); + + stream.Put (fData->Buffer (), + fData->LogicalSize ()); + + } + + else + { + + stream.Put_uint32 (0); + + } + + } + +/*****************************************************************************/ + +void dng_opcode_Unknown::Apply (dng_host & /* host */, + dng_negative & /* negative */, + AutoPtr & /* image */) + { + + // We should never need to apply an unknown opcode. + + if (!Optional ()) + { + + ThrowBadFormat (); + + } + + } + +/*****************************************************************************/ + +class dng_filter_opcode_task: public dng_filter_task + { + + private: + + dng_filter_opcode &fOpcode; + + dng_negative &fNegative; + + public: + + dng_filter_opcode_task (dng_filter_opcode &opcode, + dng_negative &negative, + const dng_image &srcImage, + dng_image &dstImage) + + : dng_filter_task ("dng_filter_opcode_task", + srcImage, + dstImage) + + , fOpcode (opcode) + , fNegative (negative) + + { + + fSrcPixelType = fOpcode.BufferPixelType (srcImage.PixelType ()); + + fDstPixelType = fSrcPixelType; + + fSrcRepeat = opcode.SrcRepeat (); + + } + + virtual dng_rect SrcArea (const dng_rect &dstArea) + { + + return fOpcode.SrcArea (dstArea, + fDstImage.Bounds ()); + + } + + virtual dng_point SrcTileSize (const dng_point &dstTileSize) + { + + return fOpcode.SrcTileSize (dstTileSize, + fDstImage.Bounds ()); + + } + + virtual void ProcessArea (uint32 threadIndex, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer) + { + + fOpcode.ProcessArea (fNegative, + threadIndex, + srcBuffer, + dstBuffer, + dstBuffer.Area (), + fDstImage.Bounds ()); + + } + + virtual void Start (uint32 threadCount, + const dng_rect &dstArea, + const dng_point &tileSize, + dng_memory_allocator *allocator, + dng_abort_sniffer *sniffer) + { + + dng_filter_task::Start (threadCount, + dstArea, + tileSize, + allocator, + sniffer); + + fOpcode.Prepare (fNegative, + threadCount, + tileSize, + fDstImage.Bounds (), + fDstImage.Planes (), + fDstPixelType, + *allocator); + + } + + }; + +/*****************************************************************************/ + +dng_filter_opcode::dng_filter_opcode (uint32 opcodeID, + uint32 minVersion, + uint32 flags) + + : dng_opcode (opcodeID, + minVersion, + flags) + + { + + } + +/*****************************************************************************/ + +dng_filter_opcode::dng_filter_opcode (uint32 opcodeID, + dng_stream &stream, + const char *name) + + : dng_opcode (opcodeID, + stream, + name) + + { + + } + +/*****************************************************************************/ + +void dng_filter_opcode::Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image) + { + + dng_rect modifiedBounds = ModifiedBounds (image->Bounds ()); + + if (modifiedBounds.NotEmpty ()) + { + + // Allocate destination image. + + AutoPtr dstImage; + + // If we are processing the entire image, allocate an + // undefined image. + + if (modifiedBounds == image->Bounds ()) + { + + dstImage.Reset (host.Make_dng_image (image->Bounds (), + image->Planes (), + image->PixelType ())); + + } + + // Else start with a clone of the existing image. + + else + { + + dstImage.Reset (image->Clone ()); + + } + + // Filter the image. + + dng_filter_opcode_task task (*this, + negative, + *image, + *dstImage); + + host.PerformAreaTask (task, + modifiedBounds); + + // Return the new image. + + image.Reset (dstImage.Release ()); + + } + + } + +/*****************************************************************************/ + +class dng_inplace_opcode_task: public dng_area_task + { + + private: + + dng_inplace_opcode &fOpcode; + + dng_negative &fNegative; + + dng_image &fImage; + + uint32 fPixelType; + + AutoPtr fBuffer [kMaxMPThreads]; + + public: + + dng_inplace_opcode_task (dng_inplace_opcode &opcode, + dng_negative &negative, + dng_image &image) + + : dng_area_task ("dng_inplace_opcode_task") + + , fOpcode (opcode) + , fNegative (negative) + , fImage (image) + , fPixelType (opcode.BufferPixelType (image.PixelType ())) + + { + + } + + virtual void Start (uint32 threadCount, + const dng_rect & /* dstArea */, + const dng_point &tileSize, + dng_memory_allocator *allocator, + dng_abort_sniffer * /* sniffer */) + { + + uint32 bufferSize = ComputeBufferSize (fPixelType, + tileSize, + fImage.Planes (), + padSIMDBytes); + + for (uint32 threadIndex = 0; threadIndex < threadCount; threadIndex++) + { + + fBuffer [threadIndex] . Reset (allocator->Allocate (bufferSize)); + + } + + fOpcode.Prepare (fNegative, + threadCount, + tileSize, + fImage.Bounds (), + fImage.Planes (), + fPixelType, + *allocator); + + } + + virtual void Process (uint32 threadIndex, + const dng_rect &tile, + dng_abort_sniffer * /* sniffer */) + { + + // Setup buffer. + + dng_pixel_buffer buffer (tile, + 0, + fImage.Planes (), + fPixelType, + pcRowInterleavedAlignSIMD, + fBuffer [threadIndex]->Buffer ()); + + // Get source pixels. + + fImage.Get (buffer); + + // Process area. + + fOpcode.ProcessArea (fNegative, + threadIndex, + buffer, + tile, + fImage.Bounds ()); + + // Save result pixels. + + fImage.Put (buffer); + + } + + }; + +/*****************************************************************************/ + +dng_inplace_opcode::dng_inplace_opcode (uint32 opcodeID, + uint32 minVersion, + uint32 flags) + + : dng_opcode (opcodeID, + minVersion, + flags) + + { + + } + +/*****************************************************************************/ + +dng_inplace_opcode::dng_inplace_opcode (uint32 opcodeID, + dng_stream &stream, + const char *name) + + : dng_opcode (opcodeID, + stream, + name) + + { + + } + +/*****************************************************************************/ + +void dng_inplace_opcode::Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image) + { + + dng_rect modifiedBounds = ModifiedBounds (image->Bounds ()); + + if (modifiedBounds.NotEmpty ()) + { + + dng_inplace_opcode_task task (*this, + negative, + *image); + + host.PerformAreaTask (task, + modifiedBounds); + + } + + } + +/*****************************************************************************/ diff --git a/dng/dng_opcodes.h b/dng/dng_opcodes.h new file mode 100644 index 0000000..77fd5d6 --- /dev/null +++ b/dng/dng_opcodes.h @@ -0,0 +1,523 @@ +/*****************************************************************************/ +// Copyright 2008-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Base class and common data structures for opcodes (introduced in DNG 1.3). + */ + +/*****************************************************************************/ + +#ifndef __dng_opcodes__ +#define __dng_opcodes__ + +/*****************************************************************************/ + +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_rect.h" +#include "dng_types.h" + +/*****************************************************************************/ + +/// \brief List of supported opcodes (by ID). + +enum dng_opcode_id + { + + // Internal use only opcode. Never written to DNGs. + + dngOpcode_Private = 0, + + // Warp image to correct distortion and lateral chromatic aberration for + // rectilinear lenses. + + dngOpcode_WarpRectilinear = 1, + + // Warp image to correction distortion for fisheye lenses (i.e., map the + // fisheye projection to a perspective projection). + + dngOpcode_WarpFisheye = 2, + + // Radial vignette correction. + + dngOpcode_FixVignetteRadial = 3, + + // Patch bad Bayer pixels which are marked with a special value in the image. + + dngOpcode_FixBadPixelsConstant = 4, + + // Patch bad Bayer pixels/rectangles at a list of specified coordinates. + + dngOpcode_FixBadPixelsList = 5, + + // Trim image to specified bounds. + + dngOpcode_TrimBounds = 6, + + // Map an area through a 16-bit LUT. + + dngOpcode_MapTable = 7, + + // Map an area using a polynomial function. + + dngOpcode_MapPolynomial = 8, + + // Apply a gain map to an area. + + dngOpcode_GainMap = 9, + + // Apply a per-row delta to an area. + + dngOpcode_DeltaPerRow = 10, + + // Apply a per-column delta to an area. + + dngOpcode_DeltaPerColumn = 11, + + // Apply a per-row scale to an area. + + dngOpcode_ScalePerRow = 12, + + // Apply a per-column scale to an area. + + dngOpcode_ScalePerColumn = 13 + + }; + +/*****************************************************************************/ + +/// \brief Virtual base class for opcode. + +class dng_opcode + { + + public: + + /// Opcode flags. + + enum + { + kFlag_None = 0, //!< No flag. + kFlag_Optional = 1, //!< This opcode is optional. + kFlag_SkipIfPreview = 2 //!< May skip opcode for preview images. + }; + + private: + + uint32 fOpcodeID; + + uint32 fMinVersion; + + uint32 fFlags; + + bool fWasReadFromStream; + + uint32 fStage; + + protected: + + dng_opcode (uint32 opcodeID, + uint32 minVersion, + uint32 flags); + + dng_opcode (uint32 opcodeID, + dng_stream &stream, + const char *name); + + // This helper routine will be called by AboutToApply if AboutToApply + // passes its internal checking and plans to return true. This is an + // opportunity for subclasses to perform some internal preparation + // based on the negative, image bounds, and number of image planes. + + virtual void DoAboutToApply (dng_host & /* host */, + dng_negative & /* negative */, + const dng_rect & /* imageBounds */, + uint32 /* imagePlanes */) + { + } + + public: + + virtual ~dng_opcode (); + + /// The ID of this opcode. + + uint32 OpcodeID () const + { + return fOpcodeID; + } + + /// The first DNG version that supports this opcode. + + uint32 MinVersion () const + { + return fMinVersion; + } + + /// The flags for this opcode. + + uint32 Flags () const + { + return fFlags; + } + + /// Is this opcode optional? + + bool Optional () const + { + return (Flags () & kFlag_Optional) != 0; + } + + /// Should the opcode be skipped when rendering preview images? + + bool SkipIfPreview () const + { + return (Flags () & kFlag_SkipIfPreview) != 0; + } + + /// Was this opcode read from a data stream? + + bool WasReadFromStream () const + { + return fWasReadFromStream; + } + + /// Which image processing stage (1, 2, 3) is associated with this + /// opcode? + + uint32 Stage () const + { + return fStage; + } + + /// Set the image processing stage (1, 2, 3) for this opcode. Stage 1 is + /// the original image data, including masked areas. Stage 2 is + /// linearized image data and trimmed to the active area. Stage 3 is + /// demosaiced and trimmed to the active area. + + void SetStage (uint32 stage) + { + fStage = stage; + } + + /// Is the opcode a NOP (i.e., does nothing)? An opcode could be a NOP + /// for some specific parameters. + + virtual bool IsNOP () const + { + return false; + } + + /// Is this opcode valid for the specified negative? + + virtual bool IsValidForNegative (const dng_negative & /* negative */) const + { + return true; + } + + /// Write opcode to a stream. + /// \param stream The stream to which to write the opcode data. + + virtual void PutData (dng_stream &stream) const; + + /// Perform error checking prior to applying this opcode to the + /// specified negative. Returns true if this opcode should be applied to + /// the negative, false otherwise. + + bool AboutToApply (dng_host &host, + dng_negative &negative, + const dng_rect &imageBounds, + uint32 imagePlanes); + + /// Apply this opcode to the specified image with associated negative. + + virtual void Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image) = 0; + + }; + +/*****************************************************************************/ + +/// \brief Class to represent unknown opcodes (e.g, opcodes defined in future +/// DNG versions). + +class dng_opcode_Unknown: public dng_opcode + { + + private: + + AutoPtr fData; + + public: + + dng_opcode_Unknown (dng_host &host, + uint32 opcodeID, + dng_stream &stream); + + virtual void PutData (dng_stream &stream) const; + + virtual void Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image); + + }; + +/*****************************************************************************/ + +/// \brief Class to represent a filter opcode, such as a convolution. + +class dng_filter_opcode: public dng_opcode + { + + protected: + + dng_filter_opcode (uint32 opcodeID, + uint32 minVersion, + uint32 flags); + + dng_filter_opcode (uint32 opcodeID, + dng_stream &stream, + const char *name); + + public: + + /// The pixel data type of this opcode. + + virtual uint32 BufferPixelType (uint32 imagePixelType) + { + return imagePixelType; + } + + /// The adjusted bounds (processing area) of this opcode. It is limited to + /// the intersection of the specified image area and the GainMap area. + + virtual dng_rect ModifiedBounds (const dng_rect &imageBounds) + { + return imageBounds; + } + + /// Returns the width and height (in pixels) of the repeating mosaic pattern. + + virtual dng_point SrcRepeat () + { + return dng_point (1, 1); + } + + /// Returns the source pixel area needed to process a destination pixel area + /// that lies within the specified bounds. + /// \param dstArea The destination pixel area to be computed. + /// \param imageBounds The overall image area (dstArea will lie within these + /// bounds). + /// \retval The source pixel area needed to process the specified dstArea. + + virtual dng_rect SrcArea (const dng_rect &dstArea, + const dng_rect &imageBounds) + { + (void) imageBounds; + return dstArea; + } + + /// Given a destination tile size, calculate input tile size. Simlar to + /// SrcArea, and should seldom be overridden. + /// + /// \param dstTileSize The destination tile size that is targeted for output. + /// + /// \param imageBounds The image bounds (the destination tile will + /// always lie within these bounds). + /// + /// \retval The source tile size needed to compute a tile of the destination + /// size. + + virtual dng_point SrcTileSize (const dng_point &dstTileSize, + const dng_rect &imageBounds) + { + return SrcArea (dng_rect (dstTileSize), + imageBounds).Size (); + } + + /// Startup method called before any processing is performed on pixel areas. + /// It can be used to allocate (per-thread) memory and setup tasks. + /// + /// \param negative The negative object to be processed. + /// + /// \param threadCount Total number of threads that will be used for + /// processing. Less than or equal to MaxThreads. + /// + /// \param tileSize Size of source tiles which will be processed. (Not all + /// tiles will be this size due to edge conditions.) + /// + /// \param imageBounds Total size of image to be processed. + /// + /// \param imagePlanes Number of planes in the image. Less than or equal to + /// kMaxColorPlanes. + /// + /// \param bufferPixelType Pixel type of image buffer (see dng_tag_types.h). + /// + /// \param allocator dng_memory_allocator to use for allocating temporary + /// buffers, etc. + + virtual void Prepare (dng_negative &negative, + uint32 threadCount, + const dng_point &tileSize, + const dng_rect &imageBounds, + uint32 imagePlanes, + uint32 bufferPixelType, + dng_memory_allocator &allocator) + { + (void) negative; + (void) threadCount; + (void) tileSize; + (void) imageBounds; + (void) imagePlanes; + (void) bufferPixelType; + (void) allocator; + } + + /// Implements filtering operation from one buffer to another. Source + /// and destination pixels are set up in member fields of this class. + /// Ideally, no allocation should be done in this routine. + /// + /// \param negative The negative associated with the pixels to be + /// processed. + /// + /// \param threadIndex The thread on which this routine is being called, + /// between 0 and threadCount - 1 for the threadCount passed to Prepare + /// method. + /// + /// \param srcBuffer Input area and source pixels. + /// + /// \param dstBuffer Destination pixels. + /// + /// \param dstArea Destination pixel processing area. + /// + /// \param imageBounds Total image area to be processed; dstArea will + /// always lie within these bounds. + + virtual void ProcessArea (dng_negative &negative, + uint32 threadIndex, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer, + const dng_rect &dstArea, + const dng_rect &imageBounds) = 0; + + virtual void Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image); + + }; + +/*****************************************************************************/ + +/// \brief Class to represent an in-place (i.e., pointwise, per-pixel) opcode, +/// such as a global tone curve. + +class dng_inplace_opcode: public dng_opcode + { + + protected: + + dng_inplace_opcode (uint32 opcodeID, + uint32 minVersion, + uint32 flags); + + dng_inplace_opcode (uint32 opcodeID, + dng_stream &stream, + const char *name); + + public: + + /// The pixel data type of this opcode. + + virtual uint32 BufferPixelType (uint32 imagePixelType) + { + return imagePixelType; + } + + /// The adjusted bounds (processing area) of this opcode. It is limited to + /// the intersection of the specified image area and the GainMap area. + + virtual dng_rect ModifiedBounds (const dng_rect &imageBounds) + { + return imageBounds; + } + + /// Startup method called before any processing is performed on pixel areas. + /// It can be used to allocate (per-thread) memory and setup tasks. + /// + /// \param negative The negative object to be processed. + /// + /// \param threadCount Total number of threads that will be used for + /// processing. Less than or equal to MaxThreads. + /// + /// \param tileSize Size of source tiles which will be processed. (Not all + /// tiles will be this size due to edge conditions.) + /// + /// \param imageBounds Total size of image to be processed. + /// + /// \param imagePlanes Number of planes in the image. Less than or equal to + /// kMaxColorPlanes. + /// + /// \param bufferPixelType Pixel type of image buffer (see dng_tag_types.h). + /// + /// \param allocator dng_memory_allocator to use for allocating temporary + /// buffers, etc. + + virtual void Prepare (dng_negative &negative, + uint32 threadCount, + const dng_point &tileSize, + const dng_rect &imageBounds, + uint32 imagePlanes, + uint32 bufferPixelType, + dng_memory_allocator &allocator) + { + (void) negative; + (void) threadCount; + (void) tileSize; + (void) imageBounds; + (void) imagePlanes; + (void) bufferPixelType; + (void) allocator; + } + + /// Implements image processing operation in a single buffer. The source + /// pixels are provided as input to the buffer, and this routine + /// calculates and writes the destination pixels to the same buffer. + /// Ideally, no allocation should be done in this routine. + /// + /// \param negative The negative associated with the pixels to be + /// processed. + /// + /// \param threadIndex The thread on which this routine is being called, + /// between 0 and threadCount - 1 for the threadCount passed to Prepare + /// method. + /// + /// \param buffer Source and Destination pixels. + /// + /// \param dstArea Destination pixel processing area. + /// + /// \param imageBounds Total image area to be processed; dstArea will + /// always lie within these bounds. + + virtual void ProcessArea (dng_negative &negative, + uint32 threadIndex, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect &imageBounds) = 0; + + virtual void Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_orientation.cpp b/dng/dng_orientation.cpp new file mode 100644 index 0000000..99c92c3 --- /dev/null +++ b/dng/dng_orientation.cpp @@ -0,0 +1,391 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_orientation.h" + +/*****************************************************************************/ + +void dng_orientation::SetTIFF (uint32 tiff) + { + + switch (tiff) + { + + case 1: + { + fAdobeOrientation = kNormal; + break; + } + + case 2: + { + fAdobeOrientation = kMirror; + break; + } + + case 3: + { + fAdobeOrientation = kRotate180; + break; + } + + case 4: + { + fAdobeOrientation = kMirror180; + break; + } + + case 5: + { + fAdobeOrientation = kMirror90CCW; + break; + } + + case 6: + { + fAdobeOrientation = kRotate90CW; + break; + } + + case 7: + { + fAdobeOrientation = kMirror90CW; + break; + } + + case 8: + { + fAdobeOrientation = kRotate90CCW; + break; + } + + case 9: + { + fAdobeOrientation = kUnknown; + break; + } + + default: + { + fAdobeOrientation = kNormal; + } + + } + + } + +/*****************************************************************************/ + +uint32 dng_orientation::GetTIFF () const + { + + switch (fAdobeOrientation) + { + + case kNormal: + { + return 1; + } + + case kMirror: + { + return 2; + } + + case kRotate180: + { + return 3; + } + + case kMirror180: + { + return 4; + } + + case kMirror90CCW: + { + return 5; + } + + case kRotate90CW: + { + return 6; + } + + case kMirror90CW: + { + return 7; + } + + case kRotate90CCW: + { + return 8; + } + + case kUnknown: + { + return 9; + } + + default: + break; + + } + + return 1; + + } + +/*****************************************************************************/ + +bool dng_orientation::FlipD () const + { + + return (fAdobeOrientation & 1) != 0; + + } + +/*****************************************************************************/ + +bool dng_orientation::FlipH () const + { + + if (fAdobeOrientation & 4) + return (fAdobeOrientation & 2) == 0; + + else + return (fAdobeOrientation & 2) != 0; + + } + +/*****************************************************************************/ + +bool dng_orientation::FlipV () const + { + + if (fAdobeOrientation & 4) + return FlipD () == FlipH (); + + else + return FlipD () != FlipH (); + + } + +/*****************************************************************************/ + +dng_orientation dng_orientation::operator- () const + { + + uint32 x = GetAdobe (); + + if ((x & 5) == 5) + { + + x ^= 2; + + } + + dng_orientation result; + + result.SetAdobe (((4 - x) & 3) | (x & 4)); + + return result; + + } + +/*****************************************************************************/ + +dng_orientation dng_orientation::operator+ (const dng_orientation &b) const + { + + uint32 x = GetAdobe (); + + uint32 y = b.GetAdobe (); + + if (y & 4) + { + + if (x & 1) + x ^= 6; + else + x ^= 4; + + } + + dng_orientation result; + + result.SetAdobe (((x + y) & 3) | (x & 4)); + + return result; + + } + +/*****************************************************************************/ + +bool dng_orientation::CalcForwardMatrix3by3 (dng_matrix &orientationMatrix, + const bool horizontalFirstRow) const + { + + bool hasOrient = false; + + orientationMatrix.SetIdentity (3); + + if (FlipH ()) + { + + hasOrient = true; + + if (horizontalFirstRow) + { + + orientationMatrix = dng_matrix_3by3 (-1.0, 0.0, 1.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0) * + orientationMatrix; + + } + + else + { + + orientationMatrix = dng_matrix_3by3 (1.0, 0.0, 0.0, + 0.0, -1.0, 1.0, + 0.0, 0.0, 1.0) * + orientationMatrix; + + } + + } + + if (FlipV ()) + { + + hasOrient = true; + + if (horizontalFirstRow) + { + + orientationMatrix = dng_matrix_3by3 (1.0, 0.0, 0.0, + 0.0, -1.0, 1.0, + 0.0, 0.0, 1.0) * + orientationMatrix; + + } + + else + { + + orientationMatrix = dng_matrix_3by3 (-1.0, 0.0, 1.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0) * + orientationMatrix; + + } + + } + + if (FlipD ()) + { + + hasOrient = true; + + orientationMatrix = dng_matrix_3by3 (0.0, 1.0, 0.0, + 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0) * + orientationMatrix; + + } + + return hasOrient; + + } + +/*****************************************************************************/ + +bool dng_orientation::CalcForwardMatrix4by4 (dng_matrix &orientationMatrix, + const bool horizontalFirstRow) const + { + + bool hasOrient = false; + + orientationMatrix.SetIdentity (4); + + if (FlipH ()) + { + + hasOrient = true; + + if (horizontalFirstRow) + { + + orientationMatrix = dng_matrix_4by4 (-1.0, 0.0, 0.0, 1.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0); + + } + + else + { + + orientationMatrix = dng_matrix_4by4 (1.0, 0.0, 0.0, 0.0, + 0.0, -1.0, 0.0, 1.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0); + + } + + } + + if (FlipV ()) + { + + hasOrient = true; + + if (horizontalFirstRow) + { + + orientationMatrix = dng_matrix_4by4 (1.0, 0.0, 0.0, 0.0, + 0.0, -1.0, 0.0, 1.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0) * + orientationMatrix; + + } + + else + { + + orientationMatrix = dng_matrix_4by4 (-1.0, 0.0, 0.0, 1.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0) * + orientationMatrix; + + } + + } + + if (FlipD ()) + { + + hasOrient = true; + + orientationMatrix = dng_matrix_4by4 (0.0, 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0) * + orientationMatrix; + + } + + return hasOrient; + + } + +/*****************************************************************************/ diff --git a/dng/dng_orientation.h b/dng/dng_orientation.h new file mode 100644 index 0000000..5bf991b --- /dev/null +++ b/dng/dng_orientation.h @@ -0,0 +1,197 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef __dng_orientation__ +#define __dng_orientation__ + +/******************************************************************************/ + +#include "dng_matrix.h" +#include "dng_types.h" + +/******************************************************************************/ + +class dng_orientation + { + + private: + + // We internally use an orientation encoding ("Adobe") that is + // different than the TIFF orientation encoding ("TIFF"). + + uint32 fAdobeOrientation; + + public: + + enum + { + kNormal = 0, + kRotate90CW = 1, + kRotate180 = 2, + kRotate90CCW = 3, + kMirror = 4, + kMirror90CW = 5, + kMirror180 = 6, + kMirror90CCW = 7, + kUnknown = 8 + }; + + dng_orientation () + + : fAdobeOrientation (kNormal) + + { + } + + void SetAdobe (uint32 adobe) + { + fAdobeOrientation = adobe; + } + + uint32 GetAdobe () const + { + return fAdobeOrientation; + } + + void SetTIFF (uint32 tiff); + + uint32 GetTIFF () const; + + static dng_orientation AdobeToDNG (uint32 adobe) + { + + dng_orientation result; + + result.SetAdobe (adobe); + + return result; + + } + + static dng_orientation TIFFtoDNG (uint32 tiff) + { + + dng_orientation result; + + result.SetTIFF (tiff); + + return result; + + } + + static dng_orientation Normal () + { + return AdobeToDNG (kNormal); + } + + static dng_orientation Rotate90CW () + { + return AdobeToDNG (kRotate90CW); + } + + static dng_orientation Rotate180 () + { + return AdobeToDNG (kRotate180); + } + + static dng_orientation Rotate90CCW () + { + return AdobeToDNG (kRotate90CCW); + } + + static dng_orientation Mirror () + { + return AdobeToDNG (kMirror); + } + + static dng_orientation Mirror90CW () + { + return AdobeToDNG (kMirror90CW); + } + + static dng_orientation Mirror180 () + { + return AdobeToDNG (kMirror180); + } + + static dng_orientation Mirror90CCW () + { + return AdobeToDNG (kMirror90CCW); + } + + static dng_orientation Unknown () + { + return AdobeToDNG (kUnknown); + } + + bool IsValid () const + { + return fAdobeOrientation < kUnknown; + } + + bool NotValid () const + { + return !IsValid (); + } + + bool FlipD () const; + + bool FlipH () const; + + bool FlipV () const; + + bool operator== (const dng_orientation &b) const + { + return fAdobeOrientation == b.fAdobeOrientation; + } + + bool operator!= (const dng_orientation &b) const + { + return !(*this == b); + } + + dng_orientation operator- () const; + + dng_orientation operator+ (const dng_orientation &b) const; + + dng_orientation operator- (const dng_orientation &b) const + { + return (*this) + (-b); + } + + void operator+= (const dng_orientation &b) + { + *this = *this + b; + } + + void operator-= (const dng_orientation &b) + { + *this = *this - b; + } + + // If horizontalFirstRow is true, then the x (horizontal h) component + // of the transform will be in the first row of the resulting matrix, + // and the y (vertical v) component will be in the second row. + // + // If horizontalFirstRow is false, then the y (vertical v) component + // of the transform will be in the first row of the resulting matrix, + // and the x (horizontal h) component will be in the second row. + + bool CalcForwardMatrix3by3 (dng_matrix &matrix, + bool horizontalFirstRow) const; + + bool CalcForwardMatrix4by4 (dng_matrix &matrix, + bool horizontalFirstRow) const; + + }; + +/******************************************************************************/ + +#endif + +/******************************************************************************/ diff --git a/dng/dng_parse_utils.cpp b/dng/dng_parse_utils.cpp new file mode 100644 index 0000000..04eb4b6 --- /dev/null +++ b/dng/dng_parse_utils.cpp @@ -0,0 +1,3431 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_parse_utils.h" + +#include "dng_date_time.h" +#include "dng_globals.h" +#include "dng_ifd.h" +#include "dng_tag_codes.h" +#include "dng_tag_types.h" +#include "dng_tag_values.h" +#include "dng_types.h" +#include "dng_stream.h" +#include "dng_exceptions.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +#if qDNGValidate + +/*****************************************************************************/ + +struct dng_name_table + { + uint32 key; + const char *name; + }; + +/*****************************************************************************/ + +static const char * LookupName (uint32 key, + const dng_name_table *table, + uint32 table_entries) + { + + for (uint32 index = 0; index < table_entries; index++) + { + + if (key == table [index] . key) + { + + return table [index] . name; + + } + + } + + return NULL; + + } + +/*****************************************************************************/ + +const char * LookupParentCode (uint32 parentCode) + { + + const dng_name_table kParentCodeNames [] = + { + { 0, "IFD 0" }, + { tcExifIFD, "Exif IFD" }, + { tcGPSInfo, "GPS IFD" }, + { tcInteroperabilityIFD, "Interoperability IFD" }, + { tcKodakDCRPrivateIFD, "Kodak DCR Private IFD" }, + { tcKodakKDCPrivateIFD, "Kodak KDC Private IFD" }, + { tcCanonMakerNote, "Canon MakerNote" }, + { tcEpsonMakerNote, "Epson MakerNote" }, + { tcFujiMakerNote, "Fuji MakerNote" }, + { tcHasselbladMakerNote, "Hasselblad MakerNote" }, + { tcKodakMakerNote, "Kodak MakerNote" }, + { tcKodakMakerNote65280, "Kodak MakerNote 65280" }, + { tcLeicaMakerNote, "Leica MakerNote" }, + { tcMamiyaMakerNote, "Mamiya MakerNote" }, + { tcMinoltaMakerNote, "Minolta MakerNote" }, + { tcNikonMakerNote, "Nikon MakerNote" }, + { tcOlympusMakerNote, "Olympus MakerNote" }, + { tcOlympusMakerNote8208, "Olympus MakerNote 8208" }, + { tcOlympusMakerNote8224, "Olympus MakerNote 8224" }, + { tcOlympusMakerNote8240, "Olympus MakerNote 8240" }, + { tcOlympusMakerNote8256, "Olympus MakerNote 8256" }, + { tcOlympusMakerNote8272, "Olympus MakerNote 8272" }, + { tcOlympusMakerNote12288, "Olympus MakerNote 12288" }, + { tcPanasonicMakerNote, "Panasonic MakerNote" }, + { tcPentaxMakerNote, "Pentax MakerNote" }, + { tcPhaseOneMakerNote, "Phase One MakerNote" }, + { tcRicohMakerNote, "Ricoh MakerNote" }, + { tcRicohMakerNoteCameraInfo, "Ricoh MakerNote Camera Info" }, + { tcSonyMakerNote, "Sony MakerNote" }, + { tcSonyMakerNoteSubInfo, "Sony MakerNote SubInfo" }, + { tcSonyPrivateIFD1, "Sony Private IFD 1" }, + { tcSonyPrivateIFD2, "Sony Private IFD 2" }, + { tcSonyPrivateIFD3A, "Sony Private IFD 3A" }, + { tcSonyPrivateIFD3B, "Sony Private IFD 3B" }, + { tcSonyPrivateIFD3C, "Sony Private IFD 3C" }, + { tcCanonCRW, "Canon CRW" }, + { tcContaxRAW, "Contax RAW" }, + { tcFujiRAF, "Fuji RAF" }, + { tcLeafMOS, "Leaf MOS" }, + { tcMinoltaMRW, "Minolta MRW" }, + { tcPanasonicRAW, "Panasonic RAW" }, + { tcFoveonX3F, "Foveon X3F" }, + { tcJPEG, "JPEG" }, + { tcAdobePSD, "Adobe PSD" }, + { tcPNG, "PNG" }, + { tcHEIC, "HEIC" } + }; + + const char *name = LookupName (parentCode, + kParentCodeNames, + sizeof (kParentCodeNames ) / + sizeof (kParentCodeNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + if (parentCode >= tcFirstSubIFD && + parentCode <= tcLastSubIFD) + { + + sprintf (s, "SubIFD %u", (unsigned) (parentCode - tcFirstSubIFD + 1)); + + } + + else if (parentCode >= tcFirstChainedIFD && + parentCode <= tcLastChainedIFD) + { + + sprintf (s, "Chained IFD %u", (unsigned) (parentCode - tcFirstChainedIFD + 1)); + + } + + else + { + + sprintf (s, "ParentIFD %u", (unsigned) parentCode); + + } + + return s; + + } + +/*****************************************************************************/ + +const char * LookupTagCode (uint32 parentCode, + uint32 tagCode) + { + + const dng_name_table kTagNames [] = + { + { tcNewSubFileType, "NewSubFileType" }, + { tcSubFileType, "SubFileType" }, + { tcImageWidth, "ImageWidth" }, + { tcImageLength, "ImageLength" }, + { tcBitsPerSample, "BitsPerSample" }, + { tcCompression, "Compression" }, + { tcPhotometricInterpretation, "PhotometricInterpretation" }, + { tcThresholding, "Thresholding" }, + { tcCellWidth, "CellWidth" }, + { tcCellLength, "CellLength" }, + { tcFillOrder, "FillOrder" }, + { tcImageDescription, "ImageDescription" }, + { tcMake, "Make" }, + { tcModel, "Model" }, + { tcStripOffsets, "StripOffsets" }, + { tcOrientation, "Orientation" }, + { tcSamplesPerPixel, "SamplesPerPixel" }, + { tcRowsPerStrip, "RowsPerStrip" }, + { tcStripByteCounts, "StripByteCounts" }, + { tcMinSampleValue, "MinSampleValue" }, + { tcMaxSampleValue, "MaxSampleValue" }, + { tcXResolution, "XResolution" }, + { tcYResolution, "YResolution" }, + { tcPlanarConfiguration, "PlanarConfiguration" }, + { tcFreeOffsets, "FreeOffsets" }, + { tcFreeByteCounts, "FreeByteCounts" }, + { tcGrayResponseUnit, "GrayResponseUnit" }, + { tcGrayResponseCurve, "GrayResponseCurve" }, + { tcResolutionUnit, "ResolutionUnit" }, + { tcTransferFunction, "TransferFunction" }, + { tcSoftware, "Software" }, + { tcDateTime, "DateTime" }, + { tcArtist, "Artist" }, + { tcHostComputer, "HostComputer" }, + { tcWhitePoint, "WhitePoint" }, + { tcPrimaryChromaticities, "PrimaryChromaticities" }, + { tcColorMap, "ColorMap" }, + { tcTileWidth, "TileWidth" }, + { tcTileLength, "TileLength" }, + { tcTileOffsets, "TileOffsets" }, + { tcTileByteCounts, "TileByteCounts" }, + { tcSubIFDs, "SubIFDs" }, + { tcExtraSamples, "ExtraSamples" }, + { tcSampleFormat, "SampleFormat" }, + { tcJPEGTables, "JPEGTables" }, + { tcJPEGProc, "JPEGProc" }, + { tcJPEGInterchangeFormat, "JPEGInterchangeFormat" }, + { tcJPEGInterchangeFormatLength, "JPEGInterchangeFormatLength" }, + { tcYCbCrCoefficients, "YCbCrCoefficients" }, + { tcYCbCrSubSampling, "YCbCrSubSampling" }, + { tcYCbCrPositioning, "YCbCrPositioning" }, + { tcReferenceBlackWhite, "ReferenceBlackWhite" }, + { tcXMP, "XMP" }, + { tcKodakCameraSerialNumber, "KodakCameraSerialNumber" }, + { tcCFARepeatPatternDim, "CFARepeatPatternDim" }, + { tcCFAPattern, "CFAPattern" }, + { tcBatteryLevel, "BatteryLevel" }, + { tcKodakDCRPrivateIFD, "KodakDCRPrivateIFD" }, + { tcCopyright, "Copyright" }, + { tcExposureTime, "ExposureTime" }, + { tcFNumber, "FNumber" }, + { tcIPTC_NAA, "IPTC/NAA" }, + { tcLeafPKTS, "LeafPKTS" }, + { tcAdobeData, "AdobeData" }, + { tcExifIFD, "ExifIFD" }, + { tcICCProfile, "ICCProfile" }, + { tcExposureProgram, "ExposureProgram" }, + { tcSpectralSensitivity, "SpectralSensitivity" }, + { tcGPSInfo, "GPSInfo" }, + { tcISOSpeedRatings, "ISOSpeedRatings" }, + { tcOECF, "OECF" }, + { tcInterlace, "Interlace" }, + { tcTimeZoneOffset, "TimeZoneOffset" }, + { tcSelfTimerMode, "SelfTimerMode" }, + { tcSensitivityType, "SensitivityType" }, + { tcStandardOutputSensitivity, "StandardOutputSensitivity" }, + { tcRecommendedExposureIndex, "RecommendedExposureIndex" }, + { tcISOSpeed, "ISOSpeed" }, + { tcISOSpeedLatitudeyyy, "ISOSpeedLatitudeyyy" }, + { tcISOSpeedLatitudezzz, "ISOSpeedLatitudezzz" }, + { tcExifVersion, "ExifVersion" }, + { tcDateTimeOriginal, "DateTimeOriginal" }, + { tcDateTimeDigitized, "DateTimeDigitized" }, + { tcOffsetTime, "OffsetTime" }, + { tcOffsetTimeOriginal, "OffsetTimeOriginal" }, + { tcOffsetTimeDigitized, "OffsetTimeDigitized" }, + { tcComponentsConfiguration, "ComponentsConfiguration" }, + { tcCompressedBitsPerPixel, "CompressedBitsPerPixel" }, + { tcShutterSpeedValue, "ShutterSpeedValue" }, + { tcApertureValue, "ApertureValue" }, + { tcBrightnessValue, "BrightnessValue" }, + { tcExposureBiasValue, "ExposureBiasValue" }, + { tcMaxApertureValue, "MaxApertureValue" }, + { tcSubjectDistance, "SubjectDistance" }, + { tcMeteringMode, "MeteringMode" }, + { tcLightSource, "LightSource" }, + { tcFlash, "Flash" }, + { tcFocalLength, "FocalLength" }, + { tcFlashEnergy, "FlashEnergy" }, + { tcSpatialFrequencyResponse, "SpatialFrequencyResponse" }, + { tcNoise, "Noise" }, + { tcFocalPlaneXResolution, "FocalPlaneXResolution" }, + { tcFocalPlaneYResolution, "FocalPlaneYResolution" }, + { tcFocalPlaneResolutionUnit, "FocalPlaneResolutionUnit" }, + { tcImageNumber, "ImageNumber" }, + { tcSecurityClassification, "SecurityClassification" }, + { tcImageHistory, "ImageHistory" }, + { tcSubjectArea, "SubjectArea" }, + { tcExposureIndex, "ExposureIndex" }, + { tcTIFF_EP_StandardID, "TIFF/EPStandardID" }, + { tcSensingMethod, "SensingMethod" }, + { tcMakerNote, "MakerNote" }, + { tcUserComment, "UserComment" }, + { tcSubsecTime, "SubsecTime" }, + { tcSubsecTimeOriginal, "SubsecTimeOriginal" }, + { tcSubsecTimeDigitized, "SubsecTimeDigitized" }, + { tcAdobeLayerData, "AdobeLayerData" }, + { tcTemperature, "Temperature" }, + { tcHumidity, "Humidity" }, + { tcPressure, "Pressure" }, + { tcWaterDepth, "WaterDepth" }, + { tcAcceleration, "Acceleration" }, + { tcCameraElevationAngle, "CameraElevationAngle" }, + { tcFlashPixVersion, "FlashPixVersion" }, + { tcColorSpace, "ColorSpace" }, + { tcPixelXDimension, "PixelXDimension" }, + { tcPixelYDimension, "PixelYDimension" }, + { tcRelatedSoundFile, "RelatedSoundFile" }, + { tcInteroperabilityIFD, "InteroperabilityIFD" }, + { tcFlashEnergyExif, "FlashEnergyExif" }, + { tcSpatialFrequencyResponseExif, "SpatialFrequencyResponseExif" }, + { tcFocalPlaneXResolutionExif, "FocalPlaneXResolutionExif" }, + { tcFocalPlaneYResolutionExif, "FocalPlaneYResolutionExif" }, + { tcFocalPlaneResolutionUnitExif, "FocalPlaneResolutionUnitExif" }, + { tcSubjectLocation, "SubjectLocation" }, + { tcExposureIndexExif, "ExposureIndexExif" }, + { tcSensingMethodExif, "SensingMethodExif" }, + { tcFileSource, "FileSource" }, + { tcSceneType, "SceneType" }, + { tcCFAPatternExif, "CFAPatternExif" }, + { tcCustomRendered, "CustomRendered" }, + { tcExposureMode, "ExposureMode" }, + { tcWhiteBalance, "WhiteBalance" }, + { tcDigitalZoomRatio, "DigitalZoomRatio" }, + { tcFocalLengthIn35mmFilm, "FocalLengthIn35mmFilm" }, + { tcSceneCaptureType, "SceneCaptureType" }, + { tcGainControl, "GainControl" }, + { tcContrast, "Contrast" }, + { tcSaturation, "Saturation" }, + { tcSharpness, "Sharpness" }, + { tcDeviceSettingDescription, "DeviceSettingDescription" }, + { tcSubjectDistanceRange, "SubjectDistanceRange" }, + { tcImageUniqueID, "ImageUniqueID" }, + { tcCameraOwnerNameExif, "CameraOwnerNameExif" }, + { tcCameraSerialNumberExif, "CameraSerialNumberExif" }, + { tcLensSpecificationExif, "LensSpecificationExif" }, + { tcLensMakeExif, "LensMakeExif" }, + { tcLensModelExif, "LensModelExif" }, + { tcLensSerialNumberExif, "LensSerialNumberExif" }, + { tcGamma, "Gamma" }, + { tcPrintImageMatchingInfo, "PrintImageMatchingInfo" }, + { tcDNGVersion, "DNGVersion" }, + { tcDNGBackwardVersion, "DNGBackwardVersion" }, + { tcUniqueCameraModel, "UniqueCameraModel" }, + { tcLocalizedCameraModel, "LocalizedCameraModel" }, + { tcCFAPlaneColor, "CFAPlaneColor" }, + { tcCFALayout, "CFALayout" }, + { tcLinearizationTable, "LinearizationTable" }, + { tcBlackLevelRepeatDim, "BlackLevelRepeatDim" }, + { tcBlackLevel, "BlackLevel" }, + { tcBlackLevelDeltaH, "BlackLevelDeltaH" }, + { tcBlackLevelDeltaV, "BlackLevelDeltaV" }, + { tcWhiteLevel, "WhiteLevel" }, + { tcDefaultScale, "DefaultScale" }, + { tcDefaultCropOrigin, "DefaultCropOrigin" }, + { tcDefaultCropSize, "DefaultCropSize" }, + { tcDefaultUserCrop, "DefaultUserCrop" }, + { tcColorMatrix1, "ColorMatrix1" }, + { tcColorMatrix2, "ColorMatrix2" }, + { tcCameraCalibration1, "CameraCalibration1" }, + { tcCameraCalibration2, "CameraCalibration2" }, + { tcReductionMatrix1, "ReductionMatrix1" }, + { tcReductionMatrix2, "ReductionMatrix2" }, + { tcAnalogBalance, "AnalogBalance" }, + { tcAsShotNeutral, "AsShotNeutral" }, + { tcAsShotWhiteXY, "AsShotWhiteXY" }, + { tcBaselineExposure, "BaselineExposure" }, + { tcBaselineNoise, "BaselineNoise" }, + { tcBaselineSharpness, "BaselineSharpness" }, + { tcBayerGreenSplit, "BayerGreenSplit" }, + { tcLinearResponseLimit, "LinearResponseLimit" }, + { tcCameraSerialNumber, "CameraSerialNumber" }, + { tcLensInfo, "LensInfo" }, + { tcChromaBlurRadius, "ChromaBlurRadius" }, + { tcAntiAliasStrength, "AntiAliasStrength" }, + { tcShadowScale, "ShadowScale" }, + { tcDNGPrivateData, "DNGPrivateData" }, + { tcMakerNoteSafety, "MakerNoteSafety" }, + { tcCalibrationIlluminant1, "CalibrationIlluminant1" }, + { tcCalibrationIlluminant2, "CalibrationIlluminant2" }, + { tcBestQualityScale, "BestQualityScale" }, + { tcRawDataUniqueID, "RawDataUniqueID" }, + { tcOriginalRawFileName, "OriginalRawFileName" }, + { tcOriginalRawFileData, "OriginalRawFileData" }, + { tcActiveArea, "ActiveArea" }, + { tcMaskedAreas, "MaskedAreas" }, + { tcAsShotICCProfile, "AsShotICCProfile" }, + { tcAsShotPreProfileMatrix, "AsShotPreProfileMatrix" }, + { tcCurrentICCProfile, "CurrentICCProfile" }, + { tcCurrentPreProfileMatrix, "CurrentPreProfileMatrix" }, + { tcColorimetricReference, "ColorimetricReference" }, + { tcCameraCalibrationSignature, "CameraCalibrationSignature" }, + { tcProfileCalibrationSignature, "ProfileCalibrationSignature" }, + { tcExtraCameraProfiles, "ExtraCameraProfiles" }, + { tcAsShotProfileName, "AsShotProfileName" }, + { tcNoiseReductionApplied, "NoiseReductionApplied" }, + { tcProfileName, "ProfileName" }, + { tcProfileHueSatMapDims, "ProfileHueSatMapDims" }, + { tcProfileHueSatMapData1, "ProfileHueSatMapData1" }, + { tcProfileHueSatMapData2, "ProfileHueSatMapData2" }, + { tcProfileHueSatMapEncoding, "ProfileHueSatMapEncoding" }, + { tcProfileToneCurve, "ProfileToneCurve" }, + { tcProfileEmbedPolicy, "ProfileEmbedPolicy" }, + { tcProfileCopyright, "ProfileCopyright" }, + { tcForwardMatrix1, "ForwardMatrix1" }, + { tcForwardMatrix2, "ForwardMatrix2" }, + { tcPreviewApplicationName, "PreviewApplicationName" }, + { tcPreviewApplicationVersion, "PreviewApplicationVersion" }, + { tcPreviewSettingsName, "PreviewSettingsName" }, + { tcPreviewSettingsDigest, "PreviewSettingsDigest" }, + { tcPreviewColorSpace, "PreviewColorSpace" }, + { tcPreviewDateTime, "PreviewDateTime" }, + { tcRawImageDigest, "RawImageDigest" }, + { tcOriginalRawFileDigest, "OriginalRawFileDigest" }, + { tcSubTileBlockSize, "SubTileBlockSize" }, + { tcRowInterleaveFactor, "RowInterleaveFactor" }, + { tcProfileLookTableDims, "ProfileLookTableDims" }, + { tcProfileLookTableData, "ProfileLookTableData" }, + { tcProfileLookTableEncoding, "ProfileLookTableEncoding" }, + { tcBaselineExposureOffset, "BaselineExposureOffset" }, + { tcDefaultBlackRender, "DefaultBlackRender" }, + { tcOpcodeList1, "OpcodeList1" }, + { tcOpcodeList2, "OpcodeList2" }, + { tcOpcodeList3, "OpcodeList3" }, + { tcNoiseProfile, "NoiseProfile" }, + { tcOriginalDefaultFinalSize, "OriginalDefaultFinalSize" }, + { tcOriginalBestQualityFinalSize, "OriginalBestQualityFinalSize" }, + { tcOriginalDefaultCropSize, "OriginalDefaultCropSize" }, + { tcProfileHueSatMapEncoding, "ProfileHueSatMapEncoding" }, + { tcProfileLookTableEncoding, "ProfileLookTableEncoding" }, + { tcBaselineExposureOffset, "BaselineExposureOffset" }, + { tcDefaultBlackRender, "DefaultBlackRender" }, + { tcNewRawImageDigest, "NewRawImageDigest" }, + { tcRawToPreviewGain, "RawToPreviewGain" }, + { tcCacheBlob, "CacheBlob" }, + { tcCacheVersion, "CacheVersion" }, + { tcDefaultUserCrop, "DefaultUserCrop" }, + { tcDepthFormat, "DepthFormat" }, + { tcDepthNear, "DepthNear" }, + { tcDepthFar, "DepthFar" }, + { tcDepthUnits, "DepthUnits" }, + { tcDepthMeasureType, "DepthMeasureType" }, + { tcEnhanceParams, "EnhanceParams" }, + { tcKodakKDCPrivateIFD, "KodakKDCPrivateIFD" } + }; + + const dng_name_table kGPSTagNames [] = + { + { tcGPSVersionID, "GPSVersionID" }, + { tcGPSLatitudeRef, "GPSLatitudeRef" }, + { tcGPSLatitude, "GPSLatitude" }, + { tcGPSLongitudeRef, "GPSLongitudeRef" }, + { tcGPSLongitude, "GPSLongitude" }, + { tcGPSAltitudeRef, "GPSAltitudeRef" }, + { tcGPSAltitude, "GPSAltitude" }, + { tcGPSTimeStamp, "GPSTimeStamp" }, + { tcGPSSatellites, "GPSSatellites" }, + { tcGPSStatus, "GPSStatus" }, + { tcGPSMeasureMode, "GPSMeasureMode" }, + { tcGPSDOP, "GPSDOP" }, + { tcGPSSpeedRef, "GPSSpeedRef" }, + { tcGPSSpeed, "GPSSpeed" }, + { tcGPSTrackRef, "GPSTrackRef" }, + { tcGPSTrack, "GPSTrack" }, + { tcGPSImgDirectionRef, "GPSImgDirectionRef" }, + { tcGPSImgDirection, "GPSImgDirection" }, + { tcGPSMapDatum, "GPSMapDatum" }, + { tcGPSDestLatitudeRef, "GPSDestLatitudeRef" }, + { tcGPSDestLatitude, "GPSDestLatitude" }, + { tcGPSDestLongitudeRef, "GPSDestLongitudeRef" }, + { tcGPSDestLongitude, "GPSDestLongitude" }, + { tcGPSDestBearingRef, "GPSDestBearingRef" }, + { tcGPSDestBearing, "GPSDestBearing" }, + { tcGPSDestDistanceRef, "GPSDestDistanceRef" }, + { tcGPSDestDistance, "GPSDestDistance" }, + { tcGPSProcessingMethod, "GPSProcessingMethod" }, + { tcGPSAreaInformation, "GPSAreaInformation" }, + { tcGPSDateStamp, "GPSDateStamp" }, + { tcGPSDifferential, "GPSDifferential" }, + { tcGPSHPositioningError, "GPSHPositioningError" }, + }; + + const dng_name_table kInteroperabilityTagNames [] = + { + { tcInteroperabilityIndex, "InteroperabilityIndex" }, + { tcInteroperabilityVersion, "InteroperabilityVersion" }, + { tcRelatedImageFileFormat, "RelatedImageFileFormat" }, + { tcRelatedImageWidth, "RelatedImageWidth" }, + { tcRelatedImageLength, "RelatedImageLength" } + }; + + const dng_name_table kFujiTagNames [] = + { + { tcFujiHeader, "FujiHeader" }, + { tcFujiRawInfo1, "FujiRawInfo1" }, + { tcFujiRawInfo2, "FujiRawInfo2" } + }; + + const dng_name_table kContaxTagNames [] = + { + { tcContaxHeader, "ContaxHeader" } + }; + + const char *name = NULL; + + if (parentCode == 0 || + parentCode == tcExifIFD || + parentCode == tcLeafMOS || + (parentCode >= tcFirstSubIFD && parentCode <= tcLastSubIFD) || + (parentCode >= tcFirstChainedIFD && parentCode <= tcLastChainedIFD)) + { + + name = LookupName (tagCode, + kTagNames, + sizeof (kTagNames ) / + sizeof (kTagNames [0])); + + } + + else if (parentCode == tcGPSInfo) + { + + name = LookupName (tagCode, + kGPSTagNames, + sizeof (kGPSTagNames ) / + sizeof (kGPSTagNames [0])); + + } + + else if (parentCode == tcInteroperabilityIFD) + { + + name = LookupName (tagCode, + kInteroperabilityTagNames, + sizeof (kInteroperabilityTagNames ) / + sizeof (kInteroperabilityTagNames [0])); + + } + + else if (parentCode == tcFujiRAF) + { + + name = LookupName (tagCode, + kFujiTagNames, + sizeof (kFujiTagNames ) / + sizeof (kFujiTagNames [0])); + + } + + else if (parentCode == tcContaxRAW) + { + + name = LookupName (tagCode, + kContaxTagNames, + sizeof (kContaxTagNames ) / + sizeof (kContaxTagNames [0])); + + } + + if (name) + { + return name; + } + + static char s [32]; + + if (parentCode == tcCanonCRW) + { + sprintf (s, "CRW_%04X", (unsigned) tagCode); + } + + else if (parentCode == tcMinoltaMRW) + { + + char c1 = (char) ((tagCode >> 24) & 0xFF); + char c2 = (char) ((tagCode >> 16) & 0xFF); + char c3 = (char) ((tagCode >> 8) & 0xFF); + char c4 = (char) ((tagCode ) & 0xFF); + + if (c1 < ' ') c1 = '_'; + if (c2 < ' ') c2 = '_'; + if (c3 < ' ') c3 = '_'; + if (c4 < ' ') c4 = '_'; + + sprintf (s, "MRW%c%c%c%c", c1, c2, c3, c4); + + } + + else if (parentCode == tcFujiRawInfo1) + { + sprintf (s, "RAF1_%04X", (unsigned) tagCode); + } + + else if (parentCode == tcFujiRawInfo2) + { + sprintf (s, "RAF2_%04X", (unsigned) tagCode); + } + + else + { + sprintf (s, "Tag%u", (unsigned) tagCode); + } + + return s; + + } + +/*****************************************************************************/ + +const char * LookupTagType (uint32 tagType) + { + + const dng_name_table kTagTypeNames [] = + { + { ttByte, "Byte" }, + { ttAscii, "ASCII" }, + { ttShort, "Short" }, + { ttLong, "Long" }, + { ttRational, "Rational" }, + { ttSByte, "SByte" }, + { ttUndefined, "Undefined" }, + { ttSShort, "SShort" }, + { ttSLong, "SLong" }, + { ttSRational, "SRational" }, + { ttFloat, "Float" }, + { ttDouble, "Double" }, + { ttIFD, "IFD" }, + { ttUnicode, "Unicode" }, + { ttComplex, "Complex" } + }; + + const char *name = LookupName (tagType, + kTagTypeNames, + sizeof (kTagTypeNames ) / + sizeof (kTagTypeNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "Type%u", (unsigned) tagType); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupNewSubFileType (uint32 key) + { + + const dng_name_table kNewSubFileTypeNames [] = + { + { sfMainImage , "Main Image" }, + { sfPreviewImage , "Preview Image" }, + { sfTransparencyMask , "Transparency Mask" }, + { sfPreviewMask , "Preview Mask" }, + { sfDepthMap , "Depth Map" }, + { sfPreviewDepthMap , "Preview Depth Map" }, + { sfEnhancedImage , "Enhanced Image" }, + { sfAltPreviewImage , "Alt Preview Image" } + }; + + const char *name = LookupName (key, + kNewSubFileTypeNames, + sizeof (kNewSubFileTypeNames ) / + sizeof (kNewSubFileTypeNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupCompression (uint32 key) + { + + const dng_name_table kCompressionNames [] = + { + { ccUncompressed, "Uncompressed" }, + { ccLZW, "LZW" }, + { ccOldJPEG, "Old JPEG" }, + { ccJPEG, "JPEG" }, + { ccDeflate, "Deflate" }, + { ccPackBits, "PackBits" }, + { ccOldDeflate, "OldDeflate" }, + { ccLossyJPEG, "Lossy JPEG" } + }; + + const char *name = LookupName (key, + kCompressionNames, + sizeof (kCompressionNames ) / + sizeof (kCompressionNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupPredictor (uint32 key) + { + + const dng_name_table kPredictorNames [] = + { + { cpNullPredictor, "NullPredictor" }, + { cpHorizontalDifference, "HorizontalDifference" }, + { cpFloatingPoint, "FloatingPoint" }, + { cpHorizontalDifferenceX2, "HorizontalDifferenceX2" }, + { cpHorizontalDifferenceX4, "HorizontalDifferenceX4" }, + { cpFloatingPointX2, "FloatingPointX2" }, + { cpFloatingPointX4, "FloatingPointX4" } + }; + + const char *name = LookupName (key, + kPredictorNames, + sizeof (kPredictorNames ) / + sizeof (kPredictorNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupSampleFormat (uint32 key) + { + + const dng_name_table kSampleFormatNames [] = + { + { sfUnsignedInteger, "UnsignedInteger" }, + { sfSignedInteger, "SignedInteger" }, + { sfFloatingPoint, "FloatingPoint" }, + { sfUndefined, "Undefined" } + }; + + const char *name = LookupName (key, + kSampleFormatNames, + sizeof (kSampleFormatNames ) / + sizeof (kSampleFormatNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupPhotometricInterpretation (uint32 key) + { + + const dng_name_table kPhotometricInterpretationNames [] = + { + { piWhiteIsZero, "WhiteIsZero" }, + { piBlackIsZero, "BlackIsZero" }, + { piRGB, "RGB" }, + { piRGBPalette, "RGBPalette" }, + { piTransparencyMask, "TransparencyMask" }, + { piDepth, "Depth" }, + { piCMYK, "CMYK" }, + { piYCbCr, "YCbCr" }, + { piCIELab, "CIELab" }, + { piICCLab, "ICCLab" }, + { piCFA, "CFA" }, + { piLinearRaw, "LinearRaw" } + }; + + const char *name = LookupName (key, + kPhotometricInterpretationNames, + sizeof (kPhotometricInterpretationNames ) / + sizeof (kPhotometricInterpretationNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupOrientation (uint32 key) + { + + const dng_name_table kOrientationNames [] = + { + { 1, "1 - 0th row is top, 0th column is left" }, + { 2, "2 - 0th row is top, 0th column is right" }, + { 3, "3 - 0th row is bottom, 0th column is right" }, + { 4, "4 - 0th row is bottom, 0th column is left" }, + { 5, "5 - 0th row is left, 0th column is top" }, + { 6, "6 - 0th row is right, 0th column is top" }, + { 7, "7 - 0th row is right, 0th column is bottom" }, + { 8, "8 - 0th row is left, 0th column is bottom" }, + { 9, "9 - unknown" } + }; + + const char *name = LookupName (key, + kOrientationNames, + sizeof (kOrientationNames ) / + sizeof (kOrientationNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupResolutionUnit (uint32 key) + { + + const dng_name_table kResolutionUnitNames [] = + { + { ruNone, "None" }, + { ruInch, "Inch" }, + { ruCM, "cm" }, + { ruMM, "mm" }, + { ruMicroM, "Micrometer" } + }; + + const char *name = LookupName (key, + kResolutionUnitNames, + sizeof (kResolutionUnitNames ) / + sizeof (kResolutionUnitNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupCFAColor (uint32 key) + { + + const dng_name_table kCFAColorNames [] = + { + { 0, "Red" }, + { 1, "Green" }, + { 2, "Blue" }, + { 3, "Cyan" }, + { 4, "Magenta" }, + { 5, "Yellow" }, + { 6, "White" } + }; + + const char *name = LookupName (key, + kCFAColorNames, + sizeof (kCFAColorNames ) / + sizeof (kCFAColorNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "Color%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupSensingMethod (uint32 key) + { + + const dng_name_table kSensingMethodNames [] = + { + { 0, "Undefined" }, + { 1, "MonochromeArea" }, + { 2, "OneChipColorArea" }, + { 3, "TwoChipColorArea" }, + { 4, "ThreeChipColorArea" }, + { 5, "ColorSequentialArea" }, + { 6, "MonochromeLinear" }, + { 7, "TriLinear" }, + { 8, "ColorSequentialLinear" } + }; + + const char *name = LookupName (key, + kSensingMethodNames, + sizeof (kSensingMethodNames ) / + sizeof (kSensingMethodNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupExposureProgram (uint32 key) + { + + const dng_name_table kExposureProgramNames [] = + { + { epUnidentified, "Unidentified" }, + { epManual, "Manual" }, + { epProgramNormal, "Program Normal" }, + { epAperturePriority, "Aperture Priority" }, + { epShutterPriority, "Shutter Priority" }, + { epProgramCreative, "Program Creative" }, + { epProgramAction, "Program Action" }, + { epPortraitMode, "Portrait Mode" }, + { epLandscapeMode, "Landscape Mode" } + }; + + const char *name = LookupName (key, + kExposureProgramNames, + sizeof (kExposureProgramNames ) / + sizeof (kExposureProgramNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupMeteringMode (uint32 key) + { + + const dng_name_table kMeteringModeNames [] = + { + { mmUnidentified, "Unknown" }, + { mmAverage, "Average" }, + { mmCenterWeightedAverage, "CenterWeightedAverage" }, + { mmSpot, "Spot" }, + { mmMultiSpot, "MultiSpot" }, + { mmPattern, "Pattern" }, + { mmPartial, "Partial" }, + { mmOther, "Other" } + }; + + const char *name = LookupName (key, + kMeteringModeNames, + sizeof (kMeteringModeNames ) / + sizeof (kMeteringModeNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupLightSource (uint32 key) + { + + const dng_name_table kLightSourceNames [] = + { + { lsUnknown, "Unknown" }, + { lsDaylight, "Daylight" }, + { lsFluorescent, "Fluorescent" }, + { lsTungsten, "Tungsten (incandescent light)" }, + { lsFlash, "Flash" }, + { lsFineWeather, "Fine weather" }, + { lsCloudyWeather, "Cloudy weather" }, + { lsShade, "Shade" }, + { lsDaylightFluorescent, "Daylight fluorescent (D 5700 - 7100K)" }, + { lsDayWhiteFluorescent, "Day white fluorescent (N 4600 - 5500K)" }, + { lsCoolWhiteFluorescent, "Cool white fluorescent (W 3800 - 4500K)" }, + { lsWhiteFluorescent, "White fluorescent (WW 3250 - 3800K)" }, + { lsWarmWhiteFluorescent, "Warm white fluorescent (L 2600 - 3250K)" }, + { lsStandardLightA, "Standard light A" }, + { lsStandardLightB, "Standard light B" }, + { lsStandardLightC, "Standard light C" }, + { lsD55, "D55" }, + { lsD65, "D65" }, + { lsD75, "D75" }, + { lsD50, "D50" }, + { lsISOStudioTungsten, "ISO studio tungsten" }, + { lsOther, "Other" } + }; + + const char *name = LookupName (key, + kLightSourceNames, + sizeof (kLightSourceNames ) / + sizeof (kLightSourceNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + if (key & 0x08000) + { + + sprintf (s, "%uK", (unsigned) (key & 0x7FFF)); + + } + + else + { + + sprintf (s, "%u", (unsigned) key); + + } + + return s; + + } + +/*****************************************************************************/ + +const char * LookupColorSpace (uint32 key) + { + + const dng_name_table kColorSpaceNames [] = + { + { 1, "sRGB" }, + { 0xFFFF, "Uncalibrated" } + }; + + const char *name = LookupName (key, + kColorSpaceNames, + sizeof (kColorSpaceNames ) / + sizeof (kColorSpaceNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupFileSource (uint32 key) + { + + const dng_name_table kFileSourceNames [] = + { + { 3, "DSC" } + }; + + const char *name = LookupName (key, + kFileSourceNames, + sizeof (kFileSourceNames ) / + sizeof (kFileSourceNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupSceneType (uint32 key) + { + + const dng_name_table kSceneTypeNames [] = + { + { 1, "A directly photographed image" } + }; + + const char *name = LookupName (key, + kSceneTypeNames, + sizeof (kSceneTypeNames ) / + sizeof (kSceneTypeNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupCustomRendered (uint32 key) + { + + const dng_name_table kCustomRenderedNames [] = + { + { 0, "Normal process" }, + { 1, "Custom process" } + }; + + const char *name = LookupName (key, + kCustomRenderedNames, + sizeof (kCustomRenderedNames ) / + sizeof (kCustomRenderedNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupExposureMode (uint32 key) + { + + const dng_name_table kExposureModeNames [] = + { + { 0, "Auto exposure" }, + { 1, "Manual exposure" }, + { 2, "Auto bracket" } + }; + + const char *name = LookupName (key, + kExposureModeNames, + sizeof (kExposureModeNames ) / + sizeof (kExposureModeNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupWhiteBalance (uint32 key) + { + + const dng_name_table kWhiteBalanceNames [] = + { + { 0, "Auto white balance" }, + { 1, "Manual white balance" } + }; + + const char *name = LookupName (key, + kWhiteBalanceNames, + sizeof (kWhiteBalanceNames ) / + sizeof (kWhiteBalanceNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupSceneCaptureType (uint32 key) + { + + const dng_name_table kSceneCaptureTypeNames [] = + { + { 0, "Standard" }, + { 1, "Landscape" }, + { 2, "Portrait" }, + { 3, "Night scene" } + }; + + const char *name = LookupName (key, + kSceneCaptureTypeNames, + sizeof (kSceneCaptureTypeNames ) / + sizeof (kSceneCaptureTypeNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupGainControl (uint32 key) + { + + const dng_name_table kGainControlNames [] = + { + { 0, "None" }, + { 1, "Low gain up" }, + { 2, "High gain up" }, + { 3, "Low gain down" }, + { 4, "High gain down" } + }; + + const char *name = LookupName (key, + kGainControlNames, + sizeof (kGainControlNames ) / + sizeof (kGainControlNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupContrast (uint32 key) + { + + const dng_name_table kContrastNames [] = + { + { 0, "Normal" }, + { 1, "Soft" }, + { 2, "Hard" } + }; + + const char *name = LookupName (key, + kContrastNames, + sizeof (kContrastNames ) / + sizeof (kContrastNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupSaturation (uint32 key) + { + + const dng_name_table kSaturationNames [] = + { + { 0, "Normal" }, + { 1, "Low saturation" }, + { 2, "High saturation" } + }; + + const char *name = LookupName (key, + kSaturationNames, + sizeof (kSaturationNames ) / + sizeof (kSaturationNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupSharpness (uint32 key) + { + + const dng_name_table kSharpnessNames [] = + { + { 0, "Normal" }, + { 1, "Soft" }, + { 2, "Hard" } + }; + + const char *name = LookupName (key, + kSharpnessNames, + sizeof (kSharpnessNames ) / + sizeof (kSharpnessNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupSubjectDistanceRange (uint32 key) + { + + const dng_name_table kSubjectDistanceRangeNames [] = + { + { 0, "Unknown" }, + { 1, "Macro" }, + { 2, "Close view" }, + { 3, "Distant view" } + }; + + const char *name = LookupName (key, + kSubjectDistanceRangeNames, + sizeof (kSubjectDistanceRangeNames ) / + sizeof (kSubjectDistanceRangeNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupComponent (uint32 key) + { + + const dng_name_table kComponentNames [] = + { + { 0, "-" }, + { 1, "Y" }, + { 2, "Cb" }, + { 3, "Cr" }, + { 4, "R" }, + { 5, "G" }, + { 6, "B" } + }; + + const char *name = LookupName (key, + kComponentNames, + sizeof (kComponentNames ) / + sizeof (kComponentNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupCFALayout (uint32 key) + { + + const dng_name_table kCFALayoutNames [] = + { + { 1, "Rectangular (or square) layout" }, + { 2, "Staggered layout A: even columns are offset down by 1/2 row" }, + { 3, "Staggered layout B: even columns are offset up by 1/2 row" }, + { 4, "Staggered layout C: even rows are offset right by 1/2 column" }, + { 5, "Staggered layout D: even rows are offset left by 1/2 column" }, + { 6, "Staggered layout E: even rows are offset up by 1/2 row, even columns are offset left by 1/2 column" }, + { 7, "Staggered layout F: even rows are offset up by 1/2 row, even columns are offset right by 1/2 column" }, + { 8, "Staggered layout G: even rows are offset down by 1/2 row, even columns are offset left by 1/2 column" }, + { 9, "Staggered layout H: even rows are offset down by 1/2 row, even columns are offset right by 1/2 column" } + }; + + const char *name = LookupName (key, + kCFALayoutNames, + sizeof (kCFALayoutNames ) / + sizeof (kCFALayoutNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupMakerNoteSafety (uint32 key) + { + + const dng_name_table kMakerNoteSafetyNames [] = + { + { 0, "Unsafe" }, + { 1, "Safe" } + }; + + const char *name = LookupName (key, + kMakerNoteSafetyNames, + sizeof (kMakerNoteSafetyNames ) / + sizeof (kMakerNoteSafetyNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupColorimetricReference (uint32 key) + { + + const dng_name_table kColorimetricReferenceNames [] = + { + { crSceneReferred, "Scene Referred" }, + { crICCProfilePCS, "ICC Profile PCS" } + }; + + const char *name = LookupName (key, + kColorimetricReferenceNames, + sizeof (kColorimetricReferenceNames ) / + sizeof (kColorimetricReferenceNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupPreviewColorSpace (uint32 key) + { + + const dng_name_table kPreviewColorSpaceNames [] = + { + { previewColorSpace_Unknown , "Unknown" }, + { previewColorSpace_GrayGamma22, "Gray Gamma 2.2" }, + { previewColorSpace_sRGB , "sRGB" }, + { previewColorSpace_AdobeRGB , "Adobe RGB (1998)" }, + { previewColorSpace_ProPhotoRGB, "Pro Photo RGB" } + }; + + const char *name = LookupName (key, + kPreviewColorSpaceNames, + sizeof (kPreviewColorSpaceNames ) / + sizeof (kPreviewColorSpaceNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupJPEGMarker (uint32 key) + { + + const dng_name_table kJPEGMarkerNames [] = + { + { M_TEM, "TEM" }, + { M_SOF0, "SOF0" }, + { M_SOF1, "SOF1" }, + { M_SOF2, "SOF2" }, + { M_SOF3, "SOF3" }, + { M_DHT, "DHT" }, + { M_SOF5, "SOF5" }, + { M_SOF6, "SOF6" }, + { M_SOF7, "SOF7" }, + { M_JPG, "JPG" }, + { M_SOF9, "SOF9" }, + { M_SOF10, "SOF10" }, + { M_SOF11, "SOF11" }, + { M_DAC, "DAC" }, + { M_SOF13, "SOF13" }, + { M_SOF14, "SOF14" }, + { M_SOF15, "SOF15" }, + { M_RST0, "RST0" }, + { M_RST1, "RST1" }, + { M_RST2, "RST2" }, + { M_RST3, "RST3" }, + { M_RST4, "RST4" }, + { M_RST5, "RST5" }, + { M_RST6, "RST6" }, + { M_RST7, "RST7" }, + { M_SOI, "SOI" }, + { M_EOI, "EOI" }, + { M_SOS, "SOS" }, + { M_DQT, "DQT" }, + { M_DNL, "DNL" }, + { M_DRI, "DRI" }, + { M_DHP, "DHP" }, + { M_EXP, "EXP" }, + { M_APP0, "APP0" }, + { M_APP1, "APP1" }, + { M_APP2, "APP2" }, + { M_APP3, "APP3" }, + { M_APP4, "APP4" }, + { M_APP5, "APP5" }, + { M_APP6, "APP6" }, + { M_APP7, "APP7" }, + { M_APP8, "APP8" }, + { M_APP9, "APP9" }, + { M_APP10, "APP10" }, + { M_APP11, "APP11" }, + { M_APP12, "APP12" }, + { M_APP13, "APP13" }, + { M_APP14, "APP14" }, + { M_APP15, "APP15" }, + { M_JPG0, "JPG0" }, + { M_JPG1, "JPG1" }, + { M_JPG2, "JPG2" }, + { M_JPG3, "JPG3" }, + { M_JPG4, "JPG4" }, + { M_JPG5, "JPG5" }, + { M_JPG6, "JPG6" }, + { M_JPG7, "JPG7" }, + { M_JPG8, "JPG8" }, + { M_JPG9, "JPG9" }, + { M_JPG10, "JPG10" }, + { M_JPG11, "JPG11" }, + { M_JPG12, "JPG12" }, + { M_JPG13, "JPG13" }, + { M_COM, "COM" }, + { M_ERROR, "ERROR" } + }; + + const char *name = LookupName (key, + kJPEGMarkerNames, + sizeof (kJPEGMarkerNames ) / + sizeof (kJPEGMarkerNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "0x%02X", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupSensitivityType (uint32 key) + { + + const dng_name_table kSensitivityTypeNames [] = + { + { stUnknown, "Unknown" }, + { stStandardOutputSensitivity, "Standard Output Sensitivity (SOS)" }, + { stRecommendedExposureIndex, "Recommended Exposure Index (REI)" }, + { stISOSpeed, "ISO Speed" }, + { stSOSandREI, "Standard Output Sensitivity (SOS) and Recommended Exposure Index (REI)" }, + { stSOSandISOSpeed, "Standard Output Sensitivity (SOS) and ISO Speed" }, + { stREIandISOSpeed, "Recommended Exposure Index (REI) and ISO Speed" }, + { stSOSandREIandISOSpeed, "Standard Output Sensitivity (SOS) and Recommended Exposure Index (REI) and ISO Speed" }, + }; + + const char *name = LookupName (key, + kSensitivityTypeNames, + sizeof (kSensitivityTypeNames ) / + sizeof (kSensitivityTypeNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupDepthFormat (uint32 key) + { + + const dng_name_table kDepthFormatNames [] = + { + { depthFormatUnknown, "Unknown" }, + { depthFormatLinear, "Linear" }, + { depthFormatInverse, "Inverse" }, + }; + + const char *name = LookupName (key, + kDepthFormatNames, + sizeof (kDepthFormatNames ) / + sizeof (kDepthFormatNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupDepthUnits (uint32 key) + { + + const dng_name_table kDepthUnitNames [] = + { + { depthUnitsUnknown, "Unknown" }, + { depthUnitsMeters, "Meters" }, + }; + + const char *name = LookupName (key, + kDepthUnitNames, + sizeof (kDepthUnitNames ) / + sizeof (kDepthUnitNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupDepthMeasureType (uint32 key) + { + + const dng_name_table kDepthMeasureTypeNames [] = + { + { depthMeasureUnknown, "Unknown" }, + { depthMeasureOpticalAxis, "Optical Axis" }, + { depthMeasureOpticalRay, "Optical Ray" }, + }; + + const char *name = LookupName (key, + kDepthMeasureTypeNames, + sizeof (kDepthMeasureTypeNames ) / + sizeof (kDepthMeasureTypeNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +void DumpHexAscii (dng_stream &stream, + uint32 count) + { + + uint32 rows = (count + 15) >> 4; + + if (rows > gDumpLineLimit) + rows = gDumpLineLimit; + + for (uint32 row = 0; row < rows; row++) + { + + printf (" "); + + uint32 col; + + uint32 cols = count - (row << 4); + + if (cols > 16) + cols = 16; + + uint8 x [16]; + + for (col = 0; col < 16; col++) + { + + x [col] = ' '; + + if (col < cols) + { + + x [col] = stream.Get_uint8 (); + + printf ("%02x ", x [col]); + + } + + else + { + printf (" "); + } + + } + + printf (" "); + + for (col = 0; col < 16; col++) + { + + if (x [col] >= (uint8) ' ' && x [col] <= (uint8) '~') + { + printf ("%c", x [col]); + } + + else + { + printf ("."); + } + + } + + printf ("\n"); + + } + + if (count > rows * 16) + { + printf (" ... %u more bytes\n", (unsigned) (count - rows * 16)); + } + + } + +/*****************************************************************************/ + +void DumpHexAscii (const uint8 *buf, + uint32 count) + { + + uint32 rows = (count + 15) >> 4; + + if (rows > gDumpLineLimit) + rows = gDumpLineLimit; + + for (uint32 row = 0; row < rows; row++) + { + + printf (" "); + + uint32 col; + + uint32 cols = count - (row << 4); + + if (cols > 16) + cols = 16; + + uint8 x [16]; + + for (col = 0; col < 16; col++) + { + + x [col] = ' '; + + if (col < cols) + { + + x [col] = *(buf++); + + printf ("%02x ", x [col]); + + } + + else + { + printf (" "); + } + + } + + printf (" "); + + for (col = 0; col < 16; col++) + { + + if (x [col] >= (uint8) ' ' && x [col] <= (uint8) '~') + { + printf ("%c", x [col]); + } + + else + { + printf ("."); + } + + } + + printf ("\n"); + + } + + if (count > rows * 16) + { + printf (" ... %u more bytes\n", (unsigned) (count - rows * 16)); + } + + } + +/*****************************************************************************/ + +void DumpXMP (dng_stream &stream, + uint32 count) + { + + uint32 lineLength = 0; + + while (count > 0) + { + + uint32 x = stream.Get_uint8 (); + + if (x == 0) break; + + count--; + + if (lineLength == 0) + { + + printf ("XMP: "); + + lineLength = 5; + + } + + if (x == '\n' || + x == '\r') + { + + printf ("\n"); + + lineLength = 0; + + } + + else + { + + if (lineLength >= 128) + { + + printf ("\nXMP: "); + + lineLength = 5; + + } + + if (x >= ' ' && x <= '~') + { + + printf ("%c", (char) x); + + lineLength += 1; + + } + + else + { + + printf ("\\%03o", (unsigned) x); + + lineLength += 4; + + } + + } + + } + + if (lineLength != 0) + { + + printf ("\n"); + + } + + } + +/*****************************************************************************/ + +void DumpString (const dng_string &s) + { + + const uint32 kMaxDumpString = gDumpLineLimit * 64; + + printf ("\""); + + const char *ss = s.Get (); + + uint32 total = 0; + + while (*ss != 0 && total++ < kMaxDumpString) + { + + uint32 c = dng_string::DecodeUTF8 (ss); + + if (c >= ' ' && c <= '~') + { + printf ("%c", (char) c); + } + + else switch (c) + { + + case '\t': + { + printf ("\\t"); + break; + } + + case '\n': + { + printf ("\\n"); + break; + } + + case '\r': + { + printf ("\\r"); + break; + } + + default: + { + printf ("[%X]", (unsigned) c); + } + + } + + } + + uint32 extra = (uint32) strlen (ss); + + if (extra > 0) + { + printf ("...\" (%u more bytes)", (unsigned) extra); + } + + else + { + printf ("\""); + } + + } + +/*****************************************************************************/ + +void DumpTagValues (dng_stream &stream, + const char *entry_name, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + const char *tag_name) + { + + const uint32 kMaxDumpSingleLine = 4; + + const uint32 kMaxDumpArray = Max_uint32 (gDumpLineLimit, kMaxDumpSingleLine); + + printf ("%s:", tag_name ? tag_name + : LookupTagCode (parentCode, tagCode)); + + switch (tagType) + { + + case ttShort: + case ttLong: + case ttIFD: + case ttSByte: + case ttSShort: + case ttSLong: + case ttRational: + case ttSRational: + case ttFloat: + case ttDouble: + { + + if (tagCount > kMaxDumpSingleLine) + { + + printf (" %u entries", (unsigned) tagCount); + + } + + for (uint32 j = 0; j < tagCount && j < kMaxDumpArray; j++) + { + + if (tagCount <= kMaxDumpSingleLine) + { + + if (j == 0) + { + + printf (" %s =", entry_name); + + } + + printf (" "); + + } + + else + { + + printf ("\n %s [%u] = ", entry_name, (unsigned) j); + + } + + switch (tagType) + { + + case ttByte: + case ttShort: + case ttLong: + case ttIFD: + { + + uint32 x = stream.TagValue_uint32 (tagType); + + printf ("%u", (unsigned) x); + + break; + + } + + case ttSByte: + case ttSShort: + case ttSLong: + { + + int32 x = stream.TagValue_int32 (tagType); + + printf ("%d", (int) x); + + break; + + } + + case ttRational: + { + + dng_urational x = stream.TagValue_urational (tagType); + + printf ("%u/%u", (unsigned) x.n, (unsigned) x.d); + + break; + + } + + case ttSRational: + { + + dng_srational x = stream.TagValue_srational (tagType); + + printf ("%d/%d", (int) x.n, (int) x.d); + + break; + + } + + default: + { + + real64 x = stream.TagValue_real64 (tagType); + + printf ("%f", x); + + } + + } + + } + + printf ("\n"); + + if (tagCount > kMaxDumpArray) + { + + printf (" ... %u more entries\n", (unsigned) (tagCount - kMaxDumpArray)); + + } + + break; + + } + + case ttAscii: + { + + dng_string s; + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + s, + false); + + printf (" "); + + DumpString (s); + + printf ("\n"); + + break; + + } + + default: + { + + uint32 tagSize = tagCount * TagTypeSize (tagType); + + if (tagCount == 1 && (tagType == ttByte || + tagType == ttUndefined)) + { + + uint8 x = stream.Get_uint8 (); + + printf (" %s = %u\n", LookupTagType (tagType), x); + + } + + else + { + + printf (" %s, size = %u\n", LookupTagType (tagType), (unsigned) tagSize); + + DumpHexAscii (stream, tagSize); + + } + + } + + } + + } + +/*****************************************************************************/ + +void DumpMatrix (const dng_matrix &m) + { + + for (uint32 row = 0; row < m.Rows (); row++) + { + + for (uint32 col = 0; col < m.Cols (); col++) + { + + if (col == 0) + printf (" "); + else + printf (" "); + + printf ("%8.4f", m [row] [col]); + + } + + printf ("\n"); + + } + + } + +/*****************************************************************************/ + +void DumpVector (const dng_vector &v) + { + + for (uint32 index = 0; index < v.Count (); index++) + { + + printf (" %0.4f", v [index]); + + } + + printf ("\n"); + + } + +/*****************************************************************************/ + +void DumpDateTime (const dng_date_time &dt) + { + + printf ("%04d:%02d:%02d %02d:%02d:%02d", + (int) dt.fYear, + (int) dt.fMonth, + (int) dt.fDay, + (int) dt.fHour, + (int) dt.fMinute, + (int) dt.fSecond); + + } + +/*****************************************************************************/ + +void DumpExposureTime (real64 x) + { + + if (x > 0.0) + { + + if (x >= 0.25) + { + printf ("%0.2f sec", x); + } + + else if (x >= 0.01) + { + printf ("1/%0.1f sec", 1.0 / x); + } + + else + { + printf ("1/%0.0f sec", 1.0 / x); + } + + } + + else + { + + printf (""); + + } + + } + +/*****************************************************************************/ + +void DumpFingerprint (const dng_fingerprint &p) + { + + printf ("<"); + + for (uint32 j = 0; j < 16; j++) + { + printf ("%02x", p.data [j]); + } + + printf (">"); + + } + +/*****************************************************************************/ + +void DumpHueSatMap (dng_stream &stream, + uint32 hues, + uint32 sats, + uint32 vals, + bool skipSat0) + { + + uint32 doneLines = 0; + uint32 skipLines = 0; + + for (uint32 v = 0; v < vals; v++) + { + + for (uint32 h = 0; h < hues; h++) + { + + for (uint32 s = skipSat0 ? 1 : 0; s < sats; s++) + { + + real32 dh = stream.Get_real32 (); + real32 ds = stream.Get_real32 (); + real32 dv = stream.Get_real32 (); + + if (gDumpLineLimit == 0 || + gDumpLineLimit > doneLines) + { + + doneLines++; + + if (vals == 1) + { + + printf (" h [%2u] s [%2u]: h=%8.4f s=%6.4f v=%6.4f\n", + (unsigned) h, + (unsigned) s, + (double) dh, + (double) ds, + (double) dv); + + } + + else + { + + printf (" v [%2u] h [%2u] s [%2u]: h=%8.4f s=%6.4f v=%6.4f\n", + (unsigned) v, + (unsigned) h, + (unsigned) s, + (double) dh, + (double) ds, + (double) dv); + + } + + } + + else + { + + skipLines++; + + } + + } + + } + + } + + if (skipLines > 0) + { + + printf (" ... %u more entries\n", (unsigned) skipLines); + + } + + } + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ + +bool CheckTagType (uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint16 validType0, + uint16 validType1, + uint16 validType2, + uint16 validType3) + { + + if (tagType != validType0 && + tagType != validType1 && + tagType != validType2 && + tagType != validType3) + { + + #if qDNGValidate + + { + + char message [256]; + + sprintf (message, + "%s %s has unexpected type (%s)", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode), + LookupTagType (tagType)); + + ReportWarning (message); + + } + + #else + + (void) parentCode; // Unused + (void) tagCode; // Unused + + #endif + + return false; + + } + + return true; + + } + +/*****************************************************************************/ + +bool CheckTagCount (uint32 parentCode, + uint32 tagCode, + uint32 tagCount, + uint32 minCount, + uint32 maxCount) + { + + if (maxCount < minCount) + maxCount = minCount; + + if (tagCount < minCount || + tagCount > maxCount) + { + + #if qDNGValidate + + { + + char message [256]; + + sprintf (message, + "%s %s has unexpected count (%u)", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode), + (unsigned) tagCount); + + ReportWarning (message); + + } + + #else + + (void) parentCode; // Unused + (void) tagCode; // Unused + + #endif + + return false; + + } + + return true; + + } + +/*****************************************************************************/ + +bool CheckColorImage (uint32 parentCode, + uint32 tagCode, + uint32 colorPlanes) + { + + if (colorPlanes == 0) + { + + #if qDNGValidate + + { + + char message [256]; + + sprintf (message, + "%s %s is not allowed with unknown color plane count " + " (missing ColorMatrix1 tag?)", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + #else + + (void) parentCode; // Unused + (void) tagCode; // Unused + + #endif + + return false; + + } + + if (colorPlanes == 1) + { + + #if qDNGValidate + + { + + char message [256]; + + sprintf (message, + "%s %s is not allowed with monochrome images", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + #endif + + return false; + + } + + return true; + + } + +/*****************************************************************************/ + +bool CheckMainIFD (uint32 parentCode, + uint32 tagCode, + uint32 newSubFileType) + { + + if (newSubFileType != sfMainImage) + { + + #if qDNGValidate + + { + + char message [256]; + + sprintf (message, + "%s %s is not allowed IFDs with NewSubFileType != 0", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + #else + + (void) parentCode; // Unused + (void) tagCode; // Unused + + #endif + + return false; + + } + + return true; + + } + +/*****************************************************************************/ + +bool CheckRawIFD (uint32 parentCode, + uint32 tagCode, + uint32 photometricInterpretation) + { + + if (photometricInterpretation != piCFA && + photometricInterpretation != piLinearRaw) + { + + #if qDNGValidate + + { + + char message [256]; + + sprintf (message, + "%s %s is not allowed in IFDs with a non-raw PhotometricInterpretation", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + #else + + (void) parentCode; // Unused + (void) tagCode; // Unused + + #endif + + return false; + + } + + return true; + + } + +/*****************************************************************************/ + +bool CheckCFA (uint32 parentCode, + uint32 tagCode, + uint32 photometricInterpretation) + { + + if (photometricInterpretation != piCFA) + { + + #if qDNGValidate + + { + + char message [256]; + + sprintf (message, + "%s %s is not allowed in IFDs with a non-CFA PhotometricInterpretation", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + #else + + (void) parentCode; // Unused + (void) tagCode; // Unused + + #endif + + return false; + + } + + return true; + + } + +/*****************************************************************************/ + +void ParseStringTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagCount, + dng_string &s, + bool trimBlanks) + { + + if (tagCount == 0 || + tagCount == 0xFFFFFFFF) + { + + s.Clear (); + + return; + + } + + dng_memory_data temp_buffer (tagCount + 1); + + char *buffer = temp_buffer.Buffer_char (); + + stream.Get (buffer, tagCount); + + // Make sure the string is null terminated. + + if (buffer [tagCount - 1] != 0) + { + + buffer [tagCount] = 0; + + #if qDNGValidate + + { + + bool hasNull = false; + + for (uint32 j = 0; j < tagCount; j++) + { + + if (buffer [j] == 0) + { + + hasNull = true; + + break; + + } + + } + + if (!hasNull && parentCode < tcFirstMakerNoteIFD) + { + + char message [256]; + + sprintf (message, + "%s %s is not NULL terminated", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + } + + #else + + (void) parentCode; // Unused + (void) tagCode; // Unused + + #endif + + } + + // Medata working group - Allow UTF-8 + + s.Set_UTF8_or_System (buffer); + + if (trimBlanks) + { + + s.TrimTrailingBlanks (); + + } + + } + +/*****************************************************************************/ + +void ParseDualStringTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagCount, + dng_string &s1, + dng_string &s2) + { + + if (tagCount == 0 || + tagCount == 0xFFFFFFFF) + { + + s1.Clear (); + s2.Clear (); + + return; + + } + + dng_memory_data temp_buffer (tagCount + 1); + + char *buffer = temp_buffer.Buffer_char (); + + stream.Get (buffer, tagCount); + + // Make sure the string is null terminated. + + if (buffer [tagCount - 1] != 0) + { + + buffer [tagCount] = 0; + + #if qDNGValidate + + { + + uint32 nullCount = 0; + + for (uint32 j = 0; j < tagCount; j++) + { + + if (buffer [j] == 0) + { + + nullCount++; + + } + + } + + if (nullCount < 2 && parentCode < tcFirstMakerNoteIFD) + { + + char message [256]; + + sprintf (message, + "%s %s is not NULL terminated", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + } + + #else + + (void) parentCode; // Unused + (void) tagCode; // Unused + + #endif + + } + + // Medata working group - Allow UTF-8 + + s1.Set_UTF8_or_System (buffer); + + s2.Set_ASCII (NULL); + + for (uint32 j = 1; j < tagCount - 1; j++) + { + + if (buffer [j - 1] != 0 && + buffer [j ] == 0) + { + + // Medata working group - Allow UTF-8 + + s2.Set_UTF8_or_System (buffer + j + 1); + + break; + + } + + } + + s1.TrimTrailingBlanks (); + s2.TrimTrailingBlanks (); + + } + +/*****************************************************************************/ + +void ParseEncodedStringTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagCount, + dng_string &s) + { + + if (tagCount < 8) + { + + #if qDNGValidate + + { + + char message [256]; + + sprintf (message, + "%s %s has unexpected count (%u)", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode), + (unsigned) tagCount); + + ReportWarning (message); + + } + + #else + + (void) parentCode; // Unused + (void) tagCode; // Unused + + #endif + + s.Clear (); + + return; + + } + + char label [8]; + + stream.Get (label, 8); + + // Sometimes lowercase is used by mistake. Accept this, but issue + // warning. + + { + + bool hadLower = false; + + for (uint32 j = 0; j < 8; j++) + { + + if (label [j] >= 'a' && label [j] <= 'z') + { + + label [j] = 'A' + (label [j] - 'a'); + + hadLower = true; + + } + + } + + #if qDNGValidate + + if (hadLower) + { + + char message [256]; + + sprintf (message, + "%s %s text encoding label not all uppercase", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + #endif + + } + + if (memcmp (label, "UNICODE\000", 8) == 0) + { + + uint32 uChars = (tagCount - 8) >> 1; + + dng_memory_data temp_buffer ((uChars + 1) * 2); + + uint16 *buffer = temp_buffer.Buffer_uint16 (); + + for (uint32 j = 0; j < uChars; j++) + { + + buffer [j] = stream.Get_uint16 (); + + } + + buffer [uChars] = 0; + + #if qDNGValidate + + { + + // If the writer used UTF-8 rather than UTF-16, and padded + // the string with blanks, then there will be lots of 0x2020 + // (unicode dagger symbol) characters in the string. + + uint32 count2020 = 0; + + for (uint32 k = 0; buffer [k] != 0; k++) + { + + if (buffer [k] == 0x2020) + { + + count2020++; + + } + + } + + if (count2020 > 1) + { + + char message [256]; + + sprintf (message, + "%s %s text appears to be UTF-8 rather than UTF-16", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + } + + #endif + + s.Set_UTF16 (buffer); + + } + + else + { + + uint32 aChars = tagCount - 8; + + dng_memory_data temp_buffer (aChars + 1); + + char *buffer = temp_buffer.Buffer_char (); + + stream.Get (buffer, aChars); + + buffer [aChars] = 0; + + enum dng_encoding + { + dng_encoding_ascii, + dng_encoding_jis_x208_1990, + dng_encoding_unknown + }; + + dng_encoding encoding = dng_encoding_unknown; + + if (memcmp (label, "ASCII\000\000\000", 8) == 0) + { + + encoding = dng_encoding_ascii; + + } + + else if (memcmp (label, "JIS\000\000\000\000\000\000", 8) == 0) + { + + encoding = dng_encoding_jis_x208_1990; + + } + + else + { + + // Some Nikon D1 files have UserComment tags with zero encoding bits and + // garbage text values. So don't try to parse tags with unknown text + // encoding unless all the characters are printing ASCII. + + #if qDNGValidate + + if (memcmp (label, "\000\000\000\000\000\000\000\000\000", 8) == 0) + { + + // Many camera makes store null tags with all zero encoding, so + // don't report a warning message for null strings. + + if (buffer [0] != 0) + { + + char message [256]; + + sprintf (message, + "%s %s has unknown encoding", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + } + + else + { + + char message [256]; + + sprintf (message, + "%s %s has unexpected text encoding", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + #endif + + } + + // If text encoding was unknown, and the text is anything + // other than pure ASCII, then ignore it. + + if (encoding == dng_encoding_unknown) + { + + encoding = dng_encoding_ascii; + + for (uint32 i = 0; i < aChars && buffer [i] != 0; i++) + { + + if (buffer [i] < ' ' || + buffer [i] > '~') + { + + buffer [0] = 0; + + break; + + } + + } + + } + + switch (encoding) + { + + case dng_encoding_ascii: + { + + // Medata working group - allow UTF-8 for ASCII tags. + + s.Set_UTF8_or_System (buffer); + + break; + + } + + case dng_encoding_jis_x208_1990: + { + s.Set_JIS_X208_1990 (buffer); + break; + } + + case dng_encoding_unknown: + { + s.Set_SystemEncoding (buffer); + break; + } + + default: + break; + + } + + #if qDNGValidate + + { + + if (encoding == dng_encoding_ascii && !s.IsASCII ()) + { + + char message [256]; + + sprintf (message, + "%s %s has non-ASCII characters", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + } + + #endif + + } + + s.TrimTrailingBlanks (); + + } + +/*****************************************************************************/ + +bool ParseMatrixTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint32 rows, + uint32 cols, + dng_matrix &m) + { + + if (CheckTagCount (parentCode, tagCode, tagCount, rows * cols)) + { + + dng_matrix temp (rows, cols); + + for (uint32 row = 0; row < rows; row++) + for (uint32 col = 0; col < cols; col++) + { + + temp [row] [col] = stream.TagValue_real64 (tagType); + + } + + m = temp; + + return true; + + } + + return false; + + } + +/*****************************************************************************/ + +bool ParseVectorTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint32 count, + dng_vector &v) + { + + if (CheckTagCount (parentCode, tagCode, tagCount, count)) + { + + dng_vector temp (count); + + for (uint32 index = 0; index < count; index++) + { + + temp [index] = stream.TagValue_real64 (tagType); + + } + + v = temp; + + return true; + + } + + return false; + + } + +/*****************************************************************************/ + +bool ParseDateTimeTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + dng_date_time &dt) + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttAscii)) + { + return false; + } + + // Kludge: Some versions of PaintShop Pro write these fields + // with a length of 21 rather than 20. Otherwise they are + // correctly formated. So relax this test and allow these + // these longer than standard tags to be parsed. + + (void) CheckTagCount (parentCode, tagCode, tagCount, 20); + + if (tagCount < 20) + { + return false; + } + + char s [21]; + + stream.Get (s, 20); + + s [20] = 0; + + // See if this is a valid date/time string. + + if (dt.Parse (s)) + { + return true; + } + + // Accept strings that contain only blanks, colons, and zeros as + // valid "null" dates. + + dt = dng_date_time (); + + for (uint32 index = 0; index < 21; index++) + { + + char c = s [index]; + + if (c == 0) + { + return true; + } + + if (c != ' ' && c != ':' && c != '0') + { + + #if qDNGValidate + + { + + char message [256]; + + sprintf (message, + "%s %s is not a valid date/time", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + #endif + + return false; + + } + + } + + return false; + + } + +/*****************************************************************************/ diff --git a/dng/dng_parse_utils.h b/dng/dng_parse_utils.h new file mode 100644 index 0000000..bae975c --- /dev/null +++ b/dng/dng_parse_utils.h @@ -0,0 +1,231 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef __dng_parse_utils__ +#define __dng_parse_utils__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_flags.h" +#include "dng_types.h" +#include "dng_stream.h" +#include "dng_string.h" +#include "dng_matrix.h" + +/*****************************************************************************/ + +#if qDNGValidate + +/*****************************************************************************/ + +const char * LookupParentCode (uint32 parentCode); + +/*****************************************************************************/ + +const char * LookupTagCode (uint32 parentCode, + uint32 tagCode); + +/*****************************************************************************/ + +const char * LookupTagType (uint32 tagType); + +/*****************************************************************************/ + +const char * LookupNewSubFileType (uint32 key); + +const char * LookupCompression (uint32 key); + +const char * LookupPredictor (uint32 key); + +const char * LookupSampleFormat (uint32 key); + +const char * LookupPhotometricInterpretation (uint32 key); + +const char * LookupOrientation (uint32 key); + +const char * LookupResolutionUnit (uint32 key); + +const char * LookupCFAColor (uint32 key); + +const char * LookupSensingMethod (uint32 key); + +const char * LookupExposureProgram (uint32 key); + +const char * LookupMeteringMode (uint32 key); + +const char * LookupLightSource (uint32 key); + +const char * LookupColorSpace (uint32 key); + +const char * LookupFileSource (uint32 key); + +const char * LookupSceneType (uint32 key); + +const char * LookupCustomRendered (uint32 key); + +const char * LookupExposureMode (uint32 key); + +const char * LookupWhiteBalance (uint32 key); + +const char * LookupSceneCaptureType (uint32 key); + +const char * LookupGainControl (uint32 key); + +const char * LookupContrast (uint32 key); + +const char * LookupSaturation (uint32 key); + +const char * LookupSharpness (uint32 key); + +const char * LookupSubjectDistanceRange (uint32 key); + +const char * LookupComponent (uint32 key); + +const char * LookupCFALayout (uint32 key); + +const char * LookupMakerNoteSafety (uint32 key); + +const char * LookupColorimetricReference (uint32 key); + +const char * LookupPreviewColorSpace (uint32 key); + +const char * LookupJPEGMarker (uint32 key); + +const char * LookupSensitivityType (uint32 key); + +const char * LookupDepthFormat (uint32 key); + +const char * LookupDepthUnits (uint32 key); + +const char * LookupDepthMeasureType (uint32 key); + +/*****************************************************************************/ + +void DumpHexAscii (dng_stream &stream, + uint32 count); + +void DumpHexAscii (const uint8 *buf, + uint32 count); + +void DumpXMP (dng_stream &stream, + uint32 count); + +void DumpString (const dng_string &s); + +void DumpTagValues (dng_stream &stream, + const char *entry_name, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + const char *tag_name = NULL); + +void DumpMatrix (const dng_matrix &m); + +void DumpVector (const dng_vector &v); + +void DumpDateTime (const dng_date_time &dt); + +void DumpExposureTime (real64 x); + +void DumpFingerprint (const dng_fingerprint &p); + +void DumpHueSatMap (dng_stream &stream, + uint32 hues, + uint32 sats, + uint32 vals, + bool skipSat0); + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ + +bool CheckTagType (uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint16 validType0, + uint16 validType1 = 0, + uint16 validType2 = 0, + uint16 validType3 = 0); + +bool CheckTagCount (uint32 parentCode, + uint32 tagCode, + uint32 tagCount, + uint32 minCount, + uint32 maxCount = 0); + +bool CheckColorImage (uint32 parentCode, + uint32 tagCode, + uint32 colorPlanes); + +bool CheckMainIFD (uint32 parentCode, + uint32 tagCode, + uint32 newSubFileType); + +bool CheckRawIFD (uint32 parentCode, + uint32 tagCode, + uint32 photometricInterpretation); + +bool CheckCFA (uint32 parentCode, + uint32 tagCode, + uint32 photometricInterpretation); + +/*****************************************************************************/ + +void ParseStringTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagCount, + dng_string &s, + bool trimBlanks = true); + +void ParseDualStringTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagCount, + dng_string &s1, + dng_string &s2); + +void ParseEncodedStringTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagCount, + dng_string &s); + +bool ParseMatrixTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint32 rows, + uint32 cols, + dng_matrix &m); + +bool ParseVectorTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint32 count, + dng_vector &v); + +bool ParseDateTimeTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + dng_date_time &dt); + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_pixel_buffer.cpp b/dng/dng_pixel_buffer.cpp new file mode 100644 index 0000000..1d597a1 --- /dev/null +++ b/dng/dng_pixel_buffer.cpp @@ -0,0 +1,1947 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_pixel_buffer.h" + +#include "dng_bottlenecks.h" +#include "dng_exceptions.h" +#include "dng_flags.h" +#include "dng_tag_types.h" +#include "dng_tag_values.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +static bool SafeUint32ToInt32Mult (uint32 arg1, + uint32 arg2, + int32 *result) + { + + uint32 uint32_result; + + return (SafeUint32Mult (arg1, + arg2, + &uint32_result) && + + ConvertUint32ToInt32 (uint32_result, + result)); + + } + +/*****************************************************************************/ + +void OptimizeOrder (const void *&sPtr, + void *&dPtr, + uint32 sPixelSize, + uint32 dPixelSize, + uint32 &count0, + uint32 &count1, + uint32 &count2, + int32 &sStep0, + int32 &sStep1, + int32 &sStep2, + int32 &dStep0, + int32 &dStep1, + int32 &dStep2) + { + + uint32 step0; + uint32 step1; + uint32 step2; + + // Optimize the order for the data that is most spread out. + + uint32 sRange = Abs_int32 (sStep0) * (count0 - 1) + + Abs_int32 (sStep1) * (count1 - 1) + + Abs_int32 (sStep2) * (count2 - 1); + + uint32 dRange = Abs_int32 (dStep0) * (count0 - 1) + + Abs_int32 (dStep1) * (count1 - 1) + + Abs_int32 (dStep2) * (count2 - 1); + + if (dRange >= sRange) + { + + if (dStep0 < 0) + { + + sPtr = (const void *) + (((const uint8 *) sPtr) + (int32)(count0 - 1) * sStep0 * (int32)sPixelSize); + + dPtr = (void *) + (((uint8 *) dPtr) + (int32)(count0 - 1) * dStep0 * (int32)dPixelSize); + + sStep0 = -sStep0; + dStep0 = -dStep0; + + } + + if (dStep1 < 0) + { + + sPtr = (const void *) + (((const uint8 *) sPtr) + (int32)(count1 - 1) * sStep1 * (int32)sPixelSize); + + dPtr = (void *) + (((uint8 *) dPtr) + (int32)(count1 - 1) * dStep1 * (int32)dPixelSize); + + sStep1 = -sStep1; + dStep1 = -dStep1; + + } + + if (dStep2 < 0) + { + + sPtr = (const void *) + (((const uint8 *) sPtr) + (int32)(count2 - 1) * sStep2 * (int32)sPixelSize); + + dPtr = (void *) + (((uint8 *) dPtr) + (int32)(count2 - 1) * dStep2 * (int32)dPixelSize); + + sStep2 = -sStep2; + dStep2 = -dStep2; + + } + + step0 = (uint32) dStep0; + step1 = (uint32) dStep1; + step2 = (uint32) dStep2; + + } + + else + { + + if (sStep0 < 0) + { + + sPtr = (const void *) + (((const uint8 *) sPtr) + (int32)(count0 - 1) * sStep0 * (int32)sPixelSize); + + dPtr = (void *) + (((uint8 *) dPtr) + (int32)(count0 - 1) * dStep0 * (int32)dPixelSize); + + sStep0 = -sStep0; + dStep0 = -dStep0; + + } + + if (sStep1 < 0) + { + + sPtr = (const void *) + (((const uint8 *) sPtr) + (int32)(count1 - 1) * sStep1 * (int32)sPixelSize); + + dPtr = (void *) + (((uint8 *) dPtr) + (int32)(count1 - 1) * dStep1 * (int32)dPixelSize); + + sStep1 = -sStep1; + dStep1 = -dStep1; + + } + + if (sStep2 < 0) + { + + sPtr = (const void *) + (((const uint8 *) sPtr) + (int32)(count2 - 1) * sStep2 * (int32)sPixelSize); + + dPtr = (void *) + (((uint8 *) dPtr) + (int32)(count2 - 1) * dStep2 * (int32)dPixelSize); + + sStep2 = -sStep2; + dStep2 = -dStep2; + + } + + step0 = (uint32) sStep0; + step1 = (uint32) sStep1; + step2 = (uint32) sStep2; + + } + + if (count0 == 1) step0 = 0xFFFFFFFF; + if (count1 == 1) step1 = 0xFFFFFFFF; + if (count2 == 1) step2 = 0xFFFFFFFF; + + uint32 index0; + uint32 index1; + uint32 index2; + + if (step0 >= step1) + { + + if (step1 >= step2) + { + index0 = 0; + index1 = 1; + index2 = 2; + } + + else if (step2 >= step0) + { + index0 = 2; + index1 = 0; + index2 = 1; + } + + else + { + index0 = 0; + index1 = 2; + index2 = 1; + } + + } + + else + { + + if (step0 >= step2) + { + index0 = 1; + index1 = 0; + index2 = 2; + } + + else if (step2 >= step1) + { + index0 = 2; + index1 = 1; + index2 = 0; + } + + else + { + index0 = 1; + index1 = 2; + index2 = 0; + } + + } + + uint32 count [3]; + + count [0] = count0; + count [1] = count1; + count [2] = count2; + + count0 = count [index0]; + count1 = count [index1]; + count2 = count [index2]; + + int32 step [3]; + + step [0] = sStep0; + step [1] = sStep1; + step [2] = sStep2; + + sStep0 = step [index0]; + sStep1 = step [index1]; + sStep2 = step [index2]; + + step [0] = dStep0; + step [1] = dStep1; + step [2] = dStep2; + + dStep0 = step [index0]; + dStep1 = step [index1]; + dStep2 = step [index2]; + + if (sStep0 == ((int32) count1) * sStep1 && + dStep0 == ((int32) count1) * dStep1) + { + count1 *= count0; + count0 = 1; + } + + if (sStep1 == ((int32) count2) * sStep2 && + dStep1 == ((int32) count2) * dStep2) + { + count2 *= count1; + count1 = 1; + } + + } + +/*****************************************************************************/ + +void OptimizeOrder (const void *&sPtr, + uint32 sPixelSize, + uint32 &count0, + uint32 &count1, + uint32 &count2, + int32 &sStep0, + int32 &sStep1, + int32 &sStep2) + { + + void *dPtr = NULL; + + int32 dStep0 = sStep0; + int32 dStep1 = sStep1; + int32 dStep2 = sStep2; + + OptimizeOrder (sPtr, + dPtr, + sPixelSize, + sPixelSize, + count0, + count1, + count2, + sStep0, + sStep1, + sStep2, + dStep0, + dStep1, + dStep2); + + } + +/*****************************************************************************/ + +void OptimizeOrder (void *&dPtr, + uint32 dPixelSize, + uint32 &count0, + uint32 &count1, + uint32 &count2, + int32 &dStep0, + int32 &dStep1, + int32 &dStep2) + { + + const void *sPtr = NULL; + + int32 sStep0 = dStep0; + int32 sStep1 = dStep1; + int32 sStep2 = dStep2; + + OptimizeOrder (sPtr, + dPtr, + dPixelSize, + dPixelSize, + count0, + count1, + count2, + sStep0, + sStep1, + sStep2, + dStep0, + dStep1, + dStep2); + + } + +/*****************************************************************************/ + +dng_pixel_buffer::dng_pixel_buffer () + + : fArea () + , fPlane (0) + , fPlanes (1) + , fRowStep (1) + , fColStep (1) + , fPlaneStep (1) + , fPixelType (ttUndefined) + , fPixelSize (0) + , fData (NULL) + , fDirty (true) + + { + + } + +/*****************************************************************************/ + +dng_pixel_buffer::dng_pixel_buffer (const dng_rect &area, + uint32 plane, + uint32 planes, + uint32 pixelType, + uint32 planarConfiguration, + void *data) + + : fArea (area) + , fPlane (plane) + , fPlanes (planes) + , fRowStep (0) + , fColStep (0) + , fPlaneStep (0) + , fPixelType (pixelType) + , fPixelSize (TagTypeSize (pixelType)) + , fData (data) + , fDirty (true) + + { + + const char *overflowMessage = "Arithmetic overflow in pixel buffer setup"; + + // Initialize fRowStep, fColStep and fPlaneStep according to the desired + // pixel layout. + + switch (planarConfiguration) + { + + case pcInterleaved: + { + + fPlaneStep = 1; + + if (!ConvertUint32ToInt32 (fPlanes, &fColStep) || + !SafeUint32ToInt32Mult (fArea.W (), fPlanes, &fRowStep)) + { + ThrowOverflow (overflowMessage); + } + + break; + + } + + case pcPlanar: + { + + fColStep = 1; + + // Even though we've hardened dng_rect::W() to guarantee that it + // will never return a result that's out of range for an int32, we + // still protect the conversion for defense in depth. + + if (!ConvertUint32ToInt32 (fArea.W (), &fRowStep) || + !SafeUint32ToInt32Mult (fArea.H (), fArea.W (), &fPlaneStep)) + { + ThrowOverflow (overflowMessage); + } + + break; + + } + + case pcRowInterleaved: + case pcRowInterleavedAlignSIMD: + { + + fColStep = 1; + + uint32 planeStepUint32; + + if (planarConfiguration == pcRowInterleaved) + { + planeStepUint32 = fArea.W (); + } + + else + { + + if (!RoundUpForPixelSize (fArea.W (), + fPixelSize, + &planeStepUint32)) + { + ThrowOverflow (overflowMessage); + } + + } + + if (!ConvertUint32ToInt32 (planeStepUint32, &fPlaneStep) || + !SafeUint32ToInt32Mult (planeStepUint32, fPlanes, &fRowStep)) + { + ThrowOverflow (overflowMessage); + } + + break; + + } + + default: + { + ThrowProgramError ("Invalid value for 'planarConfiguration'"); + break; + } + + } + + } + +/*****************************************************************************/ + +dng_pixel_buffer::dng_pixel_buffer (const dng_pixel_buffer &buffer) + + : fArea (buffer.fArea) + , fPlane (buffer.fPlane) + , fPlanes (buffer.fPlanes) + , fRowStep (buffer.fRowStep) + , fColStep (buffer.fColStep) + , fPlaneStep (buffer.fPlaneStep) + , fPixelType (buffer.fPixelType) + , fPixelSize (buffer.fPixelSize) + , fData (buffer.fData) + , fDirty (buffer.fDirty) + + { + + } + +/*****************************************************************************/ + +dng_pixel_buffer & dng_pixel_buffer::operator= (const dng_pixel_buffer &buffer) + { + + fArea = buffer.fArea; + fPlane = buffer.fPlane; + fPlanes = buffer.fPlanes; + fRowStep = buffer.fRowStep; + fColStep = buffer.fColStep; + fPlaneStep = buffer.fPlaneStep; + fPixelType = buffer.fPixelType; + fPixelSize = buffer.fPixelSize; + fPixelType = buffer.fPixelType; + fData = buffer.fData; + fDirty = buffer.fDirty; + + return *this; + + } + +/*****************************************************************************/ + +dng_pixel_buffer::~dng_pixel_buffer () + { + + } + +/*****************************************************************************/ + +#if qDebugPixelType + +void dng_pixel_buffer::CheckPixelType (uint32 pixelType) const + { + + if (fPixelType != pixelType) + { + + DNG_REPORT ("Pixel type access mismatch"); + + } + + } + +#endif + +/*****************************************************************************/ + +uint32 dng_pixel_buffer::PixelRange () const + { + + switch (fPixelType) + { + + case ttByte: + case ttSByte: + { + return 0x0FF; + } + + case ttShort: + case ttSShort: + { + return 0x0FFFF; + } + + case ttLong: + case ttSLong: + { + return 0xFFFFFFFF; + } + + default: + break; + + } + + return 0; + + } + +/*****************************************************************************/ + +void dng_pixel_buffer::SetConstant (const dng_rect &area, + uint32 plane, + uint32 planes, + uint32 value) + { + + uint32 rows = area.H (); + uint32 cols = area.W (); + + void *dPtr = DirtyPixel (area.t, + area.l, + plane); + + int32 dRowStep = fRowStep; + int32 dColStep = fColStep; + int32 dPlaneStep = fPlaneStep; + + OptimizeOrder (dPtr, + fPixelSize, + rows, + cols, + planes, + dRowStep, + dColStep, + dPlaneStep); + + switch (fPixelSize) + { + + case 1: + { + + if (rows == 1 && cols == 1 && dPlaneStep == 1 && value == 0) + { + + DoZeroBytes (dPtr, planes); + + } + + else + { + + DoSetArea8 ((uint8 *) dPtr, + (uint8) value, + rows, + cols, + planes, + dRowStep, + dColStep, + dPlaneStep); + + } + + break; + + } + + case 2: + { + + if (rows == 1 && cols == 1 && dPlaneStep == 1 && value == 0) + { + + DoZeroBytes (dPtr, planes << 1); + + } + + else + { + + DoSetArea16 ((uint16 *) dPtr, + (uint16) value, + rows, + cols, + planes, + dRowStep, + dColStep, + dPlaneStep); + + } + + break; + + } + + case 4: + { + + if (rows == 1 && cols == 1 && dPlaneStep == 1 && value == 0) + { + + DoZeroBytes (dPtr, planes << 2); + + } + + else + { + + DoSetArea32 ((uint32 *) dPtr, + value, + rows, + cols, + planes, + dRowStep, + dColStep, + dPlaneStep); + + } + + break; + + } + + default: + { + + ThrowNotYetImplemented (); + + } + + } + + } + +/*****************************************************************************/ + +void dng_pixel_buffer::SetZero (const dng_rect &area, + uint32 plane, + uint32 planes) + { + + uint32 value = 0; + + switch (fPixelType) + { + + case ttByte: + case ttShort: + case ttLong: + case ttFloat: + { + break; + } + + case ttSShort: + { + value = 0x8000; + break; + } + + default: + { + + ThrowNotYetImplemented (); + + } + + } + + SetConstant (area, + plane, + planes, + value); + + } + +/*****************************************************************************/ + +void dng_pixel_buffer::CopyArea (const dng_pixel_buffer &src, + const dng_rect &area, + uint32 srcPlane, + uint32 dstPlane, + uint32 planes) + { + + uint32 rows = area.H (); + uint32 cols = area.W (); + + const void *sPtr = src.ConstPixel (area.t, + area.l, + srcPlane); + + void *dPtr = DirtyPixel (area.t, + area.l, + dstPlane); + + int32 sRowStep = src.fRowStep; + int32 sColStep = src.fColStep; + int32 sPlaneStep = src.fPlaneStep; + + int32 dRowStep = fRowStep; + int32 dColStep = fColStep; + int32 dPlaneStep = fPlaneStep; + + OptimizeOrder (sPtr, + dPtr, + src.fPixelSize, + fPixelSize, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + if (fPixelType == src.fPixelType) + { + + if (rows == 1 && cols == 1 && sPlaneStep == 1 && dPlaneStep == 1) + { + + DoCopyBytes (sPtr, + dPtr, + planes * fPixelSize); + + } + + else switch (fPixelSize) + { + + case 1: + { + + DoCopyArea8 ((const uint8 *) sPtr, + (uint8 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case 2: + { + + DoCopyArea16 ((const uint16 *) sPtr, + (uint16 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case 4: + { + + DoCopyArea32 ((const uint32 *) sPtr, + (uint32 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + default: + { + + ThrowNotYetImplemented (); + + } + + } + + } + + else if (src.fPixelType == ttByte) + { + + switch (fPixelType) + { + + case ttShort: + { + + DoCopyArea8_16 ((const uint8 *) sPtr, + (uint16 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case ttSShort: + { + + DoCopyArea8_S16 ((const uint8 *) sPtr, + (int16 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case ttLong: + { + + DoCopyArea8_32 ((const uint8 *) sPtr, + (uint32 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case ttFloat: + { + + DoCopyArea8_R32 ((const uint8 *) sPtr, + (real32 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep, + src.PixelRange ()); + + break; + + } + + default: + { + + ThrowNotYetImplemented (); + + } + + } + + } + + else if (src.fPixelType == ttShort) + { + + switch (fPixelType) + { + + case ttByte: + { + + DoCopyArea8 (((const uint8 *) sPtr) + (qDNGBigEndian ? 1 : 0), + (uint8 *) dPtr, + rows, + cols, + planes, + sRowStep << 1, + sColStep << 1, + sPlaneStep << 1, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case ttSShort: + { + + DoCopyArea16_S16 ((const uint16 *) sPtr, + (int16 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case ttLong: + { + + DoCopyArea16_32 ((const uint16 *) sPtr, + (uint32 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case ttFloat: + { + + DoCopyArea16_R32 ((const uint16 *) sPtr, + (real32 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep, + src.PixelRange ()); + + break; + + } + + default: + { + + ThrowNotYetImplemented (); + + } + + } + + } + + else if (src.fPixelType == ttSShort) + { + + switch (fPixelType) + { + + case ttByte: + { + + DoCopyArea8 (((const uint8 *) sPtr) + (qDNGBigEndian ? 1 : 0), + (uint8 *) dPtr, + rows, + cols, + planes, + sRowStep << 1, + sColStep << 1, + sPlaneStep << 1, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case ttShort: + { + + // Moving between signed 16 bit values and unsigned 16 + // bit values just requires toggling the sign bit. So + // we can use the "backwards" bottleneck. + + DoCopyArea16_S16 ((const uint16 *) sPtr, + (int16 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case ttFloat: + { + + DoCopyAreaS16_R32 ((const int16 *) sPtr, + (real32 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep, + src.PixelRange ()); + + break; + + } + + default: + { + + ThrowNotYetImplemented (); + + } + + } + + } + + else if (src.fPixelType == ttLong) + { + + switch (fPixelType) + { + + case ttByte: + { + + DoCopyArea8 (((const uint8 *) sPtr) + (qDNGBigEndian ? 3 : 0), + (uint8 *) dPtr, + rows, + cols, + planes, + sRowStep << 2, + sColStep << 2, + sPlaneStep << 2, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case ttShort: + { + + DoCopyArea16 (((const uint16 *) sPtr) + (qDNGBigEndian ? 1 : 0), + (uint16 *) dPtr, + rows, + cols, + planes, + sRowStep << 1, + sColStep << 1, + sPlaneStep << 1, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + default: + { + + ThrowNotYetImplemented (); + + } + + } + + } + + else if (src.fPixelType == ttFloat) + { + + switch (fPixelType) + { + + case ttByte: + { + + DoCopyAreaR32_8 ((const real32 *) sPtr, + (uint8 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep, + PixelRange ()); + + break; + + } + + case ttShort: + { + + DoCopyAreaR32_16 ((const real32 *) sPtr, + (uint16 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep, + PixelRange ()); + + break; + + } + + case ttSShort: + { + + DoCopyAreaR32_S16 ((const real32 *) sPtr, + (int16 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep, + PixelRange ()); + + break; + + } + + default: + { + + ThrowNotYetImplemented (); + + } + + } + + } + + else + { + + ThrowNotYetImplemented (); + + } + + } + +/*****************************************************************************/ + +dng_point dng_pixel_buffer::RepeatPhase (const dng_rect &srcArea, + const dng_rect &dstArea) + { + + int32 repeatV = srcArea.H (); + int32 repeatH = srcArea.W (); + + int32 phaseV; + int32 phaseH; + + if (repeatV == 0 || + repeatH == 0) + { + DNG_REPORT ("Bad srcArea in RepeatPhase"); + return dng_point (); + } + + if (srcArea.t >= dstArea.t) + { + phaseV = (repeatV - ((srcArea.t - dstArea.t) % repeatV)) % repeatV; + } + else + { + phaseV = (dstArea.t - srcArea.t) % repeatV; + } + + if (srcArea.l >= dstArea.l) + { + phaseH = (repeatH - ((srcArea.l - dstArea.l) % repeatH)) % repeatH; + } + else + { + phaseH = (dstArea.l - srcArea.l) % repeatH; + } + + return dng_point (phaseV, phaseH); + + } + +/*****************************************************************************/ + +void dng_pixel_buffer::RepeatArea (const dng_rect &srcArea, + const dng_rect &dstArea) + { + + dng_point repeat = srcArea.Size (); + + dng_point phase = RepeatPhase (srcArea, + dstArea); + + const void *sPtr = ConstPixel (srcArea.t, + srcArea.l, + fPlane); + + void *dPtr = DirtyPixel (dstArea.t, + dstArea.l, + fPlane); + + uint32 rows = dstArea.H (); + uint32 cols = dstArea.W (); + + switch (fPixelSize) + { + + case 1: + { + + DoRepeatArea8 ((const uint8 *) sPtr, + (uint8 *) dPtr, + rows, + cols, + fPlanes, + fRowStep, + fColStep, + fPlaneStep, + repeat.v, + repeat.h, + phase.v, + phase.h); + + break; + + } + + case 2: + { + + DoRepeatArea16 ((const uint16 *) sPtr, + (uint16 *) dPtr, + rows, + cols, + fPlanes, + fRowStep, + fColStep, + fPlaneStep, + repeat.v, + repeat.h, + phase.v, + phase.h); + + break; + + } + + case 4: + { + + DoRepeatArea32 ((const uint32 *) sPtr, + (uint32 *) dPtr, + rows, + cols, + fPlanes, + fRowStep, + fColStep, + fPlaneStep, + repeat.v, + repeat.h, + phase.v, + phase.h); + + break; + + } + + default: + { + + ThrowNotYetImplemented (); + + } + + } + + } + +/*****************************************************************************/ + +void dng_pixel_buffer::RepeatSubArea (const dng_rect subArea, + uint32 repeatV, + uint32 repeatH) + { + + if (fArea.t < subArea.t) + { + + RepeatArea (dng_rect (subArea.t , fArea.l, + subArea.t + repeatV, fArea.r), + dng_rect (fArea.t , fArea.l, + subArea.t , fArea.r)); + + } + + if (fArea.b > subArea.b) + { + + RepeatArea (dng_rect (subArea.b - repeatV, fArea.l, + subArea.b , fArea.r), + dng_rect (subArea.b , fArea.l, + fArea.b , fArea.r)); + + } + + if (fArea.l < subArea.l) + { + + RepeatArea (dng_rect (fArea.t, subArea.l , + fArea.b, subArea.l + repeatH), + dng_rect (fArea.t, fArea.l , + fArea.b, subArea.l )); + + } + + if (fArea.r > subArea.r) + { + + RepeatArea (dng_rect (fArea.t, subArea.r - repeatH, + fArea.b, subArea.r ), + dng_rect (fArea.t, subArea.r , + fArea.b, fArea.r )); + + } + + } + +/*****************************************************************************/ + +void dng_pixel_buffer::ShiftRight (uint32 shift) + { + + if (fPixelType != ttShort) + { + + ThrowNotYetImplemented (); + + } + + uint32 rows = fArea.H (); + uint32 cols = fArea.W (); + + uint32 planes = fPlanes; + + void *dPtr = DirtyPixel (fArea.t, + fArea.l, + fPlane); + + const void *sPtr = dPtr; + + int32 sRowStep = fRowStep; + int32 sColStep = fColStep; + int32 sPlaneStep = fPlaneStep; + + int32 dRowStep = fRowStep; + int32 dColStep = fColStep; + int32 dPlaneStep = fPlaneStep; + + OptimizeOrder (sPtr, + dPtr, + fPixelSize, + fPixelSize, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + DoShiftRight16 ((uint16 *) dPtr, + rows, + cols, + planes, + dRowStep, + dColStep, + dPlaneStep, + shift); + + } + +/*****************************************************************************/ + +void dng_pixel_buffer::FlipH () + { + + fData = InternalPixel (fArea.t, fArea.r - 1); + + fColStep = -fColStep; + + } + +/*****************************************************************************/ + +void dng_pixel_buffer::FlipV () + { + + fData = InternalPixel (fArea.b - 1, fArea.l); + + fRowStep = -fRowStep; + + } + +/*****************************************************************************/ + +void dng_pixel_buffer::FlipZ () + { + + fData = InternalPixel (fArea.t, fArea.l, fPlanes - 1); + + fPlaneStep = -fPlaneStep; + + } + +/*****************************************************************************/ + +bool dng_pixel_buffer::EqualArea (const dng_pixel_buffer &src, + const dng_rect &area, + uint32 plane, + uint32 planes) const + { + + uint32 rows = area.H (); + uint32 cols = area.W (); + + const void *sPtr = src.ConstPixel (area.t, + area.l, + plane); + + const void *dPtr = ConstPixel (area.t, + area.l, + plane); + + int32 sRowStep = src.fRowStep; + int32 sColStep = src.fColStep; + int32 sPlaneStep = src.fPlaneStep; + + int32 dRowStep = fRowStep; + int32 dColStep = fColStep; + int32 dPlaneStep = fPlaneStep; + + if (fPixelType == src.fPixelType) + { + + if (rows == 1 && cols == 1 && sPlaneStep == 1 && dPlaneStep == 1) + { + + return DoEqualBytes (sPtr, + dPtr, + planes * fPixelSize); + + } + + else switch (fPixelSize) + { + + case 1: + { + + return DoEqualArea8 ((const uint8 *) sPtr, + (const uint8 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case 2: + { + + return DoEqualArea16 ((const uint16 *) sPtr, + (const uint16 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case 4: + { + + return DoEqualArea32 ((const uint32 *) sPtr, + (const uint32 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + default: + { + + ThrowNotYetImplemented (); + + return false; + + } + + } + + } + + else + return false; + + } + +/*****************************************************************************/ + +namespace + { + + template + real64 MaxDiff (const T *src1, + int32 s1RowStep, + int32 s1PlaneStep, + const T *src2, + int32 s2RowStep, + int32 s2PlaneStep, + uint32 rows, + uint32 cols, + uint32 planes) + { + + real64 result = 0.0; + + for (uint32 plane = 0; plane < planes; plane++) + { + + const T *src1Save = src1; + const T *src2Save = src2; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 0; col < cols; col++) + { + real64 diff = fabs ((real64)src1 [col] - src2 [col]); + + if (diff > result) + result = diff; + + } + + src1 += s1RowStep; + src2 += s2RowStep; + + } + + src1 = src1Save + s1PlaneStep; + src2 = src2Save + s2PlaneStep; + + } + + return result; + + } + + template + real64 MaxDiff (const T *src1, + int32 s1ColStep, + int32 s1RowStep, + int32 s1PlaneStep, + const T *src2, + int32 s2ColStep, + int32 s2RowStep, + int32 s2PlaneStep, + uint32 rows, + uint32 cols, + uint32 planes) + { + + if (s1ColStep == s2ColStep && + s1ColStep == 1) + return MaxDiff (src1, + s1RowStep, + s1PlaneStep, + src2, + s2RowStep, + s2PlaneStep, + rows, + cols, + planes); + + real64 result = 0.0; + + for (uint32 plane = 0; plane < planes; plane++) + { + + const T *src1Save = src1; + const T *src2Save = src2; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 0; col < cols; col++) + { + real64 diff = fabs ((real64)src1 [col * s1ColStep] - src2 [col * s2ColStep]); + + if (diff > result) + result = diff; + + } + + src1 += s1RowStep; + src2 += s2RowStep; + + } + + src1 = src1Save + s1PlaneStep; + src2 = src2Save + s2PlaneStep; + + } + + + return result; + + } + } + +real64 dng_pixel_buffer::MaximumDifference (const dng_pixel_buffer &rhs, + const dng_rect &area, + uint32 plane, + uint32 planes) const + { + + uint32 rows = area.H (); + uint32 cols = area.W (); + + const void *s1Ptr = rhs.ConstPixel (area.t, + area.l, + plane); + + const void *s2Ptr = ConstPixel (area.t, + area.l, + plane); + + int32 s1RowStep = rhs.fRowStep; + int32 s1ColStep = rhs.fColStep; + int32 s1PlaneStep = rhs.fPlaneStep; + + int32 s2RowStep = fRowStep; + int32 s2ColStep = fColStep; + int32 s2PlaneStep = fPlaneStep; + + if (fPixelType == rhs.fPixelType) + { + + switch (fPixelType) + { + + case ttByte: + return MaxDiff ((const uint8 *)s1Ptr, + s1ColStep, + s1RowStep, + s1PlaneStep, + (const uint8 *)s2Ptr, + s2ColStep, + s2RowStep, + s2PlaneStep, + rows, + cols, + planes); + + break; + + case ttShort: + return MaxDiff ((const uint16 *)s1Ptr, + s1ColStep, + s1RowStep, + s1PlaneStep, + (const uint16 *)s2Ptr, + s2ColStep, + s2RowStep, + s2PlaneStep, + rows, + cols, + planes); + + break; + + case ttLong: + return MaxDiff ((const uint32 *)s1Ptr, + s1ColStep, + s1RowStep, + s1PlaneStep, + (const uint32 *)s2Ptr, + s2ColStep, + s2RowStep, + s2PlaneStep, + rows, + cols, + planes); + + break; + + case ttSByte: + return MaxDiff ((const int8 *)s1Ptr, + s1ColStep, + s1RowStep, + s1PlaneStep, + (const int8 *)s2Ptr, + s2ColStep, + s2RowStep, + s2PlaneStep, + rows, + cols, + planes); + + break; + + case ttSShort: + return MaxDiff ((const int16 *)s1Ptr, + s1ColStep, + s1RowStep, + s1PlaneStep, + (const int16 *)s2Ptr, + s2ColStep, + s2RowStep, + s2PlaneStep, + rows, + cols, + planes); + + break; + + case ttSLong: + return MaxDiff ((const int32 *)s1Ptr, + s1ColStep, + s1RowStep, + s1PlaneStep, + (const int32 *)s2Ptr, + s2ColStep, + s2RowStep, + s2PlaneStep, + rows, + cols, + planes); + + break; + + case ttFloat: + return MaxDiff ((const real32 *)s1Ptr, + s1ColStep, + s1RowStep, + s1PlaneStep, + (const real32 *)s2Ptr, + s2ColStep, + s2RowStep, + s2PlaneStep, + rows, + cols, + planes); + + break; + + case ttDouble: + return MaxDiff ((const real64 *)s1Ptr, + s1ColStep, + s1RowStep, + s1PlaneStep, + (const real64 *)s2Ptr, + s2ColStep, + s2RowStep, + s2PlaneStep, + rows, + cols, + planes); + + break; + + + default: + { + + ThrowNotYetImplemented (); + + return 0.0; + + } + + } + + } + + else + ThrowProgramError ("attempt to difference pixel buffers of different formats."); + + return 0.0; + + } + +/*****************************************************************************/ diff --git a/dng/dng_pixel_buffer.h b/dng/dng_pixel_buffer.h new file mode 100644 index 0000000..7da3da0 --- /dev/null +++ b/dng/dng_pixel_buffer.h @@ -0,0 +1,764 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Support for holding buffers of sample data. + */ + +/*****************************************************************************/ + +#ifndef __dng_pixel_buffer__ +#define __dng_pixel_buffer__ + +/*****************************************************************************/ + +#include "dng_assertions.h" +#include "dng_rect.h" +#include "dng_safe_arithmetic.h" +#include "dng_tag_types.h" + +/*****************************************************************************/ + +/// Compute best set of step values for a given source and destination area and stride. + +void OptimizeOrder (const void *&sPtr, + void *&dPtr, + uint32 sPixelSize, + uint32 dPixelSize, + uint32 &count0, + uint32 &count1, + uint32 &count2, + int32 &sStep0, + int32 &sStep1, + int32 &sStep2, + int32 &dStep0, + int32 &dStep1, + int32 &dStep2); + +void OptimizeOrder (const void *&sPtr, + uint32 sPixelSize, + uint32 &count0, + uint32 &count1, + uint32 &count2, + int32 &sStep0, + int32 &sStep1, + int32 &sStep2); + +void OptimizeOrder (void *&dPtr, + uint32 dPixelSize, + uint32 &count0, + uint32 &count1, + uint32 &count2, + int32 &dStep0, + int32 &dStep1, + int32 &dStep2); + +/*****************************************************************************/ + +#define qDebugPixelType 0 + +#if qDebugPixelType + +#define ASSERT_PIXEL_TYPE(typeVal) CheckPixelType (typeVal) + +#else + +#define ASSERT_PIXEL_TYPE(typeVal) DNG_ASSERT (fPixelType == typeVal, "Pixel type access mismatch") + +#endif + +/*****************************************************************************/ + +/// \brief Holds a buffer of pixel data with "pixel geometry" metadata. +/// +/// The pixel geometry describes the layout in terms of how many planes, rows and columns +/// plus the steps (in bytes) between each column, row and plane. + +class dng_pixel_buffer + { + + public: + + // Area this buffer holds. + + dng_rect fArea; + + // Range of planes this buffer holds. + + uint32 fPlane; + uint32 fPlanes; + + // Steps between pixels. + + int32 fRowStep; + int32 fColStep; + int32 fPlaneStep; + + // Basic pixel type (TIFF tag type code). + + uint32 fPixelType; + + // Size of pixel type in bytes. + + uint32 fPixelSize; + + // Pointer to buffer's data. + + void *fData; + + // Do we have write-access to this data? + + bool fDirty; + + private: + + void * InternalPixel (int32 row, + int32 col, + uint32 plane = 0) const + { + + // TO DO: review this. do we set up buffers sometimes with "col" parameter + // equal to 0, which would then cause this exception to throw?! + + #if 0 + + // Ensure pixel to be accessed lies inside valid area. + if (row < fArea.t || row >= fArea.b || + col < fArea.l || col >= fArea.r || + plane < fPlane || (plane - fPlane) >= fPlanes) + { + ThrowProgramError ("Out-of-range pixel access"); + } + + // Compute offset of pixel. + const int64 rowOffset = SafeInt64Mult(fRowStep, + static_cast (row) - static_cast (fArea.t)); + const int64 colOffset = SafeInt64Mult(fColStep, + static_cast (col) - static_cast (fArea.l)); + const int64 planeOffset = SafeInt64Mult(fPlaneStep, + static_cast (plane - fPlane)); + const int64 offset = SafeInt64Mult(static_cast(fPixelSize), + SafeInt64Add(SafeInt64Add(rowOffset, colOffset), planeOffset)); + + // Add offset to buffer base address. + return static_cast (static_cast (fData) + offset); + + #else + + #if qDNG64Bit + + return (void *) + (((uint8 *) fData) + (int64) fPixelSize * + (fRowStep * (int64) (row - fArea.t) + + fColStep * (int64) (col - fArea.l) + + fPlaneStep * (int64) (plane - fPlane ))); + + #else + + return (void *) + (((uint8 *) fData) + (int32) fPixelSize * + (fRowStep * (int32) (row - fArea.t) + + fColStep * (int32) (col - fArea.l) + + fPlaneStep * (int32) (plane - fPlane ))); + + #endif + + #endif + + } + + #if qDebugPixelType + + void CheckPixelType (uint32 pixelType) const; + + #endif + + public: + + dng_pixel_buffer (); + + /// Note: This constructor is for internal use only and should not be + /// considered part of the DNG SDK API. + /// + /// Initialize the pixel buffer according to the given parameters (see + /// below). May throw an error if arithmetic overflow occurs when + /// computing the row, column or plane step, or if an invalid value + /// was passed for planarConfiguration. + /// + /// \param area Area covered by the pixel buffer + /// \param plane Index of the first plane + /// \param planes Number of planes + /// \param pixelType Pixel data type (one of the values defined in + /// dng_tag_types.h) + /// \param planarConfiguration Layout of the pixel planes in memory: One + /// of pcInterleaved, pcPlanar, or pcRowInterleaved (defined in + /// dng_tag_values.h) + /// \param data Pointer to the pixel data + + dng_pixel_buffer (const dng_rect &area, + uint32 plane, + uint32 planes, + uint32 pixelType, + uint32 planarConfiguration, + void *data); + + dng_pixel_buffer (const dng_pixel_buffer &buffer); + + dng_pixel_buffer & operator= (const dng_pixel_buffer &buffer); + + virtual ~dng_pixel_buffer (); + + /// Get the range of pixel values. + /// \retval Range of value a pixel can take. (Meaning [0, max] for unsigned case. Signed case is biased so [-32768, max - 32768].) + + uint32 PixelRange () const; + + /// Get extent of pixels in buffer + /// \retval Rectangle giving valid extent of buffer. + + const dng_rect & Area () const + { + return fArea; + } + + /// Number of planes of image data. + /// \retval Number of planes held in buffer. + + uint32 Planes () const + { + return fPlanes; + } + + /// Step, in pixels not bytes, between rows of data in buffer. + /// \retval row step in pixels. May be negative. + + int32 RowStep () const + { + return fRowStep; + } + + /// Step, in pixels not bytes, between planes of data in buffer. + /// \retval plane step in pixels. May be negative. + + int32 PlaneStep () const + { + return fPlaneStep; + } + + /// Get read-only untyped (void *) pointer to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as void *. + + const void * ConstPixel (int32 row, + int32 col, + uint32 plane = 0) const + { + + return InternalPixel (row, col, plane); + + } + + /// Get a writable untyped (void *) pointer to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as void *. + + void * DirtyPixel (int32 row, + int32 col, + uint32 plane = 0) + { + + DNG_ASSERT (fDirty, "Dirty access to const pixel buffer"); + + return InternalPixel (row, col, plane); + + } + + /// Get read-only uint8 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as uint8 *. + + const uint8 * ConstPixel_uint8 (int32 row, + int32 col, + uint32 plane = 0) const + { + + ASSERT_PIXEL_TYPE (ttByte); + + return (const uint8 *) ConstPixel (row, col, plane); + + } + + const uint8 * ConstPixel_uint8_overrideType (int32 row, + int32 col, + uint32 plane = 0) const + { + + // No type check + + return (const uint8 *) ConstPixel (row, col, plane); + + } + + /// Get a writable uint8 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as uint8 *. + + uint8 * DirtyPixel_uint8 (int32 row, + int32 col, + uint32 plane = 0) + { + + ASSERT_PIXEL_TYPE (ttByte); + + return (uint8 *) DirtyPixel (row, col, plane); + + } + + uint8 * DirtyPixel_uint8_overrideType (int32 row, + int32 col, + uint32 plane = 0) + { + + // No type check + + return (uint8 *) DirtyPixel (row, col, plane); + + } + + /// Get read-only int8 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as int8 *. + + const int8 * ConstPixel_int8 (int32 row, + int32 col, + uint32 plane = 0) const + { + + ASSERT_PIXEL_TYPE (ttSByte); + + return (const int8 *) ConstPixel (row, col, plane); + + } + + /// Get a writable int8 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as int8 *. + + int8 * DirtyPixel_int8 (int32 row, + int32 col, + uint32 plane = 0) + { + + ASSERT_PIXEL_TYPE (ttSByte); + + return (int8 *) DirtyPixel (row, col, plane); + + } + + /// Get read-only uint16 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as uint16 *. + + const uint16 * ConstPixel_uint16 (int32 row, + int32 col, + uint32 plane = 0) const + { + + ASSERT_PIXEL_TYPE (ttShort); + + return (const uint16 *) ConstPixel (row, col, plane); + + } + + /// Get a writable uint16 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as uint16 *. + + uint16 * DirtyPixel_uint16 (int32 row, + int32 col, + uint32 plane = 0) + { + + ASSERT_PIXEL_TYPE (ttShort); + + return (uint16 *) DirtyPixel (row, col, plane); + + } + + /// Get read-only int16 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as int16 *. + + const int16 * ConstPixel_int16 (int32 row, + int32 col, + uint32 plane = 0) const + { + + ASSERT_PIXEL_TYPE (ttSShort); + + return (const int16 *) ConstPixel (row, col, plane); + + } + + /// Get a writable int16 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as int16 *. + + int16 * DirtyPixel_int16 (int32 row, + int32 col, + uint32 plane = 0) + { + + ASSERT_PIXEL_TYPE (ttSShort); + + return (int16 *) DirtyPixel (row, col, plane); + + } + + /// Get read-only uint32 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as uint32 *. + + const uint32 * ConstPixel_uint32 (int32 row, + int32 col, + uint32 plane = 0) const + { + + ASSERT_PIXEL_TYPE (ttLong); + + return (const uint32 *) ConstPixel (row, col, plane); + + } + + /// Get a writable uint32 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as uint32 *. + + uint32 * DirtyPixel_uint32 (int32 row, + int32 col, + uint32 plane = 0) + { + + ASSERT_PIXEL_TYPE (ttLong); + + return (uint32 *) DirtyPixel (row, col, plane); + + } + + /// Get read-only int32 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as int32 *. + + const int32 * ConstPixel_int32 (int32 row, + int32 col, + uint32 plane = 0) const + { + + ASSERT_PIXEL_TYPE (ttSLong); + + return (const int32 *) ConstPixel (row, col, plane); + + } + + /// Get a writable int32 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as int32 *. + + int32 * DirtyPixel_int32 (int32 row, + int32 col, + uint32 plane = 0) + { + + ASSERT_PIXEL_TYPE (ttSLong); + + return (int32 *) DirtyPixel (row, col, plane); + + } + + /// Get read-only real32 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as real32 *. + + const real32 * ConstPixel_real32 (int32 row, + int32 col, + uint32 plane = 0) const + { + + ASSERT_PIXEL_TYPE (ttFloat); + + return (const real32 *) ConstPixel (row, col, plane); + + } + + /// Get a writable real32 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as real32 *. + + real32 * DirtyPixel_real32 (int32 row, + int32 col, + uint32 plane = 0) + { + + ASSERT_PIXEL_TYPE (ttFloat); + + return (real32 *) DirtyPixel (row, col, plane); + + } + + /// Initialize a rectangular area of pixel buffer to a constant. + /// \param area Rectangle of pixel buffer to set. + /// \param plane Plane to start filling on. + /// \param planes Number of planes to fill. + /// \param value Constant value to set pixels to. + + void SetConstant (const dng_rect &area, + uint32 plane, + uint32 planes, + uint32 value); + + /// Initialize a rectangular area of pixel buffer to a constant unsigned 8-bit value. + /// \param area Rectangle of pixel buffer to set. + /// \param plane Plane to start filling on. + /// \param planes Number of planes to fill. + /// \param value Constant uint8 value to set pixels to. + + void SetConstant_uint8 (const dng_rect &area, + uint32 plane, + uint32 planes, + uint8 value) + { + + DNG_ASSERT (fPixelType == ttByte, "Mismatched pixel type"); + + SetConstant (area, plane, planes, (uint32) value); + + } + + /// Initialize a rectangular area of pixel buffer to a constant unsigned 16-bit value. + /// \param area Rectangle of pixel buffer to set. + /// \param plane Plane to start filling on. + /// \param planes Number of planes to fill. + /// \param value Constant uint16 value to set pixels to. + + void SetConstant_uint16 (const dng_rect &area, + uint32 plane, + uint32 planes, + uint16 value) + { + + DNG_ASSERT (fPixelType == ttShort, "Mismatched pixel type"); + + SetConstant (area, plane, planes, (uint32) value); + + } + + /// Initialize a rectangular area of pixel buffer to a constant signed 16-bit value. + /// \param area Rectangle of pixel buffer to set. + /// \param plane Plane to start filling on. + /// \param planes Number of planes to fill. + /// \param value Constant int16 value to set pixels to. + + void SetConstant_int16 (const dng_rect &area, + uint32 plane, + uint32 planes, + int16 value) + { + + DNG_ASSERT (fPixelType == ttSShort, "Mismatched pixel type"); + + SetConstant (area, plane, planes, (uint32) (uint16) value); + + } + + /// Initialize a rectangular area of pixel buffer to a constant unsigned 32-bit value. + /// \param area Rectangle of pixel buffer to set. + /// \param plane Plane to start filling on. + /// \param planes Number of planes to fill. + /// \param value Constant uint32 value to set pixels to. + + void SetConstant_uint32 (const dng_rect &area, + uint32 plane, + uint32 planes, + uint32 value) + { + + DNG_ASSERT (fPixelType == ttLong, "Mismatched pixel type"); + + SetConstant (area, plane, planes, value); + + } + + /// Initialize a rectangular area of pixel buffer to a constant real 32-bit value. + /// \param area Rectangle of pixel buffer to set. + /// \param plane Plane to start filling on. + /// \param planes Number of planes to fill. + /// \param value Constant real32 value to set pixels to. + + void SetConstant_real32 (const dng_rect &area, + uint32 plane, + uint32 planes, + real32 value) + { + + DNG_ASSERT (fPixelType == ttFloat, "Mismatched pixel type"); + + union + { + uint32 i; + real32 f; + } x; + + x.f = value; + + SetConstant (area, plane, planes, x.i); + + } + + /// Initialize a rectangular area of pixel buffer to zeros. + /// \param area Rectangle of pixel buffer to zero. + /// \param plane Plane to start filling on. + /// \param planes Number of planes to fill. + + void SetZero (const dng_rect &area, + uint32 plane, + uint32 planes); + + /// Copy image data from an area of one pixel buffer to same area of another. + /// \param src Buffer to copy from. + /// \param area Rectangle of pixel buffer to copy. + /// \param srcPlane Plane to start copy in src. + /// \param dstPlane Plane to start copy in dst. + /// \param planes Number of planes to copy. + + void CopyArea (const dng_pixel_buffer &src, + const dng_rect &area, + uint32 srcPlane, + uint32 dstPlane, + uint32 planes); + + /// Copy image data from an area of one pixel buffer to same area of another. + /// \param src Buffer to copy from. + /// \param area Rectangle of pixel buffer to copy. + /// \param plane Plane to start copy in src and this. + /// \param planes Number of planes to copy. + + void CopyArea (const dng_pixel_buffer &src, + const dng_rect &area, + uint32 plane, + uint32 planes) + { + + CopyArea (src, area, plane, plane, planes); + + } + + /// Calculate the offset phase of destination rectangle relative to source rectangle. + /// Phase is based on a 0,0 origin and the notion of repeating srcArea across dstArea. + /// It is the number of pixels into srcArea to start repeating from when tiling dstArea. + /// \retval dng_point containing horizontal and vertical phase. + + static dng_point RepeatPhase (const dng_rect &srcArea, + const dng_rect &dstArea); + + /// Repeat the image data in srcArea across dstArea. + /// (Generally used for padding operations.) + /// \param srcArea Area to repeat from. + /// \param dstArea Area to fill with data from srcArea. + + void RepeatArea (const dng_rect &srcArea, + const dng_rect &dstArea); + + /// Replicates a sub-area of a buffer to fill the entire buffer. + + void RepeatSubArea (const dng_rect subArea, + uint32 repeatV = 1, + uint32 repeatH = 1); + + /// Apply a right shift (C++ oerpator >>) to all pixel values. Only implemented for 16-bit (signed or unsigned) pixel buffers. + /// \param shift Number of bits by which to right shift each pixel value. + + void ShiftRight (uint32 shift); + + /// Change metadata so pixels are iterated in opposite horizontal order. + /// This operation does not require movement of actual pixel data. + + void FlipH (); + + /// Change metadata so pixels are iterated in opposite vertical order. + /// This operation does not require movement of actual pixel data. + + void FlipV (); + + /// Change metadata so pixels are iterated in opposite plane order. + /// This operation does not require movement of actual pixel data. + + void FlipZ (); // Flip planes + + /// Return true if the contents of an area of the pixel buffer area are the same as those of another. + /// \param rhs Buffer to compare against. + /// \param area Rectangle of pixel buffer to test. + /// \param plane Plane to start comparing. + /// \param planes Number of planes to compare. + /// \retval bool true if areas are equal, false otherwise. + + bool EqualArea (const dng_pixel_buffer &rhs, + const dng_rect &area, + uint32 plane, + uint32 planes) const; + + /// Return the absolute value of the maximum difference between two pixel buffers. Used for comparison testing with tolerance + /// \param rhs Buffer to compare against. + /// \param area Rectangle of pixel buffer to test. + /// \param plane Plane to start comparing. + /// \param planes Number of planes to compare. + /// \retval larges absolute value difference between the corresponding pixels each buffer across area. + + real64 MaximumDifference (const dng_pixel_buffer &rhs, + const dng_rect &area, + uint32 plane, + uint32 planes) const; + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_point.cpp b/dng/dng_point.cpp new file mode 100644 index 0000000..ff1b9b2 --- /dev/null +++ b/dng/dng_point.cpp @@ -0,0 +1,15 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_point.h" + +/*****************************************************************************/ + +// Currently all inlined. + +/*****************************************************************************/ diff --git a/dng/dng_point.h b/dng/dng_point.h new file mode 100644 index 0000000..17fc314 --- /dev/null +++ b/dng/dng_point.h @@ -0,0 +1,313 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef __dng_point__ +#define __dng_point__ + +/*****************************************************************************/ + +#include + +#include "dng_types.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +class dng_point + { + + public: + + int32 v; + int32 h; + + public: + + dng_point () + : v (0) + , h (0) + { + } + + dng_point (int32 vv, int32 hh) + : v (vv) + , h (hh) + { + } + + bool operator== (const dng_point &pt) const + { + return (v == pt.v) && + (h == pt.h); + } + + bool operator!= (const dng_point &pt) const + { + return !(*this == pt); + } + + real64 Length () const + { + return hypot ((real64) v, (real64) h); + } + + }; + +/*****************************************************************************/ + +class dng_point_real64 + { + + public: + + real64 v; + real64 h; + + public: + + dng_point_real64 () + : v (0.0) + , h (0.0) + { + } + + dng_point_real64 (real64 vv, real64 hh) + : v (vv) + , h (hh) + { + } + + dng_point_real64 (const dng_point &pt) + : v ((real64) pt.v) + , h ((real64) pt.h) + { + } + + bool operator== (const dng_point_real64 &pt) const + { + return (v == pt.v) && + (h == pt.h); + } + + bool operator!= (const dng_point_real64 &pt) const + { + return !(*this == pt); + } + + dng_point Round () const + { + return dng_point (Round_int32 (v), + Round_int32 (h)); + } + + real64 Length () const + { + return hypot (v, h); + } + + void Scale (real64 scale) + { + v *= scale; + h *= scale; + } + + void Normalize () + { + Scale (1.0 / Length ()); + } + + }; + +/*****************************************************************************/ + +inline dng_point operator+ (const dng_point &a, + const dng_point &b) + + + { + + return dng_point (a.v + b.v, + a.h + b.h); + + } + +/*****************************************************************************/ + +inline dng_point_real64 operator+ (const dng_point_real64 &a, + const dng_point_real64 &b) + + + { + + return dng_point_real64 (a.v + b.v, + a.h + b.h); + + } + +/*****************************************************************************/ + +inline dng_point operator- (const dng_point &a, + const dng_point &b) + + + { + + return dng_point (a.v - b.v, + a.h - b.h); + + } + +/*****************************************************************************/ + +inline dng_point_real64 operator- (const dng_point_real64 &a, + const dng_point_real64 &b) + + + { + + return dng_point_real64 (a.v - b.v, + a.h - b.h); + + } + +/*****************************************************************************/ + +inline real64 Distance (const dng_point_real64 &a, + const dng_point_real64 &b) + + + { + + return (a - b).Length (); + + } + +/*****************************************************************************/ + +inline real64 DistanceSquared (const dng_point_real64 &a, + const dng_point_real64 &b) + + + { + + dng_point_real64 diff = a - b; + + return (diff.v * diff.v) + (diff.h * diff.h); + + } + +/*****************************************************************************/ + +// Finds distance squared from point p to line segment from v to w. + +inline real64 DistanceSquared (const dng_point_real64 &p, + const dng_point_real64 &v, + const dng_point_real64 &w) + { + + real64 len2 = DistanceSquared (v, w); + + if (len2 == 0.0) + return DistanceSquared (p, v); + + real64 t = ((p.h - v.h) * (w.h - v.h) + + (p.v - v.v) * (w.v - v.v)) / len2; + + if (t <= 0.0) + return DistanceSquared (p, v); + + if (t >= 1.0) + return DistanceSquared (p, w); + + dng_point_real64 z; + + z.h = v.h + t * (w.h - v.h); + z.v = v.v + t * (w.v - v.v); + + return DistanceSquared (p, z); + + } + +/*****************************************************************************/ + +inline dng_point Transpose (const dng_point &a) + { + + return dng_point (a.h, a.v); + + } + +/*****************************************************************************/ + +inline dng_point_real64 Transpose (const dng_point_real64 &a) + { + + return dng_point_real64 (a.h, a.v); + + } + +/*****************************************************************************/ + +inline dng_point_real64 Lerp (const dng_point_real64 &a, + const dng_point_real64 &b, + const real64 t) + { + + return dng_point_real64 (Lerp_real64 (a.v, b.v, t), + Lerp_real64 (a.h, b.h, t)); + + } + +/*****************************************************************************/ + +inline real64 Dot (const dng_point_real64 &a, + const dng_point_real64 &b) + { + + return (a.h * b.h) + (a.v * b.v); + + } + +/*****************************************************************************/ + +inline dng_point_real64 operator* (const real64 scale, + const dng_point_real64 &pt) + { + + dng_point_real64 result = pt; + + result.h *= scale; + result.v *= scale; + + return result; + + } + +/*****************************************************************************/ + +inline dng_point MakePerpendicular (const dng_point &pt) + { + + return dng_point (-pt.h, pt.v); + + } + +/*****************************************************************************/ + +inline dng_point_real64 MakePerpendicular (const dng_point_real64 &pt) + { + + return dng_point_real64 (-pt.h, pt.v); + + } + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_preview.cpp b/dng/dng_preview.cpp new file mode 100644 index 0000000..58ae869 --- /dev/null +++ b/dng/dng_preview.cpp @@ -0,0 +1,802 @@ +/*****************************************************************************/ +// Copyright 2007-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_preview.h" + +#include "dng_assertions.h" +#include "dng_image.h" +#include "dng_image_writer.h" +#include "dng_memory.h" +#include "dng_stream.h" +#include "dng_tag_codes.h" +#include "dng_tag_values.h" + +/*****************************************************************************/ + +class dng_preview_tag_set: public dng_basic_tag_set + { + + private: + + tag_string fApplicationNameTag; + + tag_string fApplicationVersionTag; + + tag_string fSettingsNameTag; + + dng_fingerprint fSettingsDigest; + + tag_uint8_ptr fSettingsDigestTag; + + tag_uint32 fColorSpaceTag; + + tag_string fDateTimeTag; + + tag_real64 fRawToPreviewGainTag; + + tag_uint32 fCacheVersionTag; + + public: + + dng_preview_tag_set (dng_tiff_directory &directory, + const dng_preview &preview, + const dng_ifd &ifd); + + virtual ~dng_preview_tag_set (); + + }; + +/*****************************************************************************/ + +dng_preview_tag_set::dng_preview_tag_set (dng_tiff_directory &directory, + const dng_preview &preview, + const dng_ifd &ifd) + + : dng_basic_tag_set (directory, ifd) + + , fApplicationNameTag (tcPreviewApplicationName, + preview.fInfo.fApplicationName, + false) + + , fApplicationVersionTag (tcPreviewApplicationVersion, + preview.fInfo.fApplicationVersion, + false) + + , fSettingsNameTag (tcPreviewSettingsName, + preview.fInfo.fSettingsName, + false) + + , fSettingsDigest (preview.fInfo.fSettingsDigest) + + , fSettingsDigestTag (tcPreviewSettingsDigest, + fSettingsDigest.data, + 16) + + , fColorSpaceTag (tcPreviewColorSpace, + preview.fInfo.fColorSpace) + + , fDateTimeTag (tcPreviewDateTime, + preview.fInfo.fDateTime, + true) + + , fRawToPreviewGainTag (tcRawToPreviewGain, + preview.fInfo.fRawToPreviewGain) + + , fCacheVersionTag (tcCacheVersion, + preview.fInfo.fCacheVersion) + + { + + if (preview.fInfo.fApplicationName.NotEmpty ()) + { + + directory.Add (&fApplicationNameTag); + + } + + if (preview.fInfo.fApplicationVersion.NotEmpty ()) + { + + directory.Add (&fApplicationVersionTag); + + } + + if (preview.fInfo.fSettingsName.NotEmpty ()) + { + + directory.Add (&fSettingsNameTag); + + } + + if (preview.fInfo.fSettingsDigest.IsValid ()) + { + + directory.Add (&fSettingsDigestTag); + + } + + if (preview.fInfo.fColorSpace != previewColorSpace_MaxEnum) + { + + directory.Add (&fColorSpaceTag); + + } + + if (preview.fInfo.fDateTime.NotEmpty ()) + { + + directory.Add (&fDateTimeTag); + + } + + if (preview.fInfo.fRawToPreviewGain != 1.0) + { + + directory.Add (&fRawToPreviewGainTag); + + } + + if (preview.fInfo.fCacheVersion != 0) + { + + directory.Add (&fCacheVersionTag); + + } + + } + +/*****************************************************************************/ + +dng_preview_tag_set::~dng_preview_tag_set () + { + + } + +/*****************************************************************************/ + +dng_preview::dng_preview () + + : fInfo () + + { + + } + +/*****************************************************************************/ + +dng_preview::~dng_preview () + { + + } + +/*****************************************************************************/ + +dng_image_preview::dng_image_preview () + + : fImage () + , fIFD () + + { + + } + +/*****************************************************************************/ + +dng_image_preview::~dng_image_preview () + { + + } + +/*****************************************************************************/ + +dng_basic_tag_set * dng_image_preview::AddTagSet (dng_tiff_directory &directory) const + { + + fIFD.fNewSubFileType = fInfo.fIsPrimary ? sfPreviewImage + : sfAltPreviewImage; + + fIFD.fImageWidth = fImage->Width (); + fIFD.fImageLength = fImage->Height (); + + fIFD.fSamplesPerPixel = fImage->Planes (); + + fIFD.fPhotometricInterpretation = fIFD.fSamplesPerPixel == 1 ? piBlackIsZero + : piRGB; + + fIFD.fBitsPerSample [0] = TagTypeSize (fImage->PixelType ()) * 8; + + for (uint32 j = 1; j < fIFD.fSamplesPerPixel; j++) + { + fIFD.fBitsPerSample [j] = fIFD.fBitsPerSample [0]; + } + + fIFD.SetSingleStrip (); + + return new dng_preview_tag_set (directory, *this, fIFD); + + } + +/*****************************************************************************/ + +void dng_image_preview::WriteData (dng_host &host, + dng_image_writer &writer, + dng_basic_tag_set &basic, + dng_stream &stream) const + { + + writer.WriteImage (host, + fIFD, + basic, + stream, + *fImage.Get ()); + + } + +/*****************************************************************************/ + +class dng_jpeg_preview_tag_set: public dng_preview_tag_set + { + + private: + + dng_urational fCoefficientsData [3]; + + tag_urational_ptr fCoefficientsTag; + + uint16 fSubSamplingData [2]; + + tag_uint16_ptr fSubSamplingTag; + + tag_uint16 fPositioningTag; + + dng_urational fReferenceData [6]; + + tag_urational_ptr fReferenceTag; + + public: + + dng_jpeg_preview_tag_set (dng_tiff_directory &directory, + const dng_jpeg_preview &preview, + const dng_ifd &ifd); + + virtual ~dng_jpeg_preview_tag_set (); + + }; + +/******************************************************************************/ + +dng_jpeg_preview_tag_set::dng_jpeg_preview_tag_set (dng_tiff_directory &directory, + const dng_jpeg_preview &preview, + const dng_ifd &ifd) + + : dng_preview_tag_set (directory, preview, ifd) + + , fCoefficientsTag (tcYCbCrCoefficients, fCoefficientsData, 3) + + , fSubSamplingTag (tcYCbCrSubSampling, fSubSamplingData, 2) + + , fPositioningTag (tcYCbCrPositioning, preview.fYCbCrPositioning) + + , fReferenceTag (tcReferenceBlackWhite, fReferenceData, 6) + + { + + if (preview.fPhotometricInterpretation == piYCbCr) + { + + fCoefficientsData [0] = dng_urational (299, 1000); + fCoefficientsData [1] = dng_urational (587, 1000); + fCoefficientsData [2] = dng_urational (114, 1000); + + directory.Add (&fCoefficientsTag); + + fSubSamplingData [0] = (uint16) preview.fYCbCrSubSampling.h; + fSubSamplingData [1] = (uint16) preview.fYCbCrSubSampling.v; + + directory.Add (&fSubSamplingTag); + + directory.Add (&fPositioningTag); + + fReferenceData [0] = dng_urational ( 0, 1); + fReferenceData [1] = dng_urational (255, 1); + fReferenceData [2] = dng_urational (128, 1); + fReferenceData [3] = dng_urational (255, 1); + fReferenceData [4] = dng_urational (128, 1); + fReferenceData [5] = dng_urational (255, 1); + + directory.Add (&fReferenceTag); + + } + + } + +/*****************************************************************************/ + +dng_jpeg_preview_tag_set::~dng_jpeg_preview_tag_set () + { + + } + +/*****************************************************************************/ + +dng_jpeg_preview::dng_jpeg_preview () + + : fPreviewSize () + , fPhotometricInterpretation (piYCbCr) + , fYCbCrSubSampling (1, 1) + , fYCbCrPositioning (2) + , fCompressedData () + + { + + } + +/*****************************************************************************/ + +dng_jpeg_preview::~dng_jpeg_preview () + { + + } + +/*****************************************************************************/ + +dng_basic_tag_set * dng_jpeg_preview::AddTagSet (dng_tiff_directory &directory) const + { + + dng_ifd ifd; + + ifd.fNewSubFileType = fInfo.fIsPrimary ? sfPreviewImage + : sfAltPreviewImage; + + ifd.fImageWidth = fPreviewSize.h; + ifd.fImageLength = fPreviewSize.v; + + ifd.fPhotometricInterpretation = fPhotometricInterpretation; + + ifd.fBitsPerSample [0] = 8; + ifd.fBitsPerSample [1] = 8; + ifd.fBitsPerSample [2] = 8; + + ifd.fSamplesPerPixel = (fPhotometricInterpretation == piBlackIsZero ? 1 : 3); + + ifd.fCompression = ccJPEG; + ifd.fPredictor = cpNullPredictor; + + ifd.SetSingleStrip (); + + return new dng_jpeg_preview_tag_set (directory, *this, ifd); + + } + +/*****************************************************************************/ + +void dng_jpeg_preview::WriteData (dng_host & /* host */, + dng_image_writer & /* writer */, + dng_basic_tag_set &basic, + dng_stream &stream) const + { + + basic.SetTileOffset (0, (uint32) stream.Position ()); + + basic.SetTileByteCount (0, fCompressedData->LogicalSize ()); + + stream.Put (fCompressedData->Buffer (), + fCompressedData->LogicalSize ()); + + if (fCompressedData->LogicalSize () & 1) + { + stream.Put_uint8 (0); + } + + } + +/*****************************************************************************/ + +void dng_jpeg_preview::SpoolAdobeThumbnail (dng_stream &stream) const + { + + DNG_ASSERT (fCompressedData.Get (), + "SpoolAdobeThumbnail: no data"); + + DNG_ASSERT (fPhotometricInterpretation == piYCbCr, + "SpoolAdobeThumbnail: Non-YCbCr"); + + uint32 compressedSize = fCompressedData->LogicalSize (); + + stream.Put_uint32 (DNG_CHAR4 ('8','B','I','M')); + stream.Put_uint16 (1036); + stream.Put_uint16 (0); + + stream.Put_uint32 (compressedSize + 28); + + uint32 widthBytes = (fPreviewSize.h * 24 + 31) / 32 * 4; + + stream.Put_uint32 (1); + stream.Put_uint32 (fPreviewSize.h); + stream.Put_uint32 (fPreviewSize.v); + stream.Put_uint32 (widthBytes); + stream.Put_uint32 (widthBytes * fPreviewSize.v); + stream.Put_uint32 (compressedSize); + stream.Put_uint16 (24); + stream.Put_uint16 (1); + + stream.Put (fCompressedData->Buffer (), + compressedSize); + + if (compressedSize & 1) + { + stream.Put_uint8 (0); + } + + } + +/*****************************************************************************/ + +class dng_raw_preview_tag_set: public dng_preview_tag_set + { + + private: + + tag_data_ptr fOpcodeList2Tag; + + tag_uint32_ptr fWhiteLevelTag; + + uint32 fWhiteLevelData [kMaxColorPlanes]; + + tag_urational_ptr fBlackLevelTag; + + dng_urational fBlackLevelData [kMaxColorPlanes]; + + public: + + dng_raw_preview_tag_set (dng_tiff_directory &directory, + const dng_raw_preview &preview, + const dng_ifd &ifd); + + virtual ~dng_raw_preview_tag_set (); + + }; + +/*****************************************************************************/ + +dng_raw_preview_tag_set::dng_raw_preview_tag_set (dng_tiff_directory &directory, + const dng_raw_preview &preview, + const dng_ifd &ifd) + + : dng_preview_tag_set (directory, preview, ifd) + + , fOpcodeList2Tag (tcOpcodeList2, + ttUndefined, + 0, + NULL) + + , fWhiteLevelTag (tcWhiteLevel, + fWhiteLevelData, + preview.fImage->Planes ()) + + , fBlackLevelTag (tcBlackLevel, + fBlackLevelData, + preview.fImage->Planes ()) + + { + + if (preview.fOpcodeList2Data.Get ()) + { + + fOpcodeList2Tag.SetData (preview.fOpcodeList2Data->Buffer ()); + fOpcodeList2Tag.SetCount (preview.fOpcodeList2Data->LogicalSize ()); + + directory.Add (&fOpcodeList2Tag); + + } + + if (preview.fImage->PixelType () == ttFloat) + { + + for (uint32 j = 0; j < kMaxColorPlanes; j++) + { + fWhiteLevelData [j] = 32768; + } + + directory.Add (&fWhiteLevelTag); + + } + + else + { + + bool nonZeroBlack = false; + + for (uint32 j = 0; j < preview.fImage->Planes (); j++) + { + + fBlackLevelData [j].Set_real64 (preview.fBlackLevel [j], 1); + + nonZeroBlack = nonZeroBlack || (preview.fBlackLevel [j] != 0.0); + + } + + if (nonZeroBlack) + { + + directory.Add (&fBlackLevelTag); + + } + + } + + } + +/*****************************************************************************/ + +dng_raw_preview_tag_set::~dng_raw_preview_tag_set () + { + + } + +/*****************************************************************************/ + +dng_raw_preview::dng_raw_preview () + + : fImage () + , fOpcodeList2Data () + , fCompressionQuality (-1) + , fIFD () + + { + + for (uint32 n = 0; n < kMaxSamplesPerPixel; n++) + { + fBlackLevel [n] = 0.0; + } + + } + +/*****************************************************************************/ + +dng_raw_preview::~dng_raw_preview () + { + + } + +/*****************************************************************************/ + +dng_basic_tag_set * dng_raw_preview::AddTagSet (dng_tiff_directory &directory) const + { + + fIFD.fNewSubFileType = sfPreviewImage; + + fIFD.fImageWidth = fImage->Width (); + fIFD.fImageLength = fImage->Height (); + + fIFD.fSamplesPerPixel = fImage->Planes (); + + fIFD.fPhotometricInterpretation = piLinearRaw; + + if (fImage->PixelType () == ttFloat) + { + + fIFD.fCompression = ccDeflate; + + fIFD.fCompressionQuality = fCompressionQuality; + + fIFD.fPredictor = cpFloatingPoint; + + for (uint32 j = 0; j < fIFD.fSamplesPerPixel; j++) + { + fIFD.fBitsPerSample [j] = 16; + fIFD.fSampleFormat [j] = sfFloatingPoint; + } + + fIFD.FindTileSize (512 * 1024); + + } + + else + { + + fIFD.fCompression = ccLossyJPEG; + + fIFD.fCompressionQuality = fCompressionQuality; + + fIFD.fBitsPerSample [0] = TagTypeSize (fImage->PixelType ()) * 8; + + for (uint32 j = 1; j < fIFD.fSamplesPerPixel; j++) + { + fIFD.fBitsPerSample [j] = fIFD.fBitsPerSample [0]; + } + + fIFD.FindTileSize (512 * 512 * fIFD.fSamplesPerPixel); + + } + + return new dng_raw_preview_tag_set (directory, *this, fIFD); + + } + +/*****************************************************************************/ + +void dng_raw_preview::WriteData (dng_host &host, + dng_image_writer &writer, + dng_basic_tag_set &basic, + dng_stream &stream) const + { + + writer.WriteImage (host, + fIFD, + basic, + stream, + *fImage.Get ()); + + } + +/*****************************************************************************/ + +dng_mask_preview::dng_mask_preview () + + : fImage () + , fCompressionQuality (-1) + , fIFD () + + { + + } + +/*****************************************************************************/ + +dng_mask_preview::~dng_mask_preview () + { + + } + +/*****************************************************************************/ + +dng_basic_tag_set * dng_mask_preview::AddTagSet (dng_tiff_directory &directory) const + { + + fIFD.fNewSubFileType = sfPreviewMask; + + fIFD.fImageWidth = fImage->Width (); + fIFD.fImageLength = fImage->Height (); + + fIFD.fSamplesPerPixel = 1; + + fIFD.fPhotometricInterpretation = piTransparencyMask; + + fIFD.fCompression = ccDeflate; + fIFD.fPredictor = cpHorizontalDifference; + + fIFD.fCompressionQuality = fCompressionQuality; + + fIFD.fBitsPerSample [0] = TagTypeSize (fImage->PixelType ()) * 8; + + fIFD.FindTileSize (512 * 512 * fIFD.fSamplesPerPixel); + + return new dng_basic_tag_set (directory, fIFD); + + } + +/*****************************************************************************/ + +void dng_mask_preview::WriteData (dng_host &host, + dng_image_writer &writer, + dng_basic_tag_set &basic, + dng_stream &stream) const + { + + writer.WriteImage (host, + fIFD, + basic, + stream, + *fImage.Get ()); + + } + +/*****************************************************************************/ + +dng_depth_preview::dng_depth_preview () + + : fImage () + , fCompressionQuality (-1) + , fFullResolution (false) + , fIFD () + + { + + } + +/*****************************************************************************/ + +dng_depth_preview::~dng_depth_preview () + { + + } + +/*****************************************************************************/ + +dng_basic_tag_set * dng_depth_preview::AddTagSet (dng_tiff_directory &directory) const + { + + fIFD.fNewSubFileType = fFullResolution ? sfDepthMap + : sfPreviewDepthMap; + + fIFD.fImageWidth = fImage->Width (); + fIFD.fImageLength = fImage->Height (); + + fIFD.fSamplesPerPixel = 1; + + fIFD.fPhotometricInterpretation = piDepth; + + fIFD.fCompression = ccDeflate; + fIFD.fPredictor = cpHorizontalDifference; + + fIFD.fCompressionQuality = fCompressionQuality; + + fIFD.fBitsPerSample [0] = TagTypeSize (fImage->PixelType ()) * 8; + + fIFD.FindTileSize (512 * 512 * fIFD.fSamplesPerPixel); + + return new dng_basic_tag_set (directory, fIFD); + + } + +/*****************************************************************************/ + +void dng_depth_preview::WriteData (dng_host &host, + dng_image_writer &writer, + dng_basic_tag_set &basic, + dng_stream &stream) const + { + + writer.WriteImage (host, + fIFD, + basic, + stream, + *fImage.Get ()); + + } + +/*****************************************************************************/ + +dng_preview_list::dng_preview_list () + + : fCount (0) + + { + + } + +/*****************************************************************************/ + +dng_preview_list::~dng_preview_list () + { + + } + +/*****************************************************************************/ + +void dng_preview_list::Append (AutoPtr &preview) + { + + if (preview.Get ()) + { + + DNG_ASSERT (fCount < kMaxDNGPreviews, "DNG preview list overflow"); + + if (fCount < kMaxDNGPreviews) + { + + fPreview [fCount++] . Reset (preview.Release ()); + + } + + } + + } + +/*****************************************************************************/ diff --git a/dng/dng_preview.h b/dng/dng_preview.h new file mode 100644 index 0000000..818b5bf --- /dev/null +++ b/dng/dng_preview.h @@ -0,0 +1,241 @@ +/*****************************************************************************/ +// Copyright 2007-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef __dng_preview__ +#define __dng_preview__ + +/*****************************************************************************/ + +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_ifd.h" +#include "dng_opcode_list.h" +#include "dng_point.h" +#include "dng_sdk_limits.h" +#include "dng_uncopyable.h" + +/*****************************************************************************/ + +class dng_preview: private dng_uncopyable + { + + public: + + dng_preview_info fInfo; + + protected: + + dng_preview (); + + public: + + virtual ~dng_preview (); + + virtual dng_basic_tag_set * AddTagSet (dng_tiff_directory &directory) const = 0; + + virtual void WriteData (dng_host &host, + dng_image_writer &writer, + dng_basic_tag_set &basic, + dng_stream &stream) const = 0; + + }; + +/*****************************************************************************/ + +class dng_image_preview: public dng_preview + { + + public: + + AutoPtr fImage; + + private: + + mutable dng_ifd fIFD; + + public: + + dng_image_preview (); + + virtual ~dng_image_preview (); + + virtual dng_basic_tag_set * AddTagSet (dng_tiff_directory &directory) const; + + virtual void WriteData (dng_host &host, + dng_image_writer &writer, + dng_basic_tag_set &basic, + dng_stream &stream) const; + + }; + +/*****************************************************************************/ + +class dng_jpeg_preview: public dng_preview + { + + public: + + dng_point fPreviewSize; + + uint16 fPhotometricInterpretation; + + dng_point fYCbCrSubSampling; + + uint16 fYCbCrPositioning; + + AutoPtr fCompressedData; + + public: + + dng_jpeg_preview (); + + virtual ~dng_jpeg_preview (); + + virtual dng_basic_tag_set * AddTagSet (dng_tiff_directory &directory) const; + + virtual void WriteData (dng_host &host, + dng_image_writer &writer, + dng_basic_tag_set &basic, + dng_stream &stream) const; + + void SpoolAdobeThumbnail (dng_stream &stream) const; + + }; + +/*****************************************************************************/ + +class dng_raw_preview: public dng_preview + { + + public: + + AutoPtr fImage; + + AutoPtr fOpcodeList2Data; + + real64 fBlackLevel [kMaxSamplesPerPixel]; + + int32 fCompressionQuality; + + private: + + mutable dng_ifd fIFD; + + public: + + dng_raw_preview (); + + virtual ~dng_raw_preview (); + + virtual dng_basic_tag_set * AddTagSet (dng_tiff_directory &directory) const; + + virtual void WriteData (dng_host &host, + dng_image_writer &writer, + dng_basic_tag_set &basic, + dng_stream &stream) const; + + }; + +/*****************************************************************************/ + +class dng_depth_preview: public dng_preview + { + + public: + + AutoPtr fImage; + + int32 fCompressionQuality; + + bool fFullResolution; + + private: + + mutable dng_ifd fIFD; + + public: + + dng_depth_preview (); + + virtual ~dng_depth_preview (); + + virtual dng_basic_tag_set * AddTagSet (dng_tiff_directory &directory) const; + + virtual void WriteData (dng_host &host, + dng_image_writer &writer, + dng_basic_tag_set &basic, + dng_stream &stream) const; + + }; + +/*****************************************************************************/ + +class dng_mask_preview: public dng_preview + { + + public: + + AutoPtr fImage; + + int32 fCompressionQuality; + + private: + + mutable dng_ifd fIFD; + + public: + + dng_mask_preview (); + + virtual ~dng_mask_preview (); + + virtual dng_basic_tag_set * AddTagSet (dng_tiff_directory &directory) const; + + virtual void WriteData (dng_host &host, + dng_image_writer &writer, + dng_basic_tag_set &basic, + dng_stream &stream) const; + + }; + +/*****************************************************************************/ + +class dng_preview_list + { + + private: + + uint32 fCount; + + AutoPtr fPreview [kMaxDNGPreviews]; + + public: + + dng_preview_list (); + + ~dng_preview_list (); + + uint32 Count () const + { + return fCount; + } + + const dng_preview & Preview (uint32 index) const + { + return *(fPreview [index]); + } + + void Append (AutoPtr &preview); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_pthread.cpp b/dng/dng_pthread.cpp new file mode 100644 index 0000000..a33be43 --- /dev/null +++ b/dng/dng_pthread.cpp @@ -0,0 +1,1307 @@ +/*****************************************************************************/ +// Copyright 2002-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +//#include "dng_pthread.h" + +/*****************************************************************************/ + +#if qDNGThreadSafe + +/*****************************************************************************/ + +#include "dng_assertions.h" + +/*****************************************************************************/ + +#if qWinOS + +#pragma warning(disable : 4786) + +/* Not supporting Win98, check WINVER if this is still needed + +// Nothing in this file requires Unicode, +// However, CreateSemaphore has a path parameter +// (which is NULL always in this code) and thus +// does not work on Win98 if UNICODE is defined. +// So we force it off here. + +#undef UNICODE +#undef _UNICODE +*/ + +#include +#include +#include +#include +#include +#include + +#else + +#include + +#endif + +/*****************************************************************************/ + +#if qWinOS + +/*****************************************************************************/ + +// Turning off qDNGUseConditionVariable because the WakeConditionVariable and +// WakeAllConditionVariable Microsoft API routines are only available on +// Vista, and Camera Raw and DNG Converter 8.3 need to continue working on +// Windows XP. -erichan 2013-11-08. + +#define qDNGUseConditionVariable 0 + +/*****************************************************************************/ + +#ifndef qDNGUseConditionVariable +#if WINVER >= 0x0600 // Vista introduces a real condition variable support +#define qDNGUseConditionVariable 1 +#else +#define qDNGUseConditionVariable 0 +#endif +#endif + +/*****************************************************************************/ + +#if !qDNGUseConditionVariable +namespace { + + struct waiter { + struct waiter *prev; + struct waiter *next; + HANDLE semaphore; + bool chosen_by_signal; + }; +} +#endif + +/*****************************************************************************/ + +struct dng_pthread_mutex_impl +{ + CRITICAL_SECTION lock; + + dng_pthread_mutex_impl() { ::InitializeCriticalSection(&lock); } + ~dng_pthread_mutex_impl() { ::DeleteCriticalSection(&lock); } + void Lock() { ::EnterCriticalSection(&lock); } + void Unlock() { ::LeaveCriticalSection(&lock); } +private: + dng_pthread_mutex_impl &operator=(const dng_pthread_mutex_impl &) { } + dng_pthread_mutex_impl(const dng_pthread_mutex_impl &) { } +}; + +/*****************************************************************************/ + +struct dng_pthread_cond_impl +{ + dng_pthread_mutex_impl lock; // Mutual exclusion on next two variables + +#if qDNGUseConditionVariable + // so much simpler, but Vista+ only + CONDITION_VARIABLE cond; + + dng_pthread_cond_impl() { InitializeConditionVariable(&cond); } + ~dng_pthread_cond_impl() { } // no delete listed + +#else + + waiter *head_waiter; // List of threads waiting on this condition + waiter *tail_waiter; // Used to get FIFO, rather than LIFO, behavior for pthread_cond_signal + unsigned int broadcast_generation; // Used as sort of a separator on broadcasts + // saves having to walk the waiters list setting + // each one's "chosen_by_signal" flag while the condition is locked + + dng_pthread_cond_impl() : head_waiter(NULL), tail_waiter(NULL), broadcast_generation(0) { } + ~dng_pthread_cond_impl() { } +#endif + +// Non copyable +private: + dng_pthread_cond_impl &operator=(const dng_pthread_cond_impl &) { } + dng_pthread_cond_impl(const dng_pthread_cond_impl &) { } + +}; + +/*****************************************************************************/ + +namespace +{ + + struct ScopedLock + { + dng_pthread_mutex_impl *mutex; + + ScopedLock(dng_pthread_mutex_impl *arg) : mutex(arg) + { + mutex->Lock(); + } + ScopedLock(dng_pthread_mutex_impl &arg) : mutex(&arg) + { + mutex->Lock(); + } + ~ScopedLock() + { + mutex->Unlock(); + } + private: + ScopedLock &operator=(const ScopedLock &) { } + ScopedLock(const ScopedLock &) { } + }; + + +#if !qDNGUseConditionalVariable + // DONE: avoid this serialization lock + // do allocation at init, and then just assert ? + + dng_pthread_mutex_impl validationLock; + + void ValidateMutex(dng_pthread_mutex_t *mutex) + { + if (*mutex != DNG_PTHREAD_MUTEX_INITIALIZER) + return; + + ScopedLock lock(validationLock); + + if (*mutex == DNG_PTHREAD_MUTEX_INITIALIZER) + dng_pthread_mutex_init(mutex, NULL); + } + + void ValidateCond(dng_pthread_cond_t *cond) + { + if (*cond != DNG_PTHREAD_COND_INITIALIZER) + return; + + ScopedLock lock(validationLock); + + if (*cond == DNG_PTHREAD_COND_INITIALIZER) + dng_pthread_cond_init(cond, NULL); + } +#endif + + DWORD thread_wait_sema_TLS_index; + bool thread_wait_sema_inited = false; + dng_pthread_once_t once_thread_TLS = DNG_PTHREAD_ONCE_INIT; + + void init_thread_TLS() + { + thread_wait_sema_TLS_index = ::TlsAlloc(); + thread_wait_sema_inited = true; + } + + void finalize_thread_TLS() + { + if (thread_wait_sema_inited) + { + ::TlsFree(thread_wait_sema_TLS_index); + thread_wait_sema_inited = false; + } + } + + dng_pthread_mutex_impl primaryHandleMapLock; + + typedef std::map > ThreadMapType; + + // A map to make sure handles are freed and to allow returning a pointer sized result + // even on 64-bit Windows. + ThreadMapType primaryHandleMap; + + HANDLE GetThreadSemaphore() + { + dng_pthread_once(&once_thread_TLS, init_thread_TLS); + + HANDLE semaphore = ::TlsGetValue(thread_wait_sema_TLS_index); + if (semaphore == NULL) + { + semaphore = ::CreateSemaphore(NULL, 0, 1, NULL); + ::TlsSetValue(thread_wait_sema_TLS_index, semaphore); + } + + return semaphore; + } + + void FreeThreadSemaphore() + { + if (thread_wait_sema_inited) + { + HANDLE semaphore = (HANDLE)::TlsGetValue(thread_wait_sema_TLS_index); + + if (semaphore != NULL) + { + ::TlsSetValue(thread_wait_sema_TLS_index, NULL); + ::CloseHandle(semaphore); + } + } + } + + struct trampoline_args + { + void *(*func)(void *); + void *arg; + }; + + // This trampoline takes care of the return type being different + // between pthreads thread funcs and Windows C lib thread funcs + unsigned __stdcall trampoline(void *arg_arg) + { + trampoline_args *args_ptr = (trampoline_args *)arg_arg; + trampoline_args args = *args_ptr; + + delete args_ptr; + + GetThreadSemaphore(); + + void *result = args.func(args.arg); + + { + ScopedLock lockMap(primaryHandleMapLock); + + ThreadMapType::iterator iter = primaryHandleMap.find(pthread_self()); + if (iter != primaryHandleMap.end()) + *iter->second.second = result; + } + + FreeThreadSemaphore(); + + return S_OK; + } + +} + +/*****************************************************************************/ + +extern "C" { + +/*****************************************************************************/ + +struct dng_pthread_attr_impl + { + size_t stacksize; + }; + +/*****************************************************************************/ + +int dng_pthread_attr_init(pthread_attr_t *attr) + { + dng_pthread_attr_impl *newAttrs; + + newAttrs = new (std::nothrow) dng_pthread_attr_impl; + if (newAttrs == NULL) + return -1; // ENOMEM; + + newAttrs->stacksize = 0; + + *attr = newAttrs; + + return 0; + } + +/*****************************************************************************/ + +int dng_pthread_attr_destroy(pthread_attr_t *attr) + { + if (*attr == NULL) + return -1; // EINVAL + + delete *attr; + + *attr = NULL; + + return 0; + } + +/*****************************************************************************/ + +int dng_pthread_attr_setstacksize(dng_pthread_attr_t *attr, size_t stacksize) + { + if (attr == NULL || (*attr) == NULL) + return -1; // EINVAL + + (*attr)->stacksize = stacksize; + + return 0; + } + +/*****************************************************************************/ + +int dng_pthread_attr_getstacksize(const dng_pthread_attr_t *attr, size_t *stacksize) + { + if (attr == NULL || (*attr) == NULL || stacksize == NULL) + return -1; // EINVAL + + *stacksize = (*attr)->stacksize; + + return 0; + } + +/*****************************************************************************/ + +int dng_pthread_create(dng_pthread_t *thread, const pthread_attr_t *attrs, void * (*func)(void *), void *arg) +{ + try + { + uintptr_t result; + unsigned threadID; + std::auto_ptr args(new (std::nothrow) trampoline_args); + std::auto_ptr resultHolder(new (std::nothrow) (void *)); + + if (args.get() == NULL || resultHolder.get () == NULL) + return -1; // ENOMEM + + args->func = func; + args->arg = arg; + + size_t stacksize = 0; + + if (attrs != NULL) + dng_pthread_attr_getstacksize (attrs, &stacksize); + + { + ScopedLock lockMap(primaryHandleMapLock); + + result = _beginthreadex(NULL, (unsigned)stacksize, trampoline, args.get(), 0, &threadID); + if (result == NULL) + return -1; // ENOMEM + args.release(); + + std::pair > newMapEntry(threadID, + std::pair((HANDLE)result, resultHolder.get ())); + std::pair insertion = primaryHandleMap.insert(newMapEntry); + + // If there is a handle open on the thread, its ID should not be reused so assert that an insertion was made. + DNG_ASSERT(insertion.second, "pthread emulation logic error"); + } + + + resultHolder.release (); + + *thread = (dng_pthread_t)threadID; + return 0; + } + catch (const std::bad_alloc &) + { + return -1; + } +} + +/*****************************************************************************/ + +int dng_pthread_detach(dng_pthread_t thread) +{ + HANDLE primaryHandle; + void **resultHolder = NULL; + + { + ScopedLock lockMap(primaryHandleMapLock); + + ThreadMapType::iterator iter = primaryHandleMap.find(thread); + if (iter == primaryHandleMap.end()) + return -1; + + primaryHandle = iter->second.first; + + // A join is waiting on the thread. + if (primaryHandle == NULL) + return -1; + + resultHolder = iter->second.second; + + primaryHandleMap.erase(iter); + } + + delete resultHolder; + +#if qWinRT + if (!::WinRT_CloseThreadHandle(primaryHandle)) +#else + if (!::CloseHandle(primaryHandle)) +#endif + return -1; + + return 0; +} + +/*****************************************************************************/ + +int dng_pthread_join(dng_pthread_t thread, void **result) +{ + bool found = false; + HANDLE primaryHandle = NULL; + void **resultHolder = NULL; + + ThreadMapType::iterator iter; + + { + ScopedLock lockMap(primaryHandleMapLock); + + iter = primaryHandleMap.find(thread); + found = iter != primaryHandleMap.end(); + if (found) + { + primaryHandle = iter->second.first; + resultHolder = iter->second.second; + + // Set HANDLE to NULL to force any later join or detach to fail. + iter->second.first = NULL; + } + } + + // This case can happens when joining a thread not created with pthread_create, + // which is a bad idea, but it gets mapped to doing the join, but always returns NULL. + if (!found) + primaryHandle = ::OpenThread(SYNCHRONIZE|THREAD_QUERY_INFORMATION, FALSE, thread); + + if (primaryHandle == NULL) + return -1; + + DWORD err; + if (::WaitForSingleObject(primaryHandle, INFINITE) != WAIT_OBJECT_0) + { + err = ::GetLastError(); + return -1; + } + + { + ScopedLock lockMap(primaryHandleMapLock); + + if (iter != primaryHandleMap.end()) + primaryHandleMap.erase(iter); + } + +#if qWinRT + ::WinRT_CloseThreadHandle(primaryHandle); +#else + ::CloseHandle(primaryHandle); +#endif + if (result != NULL && resultHolder != NULL) + *result = *resultHolder; + + delete resultHolder; + + return 0; +} + +/*****************************************************************************/ + +dng_pthread_t dng_pthread_self() +{ + return (dng_pthread_t)::GetCurrentThreadId(); +} + +/*****************************************************************************/ + +void dng_pthread_exit(void *result) +{ + { + ScopedLock lockMap(primaryHandleMapLock); + + ThreadMapType::iterator iter = primaryHandleMap.find(pthread_self()); + if (iter != primaryHandleMap.end()) + *iter->second.second = result; + } + + FreeThreadSemaphore(); + + _endthreadex(S_OK); +} + +/*****************************************************************************/ + +int dng_pthread_mutex_init(dng_pthread_mutex_t *mutex, void * /* attrs */) +{ + dng_pthread_mutex_t result; + try { + result = new(dng_pthread_mutex_impl); + } catch (const std::bad_alloc &) + { + return -1; + } + + if (result == NULL) + return -1; + *mutex = result; + return 0; +} + +/*****************************************************************************/ + +int dng_pthread_mutex_destroy(dng_pthread_mutex_t *mutex) +{ + if (*mutex == DNG_PTHREAD_MUTEX_INITIALIZER) + { + *mutex = NULL; + return 0; + } + + delete *mutex; + *mutex = NULL; + return 0; +} + +/*****************************************************************************/ + +int dng_pthread_cond_init(dng_pthread_cond_t *cond, void * /* attrs */) +{ + dng_pthread_cond_t result; + try { + result = new(dng_pthread_cond_impl); + } catch (const std::bad_alloc &) + { + return -1; + } + + if (result == NULL) + return -1; + *cond = result; + return 0; +} + +/*****************************************************************************/ + +int dng_pthread_cond_destroy(dng_pthread_cond_t *cond) +{ + if (*cond == DNG_PTHREAD_COND_INITIALIZER) + { + *cond = NULL; + return 0; + } + + delete *cond; + *cond = NULL; + return 0; +} + +/*****************************************************************************/ + +int dng_pthread_mutexattr_init(dng_pthread_mutexattr_t* mutexattr) +{ + return 0; +} + +/*****************************************************************************/ + +int dng_pthread_mutexattr_settype(dng_pthread_mutexattr_t* mutexattr, int type) +{ + return 0; +} + +/*****************************************************************************/ + +int dng_pthread_mutex_lock(dng_pthread_mutex_t *mutex) +{ + ValidateMutex(mutex); + (*mutex)->Lock(); + return 0; +} + +/*****************************************************************************/ + +int dng_pthread_mutex_unlock(dng_pthread_mutex_t *mutex) +{ + ValidateMutex(mutex); + (*mutex)->Unlock(); + return 0; +} + +/*****************************************************************************/ + +static int cond_wait_internal(dng_pthread_cond_t *cond, dng_pthread_mutex_t *mutex, int timeout_milliseconds) +{ +#if qDNGUseConditionVariable + int result = 0; + + BOOL success = SleepConditionVariableCS(&(*cond)->cond, &(*mutex)->lock, timeout_milliseconds); + if (!success) + if (GetLastError() == ERROR_TIMEOUT) + result = DNG_ETIMEDOUT; + + return result; + +#else + + dng_pthread_cond_impl &real_cond = **cond; + dng_pthread_mutex_impl &real_mutex = **mutex; + + waiter this_wait; + HANDLE semaphore = GetThreadSemaphore(); + int my_generation; // The broadcast generation this waiter is in + + { + this_wait.next = NULL; + this_wait.semaphore = semaphore; + this_wait.chosen_by_signal = 0; + + ScopedLock lock1(real_cond.lock); + + // Add this waiter to the end of the list. + this_wait.prev = real_cond.tail_waiter; + if (real_cond.tail_waiter != NULL) + real_cond.tail_waiter->next = &this_wait; + real_cond.tail_waiter = &this_wait; + + // If the list was empty, set the head of the list to this waiter. + if (real_cond.head_waiter == NULL) + real_cond.head_waiter = &this_wait; + + // Note which broadcast generation this waiter belongs to. + my_generation = real_cond.broadcast_generation; + } + + real_mutex.Unlock(); + + DWORD result = ::WaitForSingleObject(semaphore, timeout_milliseconds); + + if (result == WAIT_TIMEOUT) + { + // If the wait timed out, this thread is likely still on the waiters list + // of the condition. However, there is a race in that the thread may have been + // signaled or broadcast between when WaitForSingleObject decided + // we had timed out and this code running. + + bool mustConsumeSemaphore = false; + { + ScopedLock lock2(real_cond.lock); + + bool chosen_by_signal = this_wait.chosen_by_signal; + bool chosen_by_broadcast = my_generation != real_cond.broadcast_generation; + + if (chosen_by_signal || chosen_by_broadcast) + mustConsumeSemaphore = true; + else + { + // Still on waiters list. Remove this waiter from list. + if (this_wait.next != NULL) + this_wait.next->prev = this_wait.prev; + else + real_cond.tail_waiter = this_wait.prev; + + if (this_wait.prev != NULL) + this_wait.prev->next = this_wait.next; + else + real_cond.head_waiter = this_wait.next; + } + } + + if (mustConsumeSemaphore) + { + ::WaitForSingleObject(semaphore, INFINITE); + result = WAIT_OBJECT_0; + } + } + else + DNG_ASSERT (result == WAIT_OBJECT_0, "pthread emulation logic error"); + + // reacquire the mutex + real_mutex.Lock(); + + return (result == WAIT_TIMEOUT) ? DNG_ETIMEDOUT : 0; +#endif +} + +/*****************************************************************************/ + +int dng_pthread_cond_wait(dng_pthread_cond_t *cond, dng_pthread_mutex_t *mutex) +{ + ValidateCond(cond); + + return cond_wait_internal(cond, mutex, INFINITE); +} + +/*****************************************************************************/ + +int dng_pthread_cond_timedwait(dng_pthread_cond_t *cond, dng_pthread_mutex_t *mutex, struct dng_timespec *latest_time) +{ + ValidateCond(cond); + + struct dng_timespec sys_timespec; + +#if defined(_MSC_VER) && _MSC_VER >= 1900 + + struct timespec temp; + dng_pthread_now (&temp); + + sys_timespec.tv_sec = (long)temp.tv_sec; + sys_timespec.tv_nsec = temp.tv_nsec; + +#else + + dng_pthread_now (&sys_timespec); + +#endif + + __int64 sys_time = (__int64)sys_timespec.tv_sec * 1000000000 + sys_timespec.tv_nsec; + __int64 lock_time = (__int64)latest_time->tv_sec * 1000000000 + latest_time->tv_nsec; + + int wait_millisecs = (int)((lock_time - sys_time + 500000) / 1000000); + + if (wait_millisecs < 0) + wait_millisecs = 0; + + return cond_wait_internal(cond, mutex, wait_millisecs); +} + +/*****************************************************************************/ + +int dng_pthread_cond_signal(dng_pthread_cond_t *cond) +{ + ValidateCond(cond); + +#if qDNGUseConditionVariable + + WakeConditionVariable(&(*cond)->cond); + return 0; + +#else + + waiter *first; + dng_pthread_cond_impl &real_cond = **cond; + + { + ScopedLock lock(real_cond.lock); + + first = real_cond.head_waiter; + if (first != NULL) + { + if (first->next != NULL) + first->next->prev = NULL; + else + real_cond.tail_waiter = NULL; // Or first->prev, which is always NULL in this case + + first->chosen_by_signal = true; + + real_cond.head_waiter = first->next; + } + } + + if (first != NULL) + ::ReleaseSemaphore(first->semaphore, 1, NULL); + + return 0; +#endif +} + +/*****************************************************************************/ + +int dng_pthread_cond_broadcast(dng_pthread_cond_t *cond) +{ + ValidateCond(cond); + +#if qDNGUseConditionVariable + + WakeAllConditionVariable(&(*cond)->cond); + return 0; + +#else + + waiter *first; + dng_pthread_cond_impl &real_cond = **cond; + + { + ScopedLock lock(real_cond.lock); + + first = real_cond.head_waiter; + real_cond.head_waiter = NULL; + real_cond.tail_waiter = NULL; + + real_cond.broadcast_generation++; + } + + while (first != NULL) + { + waiter *next = first->next; + ::ReleaseSemaphore(first->semaphore, 1, NULL); + first = next; + } + + return 0; +#endif +} + +/*****************************************************************************/ + +int dng_pthread_once(dng_pthread_once_t *once, void (*init_func)()) +{ + if (once == NULL || init_func == NULL) + return EINVAL; + + if (once->inited) + return 0; + + if (::InterlockedIncrement(&once->semaphore) == 0) + { + init_func(); + once->inited = 1; + } + else + { + while (!once->inited) + Sleep(0); + } + + return 0; +} + +/*****************************************************************************/ + +int dng_pthread_key_create(dng_pthread_key_t * key, void (*destructor) (void *)) +{ + if (destructor != NULL) + return -1; + + DWORD result = ::TlsAlloc(); + if (result == TLS_OUT_OF_INDEXES) + return -1; + *key = (unsigned long)result; + return 0; +} + +/*****************************************************************************/ + +int dng_pthread_key_delete(dng_pthread_key_t key) +{ + if (::TlsFree((DWORD)key)) + return 0; + return -1; +} + +/*****************************************************************************/ + +int dng_pthread_setspecific(dng_pthread_key_t key, const void *value) +{ + if (::TlsSetValue((DWORD)key, const_cast(value))) + return 0; + return -1; +} + +/*****************************************************************************/ + +void *dng_pthread_getspecific(dng_pthread_key_t key) +{ + return ::TlsGetValue((DWORD)key); +} + +/*****************************************************************************/ + +#if !qDNGUseConditionVariable + +namespace { + struct rw_waiter { + struct rw_waiter *prev; + struct rw_waiter *next; + HANDLE semaphore; + bool is_writer; + }; +} + +#endif + +struct dng_pthread_rwlock_impl +{ + + +#if qDNGUseConditionVariable + SRWLOCK rwlock; + bool fWriteLockExclusive; + + dng_pthread_rwlock_impl () { InitializeSRWLock(&rwlock); } + ~dng_pthread_rwlock_impl () { } // no delete listed + + +#else + dng_pthread_mutex_impl mutex; + + rw_waiter *head_waiter; + rw_waiter *tail_waiter; + + unsigned long readers_active; + unsigned long writers_waiting; + bool writer_active; + + dng_pthread_cond_impl read_wait; + dng_pthread_cond_impl write_wait; + + dng_pthread_rwlock_impl () + : mutex () + , head_waiter (NULL) + , tail_waiter (NULL) + , readers_active (0) + , writers_waiting (0) + , read_wait () + , write_wait () + , writer_active (false) + { + } + + ~dng_pthread_rwlock_impl () + { + } + + void WakeHeadWaiter () + { + HANDLE semaphore = head_waiter->semaphore; + + head_waiter = head_waiter->next; + if (head_waiter == NULL) + tail_waiter = NULL; + + ::ReleaseSemaphore(semaphore, 1, NULL); + } +#endif + + // Non copyable +private: + dng_pthread_rwlock_impl &operator=(const dng_pthread_rwlock_impl &) { } + dng_pthread_rwlock_impl(const dng_pthread_rwlock_impl &) { } + + +}; + +/*****************************************************************************/ + +int dng_pthread_rwlock_init(dng_pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attrs) +{ + dng_pthread_rwlock_impl *newRWLock; + + newRWLock = new (std::nothrow) dng_pthread_rwlock_impl; + if (newRWLock == NULL) + return -1; // ENOMEM; + + *rwlock = newRWLock; + + return 0; +} + +/*****************************************************************************/ + +int dng_pthread_rwlock_destroy(dng_pthread_rwlock_t *rwlock) +{ + dng_pthread_rwlock_impl &real_rwlock = **rwlock; + +#if !qDNGUseConditionVariable + + { + ScopedLock lock (real_rwlock.mutex); + + if (real_rwlock.head_waiter != NULL || + real_rwlock.readers_active != 0 || + real_rwlock.writers_waiting != 0 || + real_rwlock.writer_active) + return -1; // EBUSY + } +#endif + + delete *rwlock; + *rwlock = NULL; + return 0; +} + +/*****************************************************************************/ + +#if !qDNGUseConditionVariable + +#define CHECK_RWLOCK_STATE(real_rwlock) \ + DNG_ASSERT (!real_rwlock.writer_active || real_rwlock.readers_active == 0, "dng_pthread_rwlock_t logic error") + +#endif + +/*****************************************************************************/ + +int dng_pthread_rwlock_rdlock(dng_pthread_rwlock_t *rwlock) +{ +#if qDNGUseConditionVariable + // Note: Aquire cannot be called resursively from same thread, once aquired or deadlock will occur + + AcquireSRWLockShared(&(*rwlock)->rwlock); + (*rwlock)->fWriteLockExclusive = false; + + return 0; + +#else + + dng_pthread_rwlock_impl &real_rwlock = **rwlock; + + struct rw_waiter this_wait; + bool doWait = false;; + int result = 0; + HANDLE semaphore=NULL; + + { + + ScopedLock lock (real_rwlock.mutex); + + CHECK_RWLOCK_STATE (real_rwlock); + + if (real_rwlock.writers_waiting > 0 || real_rwlock.writer_active) + { + semaphore = GetThreadSemaphore(); + + this_wait.next = NULL; + this_wait.semaphore = semaphore; + this_wait.is_writer = false; + + // Add this waiter to the end of the list. + this_wait.prev = real_rwlock.tail_waiter; + if (real_rwlock.tail_waiter != NULL) + real_rwlock.tail_waiter->next = &this_wait; + real_rwlock.tail_waiter = &this_wait; + + // If the list was empty, set the head of the list to this waiter. + if (real_rwlock.head_waiter == NULL) + real_rwlock.head_waiter = &this_wait; + + doWait = true; + } + else + real_rwlock.readers_active++; + } + + if (result == 0 && doWait) + result = (WaitForSingleObject(semaphore, INFINITE) == WAIT_OBJECT_0) ? 0 : -1; + + return result; +#endif +} + +/*****************************************************************************/ + +int dng_pthread_rwlock_tryrdlock(dng_pthread_rwlock_t *rwlock) +{ +#if qDNGUseConditionVariable + + if (TryAcquireSRWLockExclusive(&(*rwlock)->rwlock) == 0) + return 0; + + (*rwlock)->fWriteLockExclusive = false; + return -1; + +#else + dng_pthread_rwlock_impl &real_rwlock = **rwlock; + + ScopedLock lock (real_rwlock.mutex); + + CHECK_RWLOCK_STATE (real_rwlock); + + if (real_rwlock.writers_waiting == 0 && !real_rwlock.writer_active) + { + real_rwlock.readers_active++; + return 0; + } + + return -1; +#endif +} + +/*****************************************************************************/ + +int dng_pthread_rwlock_trywrlock(dng_pthread_rwlock_t *rwlock) +{ +#if qDNGUseConditionVariable + + if (TryAcquireSRWLockShared(&(*rwlock)->rwlock) == 0) + return 0; + + (*rwlock)->fWriteLockExclusive = true; + return -1; + +#else + dng_pthread_rwlock_impl &real_rwlock = **rwlock; + + ScopedLock lock (real_rwlock.mutex); + + CHECK_RWLOCK_STATE (real_rwlock); + + if (real_rwlock.readers_active == 0 && + real_rwlock.writers_waiting == 0 && + !real_rwlock.writer_active) + { + real_rwlock.writer_active = true; + return 0; + } + + return -1; +#endif +} + +/*****************************************************************************/ + +int dng_pthread_rwlock_unlock(dng_pthread_rwlock_t *rwlock) + { +#if qDNGUseConditionVariable + + if ((*rwlock)->fWriteLockExclusive) + ReleaseSRWLockExclusive(&(*rwlock)->rwlock); + else + ReleaseSRWLockShared(&(*rwlock)->rwlock); + + return 0; + +#else + dng_pthread_rwlock_impl &real_rwlock = **rwlock; + + int result = 0; + + ScopedLock lock (real_rwlock.mutex); + + CHECK_RWLOCK_STATE (real_rwlock); + + if (real_rwlock.readers_active > 0) + --real_rwlock.readers_active; + else + real_rwlock.writer_active = false; + + while (real_rwlock.head_waiter != NULL) + { + if (real_rwlock.head_waiter->is_writer) + { + if (real_rwlock.readers_active == 0) + { + real_rwlock.writers_waiting--; + real_rwlock.writer_active = true; + real_rwlock.WakeHeadWaiter (); + } + + break; + } + else + { + ++real_rwlock.readers_active; + real_rwlock.WakeHeadWaiter (); + } + } + + return result; +#endif + } + +/*****************************************************************************/ + +int dng_pthread_rwlock_wrlock(dng_pthread_rwlock_t *rwlock) + { +#if qDNGUseConditionVariable + + AcquireSRWLockExclusive(&(*rwlock)->rwlock); + (*rwlock)->fWriteLockExclusive = true; + + return 0; + +#else + dng_pthread_rwlock_impl &real_rwlock = **rwlock; + + int result = 0; + struct rw_waiter this_wait; + HANDLE semaphore=NULL; + bool doWait = false; + + { + ScopedLock lock (real_rwlock.mutex); + + CHECK_RWLOCK_STATE (real_rwlock); + + if (real_rwlock.readers_active || + real_rwlock.writers_waiting || + real_rwlock.writer_active) + { + semaphore = GetThreadSemaphore(); + + this_wait.next = NULL; + this_wait.semaphore = semaphore; + this_wait.is_writer = true; + + // Add this waiter to the end of the list. + this_wait.prev = real_rwlock.tail_waiter; + if (real_rwlock.tail_waiter != NULL) + real_rwlock.tail_waiter->next = &this_wait; + real_rwlock.tail_waiter = &this_wait; + + // If the list was empty, set the head of the list to this waiter. + if (real_rwlock.head_waiter == NULL) + real_rwlock.head_waiter = &this_wait; + + real_rwlock.writers_waiting++; + + doWait = true; + } + else + real_rwlock.writer_active = true; + } + + if (result == 0 && doWait) + result = (WaitForSingleObject(semaphore, INFINITE) == WAIT_OBJECT_0) ? 0 : -1; + + return result; +#endif + } + +/*****************************************************************************/ + +void dng_pthread_disassociate() +{ + FreeThreadSemaphore(); +} + +void dng_pthread_terminate() + { + finalize_thread_TLS(); + } + +/*****************************************************************************/ + +} // extern "C" + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ + +int dng_pthread_now (struct timespec *now) + { + + if (now == NULL) + return -1; // EINVAL + + #if qWinOS + + FILETIME ft; + ::GetSystemTimeAsFileTime(&ft); + + __int64 sys_time = ((__int64)ft.dwHighDateTime << 32) + ft.dwLowDateTime; + + #define SecsFrom1601To1970 11644473600 + + sys_time -= SecsFrom1601To1970 * 10000000LL; + + sys_time *= 100; // Convert from 100ns to 1ns units + + now->tv_sec = (long)(sys_time / 1000000000); + now->tv_nsec = (long)(sys_time % 1000000000); + + #else + + struct timeval tv; + + if (gettimeofday (&tv, NULL) != 0) + return errno; + + now->tv_sec = tv.tv_sec; + now->tv_nsec = tv.tv_usec * 1000; + + #endif + + return 0; + + } + +/*****************************************************************************/ + +#endif // qDNGThreadSafe + +/*****************************************************************************/ diff --git a/dng/dng_pthread.h b/dng/dng_pthread.h new file mode 100644 index 0000000..ec2951a --- /dev/null +++ b/dng/dng_pthread.h @@ -0,0 +1,250 @@ +/*****************************************************************************/ +// Copyright 2002-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef DNG_PTHREAD +#define DNG_PTHREAD + +/*****************************************************************************/ + +#include "dng_flags.h" + +/*****************************************************************************/ + +#if qDNGThreadSafe + +/*****************************************************************************/ + +#if !qWinOS + +/*****************************************************************************/ + +/* Try generic POSIX compile */ + +#include +#include + +#define dng_pthread_disassociate() +#define dng_pthread_terminate() + +/*****************************************************************************/ + +#else + +/*****************************************************************************/ + +#include + +#if _MSC_VER >= 1600 + +// Get this included so ETIMEDOUT is predefined. +#include + +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*****************************************************************************/ + +#define DNG_ETIMEDOUT 60 /* Operation timed out */ + +struct dng_timespec { + long tv_sec; + long tv_nsec; +}; + + +typedef unsigned long dng_pthread_t; + +typedef struct dng_pthread_mutex_impl *dng_pthread_mutex_t; +typedef struct dng_pthread_cond_impl *dng_pthread_cond_t; +typedef unsigned long dng_pthread_key_t; + + +#define DNG_PTHREAD_MUTEX_INITIALIZER ((struct dng_pthread_mutex_impl *)-1) +#define DNG_PTHREAD_COND_INITIALIZER ((struct dng_pthread_cond_impl *)-1) + +struct _dng_pthread_once_t { + int inited; + long semaphore; +}; + +typedef struct _dng_pthread_once_t dng_pthread_once_t; +#define DNG_PTHREAD_ONCE_INIT { 0, -1 } + +#define dng_pthread_equal(t1, t2) ((t1) == (t2)) + +typedef struct dng_pthread_attr_impl *dng_pthread_attr_t; + +int dng_pthread_attr_init(dng_pthread_attr_t *attr); +int dng_pthread_attr_destroy(dng_pthread_attr_t *attr); + +int dng_pthread_attr_setstacksize(dng_pthread_attr_t *attr, size_t stacksize); +int dng_pthread_attr_getstacksize(const dng_pthread_attr_t *attr, size_t *stacksize); + +int dng_pthread_create(dng_pthread_t *thread, const dng_pthread_attr_t * /* attrs */, void * (*func)(void *), void *arg); +int dng_pthread_detach(dng_pthread_t thread); +int dng_pthread_join(dng_pthread_t thread, void **result); +dng_pthread_t dng_pthread_self(); +void dng_pthread_exit(void *result); + +#define DNG_PTHREAD_MUTEX_RECURSIVE 0 +typedef unsigned long dng_pthread_mutexattr_t; + +int dng_pthread_mutexattr_init(dng_pthread_mutexattr_t *mutexattr); +int dng_pthread_mutexattr_settype(dng_pthread_mutexattr_t *mutexattr, int /*the options*/); + +int dng_pthread_mutex_init(dng_pthread_mutex_t *mutex, void * /* attrs */); +int dng_pthread_mutex_destroy(dng_pthread_mutex_t *mutex); +int dng_pthread_mutex_lock(dng_pthread_mutex_t *mutex); +int dng_pthread_mutex_unlock(dng_pthread_mutex_t *mutex); + +int dng_pthread_cond_init(dng_pthread_cond_t *cond, void * /* attrs */); +int dng_pthread_cond_destroy(dng_pthread_cond_t *cond); +int dng_pthread_cond_wait(dng_pthread_cond_t *cond, dng_pthread_mutex_t *mutex); +int dng_pthread_cond_timedwait(dng_pthread_cond_t *cond, dng_pthread_mutex_t *mutex, struct dng_timespec *latest_time); +int dng_pthread_cond_signal(dng_pthread_cond_t *cond); +int dng_pthread_cond_broadcast(dng_pthread_cond_t *cond); + +int dng_pthread_once(dng_pthread_once_t *once, void (*init_func)()); + +int dng_pthread_key_create(dng_pthread_key_t * key, void (*destructor) (void *)); +int dng_pthread_key_delete(dng_pthread_key_t key); +int dng_pthread_setspecific(dng_pthread_key_t key, const void *value); +void *dng_pthread_getspecific(dng_pthread_key_t key); + +typedef struct dng_pthread_rwlock_impl *dng_pthread_rwlock_t; +typedef void *pthread_rwlockattr1_t; + +int dng_pthread_rwlock_destroy(dng_pthread_rwlock_t * rwlock); +int dng_pthread_rwlock_init(dng_pthread_rwlock_t * rwlock, const pthread_rwlockattr1_t * attrs); +int dng_pthread_rwlock_rdlock(dng_pthread_rwlock_t * rwlock); +int dng_pthread_rwlock_tryrdlock(dng_pthread_rwlock_t * rwlock); +int dng_pthread_rwlock_trywrlock(dng_pthread_rwlock_t * rwlock); +int dng_pthread_rwlock_unlock(dng_pthread_rwlock_t * rwlock); +int dng_pthread_rwlock_wrlock(dng_pthread_rwlock_t * rwlock); + +// dng_pthread may maintain per-thread global state. This routine frees that global state. +// there is no need to call this for threads created by dng_pthread and one can call +// dng_pthread routines of a thread after dng_pthread_disassociate as the global state will +// be recreated as necessary. However dng_pthread_disassociate will need to be called again +// and there is a slight performance cost. Do not call this routine while holding a mutex, etc. +void dng_pthread_disassociate(); + +void dng_pthread_terminate(); + +/*****************************************************************************/ + +// Map symbols back to plain pthread names. This whole mechanism is so the DNG pthreads library +// symbols do not collide with another pthread emulation library +// that may be in use in the same linked entity. However if that is the case, it would be far better +// to have the DNG code use the same pthread library as the rest of the code. + +//HERWIG#define pthread_t dng_pthread_t +//HERWIG#define pthread_mutex_t dng_pthread_mutex_t +//HERWIG#define pthread_cond_t dng_pthread_cond_t +//HERWIG#define pthread_once_t dng_pthread_once_t +//HERWIG#define pthread_key_t dng_pthread_key_t + +#undef PTHREAD_MUTEX_INITIALIZER +#define PTHREAD_MUTEX_INITIALIZER DNG_PTHREAD_MUTEX_INITIALIZER +#undef PTHREAD_COND_INITIALIZER +#define PTHREAD_COND_INITIALIZER DNG_PTHREAD_COND_INITIALIZER + +#undef PTHREAD_ONCE_INIT +#define PTHREAD_ONCE_INIT DNG_PTHREAD_ONCE_INIT + +#if _MSC_VER < 1900 +#define timespec dng_timespec +#endif + +/* If it is defined on Windows, it probably has the wrong value... */ +#if defined(WIN32) || !defined(ETIMEDOUT) +#undef ETIMEDOUT +#define ETIMEDOUT DNG_ETIMEDOUT +#endif + +//HERWIG#define pthread_equal dng_pthread_equal + +//HERWIG#define pthread_attr_t dng_pthread_attr_t + +//HERWIG#define pthread_attr_init dng_pthread_attr_init +//HERWIG#define pthread_attr_destroy dng_pthread_attr_destroy + +//HERWIG#define pthread_attr_setstacksize dng_pthread_attr_setstacksize +//HERWIG#define pthread_attr_getstacksize dng_pthread_attr_getstacksize + +//HERWIG#define pthread_create dng_pthread_create +//HERWIG#define pthread_detach dng_pthread_detach +//HERWIG#define pthread_join dng_pthread_join +//HERWIG#define pthread_self dng_pthread_self +#define pthread_exit dng_pthread_exit + +//HERWIG#define pthread_mutex_init dng_pthread_mutex_init +//HERWIG#define pthread_mutex_destroy dng_pthread_mutex_destroy +//HERWIG#define pthread_mutex_lock dng_pthread_mutex_lock +//HERWIG#define pthread_mutex_unlock dng_pthread_mutex_unlock + +//HERWIG#define pthread_cond_init dng_pthread_cond_init +//HERWIG#define pthread_cond_destroy dng_pthread_cond_destroy +//HERWIG#define pthread_cond_wait dng_pthread_cond_wait +//HERWIG#define pthread_cond_timedwait dng_pthread_cond_timedwait +//HERWIG#define pthread_cond_signal dng_pthread_cond_signal +//HERWIG#define pthread_cond_broadcast dng_pthread_cond_broadcast + +//HERWIG#define pthread_once dng_pthread_once + +//HERWIG#define pthread_key_create dng_pthread_key_create +//HERWIG#define pthread_key_delete dng_pthread_key_delete +//HERWIG#define pthread_setspecific dng_pthread_setspecific +//HERWIG#define pthread_getspecific dng_pthread_getspecific + +//HERWIG#define pthread_rwlock_t dng_pthread_rwlock_t + +//HERWIG#define pthread_rwlock_destroy dng_pthread_rwlock_destroy +//HERWIG#define pthread_rwlock_init dng_pthread_rwlock_init +//HERWIG#define pthread_rwlock_rdlock dng_pthread_rwlock_rdlock +//HERWIG#define pthread_rwlock_tryrdlock dng_pthread_rwlock_tryrdlock +//HERWIG#define pthread_rwlock_trywrlock dng_pthread_rwlock_trywrlock +//HERWIG#define pthread_rwlock_unlock dng_pthread_rwlock_unlock +//HERWIG#define pthread_rwlock_wrlock dng_pthread_rwlock_wrlock + +/*****************************************************************************/ + +#ifdef __cplusplus +} +#endif + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ + +#ifdef __cplusplus +extern "C" +{ +#endif + +int dng_pthread_now (struct timespec *now); + +#ifdef __cplusplus +} +#endif + +/*****************************************************************************/ + +#endif // qDNGThreadSafe + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_rational.cpp b/dng/dng_rational.cpp new file mode 100644 index 0000000..b9de06f --- /dev/null +++ b/dng/dng_rational.cpp @@ -0,0 +1,143 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_rational.h" + +#include "dng_utils.h" + +/*****************************************************************************/ + +real64 dng_srational::As_real64 () const + { + + if (d) + return (real64) n / (real64) d; + + else + return 0.0; + + } + +/*****************************************************************************/ + +void dng_srational::Set_real64 (real64 x, int32 dd) + { + + if (x == 0.0) + { + + *this = dng_srational (0, 1); + + } + + if (dd == 0) + { + + real64 y = Abs_real64 (x); + + if (y >= 32768.0) + { + dd = 1; + } + + else if (y >= 1.0) + { + dd = 32768; + } + + else + { + dd = 32768 * 32768; + } + + } + + *this = dng_srational (Round_int32 (x * dd), dd); + + } + +/*****************************************************************************/ + +void dng_srational::ReduceByFactor (int32 factor) + { + + while (n % factor == 0 && + d % factor == 0 && + d >= factor) + { + n /= factor; + d /= factor; + } + + } + +/*****************************************************************************/ + +real64 dng_urational::As_real64 () const + { + + if (d) + return (real64) n / (real64) d; + + else + return 0.0; + + } + +/*****************************************************************************/ + +void dng_urational::Set_real64 (real64 x, uint32 dd) + { + + if (x <= 0.0) + { + + *this = dng_urational (0, 1); + + } + + if (dd == 0) + { + + if (x >= 32768.0) + { + dd = 1; + } + + else if (x >= 1.0) + { + dd = 32768; + } + + else + { + dd = 32768 * 32768; + } + + } + + *this = dng_urational (Round_uint32 (x * dd), dd); + + } + +/*****************************************************************************/ + +void dng_urational::ReduceByFactor (uint32 factor) + { + + while (n % factor == 0 && + d % factor == 0 && + d >= factor) + { + n /= factor; + d /= factor; + } + + } + +/*****************************************************************************/ diff --git a/dng/dng_rational.h b/dng/dng_rational.h new file mode 100644 index 0000000..17e2589 --- /dev/null +++ b/dng/dng_rational.h @@ -0,0 +1,144 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Signed and unsigned rational data types. + */ + +/*****************************************************************************/ + +#ifndef __dng_rational__ +#define __dng_rational__ + +/*****************************************************************************/ + +#include "dng_types.h" + +/*****************************************************************************/ + +class dng_srational + { + + public: + + int32 n; // Numerator + int32 d; // Denominator + + public: + + dng_srational () + : n (0) + , d (0) + { + } + + dng_srational (int32 nn, int32 dd) + : n (nn) + , d (dd) + { + } + + void Clear () + { + n = 0; + d = 0; + } + + bool IsValid () const + { + return d != 0; + } + + bool NotValid () const + { + return !IsValid (); + } + + bool operator== (const dng_srational &r) const + { + return (n == r.n) && + (d == r.d); + } + + bool operator!= (const dng_srational &r) const + { + return !(*this == r); + } + + real64 As_real64 () const; + + void Set_real64 (real64 x, int32 dd = 0); + + void ReduceByFactor (int32 factor); + + }; + +/*****************************************************************************/ + +class dng_urational + { + + public: + + uint32 n; // Numerator + uint32 d; // Denominator + + public: + + dng_urational () + : n (0) + , d (0) + { + } + + dng_urational (uint32 nn, uint32 dd) + : n (nn) + , d (dd) + { + } + + void Clear () + { + n = 0; + d = 0; + } + + bool IsValid () const + { + return d != 0; + } + + bool NotValid () const + { + return !IsValid (); + } + + bool operator== (const dng_urational &r) const + { + return (n == r.n) && + (d == r.d); + } + + bool operator!= (const dng_urational &r) const + { + return !(*this == r); + } + + real64 As_real64 () const; + + void Set_real64 (real64 x, uint32 dd = 0); + + void ReduceByFactor (uint32 factor); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_read_image.cpp b/dng/dng_read_image.cpp new file mode 100644 index 0000000..edc6ebf --- /dev/null +++ b/dng/dng_read_image.cpp @@ -0,0 +1,3541 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_read_image.h" + +#include "dng_abort_sniffer.h" +#include "dng_area_task.h" +#include "dng_bottlenecks.h" +#include "dng_exceptions.h" +#include "dng_flags.h" +#include "dng_globals.h" +#include "dng_host.h" +#include "dng_image.h" +#include "dng_ifd.h" +#include "dng_jpeg_image.h" +#include "dng_lossless_jpeg.h" +#include "dng_mutex.h" +#include "dng_memory.h" +#include "dng_pixel_buffer.h" +#include "dng_safe_arithmetic.h" +#include "dng_tag_types.h" +#include "dng_tag_values.h" +#include "dng_uncopyable.h" +#include "dng_utils.h" + +#include "zlib.h" + +#if qDNGUseLibJPEG +#include "jpeglib.h" +#include "jerror.h" +#endif + +/******************************************************************************/ + +static void DecodeDelta8 (uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 channels) + { + + const uint32 dRowStep = cols * channels; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 1; col < cols; col++) + { + + for (uint32 channel = 0; channel < channels; channel++) + { + + dPtr [col * channels + channel] += dPtr [(col - 1) * channels + channel]; + + } + + } + + dPtr += dRowStep; + + } + + } + +/******************************************************************************/ + +static void DecodeDelta16 (uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 channels) + { + + const uint32 dRowStep = cols * channels; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 1; col < cols; col++) + { + + for (uint32 channel = 0; channel < channels; channel++) + { + + dPtr [col * channels + channel] += dPtr [(col - 1) * channels + channel]; + + } + + } + + dPtr += dRowStep; + + } + + } + +/******************************************************************************/ + +static void DecodeDelta32 (uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 channels) + { + + const uint32 dRowStep = cols * channels; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 1; col < cols; col++) + { + + for (uint32 channel = 0; channel < channels; channel++) + { + + dPtr [col * channels + channel] += dPtr [(col - 1) * channels + channel]; + + } + + } + + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +inline void DecodeDeltaBytes (uint8 *bytePtr, int32 cols, int32 channels) + { + + if (channels == 1) + { + + uint8 b0 = bytePtr [0]; + + bytePtr += 1; + + for (int32 col = 1; col < cols; ++col) + { + + b0 += bytePtr [0]; + + bytePtr [0] = b0; + + bytePtr += 1; + + } + + } + + else if (channels == 3) + { + + uint8 b0 = bytePtr [0]; + uint8 b1 = bytePtr [1]; + uint8 b2 = bytePtr [2]; + + bytePtr += 3; + + for (int32 col = 1; col < cols; ++col) + { + + b0 += bytePtr [0]; + b1 += bytePtr [1]; + b2 += bytePtr [2]; + + bytePtr [0] = b0; + bytePtr [1] = b1; + bytePtr [2] = b2; + + bytePtr += 3; + + } + + } + + else if (channels == 4) + { + + uint8 b0 = bytePtr [0]; + uint8 b1 = bytePtr [1]; + uint8 b2 = bytePtr [2]; + uint8 b3 = bytePtr [3]; + + bytePtr += 4; + + for (int32 col = 1; col < cols; ++col) + { + + b0 += bytePtr [0]; + b1 += bytePtr [1]; + b2 += bytePtr [2]; + b3 += bytePtr [3]; + + bytePtr [0] = b0; + bytePtr [1] = b1; + bytePtr [2] = b2; + bytePtr [3] = b3; + + bytePtr += 4; + + } + + } + + else + { + + for (int32 col = 1; col < cols; ++col) + { + + for (int32 chan = 0; chan < channels; ++chan) + { + + bytePtr [chan + channels] += bytePtr [chan]; + + } + + bytePtr += channels; + + } + + } + + } + +/*****************************************************************************/ + +static void DecodeFPDelta (uint8 *input, + uint8 *output, + int32 cols, + int32 channels, + int32 bytesPerSample) + { + + DecodeDeltaBytes (input, cols * bytesPerSample, channels); + + int32 rowIncrement = cols * channels; + + if (bytesPerSample == 2) + { + + #if qDNGBigEndian + const uint8 *input0 = input; + const uint8 *input1 = input + rowIncrement; + #else + const uint8 *input1 = input; + const uint8 *input0 = input + rowIncrement; + #endif + + for (int32 col = 0; col < rowIncrement; ++col) + { + + output [0] = input0 [col]; + output [1] = input1 [col]; + + output += 2; + + } + + } + + else if (bytesPerSample == 3) + { + + const uint8 *input0 = input; + const uint8 *input1 = input + rowIncrement; + const uint8 *input2 = input + rowIncrement * 2; + + for (int32 col = 0; col < rowIncrement; ++col) + { + + output [0] = input0 [col]; + output [1] = input1 [col]; + output [2] = input2 [col]; + + output += 3; + + } + + } + + else + { + + #if qDNGBigEndian + const uint8 *input0 = input; + const uint8 *input1 = input + rowIncrement; + const uint8 *input2 = input + rowIncrement * 2; + const uint8 *input3 = input + rowIncrement * 3; + #else + const uint8 *input3 = input; + const uint8 *input2 = input + rowIncrement; + const uint8 *input1 = input + rowIncrement * 2; + const uint8 *input0 = input + rowIncrement * 3; + #endif + + for (int32 col = 0; col < rowIncrement; ++col) + { + + output [0] = input0 [col]; + output [1] = input1 [col]; + output [2] = input2 [col]; + output [3] = input3 [col]; + + output += 4; + + } + + } + + } + +/*****************************************************************************/ + +bool DecodePackBits (dng_stream &stream, + uint8 *dPtr, + int32 dstCount) + { + + while (dstCount > 0) + { + + int32 runCount = (int8) stream.Get_uint8 (); + + if (runCount >= 0) + { + + ++runCount; + + dstCount -= runCount; + + if (dstCount < 0) + return false; + + stream.Get (dPtr, runCount); + + dPtr += runCount; + + } + + else + { + + runCount = -runCount + 1; + + dstCount -= runCount; + + if (dstCount < 0) + return false; + + uint8 x = stream.Get_uint8 (); + + while (runCount--) + { + + *(dPtr++) = x; + + } + + } + + } + + return true; + + } + +/******************************************************************************/ + +class dng_lzw_expander: private dng_uncopyable + { + + private: + + enum + { + kResetCode = 256, + kEndCode = 257, + kTableSize = 4096 + }; + + struct LZWExpanderNode + { + int16 prefix; + int16 final; + int16 depth; + int16 fake_for_padding; + }; + + dng_memory_data fBuffer; + + LZWExpanderNode *fTable; + + const uint8 *fSrcPtr; + + int32 fSrcCount; + + int32 fByteOffset; + + uint32 fBitBuffer; + int32 fBitBufferCount; + + int32 fNextCode; + + int32 fCodeSize; + + public: + + dng_lzw_expander (); + + bool Expand (const uint8 *sPtr, + uint8 *dPtr, + int32 sCount, + int32 dCount); + + private: + + void InitTable (); + + void AddTable (int32 w, int32 k); + + bool GetCodeWord (int32 &code); + + }; + +/******************************************************************************/ + +dng_lzw_expander::dng_lzw_expander () + + : fBuffer () + , fTable (NULL) + , fSrcPtr (NULL) + , fSrcCount (0) + , fByteOffset (0) + , fBitBuffer (0) + , fBitBufferCount (0) + , fNextCode (0) + , fCodeSize (0) + + { + + fBuffer.Allocate ((kTableSize + 1) * sizeof (LZWExpanderNode)); + + fTable = (LZWExpanderNode *) fBuffer.Buffer (); + + } + +/******************************************************************************/ + +void dng_lzw_expander::InitTable () + { + + fCodeSize = 9; + + fNextCode = 258; + + LZWExpanderNode *node = &fTable [0]; + + for (int32 code = 0; code <= kTableSize; code++) + { + + node->prefix = -1; + node->final = (int16) code; + node->depth = 1; + + node++; + + } + + } + +/******************************************************************************/ + +void dng_lzw_expander::AddTable (int32 w, int32 k) + { + + DNG_ASSERT ((w >= 0) && (w <= kTableSize), + "bad w value in dng_lzw_expander::AddTable"); + + LZWExpanderNode *parentNode = &fTable [w]; + + int32 nextCode = fNextCode; + + fNextCode++; + + DNG_ASSERT ((nextCode >= 0) && (nextCode <= kTableSize), + "bad fNextCode value in dng_lzw_expander::AddTable"); + + LZWExpanderNode *node = &fTable [nextCode]; + + node->prefix = (int16) w; + node->final = (int16) k; + node->depth = 1 + parentNode->depth; + + if (nextCode + 1 == (1 << fCodeSize) - 1) + { + if (fCodeSize != 12) + fCodeSize++; + } + + } + +/******************************************************************************/ + +bool dng_lzw_expander::GetCodeWord (int32 &code) + { + + // The bit buffer has the current code in the most significant bits, + // so shift off the low orders. + + int32 codeSize = fCodeSize; + + code = fBitBuffer >> (32 - codeSize); + + if (fBitBufferCount >= codeSize) + { + + // Typical case; get the code from the bit buffer. + + fBitBuffer <<= codeSize; + fBitBufferCount -= codeSize; + + } + + else + { + + // The buffer needs to be refreshed. + + const int32 bitsSoFar = fBitBufferCount; + + if (fByteOffset >= fSrcCount) + return false; + + // Buffer a long word + + const uint8 *ptr = fSrcPtr + fByteOffset; + + #if qDNGBigEndian + + fBitBuffer = *((const uint32 *) ptr); + + #else + + { + + uint32 b0 = ptr [0]; + uint32 b1 = ptr [1]; + uint32 b2 = ptr [2]; + uint32 b3 = ptr [3]; + + fBitBuffer = (((((b0 << 8) | b1) << 8) | b2) << 8) | b3; + + } + + #endif + + fBitBufferCount = 32; + + fByteOffset += 4; + + // Number of additional bits we need + + const int32 bitsUsed = codeSize - bitsSoFar; + + // Number of low order bits in the current buffer we don't care about + + const int32 bitsNotUsed = 32 - bitsUsed; + + code |= fBitBuffer >> bitsNotUsed; + + fBitBuffer <<= bitsUsed; + fBitBufferCount -= bitsUsed; + + } + + return true; + + } + +/******************************************************************************/ + +bool dng_lzw_expander::Expand (const uint8 *sPtr, + uint8 *dPtr, + int32 sCount, + int32 dCount) + { + + if (sCount < 0 || dCount < 0) + { + return false; + } + + void *dStartPtr = dPtr; + + fSrcPtr = sPtr; + + fSrcCount = sCount; + + fByteOffset = 0; + + /* the master decode loop */ + + while (true) + { + + InitTable (); + + int32 code; + + do + { + + if (!GetCodeWord (code)) + return false; + + DNG_ASSERT (code <= fNextCode, + "Unexpected LZW code in dng_lzw_expander::Expand"); + + } + while (code == kResetCode); + + if (code == kEndCode) + return true; + + if (code > kEndCode) + return false; + + int32 oldCode = code; + int32 inChar = code; + + (void) inChar; + + *(dPtr++) = (uint8) code; + + if (--dCount == 0) + return true; + + while (true) + { + + if (!GetCodeWord (code)) + return false; + + if (code == kResetCode) + break; + + if (code == kEndCode) + return true; + + const int32 inCode = code; + + bool repeatLastPixel = false; + + if (code >= fNextCode) + { + + // This is either a bad file or our code table is not big enough; we + // are going to repeat the last code seen and attempt to muddle thru. + + code = oldCode; + + repeatLastPixel = true; + + } + + // this can only happen if we hit 2 bad codes in a row + + if (code > fNextCode) + return false; + + const int32 depth = fTable [code].depth; + + if (depth < dCount) + { + + dCount -= depth; + + dPtr += depth; + + uint8 *ptr = dPtr; + + // give the compiler an extra hint to optimize these as registers + + const LZWExpanderNode *localTable = fTable; + + int32 localCode = code; + + // this is usually the hottest loop in LZW expansion + + while (localCode >= kResetCode) + { + + if (ptr <= dStartPtr) + return false; // about to trash memory + + const LZWExpanderNode &node = localTable [localCode]; + + uint8 tempFinal = (uint8) node.final; + + localCode = node.prefix; + + // Check for bogus table entry + + if (localCode < 0 || localCode > kTableSize) + return false; + + *(--ptr) = tempFinal; + + } + + code = localCode; + + inChar = localCode; + + if (ptr <= dStartPtr) + return false; // about to trash memory + + *(--ptr) = (uint8) inChar; + + } + + else + { + + // There might not be enough room for the full code + // so skip the end of it. + + const int32 skip = depth - dCount; + + for (int32 i = 0; i < skip ; i++) + { + const LZWExpanderNode &node = fTable [code]; + code = node.prefix; + } + + int32 depthUsed = depth - skip; + + dCount -= depthUsed; + + (void) dCount; + + dPtr += depthUsed; + + uint8 *ptr = dPtr; + + while (code >= 0) + { + + if (ptr <= dStartPtr) + return false; // about to trash memory + + const LZWExpanderNode &node = fTable [code]; + + *(--ptr) = (uint8) node.final; + + code = node.prefix; + + // Check for bogus table entry + + if (code > kTableSize) + return false; + + } + + return true; + + } + + if (repeatLastPixel) + { + + *(dPtr++) = (uint8) inChar; + + if (--dCount == 0) + return true; + + } + + if (fNextCode < kTableSize) + { + + AddTable (oldCode, code); + + } + + oldCode = inCode; + + } + + } + + return false; + + } + +/*****************************************************************************/ + +dng_row_interleaved_image::dng_row_interleaved_image (dng_image &image, + uint32 factor) + + : dng_image (image.Bounds (), + image.Planes (), + image.PixelType ()) + + , fImage (image ) + , fFactor (factor) + + { + + } + +/*****************************************************************************/ + +int32 dng_row_interleaved_image::MapRow (int32 row) const + { + + uint32 rows = Height (); + + int32 top = Bounds ().t; + + uint32 fieldRow = row - top; + + uint32 field; + + for (field = 0; true; field++) + { + + uint32 fieldRows = (rows - field + fFactor - 1) / fFactor; + + if (fieldRow < fieldRows) + { + + break; + + } + + fieldRow -= fieldRows; + + } + + return fieldRow * fFactor + field + top; + + } + +/*****************************************************************************/ + +void dng_row_interleaved_image::DoGet (dng_pixel_buffer &buffer) const + { + + dng_pixel_buffer tempBuffer (buffer); + + for (int32 row = buffer.fArea.t; row < buffer.fArea.b; row++) + { + + tempBuffer.fArea.t = MapRow (row); + + tempBuffer.fArea.b = tempBuffer.fArea.t + 1; + + tempBuffer.fData = (void *) buffer.DirtyPixel (row, + buffer.fArea.l, + buffer.fPlane); + + fImage.Get (tempBuffer); + + } + + } + +/*****************************************************************************/ + +void dng_row_interleaved_image::DoPut (const dng_pixel_buffer &buffer) + { + + dng_pixel_buffer tempBuffer (buffer); + + for (int32 row = buffer.fArea.t; row < buffer.fArea.b; row++) + { + + tempBuffer.fArea.t = MapRow (row); + + tempBuffer.fArea.b = tempBuffer.fArea.t + 1; + + tempBuffer.fData = (void *) buffer.ConstPixel (row, + buffer.fArea.l, + buffer.fPlane); + + fImage.Put (tempBuffer); + + } + + } + +/*****************************************************************************/ + +static void ReorderSubTileBlocks (dng_host &host, + const dng_ifd &ifd, + dng_pixel_buffer &buffer, + AutoPtr &tempBuffer) + { + + uint32 tempBufferSize = ComputeBufferSize (buffer.fPixelType, + buffer.fArea.Size (), + buffer.fPlanes, + padNone); + + if (!tempBuffer.Get () || tempBuffer->LogicalSize () < tempBufferSize) + { + + tempBuffer.Reset (host.Allocate (tempBufferSize)); + + } + + uint32 blockRows = ifd.fSubTileBlockRows; + uint32 blockCols = ifd.fSubTileBlockCols; + + uint32 rowBlocks = buffer.fArea.H () / blockRows; + uint32 colBlocks = buffer.fArea.W () / blockCols; + + int32 rowStep = buffer.fRowStep * buffer.fPixelSize; + int32 colStep = buffer.fColStep * buffer.fPixelSize; + + int32 rowBlockStep = rowStep * blockRows; + int32 colBlockStep = colStep * blockCols; + + uint32 blockColBytes = blockCols * buffer.fPlanes * buffer.fPixelSize; + + const uint8 *s0 = (const uint8 *) buffer.fData; + uint8 *d0 = tempBuffer->Buffer_uint8 (); + + for (uint32 rowBlock = 0; rowBlock < rowBlocks; rowBlock++) + { + + uint8 *d1 = d0; + + for (uint32 colBlock = 0; colBlock < colBlocks; colBlock++) + { + + uint8 *d2 = d1; + + for (uint32 blockRow = 0; blockRow < blockRows; blockRow++) + { + + for (uint32 j = 0; j < blockColBytes; j++) + { + + d2 [j] = s0 [j]; + + } + + s0 += blockColBytes; + + d2 += rowStep; + + } + + d1 += colBlockStep; + + } + + d0 += rowBlockStep; + + } + + // Copy back reordered pixels. + + DoCopyBytes (tempBuffer->Buffer (), + buffer.fData, + tempBufferSize); + + } + +/*****************************************************************************/ + +class dng_image_spooler: public dng_spooler, + private dng_uncopyable + { + + private: + + dng_host &fHost; + + const dng_ifd &fIFD; + + dng_image &fImage; + + dng_rect fTileArea; + + uint32 fPlane; + uint32 fPlanes; + + dng_memory_block &fBlock; + + AutoPtr &fSubTileBuffer; + + dng_rect fTileStrip; + + uint8 *fBuffer; + + uint32 fBufferCount; + uint32 fBufferSize; + + public: + + dng_image_spooler (dng_host &host, + const dng_ifd &ifd, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + dng_memory_block &block, + AutoPtr &subTileBuffer); + + virtual ~dng_image_spooler (); + + virtual void Spool (const void *data, + uint32 count); + + }; + +/*****************************************************************************/ + +dng_image_spooler::dng_image_spooler (dng_host &host, + const dng_ifd &ifd, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + dng_memory_block &block, + AutoPtr &subTileBuffer) + + : fHost (host) + , fIFD (ifd) + , fImage (image) + , fTileArea (tileArea) + , fPlane (plane) + , fPlanes (planes) + , fBlock (block) + , fSubTileBuffer (subTileBuffer) + + , fTileStrip () + , fBuffer (NULL) + , fBufferCount (0) + , fBufferSize (0) + + { + + uint32 bytesPerRow = fTileArea.W () * fPlanes * (uint32) sizeof (uint16); + + DNG_REQUIRE (bytesPerRow > 0, "Bad bytesPerRow in dng_image_spooler"); + + uint32 stripLength = Pin_uint32 (ifd.fSubTileBlockRows, + fBlock.LogicalSize () / bytesPerRow, + fTileArea.H ()); + + stripLength = stripLength / ifd.fSubTileBlockRows + * ifd.fSubTileBlockRows; + + fTileStrip = fTileArea; + fTileStrip.b = fTileArea.t + stripLength; + + fBuffer = (uint8 *) fBlock.Buffer (); + + fBufferCount = 0; + fBufferSize = bytesPerRow * stripLength; + + } + +/*****************************************************************************/ + +dng_image_spooler::~dng_image_spooler () + { + + } + +/*****************************************************************************/ + +void dng_image_spooler::Spool (const void *data, + uint32 count) + { + + while (count) + { + + uint32 block = Min_uint32 (count, fBufferSize - fBufferCount); + + if (block == 0) + { + return; + } + + DoCopyBytes (data, + fBuffer + fBufferCount, + block); + + data = ((const uint8 *) data) + block; + + count -= block; + + fBufferCount += block; + + if (fBufferCount == fBufferSize) + { + + fHost.SniffForAbort (); + + dng_pixel_buffer buffer (fTileStrip, + fPlane, + fPlanes, + ttShort, + pcInterleaved, + fBuffer); + + if (fIFD.fSubTileBlockRows > 1) + { + + ReorderSubTileBlocks (fHost, + fIFD, + buffer, + fSubTileBuffer); + + } + + fImage.Put (buffer); + + uint32 stripLength = fTileStrip.H (); + + fTileStrip.t = fTileStrip.b; + + fTileStrip.b = Min_int32 (fTileStrip.t + stripLength, + fTileArea.b); + + fBufferCount = 0; + + fBufferSize = fTileStrip.W () * + fTileStrip.H () * + fPlanes * (uint32) sizeof (uint16); + + } + + } + + } + +/*****************************************************************************/ + +dng_read_image::dng_read_image () + + : fJPEGTables () + + { + + } + +/*****************************************************************************/ + +dng_read_image::~dng_read_image () + { + + } + +/*****************************************************************************/ + +bool dng_read_image::ReadUncompressed (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer) + { + + uint32 rows = tileArea.H (); + uint32 samplesPerRow = tileArea.W (); + + if (ifd.fPlanarConfiguration == pcRowInterleaved) + { + rows *= planes; + } + else + { + samplesPerRow *= planes; + } + + uint32 samplesPerTile = samplesPerRow * rows; + + if (uncompressedBuffer.Get () == NULL) + { + + #if qDNGValidate + + ReportError ("Fuzz: Missing uncompressed buffer"); + + #endif + + ThrowBadFormat (); + + } + + uint32 bitDepth = ifd.fBitsPerSample [plane]; + + uint32 pixelType = ttUndefined; + + if (bitDepth == 8) + { + + pixelType = ttByte; + + stream.Get (uncompressedBuffer->Buffer (), samplesPerTile); + + } + + else if (bitDepth == 16 && ifd.fSampleFormat [0] == sfFloatingPoint) + { + + pixelType = ttFloat; + + uint32 *p_uint32 = (uint32 *) uncompressedBuffer->Buffer (); + + for (uint32 j = 0; j < samplesPerTile; j++) + { + + p_uint32 [j] = DNG_HalfToFloat (stream.Get_uint16 ()); + + } + + } + + else if (bitDepth == 24 && ifd.fSampleFormat [0] == sfFloatingPoint) + { + + pixelType = ttFloat; + + uint32 *p_uint32 = (uint32 *) uncompressedBuffer->Buffer (); + + for (uint32 j = 0; j < samplesPerTile; j++) + { + + uint8 input [3]; + + if (stream.LittleEndian ()) + { + input [2] = stream.Get_uint8 (); + input [1] = stream.Get_uint8 (); + input [0] = stream.Get_uint8 (); + } + + else + { + input [0] = stream.Get_uint8 (); + input [1] = stream.Get_uint8 (); + input [2] = stream.Get_uint8 (); + } + + p_uint32 [j] = DNG_FP24ToFloat (input); + + } + + } + + else if (bitDepth == 16) + { + + pixelType = ttShort; + + stream.Get (uncompressedBuffer->Buffer (), samplesPerTile * 2); + + if (stream.SwapBytes ()) + { + + DoSwapBytes16 ((uint16 *) uncompressedBuffer->Buffer (), + samplesPerTile); + + } + + } + + else if (bitDepth == 32) + { + + pixelType = image.PixelType (); + + stream.Get (uncompressedBuffer->Buffer (), samplesPerTile * 4); + + if (stream.SwapBytes ()) + { + + DoSwapBytes32 ((uint32 *) uncompressedBuffer->Buffer (), + samplesPerTile); + + } + + } + + else if (bitDepth == 12) + { + + pixelType = ttShort; + + uint16 *p = (uint16 *) uncompressedBuffer->Buffer (); + + uint32 evenSamples = samplesPerRow >> 1; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 j = 0; j < evenSamples; j++) + { + + uint32 b0 = stream.Get_uint8 (); + uint32 b1 = stream.Get_uint8 (); + uint32 b2 = stream.Get_uint8 (); + + p [0] = (uint16) ((b0 << 4) | (b1 >> 4)); + p [1] = (uint16) (((b1 << 8) | b2) & 0x0FFF); + + p += 2; + + } + + if (samplesPerRow & 1) + { + + uint32 b0 = stream.Get_uint8 (); + uint32 b1 = stream.Get_uint8 (); + + p [0] = (uint16) ((b0 << 4) | (b1 >> 4)); + + p += 1; + + } + + } + + } + + else if (bitDepth > 8 && bitDepth < 16) + { + + pixelType = ttShort; + + uint16 *p = (uint16 *) uncompressedBuffer->Buffer (); + + uint32 bitMask = (1 << bitDepth) - 1; + + for (uint32 row = 0; row < rows; row++) + { + + uint32 bitBuffer = 0; + uint32 bufferBits = 0; + + for (uint32 j = 0; j < samplesPerRow; j++) + { + + while (bufferBits < bitDepth) + { + + bitBuffer = (bitBuffer << 8) | stream.Get_uint8 (); + + bufferBits += 8; + + } + + p [j] = (uint16) ((bitBuffer >> (bufferBits - bitDepth)) & bitMask); + + bufferBits -= bitDepth; + + } + + p += samplesPerRow; + + } + + } + + else if (bitDepth > 16 && bitDepth < 32) + { + + pixelType = ttLong; + + uint32 *p = (uint32 *) uncompressedBuffer->Buffer (); + + uint32 bitMask = (1 << bitDepth) - 1; + + for (uint32 row = 0; row < rows; row++) + { + + uint64 bitBuffer = 0; + uint32 bufferBits = 0; + + for (uint32 j = 0; j < samplesPerRow; j++) + { + + while (bufferBits < bitDepth) + { + + bitBuffer = (bitBuffer << 8) | stream.Get_uint8 (); + + bufferBits += 8; + + } + + p [j] = ((uint32) (bitBuffer >> (bufferBits - bitDepth))) & bitMask; + + bufferBits -= bitDepth; + + } + + p += samplesPerRow; + + } + + } + + else + { + + return false; + + } + + dng_pixel_buffer buffer (tileArea, + plane, + planes, + pixelType, + ifd.fPlanarConfiguration, + uncompressedBuffer->Buffer ()); + + if (ifd.fSampleBitShift) + { + + buffer.ShiftRight (ifd.fSampleBitShift); + + } + + if (ifd.fSubTileBlockRows > 1) + { + + ReorderSubTileBlocks (host, + ifd, + buffer, + subTileBlockBuffer); + + } + + image.Put (buffer); + + return true; + + } + +/*****************************************************************************/ + +#if qDNGUseLibJPEG + +/*****************************************************************************/ + +static void dng_error_exit (j_common_ptr cinfo) + { + + // Output message. + + (*cinfo->err->output_message) (cinfo); + + // Convert to a dng_exception. + + switch (cinfo->err->msg_code) + { + + case JERR_OUT_OF_MEMORY: + { + ThrowMemoryFull (); + break; + } + + default: + { + ThrowBadFormat (); + } + + } + + } + +/*****************************************************************************/ + +static void dng_output_message (j_common_ptr cinfo) + { + + // Format message to string. + + char buffer [JMSG_LENGTH_MAX]; + + (*cinfo->err->format_message) (cinfo, buffer); + + // Report the libjpeg message as a warning. + + ReportWarning ("libjpeg", buffer); + + } + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ + +void dng_read_image::DecodeLossyJPEG (dng_host &host, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + uint32 /* photometricInterpretation */, + uint32 jpegDataSize, + uint8 *jpegDataInMemory, + bool /* usingMultipleThreads */) + { + + #if qDNGUseLibJPEG + + struct jpeg_decompress_struct cinfo; + + // Setup the error manager. + + struct jpeg_error_mgr jerr; + + cinfo.err = jpeg_std_error (&jerr); + + jerr.error_exit = dng_error_exit; + jerr.output_message = dng_output_message; + + try + { + + // Create the decompression context. + + jpeg_create_decompress (&cinfo); + + // Set up the memory data source manager. + + jpeg_mem_src (&cinfo, + jpegDataInMemory, + jpegDataSize); + + // Read the JPEG header. + + jpeg_read_header (&cinfo, TRUE); + + // Check header. + + { + + // Number of components may not be negative. + + if (cinfo.num_components < 0) + { + ThrowBadFormat ("invalid cinfo.num_components"); + } + + // Convert relevant values from header to uint32. + + uint32 imageWidthAsUint32 = 0; + uint32 imageHeightAsUint32 = 0; + uint32 numComponentsAsUint32 = 0; + + ConvertUnsigned (cinfo.image_width, &imageWidthAsUint32); + ConvertUnsigned (cinfo.image_height, &imageHeightAsUint32); + + // num_components is an int. Casting to unsigned is safe because + // the test above guarantees num_components is not negative. + + ConvertUnsigned (static_cast (cinfo.num_components), + &numComponentsAsUint32); + + // Check that dimensions of JPEG correspond to dimensions of tile. + + if (imageWidthAsUint32 != tileArea.W () || + imageHeightAsUint32 != tileArea.H () || + numComponentsAsUint32 != planes) + { + ThrowBadFormat ("JPEG dimensions do not match tile"); + } + + } + + // Start the compression. + + jpeg_start_decompress (&cinfo); + + // Setup a one-scanline size buffer. + + dng_pixel_buffer buffer (tileArea, + plane, + planes, + ttByte, + pcInterleaved, + NULL); + + buffer.fArea.b = tileArea.t + 1; + + buffer.fDirty = true; + + AutoPtr bufferData (host.Allocate (buffer.fRowStep)); + + buffer.fData = bufferData->Buffer (); + + uint8 *sampArray [1]; + + sampArray [0] = bufferData->Buffer_uint8 (); + + // Read each scanline and save to image. + + while (buffer.fArea.t < tileArea.b) + { + + jpeg_read_scanlines (&cinfo, sampArray, 1); + + image.Put (buffer); + + buffer.fArea.t = buffer.fArea.b; + buffer.fArea.b = buffer.fArea.t + 1; + + } + + // Cleanup. + + jpeg_finish_decompress (&cinfo); + + jpeg_destroy_decompress (&cinfo); + + } + + catch (...) + { + + jpeg_destroy_decompress (&cinfo); + + throw; + + } + + #else + + // The dng_sdk does not include a lossy JPEG decoder. Override this + // this method to add lossy JPEG support. + + (void) host; + (void) image; + (void) tileArea; + (void) plane; + (void) planes; + (void) jpegDataSize; + (void) jpegDataInMemory; + + ThrowProgramError ("Missing lossy JPEG decoder"); + + #endif + + } + +/*****************************************************************************/ + +static dng_memory_block * ReadJPEGDataToBlock (dng_host &host, + dng_stream &stream, + dng_memory_block *tablesBlock, + uint64 tileOffset, + uint32 tileByteCount, + bool patchFirstByte) + { + + if (tileByteCount <= 2) + { + ThrowEndOfFile (); + } + + uint32 tablesByteCount = tablesBlock ? tablesBlock->LogicalSize () : 0; + + if (tablesByteCount && tablesByteCount < 4) + { + ThrowEndOfFile (); + } + + // The JPEG tables start with a two byte SOI marker, and + // and end with a two byte EOI marker. The JPEG tile + // data also starts with a two byte SOI marker. We can + // convert this combination a normal JPEG stream removing + // the last two bytes of the JPEG tables and the first two + // bytes of the tile data, and then concatenating them. + + if (tablesByteCount) + { + + // Ensure the "tileOffset += 2" operation below will not wrap around. + + if (tileOffset > std::numeric_limits::max () - 2) + { + ThrowEndOfFile (); + } + + tablesByteCount -= 2; + + tileOffset += 2; + tileByteCount -= 2; + + } + + // Allocate buffer. + + AutoPtr buffer + (host.Allocate (SafeUint32Add (tablesByteCount, tileByteCount))); + + // Read in table. + + if (tablesByteCount) + { + + DoCopyBytes (tablesBlock->Buffer (), + buffer->Buffer (), + tablesByteCount); + + } + + // Read in tile data. + + stream.SetReadPosition (tileOffset); + + stream.Get (buffer->Buffer_uint8 () + tablesByteCount, tileByteCount); + + // Patch first byte, if required. + + if (patchFirstByte) + { + + buffer->Buffer_uint8 () [0] = 0xFF; + + } + + // Return buffer. + + return buffer.Release (); + + } + +/*****************************************************************************/ + +bool dng_read_image::ReadBaselineJPEG (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + uint32 tileByteCount, + uint8 *jpegDataInMemory, + bool usingMultipleThreads) + { + + // Setup the data source. + + if (fJPEGTables.Get () || !jpegDataInMemory) + { + + AutoPtr jpegDataBlock; + + jpegDataBlock.Reset (ReadJPEGDataToBlock (host, + stream, + fJPEGTables.Get (), + stream.Position (), + tileByteCount, + ifd.fPatchFirstJPEGByte)); + + DecodeLossyJPEG (host, + image, + tileArea, + plane, + planes, + ifd.fPhotometricInterpretation, + jpegDataBlock->LogicalSize (), + jpegDataBlock->Buffer_uint8 (), + usingMultipleThreads); + + } + + else + { + + if (ifd.fPatchFirstJPEGByte && tileByteCount) + { + jpegDataInMemory [0] = 0xFF; + } + + DecodeLossyJPEG (host, + image, + tileArea, + plane, + planes, + ifd.fPhotometricInterpretation, + tileByteCount, + jpegDataInMemory, + usingMultipleThreads); + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_read_image::ReadLosslessJPEG (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + uint32 tileByteCount, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer) + { + + // If the tile area is empty, there's nothing to read. + + if (tileArea.IsEmpty ()) + { + return true; + } + + dng_safe_uint32 bytesPerRow = + (dng_safe_uint32 (tileArea.W ()) * planes * + static_cast (sizeof (uint16))); + + uint32 rowsPerStrip = Pin_uint32 (ifd.fSubTileBlockRows, + kImageBufferSize / bytesPerRow.Get (), + tileArea.H ()); + + rowsPerStrip = rowsPerStrip / ifd.fSubTileBlockRows + * ifd.fSubTileBlockRows; + + dng_safe_uint32 bufferSize = bytesPerRow * rowsPerStrip; + + if (uncompressedBuffer.Get () && + uncompressedBuffer->LogicalSize () < bufferSize.Get ()) + { + + uncompressedBuffer.Reset (); + + } + + if (uncompressedBuffer.Get () == NULL) + { + + uncompressedBuffer.Reset (host.Allocate (bufferSize.Get ())); + + } + + dng_image_spooler spooler (host, + ifd, + image, + tileArea, + plane, + planes, + *uncompressedBuffer.Get (), + subTileBlockBuffer); + + + + dng_safe_uint32 decodedSize = (dng_safe_uint32 (tileArea.W ()) * + tileArea.H () * + planes * + (uint32) sizeof (uint16)); + + bool bug16 = ifd.fLosslessJPEGBug16; + + uint64 tileOffset = stream.Position (); + + DecodeLosslessJPEG (stream, + spooler, + decodedSize.Get (), + decodedSize.Get (), + bug16, + tileOffset + tileByteCount); + + return true; + + } + +/*****************************************************************************/ + +bool dng_read_image::CanReadTile (const dng_ifd &ifd) + { + + if (ifd.fSampleFormat [0] != sfUnsignedInteger && + ifd.fSampleFormat [0] != sfFloatingPoint) + { + return false; + } + + switch (ifd.fCompression) + { + + case ccUncompressed: + { + + if (ifd.fSampleFormat [0] == sfFloatingPoint) + { + + return (ifd.fBitsPerSample [0] == 16 || + ifd.fBitsPerSample [0] == 24 || + ifd.fBitsPerSample [0] == 32); + + } + + return ifd.fBitsPerSample [0] >= 8 && + ifd.fBitsPerSample [0] <= 32; + + } + + case ccJPEG: + { + + if (ifd.fSampleFormat [0] != sfUnsignedInteger) + { + return false; + } + + if (ifd.IsBaselineJPEG ()) + { + + // Baseline JPEG. + + return true; + + } + + else + { + + // Lossless JPEG. + + return ifd.fBitsPerSample [0] >= 8 && + ifd.fBitsPerSample [0] <= 16; + + } + + break; + + } + + case ccLZW: + case ccDeflate: + case ccOldDeflate: + case ccPackBits: + { + + if (ifd.fSampleFormat [0] == sfFloatingPoint) + { + + if (ifd.fCompression == ccPackBits) + { + return false; + } + + if (ifd.fPredictor != cpNullPredictor && + ifd.fPredictor != cpFloatingPoint && + ifd.fPredictor != cpFloatingPointX2 && + ifd.fPredictor != cpFloatingPointX4) + { + return false; + } + + if (ifd.fBitsPerSample [0] != 16 && + ifd.fBitsPerSample [0] != 24 && + ifd.fBitsPerSample [0] != 32) + { + return false; + } + + } + + else + { + + if (ifd.fPredictor != cpNullPredictor && + ifd.fPredictor != cpHorizontalDifference && + ifd.fPredictor != cpHorizontalDifferenceX2 && + ifd.fPredictor != cpHorizontalDifferenceX4) + { + return false; + } + + if (ifd.fBitsPerSample [0] != 8 && + ifd.fBitsPerSample [0] != 16 && + ifd.fBitsPerSample [0] != 32) + { + return false; + } + + } + + return true; + + } + + default: + { + break; + } + + } + + return false; + + } + +/*****************************************************************************/ + +bool dng_read_image::NeedsCompressedBuffer (const dng_ifd &ifd) + { + + if (ifd.fCompression == ccLZW || + ifd.fCompression == ccDeflate || + ifd.fCompression == ccOldDeflate || + ifd.fCompression == ccPackBits) + { + return true; + } + + return false; + + } + +/*****************************************************************************/ + +void dng_read_image::ByteSwapBuffer (dng_host & /* host */, + dng_pixel_buffer &buffer) + { + + uint32 pixels = buffer.fRowStep * buffer.fArea.H (); + + switch (buffer.fPixelSize) + { + + case 2: + { + + DoSwapBytes16 ((uint16 *) buffer.fData, + pixels); + + break; + + } + + case 4: + { + + DoSwapBytes32 ((uint32 *) buffer.fData, + pixels); + + break; + + } + + default: + break; + + } + + } + +/*****************************************************************************/ + +void dng_read_image::DecodePredictor (dng_host & /* host */, + const dng_ifd &ifd, + dng_pixel_buffer &buffer) + { + + switch (ifd.fPredictor) + { + + case cpNullPredictor: + { + + return; + + } + + case cpHorizontalDifference: + case cpHorizontalDifferenceX2: + case cpHorizontalDifferenceX4: + { + + int32 xFactor = 1; + + if (ifd.fPredictor == cpHorizontalDifferenceX2) + { + xFactor = 2; + } + + else if (ifd.fPredictor == cpHorizontalDifferenceX4) + { + xFactor = 4; + } + + switch (buffer.fPixelType) + { + + case ttByte: + { + + DecodeDelta8 ((uint8 *) buffer.fData, + buffer.fArea.H (), + buffer.fArea.W () / xFactor, + buffer.fPlanes * xFactor); + + return; + + } + + case ttShort: + { + + DecodeDelta16 ((uint16 *) buffer.fData, + buffer.fArea.H (), + buffer.fArea.W () / xFactor, + buffer.fPlanes * xFactor); + + return; + + } + + case ttLong: + { + + DecodeDelta32 ((uint32 *) buffer.fData, + buffer.fArea.H (), + buffer.fArea.W () / xFactor, + buffer.fPlanes * xFactor); + + return; + + } + + default: + break; + + } + + break; + + } + + default: + break; + + } + + ThrowBadFormat (); + + } + +/*****************************************************************************/ + +void dng_read_image::ReadTile (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + uint32 tileByteCount, + AutoPtr &compressedBuffer, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer, + bool usingMultipleThreads) + { + + switch (ifd.fCompression) + { + + case ccLZW: + case ccDeflate: + case ccOldDeflate: + case ccPackBits: + { + + // Figure out uncompressed size. + + dng_safe_uint32 bytesPerSample = (ifd.fBitsPerSample [0] >> 3); + + dng_safe_uint32 sampleCount = (dng_safe_uint32 (planes) * + dng_safe_uint32 (tileArea.W ()) * + dng_safe_uint32 (tileArea.H ())); + + dng_safe_uint32 uncompressedSize = sampleCount * bytesPerSample; + + // Setup pixel buffer to hold uncompressed data. + + uint32 pixelType = ttUndefined; + + if (ifd.fSampleFormat [0] == sfFloatingPoint) + { + pixelType = ttFloat; + } + + else if (ifd.fBitsPerSample [0] == 8) + { + pixelType = ttByte; + } + + else if (ifd.fBitsPerSample [0] == 16) + { + pixelType = ttShort; + } + + else if (ifd.fBitsPerSample [0] == 32) + { + pixelType = ttLong; + } + + else + { + ThrowBadFormat (); + } + + dng_pixel_buffer buffer (tileArea, + plane, + planes, + pixelType, + pcInterleaved, + NULL); + + // Specify custom pixel size (e.g., ttFloat pixel type but + // pixel size may be 2 for stored 16-bit half-float). + + buffer.fPixelSize = bytesPerSample.Get (); + + dng_safe_uint32 bufferSize = uncompressedSize; + + // If we are using the floating point predictor, we need an extra + // buffer row. + + if (ifd.fPredictor == cpFloatingPoint || + ifd.fPredictor == cpFloatingPointX2 || + ifd.fPredictor == cpFloatingPointX4) + { + + bufferSize += (dng_safe_uint32 (dng_safe_int32 (buffer.fRowStep)) * + dng_safe_uint32 (buffer.fPixelSize)); + + } + + // If are processing less than full size floating point data, + // we need space to expand the data to full floating point size. + + if (buffer.fPixelType == ttFloat) + { + bufferSize = Max_uint32 (bufferSize.Get (), + (sampleCount * 4u).Get ()); + } + + // Sometimes with multi-threading and planar image using strips, + // we can process a small tile before a large tile on a thread. + // Simple fix is to just reallocate the buffer if it is too small. + + if (uncompressedBuffer.Get () && + uncompressedBuffer->LogicalSize () < bufferSize.Get ()) + { + + uncompressedBuffer.Reset (); + + } + + if (uncompressedBuffer.Get () == NULL) + { + + uncompressedBuffer.Reset (host.Allocate (bufferSize.Get ())); + + } + + buffer.fData = uncompressedBuffer->Buffer (); + + // If using floating point predictor, move buffer pointer to second row. + + if (ifd.fPredictor == cpFloatingPoint || + ifd.fPredictor == cpFloatingPointX2 || + ifd.fPredictor == cpFloatingPointX4) + { + + buffer.fData = (uint8 *) buffer.fData + + buffer.fRowStep * buffer.fPixelSize; + + } + + // Decompress the data. + + if (ifd.fCompression == ccLZW) + { + + dng_lzw_expander expander; + + if (!expander.Expand (compressedBuffer->Buffer_uint8 (), + (uint8 *) buffer.fData, + tileByteCount, + uncompressedSize.Get ())) + { + ThrowBadFormat (); + } + + } + + else if (ifd.fCompression == ccPackBits) + { + + dng_stream subStream (compressedBuffer->Buffer_uint8 (), + tileByteCount); + + if (!DecodePackBits (subStream, + (uint8 *) buffer.fData, + uncompressedSize.Get ())) + { + ThrowBadFormat (); + } + + } + + else + { + + uLongf dstLen = uncompressedSize.Get (); + + int err = uncompress ((Bytef *) buffer.fData, + &dstLen, + (const Bytef *) compressedBuffer->Buffer (), + tileByteCount); + + if (err != Z_OK) + { + + if (err == Z_MEM_ERROR) + { + ThrowMemoryFull (); + } + + else if (err == Z_DATA_ERROR) + { + // Most other TIFF readers do not fail for this error + // so we should not either, even if it means showing + // a corrupted image to the user. Watson #2530216 + // - tknoll 12/20/11 + } + + else + { + ThrowBadFormat (); + } + + } + + if (dstLen != uncompressedSize.Get ()) + { + ThrowBadFormat (); + } + + } + + // The floating point predictor is byte order independent. + + if (ifd.fPredictor == cpFloatingPoint || + ifd.fPredictor == cpFloatingPointX2 || + ifd.fPredictor == cpFloatingPointX4) + { + + int32 xFactor = 1; + + if (ifd.fPredictor == cpFloatingPointX2) + { + xFactor = 2; + } + + else if (ifd.fPredictor == cpFloatingPointX4) + { + xFactor = 4; + } + + for (int32 row = tileArea.t; row < tileArea.b; row++) + { + + uint8 *srcPtr = (uint8 *) buffer.DirtyPixel (row , tileArea.l, plane); + uint8 *dstPtr = (uint8 *) buffer.DirtyPixel (row - 1, tileArea.l, plane); + + DecodeFPDelta (srcPtr, + dstPtr, + tileArea.W () / xFactor, + planes * xFactor, + bytesPerSample.Get ()); + + } + + buffer.fData = (uint8 *) buffer.fData - + buffer.fRowStep * buffer.fPixelSize; + + } + + else + { + + // Both these compression algorithms are byte based. + + if (stream.SwapBytes ()) + { + + ByteSwapBuffer (host, + buffer); + + } + + // Undo the predictor. + + DecodePredictor (host, + ifd, + buffer); + + } + + // Expand floating point data, if needed. + + if (buffer.fPixelType == ttFloat && buffer.fPixelSize == 2) + { + + uint16 *srcPtr = (uint16 *) buffer.fData; + uint32 *dstPtr = (uint32 *) buffer.fData; + + for (int32 index = sampleCount.Get () - 1; index >= 0; index--) + { + + dstPtr [index] = DNG_HalfToFloat (srcPtr [index]); + + } + + buffer.fPixelSize = 4; + + } + + else if (buffer.fPixelType == ttFloat && buffer.fPixelSize == 3) + { + + uint8 *srcPtr = ((uint8 *) buffer.fData) + (sampleCount.Get () - 1) * 3; + uint32 *dstPtr = ((uint32 *) buffer.fData) + (sampleCount.Get () - 1); + + if (stream.BigEndian () || ifd.fPredictor == cpFloatingPoint || + ifd.fPredictor == cpFloatingPointX2 || + ifd.fPredictor == cpFloatingPointX4) + { + + for (uint32 index = 0; index < sampleCount.Get (); index++) + { + + *(dstPtr--) = DNG_FP24ToFloat (srcPtr); + + srcPtr -= 3; + + } + + } + + else + { + + for (uint32 index = 0; index < sampleCount.Get (); index++) + { + + uint8 input [3]; + + input [2] = srcPtr [0]; + input [1] = srcPtr [1]; + input [0] = srcPtr [2]; + + *(dstPtr--) = DNG_FP24ToFloat (input); + + srcPtr -= 3; + + } + + } + + buffer.fPixelSize = 4; + + } + + // Save the data. + + image.Put (buffer); + + return; + + } + + case ccUncompressed: + { + + if (ReadUncompressed (host, + ifd, + stream, + image, + tileArea, + plane, + planes, + uncompressedBuffer, + subTileBlockBuffer)) + { + + return; + + } + + break; + + } + + case ccJPEG: + { + + if (ifd.IsBaselineJPEG ()) + { + + // Baseline JPEG. + + if (ReadBaselineJPEG (host, + ifd, + stream, + image, + tileArea, + plane, + planes, + tileByteCount, + compressedBuffer.Get () ? compressedBuffer->Buffer_uint8 () : NULL, + usingMultipleThreads)) + { + + return; + + } + + } + + else + { + + // Otherwise is should be lossless JPEG. + + if (ReadLosslessJPEG (host, + ifd, + stream, + image, + tileArea, + plane, + planes, + tileByteCount, + uncompressedBuffer, + subTileBlockBuffer)) + { + + return; + + } + + } + + break; + + } + + case ccLossyJPEG: + { + + if (ReadBaselineJPEG (host, + ifd, + stream, + image, + tileArea, + plane, + planes, + tileByteCount, + compressedBuffer.Get () ? compressedBuffer->Buffer_uint8 () : NULL, + usingMultipleThreads)) + { + + return; + + } + + break; + + } + + default: + break; + + } + + ThrowBadFormat (); + + } + +/*****************************************************************************/ + +bool dng_read_image::CanRead (const dng_ifd &ifd) + { + + if (ifd.fImageWidth < 1 || + ifd.fImageLength < 1) + { + return false; + } + + if (ifd.fSamplesPerPixel < 1) + { + return false; + } + + if (ifd.fBitsPerSample [0] < 1) + { + return false; + } + + for (uint32 j = 1; j < Min_uint32 (ifd.fSamplesPerPixel, + kMaxSamplesPerPixel); j++) + { + + if (ifd.fBitsPerSample [j] != + ifd.fBitsPerSample [0]) + { + return false; + } + + if (ifd.fSampleFormat [j] != + ifd.fSampleFormat [0]) + { + return false; + } + + } + + if ((ifd.fPlanarConfiguration != pcInterleaved ) && + (ifd.fPlanarConfiguration != pcPlanar ) && + (ifd.fPlanarConfiguration != pcRowInterleaved)) + { + return false; + } + + if (ifd.fUsesStrips == ifd.fUsesTiles) + { + return false; + } + + uint32 tileCount = ifd.TilesPerImage (); + + if (tileCount < 1) + { + return false; + } + + bool needTileByteCounts = (ifd.TileByteCount (ifd.TileArea (0, 0)) == 0); + + if (tileCount == 1) + { + + if (needTileByteCounts) + { + + if (ifd.fTileByteCount [0] < 1) + { + return false; + } + + } + + } + + else + { + + if (ifd.fTileOffsetsCount != tileCount) + { + return false; + } + + if (needTileByteCounts) + { + + if (ifd.fTileByteCountsCount != tileCount) + { + return false; + } + + } + + } + + if (!CanReadTile (ifd)) + { + return false; + } + + return true; + + } + +/*****************************************************************************/ + +dng_read_tiles_task::dng_read_tiles_task (dng_read_image &readImage, + dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + dng_jpeg_image *jpegImage, + dng_fingerprint *jpegTileDigest, + uint32 outerSamples, + uint32 innerSamples, + uint32 tilesDown, + uint32 tilesAcross, + uint64 *tileOffset, + uint32 *tileByteCount, + uint32 compressedSize, + uint32 uncompressedSize) + + : dng_area_task ("dng_read_tiles_task") + + , fReadImage (readImage) + , fHost (host) + , fIFD (ifd) + , fStream (stream) + , fImage (image) + , fJPEGImage (jpegImage) + , fJPEGTileDigest (jpegTileDigest) + , fOuterSamples (outerSamples) + , fInnerSamples (innerSamples) + , fTilesDown (tilesDown) + , fTilesAcross (tilesAcross) + , fTileOffset (tileOffset) + , fTileByteCount (tileByteCount) + , fCompressedSize (compressedSize) + , fUncompressedSize (uncompressedSize) + , fMutex ("dng_read_tiles_task") + , fNextTileIndex (0) + + { + + fMinTaskArea = 16 * 16; + fUnitCell = dng_point (16, 16); + fMaxTileSize = dng_point (16, 16); + + } + +/*****************************************************************************/ + +void dng_read_tiles_task::Process (uint32 /* threadIndex */, + const dng_rect & /* tile */, + dng_abort_sniffer *sniffer) + { + + AutoPtr compressedBuffer; + AutoPtr uncompressedBuffer; + AutoPtr subTileBlockBuffer; + + if (!fJPEGImage) + { + compressedBuffer.Reset (fHost.Allocate (fCompressedSize)); + } + + if (fUncompressedSize) + { + uncompressedBuffer.Reset (fHost.Allocate (fUncompressedSize)); + } + + while (true) + { + + uint32 tileIndex; + uint32 byteCount; + + { + + dng_lock_mutex lock (&fMutex); + + if (fNextTileIndex == fOuterSamples * fTilesDown * fTilesAcross) + { + return; + } + + tileIndex = fNextTileIndex++; + + ReadTask (tileIndex, + byteCount, + compressedBuffer.Get ()); + + } + + ProcessTask (tileIndex, + byteCount, + sniffer, + compressedBuffer, + uncompressedBuffer, + subTileBlockBuffer); + + } + + } + +/*****************************************************************************/ + +void dng_read_tiles_task::ReadTask (uint32 tileIndex, + uint32 &byteCount, + dng_memory_block *compressedBuffer) + { + + TempStreamSniffer noSniffer (fStream, NULL); + + fStream.SetReadPosition (fTileOffset [tileIndex]); + + byteCount = fTileByteCount [tileIndex]; + + if (fJPEGImage) + { + + fJPEGImage->fJPEGData [tileIndex] . Reset (fHost.Allocate (byteCount)); + + } + + fStream.Get (fJPEGImage ? fJPEGImage->fJPEGData [tileIndex]->Buffer () + : compressedBuffer->Buffer (), + byteCount); + + } + +/*****************************************************************************/ + +void dng_read_tiles_task::ProcessTask (uint32 tileIndex, + uint32 byteCount, + dng_abort_sniffer *sniffer, + AutoPtr &compressedBuffer, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer) + { + + dng_abort_sniffer::SniffForAbort (sniffer); + + if (fJPEGTileDigest) + { + + dng_md5_printer printer; + + printer.Process (compressedBuffer->Buffer (), + byteCount); + + fJPEGTileDigest [tileIndex] = printer.Result (); + + } + + dng_stream tileStream (fJPEGImage ? fJPEGImage->fJPEGData [tileIndex]->Buffer () + : compressedBuffer->Buffer (), + byteCount); + + tileStream.SetLittleEndian (fStream.LittleEndian ()); + + uint32 plane = tileIndex / (fTilesDown * fTilesAcross); + + uint32 rowIndex = (tileIndex - plane * fTilesDown * fTilesAcross) / fTilesAcross; + + uint32 colIndex = tileIndex - (plane * fTilesDown + rowIndex) * fTilesAcross; + + dng_rect tileArea = fIFD.TileArea (rowIndex, colIndex); + + dng_host host (&fHost.Allocator (), + sniffer); // Cannot use sniffer attached to main host + + fReadImage.ReadTile (host, + fIFD, + tileStream, + fImage, + tileArea, + plane, + fInnerSamples, + byteCount, + fJPEGImage ? fJPEGImage->fJPEGData [tileIndex] + : compressedBuffer, + uncompressedBuffer, + subTileBlockBuffer, + true); + + } + +/*****************************************************************************/ + +void dng_read_image::Read (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + dng_jpeg_image *jpegImage, + dng_fingerprint *jpegDigest) + { + + // Reject images that are too big. + + // Allow up to 2 * kMaxImageSide to deal with intermediate image objects + // (e.g., rotated and padded). + + static const uint32 kLimit = 2 * kMaxImageSide; + + if (ifd.fImageWidth > kLimit || + ifd.fImageLength > kLimit) + { + + ThrowBadFormat ("dng_read_image::Read image too large"); + + } + + uint32 tileIndex; + + // Deal with row interleaved images. + + if (ifd.fRowInterleaveFactor > 1 && + ifd.fRowInterleaveFactor < ifd.fImageLength) + { + + dng_ifd tempIFD (ifd); + + tempIFD.fRowInterleaveFactor = 1; + + dng_row_interleaved_image tempImage (image, + ifd.fRowInterleaveFactor); + + Read (host, + tempIFD, + stream, + tempImage, + jpegImage, + jpegDigest); + + return; + + } + + // Figure out inner and outer samples. + + uint32 innerSamples = 1; + uint32 outerSamples = 1; + + if (ifd.fPlanarConfiguration == pcPlanar) + { + outerSamples = ifd.fSamplesPerPixel; + } + else + { + innerSamples = ifd.fSamplesPerPixel; + } + + // Calculate number of tiles to read. + + uint32 tilesAcross = ifd.TilesAcross (); + uint32 tilesDown = ifd.TilesDown (); + + uint32 tileCount = SafeUint32Mult (tilesAcross, tilesDown, outerSamples); + + // Find the tile offsets. + + dng_memory_data tileOffsetData (tileCount, sizeof (uint64)); + + uint64 *tileOffset = tileOffsetData.Buffer_uint64 (); + + if (tileCount <= dng_ifd::kMaxTileInfo) + { + + for (tileIndex = 0; tileIndex < tileCount; tileIndex++) + { + + tileOffset [tileIndex] = ifd.fTileOffset [tileIndex]; + + } + + } + + else + { + + stream.SetReadPosition (ifd.fTileOffsetsOffset); + + for (tileIndex = 0; tileIndex < tileCount; tileIndex++) + { + + tileOffset [tileIndex] = stream.TagValue_uint32 (ifd.fTileOffsetsType); + + } + + } + + // Quick validity check on tile offsets. + + for (tileIndex = 0; tileIndex < tileCount; tileIndex++) + { + + #if qDNGValidate + + if (tileOffset [tileIndex] < 8) + { + + ReportWarning ("Tile/Strip offset less than 8"); + + } + + #endif + + if (tileOffset [tileIndex] >= stream.Length ()) + { + + ThrowBadFormat (); + + } + + } + + // Buffer to hold the tile byte counts, if needed. + + dng_memory_data tileByteCountData; + + uint32 *tileByteCount = NULL; + + // If we can compute the number of bytes needed to store the + // data, we can split the read for each tile into sub-tiles. + + uint32 uncompressedSize = 0; + + uint32 subTileLength = ifd.fTileLength; + + if (ifd.TileByteCount (ifd.TileArea (0, 0)) != 0) + { + + uint32 bytesPerPixel = TagTypeSize (ifd.PixelType ()); + + uint32 bytesPerRow = SafeUint32Mult (ifd.fTileWidth, + innerSamples, + bytesPerPixel); + + subTileLength = Pin_uint32 (ifd.fSubTileBlockRows, + kImageBufferSize / bytesPerRow, + ifd.fTileLength); + + subTileLength = subTileLength / ifd.fSubTileBlockRows + * ifd.fSubTileBlockRows; + + uncompressedSize = SafeUint32Mult (subTileLength, bytesPerRow); + + } + + // Else we need to know the byte counts. + + else + { + + tileByteCountData.Allocate (tileCount, sizeof (uint32)); + + tileByteCount = tileByteCountData.Buffer_uint32 (); + + if (tileCount <= dng_ifd::kMaxTileInfo) + { + + for (tileIndex = 0; tileIndex < tileCount; tileIndex++) + { + + tileByteCount [tileIndex] = ifd.fTileByteCount [tileIndex]; + + } + + } + + else + { + + stream.SetReadPosition (ifd.fTileByteCountsOffset); + + for (tileIndex = 0; tileIndex < tileCount; tileIndex++) + { + + tileByteCount [tileIndex] = stream.TagValue_uint32 (ifd.fTileByteCountsType); + + } + + } + + // Quick validity check on tile byte counts. + + for (tileIndex = 0; tileIndex < tileCount; tileIndex++) + { + + if (tileByteCount [tileIndex] < 1 || + tileByteCount [tileIndex] > stream.Length ()) + { + + ThrowBadFormat (); + + } + + } + + } + + // Find maximum tile size, if possible. + + uint32 maxTileByteCount = 0; + + if (tileByteCount) + { + + for (tileIndex = 0; tileIndex < tileCount; tileIndex++) + { + + maxTileByteCount = Max_uint32 (maxTileByteCount, + tileByteCount [tileIndex]); + + } + + } + + // Do we need a compressed data buffer? + + uint32 compressedSize = 0; + + bool needsCompressedBuffer = NeedsCompressedBuffer (ifd); + + if (needsCompressedBuffer) + { + + if (!tileByteCount) + { + ThrowBadFormat (); + } + + compressedSize = maxTileByteCount; + + } + + // Are we keeping the compressed JPEG image data? + + if (jpegImage) + { + + if (ifd.IsBaselineJPEG ()) + { + + jpegImage->fImageSize.h = ifd.fImageWidth; + jpegImage->fImageSize.v = ifd.fImageLength; + + jpegImage->fTileSize.h = ifd.fTileWidth; + jpegImage->fTileSize.v = ifd.fTileLength; + + jpegImage->fUsesStrips = ifd.fUsesStrips; + + jpegImage->fJPEGData.Reset (new dng_jpeg_image_tile_ptr [tileCount]); + + } + + else + { + + jpegImage = NULL; + + } + + } + + // Do we need to read the JPEG tables? + + if (ifd.fJPEGTablesOffset && ifd.fJPEGTablesCount) + { + + if (ifd.IsBaselineJPEG ()) + { + + fJPEGTables.Reset (host.Allocate (ifd.fJPEGTablesCount)); + + stream.SetReadPosition (ifd.fJPEGTablesOffset); + + stream.Get (fJPEGTables->Buffer (), + fJPEGTables->LogicalSize ()); + + } + + } + + AutoArray jpegTileDigest; + + if (jpegDigest) + { + + jpegTileDigest.Reset (new dng_fingerprint + [SafeUint32Add (tileCount, (fJPEGTables.Get () ? 1 : 0))]); + + } + + // Don't read planes we are not actually saving. + + outerSamples = Min_uint32 (image.Planes (), outerSamples); + + // Performance optimization. We are reading in a potentially large image, which + // is usually in a fairly contiguous byte range in the file. See if it makes sense + // to increase the stream buffer size for this operation. + + uint64 contiguousByteCount = 0; + + { + + bool tilesInOrder = true; + + uint64 totalTileBytes = 0; + + uint64 minFileOffset = tileOffset [0]; + uint64 maxFileOffset = tileOffset [0]; + + tileIndex = 0; + + for (uint32 plane = 0; plane < outerSamples; plane++) + { + + for (uint32 rowIndex = 0; rowIndex < tilesDown; rowIndex++) + { + + for (uint32 colIndex = 0; colIndex < tilesAcross; colIndex++) + { + + uint64 thisOffset = tileOffset [tileIndex]; + + uint64 thisByteCount; + + if (tileByteCount) + { + + thisByteCount = tileByteCount [tileIndex]; + + } + + else + { + + thisByteCount = ifd.TileByteCount (ifd.TileArea (rowIndex, colIndex)); + + } + + if (thisOffset < maxFileOffset) + { + tilesInOrder = false; + } + + totalTileBytes += thisByteCount; + + minFileOffset = Min_uint64 (minFileOffset, thisOffset); + maxFileOffset = Max_uint64 (maxFileOffset, thisOffset + thisByteCount); + + tileIndex++; + + } + + } + + } + + // Quick check for enough data in file. + + if (maxFileOffset > stream.Length ()) + { + + ThrowBadFormat (); + + } + + // Are all the tiles in order? + + if (tilesInOrder) + { + + // And are going to read at least 90% of the bytes in the range? + + uint64 totalFileBytes = maxFileOffset - minFileOffset; + + if (totalTileBytes >= totalFileBytes * 9 / 10) + { + + contiguousByteCount = totalFileBytes; + + } + + } + + } + + dng_stream_contiguous_read_hint readHint (stream, + host.Allocator (), + tileOffset [0], + contiguousByteCount); + + // See if we can do this read using multiple threads. + + bool useMultipleThreads = (outerSamples * tilesDown * tilesAcross >= 2) && + (host.PerformAreaTaskThreads () > 1) && + (maxTileByteCount > 0 && maxTileByteCount <= 1024 * 1024) && + (subTileLength == ifd.fTileLength) && + (ifd.fCompression != ccUncompressed); + + if (useMultipleThreads) + { + + DoReadTiles (host, + ifd, + stream, + image, + jpegImage, + jpegTileDigest.Get (), + outerSamples, + innerSamples, + tilesDown, + tilesAcross, + tileOffset, + tileByteCount, + maxTileByteCount, + uncompressedSize); + + } + + // Else use a single thread to read all the tiles. + + else + { + + AutoPtr compressedBuffer; + AutoPtr uncompressedBuffer; + AutoPtr subTileBlockBuffer; + + if (uncompressedSize) + { + uncompressedBuffer.Reset (host.Allocate (uncompressedSize)); + } + + if (compressedSize && !jpegImage) + { + compressedBuffer.Reset (host.Allocate (compressedSize)); + } + + else if (jpegDigest) + { + compressedBuffer.Reset (host.Allocate (maxTileByteCount)); + } + + tileIndex = 0; + + for (uint32 plane = 0; plane < outerSamples; plane++) + { + + for (uint32 rowIndex = 0; rowIndex < tilesDown; rowIndex++) + { + + for (uint32 colIndex = 0; colIndex < tilesAcross; colIndex++) + { + + stream.SetReadPosition (tileOffset [tileIndex]); + + dng_rect tileArea = ifd.TileArea (rowIndex, colIndex); + + uint32 subTileCount = (tileArea.H () + subTileLength - 1) / + subTileLength; + + for (uint32 subIndex = 0; subIndex < subTileCount; subIndex++) + { + + host.SniffForAbort (); + + dng_rect subArea (tileArea); + + subArea.t = tileArea.t + subIndex * subTileLength; + + subArea.b = Min_int32 (subArea.t + subTileLength, + tileArea.b); + + uint32 subByteCount; + + if (tileByteCount) + { + subByteCount = tileByteCount [tileIndex]; + } + else + { + subByteCount = ifd.TileByteCount (subArea); + } + + if (jpegImage) + { + + jpegImage->fJPEGData [tileIndex].Reset (host.Allocate (subByteCount)); + + stream.Get (jpegImage->fJPEGData [tileIndex]->Buffer (), subByteCount); + + stream.SetReadPosition (tileOffset [tileIndex]); + + } + + else if ((needsCompressedBuffer || jpegDigest) && subByteCount) + { + + stream.Get (compressedBuffer->Buffer (), subByteCount); + + if (jpegDigest) + { + + dng_md5_printer printer; + + printer.Process (compressedBuffer->Buffer (), + subByteCount); + + jpegTileDigest [tileIndex] = printer.Result (); + + } + + } + + ReadTile (host, + ifd, + stream, + image, + subArea, + plane, + innerSamples, + subByteCount, + jpegImage ? jpegImage->fJPEGData [tileIndex] : compressedBuffer, + uncompressedBuffer, + subTileBlockBuffer, + useMultipleThreads); + + } + + tileIndex++; + + } + + } + + } + + } + + // Finish up JPEG digest computation, if needed. + + if (jpegDigest) + { + + if (fJPEGTables.Get ()) + { + + dng_md5_printer printer; + + printer.Process (fJPEGTables->Buffer (), + fJPEGTables->LogicalSize ()); + + jpegTileDigest [tileCount] = printer.Result (); + + } + + dng_md5_printer printer2; + + for (uint32 j = 0; j < tileCount + (fJPEGTables.Get () ? 1 : 0); j++) + { + + printer2.Process (jpegTileDigest [j].data, + dng_fingerprint::kDNGFingerprintSize); + + } + + *jpegDigest = printer2.Result (); + + } + + // Keep the JPEG table in the jpeg image, if any. + + if (jpegImage) + { + + jpegImage->fJPEGTables.Reset (fJPEGTables.Release ()); + + } + + } + +/*****************************************************************************/ + +void dng_read_image::DoReadTiles (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + dng_jpeg_image *jpegImage, + dng_fingerprint *jpegTileDigest, + uint32 outerSamples, + uint32 innerSamples, + uint32 tilesDown, + uint32 tilesAcross, + uint64 *tileOffset, + uint32 *tileByteCount, + uint32 compressedSize, + uint32 uncompressedSize) + { + + uint32 threadCount = Min_uint32 (outerSamples * tilesDown * tilesAcross, + host.PerformAreaTaskThreads ()); + + dng_read_tiles_task task (*this, + host, + ifd, + stream, + image, + jpegImage, + jpegTileDigest, + outerSamples, + innerSamples, + tilesDown, + tilesAcross, + tileOffset, + tileByteCount, + compressedSize, + uncompressedSize); + + host.PerformAreaTask (task, + dng_rect (0, 0, 16, 16 * threadCount)); + + } + +/*****************************************************************************/ diff --git a/dng/dng_read_image.h b/dng/dng_read_image.h new file mode 100644 index 0000000..af8a603 --- /dev/null +++ b/dng/dng_read_image.h @@ -0,0 +1,263 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Support for DNG image reading. + */ + +/*****************************************************************************/ + +#ifndef __dng_read_image__ +#define __dng_read_image__ + +/*****************************************************************************/ + +#include "dng_area_task.h" +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_image.h" +#include "dng_memory.h" +#include "dng_mutex.h" +#include "dng_types.h" + +/******************************************************************************/ + +bool DecodePackBits (dng_stream &stream, + uint8 *dPtr, + int32 dstCount); + +/*****************************************************************************/ + +class dng_row_interleaved_image: public dng_image + { + + private: + + dng_image &fImage; + + uint32 fFactor; + + public: + + dng_row_interleaved_image (dng_image &image, + uint32 factor); + + virtual void DoGet (dng_pixel_buffer &buffer) const; + + virtual void DoPut (const dng_pixel_buffer &buffer); + + private: + + int32 MapRow (int32 row) const; + + }; + +/*****************************************************************************/ + +class dng_read_image + { + + friend class dng_read_tiles_task; + + protected: + + enum + { + + // Target size for buffer used to copy data to the image. + + kImageBufferSize = 128 * 1024 + + }; + + AutoPtr fJPEGTables; + + public: + + dng_read_image (); + + virtual ~dng_read_image (); + + virtual bool CanRead (const dng_ifd &ifd); + + virtual void Read (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + dng_jpeg_image *jpegImage, + dng_fingerprint *jpegDigest); + + protected: + + virtual bool ReadUncompressed (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer); + + virtual void DecodeLossyJPEG (dng_host &host, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + uint32 photometricInterpretation, + uint32 jpegDataSize, + uint8 *jpegDataInMemory, + bool usingMultipleThreads); + + virtual bool ReadBaselineJPEG (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + uint32 tileByteCount, + uint8 *jpegDataInMemory, + bool usingMultipleThreads); + + virtual bool ReadLosslessJPEG (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + uint32 tileByteCount, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer); + + virtual bool CanReadTile (const dng_ifd &ifd); + + virtual bool NeedsCompressedBuffer (const dng_ifd &ifd); + + virtual void ByteSwapBuffer (dng_host &host, + dng_pixel_buffer &buffer); + + virtual void DecodePredictor (dng_host &host, + const dng_ifd &ifd, + dng_pixel_buffer &buffer); + + virtual void ReadTile (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + uint32 tileByteCount, + AutoPtr &compressedBuffer, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer, + bool usingMultipleThreads); + + virtual void DoReadTiles (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + dng_jpeg_image *jpegImage, + dng_fingerprint *jpegTileDigest, + uint32 outerSamples, + uint32 innerSamples, + uint32 tilesDown, + uint32 tilesAcross, + uint64 *tileOffset, + uint32 *tileByteCount, + uint32 compressedSize, + uint32 uncompressedSize); + + }; + +/*****************************************************************************/ + +class dng_read_tiles_task : public dng_area_task, + private dng_uncopyable + { + + protected: + + dng_read_image &fReadImage; + + dng_host &fHost; + + const dng_ifd &fIFD; + + dng_stream &fStream; + + dng_image &fImage; + + dng_jpeg_image *fJPEGImage; + + dng_fingerprint *fJPEGTileDigest; + + uint32 fOuterSamples; + + uint32 fInnerSamples; + + uint32 fTilesDown; + + uint32 fTilesAcross; + + uint64 *fTileOffset; + + uint32 *fTileByteCount; + + uint32 fCompressedSize; + + uint32 fUncompressedSize; + + dng_mutex fMutex; + + uint32 fNextTileIndex; + + public: + + dng_read_tiles_task (dng_read_image &readImage, + dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + dng_jpeg_image *jpegImage, + dng_fingerprint *jpegTileDigest, + uint32 outerSamples, + uint32 innerSamples, + uint32 tilesDown, + uint32 tilesAcross, + uint64 *tileOffset, + uint32 *tileByteCount, + uint32 compressedSize, + uint32 uncompressedSize); + + void Process (uint32 threadIndex, + const dng_rect &tile, + dng_abort_sniffer *sniffer); + + protected: + + void ReadTask (uint32 tileIndex, + uint32 &byteCount, + dng_memory_block *compressedBuffer); + + void ProcessTask (uint32 tileIndex, + uint32 byteCount, + dng_abort_sniffer *sniffer, + AutoPtr &compressedBuffer, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_rect.cpp b/dng/dng_rect.cpp new file mode 100644 index 0000000..18c44ae --- /dev/null +++ b/dng/dng_rect.cpp @@ -0,0 +1,182 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_rect.h" + +#include "dng_utils.h" + +/*****************************************************************************/ + +bool dng_rect::operator== (const dng_rect &rect) const + { + + return (rect.t == t) && + (rect.l == l) && + (rect.b == b) && + (rect.r == r); + + } + +/*****************************************************************************/ + +bool dng_rect::IsZero () const + { + + return (t == 0) && (l == 0) && (b == 0) && (r == 0); + + } + +/*****************************************************************************/ + +bool dng_rect_real64::operator== (const dng_rect_real64 &rect) const + { + + return (rect.t == t) && + (rect.l == l) && + (rect.b == b) && + (rect.r == r); + + } + +/*****************************************************************************/ + +bool dng_rect_real64::IsZero () const + { + + return (t == 0.0) && (l == 0.0) && (b == 0.0) && (r == 0.0); + + } + +/*****************************************************************************/ + +dng_rect operator& (const dng_rect &a, + const dng_rect &b) + { + + dng_rect c; + + c.t = Max_int32 (a.t, b.t); + c.l = Max_int32 (a.l, b.l); + + c.b = Min_int32 (a.b, b.b); + c.r = Min_int32 (a.r, b.r); + + if (c.IsEmpty ()) + { + + c = dng_rect (); + + } + + return c; + + } + +/*****************************************************************************/ + +dng_rect operator| (const dng_rect &a, + const dng_rect &b) + { + + if (a.IsEmpty ()) + { + return b; + } + + if (b.IsEmpty ()) + { + return a; + } + + dng_rect c; + + c.t = Min_int32 (a.t, b.t); + c.l = Min_int32 (a.l, b.l); + + c.b = Max_int32 (a.b, b.b); + c.r = Max_int32 (a.r, b.r); + + return c; + + } + +/*****************************************************************************/ + +dng_rect_real64 operator& (const dng_rect_real64 &a, + const dng_rect_real64 &b) + { + + dng_rect_real64 c; + + c.t = Max_real64 (a.t, b.t); + c.l = Max_real64 (a.l, b.l); + + c.b = Min_real64 (a.b, b.b); + c.r = Min_real64 (a.r, b.r); + + if (c.IsEmpty ()) + { + + c = dng_rect_real64 (); + + } + + return c; + + } + +/*****************************************************************************/ + +dng_rect_real64 operator| (const dng_rect_real64 &a, + const dng_rect_real64 &b) + { + + if (a.IsEmpty ()) + { + return b; + } + + if (b.IsEmpty ()) + { + return a; + } + + dng_rect_real64 c; + + c.t = Min_real64 (a.t, b.t); + c.l = Min_real64 (a.l, b.l); + + c.b = Max_real64 (a.b, b.b); + c.r = Max_real64 (a.r, b.r); + + return c; + + } + +/*****************************************************************************/ + +dng_rect_real64 Bounds (const dng_point_real64 &a, + const dng_point_real64 &b, + const dng_point_real64 &c, + const dng_point_real64 &d) + { + + real64 xMin = Min_real64 (a.h, Min_real64 (b.h, Min_real64 (c.h, d.h))); + real64 xMax = Max_real64 (a.h, Max_real64 (b.h, Max_real64 (c.h, d.h))); + + real64 yMin = Min_real64 (a.v, Min_real64 (b.v, Min_real64 (c.v, d.v))); + real64 yMax = Max_real64 (a.v, Max_real64 (b.v, Max_real64 (c.v, d.v))); + + return dng_rect_real64 (yMin, + xMin, + yMax, + xMax); + + } + +/*****************************************************************************/ diff --git a/dng/dng_rect.h b/dng/dng_rect.h new file mode 100644 index 0000000..a986742 --- /dev/null +++ b/dng/dng_rect.h @@ -0,0 +1,626 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef __dng_rect__ +#define __dng_rect__ + +/*****************************************************************************/ + +#include "dng_exceptions.h" +#include "dng_point.h" +#include "dng_safe_arithmetic.h" +#include "dng_types.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +class dng_rect + { + + public: + + int32 t; + int32 l; + int32 b; + int32 r; + + public: + + dng_rect () + : t (0) + , l (0) + , b (0) + , r (0) + { + } + + // Constructs a dng_rect from the top-left and bottom-right corner. + // Throws an exception if the resulting height or width are too large + // to be represented as an int32. The intent of this is to protect + // code that may be computing the height or width directly from the + // member variables (instead of going through H() or W()). + + dng_rect (int32 tt, int32 ll, int32 bb, int32 rr) + : t (tt) + , l (ll) + , b (bb) + , r (rr) + { + + int32 dummy; + + if (!SafeInt32Sub (r, l, &dummy) || + !SafeInt32Sub (b, t, &dummy)) + { + ThrowProgramError ("Overflow in dng_rect constructor"); + } + + } + + dng_rect (uint32 h, uint32 w) + : t (0) + , l (0) + { + + if (!ConvertUint32ToInt32 (h, &b) || + !ConvertUint32ToInt32 (w, &r)) + { + ThrowProgramError ("Overflow in dng_rect constructor"); + } + + } + + dng_rect (const dng_point &size) + : t (0) + , l (0) + , b (size.v) + , r (size.h) + { + } + + void Clear () + { + *this = dng_rect (); + } + + bool operator== (const dng_rect &rect) const; + + bool operator!= (const dng_rect &rect) const + { + return !(*this == rect); + } + + bool IsZero () const; + + bool NotZero () const + { + return !IsZero (); + } + + bool IsEmpty () const + { + return (t >= b) || (l >= r); + } + + bool NotEmpty () const + { + return !IsEmpty (); + } + + // Returns the width of the rectangle, or 0 if r is smaller than l. + // Throws an exception if the width is too large to be represented as + // a _signed_ int32 (even if it would fit in a uint32). This is + // consciously conservative -- there are existing uses of W() where + // the result is converted to an int32 without an overflow check, and + // we want to make sure no overflow can occur in such cases. We + // provide this check in addition to the check performed in the + // "two-corners" constructor to protect client code that produes a + // dng_rect with excessive size by initializing or modifying the + // member variables directly. + + uint32 W () const + { + + if (r >= l) + { + + int32 width; + + if (!SafeInt32Sub (r, l, &width)) + { + ThrowProgramError ("Overflow computing rectangle width"); + } + + return static_cast (width); + + } + + else + { + return 0; + } + + } + + // Returns the height of the rectangle, or 0 if b is smaller than t. + // Throws an exception if the height is too large to be represented as + // a _signed_ int32 (see W() for rationale). + + uint32 H () const + { + + if (b >= t) + { + + int32 height; + + if (!SafeInt32Sub (b, t, &height)) + { + ThrowProgramError ("Overflow computing rectangle height"); + } + + return static_cast (height); + + } + + else + { + return 0; + } + + } + + dng_point TL () const + { + return dng_point (t, l); + } + + dng_point TR () const + { + return dng_point (t, r); + } + + dng_point BL () const + { + return dng_point (b, l); + } + + dng_point BR () const + { + return dng_point (b, r); + } + + dng_point Size () const + { + return dng_point ((int32) H (), (int32) W ()); + } + + uint32 LongSide () const + { + return Max_uint32 (W (), H ()); + } + + uint32 ShortSide () const + { + return Min_uint32 (W (), H ()); + } + + real64 Diagonal () const + { + return hypot ((real64) W (), + (real64) H ()); + } + + }; + +/*****************************************************************************/ + +class dng_rect_real64 + { + + public: + + real64 t; + real64 l; + real64 b; + real64 r; + + public: + + dng_rect_real64 () + : t (0.0) + , l (0.0) + , b (0.0) + , r (0.0) + { + } + + dng_rect_real64 (real64 tt, real64 ll, real64 bb, real64 rr) + : t (tt) + , l (ll) + , b (bb) + , r (rr) + { + } + + dng_rect_real64 (real64 h, real64 w) + : t (0) + , l (0) + , b (h) + , r (w) + { + } + + dng_rect_real64 (const dng_point_real64 &size) + : t (0) + , l (0) + , b (size.v) + , r (size.h) + { + } + + dng_rect_real64 (const dng_point_real64 &pt1, + const dng_point_real64 &pt2) + : t (Min_real64 (pt1.v, pt2.v)) + , l (Min_real64 (pt1.h, pt2.h)) + , b (Max_real64 (pt1.v, pt2.v)) + , r (Max_real64 (pt1.h, pt2.h)) + { + } + + dng_rect_real64 (const dng_rect &rect) + : t ((real64) rect.t) + , l ((real64) rect.l) + , b ((real64) rect.b) + , r ((real64) rect.r) + { + } + + void Clear () + { + *this = dng_point_real64 (); + } + + bool operator== (const dng_rect_real64 &rect) const; + + bool operator!= (const dng_rect_real64 &rect) const + { + return !(*this == rect); + } + + bool IsZero () const; + + bool NotZero () const + { + return !IsZero (); + } + + bool IsEmpty () const + { + return (t >= b) || (l >= r); + } + + bool NotEmpty () const + { + return !IsEmpty (); + } + + real64 W () const + { + return Max_real64 (r - l, 0.0); + } + + real64 H () const + { + return Max_real64 (b - t, 0.0); + } + + dng_point_real64 TL () const + { + return dng_point_real64 (t, l); + } + + dng_point_real64 TR () const + { + return dng_point_real64 (t, r); + } + + dng_point_real64 BL () const + { + return dng_point_real64 (b, l); + } + + dng_point_real64 BR () const + { + return dng_point_real64 (b, r); + } + + dng_point_real64 Size () const + { + return dng_point_real64 (H (), W ()); + } + + dng_rect Round () const + { + return dng_rect (Round_int32 (t), + Round_int32 (l), + Round_int32 (b), + Round_int32 (r)); + } + + real64 LongSide () const + { + return Max_real64 (W (), H ()); + } + + real64 ShortSide () const + { + return Min_real64 (W (), H ()); + } + + real64 Diagonal () const + { + return hypot (W (), H ()); + } + + dng_point_real64 Center () const + { + return dng_point_real64 ((t + b) * 0.5, + (l + r) * 0.5); + } + + }; + +/*****************************************************************************/ + +dng_rect operator& (const dng_rect &a, + const dng_rect &b); + +dng_rect operator| (const dng_rect &a, + const dng_rect &b); + +/*****************************************************************************/ + +dng_rect_real64 operator& (const dng_rect_real64 &a, + const dng_rect_real64 &b); + +dng_rect_real64 operator| (const dng_rect_real64 &a, + const dng_rect_real64 &b); + +/*****************************************************************************/ + +inline dng_rect operator+ (const dng_rect &a, + const dng_point &b) + { + + return dng_rect (a.t + b.v, + a.l + b.h, + a.b + b.v, + a.r + b.h); + + } + +/*****************************************************************************/ + +inline dng_rect_real64 operator+ (const dng_rect_real64 &a, + const dng_point_real64 &b) + { + + return dng_rect_real64 (a.t + b.v, + a.l + b.h, + a.b + b.v, + a.r + b.h); + + } + +/*****************************************************************************/ + +inline dng_rect operator- (const dng_rect &a, + const dng_point &b) + { + + return dng_rect (a.t - b.v, + a.l - b.h, + a.b - b.v, + a.r - b.h); + + } + +/*****************************************************************************/ + +inline dng_rect_real64 operator- (const dng_rect_real64 &a, + const dng_point_real64 &b) + { + + return dng_rect_real64 (a.t - b.v, + a.l - b.h, + a.b - b.v, + a.r - b.h); + + } + +/*****************************************************************************/ + +inline dng_rect Transpose (const dng_rect &a) + { + + return dng_rect (a.l, a.t, a.r, a.b); + + } + +/*****************************************************************************/ + +inline dng_rect_real64 Transpose (const dng_rect_real64 &a) + { + + return dng_rect_real64 (a.l, a.t, a.r, a.b); + + } + +/*****************************************************************************/ + +inline void HalfRect (dng_rect &rect) + { + + rect.r = rect.l + (int32) (rect.W () >> 1); + rect.b = rect.t + (int32) (rect.H () >> 1); + + } + +/*****************************************************************************/ + +inline void DoubleRect (dng_rect &rect) + { + + rect.r = rect.l + (int32) (rect.W () << 1); + rect.b = rect.t + (int32) (rect.H () << 1); + + } + +/*****************************************************************************/ + +inline void InnerPadRect (dng_rect &rect, + int32 pad) + { + + rect.l += pad; + rect.r -= pad; + rect.t += pad; + rect.b -= pad; + + } + +/*****************************************************************************/ + +inline void OuterPadRect (dng_rect &rect, + int32 pad) + { + + InnerPadRect (rect, -pad); + + } + +/*****************************************************************************/ + +inline void InnerPadRectH (dng_rect &rect, + int32 pad) + { + + rect.l += pad; + rect.r -= pad; + + } + +/*****************************************************************************/ + +inline void InnerPadRectV (dng_rect &rect, + int32 pad) + { + + rect.t += pad; + rect.b -= pad; + + } + +/*****************************************************************************/ + +inline dng_rect MakeHalfRect (const dng_rect &rect) + { + + dng_rect out = rect; + + HalfRect (out); + + return out; + + } + +/*****************************************************************************/ + +inline dng_rect MakeDoubleRect (const dng_rect &rect) + { + + dng_rect out = rect; + + DoubleRect (out); + + return out; + + } + +/*****************************************************************************/ + +inline dng_rect MakeInnerPadRect (const dng_rect &rect, + int32 pad) + { + + dng_rect out = rect; + + InnerPadRect (out, pad); + + return out; + + } + +/*****************************************************************************/ + +inline dng_rect MakeOuterPadRect (const dng_rect &rect, + int32 pad) + { + + dng_rect out = rect; + + OuterPadRect (out, pad); + + return out; + + } + +/*****************************************************************************/ + +inline dng_rect_real64 MakeOuterPadRect (const dng_rect_real64 &rect, + const real64 pad) + { + + dng_rect_real64 result = rect; + + result.t -= pad; + result.l -= pad; + result.b += pad; + result.r += pad; + + return result; + + } + +/*****************************************************************************/ + +inline dng_rect_real64 Lerp (const dng_rect_real64 &a, + const dng_rect_real64 &b, + const real64 t) + { + + return dng_rect_real64 (Lerp_real64 (a.t, b.t, t), + Lerp_real64 (a.l, b.l, t), + Lerp_real64 (a.b, b.b, t), + Lerp_real64 (a.r, b.r, t)); + + } + +/*****************************************************************************/ + +dng_rect_real64 Bounds (const dng_point_real64 &a, + const dng_point_real64 &b, + const dng_point_real64 &c, + const dng_point_real64 &d); + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_ref_counted_block.cpp b/dng/dng_ref_counted_block.cpp new file mode 100644 index 0000000..eff57c5 --- /dev/null +++ b/dng/dng_ref_counted_block.cpp @@ -0,0 +1,194 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include + +#include "dng_ref_counted_block.h" + +#include "dng_exceptions.h" + +/*****************************************************************************/ + +dng_ref_counted_block::dng_ref_counted_block () + + : fBuffer (NULL) + + { + + } + +/*****************************************************************************/ + +dng_ref_counted_block::dng_ref_counted_block (uint32 size) + + : fBuffer (NULL) + + { + + Allocate (size); + + } + +/*****************************************************************************/ + +dng_ref_counted_block::~dng_ref_counted_block () + { + + Clear (); + + } + +/*****************************************************************************/ + +void dng_ref_counted_block::Allocate (uint32 size) + { + + Clear (); + + if (size) + { + + fBuffer = malloc (size + sizeof (header)); + + if (!fBuffer) + { + + ThrowMemoryFull (); + + } + + new (fBuffer) header (size); + + } + + } + +/*****************************************************************************/ + +void dng_ref_counted_block::Clear () + { + + if (fBuffer) + { + + bool doFree = false; + + header *blockHeader = (struct header *)fBuffer; + + { + + dng_lock_std_mutex lock (blockHeader->fMutex); + + if (--blockHeader->fRefCount == 0) + doFree = true; + + } + + if (doFree) + { + + blockHeader->~header (); + + free (fBuffer); + + } + + fBuffer = NULL; + + } + + } + +/*****************************************************************************/ + +dng_ref_counted_block::dng_ref_counted_block (const dng_ref_counted_block &data) + + : fBuffer (NULL) + + { + + header *blockHeader = (struct header *) data.fBuffer; + + if (blockHeader) + { + + dng_lock_std_mutex lock (blockHeader->fMutex); + + blockHeader->fRefCount++; + + fBuffer = blockHeader; + + } + + } + +/*****************************************************************************/ + +dng_ref_counted_block & dng_ref_counted_block::operator= (const dng_ref_counted_block &data) + { + + if (this != &data) + { + + Clear (); + + header *blockHeader = (struct header *) data.fBuffer; + + if (blockHeader) + { + + dng_lock_std_mutex lock (blockHeader->fMutex); + + blockHeader->fRefCount++; + + fBuffer = blockHeader; + + } + + } + + return *this; + + } + +/*****************************************************************************/ + +void dng_ref_counted_block::EnsureWriteable () + { + + if (fBuffer) + { + + header *possiblySharedHeader = (header *) fBuffer; + + { + + dng_lock_std_mutex lock (possiblySharedHeader->fMutex); + + if (possiblySharedHeader->fRefCount > 1) + { + + fBuffer = NULL; + + Allocate ((uint32)possiblySharedHeader->fSize); + + memcpy (Buffer (), + ((char *)possiblySharedHeader) + sizeof (struct header), // could just do + 1 w/o cast, but this makes the type mixing more explicit + possiblySharedHeader->fSize); + + possiblySharedHeader->fRefCount--; + + } + + } + + } + + } + +/*****************************************************************************/ diff --git a/dng/dng_ref_counted_block.h b/dng/dng_ref_counted_block.h new file mode 100644 index 0000000..8c5228e --- /dev/null +++ b/dng/dng_ref_counted_block.h @@ -0,0 +1,287 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** Support for a refcounted block, with optional copy-on-write + */ + +/*****************************************************************************/ + +#ifndef __dng_ref_counted_block__ +#define __dng_ref_counted_block__ + +/*****************************************************************************/ + +#include "dng_types.h" +#include "dng_mutex.h" + +#include + +/*****************************************************************************/ + +/// \brief Class to provide resource acquisition is instantiation discipline +/// for small memory allocations. +/// +/// This class does not use dng_memory_allocator for memory allocation. + +class dng_ref_counted_block + { + + private: + + struct header + { + + dng_std_mutex fMutex; + + uint32 fRefCount; + + uint32 fSize; + + header (uint32 size) + : fMutex () + , fRefCount (1) + , fSize (size) + { + } + + ~header () + { + } + + }; + + void *fBuffer; + + public: + + /// Construct an empty memory buffer using malloc. + /// \exception dng_memory_full with fErrorCode equal to dng_error_memory. + + dng_ref_counted_block (); + + /// Construct memory buffer of size bytes using malloc. + /// \param size Number of bytes of memory needed. + /// \exception dng_memory_full with fErrorCode equal to dng_error_memory. + + dng_ref_counted_block (uint32 size); + + /// Release memory buffer using free. + + ~dng_ref_counted_block (); + + /// Copy constructore, which takes a reference to data and does not copy the block. + + dng_ref_counted_block (const dng_ref_counted_block &data); + + /// Assignment operatore takes a reference to right hand side and does not copy the data. + + dng_ref_counted_block & operator= (const dng_ref_counted_block &data); + + /// Clear existing memory buffer and allocate new memory of size bytes. + /// \param size Number of bytes of memory needed. + /// \exception dng_memory_full with fErrorCode equal to dng_error_memory. + + void Allocate (uint32 size); + + /// Release any allocated memory using free. Object is still valid and + /// Allocate can be called again. + + void Clear (); + + /// If there is only one reference, do nothing, otherwise copy the data into a new block and return an object with that block as the data. + + void EnsureWriteable (); + + /// Return pointer to allocated memory as a void *.. + /// \retval void * valid for as many bytes as were allocated. + + uint32 LogicalSize () const + { + return fBuffer ? ((header *) fBuffer)->fSize : 0; + } + + void * Buffer () + { + return fBuffer ? (void *) ((char *) fBuffer + sizeof (header)) : NULL; + } + + /// Return pointer to allocated memory as a const void *. + /// \retval const void * valid for as many bytes as were allocated. + + const void * Buffer () const + { + return fBuffer ? (const void *) ((char *) fBuffer + sizeof (header)) : NULL; + } + + /// Return pointer to allocated memory as a char *. + /// \retval char * valid for as many bytes as were allocated. + + char * Buffer_char () + { + return (char *) Buffer (); + } + + /// Return pointer to allocated memory as a const char *. + /// \retval const char * valid for as many bytes as were allocated. + + const char * Buffer_char () const + { + return (const char *) Buffer (); + } + + /// Return pointer to allocated memory as a uint8 *. + /// \retval uint8 * valid for as many bytes as were allocated. + + uint8 * Buffer_uint8 () + { + return (uint8 *) Buffer (); + } + + /// Return pointer to allocated memory as a const uint8 *. + /// \retval const uint8 * valid for as many bytes as were allocated. + + const uint8 * Buffer_uint8 () const + { + return (const uint8 *) Buffer (); + } + + /// Return pointer to allocated memory as a uint16 *. + /// \retval uint16 * valid for as many bytes as were allocated. + + uint16 * Buffer_uint16 () + { + return (uint16 *) Buffer (); + } + + /// Return pointer to allocated memory as a const uint16 *. + /// \retval const uint16 * valid for as many bytes as were allocated. + + const uint16 * Buffer_uint16 () const + { + return (const uint16 *) Buffer (); + } + + /// Return pointer to allocated memory as a int16 *. + /// \retval int16 * valid for as many bytes as were allocated. + + int16 * Buffer_int16 () + { + return (int16 *) Buffer (); + } + + /// Return pointer to allocated memory as a const int16 *. + /// \retval const int16 * valid for as many bytes as were allocated. + + const int16 * Buffer_int16 () const + { + return (const int16 *) Buffer (); + } + + /// Return pointer to allocated memory as a uint32 *. + /// \retval uint32 * valid for as many bytes as were allocated. + + uint32 * Buffer_uint32 () + { + return (uint32 *) Buffer (); + } + + /// Return pointer to allocated memory as a uint32 *. + /// \retval uint32 * valid for as many bytes as were allocated. + + const uint32 * Buffer_uint32 () const + { + return (const uint32 *) Buffer (); + } + + /// Return pointer to allocated memory as a const int32 *. + /// \retval const int32 * valid for as many bytes as were allocated. + + int32 * Buffer_int32 () + { + return (int32 *) Buffer (); + } + + /// Return pointer to allocated memory as a const int32 *. + /// \retval const int32 * valid for as many bytes as were allocated. + + const int32 * Buffer_int32 () const + { + return (const int32 *) Buffer (); + } + + /// Return pointer to allocated memory as a uint64 *. + /// \retval uint64 * valid for as many bytes as were allocated. + + uint64 * Buffer_uint64 () + { + return (uint64 *) Buffer (); + } + + /// Return pointer to allocated memory as a uint64 *. + /// \retval uint64 * valid for as many bytes as were allocated. + + const uint64 * Buffer_uint64 () const + { + return (const uint64 *) Buffer (); + } + + /// Return pointer to allocated memory as a const int64 *. + /// \retval const int64 * valid for as many bytes as were allocated. + + int64 * Buffer_int64 () + { + return (int64 *) Buffer (); + } + + /// Return pointer to allocated memory as a const int64 *. + /// \retval const int64 * valid for as many bytes as were allocated. + + const int64 * Buffer_int64 () const + { + return (const int64 *) Buffer (); + } + + /// Return pointer to allocated memory as a real32 *. + /// \retval real32 * valid for as many bytes as were allocated. + + real32 * Buffer_real32 () + { + return (real32 *) Buffer (); + } + + /// Return pointer to allocated memory as a const real32 *. + /// \retval const real32 * valid for as many bytes as were allocated. + + const real32 * Buffer_real32 () const + { + return (const real32 *) Buffer (); + } + + /// Return pointer to allocated memory as a real64 *. + /// \retval real64 * valid for as many bytes as were allocated. + + real64 * Buffer_real64 () + { + return (real64 *) Buffer (); + } + + /// Return pointer to allocated memory as a const real64 *. + /// \retval const real64 * valid for as many bytes as were allocated. + + const real64 * Buffer_real64 () const + { + return (const real64 *) Buffer (); + } + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_reference.cpp b/dng/dng_reference.cpp new file mode 100644 index 0000000..fc41de6 --- /dev/null +++ b/dng/dng_reference.cpp @@ -0,0 +1,3204 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_reference.h" + +#include "dng_1d_table.h" +#include "dng_flags.h" +#include "dng_hue_sat_map.h" +#include "dng_matrix.h" +#include "dng_resample.h" +#include "dng_simd_type.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +// This module contains routines that should be as fast as possible, even +// at the expense of slight code size increases. + +#include "dng_fast_module.h" + +/*****************************************************************************/ + +void RefZeroBytes (void *dPtr, + uint32 count) + { + + memset (dPtr, 0, count); + + } + +/*****************************************************************************/ + +void RefCopyBytes (const void *sPtr, + void *dPtr, + uint32 count) + { + + memcpy (dPtr, sPtr, count); + + } + +/*****************************************************************************/ + +void RefSwapBytes16 (uint16 *dPtr, + uint32 count) + { + + for (uint32 j = 0; j < count; j++) + { + + dPtr [j] = SwapBytes16 (dPtr [j]); + + } + + } + +/*****************************************************************************/ + +void RefSwapBytes32 (uint32 *dPtr, + uint32 count) + { + + for (uint32 j = 0; j < count; j++) + { + + dPtr [j] = SwapBytes32 (dPtr [j]); + + } + + } + +/*****************************************************************************/ + +void RefSetArea8 (uint8 *dPtr, + uint8 value, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep) + { + + for (uint32 row = 0; row < rows; row++) + { + + uint8 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + uint8 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = value; + + dPtr2 += planeStep; + + } + + dPtr1 += colStep; + + } + + dPtr += rowStep; + + } + + } + +/*****************************************************************************/ + +template +void RefSetArea (destType *dPtr, + destType value, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep) + { + + INTEL_COMPILER_NEEDED_NOTE + SET_CPU_FEATURE(simd); + + if ((planeStep == 0) && (colStep == 1)) + { + + for (uint32 row = 0; row < rows; row++) + { + + INTEL_PRAGMA_SIMD_ASSERT + for (uint32 col = 0; col < cols; col++) + { + + dPtr [col] = value; + + } + + dPtr += rowStep; + + } + } + + else if (planeStep == 1) + { + + for (uint32 row = 0; row < rows; row++) + { + + destType *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + destType *dPtr2 = dPtr1; + + INTEL_PRAGMA_SIMD_ASSERT + for (uint32 plane = 0; plane < planes; plane++) + { + + dPtr2 [plane] = value; + + } + + dPtr1 += colStep; + + } + + dPtr += rowStep; + + } + + } + + else + { + + for (uint32 row = 0; row < rows; row++) + { + + destType *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + destType *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = value; + + dPtr2 += planeStep; + + } + + dPtr1 += colStep; + + } + + dPtr += rowStep; + + } + + } + + } + +/*****************************************************************************/ + +#if !qDNGIntelCompiler +template +void RefSetArea(uint16 *dPtr, + uint16 value, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep); +template +void RefSetArea(uint32 *dPtr, + uint32 value, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep); +#else +template SetArea16Proc RefSetArea; +template SetArea16Proc RefSetArea; +template SetArea32Proc RefSetArea; +template SetArea32Proc RefSetArea; +#endif + +/*****************************************************************************/ + +void RefCopyArea8 (const uint8 *sPtr, + uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + for (uint32 row = 0; row < rows; row++) + { + + const uint8 *sPtr1 = sPtr; + uint8 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint8 *sPtr2 = sPtr1; + uint8 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = *sPtr2; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefCopyArea16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + for (uint32 row = 0; row < rows; row++) + { + + const uint16 *sPtr1 = sPtr; + uint16 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint16 *sPtr2 = sPtr1; + uint16 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = *sPtr2; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefCopyArea32 (const uint32 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + for (uint32 row = 0; row < rows; row++) + { + + const uint32 *sPtr1 = sPtr; + uint32 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint32 *sPtr2 = sPtr1; + uint32 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = *sPtr2; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefCopyArea8_16 (const uint8 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + for (uint32 row = 0; row < rows; row++) + { + + const uint8 *sPtr1 = sPtr; + uint16 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint8 *sPtr2 = sPtr1; + uint16 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = *sPtr2; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefCopyArea8_S16 (const uint8 *sPtr, + int16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + for (uint32 row = 0; row < rows; row++) + { + + const uint8 *sPtr1 = sPtr; + int16 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint8 *sPtr2 = sPtr1; + int16 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + int16 x = *sPtr; + + *dPtr2 = x ^ 0x8000; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefCopyArea8_32 (const uint8 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + for (uint32 row = 0; row < rows; row++) + { + + const uint8 *sPtr1 = sPtr; + uint32 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint8 *sPtr2 = sPtr1; + uint32 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = *sPtr2; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +template +void RefCopyArea16_S16 (const uint16 *sPtr, + int16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + INTEL_COMPILER_NEEDED_NOTE + SET_CPU_FEATURE(simd); + + for (uint32 row = 0; row < rows; row++) + { + + const uint16 *sPtr1 = sPtr; + int16 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint16 *sPtr2 = sPtr1; + int16 *dPtr2 = dPtr1; + + // Vectorizing if both sPlaneStep and dPlaneStep are 1. Else, + // regular operation is performed. + + if (sPlaneStep == 1 && dPlaneStep == 1) + { + + INTEL_PRAGMA_SIMD_ASSERT + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = *sPtr2 ^ 0x8000; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + } + + } + + else + { + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = *sPtr2 ^ 0x8000; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +INTEL_COMPILER_NEEDED_NOTE +#if !qDNGIntelCompiler +template +void RefCopyArea16_S16 (const uint16 *sPtr, + int16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); +#else +template CopyArea16_S16Proc RefCopyArea16_S16; +template CopyArea16_S16Proc RefCopyArea16_S16; +#endif + +/*****************************************************************************/ + +void RefCopyArea16_32 (const uint16 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + for (uint32 row = 0; row < rows; row++) + { + + const uint16 *sPtr1 = sPtr; + uint32 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint16 *sPtr2 = sPtr1; + uint32 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = *sPtr2; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefCopyArea8_R32 (const uint8 *sPtr, + real32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange) + { + + real32 scale = 1.0f / (real32) pixelRange; + + for (uint32 row = 0; row < rows; row++) + { + + const uint8 *sPtr1 = sPtr; + real32 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint8 *sPtr2 = sPtr1; + real32 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = scale * (real32) *sPtr2; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefCopyArea16_R32 (const uint16 *sPtr, + real32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange) + { + + real32 scale = 1.0f / (real32) pixelRange; + + for (uint32 row = 0; row < rows; row++) + { + + const uint16 *sPtr1 = sPtr; + real32 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint16 *sPtr2 = sPtr1; + real32 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = scale * (real32) *sPtr2; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefCopyAreaS16_R32 (const int16 *sPtr, + real32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange) + { + + real32 scale = 1.0f / (real32) pixelRange; + + for (uint32 row = 0; row < rows; row++) + { + + const int16 *sPtr1 = sPtr; + real32 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const int16 *sPtr2 = sPtr1; + real32 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + int32 x = (*sPtr ^ 0x8000); + + *dPtr2 = scale * (real32) x; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefCopyAreaR32_8 (const real32 *sPtr, + uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange) + { + + real32 scale = (real32) pixelRange; + + for (uint32 row = 0; row < rows; row++) + { + + const real32 *sPtr1 = sPtr; + uint8 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const real32 *sPtr2 = sPtr1; + uint8 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = (uint8) (Pin_Overrange (*sPtr2) * scale + 0.5f); + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefCopyAreaR32_16 (const real32 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange) + { + + real32 scale = (real32) pixelRange; + + for (uint32 row = 0; row < rows; row++) + { + + const real32 *sPtr1 = sPtr; + uint16 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const real32 *sPtr2 = sPtr1; + uint16 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = (uint16) (Pin_Overrange (*sPtr2) * scale + 0.5f); + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefCopyAreaR32_S16 (const real32 *sPtr, + int16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange) + { + + real32 scale = (real32) pixelRange; + + for (uint32 row = 0; row < rows; row++) + { + + const real32 *sPtr1 = sPtr; + int16 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const real32 *sPtr2 = sPtr1; + int16 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + int32 x = (int32) (Pin_Overrange (*sPtr2) * scale + 0.5f); + + *dPtr2 = (int16) (x ^ 0x8000); + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefRepeatArea8 (const uint8 *sPtr, + uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 repeatV, + uint32 repeatH, + uint32 phaseV, + uint32 phaseH) + { + + const uint8 *sPtr0 = sPtr + phaseV * rowStep + + phaseH * colStep; + + int32 backStepV = (repeatV - 1) * rowStep; + int32 backStepH = (repeatH - 1) * colStep; + + for (uint32 row = 0; row < rows; row++) + { + + const uint8 *sPtr1 = sPtr0; + uint8 *dPtr1 = dPtr; + + uint32 colPhase = phaseH; + + for (uint32 col = 0; col < cols; col++) + { + + const uint8 *sPtr2 = sPtr1; + uint8 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = *sPtr2; + + sPtr2 += planeStep; + dPtr2 += planeStep; + + } + + if (++colPhase == repeatH) + { + colPhase = 0; + sPtr1 -= backStepH; + } + else + { + sPtr1 += colStep; + } + + dPtr1 += colStep; + + } + + if (++phaseV == repeatV) + { + phaseV = 0; + sPtr0 -= backStepV; + } + else + { + sPtr0 += rowStep; + } + + dPtr += rowStep; + + } + + } + +/*****************************************************************************/ + +void RefRepeatArea16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 repeatV, + uint32 repeatH, + uint32 phaseV, + uint32 phaseH) + { + + const uint16 *sPtr0 = sPtr + phaseV * rowStep + + phaseH * colStep; + + int32 backStepV = (repeatV - 1) * rowStep; + int32 backStepH = (repeatH - 1) * colStep; + + for (uint32 row = 0; row < rows; row++) + { + + const uint16 *sPtr1 = sPtr0; + uint16 *dPtr1 = dPtr; + + uint32 colPhase = phaseH; + + for (uint32 col = 0; col < cols; col++) + { + + const uint16 *sPtr2 = sPtr1; + uint16 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = *sPtr2; + + sPtr2 += planeStep; + dPtr2 += planeStep; + + } + + if (++colPhase == repeatH) + { + colPhase = 0; + sPtr1 -= backStepH; + } + else + { + sPtr1 += colStep; + } + + dPtr1 += colStep; + + } + + if (++phaseV == repeatV) + { + phaseV = 0; + sPtr0 -= backStepV; + } + else + { + sPtr0 += rowStep; + } + + dPtr += rowStep; + + } + + } + +/*****************************************************************************/ + +void RefRepeatArea32 (const uint32 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 repeatV, + uint32 repeatH, + uint32 phaseV, + uint32 phaseH) + { + + const uint32 *sPtr0 = sPtr + phaseV * rowStep + + phaseH * colStep; + + int32 backStepV = (repeatV - 1) * rowStep; + int32 backStepH = (repeatH - 1) * colStep; + + for (uint32 row = 0; row < rows; row++) + { + + const uint32 *sPtr1 = sPtr0; + uint32 *dPtr1 = dPtr; + + uint32 colPhase = phaseH; + + for (uint32 col = 0; col < cols; col++) + { + + const uint32 *sPtr2 = sPtr1; + uint32 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = *sPtr2; + + sPtr2 += planeStep; + dPtr2 += planeStep; + + } + + if (++colPhase == repeatH) + { + colPhase = 0; + sPtr1 -= backStepH; + } + else + { + sPtr1 += colStep; + } + + dPtr1 += colStep; + + } + + if (++phaseV == repeatV) + { + phaseV = 0; + sPtr0 -= backStepV; + } + else + { + sPtr0 += rowStep; + } + + dPtr += rowStep; + + } + + } + +/*****************************************************************************/ + +void RefShiftRight16 (uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 shift) + { + + for (uint32 row = 0; row < rows; row++) + { + + uint16 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + uint16 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 >>= shift; + + dPtr2 += planeStep; + + } + + dPtr1 += colStep; + + } + + dPtr += rowStep; + + } + + } + +/*****************************************************************************/ + +void RefBilinearRow16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 cols, + uint32 patPhase, + uint32 patCount, + const uint32 * kernCounts, + const int32 * const * kernOffsets, + const uint16 * const * kernWeights, + uint32 sShift) + { + + for (uint32 j = 0; j < cols; j++) + { + + const uint16 *p = sPtr + (j >> sShift); + + uint32 count = kernCounts [patPhase]; + + const int32 *offsets = kernOffsets [patPhase]; + const uint16 *weights = kernWeights [patPhase]; + + if (++patPhase == patCount) + { + patPhase = 0; + } + + uint32 total = 128; + + for (uint32 k = 0; k < count; k++) + { + + int32 offset = offsets [k]; + uint32 weight = weights [k]; + + uint32 pixel = p [offset]; + + total += pixel * weight; + + } + + dPtr [j] = (uint16) (total >> 8); + + } + + } + +/*****************************************************************************/ + +void RefBilinearRow32 (const real32 *sPtr, + real32 *dPtr, + uint32 cols, + uint32 patPhase, + uint32 patCount, + const uint32 * kernCounts, + const int32 * const * kernOffsets, + const real32 * const * kernWeights, + uint32 sShift) + { + + for (uint32 j = 0; j < cols; j++) + { + + const real32 *p = sPtr + (j >> sShift); + + uint32 count = kernCounts [patPhase]; + + const int32 *offsets = kernOffsets [patPhase]; + const real32 *weights = kernWeights [patPhase]; + + if (++patPhase == patCount) + { + patPhase = 0; + } + + real32 total = 0.0f; + + for (uint32 k = 0; k < count; k++) + { + + int32 offset = offsets [k]; + real32 weight = weights [k]; + + real32 pixel = p [offset]; + + total += pixel * weight; + + } + + dPtr [j] = total; + + } + + } + +/*****************************************************************************/ + +void RefBaselineABCtoRGB (const real32 *sPtrA, + const real32 *sPtrB, + const real32 *sPtrC, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_vector &cameraWhite, + const dng_matrix &cameraToRGB) + { + + real32 clipA = (real32) cameraWhite [0]; + real32 clipB = (real32) cameraWhite [1]; + real32 clipC = (real32) cameraWhite [2]; + + real32 m00 = (real32) cameraToRGB [0] [0]; + real32 m01 = (real32) cameraToRGB [0] [1]; + real32 m02 = (real32) cameraToRGB [0] [2]; + + real32 m10 = (real32) cameraToRGB [1] [0]; + real32 m11 = (real32) cameraToRGB [1] [1]; + real32 m12 = (real32) cameraToRGB [1] [2]; + + real32 m20 = (real32) cameraToRGB [2] [0]; + real32 m21 = (real32) cameraToRGB [2] [1]; + real32 m22 = (real32) cameraToRGB [2] [2]; + + for (uint32 col = 0; col < count; col++) + { + + real32 A = sPtrA [col]; + real32 B = sPtrB [col]; + real32 C = sPtrC [col]; + + A = Min_real32 (A, clipA); + B = Min_real32 (B, clipB); + C = Min_real32 (C, clipC); + + real32 r = m00 * A + m01 * B + m02 * C; + real32 g = m10 * A + m11 * B + m12 * C; + real32 b = m20 * A + m21 * B + m22 * C; + + r = Pin_real32 (0.0f, r, 1.0f); + g = Pin_real32 (0.0f, g, 1.0f); + b = Pin_real32 (0.0f, b, 1.0f); + + dPtrR [col] = r; + dPtrG [col] = g; + dPtrB [col] = b; + + } + + } + +/*****************************************************************************/ + +void RefBaselineABCDtoRGB (const real32 *sPtrA, + const real32 *sPtrB, + const real32 *sPtrC, + const real32 *sPtrD, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_vector &cameraWhite, + const dng_matrix &cameraToRGB) + { + + real32 clipA = (real32) cameraWhite [0]; + real32 clipB = (real32) cameraWhite [1]; + real32 clipC = (real32) cameraWhite [2]; + real32 clipD = (real32) cameraWhite [3]; + + real32 m00 = (real32) cameraToRGB [0] [0]; + real32 m01 = (real32) cameraToRGB [0] [1]; + real32 m02 = (real32) cameraToRGB [0] [2]; + real32 m03 = (real32) cameraToRGB [0] [3]; + + real32 m10 = (real32) cameraToRGB [1] [0]; + real32 m11 = (real32) cameraToRGB [1] [1]; + real32 m12 = (real32) cameraToRGB [1] [2]; + real32 m13 = (real32) cameraToRGB [1] [3]; + + real32 m20 = (real32) cameraToRGB [2] [0]; + real32 m21 = (real32) cameraToRGB [2] [1]; + real32 m22 = (real32) cameraToRGB [2] [2]; + real32 m23 = (real32) cameraToRGB [2] [3]; + + for (uint32 col = 0; col < count; col++) + { + + real32 A = sPtrA [col]; + real32 B = sPtrB [col]; + real32 C = sPtrC [col]; + real32 D = sPtrD [col]; + + A = Min_real32 (A, clipA); + B = Min_real32 (B, clipB); + C = Min_real32 (C, clipC); + D = Min_real32 (D, clipD); + + real32 r = m00 * A + m01 * B + m02 * C + m03 * D; + real32 g = m10 * A + m11 * B + m12 * C + m13 * D; + real32 b = m20 * A + m21 * B + m22 * C + m23 * D; + + r = Pin_real32 (0.0f, r, 1.0f); + g = Pin_real32 (0.0f, g, 1.0f); + b = Pin_real32 (0.0f, b, 1.0f); + + dPtrR [col] = r; + dPtrG [col] = g; + dPtrB [col] = b; + + } + + } + +/*****************************************************************************/ + +void RefBaselineHueSatMap (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_hue_sat_map &lut, + const dng_1d_table *encodeTable, + const dng_1d_table *decodeTable) + { + + uint32 hueDivisions; + uint32 satDivisions; + uint32 valDivisions; + + lut.GetDivisions (hueDivisions, + satDivisions, + valDivisions); + + real32 hScale = (hueDivisions < 2) ? 0.0f : (hueDivisions * (1.0f / 6.0f)); + real32 sScale = (real32) ((int32) satDivisions - 1); + real32 vScale = (real32) ((int32) valDivisions - 1); + + int32 maxHueIndex0 = (int32) hueDivisions - 1; + int32 maxSatIndex0 = (int32) satDivisions - 2; + int32 maxValIndex0 = (int32) valDivisions - 2; + + const bool hasEncodeTable = ((encodeTable != NULL) && (encodeTable->Table () != NULL)); + const bool hasDecodeTable = ((decodeTable != NULL) && (decodeTable->Table () != NULL)); + + const bool hasTable = hasEncodeTable && hasDecodeTable; + + const dng_hue_sat_map::HSBModify *tableBase = lut.GetConstDeltas (); + + int32 hueStep = satDivisions; + int32 valStep = hueDivisions * hueStep; + + #if 0 // Not required with "2.5D" table optimization. + + if (valDivisions < 2) + { + valStep = 0; + maxValIndex0 = 0; + } + + #endif + + for (uint32 j = 0; j < count; j++) + { + + real32 r = sPtrR [j]; + real32 g = sPtrG [j]; + real32 b = sPtrB [j]; + + real32 h, s, v; + + DNG_RGBtoHSV (r, g, b, h, s, v); + + real32 vEncoded = v; + + real32 hueShift; + real32 satScale; + real32 valScale; + + if (valDivisions < 2) // Optimize most common case of "2.5D" table. + { + + real32 hScaled = h * hScale; + real32 sScaled = s * sScale; + + int32 hIndex0 = (int32) hScaled; + int32 sIndex0 = (int32) sScaled; + + sIndex0 = Min_int32 (sIndex0, maxSatIndex0); + + int32 hIndex1 = hIndex0 + 1; + + if (hIndex0 >= maxHueIndex0) + { + hIndex0 = maxHueIndex0; + hIndex1 = 0; + } + + real32 hFract1 = hScaled - (real32) hIndex0; + real32 sFract1 = sScaled - (real32) sIndex0; + + real32 hFract0 = 1.0f - hFract1; + real32 sFract0 = 1.0f - sFract1; + + const dng_hue_sat_map::HSBModify *entry00 = tableBase + hIndex0 * hueStep + + sIndex0; + + const dng_hue_sat_map::HSBModify *entry01 = entry00 + (hIndex1 - hIndex0) * hueStep; + + real32 hueShift0 = hFract0 * entry00->fHueShift + + hFract1 * entry01->fHueShift; + + real32 satScale0 = hFract0 * entry00->fSatScale + + hFract1 * entry01->fSatScale; + + real32 valScale0 = hFract0 * entry00->fValScale + + hFract1 * entry01->fValScale; + + entry00++; + entry01++; + + real32 hueShift1 = hFract0 * entry00->fHueShift + + hFract1 * entry01->fHueShift; + + real32 satScale1 = hFract0 * entry00->fSatScale + + hFract1 * entry01->fSatScale; + + real32 valScale1 = hFract0 * entry00->fValScale + + hFract1 * entry01->fValScale; + + hueShift = sFract0 * hueShift0 + sFract1 * hueShift1; + satScale = sFract0 * satScale0 + sFract1 * satScale1; + valScale = sFract0 * valScale0 + sFract1 * valScale1; + + } + + else + { + + if (hasTable) + { + vEncoded = encodeTable->Interpolate (Pin_real32 (v)); + } + + real32 hScaled = h * hScale; + real32 sScaled = s * sScale; + real32 vScaled = vEncoded * vScale; + + int32 hIndex0 = (int32) hScaled; + int32 sIndex0 = (int32) sScaled; + int32 vIndex0 = (int32) vScaled; + + sIndex0 = Min_int32 (sIndex0, maxSatIndex0); + vIndex0 = Min_int32 (vIndex0, maxValIndex0); + + int32 hIndex1 = hIndex0 + 1; + + if (hIndex0 >= maxHueIndex0) + { + hIndex0 = maxHueIndex0; + hIndex1 = 0; + } + + real32 hFract1 = hScaled - (real32) hIndex0; + real32 sFract1 = sScaled - (real32) sIndex0; + real32 vFract1 = vScaled - (real32) vIndex0; + + real32 hFract0 = 1.0f - hFract1; + real32 sFract0 = 1.0f - sFract1; + real32 vFract0 = 1.0f - vFract1; + + const dng_hue_sat_map::HSBModify *entry00 = tableBase + vIndex0 * valStep + + hIndex0 * hueStep + + sIndex0; + + const dng_hue_sat_map::HSBModify *entry01 = entry00 + (hIndex1 - hIndex0) * hueStep; + + const dng_hue_sat_map::HSBModify *entry10 = entry00 + valStep; + const dng_hue_sat_map::HSBModify *entry11 = entry01 + valStep; + + real32 hueShift0 = vFract0 * (hFract0 * entry00->fHueShift + + hFract1 * entry01->fHueShift) + + vFract1 * (hFract0 * entry10->fHueShift + + hFract1 * entry11->fHueShift); + + real32 satScale0 = vFract0 * (hFract0 * entry00->fSatScale + + hFract1 * entry01->fSatScale) + + vFract1 * (hFract0 * entry10->fSatScale + + hFract1 * entry11->fSatScale); + + real32 valScale0 = vFract0 * (hFract0 * entry00->fValScale + + hFract1 * entry01->fValScale) + + vFract1 * (hFract0 * entry10->fValScale + + hFract1 * entry11->fValScale); + + entry00++; + entry01++; + entry10++; + entry11++; + + real32 hueShift1 = vFract0 * (hFract0 * entry00->fHueShift + + hFract1 * entry01->fHueShift) + + vFract1 * (hFract0 * entry10->fHueShift + + hFract1 * entry11->fHueShift); + + real32 satScale1 = vFract0 * (hFract0 * entry00->fSatScale + + hFract1 * entry01->fSatScale) + + vFract1 * (hFract0 * entry10->fSatScale + + hFract1 * entry11->fSatScale); + + real32 valScale1 = vFract0 * (hFract0 * entry00->fValScale + + hFract1 * entry01->fValScale) + + vFract1 * (hFract0 * entry10->fValScale + + hFract1 * entry11->fValScale); + + hueShift = sFract0 * hueShift0 + sFract1 * hueShift1; + satScale = sFract0 * satScale0 + sFract1 * satScale1; + valScale = sFract0 * valScale0 + sFract1 * valScale1; + + } + + hueShift *= (6.0f / 360.0f); // Convert to internal hue range. + + h += hueShift; + + s = Min_real32 (s * satScale, 1.0f); + + vEncoded = Pin_real32 (vEncoded * valScale); + + v = hasTable ? decodeTable->Interpolate (vEncoded) : vEncoded; + + DNG_HSVtoRGB (h, s, v, r, g, b); + + dPtrR [j] = r; + dPtrG [j] = g; + dPtrB [j] = b; + + } + + } + +/*****************************************************************************/ + +void RefBaselineRGBtoGray (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrG, + uint32 count, + const dng_matrix &matrix) + { + + real32 m00 = (real32) matrix [0] [0]; + real32 m01 = (real32) matrix [0] [1]; + real32 m02 = (real32) matrix [0] [2]; + + for (uint32 col = 0; col < count; col++) + { + + real32 R = sPtrR [col]; + real32 G = sPtrG [col]; + real32 B = sPtrB [col]; + + real32 g = m00 * R + m01 * G + m02 * B; + + g = Pin_real32 (0.0f, g, 1.0f); + + dPtrG [col] = g; + + } + + } + +/*****************************************************************************/ + +void RefBaselineRGBtoRGB (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_matrix &matrix) + { + + real32 m00 = (real32) matrix [0] [0]; + real32 m01 = (real32) matrix [0] [1]; + real32 m02 = (real32) matrix [0] [2]; + + real32 m10 = (real32) matrix [1] [0]; + real32 m11 = (real32) matrix [1] [1]; + real32 m12 = (real32) matrix [1] [2]; + + real32 m20 = (real32) matrix [2] [0]; + real32 m21 = (real32) matrix [2] [1]; + real32 m22 = (real32) matrix [2] [2]; + + for (uint32 col = 0; col < count; col++) + { + + real32 R = sPtrR [col]; + real32 G = sPtrG [col]; + real32 B = sPtrB [col]; + + real32 r = m00 * R + m01 * G + m02 * B; + real32 g = m10 * R + m11 * G + m12 * B; + real32 b = m20 * R + m21 * G + m22 * B; + + r = Pin_real32 (0.0f, r, 1.0f); + g = Pin_real32 (0.0f, g, 1.0f); + b = Pin_real32 (0.0f, b, 1.0f); + + dPtrR [col] = r; + dPtrG [col] = g; + dPtrB [col] = b; + + } + + } + +/*****************************************************************************/ + +void RefBaseline1DTable (const real32 *sPtr, + real32 *dPtr, + uint32 count, + const dng_1d_table &table) + { + + for (uint32 col = 0; col < count; col++) + { + + real32 x = sPtr [col]; + + real32 y = table.Interpolate (x); + + dPtr [col] = y; + + } + + } + +/*****************************************************************************/ + +void RefBaselineRGBTone (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_1d_table &table) + { + + for (uint32 col = 0; col < count; col++) + { + + real32 r = sPtrR [col]; + real32 g = sPtrG [col]; + real32 b = sPtrB [col]; + + real32 rr; + real32 gg; + real32 bb; + + #define RGBTone(r, g, b, rr, gg, bb)\ + {\ + \ + DNG_ASSERT (r >= g && g >= b && r > b, "Logic Error RGBTone");\ + \ + rr = table.Interpolate (r);\ + bb = table.Interpolate (b);\ + \ + gg = bb + ((rr - bb) * (g - b) / (r - b));\ + \ + } + + if (r >= g) + { + + if (g > b) + { + + // Case 1: r >= g > b + + RGBTone (r, g, b, rr, gg, bb); + + } + + else if (b > r) + { + + // Case 2: b > r >= g + + RGBTone (b, r, g, bb, rr, gg); + + } + + else if (b > g) + { + + // Case 3: r >= b > g + + RGBTone (r, b, g, rr, bb, gg); + + } + + else + { + + // Case 4: r >= g == b + + DNG_ASSERT (r >= g && g == b, "Logic Error 2"); + + rr = table.Interpolate (r); + gg = table.Interpolate (g); + bb = gg; + + } + + } + + else + { + + if (r >= b) + { + + // Case 5: g > r >= b + + RGBTone (g, r, b, gg, rr, bb); + + } + + else if (b > g) + { + + // Case 6: b > g > r + + RGBTone (b, g, r, bb, gg, rr); + + } + + else + { + + // Case 7: g >= b > r + + RGBTone (g, b, r, gg, bb, rr); + + } + + } + + #undef RGBTone + + dPtrR [col] = rr; + dPtrG [col] = gg; + dPtrB [col] = bb; + + } + + } + +/*****************************************************************************/ + +void RefResampleDown16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 sCount, + int32 sRowStep, + const int16 *wPtr, + uint32 wCount, + uint32 pixelRange) + { + + for (uint32 j = 0; j < sCount; j++) + { + + int32 total = 8192; + + const uint16 *s = sPtr + j; + + for (uint32 k = 0; k < wCount; k++) + { + + total += wPtr [k] * (int32) s [0]; + + s += sRowStep; + + } + + dPtr [j] = (uint16) Pin_int32 (0, + total >> 14, + pixelRange); + + } + + } + +/*****************************************************************************/ + +void RefResampleDown32 (const real32 *sPtr, + real32 *dPtr, + uint32 sCount, + int32 sRowStep, + const real32 *wPtr, + uint32 wCount) + { + + uint32 col; + + // Process first row. + + real32 w = wPtr [0]; + + for (col = 0; col < sCount; col++) + { + + dPtr [col] = w * sPtr [col]; + + } + + sPtr += sRowStep; + + // Process middle rows. + + for (uint32 j = 1; j < wCount - 1; j++) + { + + w = wPtr [j]; + + for (col = 0; col < sCount; col++) + { + + dPtr [col] += w * sPtr [col]; + + } + + sPtr += sRowStep; + + } + + // Process last row. + + w = wPtr [wCount - 1]; + + for (col = 0; col < sCount; col++) + { + + dPtr [col] = Pin_real32 (0.0f, + dPtr [col] + w * sPtr [col], + 1.0f); + + } + + } + +/******************************************************************************/ + +void RefResampleAcross16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 dCount, + const int32 *coord, + const int16 *wPtr, + uint32 wCount, + uint32 wStep, + uint32 pixelRange) + { + + for (uint32 j = 0; j < dCount; j++) + { + + int32 sCoord = coord [j]; + + int32 sFract = sCoord & kResampleSubsampleMask; + int32 sPixel = sCoord >> kResampleSubsampleBits; + + const int16 *w = wPtr + sFract * wStep; + const uint16 *s = sPtr + sPixel; + + int32 total = w [0] * (int32) s [0]; + + for (uint32 k = 1; k < wCount; k++) + { + + total += w [k] * (int32) s [k]; + + } + + dPtr [j] = (uint16) Pin_int32 (0, + (total + 8192) >> 14, + pixelRange); + + } + + } + +/******************************************************************************/ + +void RefResampleAcross32 (const real32 *sPtr, + real32 *dPtr, + uint32 dCount, + const int32 *coord, + const real32 *wPtr, + uint32 wCount, + uint32 wStep) + { + + for (uint32 j = 0; j < dCount; j++) + { + + int32 sCoord = coord [j]; + + int32 sFract = sCoord & kResampleSubsampleMask; + int32 sPixel = sCoord >> kResampleSubsampleBits; + + const real32 *w = wPtr + sFract * wStep; + const real32 *s = sPtr + sPixel; + + real32 total = w [0] * s [0]; + + for (uint32 k = 1; k < wCount; k++) + { + + total += w [k] * s [k]; + + } + + dPtr [j] = Pin_real32 (0.0f, total, 1.0f); + + } + + } + +/*****************************************************************************/ + +bool RefEqualBytes (const void *sPtr, + const void *dPtr, + uint32 count) + { + + return memcmp (dPtr, sPtr, count) == 0; + + } + +/*****************************************************************************/ + +bool RefEqualArea8 (const uint8 *sPtr, + const uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + for (uint32 row = 0; row < rows; row++) + { + + const uint8 *sPtr1 = sPtr; + const uint8 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint8 *sPtr2 = sPtr1; + const uint8 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + if (*dPtr2 != *sPtr2) + return false; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + return true; + + } + +/*****************************************************************************/ + +bool RefEqualArea16 (const uint16 *sPtr, + const uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + for (uint32 row = 0; row < rows; row++) + { + + const uint16 *sPtr1 = sPtr; + const uint16 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint16 *sPtr2 = sPtr1; + const uint16 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + if (*dPtr2 != *sPtr2) + return false; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + return true; + + } + +/*****************************************************************************/ + +bool RefEqualArea32 (const uint32 *sPtr, + const uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + for (uint32 row = 0; row < rows; row++) + { + + const uint32 *sPtr1 = sPtr; + const uint32 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint32 *sPtr2 = sPtr1; + const uint32 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + if (*dPtr2 != *sPtr2) + return false; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + return true; + + } + +/*****************************************************************************/ + +void RefVignetteMask16 (uint16 *mPtr, + uint32 rows, + uint32 cols, + int32 rowStep, + int64 offsetH, + int64 offsetV, + int64 stepH, + int64 stepV, + uint32 tBits, + const uint16 *table) + { + + uint32 tShift = 32 - tBits; + uint32 tRound = (1 << (tShift - 1)); + uint32 tLimit = 1 << tBits; + + for (uint32 row = 0; row < rows; row++) + { + + int64 baseDelta = (offsetV + 32768) >> 16; + + baseDelta = baseDelta * baseDelta + tRound; + + int64 deltaH = offsetH + 32768; + + for (uint32 col = 0; col < cols; col++) + { + + int64 temp = deltaH >> 16; + + int64 delta = baseDelta + (temp * temp); + + uint32 index = Min_uint32 ((uint32) (delta >> tShift), tLimit); + + mPtr [col] = table [index]; + + deltaH += stepH; + + } + + offsetV += stepV; + + mPtr += rowStep; + + } + + } + +/*****************************************************************************/ + +void RefVignette16 (int16 *sPtr, + const uint16 *mPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sPlaneStep, + int32 mRowStep, + uint32 mBits) + { + + const uint32 mRound = 1 << (mBits - 1); + + switch (planes) + { + + case 1: + { + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 0; col < cols; col++) + { + + uint32 s = sPtr [col] + 32768; + + uint32 m = mPtr [col]; + + s = (s * m + mRound) >> mBits; + + s = Min_uint32 (s, 65535); + + sPtr [col] = (int16) (s - 32768); + + } + + sPtr += sRowStep; + + mPtr += mRowStep; + + } + + break; + + } + + case 3: + { + + int16 *rPtr = sPtr; + int16 *gPtr = rPtr + sPlaneStep; + int16 *bPtr = gPtr + sPlaneStep; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 0; col < cols; col++) + { + + uint32 r = rPtr [col] + 32768; + uint32 g = gPtr [col] + 32768; + uint32 b = bPtr [col] + 32768; + + uint32 m = mPtr [col]; + + r = (r * m + mRound) >> mBits; + g = (g * m + mRound) >> mBits; + b = (b * m + mRound) >> mBits; + + r = Min_uint32 (r, 65535); + g = Min_uint32 (g, 65535); + b = Min_uint32 (b, 65535); + + rPtr [col] = (int16) (r - 32768); + gPtr [col] = (int16) (g - 32768); + bPtr [col] = (int16) (b - 32768); + + } + + rPtr += sRowStep; + gPtr += sRowStep; + bPtr += sRowStep; + + mPtr += mRowStep; + + } + + break; + + } + + case 4: + { + + int16 *aPtr = sPtr; + int16 *bPtr = aPtr + sPlaneStep; + int16 *cPtr = bPtr + sPlaneStep; + int16 *dPtr = cPtr + sPlaneStep; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 0; col < cols; col++) + { + + uint32 a = aPtr [col] + 32768; + uint32 b = bPtr [col] + 32768; + uint32 c = cPtr [col] + 32768; + uint32 d = dPtr [col] + 32768; + + uint32 m = mPtr [col]; + + a = (a * m + mRound) >> mBits; + b = (b * m + mRound) >> mBits; + c = (c * m + mRound) >> mBits; + d = (d * m + mRound) >> mBits; + + a = Min_uint32 (a, 65535); + b = Min_uint32 (b, 65535); + c = Min_uint32 (c, 65535); + d = Min_uint32 (d, 65535); + + aPtr [col] = (int16) (a - 32768); + bPtr [col] = (int16) (b - 32768); + cPtr [col] = (int16) (c - 32768); + dPtr [col] = (int16) (d - 32768); + + } + + aPtr += sRowStep; + bPtr += sRowStep; + cPtr += sRowStep; + dPtr += sRowStep; + + mPtr += mRowStep; + + } + + break; + + } + + default: + { + + for (uint32 plane = 0; plane < planes; plane++) + { + + int16 *planePtr = sPtr; + + const uint16 *maskPtr = mPtr; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 0; col < cols; col++) + { + + uint32 s = planePtr [col] + 32768; + + uint32 m = maskPtr [col]; + + s = (s * m + mRound) >> mBits; + + s = Min_uint32 (s, 65535); + + planePtr [col] = (int16) (s - 32768); + + } + + planePtr += sRowStep; + + maskPtr += mRowStep; + + } + + sPtr += sPlaneStep; + + } + + break; + + } + + } + + } + +/*****************************************************************************/ + +void RefVignette32 (real32 *sPtr, + const uint16 *mPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sPlaneStep, + int32 mRowStep, + uint32 mBits, + uint16 blackLevel) + { + + real32 *basePtr = sPtr; + + real32 blackScale1 = 1.0f; + real32 blackScale2 = 1.0f; + real32 blackOffset1 = 0.0f; + real32 blackOffset2 = 0.0f; + + if (blackLevel != 0) + { + + blackOffset2 = ((real32) blackLevel) / 65535.0f; + blackScale2 = 1.0f - blackOffset2; + blackScale1 = 1.0f / blackScale2; + blackOffset1 = 1.0f - blackScale1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + real32 *dPtr = basePtr + plane * sPlaneStep; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 0; col < cols; col++) + { + + dPtr [col] = dPtr [col] * blackScale1 + blackOffset1; + + } + + dPtr += sRowStep; + + } + + } + + } + + const real32 kNorm = 1.0f / (1 << mBits); + + switch (planes) + { + + case 1: + { + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 0; col < cols; col++) + { + + real32 s = sPtr [col]; + + uint16 m = mPtr [col]; + + real32 scale = m * kNorm; + + s = Min_real32 (s * scale, 1.0f); + + sPtr [col] = s; + + } + + sPtr += sRowStep; + + mPtr += mRowStep; + + } + + break; + + } + + case 3: + { + + real32 *rPtr = sPtr; + real32 *gPtr = rPtr + sPlaneStep; + real32 *bPtr = gPtr + sPlaneStep; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 0; col < cols; col++) + { + + real32 r = rPtr [col]; + real32 g = gPtr [col]; + real32 b = bPtr [col]; + + uint16 m = mPtr [col]; + + real32 scale = m * kNorm; + + r = Min_real32 (r * scale, 1.0f); + g = Min_real32 (g * scale, 1.0f); + b = Min_real32 (b * scale, 1.0f); + + rPtr [col] = r; + gPtr [col] = g; + bPtr [col] = b; + + } + + rPtr += sRowStep; + gPtr += sRowStep; + bPtr += sRowStep; + + mPtr += mRowStep; + + } + + break; + + } + + case 4: + { + + real32 *aPtr = sPtr; + real32 *bPtr = aPtr + sPlaneStep; + real32 *cPtr = bPtr + sPlaneStep; + real32 *dPtr = cPtr + sPlaneStep; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 0; col < cols; col++) + { + + real32 a = aPtr [col]; + real32 b = bPtr [col]; + real32 c = cPtr [col]; + real32 d = dPtr [col]; + + uint16 m = mPtr [col]; + + real32 scale = m * kNorm; + + a = Min_real32 (a * scale, 1.0f); + b = Min_real32 (b * scale, 1.0f); + c = Min_real32 (c * scale, 1.0f); + d = Min_real32 (d * scale, 1.0f); + + aPtr [col] = a; + bPtr [col] = b; + cPtr [col] = c; + dPtr [col] = d; + + } + + aPtr += sRowStep; + bPtr += sRowStep; + cPtr += sRowStep; + dPtr += sRowStep; + + mPtr += mRowStep; + + } + + break; + + } + + default: + { + + for (uint32 plane = 0; plane < planes; plane++) + { + + real32 *planePtr = sPtr; + + const uint16 *maskPtr = mPtr; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 0; col < cols; col++) + { + + real32 s = planePtr [col]; + + uint16 m = maskPtr [col]; + + real32 scale = m * kNorm; + + s = Min_real32 (s * scale, 1.0f); + + planePtr [col] = s; + + } + + planePtr += sRowStep; + + maskPtr += mRowStep; + + } + + sPtr += sPlaneStep; + + } + + break; + + } + + } + + if (blackLevel != 0) + { + + for (uint32 plane = 0; plane < planes; plane++) + { + + real32 *dPtr = basePtr + plane * sPlaneStep; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 0; col < cols; col++) + { + + dPtr [col] = dPtr [col] * blackScale2 + blackOffset2; + + } + + dPtr += sRowStep; + + } + + } + + } + + } + +/******************************************************************************/ + +void RefMapArea16 (uint16 *dPtr, + uint32 count0, + uint32 count1, + uint32 count2, + int32 step0, + int32 step1, + int32 step2, + const uint16 *map) + { + + if (step2 == 1 && count2 >= 32) + { + + for (uint32 index0 = 0; index0 < count0; index0++) + { + + uint16 *d1 = dPtr; + + for (uint32 index1 = 0; index1 < count1; index1++) + { + + uint16 *d2 = d1; + + uint32 count = count2; + + // Get the data 32-bit aligned if it is not. + + if (!IsAligned32 (dPtr)) + { + + d2 [0] = map [d2 [0]]; + + count--; + + d2++; + + } + + // Use 32-bit reads and writes for bulk processing. + + uint32 *dPtr32 = (uint32 *) d2; + + // Process in blocks of 16 pixels. + + uint32 blocks = count >> 4; + + count -= blocks << 4; + d2 += blocks << 4; + + while (blocks--) + { + + uint32 x0, x1, x2, x3, x4, x5, x6, x7; + uint32 p0, p1, p2, p3, p4, p5, p6, p7; + + // Use 32 bit reads & writes, and pack and unpack the 16-bit values. + // This results in slightly higher performance. + + // Note that this code runs on both little-endian and big-endian systems, + // since the pixels are either never swapped or double swapped. + + x0 = dPtr32 [0]; + x1 = dPtr32 [1]; + x2 = dPtr32 [2]; + x3 = dPtr32 [3]; + + p0 = map [x0 >> 16 ]; + p1 = map [x0 & 0x0FFFF]; + p2 = map [x1 >> 16 ]; + p3 = map [x1 & 0x0FFFF]; + p4 = map [x2 >> 16 ]; + p5 = map [x2 & 0x0FFFF]; + p6 = map [x3 >> 16 ]; + p7 = map [x3 & 0x0FFFF]; + + x0 = (p0 << 16) | p1; + x1 = (p2 << 16) | p3; + x2 = (p4 << 16) | p5; + x3 = (p6 << 16) | p7; + + x4 = dPtr32 [4]; + x5 = dPtr32 [5]; + x6 = dPtr32 [6]; + x7 = dPtr32 [7]; + + dPtr32 [0] = x0; + dPtr32 [1] = x1; + dPtr32 [2] = x2; + dPtr32 [3] = x3; + + p0 = map [x4 >> 16 ]; + p1 = map [x4 & 0x0FFFF]; + p2 = map [x5 >> 16 ]; + p3 = map [x5 & 0x0FFFF]; + p4 = map [x6 >> 16 ]; + p5 = map [x6 & 0x0FFFF]; + p6 = map [x7 >> 16 ]; + p7 = map [x7 & 0x0FFFF]; + + x4 = (p0 << 16) | p1; + x5 = (p2 << 16) | p3; + x6 = (p4 << 16) | p5; + x7 = (p6 << 16) | p7; + + dPtr32 [4] = x4; + dPtr32 [5] = x5; + dPtr32 [6] = x6; + dPtr32 [7] = x7; + + dPtr32 += 8; + + } + + // Process remaining columns. + + for (uint32 j = 0; j < count; j++) + { + + d2 [j] = map [d2 [j]]; + + } + + d1 += step1; + + } + + dPtr += step0; + + } + + } + + else + { + + for (uint32 index0 = 0; index0 < count0; index0++) + { + + uint16 *d1 = dPtr; + + for (uint32 index1 = 0; index1 < count1; index1++) + { + + uint16 *d2 = d1; + + for (uint32 index2 = 0; index2 < count2; index2++) + { + + d2 [0] = map [d2 [0]]; + + d2 += step2; + + } + + d1 += step1; + + } + + dPtr += step0; + + } + + } + + } + +/*****************************************************************************/ + +void RefBaselineMapPoly32 (real32 *dPtr, + const int32 rowStep, + const uint32 rows, + const uint32 cols, + const uint32 rowPitch, + const uint32 colPitch, + const real32 *coefficients, + const uint32 degree, + uint16 blackLevel) + { + + real32 blackScale1 = 1.0f; + real32 blackScale2 = 1.0f; + real32 blackOffset1 = 0.0f; + real32 blackOffset2 = 0.0f; + + if (blackLevel != 0) + { + + blackOffset2 = ((real32) blackLevel) / 65535.0f; + blackScale2 = 1.0f - blackOffset2; + blackScale1 = 1.0f / blackScale2; + blackOffset1 = 1.0f - blackScale1; + + } + + for (uint32 row = 0; row < rows; row += rowPitch) + { + + if (blackLevel != 0) + { + + for (uint32 col = 0; col < cols; col += colPitch) + { + + dPtr [col] = dPtr [col] * blackScale1 + blackOffset1; + + } + + } + + switch (degree) + { + + case 0: + { + + real32 y = Pin_real32 (-1.0f, + coefficients [0], + 1.0f); + + for (uint32 col = 0; col < cols; col += colPitch) + { + + dPtr [col] = y; + + } + + break; + + } + + case 1: + { + + for (uint32 col = 0; col < cols; col += colPitch) + { + + real32 x = dPtr [col]; + + real32 y = coefficients [0] + x * coefficients [1]; + + dPtr [col] = Pin_real32 (-1.0f, y, 1.0f); + + } + + break; + + } + + case 2: + { + + for (uint32 col = 0; col < cols; col += colPitch) + { + + real32 x = dPtr [col]; + + real32 y; + + if (x < 0.0f) + { + + y = coefficients [0] + x * + (coefficients [1] - x * + (coefficients [2])); + + } + + else + { + + y = coefficients [0] + x * + (coefficients [1] + x * + (coefficients [2])); + + } + + dPtr [col] = Pin_real32 (-1.0f, y, 1.0f); + + } + + break; + + } + + case 3: + { + + for (uint32 col = 0; col < cols; col += colPitch) + { + + real32 x = dPtr [col]; + + real32 y; + + if (x < 0.0f) + { + + y = coefficients [0] + x * + (coefficients [1] - x * + (coefficients [2] - x * + (coefficients [3]))); + + } + + else + { + + y = coefficients [0] + x * + (coefficients [1] + x * + (coefficients [2] + x * + (coefficients [3]))); + + } + + dPtr [col] = Pin_real32 (-1.0f, y, 1.0f); + + } + + break; + + } + + case 4: + { + + for (uint32 col = 0; col < cols; col += colPitch) + { + + real32 x = dPtr [col]; + + real32 y; + + if (x < 0.0f) + { + + y = coefficients [0] + x * + (coefficients [1] - x * + (coefficients [2] - x * + (coefficients [3] - x * + (coefficients [4])))); + + } + + else + { + + y = coefficients [0] + x * + (coefficients [1] + x * + (coefficients [2] + x * + (coefficients [3] + x * + (coefficients [4])))); + + } + + dPtr [col] = Pin_real32 (-1.0f, y, 1.0f); + + } + + break; + + } + + default: + { + + for (uint32 col = 0; col < cols; col += colPitch) + { + + real32 x = dPtr [col]; + + real32 y = coefficients [0]; + + if (x < 0.0f) + { + + x = -x; + + real32 xx = x; + + for (uint32 j = 1; j <= degree; j++) + { + + y -= coefficients [j] * xx; + + xx *= x; + + } + + } + + else + { + + real32 xx = x; + + for (uint32 j = 1; j <= degree; j++) + { + + y += coefficients [j] * xx; + + xx *= x; + + } + + } + + dPtr [col] = Pin_real32 (-1.0f, y, 1.0f); + + } + + } + + } + + if (blackLevel != 0) + { + + for (uint32 col = 0; col < cols; col += colPitch) + { + + dPtr [col] = dPtr [col] * blackScale2 + blackOffset2; + + } + + } + + // Advance to the next row. Note that rowStep already accounts for the + // row pitch. + + dPtr += rowStep; + + } + + } + +/*****************************************************************************/ diff --git a/dng/dng_reference.h b/dng/dng_reference.h new file mode 100644 index 0000000..75f7151 --- /dev/null +++ b/dng/dng_reference.h @@ -0,0 +1,522 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef __dng_reference__ +#define __dng_reference__ + +/*****************************************************************************/ + +#include "dng_bottlenecks.h" +#include "dng_simd_type.h" +#include "dng_flags.h" + +/*****************************************************************************/ + +void RefZeroBytes (void *dPtr, + uint32 count); + +void RefCopyBytes (const void *sPtr, + void *dPtr, + uint32 count); + +/*****************************************************************************/ + +void RefSwapBytes16 (uint16 *dPtr, + uint32 count); + +void RefSwapBytes32 (uint32 *dPtr, + uint32 count); + +/*****************************************************************************/ + +void RefSetArea8 (uint8 *dPtr, + uint8 value, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep); + +template +void RefSetArea (destType *dPtr, + destType value, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep); + +/*****************************************************************************/ + +void RefCopyArea8 (const uint8 *sPtr, + uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +void RefCopyArea16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +void RefCopyArea32 (const uint32 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +void RefCopyArea8_16 (const uint8 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +void RefCopyArea8_S16 (const uint8 *sPtr, + int16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +void RefCopyArea8_32 (const uint8 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +template +void RefCopyArea16_S16 (const uint16 *sPtr, + int16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +void RefCopyArea16_32 (const uint16 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +void RefCopyArea8_R32 (const uint8 *sPtr, + real32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange); + +void RefCopyArea16_R32 (const uint16 *sPtr, + real32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange); + +void RefCopyAreaS16_R32 (const int16 *sPtr, + real32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange); + +void RefCopyAreaR32_8 (const real32 *sPtr, + uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange); + +void RefCopyAreaR32_16 (const real32 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange); + +void RefCopyAreaR32_S16 (const real32 *sPtr, + int16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange); + +/*****************************************************************************/ + +void RefRepeatArea8 (const uint8 *sPtr, + uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 repeatV, + uint32 repeatH, + uint32 phaseV, + uint32 phaseH); + +void RefRepeatArea16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 repeatV, + uint32 repeatH, + uint32 phaseV, + uint32 phaseH); + +void RefRepeatArea32 (const uint32 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 repeatV, + uint32 repeatH, + uint32 phaseV, + uint32 phaseH); + +/*****************************************************************************/ + +void RefShiftRight16 (uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 shift); + +/*****************************************************************************/ + +void RefBilinearRow16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 cols, + uint32 patPhase, + uint32 patCount, + const uint32 * kernCounts, + const int32 * const * kernOffsets, + const uint16 * const * kernWeights, + uint32 sShift); + +void RefBilinearRow32 (const real32 *sPtr, + real32 *dPtr, + uint32 cols, + uint32 patPhase, + uint32 patCount, + const uint32 * kernCounts, + const int32 * const * kernOffsets, + const real32 * const * kernWeights, + uint32 sShift); + +/*****************************************************************************/ + +void RefBaselineABCtoRGB (const real32 *sPtrA, + const real32 *sPtrB, + const real32 *sPtrC, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_vector &cameraWhite, + const dng_matrix &cameraToRGB); + +void RefBaselineABCDtoRGB (const real32 *sPtrA, + const real32 *sPtrB, + const real32 *sPtrC, + const real32 *sPtrD, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_vector &cameraWhite, + const dng_matrix &cameraToRGB); + +/*****************************************************************************/ + +void RefBaselineHueSatMap (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_hue_sat_map &lut, + const dng_1d_table *encodeTable, + const dng_1d_table *decodeTable); + +/*****************************************************************************/ + +void RefBaselineRGBtoGray (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrG, + uint32 count, + const dng_matrix &matrix); + +void RefBaselineRGBtoRGB (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_matrix &matrix); + +/*****************************************************************************/ + +void RefBaseline1DTable (const real32 *sPtr, + real32 *dPtr, + uint32 count, + const dng_1d_table &table); + +/*****************************************************************************/ + +void RefBaselineRGBTone (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_1d_table &table); + +/*****************************************************************************/ + +void RefResampleDown16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 sCount, + int32 sRowStep, + const int16 *wPtr, + uint32 wCount, + uint32 pixelRange); + +void RefResampleDown32 (const real32 *sPtr, + real32 *dPtr, + uint32 sCount, + int32 sRowStep, + const real32 *wPtr, + uint32 wCount); + +/*****************************************************************************/ + +void RefResampleAcross16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 dCount, + const int32 *coord, + const int16 *wPtr, + uint32 wCount, + uint32 wStep, + uint32 pixelRange); + +void RefResampleAcross32 (const real32 *sPtr, + real32 *dPtr, + uint32 dCount, + const int32 *coord, + const real32 *wPtr, + uint32 wCount, + uint32 wStep); + +/*****************************************************************************/ + +bool RefEqualBytes (const void *sPtr, + const void *dPtr, + uint32 count); + +bool RefEqualArea8 (const uint8 *sPtr, + const uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +bool RefEqualArea16 (const uint16 *sPtr, + const uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +bool RefEqualArea32 (const uint32 *sPtr, + const uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +/*****************************************************************************/ + +void RefVignetteMask16 (uint16 *mPtr, + uint32 rows, + uint32 cols, + int32 rowStep, + int64 offsetH, + int64 offsetV, + int64 stepH, + int64 stepV, + uint32 tBits, + const uint16 *table); + +/*****************************************************************************/ + +void RefVignette16 (int16 *sPtr, + const uint16 *mPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sPlaneStep, + int32 mRowStep, + uint32 mBits); + +/*****************************************************************************/ + +void RefVignette32 (real32 *sPtr, + const uint16 *mPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sPlaneStep, + int32 mRowStep, + uint32 mBits, + uint16 blackLevel); + +/*****************************************************************************/ + +void RefMapArea16 (uint16 *dPtr, + uint32 count0, + uint32 count1, + uint32 count2, + int32 step0, + int32 step1, + int32 step2, + const uint16 *map); + +/*****************************************************************************/ + +void RefBaselineMapPoly32 (real32 *dPtr, + const int32 rowStep, + const uint32 rows, + const uint32 cols, + const uint32 rowPitch, + const uint32 colPitch, + const real32 *coefficients, + const uint32 degree, + uint16 blackLevel); + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_render.cpp b/dng/dng_render.cpp new file mode 100644 index 0000000..06b61a6 --- /dev/null +++ b/dng/dng_render.cpp @@ -0,0 +1,1491 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_render.h" + +#include "dng_1d_table.h" +#include "dng_bottlenecks.h" +#include "dng_camera_profile.h" +#include "dng_color_space.h" +#include "dng_color_spec.h" +#include "dng_filter_task.h" +#include "dng_host.h" +#include "dng_image.h" +#include "dng_negative.h" +#include "dng_resample.h" +#include "dng_safe_arithmetic.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_function_zero_offset::dng_function_zero_offset (real64 zeroOffset) + + : fZeroOffset (zeroOffset) + + , fScale (1.0 / (1.0 - zeroOffset)) + + { + + } + +/*****************************************************************************/ + +real64 dng_function_zero_offset::Evaluate (real64 x) const + { + + return Pin_real64 (0.0, (x - fZeroOffset) * fScale, 1.0); + + } + +/*****************************************************************************/ + +dng_function_exposure_ramp::dng_function_exposure_ramp (real64 white, + real64 black, + real64 minBlack) + + : fSlope (1.0 / (white - black)) + , fBlack (black) + + , fRadius (0.0) + , fQScale (0.0) + + { + + const real64 kMaxCurveX = 0.5; // Fraction of minBlack. + + const real64 kMaxCurveY = 1.0 / 16.0; // Fraction of white. + + fRadius = Min_real64 (kMaxCurveX * minBlack, + kMaxCurveY / fSlope); + + if (fRadius > 0.0) + fQScale= fSlope / (4.0 * fRadius); + else + fQScale = 0.0; + + } + +/*****************************************************************************/ + +real64 dng_function_exposure_ramp::Evaluate (real64 x) const + { + + if (x <= fBlack - fRadius) + return 0.0; + + if (x >= fBlack + fRadius) + return Min_real64 ((x - fBlack) * fSlope, 1.0); + + real64 y = x - (fBlack - fRadius); + + return fQScale * y * y; + + } + +/*****************************************************************************/ + +dng_function_exposure_tone::dng_function_exposure_tone (real64 exposure) + + : fIsNOP (exposure >= 0.0) + + , fSlope (0.0) + + , a (0.0) + , b (0.0) + , c (0.0) + + { + + if (!fIsNOP) + { + + // Find slope to use for the all except the highest two f-stops. + + fSlope = pow (2.0, exposure); + + // Find quadradic parameters that match this darking at the crossover + // point, yet still map pure white to pure white. + + a = 16.0 / 9.0 * (1.0 - fSlope); + + b = fSlope - 0.5 * a; + + c = 1.0 - a - b; + + } + + } + +/*****************************************************************************/ + +real64 dng_function_exposure_tone::Evaluate (real64 x) const + { + + if (!fIsNOP) + { + + if (x <= 0.25) + x = x * fSlope; + + else + x = (a * x + b) * x + c; + + } + + return x; + + } + +/*****************************************************************************/ + +real64 dng_tone_curve_acr3_default::Evaluate (real64 x) const + { + + static const real32 kTable [] = + { + 0.00000f, 0.00078f, 0.00160f, 0.00242f, + 0.00314f, 0.00385f, 0.00460f, 0.00539f, + 0.00623f, 0.00712f, 0.00806f, 0.00906f, + 0.01012f, 0.01122f, 0.01238f, 0.01359f, + 0.01485f, 0.01616f, 0.01751f, 0.01890f, + 0.02033f, 0.02180f, 0.02331f, 0.02485f, + 0.02643f, 0.02804f, 0.02967f, 0.03134f, + 0.03303f, 0.03475f, 0.03648f, 0.03824f, + 0.04002f, 0.04181f, 0.04362f, 0.04545f, + 0.04730f, 0.04916f, 0.05103f, 0.05292f, + 0.05483f, 0.05675f, 0.05868f, 0.06063f, + 0.06259f, 0.06457f, 0.06655f, 0.06856f, + 0.07057f, 0.07259f, 0.07463f, 0.07668f, + 0.07874f, 0.08081f, 0.08290f, 0.08499f, + 0.08710f, 0.08921f, 0.09134f, 0.09348f, + 0.09563f, 0.09779f, 0.09996f, 0.10214f, + 0.10433f, 0.10652f, 0.10873f, 0.11095f, + 0.11318f, 0.11541f, 0.11766f, 0.11991f, + 0.12218f, 0.12445f, 0.12673f, 0.12902f, + 0.13132f, 0.13363f, 0.13595f, 0.13827f, + 0.14061f, 0.14295f, 0.14530f, 0.14765f, + 0.15002f, 0.15239f, 0.15477f, 0.15716f, + 0.15956f, 0.16197f, 0.16438f, 0.16680f, + 0.16923f, 0.17166f, 0.17410f, 0.17655f, + 0.17901f, 0.18148f, 0.18395f, 0.18643f, + 0.18891f, 0.19141f, 0.19391f, 0.19641f, + 0.19893f, 0.20145f, 0.20398f, 0.20651f, + 0.20905f, 0.21160f, 0.21416f, 0.21672f, + 0.21929f, 0.22185f, 0.22440f, 0.22696f, + 0.22950f, 0.23204f, 0.23458f, 0.23711f, + 0.23963f, 0.24215f, 0.24466f, 0.24717f, + 0.24967f, 0.25216f, 0.25465f, 0.25713f, + 0.25961f, 0.26208f, 0.26454f, 0.26700f, + 0.26945f, 0.27189f, 0.27433f, 0.27676f, + 0.27918f, 0.28160f, 0.28401f, 0.28641f, + 0.28881f, 0.29120f, 0.29358f, 0.29596f, + 0.29833f, 0.30069f, 0.30305f, 0.30540f, + 0.30774f, 0.31008f, 0.31241f, 0.31473f, + 0.31704f, 0.31935f, 0.32165f, 0.32395f, + 0.32623f, 0.32851f, 0.33079f, 0.33305f, + 0.33531f, 0.33756f, 0.33981f, 0.34205f, + 0.34428f, 0.34650f, 0.34872f, 0.35093f, + 0.35313f, 0.35532f, 0.35751f, 0.35969f, + 0.36187f, 0.36404f, 0.36620f, 0.36835f, + 0.37050f, 0.37264f, 0.37477f, 0.37689f, + 0.37901f, 0.38112f, 0.38323f, 0.38533f, + 0.38742f, 0.38950f, 0.39158f, 0.39365f, + 0.39571f, 0.39777f, 0.39982f, 0.40186f, + 0.40389f, 0.40592f, 0.40794f, 0.40996f, + 0.41197f, 0.41397f, 0.41596f, 0.41795f, + 0.41993f, 0.42191f, 0.42388f, 0.42584f, + 0.42779f, 0.42974f, 0.43168f, 0.43362f, + 0.43554f, 0.43747f, 0.43938f, 0.44129f, + 0.44319f, 0.44509f, 0.44698f, 0.44886f, + 0.45073f, 0.45260f, 0.45447f, 0.45632f, + 0.45817f, 0.46002f, 0.46186f, 0.46369f, + 0.46551f, 0.46733f, 0.46914f, 0.47095f, + 0.47275f, 0.47454f, 0.47633f, 0.47811f, + 0.47989f, 0.48166f, 0.48342f, 0.48518f, + 0.48693f, 0.48867f, 0.49041f, 0.49214f, + 0.49387f, 0.49559f, 0.49730f, 0.49901f, + 0.50072f, 0.50241f, 0.50410f, 0.50579f, + 0.50747f, 0.50914f, 0.51081f, 0.51247f, + 0.51413f, 0.51578f, 0.51742f, 0.51906f, + 0.52069f, 0.52232f, 0.52394f, 0.52556f, + 0.52717f, 0.52878f, 0.53038f, 0.53197f, + 0.53356f, 0.53514f, 0.53672f, 0.53829f, + 0.53986f, 0.54142f, 0.54297f, 0.54452f, + 0.54607f, 0.54761f, 0.54914f, 0.55067f, + 0.55220f, 0.55371f, 0.55523f, 0.55673f, + 0.55824f, 0.55973f, 0.56123f, 0.56271f, + 0.56420f, 0.56567f, 0.56715f, 0.56861f, + 0.57007f, 0.57153f, 0.57298f, 0.57443f, + 0.57587f, 0.57731f, 0.57874f, 0.58017f, + 0.58159f, 0.58301f, 0.58443f, 0.58583f, + 0.58724f, 0.58864f, 0.59003f, 0.59142f, + 0.59281f, 0.59419f, 0.59556f, 0.59694f, + 0.59830f, 0.59966f, 0.60102f, 0.60238f, + 0.60373f, 0.60507f, 0.60641f, 0.60775f, + 0.60908f, 0.61040f, 0.61173f, 0.61305f, + 0.61436f, 0.61567f, 0.61698f, 0.61828f, + 0.61957f, 0.62087f, 0.62216f, 0.62344f, + 0.62472f, 0.62600f, 0.62727f, 0.62854f, + 0.62980f, 0.63106f, 0.63232f, 0.63357f, + 0.63482f, 0.63606f, 0.63730f, 0.63854f, + 0.63977f, 0.64100f, 0.64222f, 0.64344f, + 0.64466f, 0.64587f, 0.64708f, 0.64829f, + 0.64949f, 0.65069f, 0.65188f, 0.65307f, + 0.65426f, 0.65544f, 0.65662f, 0.65779f, + 0.65897f, 0.66013f, 0.66130f, 0.66246f, + 0.66362f, 0.66477f, 0.66592f, 0.66707f, + 0.66821f, 0.66935f, 0.67048f, 0.67162f, + 0.67275f, 0.67387f, 0.67499f, 0.67611f, + 0.67723f, 0.67834f, 0.67945f, 0.68055f, + 0.68165f, 0.68275f, 0.68385f, 0.68494f, + 0.68603f, 0.68711f, 0.68819f, 0.68927f, + 0.69035f, 0.69142f, 0.69249f, 0.69355f, + 0.69461f, 0.69567f, 0.69673f, 0.69778f, + 0.69883f, 0.69988f, 0.70092f, 0.70196f, + 0.70300f, 0.70403f, 0.70506f, 0.70609f, + 0.70711f, 0.70813f, 0.70915f, 0.71017f, + 0.71118f, 0.71219f, 0.71319f, 0.71420f, + 0.71520f, 0.71620f, 0.71719f, 0.71818f, + 0.71917f, 0.72016f, 0.72114f, 0.72212f, + 0.72309f, 0.72407f, 0.72504f, 0.72601f, + 0.72697f, 0.72794f, 0.72890f, 0.72985f, + 0.73081f, 0.73176f, 0.73271f, 0.73365f, + 0.73460f, 0.73554f, 0.73647f, 0.73741f, + 0.73834f, 0.73927f, 0.74020f, 0.74112f, + 0.74204f, 0.74296f, 0.74388f, 0.74479f, + 0.74570f, 0.74661f, 0.74751f, 0.74842f, + 0.74932f, 0.75021f, 0.75111f, 0.75200f, + 0.75289f, 0.75378f, 0.75466f, 0.75555f, + 0.75643f, 0.75730f, 0.75818f, 0.75905f, + 0.75992f, 0.76079f, 0.76165f, 0.76251f, + 0.76337f, 0.76423f, 0.76508f, 0.76594f, + 0.76679f, 0.76763f, 0.76848f, 0.76932f, + 0.77016f, 0.77100f, 0.77183f, 0.77267f, + 0.77350f, 0.77432f, 0.77515f, 0.77597f, + 0.77680f, 0.77761f, 0.77843f, 0.77924f, + 0.78006f, 0.78087f, 0.78167f, 0.78248f, + 0.78328f, 0.78408f, 0.78488f, 0.78568f, + 0.78647f, 0.78726f, 0.78805f, 0.78884f, + 0.78962f, 0.79040f, 0.79118f, 0.79196f, + 0.79274f, 0.79351f, 0.79428f, 0.79505f, + 0.79582f, 0.79658f, 0.79735f, 0.79811f, + 0.79887f, 0.79962f, 0.80038f, 0.80113f, + 0.80188f, 0.80263f, 0.80337f, 0.80412f, + 0.80486f, 0.80560f, 0.80634f, 0.80707f, + 0.80780f, 0.80854f, 0.80926f, 0.80999f, + 0.81072f, 0.81144f, 0.81216f, 0.81288f, + 0.81360f, 0.81431f, 0.81503f, 0.81574f, + 0.81645f, 0.81715f, 0.81786f, 0.81856f, + 0.81926f, 0.81996f, 0.82066f, 0.82135f, + 0.82205f, 0.82274f, 0.82343f, 0.82412f, + 0.82480f, 0.82549f, 0.82617f, 0.82685f, + 0.82753f, 0.82820f, 0.82888f, 0.82955f, + 0.83022f, 0.83089f, 0.83155f, 0.83222f, + 0.83288f, 0.83354f, 0.83420f, 0.83486f, + 0.83552f, 0.83617f, 0.83682f, 0.83747f, + 0.83812f, 0.83877f, 0.83941f, 0.84005f, + 0.84069f, 0.84133f, 0.84197f, 0.84261f, + 0.84324f, 0.84387f, 0.84450f, 0.84513f, + 0.84576f, 0.84639f, 0.84701f, 0.84763f, + 0.84825f, 0.84887f, 0.84949f, 0.85010f, + 0.85071f, 0.85132f, 0.85193f, 0.85254f, + 0.85315f, 0.85375f, 0.85436f, 0.85496f, + 0.85556f, 0.85615f, 0.85675f, 0.85735f, + 0.85794f, 0.85853f, 0.85912f, 0.85971f, + 0.86029f, 0.86088f, 0.86146f, 0.86204f, + 0.86262f, 0.86320f, 0.86378f, 0.86435f, + 0.86493f, 0.86550f, 0.86607f, 0.86664f, + 0.86720f, 0.86777f, 0.86833f, 0.86889f, + 0.86945f, 0.87001f, 0.87057f, 0.87113f, + 0.87168f, 0.87223f, 0.87278f, 0.87333f, + 0.87388f, 0.87443f, 0.87497f, 0.87552f, + 0.87606f, 0.87660f, 0.87714f, 0.87768f, + 0.87821f, 0.87875f, 0.87928f, 0.87981f, + 0.88034f, 0.88087f, 0.88140f, 0.88192f, + 0.88244f, 0.88297f, 0.88349f, 0.88401f, + 0.88453f, 0.88504f, 0.88556f, 0.88607f, + 0.88658f, 0.88709f, 0.88760f, 0.88811f, + 0.88862f, 0.88912f, 0.88963f, 0.89013f, + 0.89063f, 0.89113f, 0.89163f, 0.89212f, + 0.89262f, 0.89311f, 0.89360f, 0.89409f, + 0.89458f, 0.89507f, 0.89556f, 0.89604f, + 0.89653f, 0.89701f, 0.89749f, 0.89797f, + 0.89845f, 0.89892f, 0.89940f, 0.89987f, + 0.90035f, 0.90082f, 0.90129f, 0.90176f, + 0.90222f, 0.90269f, 0.90316f, 0.90362f, + 0.90408f, 0.90454f, 0.90500f, 0.90546f, + 0.90592f, 0.90637f, 0.90683f, 0.90728f, + 0.90773f, 0.90818f, 0.90863f, 0.90908f, + 0.90952f, 0.90997f, 0.91041f, 0.91085f, + 0.91130f, 0.91173f, 0.91217f, 0.91261f, + 0.91305f, 0.91348f, 0.91392f, 0.91435f, + 0.91478f, 0.91521f, 0.91564f, 0.91606f, + 0.91649f, 0.91691f, 0.91734f, 0.91776f, + 0.91818f, 0.91860f, 0.91902f, 0.91944f, + 0.91985f, 0.92027f, 0.92068f, 0.92109f, + 0.92150f, 0.92191f, 0.92232f, 0.92273f, + 0.92314f, 0.92354f, 0.92395f, 0.92435f, + 0.92475f, 0.92515f, 0.92555f, 0.92595f, + 0.92634f, 0.92674f, 0.92713f, 0.92753f, + 0.92792f, 0.92831f, 0.92870f, 0.92909f, + 0.92947f, 0.92986f, 0.93025f, 0.93063f, + 0.93101f, 0.93139f, 0.93177f, 0.93215f, + 0.93253f, 0.93291f, 0.93328f, 0.93366f, + 0.93403f, 0.93440f, 0.93478f, 0.93515f, + 0.93551f, 0.93588f, 0.93625f, 0.93661f, + 0.93698f, 0.93734f, 0.93770f, 0.93807f, + 0.93843f, 0.93878f, 0.93914f, 0.93950f, + 0.93986f, 0.94021f, 0.94056f, 0.94092f, + 0.94127f, 0.94162f, 0.94197f, 0.94231f, + 0.94266f, 0.94301f, 0.94335f, 0.94369f, + 0.94404f, 0.94438f, 0.94472f, 0.94506f, + 0.94540f, 0.94573f, 0.94607f, 0.94641f, + 0.94674f, 0.94707f, 0.94740f, 0.94774f, + 0.94807f, 0.94839f, 0.94872f, 0.94905f, + 0.94937f, 0.94970f, 0.95002f, 0.95035f, + 0.95067f, 0.95099f, 0.95131f, 0.95163f, + 0.95194f, 0.95226f, 0.95257f, 0.95289f, + 0.95320f, 0.95351f, 0.95383f, 0.95414f, + 0.95445f, 0.95475f, 0.95506f, 0.95537f, + 0.95567f, 0.95598f, 0.95628f, 0.95658f, + 0.95688f, 0.95718f, 0.95748f, 0.95778f, + 0.95808f, 0.95838f, 0.95867f, 0.95897f, + 0.95926f, 0.95955f, 0.95984f, 0.96013f, + 0.96042f, 0.96071f, 0.96100f, 0.96129f, + 0.96157f, 0.96186f, 0.96214f, 0.96242f, + 0.96271f, 0.96299f, 0.96327f, 0.96355f, + 0.96382f, 0.96410f, 0.96438f, 0.96465f, + 0.96493f, 0.96520f, 0.96547f, 0.96574f, + 0.96602f, 0.96629f, 0.96655f, 0.96682f, + 0.96709f, 0.96735f, 0.96762f, 0.96788f, + 0.96815f, 0.96841f, 0.96867f, 0.96893f, + 0.96919f, 0.96945f, 0.96971f, 0.96996f, + 0.97022f, 0.97047f, 0.97073f, 0.97098f, + 0.97123f, 0.97149f, 0.97174f, 0.97199f, + 0.97223f, 0.97248f, 0.97273f, 0.97297f, + 0.97322f, 0.97346f, 0.97371f, 0.97395f, + 0.97419f, 0.97443f, 0.97467f, 0.97491f, + 0.97515f, 0.97539f, 0.97562f, 0.97586f, + 0.97609f, 0.97633f, 0.97656f, 0.97679f, + 0.97702f, 0.97725f, 0.97748f, 0.97771f, + 0.97794f, 0.97817f, 0.97839f, 0.97862f, + 0.97884f, 0.97907f, 0.97929f, 0.97951f, + 0.97973f, 0.97995f, 0.98017f, 0.98039f, + 0.98061f, 0.98082f, 0.98104f, 0.98125f, + 0.98147f, 0.98168f, 0.98189f, 0.98211f, + 0.98232f, 0.98253f, 0.98274f, 0.98295f, + 0.98315f, 0.98336f, 0.98357f, 0.98377f, + 0.98398f, 0.98418f, 0.98438f, 0.98458f, + 0.98478f, 0.98498f, 0.98518f, 0.98538f, + 0.98558f, 0.98578f, 0.98597f, 0.98617f, + 0.98636f, 0.98656f, 0.98675f, 0.98694f, + 0.98714f, 0.98733f, 0.98752f, 0.98771f, + 0.98789f, 0.98808f, 0.98827f, 0.98845f, + 0.98864f, 0.98882f, 0.98901f, 0.98919f, + 0.98937f, 0.98955f, 0.98973f, 0.98991f, + 0.99009f, 0.99027f, 0.99045f, 0.99063f, + 0.99080f, 0.99098f, 0.99115f, 0.99133f, + 0.99150f, 0.99167f, 0.99184f, 0.99201f, + 0.99218f, 0.99235f, 0.99252f, 0.99269f, + 0.99285f, 0.99302f, 0.99319f, 0.99335f, + 0.99351f, 0.99368f, 0.99384f, 0.99400f, + 0.99416f, 0.99432f, 0.99448f, 0.99464f, + 0.99480f, 0.99495f, 0.99511f, 0.99527f, + 0.99542f, 0.99558f, 0.99573f, 0.99588f, + 0.99603f, 0.99619f, 0.99634f, 0.99649f, + 0.99664f, 0.99678f, 0.99693f, 0.99708f, + 0.99722f, 0.99737f, 0.99751f, 0.99766f, + 0.99780f, 0.99794f, 0.99809f, 0.99823f, + 0.99837f, 0.99851f, 0.99865f, 0.99879f, + 0.99892f, 0.99906f, 0.99920f, 0.99933f, + 0.99947f, 0.99960f, 0.99974f, 0.99987f, + 1.00000f + }; + + const uint32 kTableSize = sizeof (kTable ) / + sizeof (kTable [0]); + + real32 y = (real32) x * (real32) (kTableSize - 1); + + int32 index = Pin_int32 (0, (int32) y, kTableSize - 2); + + real32 fract = y - (real32) index; + + return kTable [index ] * (1.0f - fract) + + kTable [index + 1] * ( fract); + + } + +/*****************************************************************************/ + +real64 dng_tone_curve_acr3_default::EvaluateInverse (real64 x) const + { + + static const real32 kTable [] = + { + 0.00000f, 0.00121f, 0.00237f, 0.00362f, + 0.00496f, 0.00621f, 0.00738f, 0.00848f, + 0.00951f, 0.01048f, 0.01139f, 0.01227f, + 0.01312f, 0.01393f, 0.01471f, 0.01547f, + 0.01620f, 0.01692f, 0.01763f, 0.01831f, + 0.01899f, 0.01965f, 0.02030f, 0.02094f, + 0.02157f, 0.02218f, 0.02280f, 0.02340f, + 0.02399f, 0.02458f, 0.02517f, 0.02574f, + 0.02631f, 0.02688f, 0.02744f, 0.02800f, + 0.02855f, 0.02910f, 0.02965f, 0.03019f, + 0.03072f, 0.03126f, 0.03179f, 0.03232f, + 0.03285f, 0.03338f, 0.03390f, 0.03442f, + 0.03493f, 0.03545f, 0.03596f, 0.03647f, + 0.03698f, 0.03749f, 0.03799f, 0.03849f, + 0.03899f, 0.03949f, 0.03998f, 0.04048f, + 0.04097f, 0.04146f, 0.04195f, 0.04244f, + 0.04292f, 0.04341f, 0.04389f, 0.04437f, + 0.04485f, 0.04533f, 0.04580f, 0.04628f, + 0.04675f, 0.04722f, 0.04769f, 0.04816f, + 0.04863f, 0.04910f, 0.04956f, 0.05003f, + 0.05049f, 0.05095f, 0.05141f, 0.05187f, + 0.05233f, 0.05278f, 0.05324f, 0.05370f, + 0.05415f, 0.05460f, 0.05505f, 0.05551f, + 0.05595f, 0.05640f, 0.05685f, 0.05729f, + 0.05774f, 0.05818f, 0.05863f, 0.05907f, + 0.05951f, 0.05995f, 0.06039f, 0.06083f, + 0.06126f, 0.06170f, 0.06214f, 0.06257f, + 0.06301f, 0.06344f, 0.06388f, 0.06431f, + 0.06474f, 0.06517f, 0.06560f, 0.06602f, + 0.06645f, 0.06688f, 0.06731f, 0.06773f, + 0.06815f, 0.06858f, 0.06900f, 0.06943f, + 0.06985f, 0.07027f, 0.07069f, 0.07111f, + 0.07152f, 0.07194f, 0.07236f, 0.07278f, + 0.07319f, 0.07361f, 0.07402f, 0.07444f, + 0.07485f, 0.07526f, 0.07567f, 0.07608f, + 0.07650f, 0.07691f, 0.07732f, 0.07772f, + 0.07813f, 0.07854f, 0.07895f, 0.07935f, + 0.07976f, 0.08016f, 0.08057f, 0.08098f, + 0.08138f, 0.08178f, 0.08218f, 0.08259f, + 0.08299f, 0.08339f, 0.08379f, 0.08419f, + 0.08459f, 0.08499f, 0.08539f, 0.08578f, + 0.08618f, 0.08657f, 0.08697f, 0.08737f, + 0.08776f, 0.08816f, 0.08855f, 0.08894f, + 0.08934f, 0.08973f, 0.09012f, 0.09051f, + 0.09091f, 0.09130f, 0.09169f, 0.09208f, + 0.09247f, 0.09286f, 0.09324f, 0.09363f, + 0.09402f, 0.09440f, 0.09479f, 0.09518f, + 0.09556f, 0.09595f, 0.09633f, 0.09672f, + 0.09710f, 0.09749f, 0.09787f, 0.09825f, + 0.09863f, 0.09901f, 0.09939f, 0.09978f, + 0.10016f, 0.10054f, 0.10092f, 0.10130f, + 0.10167f, 0.10205f, 0.10243f, 0.10281f, + 0.10319f, 0.10356f, 0.10394f, 0.10432f, + 0.10469f, 0.10507f, 0.10544f, 0.10582f, + 0.10619f, 0.10657f, 0.10694f, 0.10731f, + 0.10768f, 0.10806f, 0.10843f, 0.10880f, + 0.10917f, 0.10954f, 0.10991f, 0.11029f, + 0.11066f, 0.11103f, 0.11141f, 0.11178f, + 0.11215f, 0.11253f, 0.11290f, 0.11328f, + 0.11365f, 0.11403f, 0.11440f, 0.11478f, + 0.11516f, 0.11553f, 0.11591f, 0.11629f, + 0.11666f, 0.11704f, 0.11742f, 0.11780f, + 0.11818f, 0.11856f, 0.11894f, 0.11932f, + 0.11970f, 0.12008f, 0.12046f, 0.12084f, + 0.12122f, 0.12161f, 0.12199f, 0.12237f, + 0.12276f, 0.12314f, 0.12352f, 0.12391f, + 0.12429f, 0.12468f, 0.12506f, 0.12545f, + 0.12583f, 0.12622f, 0.12661f, 0.12700f, + 0.12738f, 0.12777f, 0.12816f, 0.12855f, + 0.12894f, 0.12933f, 0.12972f, 0.13011f, + 0.13050f, 0.13089f, 0.13129f, 0.13168f, + 0.13207f, 0.13247f, 0.13286f, 0.13325f, + 0.13365f, 0.13404f, 0.13444f, 0.13483f, + 0.13523f, 0.13563f, 0.13603f, 0.13642f, + 0.13682f, 0.13722f, 0.13762f, 0.13802f, + 0.13842f, 0.13882f, 0.13922f, 0.13962f, + 0.14003f, 0.14043f, 0.14083f, 0.14124f, + 0.14164f, 0.14204f, 0.14245f, 0.14285f, + 0.14326f, 0.14366f, 0.14407f, 0.14448f, + 0.14489f, 0.14530f, 0.14570f, 0.14611f, + 0.14652f, 0.14693f, 0.14734f, 0.14776f, + 0.14817f, 0.14858f, 0.14900f, 0.14941f, + 0.14982f, 0.15024f, 0.15065f, 0.15107f, + 0.15148f, 0.15190f, 0.15232f, 0.15274f, + 0.15316f, 0.15357f, 0.15399f, 0.15441f, + 0.15483f, 0.15526f, 0.15568f, 0.15610f, + 0.15652f, 0.15695f, 0.15737f, 0.15779f, + 0.15822f, 0.15864f, 0.15907f, 0.15950f, + 0.15992f, 0.16035f, 0.16078f, 0.16121f, + 0.16164f, 0.16207f, 0.16250f, 0.16293f, + 0.16337f, 0.16380f, 0.16423f, 0.16467f, + 0.16511f, 0.16554f, 0.16598f, 0.16641f, + 0.16685f, 0.16729f, 0.16773f, 0.16816f, + 0.16860f, 0.16904f, 0.16949f, 0.16993f, + 0.17037f, 0.17081f, 0.17126f, 0.17170f, + 0.17215f, 0.17259f, 0.17304f, 0.17349f, + 0.17393f, 0.17438f, 0.17483f, 0.17528f, + 0.17573f, 0.17619f, 0.17664f, 0.17709f, + 0.17754f, 0.17799f, 0.17845f, 0.17890f, + 0.17936f, 0.17982f, 0.18028f, 0.18073f, + 0.18119f, 0.18165f, 0.18211f, 0.18257f, + 0.18303f, 0.18350f, 0.18396f, 0.18442f, + 0.18489f, 0.18535f, 0.18582f, 0.18629f, + 0.18676f, 0.18723f, 0.18770f, 0.18817f, + 0.18864f, 0.18911f, 0.18958f, 0.19005f, + 0.19053f, 0.19100f, 0.19147f, 0.19195f, + 0.19243f, 0.19291f, 0.19339f, 0.19387f, + 0.19435f, 0.19483f, 0.19531f, 0.19579f, + 0.19627f, 0.19676f, 0.19724f, 0.19773f, + 0.19821f, 0.19870f, 0.19919f, 0.19968f, + 0.20017f, 0.20066f, 0.20115f, 0.20164f, + 0.20214f, 0.20263f, 0.20313f, 0.20362f, + 0.20412f, 0.20462f, 0.20512f, 0.20561f, + 0.20611f, 0.20662f, 0.20712f, 0.20762f, + 0.20812f, 0.20863f, 0.20913f, 0.20964f, + 0.21015f, 0.21066f, 0.21117f, 0.21168f, + 0.21219f, 0.21270f, 0.21321f, 0.21373f, + 0.21424f, 0.21476f, 0.21527f, 0.21579f, + 0.21631f, 0.21683f, 0.21735f, 0.21787f, + 0.21839f, 0.21892f, 0.21944f, 0.21997f, + 0.22049f, 0.22102f, 0.22155f, 0.22208f, + 0.22261f, 0.22314f, 0.22367f, 0.22420f, + 0.22474f, 0.22527f, 0.22581f, 0.22634f, + 0.22688f, 0.22742f, 0.22796f, 0.22850f, + 0.22905f, 0.22959f, 0.23013f, 0.23068f, + 0.23123f, 0.23178f, 0.23232f, 0.23287f, + 0.23343f, 0.23398f, 0.23453f, 0.23508f, + 0.23564f, 0.23620f, 0.23675f, 0.23731f, + 0.23787f, 0.23843f, 0.23899f, 0.23956f, + 0.24012f, 0.24069f, 0.24125f, 0.24182f, + 0.24239f, 0.24296f, 0.24353f, 0.24410f, + 0.24468f, 0.24525f, 0.24582f, 0.24640f, + 0.24698f, 0.24756f, 0.24814f, 0.24872f, + 0.24931f, 0.24989f, 0.25048f, 0.25106f, + 0.25165f, 0.25224f, 0.25283f, 0.25342f, + 0.25401f, 0.25460f, 0.25520f, 0.25579f, + 0.25639f, 0.25699f, 0.25759f, 0.25820f, + 0.25880f, 0.25940f, 0.26001f, 0.26062f, + 0.26122f, 0.26183f, 0.26244f, 0.26306f, + 0.26367f, 0.26429f, 0.26490f, 0.26552f, + 0.26614f, 0.26676f, 0.26738f, 0.26800f, + 0.26863f, 0.26925f, 0.26988f, 0.27051f, + 0.27114f, 0.27177f, 0.27240f, 0.27303f, + 0.27367f, 0.27431f, 0.27495f, 0.27558f, + 0.27623f, 0.27687f, 0.27751f, 0.27816f, + 0.27881f, 0.27945f, 0.28011f, 0.28076f, + 0.28141f, 0.28207f, 0.28272f, 0.28338f, + 0.28404f, 0.28470f, 0.28536f, 0.28602f, + 0.28669f, 0.28736f, 0.28802f, 0.28869f, + 0.28937f, 0.29004f, 0.29071f, 0.29139f, + 0.29207f, 0.29274f, 0.29342f, 0.29410f, + 0.29479f, 0.29548f, 0.29616f, 0.29685f, + 0.29754f, 0.29823f, 0.29893f, 0.29962f, + 0.30032f, 0.30102f, 0.30172f, 0.30242f, + 0.30312f, 0.30383f, 0.30453f, 0.30524f, + 0.30595f, 0.30667f, 0.30738f, 0.30809f, + 0.30881f, 0.30953f, 0.31025f, 0.31097f, + 0.31170f, 0.31242f, 0.31315f, 0.31388f, + 0.31461f, 0.31534f, 0.31608f, 0.31682f, + 0.31755f, 0.31829f, 0.31904f, 0.31978f, + 0.32053f, 0.32127f, 0.32202f, 0.32277f, + 0.32353f, 0.32428f, 0.32504f, 0.32580f, + 0.32656f, 0.32732f, 0.32808f, 0.32885f, + 0.32962f, 0.33039f, 0.33116f, 0.33193f, + 0.33271f, 0.33349f, 0.33427f, 0.33505f, + 0.33583f, 0.33662f, 0.33741f, 0.33820f, + 0.33899f, 0.33978f, 0.34058f, 0.34138f, + 0.34218f, 0.34298f, 0.34378f, 0.34459f, + 0.34540f, 0.34621f, 0.34702f, 0.34783f, + 0.34865f, 0.34947f, 0.35029f, 0.35111f, + 0.35194f, 0.35277f, 0.35360f, 0.35443f, + 0.35526f, 0.35610f, 0.35694f, 0.35778f, + 0.35862f, 0.35946f, 0.36032f, 0.36117f, + 0.36202f, 0.36287f, 0.36372f, 0.36458f, + 0.36545f, 0.36631f, 0.36718f, 0.36805f, + 0.36891f, 0.36979f, 0.37066f, 0.37154f, + 0.37242f, 0.37331f, 0.37419f, 0.37507f, + 0.37596f, 0.37686f, 0.37775f, 0.37865f, + 0.37955f, 0.38045f, 0.38136f, 0.38227f, + 0.38317f, 0.38409f, 0.38500f, 0.38592f, + 0.38684f, 0.38776f, 0.38869f, 0.38961f, + 0.39055f, 0.39148f, 0.39242f, 0.39335f, + 0.39430f, 0.39524f, 0.39619f, 0.39714f, + 0.39809f, 0.39904f, 0.40000f, 0.40097f, + 0.40193f, 0.40289f, 0.40386f, 0.40483f, + 0.40581f, 0.40679f, 0.40777f, 0.40875f, + 0.40974f, 0.41073f, 0.41172f, 0.41272f, + 0.41372f, 0.41472f, 0.41572f, 0.41673f, + 0.41774f, 0.41875f, 0.41977f, 0.42079f, + 0.42181f, 0.42284f, 0.42386f, 0.42490f, + 0.42594f, 0.42697f, 0.42801f, 0.42906f, + 0.43011f, 0.43116f, 0.43222f, 0.43327f, + 0.43434f, 0.43540f, 0.43647f, 0.43754f, + 0.43862f, 0.43970f, 0.44077f, 0.44186f, + 0.44295f, 0.44404f, 0.44514f, 0.44624f, + 0.44734f, 0.44845f, 0.44956f, 0.45068f, + 0.45179f, 0.45291f, 0.45404f, 0.45516f, + 0.45630f, 0.45744f, 0.45858f, 0.45972f, + 0.46086f, 0.46202f, 0.46318f, 0.46433f, + 0.46550f, 0.46667f, 0.46784f, 0.46901f, + 0.47019f, 0.47137f, 0.47256f, 0.47375f, + 0.47495f, 0.47615f, 0.47735f, 0.47856f, + 0.47977f, 0.48099f, 0.48222f, 0.48344f, + 0.48467f, 0.48590f, 0.48714f, 0.48838f, + 0.48963f, 0.49088f, 0.49213f, 0.49340f, + 0.49466f, 0.49593f, 0.49721f, 0.49849f, + 0.49977f, 0.50106f, 0.50236f, 0.50366f, + 0.50496f, 0.50627f, 0.50758f, 0.50890f, + 0.51023f, 0.51155f, 0.51289f, 0.51422f, + 0.51556f, 0.51692f, 0.51827f, 0.51964f, + 0.52100f, 0.52237f, 0.52374f, 0.52512f, + 0.52651f, 0.52790f, 0.52930f, 0.53070f, + 0.53212f, 0.53353f, 0.53495f, 0.53638f, + 0.53781f, 0.53925f, 0.54070f, 0.54214f, + 0.54360f, 0.54506f, 0.54653f, 0.54800f, + 0.54949f, 0.55098f, 0.55247f, 0.55396f, + 0.55548f, 0.55699f, 0.55851f, 0.56003f, + 0.56156f, 0.56310f, 0.56464f, 0.56621f, + 0.56777f, 0.56933f, 0.57091f, 0.57248f, + 0.57407f, 0.57568f, 0.57727f, 0.57888f, + 0.58050f, 0.58213f, 0.58376f, 0.58541f, + 0.58705f, 0.58871f, 0.59037f, 0.59204f, + 0.59373f, 0.59541f, 0.59712f, 0.59882f, + 0.60052f, 0.60226f, 0.60399f, 0.60572f, + 0.60748f, 0.60922f, 0.61099f, 0.61276f, + 0.61455f, 0.61635f, 0.61814f, 0.61996f, + 0.62178f, 0.62361f, 0.62545f, 0.62730f, + 0.62917f, 0.63104f, 0.63291f, 0.63480f, + 0.63671f, 0.63862f, 0.64054f, 0.64249f, + 0.64443f, 0.64638f, 0.64835f, 0.65033f, + 0.65232f, 0.65433f, 0.65633f, 0.65836f, + 0.66041f, 0.66245f, 0.66452f, 0.66660f, + 0.66868f, 0.67078f, 0.67290f, 0.67503f, + 0.67717f, 0.67932f, 0.68151f, 0.68368f, + 0.68587f, 0.68809f, 0.69033f, 0.69257f, + 0.69482f, 0.69709f, 0.69939f, 0.70169f, + 0.70402f, 0.70634f, 0.70869f, 0.71107f, + 0.71346f, 0.71587f, 0.71829f, 0.72073f, + 0.72320f, 0.72567f, 0.72818f, 0.73069f, + 0.73323f, 0.73579f, 0.73838f, 0.74098f, + 0.74360f, 0.74622f, 0.74890f, 0.75159f, + 0.75429f, 0.75704f, 0.75979f, 0.76257f, + 0.76537f, 0.76821f, 0.77109f, 0.77396f, + 0.77688f, 0.77982f, 0.78278f, 0.78579f, + 0.78883f, 0.79187f, 0.79498f, 0.79809f, + 0.80127f, 0.80445f, 0.80767f, 0.81095f, + 0.81424f, 0.81757f, 0.82094f, 0.82438f, + 0.82782f, 0.83133f, 0.83488f, 0.83847f, + 0.84210f, 0.84577f, 0.84951f, 0.85328f, + 0.85713f, 0.86103f, 0.86499f, 0.86900f, + 0.87306f, 0.87720f, 0.88139f, 0.88566f, + 0.89000f, 0.89442f, 0.89891f, 0.90350f, + 0.90818f, 0.91295f, 0.91780f, 0.92272f, + 0.92780f, 0.93299f, 0.93828f, 0.94369f, + 0.94926f, 0.95493f, 0.96082f, 0.96684f, + 0.97305f, 0.97943f, 0.98605f, 0.99291f, + 1.00000f + }; + + const uint32 kTableSize = sizeof (kTable ) / + sizeof (kTable [0]); + + real32 y = (real32) x * (real32) (kTableSize - 1); + + int32 index = Pin_int32 (0, (int32) y, kTableSize - 2); + + real32 fract = y - (real32) index; + + return kTable [index ] * (1.0f - fract) + + kTable [index + 1] * ( fract); + + } + +/*****************************************************************************/ + +const dng_1d_function & dng_tone_curve_acr3_default::Get () + { + + static dng_tone_curve_acr3_default static_dng_tone_curve_acr3_default; + + return static_dng_tone_curve_acr3_default; + + } + +/*****************************************************************************/ + +class dng_render_task: public dng_filter_task + { + + protected: + + const dng_image *fSrcMask; + + const dng_negative &fNegative; + + const dng_render &fParams; + + dng_point fSrcOffset; + + dng_1d_table fZeroOffsetRamp; + + dng_vector fCameraWhite; + dng_matrix fCameraToRGB; + + AutoPtr fHueSatMap; + + dng_1d_table fExposureRamp; + + AutoPtr fLookTable; + + dng_1d_table fToneCurve; + + dng_matrix fRGBtoFinal; + + dng_1d_table fEncodeGamma; + + AutoPtr fHueSatMapEncode; + AutoPtr fHueSatMapDecode; + + AutoPtr fLookTableEncode; + AutoPtr fLookTableDecode; + + AutoPtr fTempBuffer [kMaxMPThreads]; + + AutoPtr fMaskBuffer [kMaxMPThreads]; + + public: + + dng_render_task (const dng_image &srcImage, + const dng_image *srcMask, + dng_image &dstImage, + const dng_negative &negative, + const dng_render ¶ms, + const dng_point &srcOffset); + + virtual dng_rect SrcArea (const dng_rect &dstArea); + + virtual void Start (uint32 threadCount, + const dng_rect &dstArea, + const dng_point &tileSize, + dng_memory_allocator *allocator, + dng_abort_sniffer *sniffer); + + virtual void ProcessArea (uint32 threadIndex, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer); + + }; + +/*****************************************************************************/ + +dng_render_task::dng_render_task (const dng_image &srcImage, + const dng_image *srcMask, + dng_image &dstImage, + const dng_negative &negative, + const dng_render ¶ms, + const dng_point &srcOffset) + + : dng_filter_task ("dng_render_task", + srcImage, + dstImage) + + , fSrcMask (srcMask ) + , fNegative (negative ) + , fParams (params ) + , fSrcOffset (srcOffset) + + , fZeroOffsetRamp () + + , fCameraWhite () + , fCameraToRGB () + + , fHueSatMap () + + , fExposureRamp () + + , fLookTable () + + , fToneCurve () + + , fRGBtoFinal () + + , fEncodeGamma () + + , fHueSatMapEncode () + , fHueSatMapDecode () + + , fLookTableEncode () + , fLookTableDecode () + + { + + fSrcPixelType = ttFloat; + fDstPixelType = ttFloat; + + } + +/*****************************************************************************/ + +dng_rect dng_render_task::SrcArea (const dng_rect &dstArea) + { + + return dstArea + fSrcOffset; + + } + +/*****************************************************************************/ + +void dng_render_task::Start (uint32 threadCount, + const dng_rect &dstArea, + const dng_point &tileSize, + dng_memory_allocator *allocator, + dng_abort_sniffer *sniffer) + { + + dng_filter_task::Start (threadCount, + dstArea, + tileSize, + allocator, + sniffer); + + // Compute zero offset ramp, if any. + + if (fNegative.Stage3BlackLevel ()) + { + + dng_function_zero_offset offsetFunction (fNegative.Stage3BlackLevelNormalized ()); + + fZeroOffsetRamp.Initialize (*allocator, offsetFunction); + + } + + // Compute camera space to linear ProPhoto RGB parameters. + + dng_camera_profile_id profileID; // Default profile ID. + + if (!fNegative.IsMonochrome ()) + { + + AutoPtr spec (fNegative.MakeColorSpec (profileID)); + + if (fParams.WhiteXY ().IsValid ()) + { + + spec->SetWhiteXY (fParams.WhiteXY ()); + + } + + else if (fNegative.HasCameraNeutral ()) + { + + spec->SetWhiteXY (spec->NeutralToXY (fNegative.CameraNeutral ())); + + } + + else if (fNegative.HasCameraWhiteXY ()) + { + + spec->SetWhiteXY (fNegative.CameraWhiteXY ()); + + } + + else + { + + spec->SetWhiteXY (D55_xy_coord ()); + + } + + fCameraWhite = spec->CameraWhite (); + + fCameraToRGB = dng_space_ProPhoto::Get ().MatrixFromPCS () * + spec->CameraToPCS (); + + // Find Hue/Sat table, if any. + + const dng_camera_profile *profile = fNegative.ProfileByID (profileID); + + if (profile) + { + + fHueSatMap.Reset (profile->HueSatMapForWhite (spec->WhiteXY ())); + + if (profile->HasLookTable ()) + { + + fLookTable.Reset (new dng_hue_sat_map (profile->LookTable ())); + + } + + if (profile->HueSatMapEncoding () != encoding_Linear) + { + + BuildHueSatMapEncodingTable (*allocator, + profile->HueSatMapEncoding (), + fHueSatMapEncode, + fHueSatMapDecode, + false); + + } + + if (profile->LookTableEncoding () != encoding_Linear) + { + + BuildHueSatMapEncodingTable (*allocator, + profile->LookTableEncoding (), + fLookTableEncode, + fLookTableDecode, + false); + + } + + } + + } + + // Compute exposure/shadows ramp. + + real64 exposure = fParams.Exposure () + + fNegative.TotalBaselineExposure (profileID) - + (log (fNegative.Stage3Gain ()) / log (2.0)); + + { + + real64 white = 1.0 / pow (2.0, Max_real64 (0.0, exposure)); + + real64 black = fParams.Shadows () * + fNegative.ShadowScale () * + fNegative.Stage3Gain () * + 0.001; + + black = Min_real64 (black, 0.99 * white); + + dng_function_exposure_ramp rampFunction (white, + black, + black); + + fExposureRamp.Initialize (*allocator, rampFunction); + + } + + // Compute tone curve. + + { + + // If there is any negative exposure compenation to perform + // (beyond what the camera provides for with its baseline exposure), + // we fake this by darkening the tone curve. + + dng_function_exposure_tone exposureTone (exposure); + + dng_1d_concatenate totalTone (exposureTone, + fParams.ToneCurve ()); + + fToneCurve.Initialize (*allocator, totalTone); + + } + + // Compute linear ProPhoto RGB to final space parameters. + + { + + const dng_color_space &finalSpace = fParams.FinalSpace (); + + fRGBtoFinal = finalSpace.MatrixFromPCS () * + dng_space_ProPhoto::Get ().MatrixToPCS (); + + fEncodeGamma.Initialize (*allocator, finalSpace.GammaFunction ()); + + } + + // Allocate temp buffer to hold one row of RGB data. + + uint32 tempBufferSize = 0; + + if (!SafeUint32Mult (tileSize.h, (uint32) sizeof (real32), &tempBufferSize) || + !SafeUint32Mult (tempBufferSize, 3, &tempBufferSize)) + { + + ThrowOverflow ("Arithmetic overflow computing buffer size."); + + } + + for (uint32 threadIndex = 0; threadIndex < threadCount; threadIndex++) + { + + fTempBuffer [threadIndex] . Reset (allocator->Allocate (tempBufferSize)); + + } + + // Allocate mask buffer to hold one tile of mask data, if needed. + + if (fSrcMask) + { + + uint32 maskBufferSize = 0; + + if (!SafeUint32Mult (tileSize.h, tileSize.v, &maskBufferSize) || + !SafeUint32Mult (maskBufferSize, (uint32) sizeof (real32), &maskBufferSize)) + { + + ThrowOverflow ("Arithmetic overflow computing buffer size."); + + } + + for (uint32 threadIndex = 0; threadIndex < threadCount; threadIndex++) + { + + fMaskBuffer [threadIndex] . Reset (allocator->Allocate (maskBufferSize)); + + } + + } + + } + +/*****************************************************************************/ + +void dng_render_task::ProcessArea (uint32 threadIndex, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer) + { + + dng_rect srcArea = srcBuffer.fArea; + dng_rect dstArea = dstBuffer.fArea; + + uint32 srcCols = srcArea.W (); + + real32 *tPtrR = fTempBuffer [threadIndex]->Buffer_real32 (); + + real32 *tPtrG = tPtrR + srcCols; + real32 *tPtrB = tPtrG + srcCols; + + dng_pixel_buffer maskBuffer; + + if (fSrcMask) + { + + maskBuffer.fArea = srcArea; + maskBuffer.fPlane = 0; + maskBuffer.fPlanes = 1; + maskBuffer.fRowStep = srcArea.W (); + maskBuffer.fColStep = 1; + maskBuffer.fPlaneStep = 0; + maskBuffer.fPixelType = ttFloat; + maskBuffer.fPixelSize = sizeof (real32); + maskBuffer.fData = fMaskBuffer [threadIndex]->Buffer_real32 (); + maskBuffer.fDirty = true; + + fSrcMask->Get (maskBuffer); + + } + + for (int32 srcRow = srcArea.t; srcRow < srcArea.b; srcRow++) + { + + if (fNegative.Stage3BlackLevel ()) + { + + for (uint32 plane = 0; plane < fSrcPlanes; plane++) + { + + real32 *sPtr = (real32 *) + srcBuffer.DirtyPixel (srcRow, + srcArea.l, + plane); + + DoBaseline1DTable (sPtr, + sPtr, + srcCols, + fZeroOffsetRamp); + + } + + } + + // First convert from camera native space to linear PhotoRGB, + // applying the white balance and camera profile. + + { + + const real32 *sPtrA = (const real32 *) + srcBuffer.ConstPixel (srcRow, + srcArea.l, + 0); + + if (fSrcPlanes == 1) + { + + // For monochrome cameras, this just requires copying + // the data into all three color channels. + + DoCopyBytes (sPtrA, tPtrR, srcCols * (uint32) sizeof (real32)); + DoCopyBytes (sPtrA, tPtrG, srcCols * (uint32) sizeof (real32)); + DoCopyBytes (sPtrA, tPtrB, srcCols * (uint32) sizeof (real32)); + + } + + else + { + + const real32 *sPtrB = sPtrA + srcBuffer.fPlaneStep; + const real32 *sPtrC = sPtrB + srcBuffer.fPlaneStep; + + if (fSrcPlanes == 3) + { + + DoBaselineABCtoRGB (sPtrA, + sPtrB, + sPtrC, + tPtrR, + tPtrG, + tPtrB, + srcCols, + fCameraWhite, + fCameraToRGB); + + } + + else + { + + const real32 *sPtrD = sPtrC + srcBuffer.fPlaneStep; + + DoBaselineABCDtoRGB (sPtrA, + sPtrB, + sPtrC, + sPtrD, + tPtrR, + tPtrG, + tPtrB, + srcCols, + fCameraWhite, + fCameraToRGB); + + } + + // Apply Hue/Sat map, if any. + + if (fHueSatMap.Get ()) + { + + DoBaselineHueSatMap (tPtrR, + tPtrG, + tPtrB, + tPtrR, + tPtrG, + tPtrB, + srcCols, + *fHueSatMap.Get (), + fHueSatMapEncode.Get (), + fHueSatMapDecode.Get ()); + + } + + } + + } + + // Apply exposure curve. + + DoBaseline1DTable (tPtrR, + tPtrR, + srcCols, + fExposureRamp); + + DoBaseline1DTable (tPtrG, + tPtrG, + srcCols, + fExposureRamp); + + DoBaseline1DTable (tPtrB, + tPtrB, + srcCols, + fExposureRamp); + + // Apply look table, if any. + + if (fLookTable.Get ()) + { + + DoBaselineHueSatMap (tPtrR, + tPtrG, + tPtrB, + tPtrR, + tPtrG, + tPtrB, + srcCols, + *fLookTable.Get (), + fLookTableEncode.Get (), + fLookTableDecode.Get ()); + + } + + // Apply baseline tone curve. + + DoBaselineRGBTone (tPtrR, + tPtrG, + tPtrB, + tPtrR, + tPtrG, + tPtrB, + srcCols, + fToneCurve); + + // Convert to final color space. + + int32 dstRow = srcRow + (dstArea.t - srcArea.t); + + if (fDstPlanes == 1) + { + + real32 *dPtrG = dstBuffer.DirtyPixel_real32 (dstRow, + dstArea.l, + 0); + + DoBaselineRGBtoGray (tPtrR, + tPtrG, + tPtrB, + dPtrG, + srcCols, + fRGBtoFinal); + + DoBaseline1DTable (dPtrG, + dPtrG, + srcCols, + fEncodeGamma); + + } + + else + { + + real32 *dPtrR = dstBuffer.DirtyPixel_real32 (dstRow, + dstArea.l, + 0); + + real32 *dPtrG = dPtrR + dstBuffer.fPlaneStep; + real32 *dPtrB = dPtrG + dstBuffer.fPlaneStep; + + DoBaselineRGBtoRGB (tPtrR, + tPtrG, + tPtrB, + dPtrR, + dPtrG, + dPtrB, + srcCols, + fRGBtoFinal); + + DoBaseline1DTable (dPtrR, + dPtrR, + srcCols, + fEncodeGamma); + + DoBaseline1DTable (dPtrG, + dPtrG, + srcCols, + fEncodeGamma); + + DoBaseline1DTable (dPtrB, + dPtrB, + srcCols, + fEncodeGamma); + + } + + if (fSrcMask) + { + + const real32 *mPtr = maskBuffer.ConstPixel_real32 (srcRow, + srcArea.l, + 0); + + for (uint32 dstPlane = 0; dstPlane < fDstPlanes; dstPlane++) + { + + real32 *dPtr = dstBuffer.DirtyPixel_real32 (dstRow, + dstArea.l, + dstPlane); + + for (uint32 col = 0; col < srcCols; col++) + { + + // White Matte + + dPtr [col] = 1.0f - (1.0f - dPtr [col]) * mPtr [col]; + + } + + } + + } + + } + + } + +/*****************************************************************************/ + +dng_render::dng_render (dng_host &host, + const dng_negative &negative) + + : fHost (host) + , fNegative (negative) + + , fWhiteXY () + + , fExposure (0.0) + , fShadows (5.0) + + , fToneCurve (&dng_tone_curve_acr3_default::Get ()) + + , fFinalSpace (&dng_space_sRGB::Get ()) + , fFinalPixelType (ttByte) + + , fMaximumSize (0) + + , fProfileToneCurve () + + { + + // Switch to NOP default parameters for non-scene referred data. + + if (fNegative.ColorimetricReference () != crSceneReferred) + { + + fShadows = 0.0; + + fToneCurve = &dng_1d_identity::Get (); + + } + + // Use default tone curve from profile if any. + + const dng_camera_profile *profile = fNegative.ProfileByID (dng_camera_profile_id ()); + + if (profile && profile->ToneCurve ().IsValid ()) + { + + fProfileToneCurve.Reset (new dng_spline_solver); + + profile->ToneCurve ().Solve (*fProfileToneCurve.Get ()); + + fToneCurve = fProfileToneCurve.Get (); + + } + + // Turn off default shadow mapping if requested by profile. + + if (profile && (profile->DefaultBlackRender () == defaultBlackRender_None)) + { + + fShadows = 0.0; + + } + + } + +/*****************************************************************************/ + +dng_image * dng_render::Render () + { + + const dng_image *srcImage = fNegative.Stage3Image (); + + const dng_image *srcMask = fNegative.TransparencyMask (); + + dng_rect srcBounds = fNegative.DefaultCropArea (); + + dng_point dstSize; + + dstSize.h = fNegative.DefaultFinalWidth (); + dstSize.v = fNegative.DefaultFinalHeight (); + + if (MaximumSize ()) + { + + if (Max_uint32 (dstSize.h, dstSize.v) > MaximumSize ()) + { + + real64 ratio = fNegative.AspectRatio (); + + if (ratio >= 1.0) + { + dstSize.h = MaximumSize (); + dstSize.v = Max_uint32 (1, Round_uint32 (dstSize.h / ratio)); + } + + else + { + dstSize.v = MaximumSize (); + dstSize.h = Max_uint32 (1, Round_uint32 (dstSize.v * ratio)); + } + + } + + } + + AutoPtr tempImage; + + AutoPtr tempMask; + + if (srcBounds.Size () != dstSize) + { + + tempImage.Reset (fHost.Make_dng_image (dstSize, + srcImage->Planes (), + srcImage->PixelType ())); + + ResampleImage (fHost, + *srcImage, + *tempImage.Get (), + srcBounds, + tempImage->Bounds (), + dng_resample_bicubic::Get ()); + + if (srcMask != NULL) + { + + tempMask.Reset (fHost.Make_dng_image (dstSize, + srcMask->Planes (), + srcMask->PixelType ())); + + ResampleImage (fHost, + *srcMask, + *tempMask.Get (), + srcBounds, + tempMask->Bounds (), + dng_resample_bicubic::Get ()); + + srcMask = tempMask.Get (); + + } + + srcImage = tempImage.Get (); + + srcBounds = tempImage->Bounds (); + + } + + uint32 dstPlanes = FinalSpace ().IsMonochrome () ? 1 : 3; + + AutoPtr dstImage (fHost.Make_dng_image (srcBounds.Size (), + dstPlanes, + FinalPixelType ())); + + dng_render_task task (*srcImage, + srcMask, + *dstImage.Get (), + fNegative, + *this, + srcBounds.TL ()); + + fHost.PerformAreaTask (task, + dstImage->Bounds ()); + + return dstImage.Release (); + + } + +/*****************************************************************************/ diff --git a/dng/dng_render.h b/dng/dng_render.h new file mode 100644 index 0000000..57180b2 --- /dev/null +++ b/dng/dng_render.h @@ -0,0 +1,321 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Classes for conversion of RAW data to final image. + */ + +/*****************************************************************************/ + +#ifndef __dng_render__ +#define __dng_render__ + +/*****************************************************************************/ + +#include "dng_1d_function.h" +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_spline.h" +#include "dng_uncopyable.h" +#include "dng_xy_coord.h" + +/******************************************************************************/ + +/// \brief Curve for removing zero offset from stage3 image. + +class dng_function_zero_offset: public dng_1d_function + { + + public: + + real64 fZeroOffset; + + real64 fScale; + + public: + + dng_function_zero_offset (real64 zeroOffset); + + virtual real64 Evaluate (real64 x) const; + + }; + +/******************************************************************************/ + +/// \brief Curve for pre-exposure-compensation adjustment based on noise floor, +/// shadows, and highlight level. + +class dng_function_exposure_ramp: public dng_1d_function + { + + public: + + real64 fSlope; // Slope of straight segment. + + real64 fBlack; // Intercept of straight segment. + + real64 fRadius; // Rounding radius. + + real64 fQScale; // Quadradic scale. + + public: + + dng_function_exposure_ramp (real64 white, + real64 black, + real64 minBlack); + + virtual real64 Evaluate (real64 x) const; + + }; + +/******************************************************************************/ + +/// \brief Exposure compensation curve for a given compensation amount in stops using +/// quadric for roll-off. + +class dng_function_exposure_tone: public dng_1d_function + { + + protected: + + bool fIsNOP; // Is this a NOP function? + + real64 fSlope; // Slope for lower part of curve. + + real64 a; // Quadradic parameters for upper two f-stops. + real64 b; + real64 c; + + public: + + dng_function_exposure_tone (real64 exposure); + + /// Returns output value for a given input tone. + + virtual real64 Evaluate (real64 x) const; + + }; + +/*****************************************************************************/ + +/// Default ACR3 tone curve. + +class dng_tone_curve_acr3_default: public dng_1d_function + { + + public: + + /// Returns output value for a given input tone. + + virtual real64 Evaluate (real64 x) const; + + /// Returns nearest input value for a given output tone. + + virtual real64 EvaluateInverse (real64 x) const; + + static const dng_1d_function & Get (); + + }; + +/*****************************************************************************/ + +/// \brief Encoding gamma curve for a given color space. + +class dng_function_gamma_encode: public dng_1d_function + { + + protected: + + const dng_color_space &fSpace; + + public: + + dng_function_gamma_encode (const dng_color_space &space); + + virtual real64 Evaluate (real64 x) const; + + }; + +/*****************************************************************************/ + +/// \brief Class used to render digital negative to displayable image. + +class dng_render: private dng_uncopyable + { + + protected: + + dng_host &fHost; + + const dng_negative &fNegative; + + dng_xy_coord fWhiteXY; + + real64 fExposure; + + real64 fShadows; + + const dng_1d_function *fToneCurve; + + const dng_color_space *fFinalSpace; + + uint32 fFinalPixelType; + + uint32 fMaximumSize; + + private: + + AutoPtr fProfileToneCurve; + + public: + + /// Construct a rendering instance that will be used to convert a given digital negative. + /// \param host The host to use for memory allocation, progress updates, and abort testing. + /// \param negative The digital negative to convert to a displayable image. + + dng_render (dng_host &host, + const dng_negative &negative); + + virtual ~dng_render () + { + } + + /// Set the white point to be used for conversion. + /// \param white White point to use. + + void SetWhiteXY (const dng_xy_coord &white) + { + fWhiteXY = white; + } + + /// Get the white point to be used for conversion. + /// \retval White point to use. + + const dng_xy_coord WhiteXY () const + { + return fWhiteXY; + } + + /// Set exposure compensation. + /// \param exposure Compensation value in stops, positive or negative. + + void SetExposure (real64 exposure) + { + fExposure = exposure; + } + + /// Get exposure compensation. + /// \retval Compensation value in stops, positive or negative. + + real64 Exposure () const + { + return fExposure; + } + + /// Set shadow clip amount. + /// \param shadows Shadow clip amount. + + void SetShadows (real64 shadows) + { + fShadows = shadows; + } + + /// Get shadow clip amount. + /// \retval Shadow clip amount. + + real64 Shadows () const + { + return fShadows; + } + + /// Set custom tone curve for conversion. + /// \param curve 1D function that defines tone mapping to use during conversion. + + void SetToneCurve (const dng_1d_function &curve) + { + fToneCurve = &curve; + } + + /// Get custom tone curve for conversion. + /// \retval 1D function that defines tone mapping to use during conversion. + + const dng_1d_function & ToneCurve () const + { + return *fToneCurve; + } + + /// Set final color space in which resulting image data should be represented. + /// (See dng_color_space.h for possible values.) + /// \param space Color space to use. + + void SetFinalSpace (const dng_color_space &space) + { + fFinalSpace = &space; + } + + /// Get final color space in which resulting image data should be represented. + /// \retval Color space to use. + + const dng_color_space & FinalSpace () const + { + return *fFinalSpace; + } + + /// Set pixel type of final image data. + /// Can be ttByte (default), ttShort, or ttFloat. + /// \param type Pixel type to use. + + void SetFinalPixelType (uint32 type) + { + fFinalPixelType = type; + } + + /// Get pixel type of final image data. + /// Can be ttByte (default), ttShort, or ttFloat. + /// \retval Pixel type to use. + + uint32 FinalPixelType () const + { + return fFinalPixelType; + } + + /// Set maximum dimension, in pixels, of resulting image. + /// If final image would have either dimension larger than maximum, the larger + /// of the two dimensions is set to this maximum size and the smaller dimension + /// is adjusted to preserve aspect ratio. + /// \param size Maximum size to allow. + + void SetMaximumSize (uint32 size) + { + fMaximumSize = size; + } + + /// Get maximum dimension, in pixels, of resulting image. + /// If the final image would have either dimension larger than this maximum, the larger + /// of the two dimensions is set to this maximum size and the smaller dimension + /// is adjusted to preserve the image's aspect ratio. + /// \retval Maximum allowed size. + + uint32 MaximumSize () const + { + return fMaximumSize; + } + + /// Actually render a digital negative to a displayable image. + /// Input digital negative is passed to the constructor of this dng_render class. + /// \retval The final resulting image. + + virtual dng_image * Render (); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_resample.cpp b/dng/dng_resample.cpp new file mode 100644 index 0000000..5d7aead --- /dev/null +++ b/dng/dng_resample.cpp @@ -0,0 +1,841 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_resample.h" + +#include "dng_assertions.h" +#include "dng_bottlenecks.h" +#include "dng_filter_task.h" +#include "dng_host.h" +#include "dng_image.h" +#include "dng_memory.h" +#include "dng_pixel_buffer.h" +#include "dng_safe_arithmetic.h" +#include "dng_tag_types.h" +#include "dng_utils.h" + +/******************************************************************************/ + +real64 dng_resample_bicubic::Extent () const + { + + return 2.0; + + } + +/******************************************************************************/ + +real64 dng_resample_bicubic::Evaluate (real64 x) const + { + + const real64 A = -0.75; + + x = Abs_real64 (x); + + if (x >= 2.0) + return 0.0; + + else if (x >= 1.0) + return (((A * x - 5.0 * A) * x + 8.0 * A) * x - 4.0 * A); + + else + return (((A + 2.0) * x - (A + 3.0)) * x * x + 1.0); + + } + +/******************************************************************************/ + +const dng_resample_function & dng_resample_bicubic::Get () + { + + static dng_resample_bicubic static_dng_resample_bicubic; + + return static_dng_resample_bicubic; + + } + +/*****************************************************************************/ + +dng_resample_coords::dng_resample_coords () + + : fOrigin (0) + , fCoords () + + { + + } + +/*****************************************************************************/ + +dng_resample_coords::~dng_resample_coords () + { + + } + +/*****************************************************************************/ + +void dng_resample_coords::Initialize (int32 srcOrigin, + int32 dstOrigin, + uint32 srcCount, + uint32 dstCount, + dng_memory_allocator &allocator) + { + + fOrigin = dstOrigin; + + uint32 dstEntries = 0; + uint32 bufferSize = 0; + + if (!RoundUpUint32ToMultiple (dstCount, 8, &dstEntries) || + !SafeUint32Mult (dstEntries, sizeof (int32), &bufferSize)) + { + + ThrowOverflow ("Arithmetic overflow computing size for coordinate " + "buffer"); + + } + + fCoords.Reset (allocator.Allocate (bufferSize)); + + int32 *coords = fCoords->Buffer_int32 (); + + real64 invScale = (real64) srcCount / + (real64) dstCount; + + for (uint32 j = 0; j < dstCount; j++) + { + + real64 x = (real64) j + 0.5; + + real64 y = x * invScale - 0.5 + (real64) srcOrigin; + + coords [j] = Round_int32 (y * (real64) kResampleSubsampleCount); + + } + + // Pad out table by replicating last entry. + + for (uint32 k = dstCount; k < dstEntries; k++) + { + + coords [k] = coords [dstCount - 1]; + + } + + } + +/*****************************************************************************/ + +dng_resample_weights::dng_resample_weights () + + : fRadius (0) + + , fWeightStep (0) + + , fWeights32 () + , fWeights16 () + + { + + } + +/*****************************************************************************/ + +dng_resample_weights::~dng_resample_weights () + { + + } + +/*****************************************************************************/ + +void dng_resample_weights::Initialize (real64 scale, + const dng_resample_function &kernel, + dng_memory_allocator &allocator) + { + + uint32 j; + + // We only adjust the kernel size for scale factors less than 1.0. + + scale = Min_real64 (scale, 1.0); + + // Find radius of this kernel. + + fRadius = (uint32) (kernel.Extent () / scale + 0.9999); + + // Width is twice the radius. + + uint32 width = fRadius * 2; + + // Round to each set to weights to a multiple of 8 entries. + + if (!RoundUpUint32ToMultiple (width, 8, &fWeightStep)) + { + + ThrowOverflow ("Arithmetic overflow computing fWeightStep"); + + } + + // Allocate and zero weight tables. + + uint32 bufferSize = 0; + + if (!SafeUint32Mult (fWeightStep, kResampleSubsampleCount, &bufferSize) || + !SafeUint32Mult (bufferSize, (uint32) sizeof (real32), &bufferSize)) + { + + ThrowOverflow ("Arithmetic overflow computing buffer size."); + + } + + fWeights32.Reset (allocator.Allocate (bufferSize)); + + DoZeroBytes (fWeights32->Buffer (), + fWeights32->LogicalSize ()); + + if (!SafeUint32Mult (fWeightStep, kResampleSubsampleCount, &bufferSize) || + !SafeUint32Mult (bufferSize, (uint32) sizeof (int16), &bufferSize)) + { + + ThrowOverflow ("Arithmetic overflow computing buffer size."); + + } + + fWeights16.Reset (allocator.Allocate (bufferSize)); + + DoZeroBytes (fWeights16->Buffer (), + fWeights16->LogicalSize ()); + + // Compute kernel for each subsample values. + + for (uint32 sample = 0; sample < kResampleSubsampleCount; sample++) + { + + real64 fract = sample * (1.0 / (real64) kResampleSubsampleCount); + + real32 *w32 = fWeights32->Buffer_real32 () + fWeightStep * sample; + + // Evaluate kernel function for 32 bit weights. + + { + + real64 t32 = 0.0; + + for (j = 0; j < width; j++) + { + + int32 k = (int32) j - (int32) fRadius + 1; + + real64 x = (k - fract) * scale; + + w32 [j] = (real32) kernel.Evaluate (x); + + t32 += w32 [j]; + + } + + // Scale 32 bit weights so total of weights is 1.0. + + real32 s32 = (real32) (1.0 / t32); + + for (j = 0; j < width; j++) + { + + w32 [j] *= s32; + + } + + } + + // Round off 32 bit weights to 16 bit weights. + + { + + int16 *w16 = fWeights16->Buffer_int16 () + fWeightStep * sample; + + int32 t16 = 0; + + for (j = 0; j < width; j++) + { + + w16 [j] = (int16) Round_int32 (w32 [j] * 16384.0); + + t16 += w16 [j]; + + } + + // Adjust center entry for any round off error so total is + // exactly 16384. + + w16 [fRadius - (fract >= 0.5 ? 0 : 1)] += (int16) (16384 - t16); + + } + + } + + } + +/*****************************************************************************/ + +dng_resample_weights_2d::dng_resample_weights_2d () + + : fRadius (0) + + , fRowStep (0) + , fColStep (0) + + , fWeights32 () + , fWeights16 () + + { + + } + +/*****************************************************************************/ + +dng_resample_weights_2d::~dng_resample_weights_2d () + { + + } + +/*****************************************************************************/ + +void dng_resample_weights_2d::Initialize (const dng_resample_function &kernel, + dng_memory_allocator &allocator) + { + + // Find radius of this kernel. Unlike with 1d resample weights (see + // dng_resample_weights), we never scale up the kernel size. + + fRadius = (uint32) (kernel.Extent () + 0.9999); + + // Width is twice the radius. + + uint32 width = 0; + uint32 widthSqr = 0; + uint32 step = 0; + + if (!SafeUint32Mult (fRadius, 2, &width) || + !SafeUint32Mult (width, width, &widthSqr) || + !RoundUpUint32ToMultiple (widthSqr, 8, &step) || + !SafeUint32Mult (step, kResampleSubsampleCount2D, &fRowStep)) + { + + ThrowOverflow ("Arithmetic overflow computing row step."); + + } + + fColStep = step; + + // Allocate and zero weight tables. + + uint32 bufferSize = 0; + + if (!SafeUint32Mult (step, kResampleSubsampleCount2D, &bufferSize) || + !SafeUint32Mult (bufferSize, kResampleSubsampleCount2D, &bufferSize) || + !SafeUint32Mult (bufferSize, (uint32) sizeof (real32), &bufferSize)) + { + + ThrowOverflow ("Arithmetic overflow computing buffer size."); + + } + + fWeights32.Reset (allocator.Allocate (bufferSize)); + + DoZeroBytes (fWeights32->Buffer (), + fWeights32->LogicalSize ()); + + if (!SafeUint32Mult (step, kResampleSubsampleCount2D, &bufferSize) || + !SafeUint32Mult (bufferSize, kResampleSubsampleCount2D, &bufferSize) || + !SafeUint32Mult (bufferSize, (uint32) sizeof (int16), &bufferSize)) + { + + ThrowOverflow ("Arithmetic overflow computing buffer size."); + + } + + fWeights16.Reset (allocator.Allocate (bufferSize)); + + DoZeroBytes (fWeights16->Buffer (), + fWeights16->LogicalSize ()); + + // Compute kernel for each subsample values. + + for (uint32 y = 0; y < kResampleSubsampleCount2D; y++) + { + + real64 yFract = y * (1.0 / (real64) kResampleSubsampleCount2D); + + for (uint32 x = 0; x < kResampleSubsampleCount2D; x++) + { + + real64 xFract = x * (1.0 / (real64) kResampleSubsampleCount2D); + + real32 *w32 = (real32 *) Weights32 (dng_point ((int32) y, + (int32) x)); + + // Evaluate kernel function for 32 bit weights. + + { + + real64 t32 = 0.0; + + uint32 index = 0; + + for (uint32 i = 0; i < width; i++) + { + + int32 yInt = ((int32) i) - (int32) fRadius + 1; + real64 yPos = yInt - yFract; + + for (uint32 j = 0; j < width; j++) + { + + int32 xInt = ((int32) j) - (int32) fRadius + 1; + real64 xPos = xInt - xFract; + + #if 0 + + // Radial. + + real64 dy2 = yPos * yPos; + real64 dx2 = xPos * xPos; + + real64 r = sqrt (dx2 + dy2); + + w32 [index] = (real32) kernel.Evaluate (r); + + #else + + // Separable. + + w32 [index] = (real32) kernel.Evaluate (xPos) * + (real32) kernel.Evaluate (yPos); + + #endif + + t32 += w32 [index]; + + index++; + + } + + } + + // Scale 32 bit weights so total of weights is 1.0. + + const real32 s32 = (real32) (1.0 / t32); + + for (uint32 i = 0; i < widthSqr; i++) + { + + w32 [i] *= s32; + + } + + } + + // Round off 32 bit weights to 16 bit weights. + + { + + int16 *w16 = (int16 *) Weights16 (dng_point ((int32) y, + (int32) x)); + + int32 t16 = 0; + + for (uint32 j = 0; j < widthSqr; j++) + { + + w16 [j] = (int16) Round_int32 (w32 [j] * 16384.0); + + t16 += w16 [j]; + + } + + // Adjust one of the center entries for any round off error so total + // is exactly 16384. + + const uint32 xOffset = fRadius - ((xFract >= 0.5) ? 0 : 1); + const uint32 yOffset = fRadius - ((yFract >= 0.5) ? 0 : 1); + const uint32 centerOffset = width * yOffset + xOffset; + + w16 [centerOffset] += (int16) (16384 - t16); + + } + + } + + } + + } + +/*****************************************************************************/ + +class dng_resample_task: public dng_filter_task + { + + protected: + + dng_rect fSrcBounds; + dng_rect fDstBounds; + + const dng_resample_function &fKernel; + + real64 fRowScale; + real64 fColScale; + + dng_resample_coords fRowCoords; + dng_resample_coords fColCoords; + + dng_resample_weights fWeightsV; + dng_resample_weights fWeightsH; + + dng_point fSrcTileSize; + + AutoPtr fTempBuffer [kMaxMPThreads]; + + public: + + dng_resample_task (const dng_image &srcImage, + dng_image &dstImage, + const dng_rect &srcBounds, + const dng_rect &dstBounds, + const dng_resample_function &kernel); + + virtual dng_rect SrcArea (const dng_rect &dstArea); + + virtual dng_point SrcTileSize (const dng_point &dstTileSize); + + virtual void Start (uint32 threadCount, + const dng_rect &dstArea, + const dng_point &tileSize, + dng_memory_allocator *allocator, + dng_abort_sniffer *sniffer); + + virtual void ProcessArea (uint32 threadIndex, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer); + + }; + +/*****************************************************************************/ + +dng_resample_task::dng_resample_task (const dng_image &srcImage, + dng_image &dstImage, + const dng_rect &srcBounds, + const dng_rect &dstBounds, + const dng_resample_function &kernel) + + : dng_filter_task ("dng_resample_task", + srcImage, + dstImage) + + , fSrcBounds (srcBounds) + , fDstBounds (dstBounds) + + , fKernel (kernel) + + , fRowScale (dstBounds.H () / (real64) srcBounds.H ()) + , fColScale (dstBounds.W () / (real64) srcBounds.W ()) + + , fRowCoords () + , fColCoords () + + , fWeightsV () + , fWeightsH () + + , fSrcTileSize () + + { + + if (srcImage.PixelSize () <= 2 && + dstImage.PixelSize () <= 2 && + srcImage.PixelRange () == dstImage.PixelRange ()) + { + fSrcPixelType = ttShort; + fDstPixelType = ttShort; + } + + else + { + fSrcPixelType = ttFloat; + fDstPixelType = ttFloat; + } + + fUnitCell = dng_point (8, 8); + + fMaxTileSize.v = Pin_int32 (fUnitCell.v, + Round_int32 (fMaxTileSize.v * fRowScale), + fMaxTileSize.v); + + fMaxTileSize.h = Pin_int32 (fUnitCell.h, + Round_int32 (fMaxTileSize.h * fColScale), + fMaxTileSize.h); + + } + +/*****************************************************************************/ + +DNG_ATTRIB_NO_SANITIZE("unsigned-integer-overflow") +dng_rect dng_resample_task::SrcArea (const dng_rect &dstArea) + { + + int32 offsetV = fWeightsV.Offset (); + int32 offsetH = fWeightsH.Offset (); + + uint32 widthV = fWeightsV.Width (); + uint32 widthH = fWeightsH.Width (); + + dng_rect srcArea; + + srcArea.t = fRowCoords.Pixel (dstArea.t) + offsetV; + srcArea.l = fColCoords.Pixel (dstArea.l) + offsetH; + + srcArea.b = fRowCoords.Pixel (dstArea.b - 1) + offsetV + widthV; + srcArea.r = fColCoords.Pixel (dstArea.r - 1) + offsetH + widthH; + + return srcArea; + + } + +/*****************************************************************************/ + +dng_point dng_resample_task::SrcTileSize (const dng_point & /* dstTileSize */) + { + + return fSrcTileSize; + + } + +/*****************************************************************************/ + +void dng_resample_task::Start (uint32 threadCount, + const dng_rect &dstArea, + const dng_point &tileSize, + dng_memory_allocator *allocator, + dng_abort_sniffer *sniffer) + { + + // Compute sub-pixel resolution coordinates in the source image for + // each row and column of the destination area. + + fRowCoords.Initialize (fSrcBounds.t, + fDstBounds.t, + fSrcBounds.H (), + fDstBounds.H (), + *allocator); + + fColCoords.Initialize (fSrcBounds.l, + fDstBounds.l, + fSrcBounds.W (), + fDstBounds.W (), + *allocator); + + // Compute resampling kernels. + + fWeightsV.Initialize (fRowScale, + fKernel, + *allocator); + + fWeightsH.Initialize (fColScale, + fKernel, + *allocator); + + // Find upper bound on source source tile. + + fSrcTileSize.v = Round_int32 (tileSize.v / fRowScale) + fWeightsV.Width () + 2; + fSrcTileSize.h = Round_int32 (tileSize.h / fColScale) + fWeightsH.Width () + 2; + + // Allocate temp buffers. + + uint32 tempBufferSize = 0; + + if (!RoundUpUint32ToMultiple (fSrcTileSize.h, 8, &tempBufferSize) || + !SafeUint32Mult (tempBufferSize, + static_cast (sizeof (real32)), + &tempBufferSize)) + { + + ThrowOverflow ("Arithmetic overflow computing buffer size."); + + } + + for (uint32 threadIndex = 0; threadIndex < threadCount; threadIndex++) + { + + fTempBuffer [threadIndex] . Reset (allocator->Allocate (tempBufferSize)); + + } + + // Allocate the pixel buffers. + + dng_filter_task::Start (threadCount, + dstArea, + tileSize, + allocator, + sniffer); + + } + +/*****************************************************************************/ + +void dng_resample_task::ProcessArea (uint32 threadIndex, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer) + { + + dng_rect srcArea = srcBuffer.fArea; + dng_rect dstArea = dstBuffer.fArea; + + uint32 srcCols = srcArea.W (); + uint32 dstCols = dstArea.W (); + + uint32 widthV = fWeightsV.Width (); + uint32 widthH = fWeightsH.Width (); + + int32 offsetV = fWeightsV.Offset (); + int32 offsetH = fWeightsH.Offset (); + + uint32 stepH = fWeightsH.Step (); + + const int32 *rowCoords = fRowCoords.Coords (0 ); + const int32 *colCoords = fColCoords.Coords (dstArea.l); + + if (fSrcPixelType == ttFloat) + { + + const real32 *weightsH = fWeightsH.Weights32 (0); + + real32 *tPtr = fTempBuffer [threadIndex]->Buffer_real32 (); + + real32 *ttPtr = tPtr + offsetH - srcArea.l; + + for (int32 dstRow = dstArea.t; dstRow < dstArea.b; dstRow++) + { + + int32 rowCoord = rowCoords [dstRow]; + + int32 rowFract = rowCoord & kResampleSubsampleMask; + + const real32 *weightsV = fWeightsV.Weights32 (rowFract); + + int32 srcRow = (rowCoord >> kResampleSubsampleBits) + offsetV; + + for (uint32 plane = 0; plane < dstBuffer.fPlanes; plane++) + { + + const real32 *sPtr = srcBuffer.ConstPixel_real32 (srcRow, + srcArea.l, + plane); + + DoResampleDown32 (sPtr, + tPtr, + srcCols, + srcBuffer.fRowStep, + weightsV, + widthV); + + real32 *dPtr = dstBuffer.DirtyPixel_real32 (dstRow, + dstArea.l, + plane); + + DoResampleAcross32 (ttPtr, + dPtr, + dstCols, + colCoords, + weightsH, + widthH, + stepH); + + } + + } + + } + + else + { + + const int16 *weightsH = fWeightsH.Weights16 (0); + + uint16 *tPtr = fTempBuffer [threadIndex]->Buffer_uint16 (); + + uint16 *ttPtr = tPtr + offsetH - srcArea.l; + + uint32 pixelRange = fDstImage.PixelRange (); + + for (int32 dstRow = dstArea.t; dstRow < dstArea.b; dstRow++) + { + + int32 rowCoord = rowCoords [dstRow]; + + int32 rowFract = rowCoord & kResampleSubsampleMask; + + const int16 *weightsV = fWeightsV.Weights16 (rowFract); + + int32 srcRow = (rowCoord >> kResampleSubsampleBits) + offsetV; + + for (uint32 plane = 0; plane < dstBuffer.fPlanes; plane++) + { + + const uint16 *sPtr = srcBuffer.ConstPixel_uint16 (srcRow, + srcArea.l, + plane); + + DoResampleDown16 (sPtr, + tPtr, + srcCols, + srcBuffer.fRowStep, + weightsV, + widthV, + pixelRange); + + uint16 *dPtr = dstBuffer.DirtyPixel_uint16 (dstRow, + dstArea.l, + plane); + + DoResampleAcross16 (ttPtr, + dPtr, + dstCols, + colCoords, + weightsH, + widthH, + stepH, + pixelRange); + + } + + } + + } + + } + +/*****************************************************************************/ + +void ResampleImage (dng_host &host, + const dng_image &srcImage, + dng_image &dstImage, + const dng_rect &srcBounds, + const dng_rect &dstBounds, + const dng_resample_function &kernel) + { + + dng_resample_task task (srcImage, + dstImage, + srcBounds, + dstBounds, + kernel); + + host.PerformAreaTask (task, + dstBounds); + + } + +/*****************************************************************************/ diff --git a/dng/dng_resample.h b/dng/dng_resample.h new file mode 100644 index 0000000..b61a2ed --- /dev/null +++ b/dng/dng_resample.h @@ -0,0 +1,268 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef __dng_resample__ +#define __dng_resample__ + +/*****************************************************************************/ + +#include "dng_assertions.h" +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_memory.h" +#include "dng_point.h" +#include "dng_types.h" + +/*****************************************************************************/ + +class dng_resample_function + { + + public: + + dng_resample_function () + { + } + + virtual ~dng_resample_function () + { + } + + virtual real64 Extent () const = 0; + + virtual real64 Evaluate (real64 x) const = 0; + + }; + +/*****************************************************************************/ + +class dng_resample_bicubic: public dng_resample_function + { + + public: + + virtual real64 Extent () const; + + virtual real64 Evaluate (real64 x) const; + + static const dng_resample_function & Get (); + + }; + +/******************************************************************************/ + +const uint32 kResampleSubsampleBits = 7; +const uint32 kResampleSubsampleCount = 1 << kResampleSubsampleBits; +const uint32 kResampleSubsampleMask = kResampleSubsampleCount - 1; + +/*****************************************************************************/ + +class dng_resample_coords + { + + protected: + + int32 fOrigin; + + AutoPtr fCoords; + + public: + + dng_resample_coords (); + + virtual ~dng_resample_coords (); + + void Initialize (int32 srcOrigin, + int32 dstOrigin, + uint32 srcCount, + uint32 dstCount, + dng_memory_allocator &allocator); + + const int32 * Coords (int32 index) const + { + return fCoords->Buffer_int32 () + (index - fOrigin); + } + + int32 Pixel (int32 index) const + { + return Coords (index) [0] >> kResampleSubsampleBits; + } + + }; + +/*****************************************************************************/ + +class dng_resample_weights + { + + protected: + + uint32 fRadius; + + uint32 fWeightStep; + + AutoPtr fWeights32; + AutoPtr fWeights16; + + public: + + dng_resample_weights (); + + virtual ~dng_resample_weights (); + + void Initialize (real64 scale, + const dng_resample_function &kernel, + dng_memory_allocator &allocator); + + uint32 Radius () const + { + return fRadius; + } + + uint32 Width () const + { + return fRadius * 2; + } + + int32 Offset () const + { + return 1 - (int32) fRadius; + } + + uint32 Step () const + { + return fWeightStep; + } + + const real32 *Weights32 (uint32 fract) const + { + + DNG_ASSERT (fWeights32->Buffer (), "Weights32 is NULL"); + + if (fract >= kResampleSubsampleCount) + { + + ThrowBadFormat (); + + } + + return fWeights32->Buffer_real32 () + fract * fWeightStep; + + } + + const int16 *Weights16 (uint32 fract) const + { + + DNG_ASSERT (fWeights16->Buffer (), "Weights16 is NULL"); + + if (fract >= kResampleSubsampleCount) + { + + ThrowBadFormat (); + + } + + return fWeights16->Buffer_int16 () + fract * fWeightStep; + + } + + }; + +/*****************************************************************************/ + +const uint32 kResampleSubsampleBits2D = 5; +const uint32 kResampleSubsampleCount2D = 1 << kResampleSubsampleBits2D; +const uint32 kResampleSubsampleMask2D = kResampleSubsampleCount2D - 1; + +/*****************************************************************************/ + +class dng_resample_weights_2d + { + + protected: + + uint32 fRadius; + + uint32 fRowStep; + uint32 fColStep; + + AutoPtr fWeights32; + AutoPtr fWeights16; + + public: + + dng_resample_weights_2d (); + + virtual ~dng_resample_weights_2d (); + + void Initialize (const dng_resample_function &kernel, + dng_memory_allocator &allocator); + + uint32 Radius () const + { + return fRadius; + } + + uint32 Width () const + { + return fRadius * 2; + } + + int32 Offset () const + { + return 1 - (int32) fRadius; + } + + uint32 RowStep () const + { + return fRowStep; + } + + uint32 ColStep () const + { + return fColStep; + } + + const real32 *Weights32 (dng_point fract) const + { + + DNG_ASSERT (fWeights32->Buffer (), "Weights32 is NULL"); + + const uint32 offset = fract.v * fRowStep + fract.h * fColStep; + + return fWeights32->Buffer_real32 () + offset; + + } + + const int16 *Weights16 (dng_point fract) const + { + + DNG_ASSERT (fWeights16->Buffer (), "Weights16 is NULL"); + + const uint32 offset = fract.v * fRowStep + fract.h * fColStep; + + return fWeights16->Buffer_int16 () + offset; + + } + + }; + +/*****************************************************************************/ + +void ResampleImage (dng_host &host, + const dng_image &srcImage, + dng_image &dstImage, + const dng_rect &srcBounds, + const dng_rect &dstBounds, + const dng_resample_function &kernel); + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_safe_arithmetic.cpp b/dng/dng_safe_arithmetic.cpp new file mode 100644 index 0000000..7d12d7a --- /dev/null +++ b/dng/dng_safe_arithmetic.cpp @@ -0,0 +1,264 @@ +/*****************************************************************************/ +// Copyright 2015-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_safe_arithmetic.h" + +#include + +#include "dng_exceptions.h" + +// Implementation of safe integer arithmetic follows guidelines from +// https://www.securecoding.cert.org/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap +// and +// https://www.securecoding.cert.org/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow + +namespace { + +// Template functions for safe arithmetic. These functions are not exposed in +// the header for the time being to avoid having to add checks for the various +// constraints on the template argument (e.g. that it is integral and possibly +// signed or unsigned only). This should be done using a static_assert(), but +// we want to be portable to pre-C++11 compilers. + +// Returns the result of adding arg1 and arg2 if it will fit in a T (where T is +// a signed or unsigned integer type). Otherwise, throws a dng_exception with +// error code dng_error_unknown. +template +T SafeAdd(T arg1, T arg2) { + // The condition is reformulated relative to the version on + // www.securecoding.cert.org to check for valid instead of invalid cases. It + // seems safer to enumerate the valid cases (and potentially miss one) than + // enumerate the invalid cases. + // If T is an unsigned type, the second half of the condition always evaluates + // to false and will presumably be compiled out by the compiler. + if ((arg1 >= 0 && arg2 <= std::numeric_limits::max() - arg1) || + (arg1 < 0 && arg2 >= std::numeric_limits::min() - arg1)) { + return arg1 + arg2; + } else { + ThrowOverflow ("Arithmetic overflow in SafeAdd"); + + // Dummy return statement. + return 0; + } +} + +// Returns the result of multiplying arg1 and arg2 if it will fit in a T (where +// T is an unsigned integer type). Otherwise, throws a dng_exception with error +// code dng_error_unknown. +template +T SafeUnsignedMult(T arg1, T arg2) { + if (arg1 == 0 || arg2 <= std::numeric_limits::max() / arg1) { + return arg1 * arg2; + } else { + ThrowOverflow ("Arithmetic overflow in SafeUnsignedMult"); + + // Dummy return statement. + return 0; + } +} + +} // namespace + +bool SafeInt32Add(int32 arg1, int32 arg2, int32 *result) { + try { + *result = SafeInt32Add(arg1, arg2); + return true; + } catch (const dng_exception &) { + return false; + } +} + +int32 SafeInt32Add(int32 arg1, int32 arg2) { + return SafeAdd(arg1, arg2); +} + +int64 SafeInt64Add(int64 arg1, int64 arg2) { + return SafeAdd(arg1, arg2); +} + +bool SafeUint32Add(uint32 arg1, uint32 arg2, + uint32 *result) { + try { + *result = SafeUint32Add(arg1, arg2); + return true; + } catch (const dng_exception &) { + return false; + } +} + +uint32 SafeUint32Add(uint32 arg1, uint32 arg2) { + return SafeAdd(arg1, arg2); +} + +bool SafeInt32Sub(int32 arg1, int32 arg2, int32 *result) { + if ((arg2 >= 0 && arg1 >= std::numeric_limits::min() + arg2) || + (arg2 < 0 && arg1 <= std::numeric_limits::max() + arg2)) { + *result = arg1 - arg2; + return true; + } else { + return false; + } +} + +int32 SafeInt32Sub(int32 arg1, int32 arg2) { + int32 result = 0; + + if (!SafeInt32Sub(arg1, arg2, &result)) { + ThrowOverflow ("Arithmetic overflow in SafeInt32Sub"); + } + + return result; +} + +bool SafeUint32Mult(uint32 arg1, uint32 arg2, + uint32 *result) { + try { + *result = SafeUint32Mult(arg1, arg2); + return true; + } catch (const dng_exception &) { + return false; + } +} + +bool SafeUint32Mult(uint32 arg1, uint32 arg2, uint32 arg3, + uint32 *result) { + try { + *result = SafeUint32Mult(arg1, arg2, arg3); + return true; + } catch (const dng_exception &) { + return false; + } +} + +bool SafeUint32Mult(uint32 arg1, uint32 arg2, uint32 arg3, + uint32 arg4, uint32 *result) { + try { + *result = SafeUint32Mult(arg1, arg2, arg3, arg4); + return true; + } catch (const dng_exception &) { + return false; + } +} + +uint32 SafeUint32Mult(uint32 arg1, uint32 arg2) { + return SafeUnsignedMult(arg1, arg2); +} + +uint32 SafeUint32Mult(uint32 arg1, uint32 arg2, + uint32 arg3) { + return SafeUint32Mult(SafeUint32Mult(arg1, arg2), arg3); +} + +uint32 SafeUint32Mult(uint32 arg1, uint32 arg2, + uint32 arg3, uint32 arg4) { + return SafeUint32Mult(SafeUint32Mult(arg1, arg2, arg3), arg4); +} + +std::size_t SafeSizetMult(std::size_t arg1, std::size_t arg2) { + return SafeUnsignedMult(arg1, arg2); +} + +int64 SafeInt64Mult(int64 arg1, int64 arg2) { + bool overflow = true; + + if (arg1 > 0) { + if (arg2 > 0) { + overflow = (arg1 > INT64_MAX / arg2); + } else { + overflow = (arg2 < INT64_MIN / arg1); + } + } else { + if (arg2 > 0) { + overflow = (arg1 < INT64_MIN / arg2); + } else { + overflow = (arg1 != 0 && arg2 < INT64_MAX / arg1); + } + } + + if (overflow) { + ThrowOverflow ("Arithmetic overflow"); + + // Dummy return. + return 0; + } else { + return arg1 * arg2; + } +} + +uint32 SafeUint32DivideUp(uint32 arg1, uint32 arg2) { + // It might seem more intuitive to implement this function simply as + // + // return arg2 == 0 ? 0 : (arg1 + arg2 - 1) / arg2; + // + // but the expression "arg1 + arg2" can wrap around. + + if (arg2 == 0) { + ThrowProgramError("Division by zero"); + + // Dummy return to avoid compiler error about missing return statement. + return 0; + } else if (arg1 == 0) { + // If arg1 is zero, return zero to avoid wraparound in the expression + // "arg1 - 1" below. + return 0; + } else { + return (arg1 - 1) / arg2 + 1; + } +} + +bool RoundUpUint32ToMultiple(uint32 val, uint32 multiple_of, + uint32 *result) { + if (multiple_of == 0) { + return false; + } + + const uint32 remainder = val % multiple_of; + if (remainder == 0) { + *result = val; + return true; + } else { + return SafeUint32Add(val, multiple_of - remainder, result); + } +} + +bool ConvertUint32ToInt32(uint32 val, int32 *result) { + const uint32 kInt32MaxAsUint32 = + static_cast(std::numeric_limits::max()); + + if (val <= kInt32MaxAsUint32) { + *result = static_cast(val); + return true; + } else { + return false; + } +} + +/*****************************************************************************/ + +dng_safe_uint32::dng_safe_uint32 (const dng_safe_int32 &x) + { + + if (x.Get () < 0) + { + ThrowOverflow ("Overflow in dng_safe_uint32"); + } + + fValue = static_cast (x.Get ()); + + } + +/*****************************************************************************/ + +dng_safe_int32::dng_safe_int32 (const dng_safe_uint32 &x) + { + + Set_uint32 (x.Get ()); + + } + +/*****************************************************************************/ diff --git a/dng/dng_safe_arithmetic.h b/dng/dng_safe_arithmetic.h new file mode 100644 index 0000000..8a0a019 --- /dev/null +++ b/dng/dng_safe_arithmetic.h @@ -0,0 +1,336 @@ +/*****************************************************************************/ +// Copyright 2015-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* + * + * Copyright (C) 2015 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. + */ + +// Functions for safe arithmetic (guarded against overflow) on integer types. + +#ifndef __dng_safe_arithmetic__ +#define __dng_safe_arithmetic__ + +#include +//#include +#include + +#include "dng_exceptions.h" +#include "dng_types.h" + +// If the result of adding arg1 and arg2 will fit in an int32_t (without +// under-/overflow), stores this result in *result and returns true. Otherwise, +// returns false and leaves *result unchanged. +bool SafeInt32Add(int32 arg1, int32 arg2, int32 *result); + +// Returns the result of adding arg1 and arg2 if it will fit in the result type +// (without under-/overflow). Otherwise, throws a dng_exception with error code +// dng_error_unknown. +int32 SafeInt32Add(int32 arg1, int32 arg2); +int64 SafeInt64Add(int64 arg1, int64 arg2); + +// If the result of adding arg1 and arg2 will fit in a uint32_t (without +// wraparound), stores this result in *result and returns true. Otherwise, +// returns false and leaves *result unchanged. +bool SafeUint32Add(uint32 arg1, uint32 arg2, + uint32 *result); + +// Returns the result of adding arg1 and arg2 if it will fit in a uint32_t +// (without wraparound). Otherwise, throws a dng_exception with error code +// dng_error_unknown. +uint32 SafeUint32Add(uint32 arg1, uint32 arg2); + +// If the subtraction of arg2 from arg1 will not result in an int32_t under- or +// overflow, stores this result in *result and returns true. Otherwise, +// returns false and leaves *result unchanged. +bool SafeInt32Sub(int32 arg1, int32 arg2, int32 *result); + +// Returns the result of subtracting arg2 from arg1 if this operation will not +// result in an int32_t under- or overflow. Otherwise, throws a dng_exception +// with error code dng_error_unknown. +int32 SafeInt32Sub(int32 arg1, int32 arg2); + +// If the result of multiplying arg1, ..., argn will fit in a uint32_t (without +// wraparound), stores this result in *result and returns true. Otherwise, +// returns false and leaves *result unchanged. +bool SafeUint32Mult(uint32 arg1, uint32 arg2, + uint32 *result); +bool SafeUint32Mult(uint32 arg1, uint32 arg2, uint32 arg3, + uint32 *result); +bool SafeUint32Mult(uint32 arg1, uint32 arg2, uint32 arg3, + uint32 arg4, uint32 *result); + +// Returns the result of multiplying arg1, ..., argn if it will fit in a +// uint32_t (without wraparound). Otherwise, throws a dng_exception with error +// code dng_error_unknown. +uint32 SafeUint32Mult(uint32 arg1, uint32 arg2); +uint32 SafeUint32Mult(uint32 arg1, uint32 arg2, + uint32 arg3); +uint32 SafeUint32Mult(uint32 arg1, uint32 arg2, + uint32 arg3, uint32 arg4); + +// Returns the result of multiplying arg1 and arg2 if it will fit in a size_t +// (without overflow). Otherwise, throws a dng_exception with error code +// dng_error_unknown. +std::size_t SafeSizetMult(std::size_t arg1, std::size_t arg2); + +// Returns the result of multiplying arg1 and arg2 if it will fit in an int64_t +// (without overflow). Otherwise, throws a dng_exception with error code +// dng_error_unknown. +int64 SafeInt64Mult(int64 arg1, int64 arg2); + +// Returns the result of dividing arg1 by arg2; if the result is not an integer, +// rounds up to the next integer. If arg2 is zero, throws a dng_exception with +// error code dng_error_unknown. +// The function is safe against wraparound and will return the correct result +// for all combinations of arg1 and arg2. +uint32 SafeUint32DivideUp(uint32 arg1, uint32 arg2); + +// Finds the smallest integer multiple of 'multiple_of' that is greater than or +// equal to 'val'. If this value will fit in a uint32_t, stores it in *result +// and returns true. Otherwise, or if 'multiple_of' is zero, returns false and +// leaves *result unchanged. +bool RoundUpUint32ToMultiple(uint32 val, uint32 multiple_of, + uint32 *result); + +// If the uint32_t value val will fit in a int32_t, converts it to a int32_t and +// stores it in *result. Otherwise, returns false and leaves *result unchanged. +bool ConvertUint32ToInt32(uint32 val, int32 *result); + +// Converts a value of the unsigned integer type TSrc to the unsigned integer +// type TDest. If the value in 'src' cannot be converted to the type TDest +// without truncation, throws a dng_exception with error code dng_error_unknown. +// +// Note: Though this function is typically used where TDest is a narrower type +// than TSrc, it is designed to work also if TDest is wider than from TSrc or +// identical to TSrc. This is useful in situations where the width of the types +// involved can change depending on the architecture -- for example, the +// conversion from size_t to uint32_t may either be narrowing, identical or even +// widening (though the latter admittedly happens only on architectures that +// aren't relevant to us). +template +static void ConvertUnsigned(TSrc src, TDest *dest) { +#if 0 +// sub-optimal run-time implementation pre-C++11 + if (!(std::numeric_limits::is_integer && + !std::numeric_limits::is_signed && + std::numeric_limits::is_integer && + !std::numeric_limits::is_signed)) + { + ThrowProgramError ("TSrc and TDest must be unsigned integer types"); + } +#else + // preferred compile-time implementation; requires C++11 + static_assert(std::numeric_limits::is_integer && + !std::numeric_limits::is_signed && + std::numeric_limits::is_integer && + !std::numeric_limits::is_signed, + "TSrc and TDest must be unsigned integer types"); +#endif + + const TDest converted = static_cast(src); + + // Convert back to TSrc to check whether truncation occurred in the + // conversion to TDest. + if (static_cast(converted) != src) { + ThrowProgramError("Overflow in unsigned integer conversion"); + } + + *dest = converted; +} + +/*****************************************************************************/ + +class dng_safe_int32; +class dng_safe_uint32; + +/*****************************************************************************/ + +#define CHECK_SAFE_UINT32 \ + static_assert (std::numeric_limits::is_integer && \ + !std::numeric_limits::is_signed && \ + (sizeof (T) == 4), \ + "src must be unsigned 32-bit integer") + +class dng_safe_uint32 + { + + private: + + uint32 fValue; + + public: + + template + dng_safe_uint32 (T x) + { + CHECK_SAFE_UINT32; + fValue = x; + } + + explicit dng_safe_uint32 (const dng_safe_int32 &x); + + inline uint32 Get () const + { + return fValue; + } + + // Compound assignment operators. + + dng_safe_uint32 & operator+= (const dng_safe_uint32 &x) + { + fValue = SafeUint32Add (fValue, x.fValue); + return *this; + } + + template + dng_safe_uint32 & operator+= (T x) + { + CHECK_SAFE_UINT32; + fValue = SafeUint32Add (fValue, x); + return *this; + } + + dng_safe_uint32 & operator*= (const dng_safe_uint32 &x) + { + fValue = SafeUint32Mult (fValue, x.fValue); + return *this; + } + + template + dng_safe_uint32 & operator*= (T x) + { + CHECK_SAFE_UINT32; + fValue = SafeUint32Mult (fValue, x); + return *this; + } + + // Binary operators. + + const dng_safe_uint32 operator+ (const dng_safe_uint32 &x) const + { + return dng_safe_uint32 (*this) += x; + } + + template + const dng_safe_uint32 operator+ (T x) const + { + CHECK_SAFE_UINT32; + return dng_safe_uint32 (*this) += x; + } + + const dng_safe_uint32 operator* (const dng_safe_uint32 &x) const + { + return dng_safe_uint32 (*this) *= x; + } + + template + const dng_safe_uint32 operator* (T x) const + { + CHECK_SAFE_UINT32; + return dng_safe_uint32 (*this) *= x; + } + + }; + +#undef CHECK_SAFE_UINT32 + +/*****************************************************************************/ + +#define CHECK_SAFE_INT32 \ + static_assert (std::numeric_limits::is_integer && \ + std::numeric_limits::is_signed && \ + (sizeof (T) == 4), \ + "src must be signed 32-bit integer") + +class dng_safe_int32 + { + + private: + + int32 fValue; + + public: + + template + dng_safe_int32 (T x) + { + CHECK_SAFE_INT32; + fValue = x; + } + + // Construct int32 from uint32. Throws exception dng_error_overflow if + // source uint32 cannot be represented as int32. + + explicit dng_safe_int32 (const dng_safe_uint32 &x); + + inline int32 Get () const + { + return fValue; + } + + // Assign from uint32. Throws exception dng_error_overflow if source + // uint32 cannot be represented as int32. + + void Set_uint32 (uint32 x) + { + if (!ConvertUint32ToInt32 (x, &fValue)) + { + ThrowProgramError ("Overflow in Set_uint32"); + } + } + + // Compound assignment operators. + + dng_safe_int32 & operator+= (const dng_safe_int32 &x) + { + fValue = SafeInt32Add (fValue, x.fValue); + return *this; + } + + template + dng_safe_int32 & operator+= (T x) + { + CHECK_SAFE_INT32; + fValue = SafeInt32Add (fValue, x); + return *this; + } + + dng_safe_int32 & operator-= (const dng_safe_int32 &x) + { + fValue = SafeInt32Sub (fValue, x.fValue); + return *this; + } + + template + dng_safe_int32 & operator-= (T x) + { + CHECK_SAFE_INT32; + fValue = SafeInt32Sub (fValue, x); + return *this; + } + + }; + +#undef CHECK_SAFE_INT32 + +/*****************************************************************************/ + +#endif // __dng_safe_arithmetic__ diff --git a/dng/dng_sdk_limits.h b/dng/dng_sdk_limits.h new file mode 100644 index 0000000..1d6d5c7 --- /dev/null +++ b/dng/dng_sdk_limits.h @@ -0,0 +1,81 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Collection of constants detailing maximum values used in processing in the DNG SDK. + */ + +/*****************************************************************************/ + +#ifndef __dng_sdk_limits__ +#define __dng_sdk_limits__ + +/*****************************************************************************/ + +#include "dng_types.h" + +/*****************************************************************************/ + +/// The maximum number of previews (in addition to the main IFD's thumbnail) +/// that we support embedded in a DNG. + +const uint32 kMaxDNGPreviews = 20; + +/// The maximum number of SubIFDs that will be parsed. + +const uint32 kMaxSubIFDs = kMaxDNGPreviews + 1; + +/// The maximum number of chained IFDs that will be parsed. + +const uint32 kMaxChainedIFDs = 10; + +/// The maximum number of samples per pixel. + +const uint32 kMaxSamplesPerPixel = 4; + +/// Maximum number of color planes. + +const uint32 kMaxColorPlanes = kMaxSamplesPerPixel; + +/// The maximum size of a CFA repeating pattern. + +const uint32 kMaxCFAPattern = 8; + +/// The maximum size of a black level repeating pattern. + +const uint32 kMaxBlackPattern = 8; + +/// The maximum number of masked area rectangles. + +const uint32 kMaxMaskedAreas = 4; + +/// The maximum image size supported (pixels per side). + +const uint32 kMaxImageSide = 65000; + +/// The maximum number of tone curve points supported. + +const uint32 kMaxToneCurvePoints = 8192; + +/// Maximum number of MP threads for dng_area_task operations. + +#if qDNG64Bit +const uint32 kMaxMPThreads = 128; // EP! Needs much larger max! +#else +const uint32 kMaxMPThreads = 8; +#endif + +/// Maximum supported value of Stage3BlackLevelNormalized. + +const real64 kMaxStage3BlackLevelNormalized = 0.2; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_shared.cpp b/dng/dng_shared.cpp new file mode 100644 index 0000000..b525b93 --- /dev/null +++ b/dng/dng_shared.cpp @@ -0,0 +1,3366 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_shared.h" + +#include "dng_camera_profile.h" +#include "dng_exceptions.h" +#include "dng_globals.h" +#include "dng_memory.h" +#include "dng_parse_utils.h" +#include "dng_tag_codes.h" +#include "dng_tag_types.h" +#include "dng_tag_values.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_camera_profile_info::dng_camera_profile_info () + + : fBigEndian (false) + + , fColorPlanes (0) + + , fCalibrationIlluminant1 (lsUnknown) + , fCalibrationIlluminant2 (lsUnknown) + + , fColorMatrix1 () + , fColorMatrix2 () + + , fForwardMatrix1 () + , fForwardMatrix2 () + + , fReductionMatrix1 () + , fReductionMatrix2 () + + , fProfileCalibrationSignature () + + , fProfileName () + + , fProfileCopyright () + + , fEmbedPolicy (pepAllowCopying) + + , fProfileHues (0) + , fProfileSats (0) + , fProfileVals (0) + + , fHueSatDeltas1Offset (0) + , fHueSatDeltas1Count (0) + + , fHueSatDeltas2Offset (0) + , fHueSatDeltas2Count (0) + + , fHueSatMapEncoding (encoding_Linear) + + , fLookTableHues (0) + , fLookTableSats (0) + , fLookTableVals (0) + + , fLookTableOffset (0) + , fLookTableCount (0) + + , fLookTableEncoding (encoding_Linear) + + , fBaselineExposureOffset (0, 100) + + , fDefaultBlackRender (defaultBlackRender_Auto) + + , fToneCurveOffset (0) + , fToneCurveCount (0) + + , fUniqueCameraModel () + + { + + } + +/*****************************************************************************/ + +dng_camera_profile_info::~dng_camera_profile_info () + { + + } + +/*****************************************************************************/ + +DNG_ATTRIB_NO_SANITIZE("unsigned-integer-overflow") +bool dng_camera_profile_info::ParseTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset) + { + + switch (tagCode) + { + + case tcCalibrationIlluminant1: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fCalibrationIlluminant1 = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CalibrationIlluminant1: %s\n", + LookupLightSource (fCalibrationIlluminant1)); + + } + + #endif + + break; + + } + + case tcCalibrationIlluminant2: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fCalibrationIlluminant2 = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CalibrationIlluminant2: %s\n", + LookupLightSource (fCalibrationIlluminant2)); + + } + + #endif + + break; + + } + + case tcColorMatrix1: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + if (fColorPlanes == 0) + { + + fColorPlanes = Pin_uint32 (0, tagCount / 3, kMaxColorPlanes); + + } + + if (!CheckColorImage (parentCode, tagCode, fColorPlanes)) + return false; + + if (!ParseMatrixTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + fColorPlanes, + 3, + fColorMatrix1)) + return false; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ColorMatrix1:\n"); + + DumpMatrix (fColorMatrix1); + + } + + #endif + + break; + + } + + case tcColorMatrix2: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + // Kludge - Hasselblad FFF files are very DNG-like, but sometimes + // only have a ColorMatrix2 tag and no ColorMatrix1 tag. + + bool hasselbladHack = (fColorPlanes == 0); + + if (hasselbladHack) + { + + fColorPlanes = Pin_uint32 (0, tagCount / 3, kMaxColorPlanes); + + #if qDNGValidate + + ReportWarning ("ColorMatrix2 without ColorMatrix1"); + + #endif + + } + + if (!CheckColorImage (parentCode, tagCode, fColorPlanes)) + return false; + + if (!ParseMatrixTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + fColorPlanes, + 3, + fColorMatrix2)) + return false; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ColorMatrix2:\n"); + + DumpMatrix (fColorMatrix2); + + } + + #endif + + if (hasselbladHack) + { + + fColorMatrix1 = fColorMatrix2; + + fColorMatrix2 = dng_matrix (); + + } + + break; + + } + + case tcForwardMatrix1: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + if (!CheckColorImage (parentCode, tagCode, fColorPlanes)) + return false; + + if (!ParseMatrixTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + 3, + fColorPlanes, + fForwardMatrix1)) + return false; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ForwardMatrix1:\n"); + + DumpMatrix (fForwardMatrix1); + + } + + #endif + + break; + + } + + case tcForwardMatrix2: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + if (!CheckColorImage (parentCode, tagCode, fColorPlanes)) + return false; + + if (!ParseMatrixTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + 3, + fColorPlanes, + fForwardMatrix2)) + return false; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ForwardMatrix2:\n"); + + DumpMatrix (fForwardMatrix2); + + } + + #endif + + break; + + } + + case tcReductionMatrix1: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + if (!CheckColorImage (parentCode, tagCode, fColorPlanes)) + return false; + + if (!ParseMatrixTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + 3, + fColorPlanes, + fReductionMatrix1)) + return false; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ReductionMatrix1:\n"); + + DumpMatrix (fReductionMatrix1); + + } + + #endif + + break; + + } + + case tcReductionMatrix2: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + if (!CheckColorImage (parentCode, tagCode, fColorPlanes)) + return false; + + if (!ParseMatrixTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + 3, + fColorPlanes, + fReductionMatrix2)) + return false; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ReductionMatrix2:\n"); + + DumpMatrix (fReductionMatrix2); + + } + + #endif + + break; + + } + + case tcProfileCalibrationSignature: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fProfileCalibrationSignature, + false); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ProfileCalibrationSignature: "); + + DumpString (fProfileCalibrationSignature); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcProfileName: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fProfileName, + false); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ProfileName: "); + + DumpString (fProfileName); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcProfileCopyright: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fProfileCopyright, + false); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ProfileCopyright: "); + + DumpString (fProfileCopyright); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcProfileEmbedPolicy: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fEmbedPolicy = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + const char *policy; + + switch (fEmbedPolicy) + { + + case pepAllowCopying: + policy = "Allow copying"; + break; + + case pepEmbedIfUsed: + policy = "Embed if used"; + break; + + case pepEmbedNever: + policy = "Embed never"; + break; + + case pepNoRestrictions: + policy = "No restrictions"; + break; + + default: + policy = "INVALID VALUE"; + + } + + printf ("ProfileEmbedPolicy: %s\n", policy); + + } + + #endif + + break; + + } + + case tcProfileHueSatMapDims: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 2, 3); + + fProfileHues = stream.TagValue_uint32 (tagType); + fProfileSats = stream.TagValue_uint32 (tagType); + + if (tagCount > 2) + fProfileVals = stream.TagValue_uint32 (tagType); + else + fProfileVals = 1; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ProfileHueSatMapDims: Hues = %u, Sats = %u, Vals = %u\n", + (unsigned) fProfileHues, + (unsigned) fProfileSats, + (unsigned) fProfileVals); + + } + + #endif + + break; + + } + + case tcProfileHueSatMapData1: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttFloat)) + return false; + + if (fProfileSats == 0) + return false; + + dng_safe_uint32 hueCount (fProfileHues); + dng_safe_uint32 valCount (fProfileVals); + + bool skipSat0 = (tagCount == (hueCount * + (fProfileSats - 1) * + (valCount * 3u)).Get ()); + + if (!skipSat0) + { + + dng_safe_uint32 expected = hueCount * valCount * fProfileSats * 3u; + + if (!CheckTagCount (parentCode, + tagCode, + tagCount, + expected.Get ())) + { + return false; + } + + } + + fBigEndian = stream.BigEndian (); + + fHueSatDeltas1Offset = tagOffset; + fHueSatDeltas1Count = tagCount; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ProfileHueSatMapData1:\n"); + + DumpHueSatMap (stream, + fProfileHues, + fProfileSats, + fProfileVals, + skipSat0); + + } + + #endif + + break; + + } + + case tcProfileHueSatMapData2: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttFloat)) + return false; + + if (fProfileSats == 0) + return false; + + dng_safe_uint32 hueCount (fProfileHues); + dng_safe_uint32 valCount (fProfileVals); + + bool skipSat0 = (tagCount == (hueCount * + (fProfileSats - 1) * + (valCount * 3u)).Get ()); + + if (!skipSat0) + { + + dng_safe_uint32 expected = hueCount * valCount * fProfileSats * 3u; + + if (!CheckTagCount (parentCode, + tagCode, + tagCount, + expected.Get ())) + { + return false; + } + + } + + fBigEndian = stream.BigEndian (); + + fHueSatDeltas2Offset = tagOffset; + fHueSatDeltas2Count = tagCount; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ProfileHueSatMapData2:\n"); + + DumpHueSatMap (stream, + fProfileHues, + fProfileSats, + fProfileVals, + skipSat0); + + } + + #endif + + break; + + } + + case tcProfileHueSatMapEncoding: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fHueSatMapEncoding = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + const char *encoding = NULL; + + switch (fHueSatMapEncoding) + { + + case encoding_Linear: + encoding = "Linear"; + break; + + case encoding_sRGB: + encoding = "sRGB"; + break; + + default: + encoding = "INVALID VALUE"; + + } + + printf ("ProfileHueSatMapEncoding: %s\n", encoding); + + } + + #endif + + break; + + } + + case tcProfileLookTableDims: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 2, 3); + + fLookTableHues = stream.TagValue_uint32 (tagType); + fLookTableSats = stream.TagValue_uint32 (tagType); + + if (tagCount > 2) + fLookTableVals = stream.TagValue_uint32 (tagType); + else + fLookTableVals = 1; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ProfileLookTableDims: Hues = %u, Sats = %u, Vals = %u\n", + (unsigned) fLookTableHues, + (unsigned) fLookTableSats, + (unsigned) fLookTableVals); + + } + + #endif + + break; + + } + + case tcProfileLookTableData: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttFloat)) + return false; + + if (fLookTableSats == 0) + return false; + + dng_safe_uint32 hueCount (fLookTableHues); + dng_safe_uint32 valCount (fLookTableVals); + + bool skipSat0 = (tagCount == (hueCount * + (fLookTableSats - 1) * + valCount * 3u).Get ()); + + if (!skipSat0) + { + + dng_safe_uint32 expected = hueCount * valCount * fLookTableSats * 3u; + + if (!CheckTagCount (parentCode, + tagCode, + tagCount, + expected.Get ())) + { + return false; + } + + } + + fBigEndian = stream.BigEndian (); + + fLookTableOffset = tagOffset; + fLookTableCount = tagCount; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ProfileLookTableData:\n"); + + DumpHueSatMap (stream, + fLookTableHues, + fLookTableSats, + fLookTableVals, + skipSat0); + + } + + #endif + + break; + + } + + case tcProfileLookTableEncoding: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fLookTableEncoding = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + const char *encoding = NULL; + + switch (fLookTableEncoding) + { + + case encoding_Linear: + encoding = "Linear"; + break; + + case encoding_sRGB: + encoding = "sRGB"; + break; + + default: + encoding = "INVALID VALUE"; + + } + + printf ("ProfileLookTableEncoding: %s\n", encoding); + + } + + #endif + + break; + + } + + case tcBaselineExposureOffset: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fBaselineExposureOffset = stream.TagValue_srational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("BaselineExposureOffset: %+0.2f\n", + fBaselineExposureOffset.As_real64 ()); + + } + + #endif + + break; + + } + + case tcDefaultBlackRender: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fDefaultBlackRender = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + const char *setting = NULL; + + switch (fDefaultBlackRender) + { + + case defaultBlackRender_Auto: + setting = "Auto"; + break; + + case defaultBlackRender_None: + setting = "None"; + break; + + default: + setting = "INVALID VALUE"; + + } + + printf ("DefaultBlackRender: %s\n", + setting); + + } + + #endif + + break; + + } + + case tcProfileToneCurve: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttFloat)) + return false; + + if (!CheckTagCount (parentCode, tagCode, tagCount, 4, tagCount)) + return false; + + if ((tagCount & 1) != 0) + { + + #if qDNGValidate + + { + + char message [256]; + + sprintf (message, + "%s %s has odd count (%u)", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode), + (unsigned) tagCount); + + ReportWarning (message); + + } + + #endif + + return false; + + } + + fBigEndian = stream.BigEndian (); + + fToneCurveOffset = tagOffset; + fToneCurveCount = tagCount; + + #if qDNGValidate + + if (gVerbose) + { + + DumpTagValues (stream, + "Coord", + parentCode, + tagCode, + tagType, + tagCount); + + + } + + #endif + + break; + + } + + case tcUniqueCameraModel: + { + + // Note: This code is only used when parsing stand-alone + // profiles. The embedded profiles are assumed to be restricted + // to the model they are embedded in. + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fUniqueCameraModel, + false); + + bool didTrim = fUniqueCameraModel.TrimTrailingBlanks (); + + #if qDNGValidate + + if (didTrim) + { + + ReportWarning ("UniqueCameraModel string has trailing blanks"); + + } + + if (gVerbose) + { + + printf ("UniqueCameraModel: "); + + DumpString (fUniqueCameraModel); + + printf ("\n"); + + } + + #else + + (void) didTrim; // Unused + + #endif + + break; + + } + + default: + { + + return false; + + } + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_camera_profile_info::ParseExtended (dng_stream &stream) + { + + try + { + + // Offsets are relative to the start of this structure, not the entire file. + + uint64 startPosition = stream.Position (); + + // Read header. Like a TIFF header, but with different magic number + // Plus all offsets are relative to the start of the IFD, not to the + // stream or file. + + uint16 byteOrder = stream.Get_uint16 (); + + if (byteOrder == byteOrderMM) + fBigEndian = true; + + else if (byteOrder == byteOrderII) + fBigEndian = false; + + else + return false; + + TempBigEndian setEndianness (stream, fBigEndian); + + uint16 magicNumber = stream.Get_uint16 (); + + if (magicNumber != magicExtendedProfile) + { + return false; + } + + uint32 offset = stream.Get_uint32 (); + + stream.Skip (offset - 8); + + // Start on IFD entries. + + uint32 ifdEntries = stream.Get_uint16 (); + + if (ifdEntries < 1) + { + return false; + } + + for (uint32 tag_index = 0; tag_index < ifdEntries; tag_index++) + { + + stream.SetReadPosition (startPosition + 8 + 2 + tag_index * 12); + + uint16 tagCode = stream.Get_uint16 (); + uint32 tagType = stream.Get_uint16 (); + uint32 tagCount = stream.Get_uint32 (); + + uint64 tagOffset = stream.Position (); + + if (TagTypeSize (tagType) * tagCount > 4) + { + + tagOffset = startPosition + stream.Get_uint32 (); + + stream.SetReadPosition (tagOffset); + + } + + if (!ParseTag (stream, + 0, + tagCode, + tagType, + tagCount, + tagOffset)) + { + + #if qDNGValidate + + if (gVerbose) + { + + stream.SetReadPosition (tagOffset); + + printf ("*"); + + DumpTagValues (stream, + LookupTagType (tagType), + 0, + tagCode, + tagType, + tagCount); + + } + + #endif + + } + + } + + return true; + + } + + catch (...) + { + + // Eat parsing errors. + + } + + return false; + + } + +/*****************************************************************************/ + +dng_shared::dng_shared () + + : fExifIFD (0) + , fGPSInfo (0) + , fInteroperabilityIFD (0) + , fKodakDCRPrivateIFD (0) + , fKodakKDCPrivateIFD (0) + + , fXMPCount (0) + , fXMPOffset (0) + + , fIPTC_NAA_Count (0) + , fIPTC_NAA_Offset (0) + + , fMakerNoteCount (0) + , fMakerNoteOffset (0) + , fMakerNoteSafety (0) + + , fDNGVersion (0) + , fDNGBackwardVersion (0) + + , fUniqueCameraModel () + , fLocalizedCameraModel () + + , fCameraProfile () + + , fExtraCameraProfiles () + + , fCameraCalibration1 () + , fCameraCalibration2 () + + , fCameraCalibrationSignature () + + , fAnalogBalance () + + , fAsShotNeutral () + + , fAsShotWhiteXY () + + , fBaselineExposure (0, 1) + , fBaselineNoise (1, 1) + , fBaselineSharpness (1, 1) + , fLinearResponseLimit (1, 1) + , fShadowScale (1, 1) + + , fHasBaselineExposure (false) + , fHasShadowScale (false) + + , fDNGPrivateDataCount (0) + , fDNGPrivateDataOffset (0) + + , fRawImageDigest () + , fNewRawImageDigest () + + , fRawDataUniqueID () + + , fOriginalRawFileName () + + , fOriginalRawFileDataCount (0) + , fOriginalRawFileDataOffset (0) + + , fOriginalRawFileDigest () + + , fAsShotICCProfileCount (0) + , fAsShotICCProfileOffset (0) + + , fAsShotPreProfileMatrix () + + , fCurrentICCProfileCount (0) + , fCurrentICCProfileOffset (0) + + , fCurrentPreProfileMatrix () + + , fColorimetricReference (crSceneReferred) + + , fAsShotProfileName () + + , fOriginalDefaultFinalSize () + , fOriginalBestQualityFinalSize () + + , fOriginalDefaultCropSizeH () + , fOriginalDefaultCropSizeV () + + , fDepthFormat (depthFormatUnknown) + , fDepthNear (0, 0) + , fDepthFar (0, 0) + , fDepthUnits (depthUnitsUnknown) + , fDepthMeasureType (depthMeasureUnknown) + + { + + } + +/*****************************************************************************/ + +dng_shared::~dng_shared () + { + + } + +/*****************************************************************************/ + +bool dng_shared::ParseTag (dng_stream &stream, + dng_exif &exif, + uint32 parentCode, + bool /* isMainIFD */, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset, + int64 /* offsetDelta */) + { + + if (parentCode == 0) + { + + if (Parse_ifd0 (stream, + exif, + parentCode, + tagCode, + tagType, + tagCount, + tagOffset)) + { + + return true; + + } + + } + + if (parentCode == 0 || + parentCode == tcExifIFD) + { + + if (Parse_ifd0_exif (stream, + exif, + parentCode, + tagCode, + tagType, + tagCount, + tagOffset)) + { + + return true; + + } + + } + + return false; + + } + +/*****************************************************************************/ + +// Parses tags that should only appear in IFD 0. + +bool dng_shared::Parse_ifd0 (dng_stream &stream, + dng_exif & /* exif */, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset) + { + + switch (tagCode) + { + + case tcXMP: + { + + CheckTagType (parentCode, tagCode, tagType, ttByte, ttUndefined); + + fXMPCount = tagCount; + fXMPOffset = fXMPCount ? tagOffset : 0; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("XMP: Count = %u, Offset = %u\n", + (unsigned) fXMPCount, + (unsigned) fXMPOffset); + + if (fXMPCount) + { + + DumpXMP (stream, fXMPCount); + + } + + } + + #endif + + break; + + } + + case tcIPTC_NAA: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong, ttAscii, ttUndefined); + + fIPTC_NAA_Count = (dng_safe_uint32 (tagCount) * TagTypeSize (tagType)).Get (); + fIPTC_NAA_Offset = fIPTC_NAA_Count ? tagOffset : 0; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("IPTC/NAA: Count = %u, Offset = %u\n", + (unsigned) fIPTC_NAA_Count, + (unsigned) fIPTC_NAA_Offset); + + if (fIPTC_NAA_Count) + { + + DumpHexAscii (stream, fIPTC_NAA_Count); + + } + + // Compute and output the digest. + + dng_memory_data buffer (fIPTC_NAA_Count); + + stream.SetReadPosition (fIPTC_NAA_Offset); + + stream.Get (buffer.Buffer (), fIPTC_NAA_Count); + + const uint8 *data = buffer.Buffer_uint8 (); + + uint32 count = fIPTC_NAA_Count; + + // Method 1: Counting all bytes (this is correct). + + { + + dng_md5_printer printer; + + printer.Process (data, count); + + printf ("IPTCDigest: "); + + DumpFingerprint (printer.Result ()); + + printf ("\n"); + + } + + // Method 2: Ignoring zero padding. + + { + + uint32 removed = 0; + + while ((removed < 3) && (count > 0) && (data [count - 1] == 0)) + { + removed++; + count--; + } + + if (removed != 0) + { + + dng_md5_printer printer; + + printer.Process (data, count); + + printf ("IPTCDigest (ignoring zero padding): "); + + DumpFingerprint (printer.Result ()); + + printf ("\n"); + + } + + } + + } + + #endif + + break; + + } + + case tcExifIFD: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fExifIFD = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("ExifIFD: %u\n", (unsigned) fExifIFD); + } + + #endif + + break; + + } + + case tcGPSInfo: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fGPSInfo = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("GPSInfo: %u\n", (unsigned) fGPSInfo); + } + + #endif + + break; + + } + + case tcKodakDCRPrivateIFD: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fKodakDCRPrivateIFD = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("KodakDCRPrivateIFD: %u\n", (unsigned) fKodakDCRPrivateIFD); + } + + #endif + + break; + + } + + case tcKodakKDCPrivateIFD: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fKodakKDCPrivateIFD = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("KodakKDCPrivateIFD: %u\n", (unsigned) fKodakKDCPrivateIFD); + } + + #endif + + break; + + } + + case tcDNGVersion: + { + + CheckTagType (parentCode, tagCode, tagType, ttByte); + + CheckTagCount (parentCode, tagCode, tagCount, 4); + + uint32 b0 = stream.Get_uint8 (); + uint32 b1 = stream.Get_uint8 (); + uint32 b2 = stream.Get_uint8 (); + uint32 b3 = stream.Get_uint8 (); + + fDNGVersion = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + + #if qDNGValidate + + if (gVerbose) + { + printf ("DNGVersion: %u.%u.%u.%u\n", + (unsigned) b0, + (unsigned) b1, + (unsigned) b2, + (unsigned) b3); + } + + #endif + + break; + + } + + case tcDNGBackwardVersion: + { + + CheckTagType (parentCode, tagCode, tagType, ttByte); + + CheckTagCount (parentCode, tagCode, tagCount, 4); + + uint32 b0 = stream.Get_uint8 (); + uint32 b1 = stream.Get_uint8 (); + uint32 b2 = stream.Get_uint8 (); + uint32 b3 = stream.Get_uint8 (); + + fDNGBackwardVersion = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + + #if qDNGValidate + + if (gVerbose) + { + printf ("DNGBackwardVersion: %u.%u.%u.%u\n", + (unsigned) b0, + (unsigned) b1, + (unsigned) b2, + (unsigned) b3); + } + + #endif + + break; + + } + + case tcUniqueCameraModel: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fUniqueCameraModel, + false); + + bool didTrim = fUniqueCameraModel.TrimTrailingBlanks (); + + #if qDNGValidate + + if (didTrim) + { + + ReportWarning ("UniqueCameraModel string has trailing blanks"); + + } + + if (gVerbose) + { + + printf ("UniqueCameraModel: "); + + DumpString (fUniqueCameraModel); + + printf ("\n"); + + } + + #else + + (void) didTrim; // Unused + + #endif + + break; + + } + + case tcLocalizedCameraModel: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fLocalizedCameraModel, + false); + + bool didTrim = fLocalizedCameraModel.TrimTrailingBlanks (); + + #if qDNGValidate + + if (didTrim) + { + + ReportWarning ("LocalizedCameraModel string has trailing blanks"); + + } + + if (gVerbose) + { + + printf ("LocalizedCameraModel: "); + + DumpString (fLocalizedCameraModel); + + printf ("\n"); + + } + + #else + + (void) didTrim; // Unused + + #endif + + break; + + } + + case tcCameraCalibration1: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes)) + return false; + + if (!ParseMatrixTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + fCameraProfile.fColorPlanes, + fCameraProfile.fColorPlanes, + fCameraCalibration1)) + return false; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CameraCalibration1:\n"); + + DumpMatrix (fCameraCalibration1); + + } + + #endif + + break; + + } + + case tcCameraCalibration2: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes)) + return false; + + if (!ParseMatrixTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + fCameraProfile.fColorPlanes, + fCameraProfile.fColorPlanes, + fCameraCalibration2)) + return false; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CameraCalibration2:\n"); + + DumpMatrix (fCameraCalibration2); + + } + + #endif + + break; + + } + + case tcCameraCalibrationSignature: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fCameraCalibrationSignature, + false); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CameraCalibrationSignature: "); + + DumpString (fCameraCalibrationSignature); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcAnalogBalance: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + // Kludge - Hasselblad FFF files are very DNG-like, but sometimes + // they don't have any ColorMatrix tags. + + bool hasselbladHack = (fDNGVersion == 0 && + fCameraProfile.fColorPlanes == 0); + + if (hasselbladHack) + { + + fCameraProfile.fColorPlanes = Pin_uint32 (0, tagCount, kMaxColorPlanes); + + #if qDNGValidate + + ReportWarning ("AnalogBalance without ColorMatrix1"); + + #endif + + } + + if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes)) + return false; + + if (!ParseVectorTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + fCameraProfile.fColorPlanes, + fAnalogBalance)) + return false; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("AnalogBalance:"); + + DumpVector (fAnalogBalance); + + } + + #endif + + break; + + } + + case tcAsShotNeutral: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + // Kludge - Hasselblad FFF files are very DNG-like, but sometimes + // they don't have any ColorMatrix tags. + + bool hasselbladHack = (fDNGVersion == 0 && + fCameraProfile.fColorPlanes == 0); + + if (hasselbladHack) + { + + fCameraProfile.fColorPlanes = Pin_uint32 (0, tagCount, kMaxColorPlanes); + + #if qDNGValidate + + ReportWarning ("AsShotNeutral without ColorMatrix1"); + + #endif + + } + + if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes)) + return false; + + if (!ParseVectorTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + fCameraProfile.fColorPlanes, + fAsShotNeutral)) + return false; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("AsShotNeutral:"); + + DumpVector (fAsShotNeutral); + + } + + #endif + + break; + + } + + case tcAsShotWhiteXY: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes)) + return false; + + if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) + return false; + + fAsShotWhiteXY.x = stream.TagValue_real64 (tagType); + fAsShotWhiteXY.y = stream.TagValue_real64 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("AsShotWhiteXY: %0.4f %0.4f\n", + fAsShotWhiteXY.x, + fAsShotWhiteXY.y); + + } + + #endif + + break; + + } + + case tcBaselineExposure: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fBaselineExposure = stream.TagValue_srational (tagType); + + fHasBaselineExposure = true; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("BaselineExposure: %+0.2f\n", + fBaselineExposure.As_real64 ()); + + } + + #endif + + break; + + } + + case tcBaselineNoise: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fBaselineNoise = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("BaselineNoise: %0.2f\n", + fBaselineNoise.As_real64 ()); + + } + + #endif + + break; + + } + + case tcBaselineSharpness: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fBaselineSharpness = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("BaselineSharpness: %0.2f\n", + fBaselineSharpness.As_real64 ()); + + } + + #endif + + break; + + } + + case tcLinearResponseLimit: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fLinearResponseLimit = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("LinearResponseLimit: %0.2f\n", + fLinearResponseLimit.As_real64 ()); + + } + + #endif + + break; + + } + + case tcShadowScale: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fShadowScale = stream.TagValue_urational (tagType); + + fHasShadowScale = true; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ShadowScale: %0.4f\n", + fShadowScale.As_real64 ()); + + } + + #endif + + break; + + } + + case tcDNGPrivateData: + { + + CheckTagType (parentCode, tagCode, tagType, ttByte); + + fDNGPrivateDataCount = tagCount; + fDNGPrivateDataOffset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("DNGPrivateData: Count = %u, Offset = %u\n", + (unsigned) fDNGPrivateDataCount, + (unsigned) fDNGPrivateDataOffset); + + DumpHexAscii (stream, tagCount); + + } + + #endif + + break; + + } + + case tcMakerNoteSafety: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fMakerNoteSafety = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("MakerNoteSafety: %s\n", + LookupMakerNoteSafety (fMakerNoteSafety)); + + } + + #endif + + break; + + } + + case tcRawImageDigest: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttByte)) + return false; + + if (!CheckTagCount (parentCode, tagCode, tagCount, 16)) + return false; + + stream.Get (fRawImageDigest.data, 16); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("RawImageDigest: "); + + DumpFingerprint (fRawImageDigest); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcNewRawImageDigest: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttByte)) + return false; + + if (!CheckTagCount (parentCode, tagCode, tagCount, 16)) + return false; + + stream.Get (fNewRawImageDigest.data, 16); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("NewRawImageDigest: "); + + DumpFingerprint (fNewRawImageDigest); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcRawDataUniqueID: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttByte)) + return false; + + if (!CheckTagCount (parentCode, tagCode, tagCount, 16)) + return false; + + stream.Get (fRawDataUniqueID.data, 16); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("RawDataUniqueID: "); + + DumpFingerprint (fRawDataUniqueID); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcOriginalRawFileName: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fOriginalRawFileName, + false); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("OriginalRawFileName: "); + + DumpString (fOriginalRawFileName); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcOriginalRawFileData: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + fOriginalRawFileDataCount = tagCount; + fOriginalRawFileDataOffset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("OriginalRawFileData: Count = %u, Offset = %u\n", + (unsigned) fOriginalRawFileDataCount, + (unsigned) fOriginalRawFileDataOffset); + + DumpHexAscii (stream, tagCount); + + } + + #endif + + break; + + } + + case tcOriginalRawFileDigest: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttByte)) + return false; + + if (!CheckTagCount (parentCode, tagCode, tagCount, 16)) + return false; + + stream.Get (fOriginalRawFileDigest.data, 16); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("OriginalRawFileDigest: "); + + DumpFingerprint (fOriginalRawFileDigest); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcAsShotICCProfile: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + fAsShotICCProfileCount = tagCount; + fAsShotICCProfileOffset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("AsShotICCProfile: Count = %u, Offset = %u\n", + (unsigned) fAsShotICCProfileCount, + (unsigned) fAsShotICCProfileOffset); + + DumpHexAscii (stream, tagCount); + + } + + #endif + + break; + + } + + case tcAsShotPreProfileMatrix: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes)) + return false; + + uint32 rows = fCameraProfile.fColorPlanes; + + if (tagCount == fCameraProfile.fColorPlanes * 3) + { + rows = 3; + } + + if (!ParseMatrixTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + rows, + fCameraProfile.fColorPlanes, + fAsShotPreProfileMatrix)) + return false; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("AsShotPreProfileMatrix:\n"); + + DumpMatrix (fAsShotPreProfileMatrix); + + } + + #endif + + break; + + } + + case tcCurrentICCProfile: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + fCurrentICCProfileCount = tagCount; + fCurrentICCProfileOffset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CurrentICCProfile: Count = %u, Offset = %u\n", + (unsigned) fCurrentICCProfileCount, + (unsigned) fCurrentICCProfileOffset); + + DumpHexAscii (stream, tagCount); + + } + + #endif + + break; + + } + + case tcCurrentPreProfileMatrix: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes)) + return false; + + uint32 rows = fCameraProfile.fColorPlanes; + + if (tagCount == fCameraProfile.fColorPlanes * 3) + { + rows = 3; + } + + if (!ParseMatrixTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + rows, + fCameraProfile.fColorPlanes, + fCurrentPreProfileMatrix)) + return false; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CurrentPreProfileMatrix:\n"); + + DumpMatrix (fCurrentPreProfileMatrix); + + } + + #endif + + break; + + } + + case tcColorimetricReference: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fColorimetricReference = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ColorimetricReference: %s\n", + LookupColorimetricReference (fColorimetricReference)); + + } + + #endif + + break; + + } + + case tcExtraCameraProfiles: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1, tagCount); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ExtraCameraProfiles: %u\n", (unsigned) tagCount); + + } + + #endif + + fExtraCameraProfiles.reserve (tagCount); + + for (uint32 index = 0; index < tagCount; index++) + { + + #if qDNGValidate + + if (gVerbose) + { + + printf ("\nExtraCameraProfile [%u]:\n\n", (unsigned) index); + + } + + #endif + + stream.SetReadPosition (tagOffset + index * 4); + + uint32 profileOffset = stream.TagValue_uint32 (tagType); + + dng_camera_profile_info profileInfo; + + stream.SetReadPosition (profileOffset); + + if (profileInfo.ParseExtended (stream)) + { + + fExtraCameraProfiles.push_back (profileInfo); + + } + + else + { + + #if qDNGValidate + + ReportWarning ("Unable to parse extra camera profile"); + + #endif + + } + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("\nDone with ExtraCameraProfiles\n\n"); + + } + + #endif + + break; + + } + + case tcAsShotProfileName: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fAsShotProfileName, + false); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("AsShotProfileName: "); + + DumpString (fAsShotProfileName); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcOriginalDefaultFinalSize: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) + return false; + + fOriginalDefaultFinalSize.h = stream.TagValue_int32 (tagType); + fOriginalDefaultFinalSize.v = stream.TagValue_int32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("OriginalDefaultFinalSize: H = %d V = %d\n", + (int) fOriginalDefaultFinalSize.h, + (int) fOriginalDefaultFinalSize.v); + + } + + #endif + + break; + + } + + case tcOriginalBestQualityFinalSize: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) + return false; + + fOriginalBestQualityFinalSize.h = stream.TagValue_int32 (tagType); + fOriginalBestQualityFinalSize.v = stream.TagValue_int32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("OriginalBestQualityFinalSize: H = %d V = %d\n", + (int) fOriginalBestQualityFinalSize.h, + (int) fOriginalBestQualityFinalSize.v); + + } + + #endif + + break; + + } + + case tcOriginalDefaultCropSize: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong, ttRational); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) + return false; + + fOriginalDefaultCropSizeH = stream.TagValue_urational (tagType); + fOriginalDefaultCropSizeV = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("OriginalDefaultCropSize: H = %0.2f V = %0.2f\n", + fOriginalDefaultCropSizeH.As_real64 (), + fOriginalDefaultCropSizeV.As_real64 ()); + + } + + #endif + + break; + + } + + case tcDepthFormat: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fDepthFormat = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("DepthFormat: %s\n", + LookupDepthFormat (fDepthFormat)); + + } + + #endif + + break; + + } + + case tcDepthNear: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fDepthNear = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("DepthNear: "); + + if (fDepthNear == dng_urational (0, 0)) + { + printf ("Unknown"); + } + else if (fDepthNear.d == 0) + { + printf ("Infinity"); + } + else + { + printf ("%0.2f", fDepthNear.As_real64 ()); + } + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcDepthFar: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fDepthFar = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("DepthFar: "); + + if (fDepthFar == dng_urational (0, 0)) + { + printf ("Unknown"); + } + else if (fDepthFar.d == 0) + { + printf ("Infinity"); + } + else + { + printf ("%0.2f", fDepthFar.As_real64 ()); + } + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcDepthUnits: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fDepthUnits = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("DepthUnits: %s\n", + LookupDepthUnits (fDepthUnits)); + + } + + #endif + + break; + + } + + case tcDepthMeasureType: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fDepthMeasureType = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("DepthMeasureType: %s\n", + LookupDepthMeasureType (fDepthMeasureType)); + + } + + #endif + + break; + + } + + default: + { + + // The main camera profile tags also appear in IFD 0 + + return fCameraProfile.ParseTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + tagOffset); + + } + + } + + return true; + + } + +/*****************************************************************************/ + +// Parses tags that should only appear in IFD 0 or EXIF IFD. + +bool dng_shared::Parse_ifd0_exif (dng_stream &stream, + dng_exif & /* exif */, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset) + { + + switch (tagCode) + { + + case tcMakerNote: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + fMakerNoteCount = tagCount; + fMakerNoteOffset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("MakerNote: Count = %u, Offset = %u\n", + (unsigned) fMakerNoteCount, + (unsigned) fMakerNoteOffset); + + DumpHexAscii (stream, tagCount); + + } + + #endif + + break; + + } + + case tcInteroperabilityIFD: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fInteroperabilityIFD = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("InteroperabilityIFD: %u\n", (unsigned) fInteroperabilityIFD); + } + + #endif + + break; + + } + + default: + { + + return false; + + } + + } + + return true; + + } + +/*****************************************************************************/ + +void dng_shared::PostParse (dng_host & /* host */, + dng_exif & /* exif */) + { + + // Fill in default values for DNG images. + + if (fDNGVersion != 0) + { + + // Support for DNG versions before 1.0.0.0. + + if (fDNGVersion < dngVersion_1_0_0_0) + { + + #if qDNGValidate + + ReportWarning ("DNGVersion less than 1.0.0.0"); + + #endif + + // The CalibrationIlluminant tags were added just before + // DNG version 1.0.0.0, and were hardcoded before that. + + fCameraProfile.fCalibrationIlluminant1 = lsStandardLightA; + fCameraProfile.fCalibrationIlluminant2 = lsD65; + + fDNGVersion = dngVersion_1_0_0_0; + + } + + // Default value for DNGBackwardVersion tag. + + if (fDNGBackwardVersion == 0) + { + + fDNGBackwardVersion = fDNGVersion & 0xFFFF0000; + + } + + // Check DNGBackwardVersion value. + + if (fDNGBackwardVersion < dngVersion_1_0_0_0) + { + + #if qDNGValidate + + ReportWarning ("DNGBackwardVersion less than 1.0.0.0"); + + #endif + + fDNGBackwardVersion = dngVersion_1_0_0_0; + + } + + if (fDNGBackwardVersion > fDNGVersion) + { + + #if qDNGValidate + + ReportWarning ("DNGBackwardVersion > DNGVersion"); + + #endif + + fDNGBackwardVersion = fDNGVersion; + + } + + // Check UniqueCameraModel. + + if (fUniqueCameraModel.IsEmpty ()) + { + + #if qDNGValidate + + ReportWarning ("Missing or invalid UniqueCameraModel"); + + #endif + + fUniqueCameraModel.Set ("Digital Negative"); + + } + + // If we don't know the color depth yet, it must be a monochrome DNG. + + if (fCameraProfile.fColorPlanes == 0) + { + + fCameraProfile.fColorPlanes = 1; + + } + + // Check color info. + + if (fCameraProfile.fColorPlanes > 1) + { + + // Check illuminant pair. + + if (fCameraProfile.fColorMatrix2.NotEmpty ()) + { + + if (fCameraProfile.fCalibrationIlluminant1 == lsUnknown || + (fCameraProfile.fCalibrationIlluminant2 == lsUnknown || + (fCameraProfile.fCalibrationIlluminant1 == fCameraProfile.fCalibrationIlluminant2))) + { + + #if qDNGValidate + + ReportWarning ("Invalid CalibrationIlluminant pair"); + + #endif + + fCameraProfile.fColorMatrix2 = dng_matrix (); + + } + + } + + // If the colorimetric reference is the ICC profile PCS, then the + // data must already be white balanced. The "AsShotWhiteXY" is required + // to be the ICC Profile PCS white point. + + if (fColorimetricReference == crICCProfilePCS) + { + + if (fAsShotNeutral.NotEmpty ()) + { + + #if qDNGValidate + + ReportWarning ("AsShotNeutral not allowed for this " + "ColorimetricReference value"); + + #endif + + fAsShotNeutral.Clear (); + + } + + dng_xy_coord pcs = PCStoXY (); + + #if qDNGValidate + + if (fAsShotWhiteXY.IsValid ()) + { + + if (Abs_real64 (fAsShotWhiteXY.x - pcs.x) > 0.01 || + Abs_real64 (fAsShotWhiteXY.y - pcs.y) > 0.01) + { + + ReportWarning ("AsShotWhiteXY does not match the ICC Profile PCS"); + + } + + } + + #endif + + fAsShotWhiteXY = pcs; + + } + + else + { + + // Warn if both AsShotNeutral and AsShotWhiteXY are specified. + + if (fAsShotNeutral.NotEmpty () && fAsShotWhiteXY.IsValid ()) + { + + #if qDNGValidate + + ReportWarning ("Both AsShotNeutral and AsShotWhiteXY included"); + + #endif + + fAsShotWhiteXY = dng_xy_coord (); + + } + + // Warn if neither AsShotNeutral nor AsShotWhiteXY are specified. + + #if qDNGValidate + + if (fAsShotNeutral.IsEmpty () && !fAsShotWhiteXY.IsValid ()) + { + + ReportWarning ("Neither AsShotNeutral nor AsShotWhiteXY included", + "legal but not recommended"); + + } + + #endif + + } + + // Default values of calibration signatures are required for legacy + // compatiblity. + + if (fCameraProfile.fCalibrationIlluminant1 == lsStandardLightA && + fCameraProfile.fCalibrationIlluminant2 == lsD65 && + fCameraCalibration1.Rows () == fCameraProfile.fColorPlanes && + fCameraCalibration1.Cols () == fCameraProfile.fColorPlanes && + fCameraCalibration2.Rows () == fCameraProfile.fColorPlanes && + fCameraCalibration2.Cols () == fCameraProfile.fColorPlanes && + fCameraCalibrationSignature.IsEmpty () && + fCameraProfile.fProfileCalibrationSignature.IsEmpty () ) + { + + fCameraCalibrationSignature.Set (kAdobeCalibrationSignature); + + fCameraProfile.fProfileCalibrationSignature.Set (kAdobeCalibrationSignature); + + } + + } + + // Check BaselineNoise. + + if (fBaselineNoise.As_real64 () <= 0.0) + { + + #if qDNGValidate + + ReportWarning ("Invalid BaselineNoise"); + + #endif + + fBaselineNoise = dng_urational (1, 1); + + } + + // Check BaselineSharpness. + + if (fBaselineSharpness.As_real64 () <= 0.0) + { + + #if qDNGValidate + + ReportWarning ("Invalid BaselineSharpness"); + + #endif + + fBaselineSharpness = dng_urational (1, 1); + + } + + // Check LinearResponseLimit. + + if (fLinearResponseLimit.As_real64 () < 0.5 || + fLinearResponseLimit.As_real64 () > 1.0) + { + + #if qDNGValidate + + ReportWarning ("Invalid LinearResponseLimit"); + + #endif + + fLinearResponseLimit = dng_urational (1, 1); + + } + + // Check ShadowScale. + + if (fShadowScale.As_real64 () <= 0.0) + { + + #if qDNGValidate + + ReportWarning ("Invalid ShadowScale"); + + #endif + + fShadowScale = dng_urational (1, 1); + + } + + } + + } + +/*****************************************************************************/ + +bool dng_shared::IsValidDNG () + { + + // Check DNGVersion value. + + if (fDNGVersion < dngVersion_1_0_0_0) + { + + #if qDNGValidate + + if (fDNGVersion != dngVersion_None) + { + + ReportError ("Invalid DNGVersion"); + + } + + #if qDNGValidateTarget + + else + { + + ReportError ("Missing DNGVersion"); + + } + + #endif + + #endif + + return false; + + } + + // Check DNGBackwardVersion value. + + if (fDNGBackwardVersion > dngVersion_Current) + { + + #if qDNGValidate + + ReportError ("DNGBackwardVersion (or DNGVersion) is too high"); + + #endif + + ThrowUnsupportedDNG (); + + } + + // Check color transform info. + + if (fCameraProfile.fColorPlanes > 1) + { + + // CameraCalibration1 is optional, but it must be valid if present. + + if (fCameraCalibration1.Cols () != 0 || + fCameraCalibration1.Rows () != 0) + { + + if (fCameraCalibration1.Cols () != fCameraProfile.fColorPlanes || + fCameraCalibration1.Rows () != fCameraProfile.fColorPlanes) + { + + #if qDNGValidate + + ReportError ("CameraCalibration1 is wrong size"); + + #endif + + return false; + + } + + // Make sure it is invertable. + + try + { + + (void) Invert (fCameraCalibration1); + + } + + catch (...) + { + + #if qDNGValidate + + ReportError ("CameraCalibration1 is not invertable"); + + #endif + + return false; + + } + + } + + // CameraCalibration2 is optional, but it must be valid if present. + + if (fCameraCalibration2.Cols () != 0 || + fCameraCalibration2.Rows () != 0) + { + + if (fCameraCalibration2.Cols () != fCameraProfile.fColorPlanes || + fCameraCalibration2.Rows () != fCameraProfile.fColorPlanes) + { + + #if qDNGValidate + + ReportError ("CameraCalibration2 is wrong size"); + + #endif + + return false; + + } + + // Make sure it is invertable. + + try + { + + (void) Invert (fCameraCalibration2); + + } + + catch (...) + { + + #if qDNGValidate + + ReportError ("CameraCalibration2 is not invertable"); + + #endif + + return false; + + } + + } + + // Check analog balance + + dng_matrix analogBalance; + + if (fAnalogBalance.NotEmpty ()) + { + + analogBalance = fAnalogBalance.AsDiagonal (); + + try + { + + (void) Invert (analogBalance); + + } + + catch (...) + { + + #if qDNGValidate + + ReportError ("AnalogBalance is not invertable"); + + #endif + + return false; + + } + + } + + } + + return true; + + } + +/*****************************************************************************/ diff --git a/dng/dng_shared.h b/dng/dng_shared.h new file mode 100644 index 0000000..bb6634e --- /dev/null +++ b/dng/dng_shared.h @@ -0,0 +1,243 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef __dng_shared__ +#define __dng_shared__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_fingerprint.h" +#include "dng_matrix.h" +#include "dng_memory.h" +#include "dng_negative.h" +#include "dng_rational.h" +#include "dng_string.h" +#include "dng_stream.h" +#include "dng_sdk_limits.h" +#include "dng_types.h" +#include "dng_xy_coord.h" + +/*****************************************************************************/ + +class dng_camera_profile_info + { + + public: + + bool fBigEndian; + + uint32 fColorPlanes; + + uint32 fCalibrationIlluminant1; + uint32 fCalibrationIlluminant2; + + dng_matrix fColorMatrix1; + dng_matrix fColorMatrix2; + + dng_matrix fForwardMatrix1; + dng_matrix fForwardMatrix2; + + dng_matrix fReductionMatrix1; + dng_matrix fReductionMatrix2; + + dng_string fProfileCalibrationSignature; + + dng_string fProfileName; + + dng_string fProfileCopyright; + + uint32 fEmbedPolicy; + + uint32 fProfileHues; + uint32 fProfileSats; + uint32 fProfileVals; + + uint64 fHueSatDeltas1Offset; + uint32 fHueSatDeltas1Count; + + uint64 fHueSatDeltas2Offset; + uint32 fHueSatDeltas2Count; + + uint32 fHueSatMapEncoding; + + uint32 fLookTableHues; + uint32 fLookTableSats; + uint32 fLookTableVals; + + uint64 fLookTableOffset; + uint32 fLookTableCount; + + uint32 fLookTableEncoding; + + dng_srational fBaselineExposureOffset; + + uint32 fDefaultBlackRender; + + uint64 fToneCurveOffset; + uint32 fToneCurveCount; + + dng_string fUniqueCameraModel; + + public: + + dng_camera_profile_info (); + + ~dng_camera_profile_info (); + + bool ParseTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset); + + bool ParseExtended (dng_stream &stream); + + }; + +/*****************************************************************************/ + +class dng_shared + { + + public: + + uint64 fExifIFD; + uint64 fGPSInfo; + uint64 fInteroperabilityIFD; + uint64 fKodakDCRPrivateIFD; + uint64 fKodakKDCPrivateIFD; + + uint32 fXMPCount; + uint64 fXMPOffset; + + uint32 fIPTC_NAA_Count; + uint64 fIPTC_NAA_Offset; + + uint32 fMakerNoteCount; + uint64 fMakerNoteOffset; + uint32 fMakerNoteSafety; + + uint32 fDNGVersion; + uint32 fDNGBackwardVersion; + + dng_string fUniqueCameraModel; + dng_string fLocalizedCameraModel; + + dng_camera_profile_info fCameraProfile; + + dng_std_vector fExtraCameraProfiles; + + dng_matrix fCameraCalibration1; + dng_matrix fCameraCalibration2; + + dng_string fCameraCalibrationSignature; + + dng_vector fAnalogBalance; + + dng_vector fAsShotNeutral; + + dng_xy_coord fAsShotWhiteXY; + + dng_srational fBaselineExposure; + dng_urational fBaselineNoise; + dng_urational fBaselineSharpness; + dng_urational fLinearResponseLimit; + dng_urational fShadowScale; + + bool fHasBaselineExposure; + bool fHasShadowScale; + + uint32 fDNGPrivateDataCount; + uint64 fDNGPrivateDataOffset; + + dng_fingerprint fRawImageDigest; + dng_fingerprint fNewRawImageDigest; + + dng_fingerprint fRawDataUniqueID; + + dng_string fOriginalRawFileName; + + uint32 fOriginalRawFileDataCount; + uint64 fOriginalRawFileDataOffset; + + dng_fingerprint fOriginalRawFileDigest; + + uint32 fAsShotICCProfileCount; + uint64 fAsShotICCProfileOffset; + + dng_matrix fAsShotPreProfileMatrix; + + uint32 fCurrentICCProfileCount; + uint64 fCurrentICCProfileOffset; + + dng_matrix fCurrentPreProfileMatrix; + + uint32 fColorimetricReference; + + dng_string fAsShotProfileName; + + dng_point fOriginalDefaultFinalSize; + dng_point fOriginalBestQualityFinalSize; + + dng_urational fOriginalDefaultCropSizeH; + dng_urational fOriginalDefaultCropSizeV; + + uint32 fDepthFormat; + dng_urational fDepthNear; + dng_urational fDepthFar; + uint32 fDepthUnits; + uint32 fDepthMeasureType; + + public: + + dng_shared (); + + virtual ~dng_shared (); + + virtual bool ParseTag (dng_stream &stream, + dng_exif &exif, + uint32 parentCode, + bool isMainIFD, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset, + int64 offsetDelta); + + virtual void PostParse (dng_host &host, + dng_exif &exif); + + virtual bool IsValidDNG (); + + protected: + + virtual bool Parse_ifd0 (dng_stream &stream, + dng_exif &exif, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset); + + virtual bool Parse_ifd0_exif (dng_stream &stream, + dng_exif &exif, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_simd_type.h b/dng/dng_simd_type.h new file mode 100644 index 0000000..061a0b7 --- /dev/null +++ b/dng/dng_simd_type.h @@ -0,0 +1,194 @@ +/*****************************************************************************/ +// Copyright 2017-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef __dng_simd_type__ +#define __dng_simd_type__ + +/*****************************************************************************/ + +#include "dng_flags.h" + +/*****************************************************************************/ + +#if qDNGIntelCompiler +#include +#endif // qDNGIntelCompiler + +/*****************************************************************************/ + +enum SIMDType + { + + Scalar, + + SSE2, // Pentium 4 + AVX, // Sandy Bridge + AVX2, // Haswell + F16C = AVX2, //Ivy bridge + AVX512_SKX, // Sky Lake Server + + SIMD_Sentinel + + }; + +/*****************************************************************************/ + +template +class SIMDTraits +{ +public: + static const int kVecSizeFloat = 1; + static const int kVecSizeInt32 = 1; +}; + +template <> +class SIMDTraits +{ +public: + static const int kVecSizeFloat = 4; + static const int kVecSizeInt32 = 4; +}; + +template <> +class SIMDTraits +{ +public: + static const int kVecSizeFloat = 8; + static const int kVecSizeInt32 = 4; +}; + +template <> +class SIMDTraits +{ +public: + static const int kVecSizeFloat = 8; + static const int kVecSizeInt32 = 8; +}; + +template <> +class SIMDTraits +{ +public: + static const int kVecSizeFloat = 16; + static const int kVecSizeInt32 = 16; +}; + +const SIMDType SIMDTypeMaxValue = SIMDType(SIMD_Sentinel - 1); + +extern SIMDType gDNGMaxSIMD; + +/*****************************************************************************/ + +#if qDNGIntelCompiler + +// Intel compiler. + +// Macros are preferred for "#pragma simd" because at some point these will +// all change to OpenMP 4.x compliant "#pragma omp simd" directives (no longer +// Intel-specific). +// +// Note that _Pragma(x) requires C99 or C++11 support. + +// Pre-defined feature levels. + +#define CR_SIMD_MIN_FEATURE (_FEATURE_SSE2) +#define CR_AVX_FEATURE (_FEATURE_AVX) +#define CR_AVX2_FEATURE (_FEATURE_AVX|_FEATURE_FMA|_FEATURE_AVX2) +#define CR_F16C_FEATURE CR_AVX2_FEATURE +#define CR_AVX512_SKX_FEATURE (_FEATURE_AVX512F|_FEATURE_AVX512CD|_FEATURE_AVX512BW|_FEATURE_AVX512DQ|_FEATURE_AVX512VL) +#define CR_COMPILER_USING_AVX512_SKX (__AVX512F__ && __AVX512VL__ && __AVX512BW__ && __AVX512DQ__ && __AVX512CD__) + +#define __SIMDTYPE_TFY(x) #x +#define _SIMDTYPE_TFY(x) __SIMDTYPE_TFY(x) + +#if qDNGDebug + +// Debug build. + +//#define INTEL_PRAGMA_SIMD_ASSERT_C(clause) _Pragma(PM2__STR1__(simd clause)) +#define INTEL_PRAGMA_SIMD_ASSERT _Pragma("simd") +#define INTEL_PRAGMA_SIMD_ASSERT_VECLEN_FLOAT(s) _Pragma(_SIMDTYPE_TFY(simd vectorlength( SIMDTraits::kVecSizeFloat ) )) +#define INTEL_PRAGMA_SIMD_ASSERT_VECLEN_INT32(s) _Pragma(_SIMDTYPE_TFY(simd vectorlength( SIMDTraits::kVecSizeInt32 ) )) +#define INTEL_PRAGMA_SIMD_ASSERT_VECLEN_INT16(s) _Pragma(_SIMDTYPE_TFY(simd vectorlength( SIMDTraits::kVecSizeInt32*2 ) )) +#define INTEL_PRAGMA_SIMD_ASSERT_VECLEN_INT8(s) _Pragma(_SIMDTYPE_TFY(simd vectorlength( SIMDTraits::kVecSizeInt32*4 ) )) + +#else + +// Release build. + +//#define INTEL_PRAGMA_SIMD_ASSERT_C(clause) _Pragma(PM2__STR1__(simd assert clause)) +#define INTEL_PRAGMA_SIMD_ASSERT _Pragma("simd assert") +#if 1 +#if (__INTEL_COMPILER < 1800) +#define INTEL_PRAGMA_SIMD_ASSERT_VECLEN_FLOAT(s) _Pragma(_SIMDTYPE_TFY(simd assert vectorlength( SIMDTraits::kVecSizeFloat ) )) +#define INTEL_PRAGMA_SIMD_ASSERT_VECLEN_INT32(s) _Pragma(_SIMDTYPE_TFY(simd assert vectorlength( SIMDTraits::kVecSizeInt32 ) )) +#define INTEL_PRAGMA_SIMD_ASSERT_VECLEN_INT16(s) _Pragma(_SIMDTYPE_TFY(simd assert vectorlength( SIMDTraits::kVecSizeInt32*2 ) )) +#define INTEL_PRAGMA_SIMD_ASSERT_VECLEN_INT8(s) _Pragma(_SIMDTYPE_TFY(simd assert vectorlength( SIMDTraits::kVecSizeInt32*4 ) )) +#else +// FIX_ME_ERIC_CHAN: I removed the assert to fix compile time error when using Intel compiler version 18. +// Need to figure out correct fix when we switch to newer version. - tknoll 10/30/2017. +#define INTEL_PRAGMA_SIMD_ASSERT_VECLEN_FLOAT(s) _Pragma(_SIMDTYPE_TFY(simd vectorlength( SIMDTraits::kVecSizeFloat ) )) +#define INTEL_PRAGMA_SIMD_ASSERT_VECLEN_INT32(s) _Pragma(_SIMDTYPE_TFY(simd vectorlength( SIMDTraits::kVecSizeInt32 ) )) +#define INTEL_PRAGMA_SIMD_ASSERT_VECLEN_INT16(s) _Pragma(_SIMDTYPE_TFY(simd vectorlength( SIMDTraits::kVecSizeInt32*2 ) )) +#define INTEL_PRAGMA_SIMD_ASSERT_VECLEN_INT8(s) _Pragma(_SIMDTYPE_TFY(simd vectorlength( SIMDTraits::kVecSizeInt32*4 ) )) +#endif +#else +// Don't force +#define INTEL_PRAGMA_SIMD_ASSERT_VECLEN_FLOAT(s) _Pragma(_SIMDTYPE_TFY(simd assert)) +#define INTEL_PRAGMA_SIMD_ASSERT_VECLEN_INT32(s) _Pragma(_SIMDTYPE_TFY(simd assert)) +#define INTEL_PRAGMA_SIMD_ASSERT_VECLEN_INT16(s) _Pragma(_SIMDTYPE_TFY(simd assert)) +#define INTEL_PRAGMA_SIMD_ASSERT_VECLEN_INT8(s) _Pragma(_SIMDTYPE_TFY(simd assert)) +#endif +#endif + +#define SET_CPU_FEATURE(simd) _allow_cpu_features( (simd >= AVX512_SKX) ? CR_AVX512_SKX_FEATURE : (simd >= AVX2) ? CR_AVX2_FEATURE : ((simd >= AVX) ? CR_AVX_FEATURE : CR_SIMD_MIN_FEATURE) ) +//#define SET_CPU_FEATURE_NOFMA(simd) _allow_cpu_features( ((simd >= AVX512_SKX) ? CR_AVX512_SKX_FEATURE : (simd >= AVX2) ? CR_AVX2_FEATURE : ((simd >= AVX) ? CR_AVX_FEATURE : CR_SIMD_MIN_FEATURE)) & ~_FEATURE_FMA ) +#define SET_CPU_FEATURE_NOFMA(simd) _allow_cpu_features( (simd >= AVX) ? CR_AVX_FEATURE : CR_SIMD_MIN_FEATURE) +#define INTEL_PRAGMA_NOVECTOR _Pragma("novector") +#define INTEL_COMPILER_NEEDED_NOTE + +#else + +// Non-Intel compiler. Use empty definitions for the macros. +// Credit: http://www.highprogrammer.com/alan/windev/visualstudio.html, but avoid using $ character +#define Stringize( L ) #L +#define MakeString( M, L ) M(L) +#define _x_Line MakeString( Stringize, __LINE__ ) + +#if qDNGValidateTarget +// Do not warn about Intel compiler if building dng_validate. +#define INTEL_COMPILER_NEEDED_NOTE +#else +#if !(defined (IOS_ENV) || defined(ANDROID_ENV)) && (defined(__x86_64__) || defined(__i386__)) +#ifndef _MSC_VER +#define INTEL_COMPILER_NEEDED_NOTE _Pragma("message(\"NOTE: Intel Compiler needed for optimizations in \" __FILE__ \":\" _x_Line )") +#else +// Intel compiler understands C99 _Pragma, but not Microsoft, so use MS-specific __pragma instead +#define INTEL_COMPILER_NEEDED_NOTE __pragma(message("NOTE: Intel Compiler needed for optimizations in " __FILE__ ":" _x_Line " in " __FUNCTION__)) +#endif +#else +#define INTEL_COMPILER_NEEDED_NOTE +#endif +#endif + +#define INTEL_PRAGMA_SIMD_ASSERT +//#define INTEL_PRAGMA_SIMD_ASSERT_C(clause) +#define SET_CPU_FEATURE(simd) +#define INTEL_PRAGMA_SIMD_ASSERT_VECLEN_FLOAT(simd) +#define INTEL_PRAGMA_SIMD_ASSERT_VECLEN_INT16(simd) +#define INTEL_PRAGMA_SIMD_ASSERT_VECLEN_INT32(simd) +#define INTEL_PRAGMA_SIMD_ASSERT_VECLEN_INT8(simd) +#define INTEL_PRAGMA_NOVECTOR + +#endif // qDNGIntelCompiler + +/*****************************************************************************/ + +#endif // __dng_simd_type__ + +/*****************************************************************************/ diff --git a/dng/dng_simple_image.cpp b/dng/dng_simple_image.cpp new file mode 100644 index 0000000..956d697 --- /dev/null +++ b/dng/dng_simple_image.cpp @@ -0,0 +1,185 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_simple_image.h" + +#include "dng_memory.h" +#include "dng_orientation.h" +#include "dng_tag_types.h" +#include "dng_tag_values.h" + +/*****************************************************************************/ + +dng_simple_image::dng_simple_image (const dng_rect &bounds, + uint32 planes, + uint32 pixelType, + dng_memory_allocator &allocator) + + : dng_image (bounds, + planes, + pixelType) + + , fBuffer () + , fMemory () + , fAllocator (allocator) + + { + + uint32 bytes = ComputeBufferSize (pixelType, + bounds.Size (), + planes, + padSIMDBytes); + + fMemory.Reset (allocator.Allocate (bytes)); + + fBuffer = dng_pixel_buffer (bounds, + 0, + planes, + pixelType, + pcInterleaved, + fMemory->Buffer ()); + + } + +/*****************************************************************************/ + +dng_simple_image::~dng_simple_image () + { + + } + +/*****************************************************************************/ + +dng_image * dng_simple_image::Clone () const + { + + AutoPtr result (new dng_simple_image (Bounds (), + Planes (), + PixelType (), + fAllocator)); + + result->fBuffer.CopyArea (fBuffer, + Bounds (), + 0, + Planes ()); + + return result.Release (); + + } + +/*****************************************************************************/ + +void dng_simple_image::SetPixelType (uint32 pixelType) + { + + dng_image::SetPixelType (pixelType); + + fBuffer.fPixelType = pixelType; + + } + +/*****************************************************************************/ + +void dng_simple_image::Trim (const dng_rect &r) + { + + fBounds.t = 0; + fBounds.l = 0; + + fBounds.b = r.H (); + fBounds.r = r.W (); + + fBuffer.fData = fBuffer.DirtyPixel (r.t, r.l); + + fBuffer.fArea = fBounds; + + } + +/*****************************************************************************/ + +void dng_simple_image::Rotate (const dng_orientation &orientation) + { + + int32 originH = fBounds.l; + int32 originV = fBounds.t; + + int32 colStep = fBuffer.fColStep; + int32 rowStep = fBuffer.fRowStep; + + uint32 width = fBounds.W (); + uint32 height = fBounds.H (); + + if (orientation.FlipH ()) + { + + originH += width - 1; + + colStep = -colStep; + + } + + if (orientation.FlipV ()) + { + + originV += height - 1; + + rowStep = -rowStep; + + } + + if (orientation.FlipD ()) + { + + int32 temp = colStep; + + colStep = rowStep; + rowStep = temp; + + width = fBounds.H (); + height = fBounds.W (); + + } + + fBuffer.fData = fBuffer.DirtyPixel (originV, originH); + + fBuffer.fColStep = colStep; + fBuffer.fRowStep = rowStep; + + fBounds.r = fBounds.l + width; + fBounds.b = fBounds.t + height; + + fBuffer.fArea = fBounds; + + } + +/*****************************************************************************/ + +void dng_simple_image::AcquireTileBuffer (dng_tile_buffer &buffer, + const dng_rect &area, + bool dirty) const + { + + buffer.fArea = area; + + buffer.fPlane = fBuffer.fPlane; + buffer.fPlanes = fBuffer.fPlanes; + buffer.fRowStep = fBuffer.fRowStep; + buffer.fColStep = fBuffer.fColStep; + buffer.fPlaneStep = fBuffer.fPlaneStep; + buffer.fPixelType = fBuffer.fPixelType; + buffer.fPixelSize = fBuffer.fPixelSize; + + buffer.fData = (void *) fBuffer.ConstPixel (buffer.fArea.t, + buffer.fArea.l, + buffer.fPlane); + + buffer.fDirty = dirty; + + } + +/*****************************************************************************/ diff --git a/dng/dng_simple_image.h b/dng/dng_simple_image.h new file mode 100644 index 0000000..db6068d --- /dev/null +++ b/dng/dng_simple_image.h @@ -0,0 +1,75 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef __dng_simple_image__ +#define __dng_simple_image__ + +/*****************************************************************************/ + +#include "dng_auto_ptr.h" +#include "dng_image.h" +#include "dng_pixel_buffer.h" + +/*****************************************************************************/ + +/// dng_image derived class with simple Trim and Rotate functionality. + +class dng_simple_image : public dng_image + { + + protected: + + dng_pixel_buffer fBuffer; + + AutoPtr fMemory; + + dng_memory_allocator &fAllocator; + + public: + + dng_simple_image (const dng_rect &bounds, + uint32 planes, + uint32 pixelType, + dng_memory_allocator &allocator); + + virtual ~dng_simple_image (); + + virtual dng_image * Clone () const; + + /// Setter for pixel type. + + virtual void SetPixelType (uint32 pixelType); + + /// Trim image data outside of given bounds. Memory is not reallocated or freed. + + virtual void Trim (const dng_rect &r); + + /// Rotate image according to orientation. + + virtual void Rotate (const dng_orientation &orientation); + + /// Get the buffer for direct processing. (Unique to dng_simple_image.) + + void GetPixelBuffer (dng_pixel_buffer &buffer) + { + buffer = fBuffer; + } + + protected: + + virtual void AcquireTileBuffer (dng_tile_buffer &buffer, + const dng_rect &area, + bool dirty) const; + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_spline.cpp b/dng/dng_spline.cpp new file mode 100644 index 0000000..3dece85 --- /dev/null +++ b/dng/dng_spline.cpp @@ -0,0 +1,226 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_spline.h" + +#include "dng_assertions.h" +#include "dng_exceptions.h" + +/******************************************************************************/ + +dng_spline_solver::dng_spline_solver () + + : X () + , Y () + , S () + + { + + } + +/******************************************************************************/ + +dng_spline_solver::~dng_spline_solver () + { + + } + +/******************************************************************************/ + +void dng_spline_solver::Reset () + { + + X.clear (); + Y.clear (); + + S.clear (); + + } + +/******************************************************************************/ + +void dng_spline_solver::Add (real64 x, real64 y) + { + + X.push_back (x); + Y.push_back (y); + + } + +/******************************************************************************/ + +void dng_spline_solver::Solve () + { + + // This code computes the unique curve such that: + // It is C0, C1, and C2 continuous + // The second derivative is zero at the end points + + int32 count = (int32) X.size (); + + DNG_REQUIRE (count >= 2, "Too few points"); + + int32 start = 0; + int32 end = count; + + real64 A = X [start+1] - X [start]; + real64 B = (Y [start+1] - Y [start]) / A; + + S.resize (count); + + S [start] = B; + + int32 j; + + // Slopes here are a weighted average of the slopes + // to each of the adjcent control points. + + for (j = start + 2; j < end; ++j) + { + + real64 C = X [j] - X [j-1]; + real64 D = (Y [j] - Y [j-1]) / C; + + S [j-1] = (B * C + D * A) / (A + C); + + A = C; + B = D; + + } + + S [end-1] = 2.0 * B - S [end-2]; + S [start] = 2.0 * S [start] - S [start+1]; + + if ((end - start) > 2) + { + + dng_std_vector E; + dng_std_vector F; + dng_std_vector G; + + E.resize (count); + F.resize (count); + G.resize (count); + + F [start] = 0.5; + E [end-1] = 0.5; + G [start] = 0.75 * (S [start] + S [start+1]); + G [end-1] = 0.75 * (S [end-2] + S [end-1]); + + for (j = start+1; j < end - 1; ++j) + { + + A = (X [j+1] - X [j-1]) * 2.0; + + E [j] = (X [j+1] - X [j]) / A; + F [j] = (X [j] - X [j-1]) / A; + G [j] = 1.5 * S [j]; + + } + + for (j = start+1; j < end; ++j) + { + + A = 1.0 - F [j-1] * E [j]; + + if (j != end-1) F [j] /= A; + + G [j] = (G [j] - G [j-1] * E [j]) / A; + + } + + for (j = end - 2; j >= start; --j) + G [j] = G [j] - F [j] * G [j+1]; + + for (j = start; j < end; ++j) + S [j] = G [j]; + + } + + } + +/******************************************************************************/ + +bool dng_spline_solver::IsIdentity () const + { + + int32 count = (int32) X.size (); + + if (count != 2) + return false; + + if (X [0] != 0.0 || X [1] != 1.0 || + Y [0] != 0.0 || Y [1] != 1.0) + return false; + + return true; + + } + +/******************************************************************************/ + +real64 dng_spline_solver::Evaluate (real64 x) const + { + + int32 count = (int32) X.size (); + + // Check for off each end of point list. + + if (x <= X [0]) + return Y [0]; + + if (x >= X [count-1]) + return Y [count-1]; + + // Binary search for the index. + + int32 lower = 1; + int32 upper = count - 1; + + while (upper > lower) + { + + int32 mid = (lower + upper) >> 1; + + if (x == X [mid]) + { + return Y [mid]; + } + + if (x > X [mid]) + lower = mid + 1; + else + upper = mid; + + } + + DNG_ASSERT (upper == lower, "Binary search error in point list"); + + int32 j = lower; + + // X [j - 1] < x <= X [j] + // A is the distance between the X [j] and X [j - 1] + // B and C describe the fractional distance to either side. B + C = 1. + + // We compute a cubic spline between the two points with slopes + // S[j-1] and S[j] at either end. Specifically, we compute the 1-D Bezier + // with control values: + // + // Y[j-1], Y[j-1] + S[j-1]*A, Y[j]-S[j]*A, Y[j] + + return EvaluateSplineSegment (x, + X [j - 1], + Y [j - 1], + S [j - 1], + X [j ], + Y [j ], + S [j ]); + + } + +/*****************************************************************************/ diff --git a/dng/dng_spline.h b/dng/dng_spline.h new file mode 100644 index 0000000..c175939 --- /dev/null +++ b/dng/dng_spline.h @@ -0,0 +1,75 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef __dng_spline__ +#define __dng_spline__ + +/*****************************************************************************/ + +#include "dng_1d_function.h" +#include "dng_memory.h" + +/*****************************************************************************/ + +inline real64 EvaluateSplineSegment (real64 x, + real64 x0, + real64 y0, + real64 s0, + real64 x1, + real64 y1, + real64 s1) + { + + real64 A = x1 - x0; + + real64 B = (x - x0) / A; + + real64 C = (x1 - x) / A; + + real64 D = ((y0 * (2.0 - C + B) + (s0 * A * B)) * (C * C)) + + ((y1 * (2.0 - B + C) - (s1 * A * C)) * (B * B)); + + return D; + + } + +/*****************************************************************************/ + +class dng_spline_solver: public dng_1d_function + { + + protected: + + dng_std_vector X; + dng_std_vector Y; + + dng_std_vector S; + + public: + + dng_spline_solver (); + + virtual ~dng_spline_solver (); + + void Reset (); + + void Add (real64 x, real64 y); + + virtual void Solve (); + + virtual bool IsIdentity () const; + + virtual real64 Evaluate (real64 x) const; + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_stream.cpp b/dng/dng_stream.cpp new file mode 100644 index 0000000..a54fa9d --- /dev/null +++ b/dng/dng_stream.cpp @@ -0,0 +1,1358 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_stream.h" + +#include "dng_abort_sniffer.h" +#include "dng_auto_ptr.h" +#include "dng_bottlenecks.h" +#include "dng_exceptions.h" +#include "dng_globals.h" +#include "dng_flags.h" +#include "dng_memory.h" +#include "dng_tag_types.h" +#include "dng_assertions.h" + +/*****************************************************************************/ + +dng_stream::dng_stream (dng_abort_sniffer *sniffer, + uint32 bufferSize, + uint64 offsetInOriginalFile) + + : fSwapBytes (false) + , fHaveLength (false) + , fLength (0) + , fOffsetInOriginalFile (offsetInOriginalFile) + , fPosition (0) + , fMemBlock () + , fBuffer (NULL) + , fBufferSize (Max_uint32 (bufferSize, gDNGStreamBlockSize * 2)) + , fBufferStart (0) + , fBufferEnd (0) + , fBufferLimit (bufferSize) + , fBufferDirty (false) + , fSniffer (sniffer) + + { + + fMemBlock.Reset (gDefaultDNGMemoryAllocator.Allocate (fBufferSize)); + + fBuffer = fMemBlock->Buffer_uint8 (); + + } + +/*****************************************************************************/ + +dng_stream::dng_stream (const void *data, + uint32 count, + uint64 offsetInOriginalFile) + + : fSwapBytes (false) + , fHaveLength (true) + , fLength (count) + , fOffsetInOriginalFile (offsetInOriginalFile) + , fPosition (0) + , fMemBlock () + , fBuffer ((uint8 *) data) + , fBufferSize (count) + , fBufferStart (0) + , fBufferEnd (count) + , fBufferLimit (count) + , fBufferDirty (false) + , fSniffer (NULL) + + { + + } + +/*****************************************************************************/ + +dng_stream::~dng_stream () + { + + } + +/*****************************************************************************/ + +uint64 dng_stream::DoGetLength () + { + + ThrowProgramError (); + + return 0; + + } + +/*****************************************************************************/ + +void dng_stream::DoRead (void * /* data */, + uint32 /* count */, + uint64 /* offset */) + { + + ThrowProgramError (); + + } + +/*****************************************************************************/ + +void dng_stream::DoSetLength (uint64 /* length */) + { + + ThrowProgramError (); + + } + +/*****************************************************************************/ + +void dng_stream::DoWrite (const void * /* data */, + uint32 /* count */, + uint64 /* offset */) + { + + ThrowProgramError (); + + } + +/*****************************************************************************/ + +bool dng_stream::BigEndian () const + { + + return fSwapBytes != (!!qDNGBigEndian); + + } + +/*****************************************************************************/ + +void dng_stream::SetBigEndian (bool bigEndian) + { + + fSwapBytes = (bigEndian != (!!qDNGBigEndian)); + + } + +/*****************************************************************************/ + +void dng_stream::SetBufferSize (dng_memory_allocator &allocator, + uint32 newBufferSize) + { + + if (newBufferSize != fBufferSize && + newBufferSize >= gDNGStreamBlockSize * 2 && + !Data () && + !fBufferDirty) + { + + try + { + + fMemBlock.Reset (allocator.Allocate (newBufferSize)); + + fBuffer = fMemBlock->Buffer_uint8 (); + + fBufferSize = newBufferSize; + + fBufferStart = 0; + fBufferEnd = 0; + fBufferLimit = newBufferSize; + + } + + catch (...) + { + + } + + } + + } + +/*****************************************************************************/ + +const void * dng_stream::Data () const + { + + if (fBufferStart == 0 && fHaveLength && fBufferEnd == fLength) + { + + return fBuffer; + + } + + return NULL; + + } + +/*****************************************************************************/ + +dng_memory_block * dng_stream::AsMemoryBlock (dng_memory_allocator &allocator) + { + + Flush (); + + uint64 len64 = Length (); + + if (len64 > 0xFFFFFFFF) + { + ThrowProgramError (); + } + + uint32 len = (uint32) len64; + + AutoPtr block (allocator.Allocate (len)); + + if (len) + { + + SetReadPosition (0); + + Get (block->Buffer (), len); + + } + + return block.Release (); + + } + +/*****************************************************************************/ + +void dng_stream::SetReadPosition (uint64 offset) + { + + fPosition = offset; + + if (fPosition > Length ()) + { + + ThrowEndOfFile (); + + } + + } + +/*****************************************************************************/ + +uint64 dng_stream::OffsetInOriginalFile () const + { + + return fOffsetInOriginalFile; + + } + +/*****************************************************************************/ + +uint64 dng_stream::PositionInOriginalFile () const + { + + if (fOffsetInOriginalFile == kDNGStreamInvalidOffset) + return kDNGStreamInvalidOffset; + + return fOffsetInOriginalFile + Position (); + + } + +/*****************************************************************************/ + +void dng_stream::Get (void *data, uint32 count, uint32 maxOverRead) + { + + while (count) + { + + // See if the request is totally inside buffer. + + if (fPosition >= fBufferStart && fPosition + count <= fBufferEnd) + { + + memcpy (data, + fBuffer + (uint32) (fPosition - fBufferStart), + count); + + fPosition += count; + + return; + + } + + // See if first part of request is inside buffer. + + if (fPosition >= fBufferStart && fPosition < fBufferEnd) + { + + uint32 block = (uint32) (fBufferEnd - fPosition); + + memcpy (data, + fBuffer + (fPosition - fBufferStart), + block); + + count -= block; + + data = (void *) (((char *) data) + block); + + fPosition += block; + + } + + // Flush buffer if dirty. + + Flush (); + + // Do large reads unbuffered. + + if (count > fBufferSize) + { + DNG_ASSERT(maxOverRead == 0, "Over-read of large size unexpected"); + if (fPosition + count > Length ()) + { + + ThrowEndOfFile (); + + } + + DoRead (data, + count, + fPosition); + + fPosition += count; + + return; + + } + + // Figure out new buffer range. + + fBufferStart = fPosition; + + if (fBufferSize >= gDNGStreamBlockSize) + { + + // Align to a file block. + + fBufferStart &= (uint64) ~((int64) (gDNGStreamBlockSize - 1)); + + } + + fBufferEnd = Min_uint64 (fBufferStart + fBufferSize, Length ()); + + if ((fBufferEnd - fPosition) < maxOverRead) + return; // ep, allow over-read requests + else + if (fBufferEnd <= fPosition) + { + + ThrowEndOfFile (); + + } + + // Read data into buffer. + + dng_abort_sniffer::SniffForAbort (fSniffer); + + DoRead (fBuffer, + (uint32) (fBufferEnd - fBufferStart), + fBufferStart); + + } + + } + +/*****************************************************************************/ + +void dng_stream::SetWritePosition (uint64 offset) + { + + fPosition = offset; + + } + +/*****************************************************************************/ + +void dng_stream::Flush () + { + + if (fBufferDirty) + { + + dng_abort_sniffer::SniffForAbort (fSniffer); + + DoWrite (fBuffer, + (uint32) (fBufferEnd - fBufferStart), + fBufferStart); + + fBufferStart = 0; + fBufferEnd = 0; + fBufferLimit = fBufferSize; + + fBufferDirty = false; + + } + + } + +/*****************************************************************************/ + +void dng_stream::SetLength (uint64 length) + { + + Flush (); + + if (Length () != length) + { + + DoSetLength (length); + + fLength = length; + + } + + } + +/*****************************************************************************/ + +void dng_stream::Put (const void *data, + uint32 count) + { + + // See if we can replace or append to the existing buffer. + + uint64 endPosition = fPosition + count; + + if (fBufferDirty && + fPosition >= fBufferStart && + fPosition <= fBufferEnd && + endPosition <= fBufferLimit) + { + + memcpy (fBuffer + (uint32) (fPosition - fBufferStart), + data, + count); + + if (fBufferEnd < endPosition) + fBufferEnd = endPosition; + + } + + // Else we need to write to the file. + + else + { + + // Write initial part of the data to buffer, if possible. + + if (fBufferDirty && + fPosition >= fBufferStart && + fPosition <= fBufferEnd && + fPosition < fBufferLimit) + { + + uint32 subCount = (uint32) (fBufferLimit - fPosition); + + memcpy (fBuffer + (uint32) (fPosition - fBufferStart), + data, + subCount); + + count -= subCount; + data = (const void *) (((const uint8 *) data) + subCount); + + fPosition = fBufferLimit; + fBufferEnd = fBufferLimit; + + } + + // Write existing buffer. + + Flush (); + + // Figure out how much space we have in buffer from + // current position to end of file block. + + uint64 blockRound = gDNGStreamBlockSize - 1; + + uint64 blockMask = ~((int64) blockRound); + + uint32 alignedSize = (uint32) + (((fPosition + fBufferSize) & blockMask) - fPosition); + + // If write request will not fit in buffer, then write everything except + // for the final unaligned part of the data. + + if (count > alignedSize) + { + + uint32 alignedCount = (uint32) + (((fPosition + count) & blockMask) - fPosition); + + dng_abort_sniffer::SniffForAbort (fSniffer); + + DoWrite (data, alignedCount, fPosition); + + count -= alignedCount; + data = (const void *) (((const uint8 *) data) + alignedCount); + + fPosition += alignedCount; + + } + + // Start a new buffer with small blocks. + + if (count > 0) + { + + fBufferDirty = true; + + fBufferStart = fPosition; + fBufferEnd = endPosition; + fBufferLimit = (fBufferStart + fBufferSize) & blockMask; + + memcpy (fBuffer, + data, + count); + + } + + } + + fPosition = endPosition; + + fLength = Max_uint64 (Length (), fPosition); + + } + +/*****************************************************************************/ + +uint16 dng_stream::Get_uint16 () + { + + uint16 x; + + Get (&x, 2); + + if (fSwapBytes) + { + + x = SwapBytes16 (x); + + } + + return x; + + } + +/*****************************************************************************/ + +void dng_stream::Put_uint16 (uint16 x) + { + + if (fSwapBytes) + { + + x = SwapBytes16 (x); + + } + + Put (&x, 2); + + } + +/*****************************************************************************/ +uint32 dng_stream::Get_uint32 () + { + + uint32 x; + + Get (&x, 4); + + if (fSwapBytes) + { + + x = SwapBytes32 (x); + + } + + return x; + + } + +/*****************************************************************************/ + +void dng_stream::Put_uint32 (uint32 x) + { + + if (fSwapBytes) + { + + x = SwapBytes32 (x); + + } + + Put (&x, 4); + + } + +/*****************************************************************************/ + +uint64 dng_stream::Get_uint64 () + { + + if (fSwapBytes) + { + + union + { + uint32 u32 [2]; + uint64 u64; + } u; + + u.u32 [1] = Get_uint32 (); + u.u32 [0] = Get_uint32 (); + + return u.u64; + + } + + uint64 x; + + Get (&x, 8); + + return x; + + } + +/*****************************************************************************/ + +void dng_stream::Put_uint64 (uint64 x) + { + + if (fSwapBytes) + { + + union + { + uint32 u32 [2]; + uint64 u64; + } u; + + u.u64 = x; + + Put_uint32 (u.u32 [1]); + Put_uint32 (u.u32 [0]); + + } + + else + { + + Put (&x, 8); + + } + + } + +/*****************************************************************************/ + +real32 dng_stream::Get_real32 () + { + + union + { + uint32 i; + real32 r; + } u; + + u.i = Get_uint32 (); + + return u.r; + + } + +/*****************************************************************************/ + +void dng_stream::Put_real32 (real32 x) + { + + if (fSwapBytes) + { + + union + { + uint32 i; + real32 r; + } u; + + u.r = x; + + Put_uint32 (u.i); + + } + + else + { + + Put (&x, 4); + + } + + } + +/*****************************************************************************/ + +real64 dng_stream::Get_real64 () + { + + if (fSwapBytes) + { + + union + { + uint32 i [2]; + real64 r; + } u; + + u.i [1] = Get_uint32 (); + u.i [0] = Get_uint32 (); + + return u.r; + + } + + real64 x; + + Get (&x, 8); + + return x; + + } + +/*****************************************************************************/ + +void dng_stream::Put_real64 (real64 x) + { + + if (fSwapBytes) + { + + union + { + uint32 i [2]; + real64 r; + } u; + + u.r = x; + + Put_uint32 (u.i [1]); + Put_uint32 (u.i [0]); + + } + + else + { + + Put (&x, 8); + + } + + } + +/*****************************************************************************/ + +void dng_stream::Get_CString (char *data, uint32 maxLength) + { + + memset (data, 0, maxLength); + + uint32 index = 0; + + while (true) + { + + char c = (char) Get_uint8 (); + + if (index + 1 < maxLength) + data [index++] = c; + + if (c == 0) + break; + + } + + } + +/*****************************************************************************/ + +void dng_stream::Get_UString (char *data, uint32 maxLength) + { + + memset (data, 0, maxLength); + + uint32 index = 0; + + while (true) + { + + char c = (char) Get_uint16 (); + + if (index + 1 < maxLength) + data [index++] = (char) c; + + if (c == 0) + break; + + } + + } + +/*****************************************************************************/ + +void dng_stream::PutZeros (uint64 count) + { + + const uint32 kZeroBufferSize = 4096; + + if (count >= kZeroBufferSize) + { + + dng_memory_data zeroBuffer (kZeroBufferSize); + + DoZeroBytes (zeroBuffer.Buffer (), + kZeroBufferSize); + + while (count) + { + + uint64 blockSize = Min_uint64 (count, kZeroBufferSize); + + Put (zeroBuffer.Buffer (), (uint32) blockSize); + + count -= blockSize; + + } + + } + + else + { + + uint32 count32 = (uint32) count; + + for (uint32 j = 0; j < count32; j++) + { + + Put_uint8 (0); + + } + + } + + } + +/*****************************************************************************/ + +void dng_stream::PadAlign2 () + { + + PutZeros (Position () & 1); + + } + +/*****************************************************************************/ + +void dng_stream::PadAlign4 () + { + + PutZeros ((4 - (Position () & 3)) & 3); + + } + +/*****************************************************************************/ + +uint32 dng_stream::TagValue_uint32 (uint32 tagType) + { + + switch (tagType) + { + + case ttByte: + return (uint32) Get_uint8 (); + + case ttShort: + return (uint32) Get_uint16 (); + + case ttLong: + case ttIFD: + return Get_uint32 (); + + } + + real64 x = TagValue_real64 (tagType); + + if (x < 0.0) + x = 0.0; + + if (x > (real64) 0xFFFFFFFF) + x = (real64) 0xFFFFFFFF; + + return (uint32) (x + 0.5); + + } + +/*****************************************************************************/ + +int32 dng_stream::TagValue_int32 (uint32 tagType) + { + + switch (tagType) + { + + case ttSByte: + return (int32) Get_int8 (); + + case ttSShort: + return (int32) Get_int16 (); + + case ttSLong: + return Get_int32 (); + + } + + real64 x = TagValue_real64 (tagType); + + if (x < 0.0) + { + + if (x < -2147483648.0) + x = -2147483648.0; + + return (int32) (x - 0.5); + + } + + else + { + + if (x > 2147483647.0) + x = 2147483647.0; + + return (int32) (x + 0.5); + + } + + } + +/*****************************************************************************/ + +dng_urational dng_stream::TagValue_urational (uint32 tagType) + { + + dng_urational result; + + result.n = 0; + result.d = 1; + + switch (tagType) + { + + case ttRational: + { + + result.n = Get_uint32 (); + result.d = Get_uint32 (); + + break; + + } + + case ttSRational: + { + + int32 n = Get_int32 (); + int32 d = Get_int32 (); + + if ((n < 0) == (d < 0)) + { + + if (d < 0) + { + n = -n; + d = -d; + } + + result.n = (uint32) n; + result.d = (uint32) d; + + } + + break; + + } + + case ttByte: + case ttShort: + case ttLong: + case ttIFD: + { + + result.n = TagValue_uint32 (tagType); + + break; + + } + + case ttSByte: + case ttSShort: + case ttSLong: + { + + int32 n = TagValue_int32 (tagType); + + if (n > 0) + { + result.n = (uint32) n; + } + + break; + + } + + default: + { + + real64 x = TagValue_real64 (tagType); + + if (x > 0.0) + { + + while (result.d < 10000 && x < 1000000) + { + + result.d *= 10; + + x *= 10.0; + + } + + result.n = (uint32) (x + 0.5); + + } + + } + + } + + return result; + + } + +/*****************************************************************************/ + +dng_srational dng_stream::TagValue_srational (uint32 tagType) + { + + dng_srational result; + + result.n = 0; + result.d = 1; + + switch (tagType) + { + + case ttSRational: + { + + result.n = Get_int32 (); + result.d = Get_int32 (); + + break; + + } + + default: + { + + real64 x = TagValue_real64 (tagType); + + if (x > 0.0) + { + + while (result.d < 10000 && x < 1000000.0) + { + + result.d *= 10; + + x *= 10.0; + + } + + result.n = (int32) (x + 0.5); + + } + + else + { + + while (result.d < 10000 && x > -1000000.0) + { + + result.d *= 10; + + x *= 10.0; + + } + + result.n = (int32) (x - 0.5); + + } + + } + + } + + return result; + + } + +/*****************************************************************************/ + +real64 dng_stream::TagValue_real64 (uint32 tagType) + { + + switch (tagType) + { + + case ttByte: + case ttShort: + case ttLong: + case ttIFD: + return (real64) TagValue_uint32 (tagType); + + case ttSByte: + case ttSShort: + case ttSLong: + return (real64) TagValue_int32 (tagType); + + case ttRational: + { + + uint32 n = Get_uint32 (); + uint32 d = Get_uint32 (); + + if (d == 0) + return 0.0; + else + return (real64) n / (real64) d; + + } + + case ttSRational: + { + + int32 n = Get_int32 (); + int32 d = Get_int32 (); + + if (d == 0) + return 0.0; + else + return (real64) n / (real64) d; + + } + + case ttFloat: + return (real64) Get_real32 (); + + case ttDouble: + return Get_real64 (); + + } + + return 0.0; + + } + +/*****************************************************************************/ + +void dng_stream::CopyToStream (dng_stream &dstStream, + uint64 count) + { + + uint8 smallBuffer [1024]; + + if (count <= sizeof (smallBuffer)) + { + + Get (smallBuffer, (uint32) count); + + dstStream.Put (smallBuffer, (uint32) count); + + } + + else + { + + const uint32 bigBufferSize = (uint32) Min_uint64 (kBigBufferSize, + count); + + dng_memory_data bigBuffer (bigBufferSize); + + while (count) + { + + uint32 blockCount = (uint32) Min_uint64 (bigBufferSize, + count); + + Get (bigBuffer.Buffer (), + blockCount); + + dstStream.Put (bigBuffer.Buffer (), + blockCount); + + count -= blockCount; + + } + + } + + } + +/*****************************************************************************/ + +void dng_stream::DuplicateStream (dng_stream &dstStream) + { + + // Turn off sniffers for this operation. + + TempStreamSniffer noSniffer1 (*this , NULL); + TempStreamSniffer noSniffer2 (dstStream, NULL); + + // First grow the destination stream if required, in an attempt to + // reserve any needed space before overwriting the existing data. + + if (dstStream.Length () < Length ()) + { + dstStream.SetLength (Length ()); + } + + SetReadPosition (0); + + dstStream.SetWritePosition (0); + + CopyToStream (dstStream, Length ()); + + dstStream.Flush (); + + dstStream.SetLength (Length ()); + + } + +/*****************************************************************************/ + +dng_stream_contiguous_read_hint::dng_stream_contiguous_read_hint + (dng_stream &stream, + dng_memory_allocator &allocator, + uint64 offset, + uint64 count) + + : fStream (stream) + , fAllocator (allocator) + , fOldBufferSize (stream.BufferSize ()) + + { + + fStream.Flush (); // Cannot change buffer size with dirty buffer + + // Don't bother changing buffer size if only a small change. + + if (count > fOldBufferSize * 4) + { + + // Round contiguous size up and down to stream blocks. + + uint64 blockRound = gDNGStreamBlockSize - 1; + + uint64 blockMask = ~((int64) blockRound); + + count = (count + (offset & blockRound) + blockRound) & blockMask; + + // Limit to maximum buffer size. + + uint64 newBufferSize = Min_uint64 (gDNGMaxStreamBufferSize, count); + + // To avoid reading too many bytes with the final read, adjust buffer + // size the to make an exact number of buffers fit. + + uint64 numBuffers = (count + newBufferSize - 1) / newBufferSize; + + newBufferSize = (count + numBuffers - 1) / numBuffers; + + // Finally round up to a block size. + + newBufferSize = (newBufferSize + blockRound) & blockMask; + + // Change the buffer size. + + fStream.SetBufferSize (fAllocator, (uint32) newBufferSize); + + } + + } + +/*****************************************************************************/ + +dng_stream_contiguous_read_hint::~dng_stream_contiguous_read_hint () + { + + fStream.SetBufferSize (fAllocator, fOldBufferSize); + + } + +/*****************************************************************************/ + +TempBigEndian::TempBigEndian (dng_stream &stream, + bool bigEndian) + + : fStream (stream) + , fOldSwap (stream.SwapBytes ()) + + { + + fStream.SetBigEndian (bigEndian); + + } + +/*****************************************************************************/ + +TempBigEndian::~TempBigEndian () + { + + fStream.SetSwapBytes (fOldSwap); + + } + +/*****************************************************************************/ + +TempStreamSniffer::TempStreamSniffer (dng_stream &stream, + dng_abort_sniffer *sniffer) + + : fStream (stream) + , fOldSniffer (stream.Sniffer ()) + + { + + fStream.SetSniffer (sniffer); + + } + +/*****************************************************************************/ + +TempStreamSniffer::~TempStreamSniffer () + { + + fStream.SetSniffer (fOldSniffer); + + } + +/*****************************************************************************/ diff --git a/dng/dng_stream.h b/dng/dng_stream.h new file mode 100644 index 0000000..014b331 --- /dev/null +++ b/dng/dng_stream.h @@ -0,0 +1,766 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** Data stream abstraction for serializing and deserializing sequences of + * basic types and RAW image data. + */ + +/*****************************************************************************/ + +#ifndef __dng_stream__ +#define __dng_stream__ + +/*****************************************************************************/ + +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_types.h" +#include "dng_memory.h" +#include "dng_rational.h" +#include "dng_uncopyable.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +// Constants for invalid offset in streams. + +const uint64 kDNGStreamInvalidOffset = (uint64) (int64) -1; + +/*****************************************************************************/ + +/// Base stream abstraction. Has support for going between stream and pointer +/// abstraction. + +class dng_stream: private dng_uncopyable + { + + public: + + enum + { + + kSmallBufferSize = 8 * 1024, + kBigBufferSize = 64 * 1024, + + kDefaultBufferSize = kSmallBufferSize + + }; + + private: + + bool fSwapBytes; + + bool fHaveLength; + + uint64 fLength; + + const uint64 fOffsetInOriginalFile; + + uint64 fPosition; + + AutoPtr fMemBlock; + + uint8 *fBuffer; + + uint32 fBufferSize; + + uint64 fBufferStart; + uint64 fBufferEnd; + uint64 fBufferLimit; + + bool fBufferDirty; + + dng_abort_sniffer *fSniffer; + + protected: + + dng_stream (dng_abort_sniffer *sniffer = NULL, + uint32 bufferSize = kDefaultBufferSize, + uint64 offsetInOriginalFile = kDNGStreamInvalidOffset); + + virtual uint64 DoGetLength (); + + virtual void DoRead (void *data, + uint32 count, + uint64 offset); + + virtual void DoSetLength (uint64 length); + + virtual void DoWrite (const void *data, + uint32 count, + uint64 offset); + + public: + + /// Construct a stream with initial data. + /// \param data Pointer to initial contents of stream. + /// \param count Number of bytes data is valid for. + /// \param offsetInOriginalFile If data came from a file originally, + /// offset can be saved here for later use. + + dng_stream (const void *data, + uint32 count, + uint64 offsetInOriginalFile = kDNGStreamInvalidOffset); + + virtual ~dng_stream (); + + /// Getter for whether stream is swapping byte order on input/output. + /// \retval If true, data will be swapped on input/output. + + bool SwapBytes () const + { + return fSwapBytes; + } + + /// Setter for whether stream is swapping byte order on input/output. + /// \param swapBytes If true, stream will swap byte order on input or + /// output for future reads/writes. + + void SetSwapBytes (bool swapBytes) + { + fSwapBytes = swapBytes; + } + + /// Getter for whether data in stream is big endian. + /// \retval If true, data in stream is big endian. + + bool BigEndian () const; + + /// Setter for whether data in stream is big endian. + /// \param bigEndian If true, data in stream is big endian. + + void SetBigEndian (bool bigEndian = true); + + /// Getter for whether data in stream is big endian. + /// \retval If true, data in stream is big endian. + + bool LittleEndian () const + { + return !BigEndian (); + } + + /// Setter for whether data in stream is big endian. + /// \param littleEndian If true, data in stream is big endian. + + void SetLittleEndian (bool littleEndian = true) + { + SetBigEndian (!littleEndian); + } + + /// Returns the size of the buffer used by the stream. + + uint32 BufferSize () const + { + return fBufferSize; + } + + /// Change the buffer size on the stream, if possible. + + void SetBufferSize (dng_memory_allocator &allocator, + uint32 newBufferSize); + + /// Getter for length of data in stream. + /// \retval Length of readable data in stream. + + uint64 Length () + { + + if (!fHaveLength) + { + + fLength = DoGetLength (); + + fHaveLength = true; + + } + + return fLength; + + } + + /// Getter for current offset in stream. + /// \retval current offset from start of stream. + + uint64 Position () const + { + return fPosition; + } + + /// Getter for current position in original file, taking into account + /// OffsetInOriginalFile stream data was taken from. + /// \retval kInvalidOffset if no offset in original file is set, sum + /// of offset in original file and current position otherwise. + + uint64 PositionInOriginalFile () const; + + /// Getter for offset in original file. + /// \retval kInvalidOffset if no offset in original file is set, + /// offset in original file otherwise. + + uint64 OffsetInOriginalFile () const; + + /// Return pointer to stream contents if the stream is entirely + /// available as a single memory block, NULL otherwise. + + const void * Data () const; + + /// Return the entire stream as a single memory block. + /// This works for all streams, but requires copying the data to a new buffer. + /// \param allocator Allocator used to allocate memory. + + dng_memory_block * AsMemoryBlock (dng_memory_allocator &allocator); + + /// Seek to a new position in stream for reading. + + void SetReadPosition (uint64 offset); + + /// Skip forward in stream. + /// \param delta Number of bytes to skip forward. + + void Skip (uint64 delta) + { + SetReadPosition (Position () + delta); + } + + /// Quick check to see if data range in completely buffered. + + bool DataInBuffer (uint32 count, + uint64 offset) + { + return (offset >= fBufferStart && + offset + count <= fBufferEnd); + } + + /// Get data from stream. Exception is thrown and no data is read if + /// insufficient data available in stream. + /// \param data Buffer to put data into. Must be valid for count bytes. + /// \param count Bytes of data to read. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + void Get (void *data, uint32 count, uint32 maxOverRead=0); + + /// Seek to a new position in stream for writing. + + void SetWritePosition (uint64 offset); + + /// Force any stored data in stream to be written to underlying storage. + + void Flush (); + + /// Set length of available data. + /// \param length Number of bytes of avialble data in stream. + + void SetLength (uint64 length); + + /// Write data to stream. + /// \param data Buffer of data to write to stream. + /// \param count Bytes of in data. + + void Put (const void *data, uint32 count); + + /// Get an unsigned 8-bit integer from stream and advance read position. + /// \retval One unsigned 8-bit integer. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + uint8 Get_uint8 () + { + + // Fast check to see if in buffer + + if (fPosition >= fBufferStart && fPosition < fBufferEnd) + { + + return fBuffer [fPosition++ - fBufferStart]; + + } + + // Not in buffer, let main routine do the work. + + uint8 x; + + Get (&x, 1); + + return x; + + } + + /// Put an unsigned 8-bit integer to stream and advance write position. + /// \param x One unsigned 8-bit integer. + + void Put_uint8 (uint8 x) + { + + if (fBufferDirty && + fPosition >= fBufferStart && + fPosition <= fBufferEnd && + fPosition < fBufferLimit) + { + + fBuffer [fPosition - fBufferStart] = x; + + fPosition++; + + if (fBufferEnd < fPosition) + fBufferEnd = fPosition; + + fLength = Max_uint64 (Length (), fPosition); + + } + + else + { + + Put (&x, 1); + + } + + } + + /// Get an unsigned 16-bit integer from stream and advance read position. + /// Byte swap if byte swapping is turned on. + /// \retval One unsigned 16-bit integer. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + uint16 Get_uint16 (); + + /// Put an unsigned 16-bit integer to stream and advance write position. + /// Byte swap if byte swapping is turned on. + /// \param x One unsigned 16-bit integer. + + void Put_uint16 (uint16 x); + + /// Get an unsigned 32-bit integer from stream and advance read position. + /// Byte swap if byte swapping is turned on. + /// \retval One unsigned 32-bit integer. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + uint32 Get_uint32(); + +#if !qDNGBigEndian + inline // ep, enable compiler inlining + uint32 Get_uint32_LE () + { + + uint32 x; + + Get (&x, 4, 3); // Allow 3-byte overread (undefined data returned but not used) + + // No check for fSwapBytes + + return x; + + } +#endif + + /// Put an unsigned 32-bit integer to stream and advance write position. + /// Byte swap if byte swapping is turned on. + /// \param x One unsigned 32-bit integer. + + void Put_uint32 (uint32 x); + + /// Get an unsigned 64-bit integer from stream and advance read position. + /// Byte swap if byte swapping is turned on. + /// \retval One unsigned 64-bit integer. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + uint64 Get_uint64 (); + + /// Put an unsigned 64-bit integer to stream and advance write position. + /// Byte swap if byte swapping is turned on. + /// \param x One unsigned 64-bit integer. + + void Put_uint64 (uint64 x); + + /// Get one 8-bit integer from stream and advance read position. + /// \retval One 8-bit integer. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + int8 Get_int8 () + { + return (int8) Get_uint8 (); + } + + /// Put one 8-bit integer to stream and advance write position. + /// \param x One 8-bit integer. + + void Put_int8 (int8 x) + { + Put_uint8 ((uint8) x); + } + + /// Get one 16-bit integer from stream and advance read position. + /// Byte swap if byte swapping is turned on. + /// \retval One 16-bit integer. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + int16 Get_int16 () + { + return (int16) Get_uint16 (); + } + + /// Put one 16-bit integer to stream and advance write position. + /// Byte swap if byte swapping is turned on. + /// \param x One 16-bit integer. + + void Put_int16 (int16 x) + { + Put_uint16 ((uint16) x); + } + + /// Get one 32-bit integer from stream and advance read position. + /// Byte swap if byte swapping is turned on. + /// \retval One 32-bit integer. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + int32 Get_int32 () + { + return (int32) Get_uint32 (); + } + + /// Put one 32-bit integer to stream and advance write position. + /// Byte swap if byte swapping is turned on. + /// \param x One 32-bit integer. + + void Put_int32 (int32 x) + { + Put_uint32 ((uint32) x); + } + + /// Get one 64-bit integer from stream and advance read position. + /// Byte swap if byte swapping is turned on. + /// \retval One 64-bit integer. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + int64 Get_int64 () + { + return (int64) Get_uint64 (); + } + + /// Put one 64-bit integer to stream and advance write position. + /// Byte swap if byte swapping is turned on. + /// \param x One 64-bit integer. + + void Put_int64 (int64 x) + { + Put_uint64 ((uint64) x); + } + + /// Get one 32-bit IEEE floating-point number from stream and advance + /// read position. Byte swap if byte swapping is turned on. + /// \retval One 32-bit IEEE floating-point number. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + real32 Get_real32 (); + + /// Put one 32-bit IEEE floating-point number to stream and advance write + /// position. Byte swap if byte swapping is turned on. + /// \param x One 32-bit IEEE floating-point number. + + void Put_real32 (real32 x); + + /// Get one 64-bit IEEE floating-point number from stream and advance + /// read position. Byte swap if byte swapping is turned on. + /// \retval One 64-bit IEEE floating-point number . + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + real64 Get_real64 (); + + /// Put one 64-bit IEEE floating-point number to stream and advance write + /// position. Byte swap if byte swapping is turned on. + /// \param x One64-bit IEEE floating-point number. + + void Put_real64 (real64 x); + + /// Get an 8-bit character string from stream and advance read position. + /// Routine always reads until a NUL character (8-bits of zero) is read. + /// (That is, only maxLength bytes will be returned in buffer, but the + /// stream is always advanced until a NUL is read or EOF is reached.) + /// \param data Buffer in which string is returned. + /// \param maxLength Maximum number of bytes to place in buffer. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if stream runs out before NUL is seen. + + void Get_CString (char *data, + uint32 maxLength); + + /// Get a 16-bit character string from stream and advance read position. + /// 16-bit characters are truncated to 8-bits. + /// Routine always reads until a NUL character (16-bits of zero) is read. + /// (That is, only maxLength bytes will be returned in buffer, but the + /// stream is always advanced until a NUL is read or EOF is reached.) + /// \param data Buffer to place string in. + /// \param maxLength Maximum number of bytes to place in buffer. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if stream runs out before NUL is seen. + + void Get_UString (char *data, + uint32 maxLength); + + /// Writes the specified number of zero bytes to stream. + /// \param count Number of zero bytes to write. + + void PutZeros (uint64 count); + + /// Writes zeros to align the stream position to a multiple of 2. + + void PadAlign2 (); + + /// Writes zeros to align the stream position to a multiple of 4. + + void PadAlign4 (); + + /// Get a value of size indicated by tag type from stream and advance + /// read position. Byte swap if byte swapping is turned on and tag type + /// is larger than a byte. Value is returned as an unsigned 32-bit integer. + /// \param tagType Tag type of data stored in stream. + /// \retval One unsigned 32-bit integer. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + uint32 TagValue_uint32 (uint32 tagType); + + /// Get a value of size indicated by tag type from stream and advance read + /// position. Byte swap if byte swapping is turned on and tag type is larger + /// than a byte. Value is returned as a 32-bit integer. + /// \param tagType Tag type of data stored in stream. + /// \retval One 32-bit integer. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + int32 TagValue_int32 (uint32 tagType); + + /// Get a value of size indicated by tag type from stream and advance read + /// position. Byte swap if byte swapping is turned on and tag type is larger + /// than a byte. Value is returned as a dng_urational. + /// \param tagType Tag type of data stored in stream. + /// \retval One dng_urational. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + dng_urational TagValue_urational (uint32 tagType); + + /// Get a value of size indicated by tag type from stream and advance read + /// position. Byte swap if byte swapping is turned on and tag type is larger + /// than a byte. Value is returned as a dng_srational. + /// \param tagType Tag type of data stored in stream. + /// \retval One dng_srational. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + dng_srational TagValue_srational (uint32 tagType); + + /// Get a value of size indicated by tag type from stream and advance read + /// position. Byte swap if byte swapping is turned on and tag type is larger + /// than a byte. Value is returned as a 64-bit IEEE floating-point number. + /// \param tagType Tag type of data stored in stream. + /// \retval One 64-bit IEEE floating-point number. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + real64 TagValue_real64 (uint32 tagType); + + /// Getter for sniffer associated with stream. + /// \retval The sniffer for this stream. + + dng_abort_sniffer * Sniffer () const + { + return fSniffer; + } + + /// Putter for sniffer associated with stream. + /// \param sniffer The new sniffer to use (or NULL for none). + + void SetSniffer (dng_abort_sniffer *sniffer) + { + fSniffer = sniffer; + } + + /// Copy a specified number of bytes to a target stream. + /// \param dstStream The target stream. + /// \param count The number of bytes to copy. + + virtual void CopyToStream (dng_stream &dstStream, + uint64 count); + + /// Makes the target stream a copy of this stream. + /// \param dstStream The target stream. + + void DuplicateStream (dng_stream &dstStream); + + }; + +/*****************************************************************************/ + +class dng_stream_double_buffered : public dng_stream + { + + private: + + dng_stream &fStream; + + public: + + dng_stream_double_buffered (dng_stream &stream, + uint32 bufferSize = kDefaultBufferSize) + + : dng_stream ((dng_abort_sniffer *) NULL, + bufferSize, + stream.OffsetInOriginalFile ()) + + , fStream (stream) + + { + SetBigEndian (fStream.BigEndian ()); + } + + protected: + + virtual uint64 DoGetLength () + { + return fStream.Length (); + } + + virtual void DoRead (void *data, + uint32 count, + uint64 offset) + { + fStream.SetReadPosition (offset); + fStream.Get (data, count); + } + + }; + +/*****************************************************************************/ + +class dng_stream_contiguous_read_hint + { + + private: + + dng_stream &fStream; + + dng_memory_allocator &fAllocator; + + uint32 fOldBufferSize; + + public: + + dng_stream_contiguous_read_hint (dng_stream &stream, + dng_memory_allocator &allocator, + uint64 offset, + uint64 count); + + ~dng_stream_contiguous_read_hint (); + + }; + +/*****************************************************************************/ + +class TempBigEndian + { + + private: + + dng_stream & fStream; + + bool fOldSwap; + + public: + + TempBigEndian (dng_stream &stream, + bool bigEndian = true); + + virtual ~TempBigEndian (); + + }; + +/*****************************************************************************/ + +class TempLittleEndian: public TempBigEndian + { + + public: + + TempLittleEndian (dng_stream &stream, + bool littleEndian = true) + + : TempBigEndian (stream, !littleEndian) + + { + } + + virtual ~TempLittleEndian () + { + } + + }; + +/*****************************************************************************/ + +class TempStreamSniffer: private dng_uncopyable + { + + private: + + dng_stream & fStream; + + dng_abort_sniffer *fOldSniffer; + + public: + + TempStreamSniffer (dng_stream &stream, + dng_abort_sniffer *sniffer); + + ~TempStreamSniffer (); + + }; + +/*****************************************************************************/ + +class PreserveStreamReadPosition: private dng_uncopyable + { + + private: + + dng_stream & fStream; + + uint64 fPosition; + + public: + + PreserveStreamReadPosition (dng_stream &stream) + + : fStream (stream) + , fPosition (stream.Position ()) + + { + } + + ~PreserveStreamReadPosition () + { + fStream.SetReadPosition (fPosition); + } + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_string.cpp b/dng/dng_string.cpp new file mode 100644 index 0000000..c2db08e --- /dev/null +++ b/dng/dng_string.cpp @@ -0,0 +1,2459 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_string.h" + +#include "dng_assertions.h" +#include "dng_exceptions.h" +#include "dng_flags.h" +#include "dng_mutex.h" +#include "dng_utils.h" +#include "dng_safe_arithmetic.h" + +#if qMacOS +#include +#endif + +#if qWinOS +#include +#define SORT_DIGITSASNUMBERS 8 +#endif + +#if qiPhone || qAndroid +#include // for isdigit +#endif + +/*****************************************************************************/ + +const uint32 kREPLACEMENT_CHARACTER = 0x0000FFFD; + +/*****************************************************************************/ + +// Returns the length of the zero-terminated string 's'. Throws a dng_exception +// if the length of 's' is too large to be represented as a uint32. + +static uint32 strlenAsUint32 (const char *s) + { + + uint32 lengthAsUint32 = 0; + + ConvertUnsigned (strlen (s), &lengthAsUint32); + + return lengthAsUint32; + + } + +/*****************************************************************************/ + +// Checks whether there is enough space left in the buffer pointed to by +// 'currentPos' to write at least 'space' elements of type T (to positions +// currentPos[0] through currentPos[space - 1]. Throws a dng_exception if +// there is not enough space left in the buffer. 'bufferEnd' should point one +// element beyond the end of the buffer. For example, if the buffer is "T +// buffer[3];", then bufferEnd should point to T + 3. + +template +static void CheckSpaceLeftInBuffer(const T *currentPos, + const T *bufferEnd, + size_t space) + { + + if (bufferEnd < currentPos || static_cast (bufferEnd - currentPos) < space) + { + ThrowMemoryFull ("Buffer overrun"); + } + + } + +/*****************************************************************************/ + +#if qMacOS + +static void Assign_Multibyte (dng_string &dngString, + const char *otherString, + TextEncoding encoding) + { + + dng_safe_uint32 aSize (strlenAsUint32 (otherString)); + + if (aSize.Get () > 0) + { + + dng_safe_uint32 aBufSize = aSize * 6u + 256u; + + dng_memory_data aBuf (aBufSize + 1u); + + UnicodeMapping aMapping; + + aMapping.unicodeEncoding = ::CreateTextEncoding (kTextEncodingUnicodeV3_0, + kUnicodeNoSubset, + kUnicodeUTF8Format); + + aMapping.otherEncoding = encoding; + aMapping.mappingVersion = kUnicodeUseLatestMapping; + + TextToUnicodeInfo aInfo = NULL; + + if (::CreateTextToUnicodeInfo (&aMapping, &aInfo) == noErr) + { + + ByteCount aInput = 0; + ByteCount aOutput = 0; + + ::ConvertFromTextToUnicode (aInfo, + aSize.Get (), + otherString, + kUnicodeUseFallbacksMask | + kUnicodeLooseMappingsMask, + 0, + NULL, + NULL, + NULL, + aBufSize.Get (), + &aInput, + &aOutput, + (UniChar *) aBuf.Buffer ()); + + ::DisposeTextToUnicodeInfo (&aInfo); + + if (aOutput > 0 && aOutput <= aBufSize.Get ()) + { + + char *aBufChar = aBuf.Buffer_char (); + + aBufChar [aOutput] = 0; + + dngString.Set (aBufChar); + + return; + + } + + } + + } + + dngString.Clear (); + + } + +static uint32 Extract_Multibyte (const dng_string &dngString, + dng_memory_data &buffer, + TextEncoding encoding) + { + + dng_safe_uint32 aSize (dngString.Length ()); + + if (aSize.Get () > 0) + { + + dng_safe_uint32 aBufSize = aSize * 2u + 256u; + + dng_memory_data tempBuffer (aBufSize); + + UnicodeMapping aMapping; + + aMapping.unicodeEncoding = ::CreateTextEncoding (kTextEncodingUnicodeV3_0, + kUnicodeNoSubset, + kUnicodeUTF8Format); + + aMapping.otherEncoding = encoding; + aMapping.mappingVersion = kUnicodeUseLatestMapping; + + UnicodeToTextInfo aInfo = NULL; + + if (::CreateUnicodeToTextInfo (&aMapping, &aInfo) == noErr) + { + + ByteCount aInput = 0; + ByteCount aOutput = 0; + + ::ConvertFromUnicodeToText (aInfo, + aSize.Get (), + (const UniChar *) dngString.Get (), + kUnicodeUseFallbacksMask | + kUnicodeLooseMappingsMask | + kUnicodeDefaultDirectionMask, + 0, + NULL, + NULL, + NULL, + aBufSize.Get (), + &aInput, + &aOutput, + tempBuffer.Buffer_char ()); + + ::DisposeUnicodeToTextInfo (&aInfo); + + if (aOutput > 0) + { + + uint32 aOutputAsUint32 = 0; + + ConvertUnsigned (aOutput, &aOutputAsUint32); + + buffer.Allocate (dng_safe_uint32 (aOutputAsUint32) + 1u); + + memcpy (buffer.Buffer (), + tempBuffer.Buffer (), + aOutputAsUint32); + + buffer.Buffer_char () [aOutputAsUint32] = 0; + + return aOutputAsUint32; + + } + + } + + } + + buffer.Allocate (1); + + buffer.Buffer_char () [0] = 0; + + return 0; + + } + +static void Assign_SystemEncoding (dng_string &dngString, + const char *otherString) + { + + TextEncoding aEncoding; + + ::UpgradeScriptInfoToTextEncoding (smSystemScript, + kTextLanguageDontCare, + kTextRegionDontCare, + NULL, + &aEncoding); + + Assign_Multibyte (dngString, + otherString, + aEncoding); + + } + +static uint32 Extract_SystemEncoding (const dng_string &dngString, + dng_memory_data &buffer) + { + + TextEncoding aEncoding; + + ::UpgradeScriptInfoToTextEncoding (smSystemScript, + kTextLanguageDontCare, + kTextRegionDontCare, + NULL, + &aEncoding); + + return Extract_Multibyte (dngString, + buffer, + aEncoding); + + } + +static void Assign_JIS_X208_1990 (dng_string &dngString, + const char *otherString) + { + + Assign_Multibyte (dngString, + otherString, + kTextEncodingJIS_X0208_90); + + } + +#endif + +/*****************************************************************************/ + +#if qWinOS + +static void Assign_Multibyte (dng_string &dngString, + const char *otherString, + UINT encoding) + { + + DNG_ASSERT (sizeof (WCHAR) == 2, "WCHAR must be 2 bytes"); + + const dng_safe_uint32 otherStringLen (strlenAsUint32 (otherString)); + + const dng_safe_int32 aSize (otherStringLen); + + if (aSize.Get () > 0) + { + + dng_safe_uint32 aBufCharsUint32 = otherStringLen * 3u + 128u; + + dng_safe_int32 aBufChars (aBufCharsUint32); + + dng_safe_uint32 bytesToAllocate = (aBufCharsUint32 + 1u) * 2u; + + dng_memory_data aBuf (bytesToAllocate); + + int aResult = ::MultiByteToWideChar (encoding, + 0, + otherString, + aSize.Get (), + (WCHAR *) aBuf.Buffer (), + aBufChars.Get ()); + + if (aResult > 0 && aResult <= aBufChars.Get ()) + { + + uint16 * aUTF16 = aBuf.Buffer_uint16 (); + + aUTF16 [aResult] = 0; + + dngString.Set_UTF16 (aUTF16); + + return; + + } + + } + + dngString.Clear (); + + } + +static uint32 Extract_Multibyte (const dng_string &dngString, + dng_memory_data &buffer, + UINT encoding) + { + + DNG_ASSERT (sizeof (WCHAR) == 2, "WCHAR must be 2 bytes"); + + dng_memory_data sBuffer; + + int aCount = dngString.Get_UTF16 (sBuffer); + + if (aCount < 0) + { + return 0; + } + + dng_safe_uint32 aCountAsUint32 (static_cast (aCount)); + + dng_safe_uint32 dBufSize = aCountAsUint32 * 2u + 256u; + + dng_memory_data dBuffer (dBufSize); + + int aResult = ::WideCharToMultiByte (encoding, + 0, + (WCHAR *) sBuffer.Buffer (), + aCount, + dBuffer.Buffer_char (), + dBufSize.Get (), + NULL, + NULL); + + if (aResult < 0) + aResult = 0; + + dng_safe_uint32 aResultAsUint32 (static_cast (aResult)); + + buffer.Allocate (aResultAsUint32 + 1u); + + memcpy (buffer.Buffer (), + dBuffer.Buffer (), + aResult); + + buffer.Buffer_char () [aResult] = 0; + + return aResultAsUint32.Get (); + + } + +static void Assign_SystemEncoding (dng_string &dngString, + const char *otherString) + { + + Assign_Multibyte (dngString, + otherString, + ::GetACP ()); + + } + +static uint32 Extract_SystemEncoding (const dng_string &dngString, + dng_memory_data &buffer) + { + + return Extract_Multibyte (dngString, + buffer, + ::GetACP ()); + + } + +static void Assign_JIS_X208_1990 (dng_string &dngString, + const char *otherString) + { + + // From MSDN documentation: 20932 = JIS X 0208-1990 & 0121-1990 + + const UINT kJIS = 20932; + + Assign_Multibyte (dngString, + otherString, + kJIS); + + } + +#endif + +/*****************************************************************************/ + +static bool IsASCII (const char *s) + { + + if (!s) + { + + return true; + + } + + while (true) + { + + uint8 c = (uint8) *(s++); + + if (c == 0) + { + + break; + + } + + if (c & 0x80) + { + + return false; + + } + + } + + return true; + + } + +/*****************************************************************************/ + +dng_string::dng_string () + + : fData () + + { + + } + +/*****************************************************************************/ + +dng_string::dng_string (const dng_string &s) + + : fData () + + { + + Set (s.Get ()); + + } + +/*****************************************************************************/ + +dng_string & dng_string::operator= (const dng_string &s) + { + + if (this != &s) + { + + Set (s.Get ()); + + } + + return *this; + + } + +/*****************************************************************************/ + +dng_string::~dng_string () + { + + } + +/*****************************************************************************/ + +const char * dng_string::Get () const + { + + if (fData.Buffer ()) + { + + return fData.Buffer_char (); + + } + + return ""; + + } + +/*****************************************************************************/ + +bool dng_string::IsASCII () const + { + + return ::IsASCII (Get ()); + + } + +/*****************************************************************************/ + +void dng_string::Set (const char *s) + { + + // Measure the new length. + + uint32 newLen = (s != NULL ? strlenAsUint32 (s) : 0); + + // If it is a NULL string, then clear the buffer. + + if (newLen == 0) + { + + fData.Clear (); + + } + + // Else we need to copy the bytes. + + else + { + + uint32 oldLen = Length (); + + // We might be setting this string to a sub-string of itself, + // so don't reallocate the data unless the string is getting + // longer. + + if (newLen > oldLen) + { + + fData.Clear (); + + fData.Allocate (dng_safe_uint32 (newLen) + 1u); + + } + + char *d = fData.Buffer_char (); + + for (uint32 k = 0; k <= newLen; k++) + { + + d [k] = s [k]; + + } + + } + + } + +/*****************************************************************************/ + +void dng_string::Set_ASCII (const char *s) + { + + if (::IsASCII (s)) + { + + Set (s); + + } + + else + { + + Set_SystemEncoding (s); + + } + + } + +/*****************************************************************************/ + +void dng_string::Set_UTF8 (const char *s) + { + + dng_safe_uint32 len (strlenAsUint32 (s)); + + const char *sEnd = s + len.Get (); + + // Worst case expansion is 1-byte characters expanding to + // replacement character, which requires 3 bytes. + + const dng_safe_uint32 destBufferLength = len * 3u + 1u; + + dng_memory_data buffer (destBufferLength); + + uint8 *d = buffer.Buffer_uint8 (); + uint8 * const destEnd = d + destBufferLength.Get (); + + while (s < sEnd) + { + + uint32 aChar = DecodeUTF8 (s, (uint32) (sEnd - s)); + + if (aChar > 0x7FFFFFFF) + { + aChar = kREPLACEMENT_CHARACTER; + } + + #if qDNGValidate + + if (aChar == kREPLACEMENT_CHARACTER) + { + ReportWarning ("Expected UTF-8 value is not valid UTF-8 (or contains a kREPLACEMENT_CHARACTER)"); + } + + #endif + + if (aChar < 0x00000080) + { + CheckSpaceLeftInBuffer (d, destEnd, 1); + *(d++) = (uint8) aChar; + } + + else if (aChar < 0x00000800) + { + CheckSpaceLeftInBuffer (d, destEnd, 2); + *(d++) = (uint8) ((aChar >> 6) | 0x000000C0); + *(d++) = (uint8) ((aChar & 0x0000003F) | 0x00000080); + } + + else if (aChar < 0x00010000) + { + CheckSpaceLeftInBuffer (d, destEnd, 3); + *(d++) = (uint8) ( (aChar >> 12) | 0x000000E0); + *(d++) = (uint8) (((aChar >> 6) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) ( (aChar & 0x0000003F) | 0x00000080); + } + + else if (aChar < 0x00200000) + { + CheckSpaceLeftInBuffer (d, destEnd, 4); + *(d++) = (uint8) ( (aChar >> 18) | 0x000000F0); + *(d++) = (uint8) (((aChar >> 12) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) (((aChar >> 6) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) ( (aChar & 0x0000003F) | 0x00000080); + } + + else if (aChar < 0x04000000) + { + CheckSpaceLeftInBuffer (d, destEnd, 5); + *(d++) = (uint8) ( (aChar >> 24) | 0x000000F8); + *(d++) = (uint8) (((aChar >> 18) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) (((aChar >> 12) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) (((aChar >> 6) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) ( (aChar & 0x0000003F) | 0x00000080); + } + + else + { + CheckSpaceLeftInBuffer (d, destEnd, 6); + *(d++) = (uint8) ( (aChar >> 30) | 0x000000FC); + *(d++) = (uint8) (((aChar >> 24) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) (((aChar >> 18) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) (((aChar >> 12) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) (((aChar >> 6) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) ( (aChar & 0x0000003F) | 0x00000080); + } + + } + + CheckSpaceLeftInBuffer (d, destEnd, 1); + *d = 0; + + Set (buffer.Buffer_char ()); + + } + +/*****************************************************************************/ + +uint32 dng_string::Get_SystemEncoding (dng_memory_data &buffer) const + { + + if (IsASCII ()) + { + + dng_safe_uint32 len (Length ()); + + const dng_safe_uint32 destBufferLength = len + 1u; + + buffer.Allocate (destBufferLength); + + memcpy (buffer.Buffer (), Get (), destBufferLength.Get ()); + + return len.Get (); + + } + + else + { + + #if qMacOS || qWinOS + + return Extract_SystemEncoding (*this, buffer); + + #else + + // Fallback logic to force the string to ASCII. + + dng_string temp (*this); + + temp.ForceASCII (); + + return temp.Get_SystemEncoding (buffer); + + #endif + + } + + } + +/*****************************************************************************/ + +void dng_string::Set_SystemEncoding (const char *s) + { + + if (::IsASCII (s)) + { + + Set (s); + + } + + else + { + + #if qMacOS || qWinOS + + Assign_SystemEncoding (*this, s); + + #else + + // Fallback logic that just grabs the ASCII characters and + // ignores the non-ASCII characters. + + dng_safe_uint32 len = strlenAsUint32 (s); + + const dng_safe_uint32 destBufferLength = len + 1u; + + dng_memory_data buffer (destBufferLength); + + uint8 *d = buffer.Buffer_uint8 (); + uint8 * const destEnd = d + destBufferLength.Get (); + + while (*s) + { + + uint8 c = (uint8) *(s++); + + if ((c & 0x80) == 0) + { + + CheckSpaceLeftInBuffer (d, destEnd, 1); + *(d++) = c; + + } + + } + + CheckSpaceLeftInBuffer (d, destEnd, 1); + *d = 0; + + Set (buffer.Buffer_char ()); + + #endif + + } + + } + +/*****************************************************************************/ + +bool dng_string::ValidSystemEncoding () const + { + + if (IsASCII ()) + { + + return true; + + } + + dng_memory_data buffer; + + Get_SystemEncoding (buffer); + + dng_string temp; + + temp.Set_SystemEncoding (buffer.Buffer_char ()); + + return (*this == temp); + + } + +/*****************************************************************************/ + +void dng_string::Set_JIS_X208_1990 (const char *s) + { + + if (::IsASCII (s)) + { + + Set (s); + + } + + else + { + + #if qMacOS || qWinOS + + Assign_JIS_X208_1990 (*this, s); + + #else + + // Fallback to the ASCII extraction logic. + + Set_SystemEncoding (s); + + #endif + + } + + } + +/*****************************************************************************/ + +uint32 dng_string::DecodeUTF8 (const char *&s, + uint32 maxBytes, + bool *isValid) + { + + static const uint8 gUTF8Bytes [256] = + { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,6,6 + }; + + if (isValid) + { + *isValid = true; + } + + const uint8 *nBuf = (const uint8 *) s; + + uint32 aChar = nBuf [0]; + + uint32 aSize = gUTF8Bytes [aChar]; + + if (aSize > maxBytes) + { + + s += maxBytes; + + if (isValid) + { + *isValid = false; + } + + return kREPLACEMENT_CHARACTER; + + } + + s += aSize; + + for (uint32 extra = 1; extra < aSize; extra++) + { + + if ((nBuf [extra] & 0xC0) != 0x80) + { + + if (isValid) + { + *isValid = false; + } + + return kREPLACEMENT_CHARACTER; + + } + + } + + switch (aSize) + { + + case 0: + { + + s++; // Don't get stuck in infinite loop + + if (isValid) + { + *isValid = false; + } + + return kREPLACEMENT_CHARACTER; + + } + + case 1: + { + + return aChar; + + } + + case 2: + { + + aChar = ((aChar << 6) + nBuf [1]) - (uint32) 0x00003080UL; + + break; + + } + + case 3: + { + + aChar = ((((aChar << 6) + nBuf [1]) + << 6) + nBuf [2]) - (uint32) 0x000E2080UL; + + break; + + } + + case 4: + { + + aChar = ((((((aChar << 6) + nBuf [1]) + << 6) + nBuf [2]) + << 6) + nBuf [3]) - (uint32) 0x03C82080UL; + + break; + + } + + case 5: + { + + aChar = ((((((((aChar << 6) + nBuf [1]) + << 6) + nBuf [2]) + << 6) + nBuf [3]) + << 6) + nBuf [4]) - (uint32) 0xFA082080UL; + + break; + + } + + case 6: + { + + aChar = ((((((((((aChar << 6) + nBuf [1]) + << 6) + nBuf [2]) + << 6) + nBuf [3]) + << 6) + nBuf [4]) + << 6) + nBuf [5]) - (uint32) 0x82082080UL; + + break; + + } + + } + + if (aChar < 0x7F || aChar > 0x0010FFFF) + { + + if (isValid) + { + *isValid = false; + } + + return kREPLACEMENT_CHARACTER; + + } + + return aChar; + + } + +/*****************************************************************************/ + +bool dng_string::IsUTF8 (const char *s) + { + + uint32 len = strlenAsUint32 (s); + + const char *sEnd = s + len; + + while (s < sEnd) + { + + bool isValid = true; + + (void) DecodeUTF8 (s, (uint32) (sEnd - s), &isValid); + + if (!isValid) + { + return false; + } + + } + + return true; + + } + +/*****************************************************************************/ + +void dng_string::Set_UTF8_or_System (const char *s) + { + + if (::IsASCII (s)) + { + + Set (s); + + } + + else if (IsUTF8 (s)) + { + + Set_UTF8 (s); + + } + + else + { + + Set_SystemEncoding (s); + + } + + } + +/*****************************************************************************/ + +uint32 dng_string::Get_UTF16 (dng_memory_data &buffer) const + { + + dng_safe_uint32 count = 0u; + + const char *sPtr = Get (); + + while (*sPtr) + { + + uint32 x = DecodeUTF8 (sPtr); + + if (x <= 0x0000FFFF || + x > 0x0010FFFF) + { + + count += 1u; + + } + + else + { + + count += 2u; + + } + + } + + const dng_safe_uint32 destBufferLength = count + 1u; + + buffer.Allocate (destBufferLength.Get (), + sizeof (uint16)); + + uint16 *dPtr = buffer.Buffer_uint16 (); + uint16 * const destEnd = dPtr + destBufferLength.Get (); + + sPtr = Get (); + + while (*sPtr) + { + + uint32 x = DecodeUTF8 (sPtr); + + if (x <= 0x0000FFFF) + { + + CheckSpaceLeftInBuffer (dPtr, destEnd, 1); + *(dPtr++) = (uint16) x; + + } + + else if (x > 0x0010FFFF) + { + + CheckSpaceLeftInBuffer (dPtr, destEnd, 1); + *(dPtr++) = (uint16) kREPLACEMENT_CHARACTER; + + } + + else + { + + x -= 0x00010000; + + CheckSpaceLeftInBuffer (dPtr, destEnd, 2); + *(dPtr++) = (uint16) ((x >> 10 ) + 0x0000D800); + *(dPtr++) = (uint16) ((x & 0x000003FF) + 0x0000DC00); + + } + + } + + CheckSpaceLeftInBuffer (dPtr, destEnd, 1); + *dPtr = 0; + + return count.Get (); + + } + +/*****************************************************************************/ + +void dng_string::Set_UTF16 (const uint16 *s) + { + + if (!s) + { + Clear (); + return; + } + + bool swap = false; + + if (s [0] == 0xFFFE) // Swapped byte order marker + { + swap = true; + s++; + } + + else if (s [0] == 0xFEFF) // Non-swapped byte order marker + { + s++; + } + + dng_safe_uint32 length16 (0u); + + while (s [length16.Get ()] != 0) + { + length16 += 1u; + } + + const uint16 *sEnd = s + length16.Get (); + + const dng_safe_uint32 destBufferSize = length16 * 6u + 1u; + + dng_memory_data buffer (destBufferSize); + + uint8 *d = buffer.Buffer_uint8 (); + uint8 * const destEnd = d + destBufferSize.Get (); + + while (s < sEnd) + { + + uint32 aChar = *s++; + + if (swap) + { + aChar = ((aChar << 8) | (aChar >> 8)) & 0x0000FFFF; + } + + if ((aChar >= 0x0000D800) && (aChar <= 0x0000DBFF) && (s < sEnd)) + { + + uint32 aLow = *s; + + if (swap) + { + aLow = ((aLow << 8) | (aLow >> 8)) & 0x0000FFFF; + } + + if ((aLow >= 0x0000DC00) && (aLow <= 0x0000DFFF)) + { + + aChar = ((aChar - 0x0000D800) << 10) + + (aLow - 0x0000DC00) + + 0x00010000; + + s++; + + } + + } + + if (aChar > 0x7FFFFFFF) + { + aChar = kREPLACEMENT_CHARACTER; + } + + if (aChar < 0x00000080) + { + CheckSpaceLeftInBuffer (d, destEnd, 1); + *(d++) = (uint8) aChar; + } + + else if (aChar < 0x00000800) + { + CheckSpaceLeftInBuffer (d, destEnd, 2); + *(d++) = (uint8) ((aChar >> 6) | 0x000000C0); + *(d++) = (uint8) ((aChar & 0x0000003F) | 0x00000080); + } + + else if (aChar < 0x00010000) + { + CheckSpaceLeftInBuffer (d, destEnd, 3); + *(d++) = (uint8) ( (aChar >> 12) | 0x000000E0); + *(d++) = (uint8) (((aChar >> 6) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) ( (aChar & 0x0000003F) | 0x00000080); + } + + else if (aChar < 0x00200000) + { + CheckSpaceLeftInBuffer (d, destEnd, 4); + *(d++) = (uint8) ( (aChar >> 18) | 0x000000F0); + *(d++) = (uint8) (((aChar >> 12) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) (((aChar >> 6) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) ( (aChar & 0x0000003F) | 0x00000080); + } + + else if (aChar < 0x04000000) + { + CheckSpaceLeftInBuffer (d, destEnd, 5); + *(d++) = (uint8) ( (aChar >> 24) | 0x000000F8); + *(d++) = (uint8) (((aChar >> 18) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) (((aChar >> 12) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) (((aChar >> 6) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) ( (aChar & 0x0000003F) | 0x00000080); + } + + else + { + CheckSpaceLeftInBuffer (d, destEnd, 6); + *(d++) = (uint8) ( (aChar >> 30) | 0x000000FC); + *(d++) = (uint8) (((aChar >> 24) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) (((aChar >> 18) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) (((aChar >> 12) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) (((aChar >> 6) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) ( (aChar & 0x0000003F) | 0x00000080); + } + + } + + CheckSpaceLeftInBuffer (d, destEnd, 1); + *d = 0; + + Set (buffer.Buffer_char ()); + + } + +/*****************************************************************************/ + +void dng_string::Clear () + { + + Set (NULL); + + } + +/*****************************************************************************/ + +void dng_string::Truncate (uint32 maxBytes) + { + + uint32 len = Length (); + + if (len > maxBytes) + { + + uint8 *s = fData.Buffer_uint8 (); + + // Don't truncate on an extension character. Extensions characters + // in UTF-8 have the 0x80 bit set and the 0x40 bit clear. + + while (maxBytes > 0 && ((s [maxBytes]) & 0xC0) == 0x80) + { + + maxBytes--; + + } + + s [maxBytes] = 0; + + } + + } + +/*****************************************************************************/ + +bool dng_string::TrimTrailingBlanks () + { + + bool didTrim = false; + + if (fData.Buffer ()) + { + + char *s = fData.Buffer_char (); + + uint32 len = strlenAsUint32 (s); + + while (len > 0 && s [len - 1] == ' ') + { + len--; + didTrim = true; + } + + s [len] = 0; + + } + + return didTrim; + + } + +/*****************************************************************************/ + +bool dng_string::TrimLeadingBlanks () + { + + bool didTrim = false; + + const char *s = Get (); + + while (*s == ' ') + { + s++; + didTrim = true; + } + + if (didTrim) + { + Set (s); + } + + return didTrim; + + } + +/*****************************************************************************/ + +bool dng_string::IsEmpty () const + { + + const char *s = Get (); + + return *s == 0; + + } + +/*****************************************************************************/ + +uint32 dng_string::Length () const + { + + const char *s = Get (); + + return strlenAsUint32 (s); + + } + +/*****************************************************************************/ + +bool dng_string::operator== (const dng_string &s) const + { + + const char *s1 = Get (); + const char *s2 = s.Get (); + + return strcmp (s1, s2) == 0; + + } + +/*****************************************************************************/ + +bool dng_string::Matches (const char *t, + const char *s, + bool case_sensitive) + { + + while (*s != 0) + { + + char c1 = *(s++); + char c2 = *(t++); + + if (!case_sensitive) + { + c1 = ForceUppercase (c1); + c2 = ForceUppercase (c2); + } + + if (c1 != c2) + { + return false; + } + + } + + return (*t == 0); + + } + +/*****************************************************************************/ + +bool dng_string::Matches (const char *s, + bool case_sensitive) const + { + + return dng_string::Matches (Get (), s, case_sensitive); + + } + +/*****************************************************************************/ + +bool dng_string::StartsWith (const char *s, + bool case_sensitive) const + { + + const char *t = Get (); + + while (*s != 0) + { + + char c1 = *(s++); + char c2 = *(t++); + + if (!case_sensitive) + { + c1 = ForceUppercase (c1); + c2 = ForceUppercase (c2); + } + + if (c1 != c2) + { + return false; + } + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_string::EndsWith (const char *s, + bool case_sensitive) const + { + + uint32 len1 = Length (); + + uint32 len2 = strlenAsUint32 (s); + + if (len1 < len2) + { + return false; + } + + const char *t = Get () + (len1 - len2); + + while (*s != 0) + { + + char c1 = *(s++); + char c2 = *(t++); + + if (!case_sensitive) + { + c1 = ForceUppercase (c1); + c2 = ForceUppercase (c2); + } + + if (c1 != c2) + { + return false; + } + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_string::Contains (const char *s, + bool case_sensitive, + int32 *match_offset) const + { + + if (match_offset) + { + *match_offset = -1; + } + + uint32 len1 = Length (); + + uint32 len2 = strlenAsUint32 (s); + + if (len1 < len2) + { + return false; + } + + uint32 offsets = len1 - len2; + + for (uint32 offset = 0; offset <= offsets; offset++) + { + + const char *ss = s; + const char *tt = Get () + offset; + + while (*ss != 0) + { + + char c1 = *(ss++); + char c2 = *(tt++); + + if (!case_sensitive) + { + c1 = ForceUppercase (c1); + c2 = ForceUppercase (c2); + } + + if (c1 != c2) + { + goto tryNextOffset; + } + + } + + if (match_offset) + { + *match_offset = offset; + } + + return true; + + tryNextOffset: ; + + } + + return false; + + } + +/*****************************************************************************/ + +bool dng_string::Replace (const char *old_string, + const char *new_string, + bool case_sensitive) + { + + int32 match_offset = -1; + + if (Contains (old_string, + case_sensitive, + &match_offset)) + { + + uint32 len1 = Length (); + + uint32 len2 = strlenAsUint32 (old_string); + uint32 len3 = strlenAsUint32 (new_string); + + if (len2 == len3) + { + + DNG_REQUIRE (fData.Buffer_char (), "Bad string in dng_string::Replace"); + + strncpy (fData.Buffer_char () + match_offset, + new_string, + len3); + + } + + else if (len2 > len3) + { + + DNG_REQUIRE (fData.Buffer_char (), "Bad string in dng_string::Replace"); + + strncpy (fData.Buffer_char () + match_offset, + new_string, + len3); + + const char *s = fData.Buffer_char () + match_offset + len2; + char *d = fData.Buffer_char () + match_offset + len3; + + uint32 extra = len1 - match_offset - len2 + 1; // + 1 for NULL termination + + for (uint32 j = 0; j < extra; j++) + { + *(d++) = *(s++); + } + + } + + else + { + + // "len1 - len2" cannot wrap around because we know that if this + // string contains old_string, len1 >= len2 must hold. + + dng_memory_data tempBuffer + (dng_safe_uint32 (len1 - len2) + len3 + 1u); + + if (match_offset) + { + + strncpy (tempBuffer.Buffer_char (), + fData .Buffer_char (), + match_offset); + + } + + if (len3) + { + + strncpy (tempBuffer.Buffer_char () + match_offset, + new_string, + len3); + + } + + uint32 extra = len1 - match_offset - len2 + 1; // + 1 for NULL termination + + DNG_REQUIRE (fData.Buffer_char (), "Bad string in dng_string::Replace"); + + strncpy (tempBuffer.Buffer_char () + match_offset + len3, + fData .Buffer_char () + match_offset + len2, + extra); + + Set (tempBuffer.Buffer_char ()); + + } + + return true; + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_string::ReplaceChars (char oldChar, + char newChar) + { + + if (fData.Buffer ()) + { + + uint32 len = Length (); + + char *dPtr = fData.Buffer_char (); + + for (uint32 j = 0; j < len; j++) + { + + if (dPtr [j] == oldChar) + { + + dPtr [j] = newChar; + + } + + } + + } + + } + +/*****************************************************************************/ + +bool dng_string::TrimLeading (const char *s, + bool case_sensitive) + { + + if (StartsWith (s, case_sensitive)) + { + + Set (Get () + strlenAsUint32 (s)); + + return true; + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_string::Append (const char *s) + { + + dng_safe_uint32 len2 (strlenAsUint32 (s)); + + if (len2.Get ()) + { + + dng_safe_uint32 len1 (Length ()); + + dng_memory_data temp (len1 + len2 + 1u); + + char *buffer = temp.Buffer_char (); + + if (len1.Get ()) + { + memcpy (buffer, Get (), len1.Get ()); + } + + memcpy (buffer + len1.Get (), s, (len2 + 1u).Get ()); + + Set (buffer); + + } + + } + +/*****************************************************************************/ + +void dng_string::SetUppercase () + { + + if (fData.Buffer ()) + { + + uint32 len = Length (); + + char *dPtr = fData.Buffer_char (); + + for (uint32 j = 0; j < len; j++) + { + + char c = dPtr [j]; + + if (c >= 'a' && c <= 'z') + { + + dPtr [j] = c - 'a' + 'A'; + + } + + } + + } + + } + +/*****************************************************************************/ + +void dng_string::SetLowercase () + { + + if (fData.Buffer ()) + { + + uint32 len = Length (); + + char *dPtr = fData.Buffer_char (); + + for (uint32 j = 0; j < len; j++) + { + + char c = dPtr [j]; + + if (c >= 'A' && c <= 'Z') + { + + dPtr [j] = c - 'A' + 'a'; + + } + + } + + } + + } + +/*****************************************************************************/ + +void dng_string::SetLineEndings (char ending) + { + + if (fData.Buffer ()) + { + + const char *sPtr = fData.Buffer_char (); + char *dPtr = fData.Buffer_char (); + + while (*sPtr) + { + + char c = *(sPtr++); + + char nc = sPtr [0]; + + if ((c == '\r' && nc == '\n') || + (c == '\n' && nc == '\r')) + { + + sPtr++; + + if (ending) + { + *(dPtr++) = ending; + } + + } + + else if (c == '\n' || + c == '\r') + { + + if (ending) + { + *(dPtr++) = ending; + } + + } + + else + { + + *(dPtr++) = c; + + } + + } + + *dPtr = 0; + + } + + } + +/*****************************************************************************/ + +void dng_string::StripLowASCII () + { + + if (fData.Buffer ()) + { + + const char *sPtr = fData.Buffer_char (); + char *dPtr = fData.Buffer_char (); + + while (*sPtr) + { + + char c = *(sPtr++); + + if (c == '\r' || c == '\n' || (uint8) c >= ' ') + { + + *(dPtr++) = c; + + } + + } + + *dPtr = 0; + + } + + } + +/*****************************************************************************/ + +void dng_string::NormalizeAsCommaSeparatedNumbers () + { + + if (fData.Buffer ()) + { + + const char *sPtr = fData.Buffer_char (); + char *dPtr = fData.Buffer_char (); + + bool commaInserted = false; + + while (*sPtr) + { + + uint32 c = DecodeUTF8 (sPtr); + + // Support number formats such as "3", "+3.0", "-3.1416", "314.16e-2", + // "0.31416E1", but no hex/octal number representations. + + if (isdigit ((int) c) || c == '.' || c == '-' || c == '+' || c == 'e' || c == 'E') + { + + *(dPtr++) = (char) c; + + if (commaInserted) + { + + commaInserted = false; + + } + + } + + else if (!commaInserted) + { + + *(dPtr++) = ','; + + commaInserted = true; + + } + + } + + *dPtr = 0; + + } + + } + +/******************************************************************************/ + +// Unicode to low-ASCII strings table. + +struct UnicodeToLowASCIIEntry + { + uint32 unicode; + const char *ascii; + }; + +static const UnicodeToLowASCIIEntry kUnicodeToLowASCII [] = + { + { 0x00A0, " " }, + { 0x00A1, "!" }, + { 0x00A9, "(C)" }, + { 0x00AA, "a" }, + { 0x00AB, "<<" }, + { 0x00AC, "!" }, + { 0x00AE, "(R)" }, + { 0x00B0, "dg" }, + { 0x00B1, "+-" }, + { 0x00B7, "." }, + { 0x00BA, "o" }, + { 0x00BB, ">>" }, + { 0x00BF, "?" }, + { 0x00C0, "A" }, + { 0x00C1, "A" }, + { 0x00C2, "A" }, + { 0x00C3, "A" }, + { 0x00C4, "A" }, + { 0x00C5, "A" }, + { 0x00C6, "AE" }, + { 0x00C7, "C" }, + { 0x00C8, "E" }, + { 0x00C9, "E" }, + { 0x00CA, "E" }, + { 0x00CB, "E" }, + { 0x00CC, "I" }, + { 0x00CD, "I" }, + { 0x00CE, "I" }, + { 0x00CF, "I" }, + { 0x00D1, "N" }, + { 0x00D2, "O" }, + { 0x00D3, "O" }, + { 0x00D4, "O" }, + { 0x00D5, "O" }, + { 0x00D6, "O" }, + { 0x00D8, "O" }, + { 0x00D9, "U" }, + { 0x00DA, "U" }, + { 0x00DB, "U" }, + { 0x00DC, "U" }, + { 0x00DD, "Y" }, + { 0x00E0, "a" }, + { 0x00E1, "a" }, + { 0x00E2, "a" }, + { 0x00E3, "a" }, + { 0x00E4, "a" }, + { 0x00E5, "a" }, + { 0x00E6, "ae" }, + { 0x00E7, "c" }, + { 0x00E8, "e" }, + { 0x00E9, "e" }, + { 0x00EA, "e" }, + { 0x00EB, "e" }, + { 0x00EC, "i" }, + { 0x00ED, "i" }, + { 0x00EE, "i" }, + { 0x00EF, "i" }, + { 0x00F1, "n" }, + { 0x00F2, "o" }, + { 0x00F3, "o" }, + { 0x00F4, "o" }, + { 0x00F5, "o" }, + { 0x00F6, "o" }, + { 0x00F7, "/" }, + { 0x00F8, "o" }, + { 0x00F9, "u" }, + { 0x00FA, "u" }, + { 0x00FB, "u" }, + { 0x00FC, "u" }, + { 0x00FD, "y" }, + { 0x00FF, "y" }, + { 0x0131, "i" }, + { 0x0152, "OE" }, + { 0x0153, "oe" }, + { 0x0178, "Y" }, + { 0x2013, "-" }, + { 0x2014, "-" }, + { 0x2018, "'" }, + { 0x2019, "'" }, + { 0x201A, "," }, + { 0x201C, "\"" }, + { 0x201D, "\"" }, + { 0x201E, ",," }, + { 0x2022, "." }, + { 0x2026, "..." }, + { 0x2039, "<" }, + { 0x203A, ">" }, + { 0x2044, "/" }, + { 0x2122, "TM" }, + { 0x2206, "d" }, + { 0x2211, "S" }, + { 0x2260, "!=" }, + { 0x2264, "<=" }, + { 0x2265, ">=" }, + { 0x2318, "#" }, + { 0xFB01, "fi" }, + { 0xFB02, "fl" } + }; + +/******************************************************************************/ + +void dng_string::ForceASCII () + { + + if (!IsASCII ()) + { + + dng_safe_uint32 tempBufferSize = dng_safe_uint32 (Length ()) * 3u + 1u; + + dng_memory_data tempBuffer (tempBufferSize); + + char *dPtr = tempBuffer.Buffer_char (); + char * const destEnd = dPtr + tempBufferSize.Get (); + + const char *sPtr = Get (); + + while (*sPtr) + { + + uint32 x = DecodeUTF8 (sPtr); + + if (x <= 0x007F) + { + + CheckSpaceLeftInBuffer (dPtr, destEnd, 1); + *(dPtr++) = (char) x; + + } + + else + { + + const char *ascii = NULL; + + const uint32 kTableEntrys = sizeof (kUnicodeToLowASCII ) / + sizeof (kUnicodeToLowASCII [0]); + + for (uint32 entry = 0; entry < kTableEntrys; entry++) + { + + if (kUnicodeToLowASCII [entry] . unicode == x) + { + + ascii = kUnicodeToLowASCII [entry] . ascii; + + break; + + } + + } + + if (ascii) + { + + while (*ascii) + { + + CheckSpaceLeftInBuffer (dPtr, destEnd, 1); + *(dPtr++) = *(ascii++); + + } + + } + + else + { + + CheckSpaceLeftInBuffer (dPtr, destEnd, 1); + *(dPtr++) ='?'; + + } + + } + + } + + CheckSpaceLeftInBuffer (dPtr, destEnd, 1); + *dPtr = 0; + + Set (tempBuffer.Buffer_char ()); + + } + + } + +/******************************************************************************/ + +static dng_std_mutex gProtectUCCalls; + +/******************************************************************************/ + +int32 dng_string::Compare (const dng_string &s, + bool digitsAsNumber) const + { + + #if qMacOS + + { + + dng_memory_data aStrA; + dng_memory_data aStrB; + + uint32 aLenA = this->Get_UTF16 (aStrA); + uint32 aLenB = s .Get_UTF16 (aStrB); + + if (aLenA > 0) + { + + if (aLenB > 0) + { + + // For some Mac OS versions anyway, UCCompareTextDefault is not + // thread safe. + + dng_lock_std_mutex lockMutex (gProtectUCCalls); + + UCCollateOptions aOptions = kUCCollateStandardOptions | + kUCCollatePunctuationSignificantMask; + + if (digitsAsNumber) + { + + aOptions |= kUCCollateDigitsOverrideMask | + kUCCollateDigitsAsNumberMask; + + } + + SInt32 aOrder = -1; + + Boolean aEqual = false; + + OSStatus searchStatus = ::UCCompareTextDefault (aOptions, + aStrA.Buffer_uint16 (), + aLenA, + aStrB.Buffer_uint16 (), + aLenB, + &aEqual, + &aOrder); + + if (searchStatus == noErr) + { + + if (aEqual || (aOrder == 0)) + { + return 0; + } + + else + { + return (aOrder > 0) ? 1 : -1; + } + + } + + else + { + + DNG_REPORT ("UCCompareTextDefault failed"); + + return -1; + + } + + } + + else + { + return 1; + } + + } + + else + { + + if (aLenB > 0) + { + return -1; + } + + else + { + return 0; + } + + } + + } + + #elif qWinOS + + { + + dng_memory_data aStrA; + dng_memory_data aStrB; + + uint32 aLenA = this->Get_UTF16 (aStrA); + uint32 aLenB = s .Get_UTF16 (aStrB); + + if (aLenA > 0) + { + + if (aLenB > 0) + { + + LCID locale = LOCALE_SYSTEM_DEFAULT; + + DWORD aFlags = NORM_IGNOREWIDTH; + + if (digitsAsNumber) + { + aFlags |= SORT_DIGITSASNUMBERS; + } + + int aOrder = ::CompareStringW (locale, + aFlags, + (const WCHAR *) aStrA.Buffer_uint16 (), + aLenA, + (const WCHAR *) aStrB.Buffer_uint16 (), + aLenB); + + if (aOrder == CSTR_EQUAL) + { + return 0; + } + + else if (aOrder == CSTR_GREATER_THAN) + { + return 1; + } + + else + { + return -1; + } + + } + + else + { + return 1; + } + + } + + else + { + + if (aLenB > 0) + { + return -1; + } + else + { + return 0; + } + + } + + } + + #else + + // Fallback to a pure Unicode sort order. + + { + + for (uint32 pass = 0; pass < 2; pass++) + { + + const char *aPtr = Get (); + const char *bPtr = s.Get (); + + while (*aPtr || *bPtr) + { + + if (!bPtr) + { + return 1; + } + + else if (!aPtr) + { + return -1; + } + + uint32 a = DecodeUTF8 (aPtr); + uint32 b = DecodeUTF8 (bPtr); + + // Ignore case on first compare pass. + + if (pass == 0) + { + + if (a >= (uint32) 'a' && a <= (uint32) 'z') + { + a = a - (uint32) 'a' + (uint32) 'A'; + } + + if (b >= (uint32) 'a' && b <= (uint32) 'z') + { + b = b - (uint32) 'a' + (uint32) 'A'; + } + + } + + if (digitsAsNumber) + { + + uint32 aNumber = 0; + uint32 aDigits = 0; + + if (a >= (uint32) '0' && a <= (uint32) '9') + { + + aNumber = a - (uint32) '0'; + aDigits = 1; + + while (aDigits < 6 && *aPtr >= '0' && *aPtr <= '9') + { + aNumber = aNumber * 10 + ((uint32) *aPtr - + (uint32) '0'); + aDigits++; + aPtr++; + } + + } + + uint32 bNumber = 0; + uint32 bDigits = 0; + + if (b >= (uint32) '0' && b <= (uint32) '9') + { + + bNumber = b - (uint32) '0'; + bDigits = 1; + + while (bDigits < 6 && *bPtr >= '0' && *bPtr <= '9') + { + bNumber = bNumber * 10 + ((uint32) *bPtr - + (uint32) '0'); + bDigits++; + bPtr++; + } + + } + + if (aDigits > 0 && bDigits > 0) + { + + if (aNumber > bNumber) + { + return 1; + } + + if (aNumber < bNumber) + { + return -1; + } + + if (aDigits > bDigits) + { + return 1; + } + + if (aDigits < bDigits) + { + return -1; + } + + continue; + + } + + } + + if (a > b) + { + return 1; + } + + else if (a < b) + { + return -1; + } + + } + + } + + } + + #endif + + return 0; + + } + +/*****************************************************************************/ diff --git a/dng/dng_string.h b/dng/dng_string.h new file mode 100644 index 0000000..a803daf --- /dev/null +++ b/dng/dng_string.h @@ -0,0 +1,164 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Text string representation. + */ + +/*****************************************************************************/ + +#ifndef __dng_string__ +#define __dng_string__ + +/*****************************************************************************/ + +#include "dng_types.h" +#include "dng_memory.h" + +/*****************************************************************************/ + +class dng_string + { + + private: + + // Always stored internally as a UTF-8 encoded string. + + dng_memory_data fData; + + public: + + dng_string (); + + dng_string (const dng_string &s); + + dng_string & operator= (const dng_string &s); + + ~dng_string (); + + const char * Get () const; + + bool IsASCII () const; + + void Set (const char *s); + + void Set_ASCII (const char *s); + + void Set_UTF8 (const char *s); + + uint32 Get_SystemEncoding (dng_memory_data &buffer) const; + + void Set_SystemEncoding (const char *s); + + bool ValidSystemEncoding () const; + + void Set_JIS_X208_1990 (const char *s); + + static uint32 DecodeUTF8 (const char *&s, + uint32 maxBytes = 6, + bool *isValid = NULL); + + static bool IsUTF8 (const char *s); + + void Set_UTF8_or_System (const char *s); + + uint32 Get_UTF16 (dng_memory_data &buffer) const; + + void Set_UTF16 (const uint16 *s); + + void Clear (); + + void Truncate (uint32 maxBytes); + + bool TrimTrailingBlanks (); + + bool TrimLeadingBlanks (); + + bool IsEmpty () const; + + bool NotEmpty () const + { + return !IsEmpty (); + } + + uint32 Length () const; + + bool operator== (const dng_string &s) const; + + bool operator!= (const dng_string &s) const + { + return !(*this == s); + } + + // A utility for doing case insensitive comparisons on strings... + + static bool Matches (const char *t, + const char *s, + bool case_sensitive = false); + + // ...wrapped up for use with dng_string. + + bool Matches (const char *s, + bool case_sensitive = false) const; + + bool StartsWith (const char *s, + bool case_sensitive = false) const; + + bool EndsWith (const char *s, + bool case_sensitive = false) const; + + bool Contains (const char *s, + bool case_sensitive = false, + int32 *match_offset = NULL) const; + + bool Replace (const char *old_string, + const char *new_string, + bool case_sensitive = true); + + void ReplaceChars (char oldChar, + char newChar); + + bool TrimLeading (const char *s, + bool case_sensitive = false); + + void Append (const char *s); + + void SetUppercase (); + + void SetLowercase (); + + void SetLineEndings (char ending); + + void SetLineEndingsToNewLines () + { + SetLineEndings ('\n'); + } + + void SetLineEndingsToReturns () + { + SetLineEndings ('\r'); + } + + void StripLowASCII (); + + void ForceASCII (); + + int32 Compare (const dng_string &s, + bool digitsAsNumber = true) const; + + // A utility to convert fields of numbers into comma separated numbers. + + void NormalizeAsCommaSeparatedNumbers (); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_string_list.cpp b/dng/dng_string_list.cpp new file mode 100644 index 0000000..be4364b --- /dev/null +++ b/dng/dng_string_list.cpp @@ -0,0 +1,156 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_string_list.h" + +#include "dng_bottlenecks.h" +#include "dng_exceptions.h" +#include "dng_string.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_string_list::dng_string_list () + + : fCount (0) + , fAllocated (0) + , fList (NULL) + + { + + } + +/*****************************************************************************/ + +dng_string_list::~dng_string_list () + { + + Clear (); + + } + +/*****************************************************************************/ + +void dng_string_list::Allocate (uint32 minSize) + { + + if (fAllocated < minSize) + { + + uint32 newSize = Max_uint32 (minSize, fAllocated * 2); + + dng_string **list = (dng_string **) + malloc (newSize * sizeof (dng_string *)); + + if (!list) + { + + ThrowMemoryFull (); + + } + + if (fCount) + { + + memcpy (list, fList, fCount * (uint32) sizeof (dng_string *)); + + } + + if (fList) + { + + free (fList); + + } + + fList = list; + + fAllocated = newSize; + + } + + } + +/*****************************************************************************/ + +void dng_string_list::Insert (uint32 index, + const dng_string &s) + { + + Allocate (fCount + 1); + + dng_string *ss = new dng_string (s); + + if (!ss) + { + + ThrowMemoryFull (); + + } + + fCount++; + + for (uint32 j = fCount - 1; j > index; j--) + { + + fList [j] = fList [j - 1]; + + } + + fList [index] = ss; + + } + +/*****************************************************************************/ + +bool dng_string_list::Contains (const dng_string &s) const + { + + for (uint32 j = 0; j < fCount; j++) + { + + if ((*this) [j] == s) + { + + return true; + + } + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_string_list::Clear () + { + + if (fList) + { + + for (uint32 index = 0; index < fCount; index++) + { + + delete fList [index]; + + } + + free (fList); + + fList = NULL; + + } + + fCount = 0; + fAllocated = 0; + + } + +/*****************************************************************************/ diff --git a/dng/dng_string_list.h b/dng/dng_string_list.h new file mode 100644 index 0000000..424b344 --- /dev/null +++ b/dng/dng_string_list.h @@ -0,0 +1,72 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef __dng_string_list__ +#define __dng_string_list__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_types.h" +#include "dng_uncopyable.h" + +/*****************************************************************************/ + +class dng_string_list: private dng_uncopyable + { + + private: + + uint32 fCount; + + uint32 fAllocated; + + dng_string **fList; + + public: + + dng_string_list (); + + ~dng_string_list (); + + uint32 Count () const + { + return fCount; + } + + dng_string & operator[] (uint32 index) + { + return *(fList [index]); + } + + const dng_string & operator[] (uint32 index) const + { + return *(fList [index]); + } + + void Allocate (uint32 minSize); + + void Insert (uint32 index, + const dng_string &s); + + void Append (const dng_string &s) + { + Insert (Count (), s); + } + + bool Contains (const dng_string &s) const; + + void Clear (); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_tag_codes.h b/dng/dng_tag_codes.h new file mode 100644 index 0000000..c18a849 --- /dev/null +++ b/dng/dng_tag_codes.h @@ -0,0 +1,561 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef __dng_tag_codes__ +#define __dng_tag_codes__ + +/*****************************************************************************/ + +#include "dng_flags.h" + +/*****************************************************************************/ + +// TIFF tags 50706 through 50741 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2003-11-04 & 2003-12-02, purpose "Digital Negative". + +// TIFF tags 50778 through 50781 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2004-08-17, purpose "Digital Negative". + +// TIFF tags 50827 through 50834 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2004-12-06, purpose "Digital Negative". + +// TIFF tag number 50879 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2006-03-23, purpose "Digital Negative". + +// TIFF compression numbers 34892 through 34895 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2003-11-04, purpose "Digital Negative". + +// TIFF tags numbers 50931 through 50942 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2007-04-30, purpose "Digital Negative". + +// TIFF tags numbers 50964 through 50975 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2007-12-17, purpose "Digital Negative". + +// TIFF tags numbers 50981 through 50982 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2008-04-01, purpose "Digital Negative". + +// TIFF tags numbers 51008 through 51009 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2008-10-15, purpose "Digital Negative". + +// TIFF tag number 51022 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2008-12-15, purpose "Digital Negative". + +// TIFF tag number 51041 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2009-5-7, purpose "Digital Negative". + +// TIFF tags numbers 51089 through 51091 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2011-07-01, purpose "Digital Negative". + +// TIFF tags numbers 51107 through 51110 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2011-09-22, purpose "Digital Negative". + +// TIFF tag number 51111 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2011-10-07, purpose "Digital Negative". + +// TIFF tags numbers 51112 through 51114 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2011-10-25, purpose "Digital Negative". + +// TIFF tag number 51125 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2012-05-31, purpose "Digital Negative". + +// TIFF tags numbers 51177 through 51191 registered at: +// Manual update on 2018-06-17, purpose "Digital Negative". + +/*****************************************************************************/ + +// TIFF, DNG, TIFF/EP, and Exif tag codes all share the main TIFF tag code +// number space. In cases where TIFF/EP and Exif have different values for +// tags with the same name, "Exif" is appended to the name of the Exif version +// of the tag. + +enum + { + tcNewSubFileType = 254, + tcSubFileType = 255, + tcImageWidth = 256, + tcImageLength = 257, + tcBitsPerSample = 258, + tcCompression = 259, + tcPhotometricInterpretation = 262, + tcThresholding = 263, + tcCellWidth = 264, + tcCellLength = 265, + tcFillOrder = 266, + tcImageDescription = 270, + tcMake = 271, + tcModel = 272, + tcStripOffsets = 273, + tcOrientation = 274, + tcSamplesPerPixel = 277, + tcRowsPerStrip = 278, + tcStripByteCounts = 279, + tcMinSampleValue = 280, + tcMaxSampleValue = 281, + tcXResolution = 282, + tcYResolution = 283, + tcPlanarConfiguration = 284, + tcFreeOffsets = 285, + tcFreeByteCounts = 286, + tcGrayResponseUnit = 290, + tcGrayResponseCurve = 291, + tcResolutionUnit = 296, + tcTransferFunction = 301, + tcSoftware = 305, + tcDateTime = 306, + tcArtist = 315, + tcHostComputer = 316, + tcPredictor = 317, + tcWhitePoint = 318, + tcPrimaryChromaticities = 319, + tcColorMap = 320, + tcTileWidth = 322, + tcTileLength = 323, + tcTileOffsets = 324, + tcTileByteCounts = 325, + tcSubIFDs = 330, + tcExtraSamples = 338, + tcSampleFormat = 339, + tcJPEGTables = 347, + tcJPEGProc = 512, + tcJPEGInterchangeFormat = 513, + tcJPEGInterchangeFormatLength = 514, + tcYCbCrCoefficients = 529, + tcYCbCrSubSampling = 530, + tcYCbCrPositioning = 531, + tcReferenceBlackWhite = 532, + tcXMP = 700, + tcKodakCameraSerialNumber = 33405, + tcCFARepeatPatternDim = 33421, + tcCFAPattern = 33422, + tcBatteryLevel = 33423, + tcKodakDCRPrivateIFD = 33424, + tcCopyright = 33432, + tcExposureTime = 33434, + tcFNumber = 33437, + tcIPTC_NAA = 33723, + tcLeafPKTS = 34310, + tcAdobeData = 34377, + tcExifIFD = 34665, + tcICCProfile = 34675, + tcExposureProgram = 34850, + tcSpectralSensitivity = 34852, + tcGPSInfo = 34853, + tcISOSpeedRatings = 34855, // EXIF 2.3: PhotographicSensitivity. + tcOECF = 34856, + tcInterlace = 34857, + tcTimeZoneOffset = 34858, + tcSelfTimerMode = 34859, + tcSensitivityType = 34864, + tcStandardOutputSensitivity = 34865, + tcRecommendedExposureIndex = 34866, + tcISOSpeed = 34867, + tcISOSpeedLatitudeyyy = 34868, + tcISOSpeedLatitudezzz = 34869, + tcExifVersion = 36864, + tcDateTimeOriginal = 36867, + tcDateTimeDigitized = 36868, + tcOffsetTime = 36880, + tcOffsetTimeOriginal = 36881, + tcOffsetTimeDigitized = 36882, + tcComponentsConfiguration = 37121, + tcCompressedBitsPerPixel = 37122, + tcShutterSpeedValue = 37377, + tcApertureValue = 37378, + tcBrightnessValue = 37379, + tcExposureBiasValue = 37380, + tcMaxApertureValue = 37381, + tcSubjectDistance = 37382, + tcMeteringMode = 37383, + tcLightSource = 37384, + tcFlash = 37385, + tcFocalLength = 37386, + tcFlashEnergy = 37387, + tcSpatialFrequencyResponse = 37388, + tcNoise = 37389, + tcFocalPlaneXResolution = 37390, + tcFocalPlaneYResolution = 37391, + tcFocalPlaneResolutionUnit = 37392, + tcImageNumber = 37393, + tcSecurityClassification = 37394, + tcImageHistory = 37395, + tcSubjectArea = 37396, + tcExposureIndex = 37397, + tcTIFF_EP_StandardID = 37398, + tcSensingMethod = 37399, + tcMakerNote = 37500, + tcUserComment = 37510, + tcSubsecTime = 37520, + tcSubsecTimeOriginal = 37521, + tcSubsecTimeDigitized = 37522, + tcAdobeLayerData = 37724, + tcTemperature = 37888, + tcHumidity = 37889, + tcPressure = 37890, + tcWaterDepth = 37891, + tcAcceleration = 37892, + tcCameraElevationAngle = 37893, + tcFlashPixVersion = 40960, + tcColorSpace = 40961, + tcPixelXDimension = 40962, + tcPixelYDimension = 40963, + tcRelatedSoundFile = 40964, + tcInteroperabilityIFD = 40965, + tcFlashEnergyExif = 41483, + tcSpatialFrequencyResponseExif = 41484, + tcFocalPlaneXResolutionExif = 41486, + tcFocalPlaneYResolutionExif = 41487, + tcFocalPlaneResolutionUnitExif = 41488, + tcSubjectLocation = 41492, + tcExposureIndexExif = 41493, + tcSensingMethodExif = 41495, + tcFileSource = 41728, + tcSceneType = 41729, + tcCFAPatternExif = 41730, + tcCustomRendered = 41985, + tcExposureMode = 41986, + tcWhiteBalance = 41987, + tcDigitalZoomRatio = 41988, + tcFocalLengthIn35mmFilm = 41989, + tcSceneCaptureType = 41990, + tcGainControl = 41991, + tcContrast = 41992, + tcSaturation = 41993, + tcSharpness = 41994, + tcDeviceSettingDescription = 41995, + tcSubjectDistanceRange = 41996, + tcImageUniqueID = 42016, + tcCameraOwnerNameExif = 42032, + tcCameraSerialNumberExif = 42033, + tcLensSpecificationExif = 42034, + tcLensMakeExif = 42035, + tcLensModelExif = 42036, + tcLensSerialNumberExif = 42037, + tcGamma = 42240, + tcPrintImageMatchingInfo = 50341, + tcDNGVersion = 50706, + tcDNGBackwardVersion = 50707, + tcUniqueCameraModel = 50708, + tcLocalizedCameraModel = 50709, + tcCFAPlaneColor = 50710, + tcCFALayout = 50711, + tcLinearizationTable = 50712, + tcBlackLevelRepeatDim = 50713, + tcBlackLevel = 50714, + tcBlackLevelDeltaH = 50715, + tcBlackLevelDeltaV = 50716, + tcWhiteLevel = 50717, + tcDefaultScale = 50718, + tcDefaultCropOrigin = 50719, + tcDefaultCropSize = 50720, + tcColorMatrix1 = 50721, + tcColorMatrix2 = 50722, + tcCameraCalibration1 = 50723, + tcCameraCalibration2 = 50724, + tcReductionMatrix1 = 50725, + tcReductionMatrix2 = 50726, + tcAnalogBalance = 50727, + tcAsShotNeutral = 50728, + tcAsShotWhiteXY = 50729, + tcBaselineExposure = 50730, + tcBaselineNoise = 50731, + tcBaselineSharpness = 50732, + tcBayerGreenSplit = 50733, + tcLinearResponseLimit = 50734, + tcCameraSerialNumber = 50735, + tcLensInfo = 50736, + tcChromaBlurRadius = 50737, + tcAntiAliasStrength = 50738, + tcShadowScale = 50739, + tcDNGPrivateData = 50740, + tcMakerNoteSafety = 50741, + tcCalibrationIlluminant1 = 50778, + tcCalibrationIlluminant2 = 50779, + tcBestQualityScale = 50780, + tcRawDataUniqueID = 50781, + tcOriginalRawFileName = 50827, + tcOriginalRawFileData = 50828, + tcActiveArea = 50829, + tcMaskedAreas = 50830, + tcAsShotICCProfile = 50831, + tcAsShotPreProfileMatrix = 50832, + tcCurrentICCProfile = 50833, + tcCurrentPreProfileMatrix = 50834, + tcColorimetricReference = 50879, + tcCameraCalibrationSignature = 50931, + tcProfileCalibrationSignature = 50932, + tcExtraCameraProfiles = 50933, + tcAsShotProfileName = 50934, + tcNoiseReductionApplied = 50935, + tcProfileName = 50936, + tcProfileHueSatMapDims = 50937, + tcProfileHueSatMapData1 = 50938, + tcProfileHueSatMapData2 = 50939, + tcProfileToneCurve = 50940, + tcProfileEmbedPolicy = 50941, + tcProfileCopyright = 50942, + tcForwardMatrix1 = 50964, + tcForwardMatrix2 = 50965, + tcPreviewApplicationName = 50966, + tcPreviewApplicationVersion = 50967, + tcPreviewSettingsName = 50968, + tcPreviewSettingsDigest = 50969, + tcPreviewColorSpace = 50970, + tcPreviewDateTime = 50971, + tcRawImageDigest = 50972, + tcOriginalRawFileDigest = 50973, + tcSubTileBlockSize = 50974, + tcRowInterleaveFactor = 50975, + tcProfileLookTableDims = 50981, + tcProfileLookTableData = 50982, + tcOpcodeList1 = 51008, + tcOpcodeList2 = 51009, + tcOpcodeList3 = 51022, + tcNoiseProfile = 51041, + tcOriginalDefaultFinalSize = 51089, + tcOriginalBestQualityFinalSize = 51090, + tcOriginalDefaultCropSize = 51091, + tcProfileHueSatMapEncoding = 51107, + tcProfileLookTableEncoding = 51108, + tcBaselineExposureOffset = 51109, + tcDefaultBlackRender = 51110, + tcNewRawImageDigest = 51111, + tcRawToPreviewGain = 51112, + tcCacheBlob = 51113, + tcCacheVersion = 51114, + tcDefaultUserCrop = 51125, + tcDepthFormat = 51177, + tcDepthNear = 51178, + tcDepthFar = 51179, + tcDepthUnits = 51180, + tcDepthMeasureType = 51181, + tcEnhanceParams = 51182, + tcKodakKDCPrivateIFD = 65024 + }; + +/*****************************************************************************/ + +// Additional values that can be passed as IFD parent codes. + +enum + { + + tcFirstSubIFD = 0x10000, + tcLastSubIFD = 0x1FFFF, + + tcFirstChainedIFD = 0x20000, + tcLastChainedIFD = 0x2FFFF, + + tcFirstMakerNoteIFD = 0x30000, + tcLastMakerNoteIFD = 0x3FFFF, + + tcCanonMakerNote = tcFirstMakerNoteIFD, + tcCasioMakerNote, + tcEpsonMakerNote, + tcFujiMakerNote, + tcHasselbladMakerNote, + tcKodakMakerNote, + tcKodakMakerNote65280, + tcLeicaMakerNote, + tcMamiyaMakerNote, + tcMinoltaMakerNote, + tcNikonMakerNote, + tcOlympusMakerNote, + tcOlympusMakerNote8208, + tcOlympusMakerNote8224, + tcOlympusMakerNote8240, + tcOlympusMakerNote8256, + tcOlympusMakerNote8272, + tcOlympusMakerNote12288, + tcPanasonicMakerNote, + tcPentaxMakerNote, + tcPhaseOneMakerNote, + tcRicohMakerNote, + tcRicohMakerNoteCameraInfo, + tcSamsungMakerNote, + tcSonyMakerNote, + tcSonyMakerNoteSubInfo, + tcSonyPrivateIFD1, + tcSonyPrivateIFD2, + tcSonyPrivateIFD3A, + tcSonyPrivateIFD3B, + tcSonyPrivateIFD3C, + + tcCanonCRW = 0x40000, + tcContaxRAW, + tcContaxHeader, + tcFujiRAF, + tcFujiHeader, + tcFujiRawInfo1, + tcFujiRawInfo2, + tcLeafMOS, + tcMinoltaMRW, + tcPanasonicRAW, + tcFoveonX3F, + tcJPEG, + tcAdobePSD, + tcPNG, + tcHEIC, + tcCanonCR3 + + }; + +/*****************************************************************************/ + +// GPS tag codes are only valid in the GPS IFD. + +enum + { + tcGPSVersionID = 0, + tcGPSLatitudeRef = 1, + tcGPSLatitude = 2, + tcGPSLongitudeRef = 3, + tcGPSLongitude = 4, + tcGPSAltitudeRef = 5, + tcGPSAltitude = 6, + tcGPSTimeStamp = 7, + tcGPSSatellites = 8, + tcGPSStatus = 9, + tcGPSMeasureMode = 10, + tcGPSDOP = 11, + tcGPSSpeedRef = 12, + tcGPSSpeed = 13, + tcGPSTrackRef = 14, + tcGPSTrack = 15, + tcGPSImgDirectionRef = 16, + tcGPSImgDirection = 17, + tcGPSMapDatum = 18, + tcGPSDestLatitudeRef = 19, + tcGPSDestLatitude = 20, + tcGPSDestLongitudeRef = 21, + tcGPSDestLongitude = 22, + tcGPSDestBearingRef = 23, + tcGPSDestBearing = 24, + tcGPSDestDistanceRef = 25, + tcGPSDestDistance = 26, + tcGPSProcessingMethod = 27, + tcGPSAreaInformation = 28, + tcGPSDateStamp = 29, + tcGPSDifferential = 30, + tcGPSHPositioningError = 31 + }; + +/*****************************************************************************/ + +// Tag codes used in the Interoperability IFD. + +enum + { + tcInteroperabilityIndex = 0x0001, + tcInteroperabilityVersion = 0x0002, + tcRelatedImageFileFormat = 0x1000, + tcRelatedImageWidth = 0x1001, + tcRelatedImageLength = 0x1002 + }; + +/*****************************************************************************/ + +// JPEG marker codes. + +enum JpegMarker + { + + M_TEM = 0x01, + + M_SOF0 = 0xc0, + M_SOF1 = 0xc1, + M_SOF2 = 0xc2, + M_SOF3 = 0xc3, + M_DHT = 0xc4, + M_SOF5 = 0xc5, + M_SOF6 = 0xc6, + M_SOF7 = 0xc7, + M_JPG = 0xc8, + M_SOF9 = 0xc9, + M_SOF10 = 0xca, + M_SOF11 = 0xcb, + M_DAC = 0xcc, + M_SOF13 = 0xcd, + M_SOF14 = 0xce, + M_SOF15 = 0xcf, + + M_RST0 = 0xd0, + M_RST1 = 0xd1, + M_RST2 = 0xd2, + M_RST3 = 0xd3, + M_RST4 = 0xd4, + M_RST5 = 0xd5, + M_RST6 = 0xd6, + M_RST7 = 0xd7, + + M_SOI = 0xd8, + M_EOI = 0xd9, + M_SOS = 0xda, + M_DQT = 0xdb, + M_DNL = 0xdc, + M_DRI = 0xdd, + M_DHP = 0xde, + M_EXP = 0xdf, + + M_APP0 = 0xe0, + M_APP1 = 0xe1, + M_APP2 = 0xe2, + M_APP3 = 0xe3, + M_APP4 = 0xe4, + M_APP5 = 0xe5, + M_APP6 = 0xe6, + M_APP7 = 0xe7, + M_APP8 = 0xe8, + M_APP9 = 0xe9, + M_APP10 = 0xea, + M_APP11 = 0xeb, + M_APP12 = 0xec, + M_APP13 = 0xed, + M_APP14 = 0xee, + M_APP15 = 0xef, + + M_JPG0 = 0xf0, + M_JPG1 = 0xf1, + M_JPG2 = 0xf2, + M_JPG3 = 0xf3, + M_JPG4 = 0xf4, + M_JPG5 = 0xf5, + M_JPG6 = 0xf6, + M_JPG7 = 0xf7, + M_JPG8 = 0xf8, + M_JPG9 = 0xf9, + M_JPG10 = 0xfa, + M_JPG11 = 0xfb, + M_JPG12 = 0xfc, + M_JPG13 = 0xfd, + M_COM = 0xfe, + + M_ERROR = 0x100 + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_tag_types.cpp b/dng/dng_tag_types.cpp new file mode 100644 index 0000000..5a3c06a --- /dev/null +++ b/dng/dng_tag_types.cpp @@ -0,0 +1,59 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_tag_types.h" + +/*****************************************************************************/ + +uint32 TagTypeSize (uint32 tagType) + { + + switch (tagType) + { + + case ttByte: + case ttAscii: + case ttSByte: + case ttUndefined: + { + return 1; + } + + case ttShort: + case ttSShort: + case ttUnicode: + { + return 2; + } + + case ttLong: + case ttSLong: + case ttFloat: + case ttIFD: + { + return 4; + } + + case ttRational: + case ttDouble: + case ttSRational: + case ttComplex: + { + return 8; + } + + default: + break; + + } + + return 0; + + } + +/*****************************************************************************/ diff --git a/dng/dng_tag_types.h b/dng/dng_tag_types.h new file mode 100644 index 0000000..eb8caf6 --- /dev/null +++ b/dng/dng_tag_types.h @@ -0,0 +1,45 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef __dng_tag_types__ +#define __dng_tag_types__ + +/*****************************************************************************/ + +#include "dng_types.h" + +/*****************************************************************************/ + +enum + { + ttByte = 1, + ttAscii, + ttShort, + ttLong, + ttRational, + ttSByte, + ttUndefined, + ttSShort, + ttSLong, + ttSRational, + ttFloat, + ttDouble, + ttIFD, + ttUnicode, + ttComplex + }; + +/*****************************************************************************/ + +uint32 TagTypeSize (uint32 tagType); + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_tag_values.h b/dng/dng_tag_values.h new file mode 100644 index 0000000..0bec5e0 --- /dev/null +++ b/dng/dng_tag_values.h @@ -0,0 +1,534 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef __dng_tag_values__ +#define __dng_tag_values__ + +/*****************************************************************************/ + +#include "dng_flags.h" + +/*****************************************************************************/ + +// Values for NewSubFileType tag. + +enum + { + + // The main image data. + + sfMainImage = 0, + + // Preview image for the primary settings. + + sfPreviewImage = 1, + + // Transparency mask + + sfTransparencyMask = 4, + + // Preview (reduced resolution raw) transparency mask. + + sfPreviewMask = sfPreviewImage + sfTransparencyMask, + + // Depth map. + + sfDepthMap = 8, + + // Preview (reduced resolution raw) depth map. + + sfPreviewDepthMap = sfPreviewImage + sfDepthMap, + + // Enhanced image (processed stage 3). + + sfEnhancedImage = 16, + + // Preview image for non-primary settings. + + sfAltPreviewImage = 0x10001 + + }; + +/******************************************************************************/ + +// Values for PhotometricInterpretation tag. + +enum + { + + piWhiteIsZero = 0, + piBlackIsZero = 1, + piRGB = 2, + piRGBPalette = 3, + piTransparencyMask = 4, + piCMYK = 5, + piYCbCr = 6, + piCIELab = 8, + piICCLab = 9, + + piCFA = 32803, // TIFF-EP spec + + piLinearRaw = 34892, + + piDepth = 51177 + + }; + +/******************************************************************************/ + +// Values for PlanarConfiguration tag. + +enum + { + + pcInterleaved = 1, + pcPlanar = 2, + + // Ordering, using an RGB image as an example: + // + // RRRRRRRRRR + // GGGGGGGGGG + // BBBBBBBBBB + // RRRRRRRRRR + // GGGGGGGGGG + // BBBBBBBBBB + // + // The "AlignSIMD" variant additionally ensures that the offset of each + // plane's row is aligned to an integer multiple of SIMD vector width (16 + // or 32) bytes from the beginning of the buffer. + pcRowInterleaved = 100000, // Internal use only + pcRowInterleavedAlignSIMD = 100001 // Internal use only + + }; + +/******************************************************************************/ + +// Values for ExtraSamples tag. + +enum + { + + esUnspecified = 0, + esAssociatedAlpha = 1, + esUnassociatedAlpha = 2 + + }; + +/******************************************************************************/ + +// Values for SampleFormat tag. + +enum + { + + sfUnsignedInteger = 1, + sfSignedInteger = 2, + sfFloatingPoint = 3, + sfUndefined = 4 + + }; + +/******************************************************************************/ + +// Values for Compression tag. + +enum + { + + ccUncompressed = 1, + ccLZW = 5, + ccOldJPEG = 6, + ccJPEG = 7, + ccDeflate = 8, + + #if qDNGSupportVC5 + ccVc5 = 9, + #endif // qDNGSupportVC5 + + ccPackBits = 32773, + ccOldDeflate = 32946, + + // Used in DNG files in places that allow lossless JPEG. + + ccLossyJPEG = 34892 + + }; + +/******************************************************************************/ + +// Values for Predictor tag. + +enum + { + + cpNullPredictor = 1, + cpHorizontalDifference = 2, + cpFloatingPoint = 3, + + cpHorizontalDifferenceX2 = 34892, + cpHorizontalDifferenceX4 = 34893, + cpFloatingPointX2 = 34894, + cpFloatingPointX4 = 34895 + + }; + +/******************************************************************************/ + +// Values for ResolutionUnit tag. + +enum + { + + ruNone = 1, + ruInch = 2, + ruCM = 3, + ruMM = 4, + ruMicroM = 5 + + }; + +/******************************************************************************/ + +// Values for LightSource tag. + +enum + { + + lsUnknown = 0, + + lsDaylight = 1, + lsFluorescent = 2, + lsTungsten = 3, + lsFlash = 4, + lsFineWeather = 9, + lsCloudyWeather = 10, + lsShade = 11, + lsDaylightFluorescent = 12, // D 5700 - 7100K + lsDayWhiteFluorescent = 13, // N 4600 - 5500K + lsCoolWhiteFluorescent = 14, // W 3800 - 4500K + lsWhiteFluorescent = 15, // WW 3250 - 3800K + lsWarmWhiteFluorescent = 16, // L 2600 - 3250K + lsStandardLightA = 17, + lsStandardLightB = 18, + lsStandardLightC = 19, + lsD55 = 20, + lsD65 = 21, + lsD75 = 22, + lsD50 = 23, + lsISOStudioTungsten = 24, + + lsOther = 255 + + }; + +/******************************************************************************/ + +// Values for ExposureProgram tag. + +enum + { + + epUnidentified = 0, + epManual = 1, + epProgramNormal = 2, + epAperturePriority = 3, + epShutterPriority = 4, + epProgramCreative = 5, + epProgramAction = 6, + epPortraitMode = 7, + epLandscapeMode = 8 + + }; + +/******************************************************************************/ + +// Values for MeteringMode tag. + +enum + { + + mmUnidentified = 0, + mmAverage = 1, + mmCenterWeightedAverage = 2, + mmSpot = 3, + mmMultiSpot = 4, + mmPattern = 5, + mmPartial = 6, + + mmOther = 255 + + }; + +/******************************************************************************/ + +// CFA color codes from the TIFF/EP specification. + +enum ColorKeyCode + { + + colorKeyRed = 0, + colorKeyGreen = 1, + colorKeyBlue = 2, + colorKeyCyan = 3, + colorKeyMagenta = 4, + colorKeyYellow = 5, + colorKeyWhite = 6, + + colorKeyMaxEnum = 0xFF + + }; + +/*****************************************************************************/ + +// Values for the SensitivityType tag. + +enum + { + + stUnknown = 0, + + stStandardOutputSensitivity = 1, + stRecommendedExposureIndex = 2, + stISOSpeed = 3, + stSOSandREI = 4, + stSOSandISOSpeed = 5, + stREIandISOSpeed = 6, + stSOSandREIandISOSpeed = 7 + + }; + +/*****************************************************************************/ + +// Values for the ColorimetricReference tag. It specifies the colorimetric +// reference used for images with PhotometricInterpretation values of CFA +// or LinearRaw. + +enum + { + + // Scene referred (default): + + crSceneReferred = 0, + + // Output referred using the parameters of the ICC profile PCS. + + crICCProfilePCS = 1 + + }; + +/*****************************************************************************/ + +// Values for the ProfileEmbedPolicy tag. + +enum + { + + // Freely embedable and copyable into installations that encounter this + // profile, so long as the profile is only used to process DNG files. + + pepAllowCopying = 0, + + // Can be embeded in a DNG for portable processing, but cannot be used + // to process other files that the profile is not embedded in. + + pepEmbedIfUsed = 1, + + // Can only be used if installed on the machine processing the file. + // Note that this only applies to stand-alone profiles. Profiles that + // are already embedded inside a DNG file allowed to remain embedded + // in that DNG, even if the DNG is resaved. + + pepEmbedNever = 2, + + // No restricts on profile use or embedding. + + pepNoRestrictions = 3 + + }; + +/*****************************************************************************/ + +// Values for the ProfileHueSatMapEncoding and ProfileLookTableEncoding tags. + +enum + { + + // 1. Convert linear ProPhoto RGB values to HSV. + // 2. Use the HSV coordinates to index into the color table. + // 3. Apply color table result to the original HSV values. + // 4. Convert modified HSV values back to linear ProPhoto RGB. + + encoding_Linear = 0, + + // 1. Convert linear ProPhoto RGB values to HSV. + // 2. Encode V coordinate using sRGB encoding curve. + // 3. Use the encoded HSV coordinates to index into the color table. + // 4. Apply color table result to the encoded values from step 2. + // 5. Decode V coordinate using sRGB decoding curve (inverse of step 2). + // 6. Convert HSV values back to linear ProPhoto RGB (inverse of step 1). + + encoding_sRGB = 1 + + }; + +/*****************************************************************************/ + +// Values for the DefaultBlackRender tag. + +enum + { + + // By default, the renderer applies (possibly auto-calculated) black subtraction + // prior to the look table. + + defaultBlackRender_Auto = 0, + + // By default, the renderer does not apply any black subtraction prior to the + // look table. + + defaultBlackRender_None = 1 + + }; + +/*****************************************************************************/ + +// Values for the PreviewColorSpace tag. + +enum PreviewColorSpaceEnum + { + + previewColorSpace_Unknown = 0, + previewColorSpace_GrayGamma22 = 1, + previewColorSpace_sRGB = 2, + previewColorSpace_AdobeRGB = 3, + previewColorSpace_ProPhotoRGB = 4, + + previewColorSpace_LastValid = previewColorSpace_ProPhotoRGB, + + previewColorSpace_MaxEnum = 0xFFFFFFFF + + }; + +/*****************************************************************************/ + +// Values for CacheVersion tag. + +enum + { + + // The low-16 bits are a rendering version number. + + cacheVersionMask = 0x0FFFF, + + // Default cache version. + + cacheVersionDefault = 0x00100, + + // Is this an integer preview of a floating point image? + + cacheVersionDefloated = 0x10000, + + // Is this an flattening preview of an image with tranparency? + + cacheVersionFlattened = 0x20000, + + // Was this preview build using a the default baseline multi-channel + // CFA merge (i.e. only using the first channel)? + + cacheVersionFakeMerge = 0x40000 + + }; + +/*****************************************************************************/ + +// Values for the DepthFormat tag. + +enum + { + depthFormatUnknown = 0, + depthFormatLinear = 1, + depthFormatInverse = 2 + }; + +// Values for the DepthUnits tag. + +enum + { + depthUnitsUnknown = 0, + depthUnitsMeters = 1 + }; + +// Values for DepthMeasureType tag. + +enum + { + depthMeasureUnknown = 0, + depthMeasureOpticalAxis = 1, + depthMeasureOpticalRay = 2 + }; + +/*****************************************************************************/ + +// TIFF-style byte order markers. + +enum + { + + byteOrderII = 0x4949, // 'II' + byteOrderMM = 0x4D4D // 'MM' + + }; + +/*****************************************************************************/ + +// "Magic" numbers. + +enum + { + + // DNG related. + + magicTIFF = 42, // TIFF (and DNG) + magicExtendedProfile = 0x4352, // 'CR' + magicRawCache = 1022, // Raw cache (fast load data) + + // Other raw formats - included here so the DNG SDK can parse them. + + magicPanasonic = 85, + magicOlympusA = 0x4F52, + magicOlympusB = 0x5352 + + }; + +/*****************************************************************************/ + +// DNG Version numbers + +enum + { + + dngVersion_None = 0, + + dngVersion_1_0_0_0 = 0x01000000, + dngVersion_1_1_0_0 = 0x01010000, + dngVersion_1_2_0_0 = 0x01020000, + dngVersion_1_3_0_0 = 0x01030000, + dngVersion_1_4_0_0 = 0x01040000, + dngVersion_1_5_0_0 = 0x01050000, + + dngVersion_Current = dngVersion_1_5_0_0, + + dngVersion_SaveDefault = dngVersion_1_4_0_0 + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_temperature.cpp b/dng/dng_temperature.cpp new file mode 100644 index 0000000..272a566 --- /dev/null +++ b/dng/dng_temperature.cpp @@ -0,0 +1,254 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_temperature.h" + +#include "dng_xy_coord.h" + +/*****************************************************************************/ + +// Scale factor between distances in uv space to a more user friendly "tint" +// parameter. + +static const real64 kTintScale = -3000.0; + +/*****************************************************************************/ + +// Table from Wyszecki & Stiles, "Color Science", second edition, page 228. + +struct ruvt + { + real64 r; + real64 u; + real64 v; + real64 t; + }; + +static const ruvt kTempTable [] = + { + { 0, 0.18006, 0.26352, -0.24341 }, + { 10, 0.18066, 0.26589, -0.25479 }, + { 20, 0.18133, 0.26846, -0.26876 }, + { 30, 0.18208, 0.27119, -0.28539 }, + { 40, 0.18293, 0.27407, -0.30470 }, + { 50, 0.18388, 0.27709, -0.32675 }, + { 60, 0.18494, 0.28021, -0.35156 }, + { 70, 0.18611, 0.28342, -0.37915 }, + { 80, 0.18740, 0.28668, -0.40955 }, + { 90, 0.18880, 0.28997, -0.44278 }, + { 100, 0.19032, 0.29326, -0.47888 }, + { 125, 0.19462, 0.30141, -0.58204 }, + { 150, 0.19962, 0.30921, -0.70471 }, + { 175, 0.20525, 0.31647, -0.84901 }, + { 200, 0.21142, 0.32312, -1.0182 }, + { 225, 0.21807, 0.32909, -1.2168 }, + { 250, 0.22511, 0.33439, -1.4512 }, + { 275, 0.23247, 0.33904, -1.7298 }, + { 300, 0.24010, 0.34308, -2.0637 }, + { 325, 0.24702, 0.34655, -2.4681 }, + { 350, 0.25591, 0.34951, -2.9641 }, + { 375, 0.26400, 0.35200, -3.5814 }, + { 400, 0.27218, 0.35407, -4.3633 }, + { 425, 0.28039, 0.35577, -5.3762 }, + { 450, 0.28863, 0.35714, -6.7262 }, + { 475, 0.29685, 0.35823, -8.5955 }, + { 500, 0.30505, 0.35907, -11.324 }, + { 525, 0.31320, 0.35968, -15.628 }, + { 550, 0.32129, 0.36011, -23.325 }, + { 575, 0.32931, 0.36038, -40.770 }, + { 600, 0.33724, 0.36051, -116.45 } + }; + +/*****************************************************************************/ + +void dng_temperature::Set_xy_coord (const dng_xy_coord &xy) + { + + // Convert to uv space. + + real64 u = 2.0 * xy.x / (1.5 - xy.x + 6.0 * xy.y); + real64 v = 3.0 * xy.y / (1.5 - xy.x + 6.0 * xy.y); + + // Search for line pair coordinate is between. + + real64 last_dt = 0.0; + + real64 last_dv = 0.0; + real64 last_du = 0.0; + + for (uint32 index = 1; index <= 30; index++) + { + + // Convert slope to delta-u and delta-v, with length 1. + + real64 du = 1.0; + real64 dv = kTempTable [index] . t; + + real64 len = sqrt (1.0 + dv * dv); + + du /= len; + dv /= len; + + // Find delta from black body point to test coordinate. + + real64 uu = u - kTempTable [index] . u; + real64 vv = v - kTempTable [index] . v; + + // Find distance above or below line. + + real64 dt = - uu * dv + vv * du; + + // If below line, we have found line pair. + + if (dt <= 0.0 || index == 30) + { + + // Find fractional weight of two lines. + + if (dt > 0.0) + dt = 0.0; + + dt = -dt; + + real64 f; + + if (index == 1) + { + f = 0.0; + } + else + { + f = dt / (last_dt + dt); + } + + // Interpolate the temperature. + + fTemperature = 1.0E6 / (kTempTable [index - 1] . r * f + + kTempTable [index ] . r * (1.0 - f)); + + // Find delta from black body point to test coordinate. + + uu = u - (kTempTable [index - 1] . u * f + + kTempTable [index ] . u * (1.0 - f)); + + vv = v - (kTempTable [index - 1] . v * f + + kTempTable [index ] . v * (1.0 - f)); + + // Interpolate vectors along slope. + + du = du * (1.0 - f) + last_du * f; + dv = dv * (1.0 - f) + last_dv * f; + + len = sqrt (du * du + dv * dv); + + du /= len; + dv /= len; + + // Find distance along slope. + + fTint = (uu * du + vv * dv) * kTintScale; + + break; + + } + + // Try next line pair. + + last_dt = dt; + + last_du = du; + last_dv = dv; + + } + + } + +/*****************************************************************************/ + +dng_xy_coord dng_temperature::Get_xy_coord () const + { + + dng_xy_coord result; + + // Find inverse temperature to use as index. + + real64 r = 1.0E6 / fTemperature; + + // Convert tint to offset is uv space. + + real64 offset = fTint * (1.0 / kTintScale); + + // Search for line pair containing coordinate. + + for (uint32 index = 0; index <= 29; index++) + { + + if (r < kTempTable [index + 1] . r || index == 29) + { + + // Find relative weight of first line. + + real64 f = (kTempTable [index + 1] . r - r) / + (kTempTable [index + 1] . r - kTempTable [index] . r); + + // Interpolate the black body coordinates. + + real64 u = kTempTable [index ] . u * f + + kTempTable [index + 1] . u * (1.0 - f); + + real64 v = kTempTable [index ] . v * f + + kTempTable [index + 1] . v * (1.0 - f); + + // Find vectors along slope for each line. + + real64 uu1 = 1.0; + real64 vv1 = kTempTable [index] . t; + + real64 uu2 = 1.0; + real64 vv2 = kTempTable [index + 1] . t; + + real64 len1 = sqrt (1.0 + vv1 * vv1); + real64 len2 = sqrt (1.0 + vv2 * vv2); + + uu1 /= len1; + vv1 /= len1; + + uu2 /= len2; + vv2 /= len2; + + // Find vector from black body point. + + real64 uu3 = uu1 * f + uu2 * (1.0 - f); + real64 vv3 = vv1 * f + vv2 * (1.0 - f); + + real64 len3 = sqrt (uu3 * uu3 + vv3 * vv3); + + uu3 /= len3; + vv3 /= len3; + + // Adjust coordinate along this vector. + + u += uu3 * offset; + v += vv3 * offset; + + // Convert to xy coordinates. + + result.x = 1.5 * u / (u - 4.0 * v + 2.0); + result.y = v / (u - 4.0 * v + 2.0); + + break; + + } + + } + + return result; + + } + +/*****************************************************************************/ diff --git a/dng/dng_temperature.h b/dng/dng_temperature.h new file mode 100644 index 0000000..1e2bb75 --- /dev/null +++ b/dng/dng_temperature.h @@ -0,0 +1,92 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Representation of color temperature and offset (tint) using black body + * radiator definition. + */ + +#ifndef __dng_temperature__ +#define __dng_temperature__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_types.h" + +/*****************************************************************************/ + +class dng_temperature + { + + private: + + real64 fTemperature; + + real64 fTint; + + public: + + dng_temperature () + + : fTemperature (0.0) + , fTint (0.0) + + { + } + + dng_temperature (real64 temperature, + real64 tint) + + : fTemperature (temperature) + , fTint (tint ) + + { + + } + + dng_temperature (const dng_xy_coord &xy) + + : fTemperature (0.0) + , fTint (0.0) + + { + Set_xy_coord (xy); + } + + void SetTemperature (real64 temperature) + { + fTemperature = temperature; + } + + real64 Temperature () const + { + return fTemperature; + } + + void SetTint (real64 tint) + { + fTint = tint; + } + + real64 Tint () const + { + return fTint; + } + + void Set_xy_coord (const dng_xy_coord &xy); + + dng_xy_coord Get_xy_coord () const; + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_tile_iterator.cpp b/dng/dng_tile_iterator.cpp new file mode 100644 index 0000000..428b0d8 --- /dev/null +++ b/dng/dng_tile_iterator.cpp @@ -0,0 +1,281 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_tile_iterator.h" + +#include "dng_exceptions.h" +#include "dng_image.h" +#include "dng_pixel_buffer.h" +#include "dng_tag_types.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_tile_iterator::dng_tile_iterator (const dng_image &image, + const dng_rect &area) + + : fArea () + , fTileWidth (0) + , fTileHeight (0) + , fTileTop (0) + , fTileLeft (0) + , fRowLeft (0) + , fLeftPage (0) + , fRightPage (0) + , fTopPage (0) + , fBottomPage (0) + , fHorizontalPage (0) + , fVerticalPage (0) + + { + + Initialize (image.RepeatingTile (), + area & image.Bounds ()); + + } + +/*****************************************************************************/ + +dng_tile_iterator::dng_tile_iterator (const dng_point &tileSize, + const dng_rect &area) + + : fArea () + , fTileWidth (0) + , fTileHeight (0) + , fTileTop (0) + , fTileLeft (0) + , fRowLeft (0) + , fLeftPage (0) + , fRightPage (0) + , fTopPage (0) + , fBottomPage (0) + , fHorizontalPage (0) + , fVerticalPage (0) + + { + + dng_rect tile (area); + + tile.b = Min_int32 (tile.b, tile.t + tileSize.v); + tile.r = Min_int32 (tile.r, tile.l + tileSize.h); + + Initialize (tile, + area); + + } + +/*****************************************************************************/ + +dng_tile_iterator::dng_tile_iterator (const dng_rect &tile, + const dng_rect &area) + + : fArea () + , fTileWidth (0) + , fTileHeight (0) + , fTileTop (0) + , fTileLeft (0) + , fRowLeft (0) + , fLeftPage (0) + , fRightPage (0) + , fTopPage (0) + , fBottomPage (0) + , fHorizontalPage (0) + , fVerticalPage (0) + + { + + Initialize (tile, + area); + + } + +/*****************************************************************************/ + +void dng_tile_iterator::Initialize (const dng_rect &tile, + const dng_rect &area) + { + + fArea = area; + + if (area.IsEmpty ()) + { + + fVerticalPage = 0; + fBottomPage = -1; + + return; + + } + + int32 vOffset = tile.t; + int32 hOffset = tile.l; + + int32 tileHeight = tile.b - vOffset; + int32 tileWidth = tile.r - hOffset; + + fTileHeight = tileHeight; + fTileWidth = tileWidth; + + fLeftPage = (fArea.l - hOffset ) / tileWidth; + fRightPage = (fArea.r - hOffset - 1) / tileWidth; + + fHorizontalPage = fLeftPage; + + fTopPage = (fArea.t - vOffset ) / tileHeight; + fBottomPage = (fArea.b - vOffset - 1) / tileHeight; + + fVerticalPage = fTopPage; + + fTileLeft = fHorizontalPage * tileWidth + hOffset; + fTileTop = fVerticalPage * tileHeight + vOffset; + + fRowLeft = fTileLeft; + + } + +/*****************************************************************************/ + +bool dng_tile_iterator::GetOneTile (dng_rect &tile) + { + + if (fVerticalPage > fBottomPage) + { + return false; + } + + if (fVerticalPage > fTopPage) + tile.t = fTileTop; + else + tile.t = fArea.t; + + if (fVerticalPage < fBottomPage) + tile.b = fTileTop + fTileHeight; + else + tile.b = fArea.b; + + if (fHorizontalPage > fLeftPage) + tile.l = fTileLeft; + else + tile.l = fArea.l; + + if (fHorizontalPage < fRightPage) + tile.r = fTileLeft + fTileWidth; + else + tile.r = fArea.r; + + if (fHorizontalPage < fRightPage) + { + fHorizontalPage++; + fTileLeft += fTileWidth; + } + + else + { + + fVerticalPage++; + fTileTop += fTileHeight; + + fHorizontalPage = fLeftPage; + fTileLeft = fRowLeft; + + } + + return true; + + } + +/*****************************************************************************/ + +dng_tile_reverse_iterator::dng_tile_reverse_iterator (const dng_image &image, + const dng_rect &area) + + : fTiles () + + , fIndex (0) + + { + + dng_tile_forward_iterator iterator (image, area); + + Initialize (iterator); + + } + +/*****************************************************************************/ + +dng_tile_reverse_iterator::dng_tile_reverse_iterator (const dng_point &tileSize, + const dng_rect &area) + + : fTiles () + + , fIndex (0) + + { + + dng_tile_forward_iterator iterator (tileSize, area); + + Initialize (iterator); + + } + +/*****************************************************************************/ + +dng_tile_reverse_iterator::dng_tile_reverse_iterator (const dng_rect &tile, + const dng_rect &area) + + : fTiles () + + , fIndex (0) + + { + + dng_tile_forward_iterator iterator (tile, area); + + Initialize (iterator); + + } + +/*****************************************************************************/ + +bool dng_tile_reverse_iterator::GetOneTile (dng_rect &tile) + { + + if (fIndex == 0) + { + + return false; + + } + + fIndex--; + + tile = fTiles [fIndex]; + + return true; + + } + +/*****************************************************************************/ + +void dng_tile_reverse_iterator::Initialize (dng_tile_forward_iterator &iterator) + { + + dng_rect tile; + + while (iterator.GetOneTile (tile)) + { + + fTiles.push_back (tile); + + } + + fIndex = fTiles.size (); + + } + +/*****************************************************************************/ diff --git a/dng/dng_tile_iterator.h b/dng/dng_tile_iterator.h new file mode 100644 index 0000000..58827aa --- /dev/null +++ b/dng/dng_tile_iterator.h @@ -0,0 +1,128 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef __dng_tile_iterator__ +#define __dng_tile_iterator__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_point.h" +#include "dng_rect.h" +#include "dng_types.h" + +#include + +/*****************************************************************************/ + +class dng_base_tile_iterator + { + + public: + + virtual ~dng_base_tile_iterator () + { + } + + virtual bool GetOneTile (dng_rect &tile) = 0; + + }; + +/*****************************************************************************/ + +class dng_tile_iterator: public dng_base_tile_iterator + { + + protected: + + dng_rect fArea; + + int32 fTileWidth; + int32 fTileHeight; + + int32 fTileTop; + int32 fTileLeft; + + int32 fRowLeft; + + int32 fLeftPage; + int32 fRightPage; + + int32 fTopPage; + int32 fBottomPage; + + int32 fHorizontalPage; + int32 fVerticalPage; + + public: + + dng_tile_iterator (const dng_image &image, + const dng_rect &area); + + dng_tile_iterator (const dng_point &tileSize, + const dng_rect &area); + + dng_tile_iterator (const dng_rect &tile, + const dng_rect &area); + + virtual ~dng_tile_iterator () + { + } + + virtual bool GetOneTile (dng_rect &tile); + + private: + + void Initialize (const dng_rect &tile, + const dng_rect &area); + + }; + +/*****************************************************************************/ + +typedef dng_tile_iterator dng_tile_forward_iterator; + +/*****************************************************************************/ + +class dng_tile_reverse_iterator: public dng_base_tile_iterator + { + + public: + + std::vector fTiles; + + size_t fIndex; + + public: + + dng_tile_reverse_iterator (const dng_image &image, + const dng_rect &area); + + dng_tile_reverse_iterator (const dng_point &tileSize, + const dng_rect &area); + + dng_tile_reverse_iterator (const dng_rect &tile, + const dng_rect &area); + + virtual ~dng_tile_reverse_iterator () + { + } + + virtual bool GetOneTile (dng_rect &tile); + + private: + + void Initialize (dng_tile_iterator &iterator); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_tone_curve.cpp b/dng/dng_tone_curve.cpp new file mode 100644 index 0000000..bcd274b --- /dev/null +++ b/dng/dng_tone_curve.cpp @@ -0,0 +1,131 @@ +/*****************************************************************************/ +// Copyright 2007-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_tone_curve.h" + +#include "dng_assertions.h" +#include "dng_spline.h" +#include "dng_utils.h" + +/******************************************************************************/ + +dng_tone_curve::dng_tone_curve () + + : fCoord () + + { + + SetNull (); + + } + +/******************************************************************************/ + +bool dng_tone_curve::operator== (const dng_tone_curve &curve) const + { + + return fCoord == curve.fCoord; + + } + +/******************************************************************************/ + +void dng_tone_curve::SetNull () + { + + fCoord.resize (2); + + fCoord [0].h = 0.0; + fCoord [0].v = 0.0; + + fCoord [1].h = 1.0; + fCoord [1].v = 1.0; + + } + +/******************************************************************************/ + +bool dng_tone_curve::IsNull () const + { + + dng_tone_curve temp; + + return (*this == temp); + + } + +/******************************************************************************/ + +void dng_tone_curve::SetInvalid () + { + + fCoord.clear (); + + } + +/******************************************************************************/ + +bool dng_tone_curve::IsValid () const + { + + if (fCoord.size () < 2) + { + + return false; + + } + + for (uint32 j = 0; j < fCoord.size (); j++) + { + + if (fCoord [j] . h < 0.0 || fCoord [j] . h > 1.0 || + fCoord [j] . v < 0.0 || fCoord [j] . v > 1.0) + { + + return false; + + } + + if (j > 0) + { + + if (fCoord [j] . h <= fCoord [j - 1] . h) + { + + return false; + + } + + } + + } + + return true; + + } + +/******************************************************************************/ + +void dng_tone_curve::Solve (dng_spline_solver &solver) const + { + + solver.Reset (); + + for (uint32 index = 0; index < fCoord.size (); index++) + { + + solver.Add (fCoord [index].h, + fCoord [index].v); + + } + + solver.Solve (); + + } + +/*****************************************************************************/ diff --git a/dng/dng_tone_curve.h b/dng/dng_tone_curve.h new file mode 100644 index 0000000..6eb6fd4 --- /dev/null +++ b/dng/dng_tone_curve.h @@ -0,0 +1,60 @@ +/*****************************************************************************/ +// Copyright 2007-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Representation of 1-dimensional tone curve. + */ + +/*****************************************************************************/ + +#ifndef __dng_tone_curve__ +#define __dng_tone_curve__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_memory.h" +#include "dng_point.h" + +/*****************************************************************************/ + +class dng_tone_curve + { + + public: + + dng_std_vector fCoord; + + public: + + dng_tone_curve (); + + bool operator== (const dng_tone_curve &curve) const; + + bool operator!= (const dng_tone_curve &curve) const + { + return !(*this == curve); + } + + void SetNull (); + + bool IsNull () const; + + void SetInvalid (); + + bool IsValid () const; + + void Solve (dng_spline_solver &solver) const; + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_types.h b/dng/dng_types.h new file mode 100644 index 0000000..71e8244 --- /dev/null +++ b/dng/dng_types.h @@ -0,0 +1,108 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef __dng_types__ +#define __dng_types__ + +/*****************************************************************************/ + +#include "dng_flags.h" + +/*****************************************************************************/ + +// Standard integer types. + +#ifdef _MSC_VER +#include +#endif + +#include + +/*****************************************************************************/ + +#if qDNGUseStdInt || 1 + +typedef int8_t int8; +typedef int16_t int16; +typedef int32_t int32; +typedef int64_t int64; + +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint64_t uint64; + +#else + +typedef signed char int8; +typedef signed short int16; +#if __LP64__ +typedef signed int int32; +#else +typedef signed long int32; +#endif +typedef signed long long int64; + +typedef unsigned char uint8; +typedef unsigned short uint16; +/*Some Mac OS X 10.5 SDK headers already define uint32.*/ +#ifndef _UINT32 +#if __LP64__ +typedef unsigned int uint32; +#else +typedef unsigned long uint32; +#endif +#define _UINT32 +#endif +typedef unsigned long long uint64; + +#endif + +typedef uintptr_t uintptr; + +/*****************************************************************************/ + +typedef float real32; +typedef double real64; + +/*****************************************************************************/ + +/// \def Build a Macintosh style four-character constant in a compiler safe way. + +#define DNG_CHAR4(a,b,c,d) ((((uint32) a) << 24) |\ + (((uint32) b) << 16) |\ + (((uint32) c) << 8) |\ + (((uint32) d) )) + +/*****************************************************************************/ + +#include +#include +#include +#include +#include + +/*****************************************************************************/ + +// Visual Studio now prefers _hypot to hypot + +#ifdef _MSC_VER + +#ifdef hypot +#undef hypot +#endif + +#define hypot _hypot + +#endif + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_uncopyable.h b/dng/dng_uncopyable.h new file mode 100644 index 0000000..01c0e8c --- /dev/null +++ b/dng/dng_uncopyable.h @@ -0,0 +1,41 @@ +/*****************************************************************************/ +// Copyright 2012-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef __dng_uncopyable__ +#define __dng_uncopyable__ + +/*****************************************************************************/ + +// Virtual base class to prevent object copies. + +class dng_uncopyable + { + + protected: + + dng_uncopyable () + { + } + + ~dng_uncopyable () + { + } + + private: + + dng_uncopyable (const dng_uncopyable &); + + dng_uncopyable & operator= (const dng_uncopyable &); + + }; + +/*****************************************************************************/ + +#endif // __dng_uncopyable__ + +/*****************************************************************************/ diff --git a/dng/dng_utils.cpp b/dng/dng_utils.cpp new file mode 100644 index 0000000..5df5936 --- /dev/null +++ b/dng/dng_utils.cpp @@ -0,0 +1,888 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_utils.h" + +#include "dng_area_task.h" +#include "dng_assertions.h" +#include "dng_bottlenecks.h" +#include "dng_flags.h" +#include "dng_globals.h" +#include "dng_host.h" +#include "dng_image.h" +#include "dng_mutex.h" +#include "dng_point.h" +#include "dng_rect.h" +#include "dng_simd_type.h" +#include "dng_tile_iterator.h" + +#if qMacOS +#include +#endif + +#if qiPhone || qMacOS +// these provide timers +#include +#include +#endif + +#if qiPhone || qLinux +#include // for raise +#endif + +#if qWinOS +#include +#else +#include +#include // for va_start/va_end +#endif + +#include + +/*****************************************************************************/ + +#if qDNGDebug + +/*****************************************************************************/ + +#if qMacOS + #define DNG_DEBUG_BREAK __asm__ volatile ("int3") +#elif qiPhone + #if qiPhoneSimulator + // simulator is running on Intel + #define DNG_DEBUG_BREAK __asm__ volatile ("int3") + #else + // You'll be one level deeper in __kill. Works on Linux, Android too. + #define DNG_DEBUG_BREAK raise(SIGTRAP) + #endif +#elif qWinOS + // DebugBreak has to be emulated on WinRT + #define DNG_DEBUG_BREAK DebugBreak() +#elif qAndroid + #define DNG_DEBUG_BREAK raise(SIGTRAP) +#elif qLinux + #define DNG_DEBUG_BREAK raise(SIGTRAP) +#else + #define DNG_DEBUG_BREAK +#endif + +/*****************************************************************************/ + +void dng_show_message (const char *s) + { + // only append a newline if there isn't already one + const char* nl = "\n"; + if (s[0] && (s[strlen(s)-1] == '\n')) + nl = ""; + + #if qDNGPrintMessages + + // display the message + if (gPrintAsserts) + fprintf (stderr, "%s%s", s, nl); + + #elif qiPhone || qAndroid || qLinux + + if (gPrintAsserts) + fprintf (stderr, "%s%s", s, nl); + + // iOS doesn't print a message to the console like DebugStr and MessageBox do, so we have to do both + // You'll have to advance the program counter manually past this statement + if (gBreakOnAsserts) + DNG_DEBUG_BREAK; + + #elif qMacOS + + if (gBreakOnAsserts) + { + // truncate the to 255 chars + char ss [256]; + + uint32 len = (uint32) strlen (s); + if (len > 255) + len = 255; + strncpy (&(ss [1]), s, len ); + ss [0] = (unsigned char) len; + + DebugStr ((unsigned char *) ss); + } + else if (gPrintAsserts) + { + fprintf (stderr, "%s%s", s, nl); + } + + #elif qWinOS + + // display a dialog + // This is not thread safe. Multiple message boxes can be launched. + // Should also be launched in its own thread so main msg queue isn't thrown off. + if (gBreakOnAsserts) + MessageBoxA (NULL, (LPSTR) s, NULL, MB_OK); + else if (gPrintAsserts) + fprintf (stderr, "%s%s", s, nl); + + #endif + + } + +/*****************************************************************************/ + +void dng_show_message_f (const char *fmt, ... ) + { + + char buffer [2048]; + + va_list ap; + va_start (ap, fmt); + + vsnprintf (buffer, sizeof (buffer), fmt, ap); + + va_end (ap); + + dng_show_message (buffer); + + } + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ + +uint32 ComputeBufferSize (uint32 pixelType, + const dng_point &tileSize, + uint32 numPlanes, + PaddingType paddingType) + { + + // Convert tile size to uint32. + + if (tileSize.h < 0 || tileSize.v < 0) + { + ThrowMemoryFull ("Negative tile size"); + } + + const uint32 tileSizeH = static_cast (tileSize.h); + const uint32 tileSizeV = static_cast (tileSize.v); + + const uint32 pixelSize = TagTypeSize (pixelType); + + // Add padding to width if necessary. + + uint32 paddedWidth = tileSizeH; + + if (paddingType == padSIMDBytes) + { + + if (!RoundUpForPixelSize (paddedWidth, + pixelSize, + &paddedWidth)) + { + ThrowOverflow ("Arithmetic overflow computing buffer size"); + } + + } + + // Compute buffer size. + + uint32 bufferSize; + + if (!SafeUint32Mult (paddedWidth, tileSizeV, &bufferSize) || + !SafeUint32Mult (bufferSize, pixelSize, &bufferSize) || + !SafeUint32Mult (bufferSize, numPlanes, &bufferSize)) + { + ThrowOverflow ("Arithmetic overflow computing buffer size"); + } + + return bufferSize; + + } + +/*****************************************************************************/ + +real64 TickTimeInSeconds () + { + + #if qWinOS + + // One might think it prudent to cache the frequency here, however + // low-power CPU modes can, and do, change the value returned. + // Thus the frequencey needs to be retrieved each time. + + // Note that the frequency changing can cause the return + // result to jump backwards, which is why the TickCountInSeconds + // (below) also exists. + + // Just plug in laptop when doing timings to minimize this. + // QPC/QPH is a slow call compared to rtdsc. + // but QPC/QPF is not tied to speed step, it's the northbridge timer. + // caching the invFrequency also avoids a costly divide + + static real64 freqMultiplier = 0.0; + + if (freqMultiplier == 0.0) + { + + LARGE_INTEGER freq; + + QueryPerformanceFrequency (&freq); + + freqMultiplier = 1.0 / (real64) freq.QuadPart; + + } + + LARGE_INTEGER cycles; + + QueryPerformanceCounter (&cycles); + + return (real64) cycles.QuadPart * freqMultiplier; + + #elif qiPhone || qMacOS + + // cache frequency of high-perf timer + static real64 freqMultiplier = 0.0; + if (freqMultiplier == 0.0) + { + + mach_timebase_info_data_t freq; + mach_timebase_info(&freq); + + // converts from nanos to micros + // numer = 125, denom = 3 * 1000 + freqMultiplier = ((real64)freq.numer / (real64)freq.denom) * 1.0e-9; + + } + + return mach_absolute_time() * freqMultiplier; + + #elif qAndroid || qLinux + + //this is a fast timer to nanos + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + return now.tv_sec + (real64)now.tv_nsec * 1.0e-9; + + #else + + // Perhaps a better call exists. (e.g. avoid adjtime effects) + + struct timeval tv; + + gettimeofday (&tv, NULL); + + return tv.tv_sec + (real64)tv.tv_usec * 1.0e-6; + + #endif + + } + +/*****************************************************************************/ + +real64 TickCountInSeconds () + { + + return TickTimeInSeconds (); + + } + +/*****************************************************************************/ + +static std::atomic_int sTimerLevel (0); + +/*****************************************************************************/ + +void DNGIncrementTimerLevel () + { + + // This isn't thread coherent, multiple threads can create/destroy cr_timer + // causing the tabbing to be invalid. Imagecore disables this. + + if (!gImagecore) + { + + sTimerLevel++; + + } + + } + +/*****************************************************************************/ + +int32 DNGDecrementTimerLevel () + { + + if (gImagecore) + { + + return 0; + + } + + else + { + + return (int32) (--sTimerLevel); + + } + + } + +/*****************************************************************************/ + +dng_timer::dng_timer (const char *message) + + : fMessage (message ) + , fStartTime (TickTimeInSeconds ()) + + { + + DNGIncrementTimerLevel (); + + } + +/*****************************************************************************/ + +dng_timer::~dng_timer () + { + + uint32 level = Pin_int32 (0, DNGDecrementTimerLevel (), 10); + + if (!gDNGShowTimers) + return; + + real64 totalTime = TickTimeInSeconds () - fStartTime; + + #if defined(qCRLogging) && qCRLogging && defined(cr_logi) + + if (gImagecore) + { + // Imagecore force includes cr_log and overrides DNG to go to its logging under a mutex. + // don't use indenting or fprintf to stderr, want these buffered + cr_logi("timer", "%s: %0.3f sec\n", fMessage, totalTime); + return; + } + + #endif + + fprintf (stderr, "%*s%s: %0.3f sec\n", level*2, "", fMessage, totalTime); + + } + +/*****************************************************************************/ + +real64 MaxSquaredDistancePointToRect (const dng_point_real64 &point, + const dng_rect_real64 &rect) + { + + real64 distSqr = DistanceSquared (point, + rect.TL ()); + + distSqr = Max_real64 (distSqr, + DistanceSquared (point, + rect.BL ())); + + distSqr = Max_real64 (distSqr, + DistanceSquared (point, + rect.BR ())); + + distSqr = Max_real64 (distSqr, + DistanceSquared (point, + rect.TR ())); + + return distSqr; + + } + +/*****************************************************************************/ + +real64 MaxDistancePointToRect (const dng_point_real64 &point, + const dng_rect_real64 &rect) + { + + return sqrt (MaxSquaredDistancePointToRect (point, + rect)); + + } + +/*****************************************************************************/ + +dng_dither::dng_dither () + + : fNoiseBuffer () + + { + + const uint32 kSeed = 1; + + fNoiseBuffer.Allocate (kRNGSize2D * sizeof (uint16)); + + uint16 *buffer = fNoiseBuffer.Buffer_uint16 (); + + uint32 seed = kSeed; + + for (uint32 i = 0; i < kRNGSize2D; i++) + { + + // The correct math for 16 to 8-bit dither would be: + // + // y = (x * 255 + r) / 65535; (0 <= r <= 65534) + // + // The bottlnecks are using a faster approximation of + // this math (using a power of two for the division): + // + // y = (x * 255 + r) / 65536; (255 <= r <= 65535) + // + // To insure that all exact 8 bit values in 16 bit space + // round trip exactly to the same 8-bit, we need to limit + // r values to the range 255 to 65535. + // + // This results in the dither effect being slightly + // imperfect, but correct round-tripping of 8-bit values + // is far more important. + + uint16 value; + + do + { + + seed = DNG_Random (seed); + + value = (uint16) seed; + + } + while (value < 255); + + buffer [i] = value; + + } + + } + +/******************************************************************************/ + +const dng_dither & dng_dither::Get () + { + + static dng_dither dither; + + return dither; + + } + +/*****************************************************************************/ + +void HistogramArea (dng_host & /* host */, + const dng_image &image, + const dng_rect &area, + uint32 *hist, + uint32 maxValue, + uint32 plane) + { + + DNG_ASSERT (image.PixelType () == ttShort, "Unsupported pixel type"); + + DoZeroBytes (hist, (maxValue + 1) * (uint32) sizeof (uint32)); + + dng_rect tile; + + dng_tile_iterator iter (image, area); + + while (iter.GetOneTile (tile)) + { + + dng_const_tile_buffer buffer (image, tile); + + const void *sPtr = buffer.ConstPixel (tile.t, + tile.l, + plane); + + uint32 count0 = 1; + uint32 count1 = tile.H (); + uint32 count2 = tile.W (); + + int32 step0 = 0; + int32 step1 = buffer.fRowStep; + int32 step2 = buffer.fColStep; + + OptimizeOrder (sPtr, + buffer.fPixelSize, + count0, + count1, + count2, + step0, + step1, + step2); + + DNG_ASSERT (count0 == 1, "OptimizeOrder logic error"); + + const uint16 *s1 = (const uint16 *) sPtr; + + for (uint32 row = 0; row < count1; row++) + { + + if (maxValue == 0x0FFFF && step2 == 1) + { + + for (uint32 col = 0; col < count2; col++) + { + + uint32 x = s1 [col]; + + hist [x] ++; + + } + + } + + else + { + + const uint16 *s2 = s1; + + for (uint32 col = 0; col < count2; col++) + { + + uint32 x = s2 [0]; + + if (x <= maxValue) + { + + hist [x] ++; + + } + + s2 += step2; + + } + + } + + s1 += step1; + + } + + } + + } + +/*****************************************************************************/ + +template +class dng_limit_float_depth_task: public dng_area_task + { + + private: + + const dng_image &fSrcImage; + + dng_image &fDstImage; + + uint32 fBitDepth; + + real32 fScale; + + public: + + dng_limit_float_depth_task (const dng_image &srcImage, + dng_image &dstImage, + uint32 bitDepth, + real32 scale); + + virtual dng_rect RepeatingTile1 () const + { + return fSrcImage.RepeatingTile (); + } + + virtual dng_rect RepeatingTile2 () const + { + return fDstImage.RepeatingTile (); + } + + virtual void Process (uint32 threadIndex, + const dng_rect &tile, + dng_abort_sniffer *sniffer); + + }; + +/*****************************************************************************/ + +template +dng_limit_float_depth_task::dng_limit_float_depth_task + (const dng_image &srcImage, + dng_image &dstImage, + uint32 bitDepth, + real32 scale) + + : dng_area_task ("dng_limit_float_depth_task") + + , fSrcImage (srcImage) + , fDstImage (dstImage) + , fBitDepth (bitDepth) + , fScale (scale) + + { + + } + +/*****************************************************************************/ + +template +void dng_limit_float_depth_task::Process (uint32 /* threadIndex */, + const dng_rect &tile, + dng_abort_sniffer * /* sniffer */) + { + + INTEL_COMPILER_NEEDED_NOTE + + SET_CPU_FEATURE (simd); + + dng_const_tile_buffer srcBuffer (fSrcImage, tile); + dng_dirty_tile_buffer dstBuffer (fDstImage, tile); + + uint32 count0 = tile.H (); + uint32 count1 = tile.W (); + uint32 count2 = fDstImage.Planes (); + + int32 sStep0 = srcBuffer.fRowStep; + int32 sStep1 = srcBuffer.fColStep; + int32 sStep2 = srcBuffer.fPlaneStep; + + int32 dStep0 = dstBuffer.fRowStep; + int32 dStep1 = dstBuffer.fColStep; + int32 dStep2 = dstBuffer.fPlaneStep; + + const void *sPtr = srcBuffer.ConstPixel (tile.t, + tile.l, + 0); + + void *dPtr = dstBuffer.DirtyPixel (tile.t, + tile.l, + 0); + + OptimizeOrder (sPtr, + dPtr, + srcBuffer.fPixelSize, + dstBuffer.fPixelSize, + count0, + count1, + count2, + sStep0, + sStep1, + sStep2, + dStep0, + dStep1, + dStep2); + + const real32 *sPtr0 = (const real32 *) sPtr; + real32 *dPtr0 = ( real32 *) dPtr; + + real32 scale = fScale; + + bool limit16 = (fBitDepth == 16); + bool limit24 = (fBitDepth == 24); + + for (uint32 index0 = 0; index0 < count0; index0++) + { + + const real32 *sPtr1 = sPtr0; + real32 *dPtr1 = dPtr0; + + for (uint32 index1 = 0; index1 < count1; index1++) + { + + // If the scale is a NOP, and the data is packed solid, we can just do memory + // copy. + + if (scale == 1.0f && sStep2 == 1 && dStep2 == 1) + { + + if (dPtr1 != sPtr1) // srcImage != dstImage + { + + memcpy (dPtr1, sPtr1, count2 * (uint32) sizeof (real32)); + + } + + } + + else + { + + const real32 *sPtr2 = sPtr1; + real32 *dPtr2 = dPtr1; + INTEL_PRAGMA_SIMD_ASSERT_VECLEN_FLOAT(simd) + for (uint32 index2 = 0; index2 < count2; index2++) + { + + real32 x = sPtr2 [0]; + + x *= scale; + + dPtr2 [0] = x; + + sPtr2 += sStep2; + dPtr2 += dStep2; + + } + + } + + // The data is now in the destination buffer. + + if (limit16) + { + + //start by using intrinsic __m256 _mm256_cvtph_ps (__m128i a) + //once the intrinsic is written, merge this branch with previous one + + uint32 *dPtr2 = (uint32 *) dPtr1; + + INTEL_PRAGMA_SIMD_ASSERT_VECLEN_INT32(simd) + + for (uint32 index2 = 0; index2 < count2; index2++) + { + + uint32 x = dPtr2 [0]; + + uint16 y = DNG_FloatToHalf (x); + + x = DNG_HalfToFloat (y); + + dPtr2 [0] = x; + + dPtr2 += dStep2; + + } + + } + + else if (limit24) + { + + uint32 *dPtr2 = (uint32 *) dPtr1; + + for (uint32 index2 = 0; index2 < count2; index2++) + { + + uint32 x = dPtr2 [0]; + + uint8 temp [3]; + + DNG_FloatToFP24 (x, temp); + + x = DNG_FP24ToFloat (temp); + + dPtr2 [0] = x; + + dPtr2 += dStep2; + + } + + } + + sPtr1 += sStep1; + dPtr1 += dStep1; + + } + + sPtr0 += sStep0; + dPtr0 += dStep0; + + } + + } + +/******************************************************************************/ + +template +void LimitFloatBitDepth (dng_host &host, + const dng_image &srcImage, + dng_image &dstImage, + uint32 bitDepth, + real32 scale) + { + + DNG_ASSERT (srcImage.PixelType () == ttFloat, "Floating point image expected"); + DNG_ASSERT (dstImage.PixelType () == ttFloat, "Floating point image expected"); + + dng_limit_float_depth_task task (srcImage, + dstImage, + bitDepth, + scale); + + host.PerformAreaTask (task, dstImage.Bounds ()); + + } + +/*****************************************************************************/ + +template +void LimitFloatBitDepth (dng_host &host, + const dng_image &srcImage, + dng_image &dstImage, + uint32 bitDepth, + real32 scale); + +/*****************************************************************************/ + +#if qDNGIntelCompiler + +template +void LimitFloatBitDepth (dng_host &host, + const dng_image &srcImage, + dng_image &dstImage, + uint32 bitDepth, + real32 scale); + +#endif // qDNGIntelCompiler + +/*****************************************************************************/ + +void LimitFloatBitDepth (dng_host &host, + const dng_image &srcImage, + dng_image &dstImage, + uint32 bitDepth, + real32 scale) + { + + // Kludge: Turning this off for now because the AVX2 path produces + // slightly different results from the Scalar routine causing a mis-match + // in raw digest values when building HDR merge result negatives which + // causes the client to display a "file appears to be damaged" warning. + // -bury 11/13/2017 + + #if (qDNGIntelCompiler && qDNGExperimental && 0) + + if (gDNGMaxSIMD >= AVX2) + { + + LimitFloatBitDepth (host, + srcImage, + dstImage, + bitDepth, + scale); + + } + + else + + #endif // qDNGIntelCompiler && qDNGExperimental + + { + + LimitFloatBitDepth (host, + srcImage, + dstImage, + bitDepth, + scale); + + } + + } + +/*****************************************************************************/ diff --git a/dng/dng_utils.h b/dng/dng_utils.h new file mode 100644 index 0000000..0127ac9 --- /dev/null +++ b/dng/dng_utils.h @@ -0,0 +1,1383 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef __dng_utils__ +#define __dng_utils__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_flags.h" +#include "dng_memory.h" +#include "dng_safe_arithmetic.h" +#include "dng_types.h" +#include "dng_uncopyable.h" + +/*****************************************************************************/ + +// The unsigned integer overflow is intended here since a wrap around is used to +// calculate the abs() in the branchless version. + +DNG_ATTRIB_NO_SANITIZE("unsigned-integer-overflow") +inline uint32 Abs_int32 (int32 x) + { + + #if 0 + + // Reference version. + + return (uint32) (x < 0 ? -x : x); + + #else + + // Branchless version. + + uint32 mask = (uint32) (x >> 31); + + return (uint32) (((uint32) x + mask) ^ mask); + + #endif + + } + +inline int32 Min_int32 (int32 x, int32 y) + { + + return (x <= y ? x : y); + + } + +inline int32 Max_int32 (int32 x, int32 y) + { + + return (x >= y ? x : y); + + } + +inline int32 Pin_int32 (int32 min, int32 x, int32 max) + { + + return Max_int32 (min, Min_int32 (x, max)); + + } + +inline int32 Pin_int32_between (int32 a, int32 x, int32 b) + { + + int32 min, max; + if (a < b) { min = a; max = b; } + else { min = b; max = a; } + + return Pin_int32 (min, x, max); + + } + +/*****************************************************************************/ + +inline uint16 Min_uint16 (uint16 x, uint16 y) + { + + return (x <= y ? x : y); + + } + +inline uint16 Max_uint16 (uint16 x, uint16 y) + { + + return (x >= y ? x : y); + + } + +inline int16 Pin_int16 (int32 x) + { + + x = Pin_int32 (-32768, x, 32767); + + return (int16) x; + + } + +/*****************************************************************************/ + +inline uint32 Min_uint32 (uint32 x, uint32 y) + { + + return (x <= y ? x : y); + + } + +inline uint32 Min_uint32 (uint32 x, uint32 y, uint32 z) + { + + return Min_uint32 (x, Min_uint32 (y, z)); + + } + +inline uint32 Max_uint32 (uint32 x, uint32 y) + { + + return (x >= y ? x : y); + + } + +inline uint32 Max_uint32 (uint32 x, uint32 y, uint32 z) + { + + return Max_uint32 (x, Max_uint32 (y, z)); + + } + +inline uint32 Pin_uint32 (uint32 min, uint32 x, uint32 max) + { + + return Max_uint32 (min, Min_uint32 (x, max)); + + } + +/*****************************************************************************/ + +inline uint16 Pin_uint16 (int32 x) + { + + #if 0 + + // Reference version. + + x = Pin_int32 (0, x, 0x0FFFF); + + #else + + // Single branch version. + + if (x & ~65535) + { + + x = ~x >> 31; + + } + + #endif + + return (uint16) x; + + } + +/*****************************************************************************/ + +inline uint32 RoundUp2 (uint32 x) + { + + return (x + 1) & (uint32) ~1; + + } + +inline uint32 RoundUp4 (uint32 x) + { + + return (x + 3) & (uint32) ~3; + + } + +inline uint32 RoundUp8 (uint32 x) + { + + return (x + 7) & (uint32) ~7; + + } + +inline uint32 RoundUp16 (uint32 x) + { + + return (x + 15) & (uint32) ~15; + + } + +inline uint32 RoundUp32 (uint32 x) + { + + return (x + 31) & (uint32) ~31; + + } + +inline uint32 RoundUpSIMD (uint32 x) + { + + #if qDNGAVXSupport + return RoundUp32 (x); + #else + return RoundUp16 (x); + #endif + + } + +inline uint32 RoundUp4096 (uint32 x) + { + + return (x + 4095) & (uint32) ~4095; + + } + +/******************************************************************************/ + +inline uint32 RoundDown2 (uint32 x) + { + + return x & (uint32) ~1; + + } + +inline uint32 RoundDown4 (uint32 x) + { + + return x & (uint32) ~3; + + } + +inline uint32 RoundDown8 (uint32 x) + { + + return x & (uint32) ~7; + + } + +inline uint32 RoundDown16 (uint32 x) + { + + return x & (uint32) ~15; + + } + +inline uint32 RoundDown32 (uint32 x) + { + + return x & (uint32) ~31; + + } + +inline uint32 RoundDownSIMD (uint32 x) + { + + #if qDNGAVXSupport + return RoundDown32 (x); + #else + return RoundDown16 (x); + #endif + + } + +/******************************************************************************/ + +inline bool RoundUpForPixelSize (uint32 x, + uint32 pixelSize, + uint32 *result) + { + + #if qDNGAVXSupport + static const uint32 kTargetMultiple = 32; + #else + static const uint32 kTargetMultiple = 16; + #endif + + uint32 multiple; + + switch (pixelSize) + { + + case 1: + case 2: + case 4: + case 8: + { + multiple = kTargetMultiple / pixelSize; + break; + } + + default: + { + multiple = kTargetMultiple; + break; + } + + } + + return RoundUpUint32ToMultiple (x, multiple, result); + + } + +/******************************************************************************/ + +inline uint32 RoundUpForPixelSize (uint32 x, + uint32 pixelSize) + { + + uint32 result = 0; + + if (!RoundUpForPixelSize (x, pixelSize, &result)) + { + ThrowOverflow ("RoundUpForPixelSize"); + } + + return result; + + } + +/******************************************************************************/ + +inline int32 RoundUpForPixelSizeAsInt32 (uint32 x, + uint32 pixelSize) + { + + uint32 result = 0; + + if (!RoundUpForPixelSize (x, pixelSize, &result)) + { + ThrowOverflow ("RoundUpForPixelSize"); + } + + dng_safe_uint32 safeResult (result); + + return dng_safe_int32 (safeResult).Get (); + + } + +/******************************************************************************/ + +// Type of padding to be performed by ComputeBufferSize. + +enum PaddingType + { + + // Don't perform any padding. + + padNone, + + // Pad each scanline to an integer multiple of SIMD vector width (16 or + // 32) bytes (in the same way that RoundUpForPixelSize() does). + + padSIMDBytes + + }; + +// Returns the number of bytes required for an image tile with the given pixel +// type, tile size, number of image planes, and desired padding. Throws a +// dng_exception with dng_error_memory error code if one of the components of +// tileSize is negative or if arithmetic overflow occurs during the +// computation. + +uint32 ComputeBufferSize (uint32 pixelType, + const dng_point &tileSize, + uint32 numPlanes, + PaddingType paddingType); + +/******************************************************************************/ + +inline uint64 Abs_int64 (int64 x) + { + + return (uint64) (x < 0 ? -x : x); + + } + +inline int64 Min_int64 (int64 x, int64 y) + { + + return (x <= y ? x : y); + + } + +inline int64 Max_int64 (int64 x, int64 y) + { + + return (x >= y ? x : y); + + } + +inline int64 Pin_int64 (int64 min, int64 x, int64 max) + { + + return Max_int64 (min, Min_int64 (x, max)); + + } + +/******************************************************************************/ + +inline uint64 Min_uint64 (uint64 x, uint64 y) + { + + return (x <= y ? x : y); + + } + +inline uint64 Max_uint64 (uint64 x, uint64 y) + { + + return (x >= y ? x : y); + + } + +inline uint64 Pin_uint64 (uint64 min, uint64 x, uint64 max) + { + + return Max_uint64 (min, Min_uint64 (x, max)); + + } + +/*****************************************************************************/ + +inline real32 Abs_real32 (real32 x) + { + + return (x < 0.0f ? -x : x); + + } + +inline real32 Min_real32 (real32 x, real32 y) + { + + return (x < y ? x : y); + + } + +inline real32 Max_real32 (real32 x, real32 y) + { + + return (x > y ? x : y); + + } + +inline real32 Pin_real32 (real32 min, real32 x, real32 max) + { + + return Max_real32 (min, Min_real32 (x, max)); + + } + +inline real32 Pin_real32 (real32 x) + { + + return Pin_real32 (0.0f, x, 1.0f); + + } + +inline real32 Pin_real32_Overrange (real32 min, + real32 x, + real32 max) + { + + // Normal numbers in (min,max). No change. + + if (x > min && x < max) + { + return x; + } + + // Map large numbers (including positive infinity) to max. + + else if (x > min) + { + return max; + } + + // Map everything else (including negative infinity and all NaNs) to min. + + return min; + + } + +inline real32 Pin_Overrange (real32 x) + { + + // Normal in-range numbers, except for plus and minus zero. + + if (x > 0.0f && x <= 1.0f) + { + return x; + } + + // Large numbers, including positive infinity. + + else if (x > 0.5f) + { + return 1.0f; + } + + // Plus and minus zero, negative numbers, negative infinity, and all NaNs. + + return 0.0f; + + } + +inline real32 Lerp_real32 (real32 a, real32 b, real32 t) + { + + return a + t * (b - a); + + } + +/*****************************************************************************/ + +inline real64 Abs_real64 (real64 x) + { + + return (x < 0.0 ? -x : x); + + } + +inline real64 Min_real64 (real64 x, real64 y) + { + + return (x < y ? x : y); + + } + +inline real64 Max_real64 (real64 x, real64 y) + { + + return (x > y ? x : y); + + } + +inline real64 Pin_real64 (real64 min, real64 x, real64 max) + { + + return Max_real64 (min, Min_real64 (x, max)); + + } + +inline real64 Pin_real64 (real64 x) + { + + return Pin_real64 (0.0, x, 1.0); + + } + +inline real64 Pin_real64_Overrange (real64 min, + real64 x, + real64 max) + { + + // Normal numbers in (min,max). No change. + + if (x > min && x < max) + { + return x; + } + + // Map large numbers (including positive infinity) to max. + + else if (x > min) + { + return max; + } + + // Map everything else (including negative infinity and all NaNs) to min. + + return min; + + } + +inline real64 Lerp_real64 (real64 a, real64 b, real64 t) + { + + return a + t * (b - a); + + } + +/*****************************************************************************/ + +inline int32 Round_int32 (real32 x) + { + + return (int32) (x > 0.0f ? x + 0.5f : x - 0.5f); + + } + +inline int32 Round_int32 (real64 x) + { + + return (int32) (x > 0.0 ? x + 0.5 : x - 0.5); + + } + +inline uint32 Floor_uint32 (real32 x) + { + + return (uint32) Max_real32 (0.0f, x); + + } + +DNG_ATTRIB_NO_SANITIZE("float-cast-overflow") +inline uint32 Floor_uint32 (real64 x) + { + + return (uint32) Max_real64 (0.0, x); + + } + +inline uint32 Round_uint32 (real32 x) + { + + return Floor_uint32 (x + 0.5f); + + } + +inline uint32 Round_uint32 (real64 x) + { + + return Floor_uint32 (x + 0.5); + + } + +/******************************************************************************/ + +inline int64 Round_int64 (real64 x) + { + + return (int64) (x >= 0.0 ? x + 0.5 : x - 0.5); + + } + +/*****************************************************************************/ + +const int64 kFixed64_One = (((int64) 1) << 32); +const int64 kFixed64_Half = (((int64) 1) << 31); + +/******************************************************************************/ + +inline int64 Real64ToFixed64 (real64 x) + { + + return Round_int64 (x * (real64) kFixed64_One); + + } + +/******************************************************************************/ + +inline real64 Fixed64ToReal64 (int64 x) + { + + return x * (1.0 / (real64) kFixed64_One); + + } + +/*****************************************************************************/ + +inline char ForceUppercase (char c) + { + + if (c >= 'a' && c <= 'z') + { + + c -= 'a' - 'A'; + + } + + return c; + + } + +/*****************************************************************************/ + +inline uint16 SwapBytes16 (uint16 x) + { + + return (uint16) ((x << 8) | + (x >> 8)); + + } + +inline uint32 SwapBytes32 (uint32 x) + { + + return (x << 24) + + ((x << 8) & 0x00FF0000) + + ((x >> 8) & 0x0000FF00) + + (x >> 24); + + } + +/*****************************************************************************/ + +inline bool IsAligned16 (const void *p) + { + + return (((uintptr) p) & 1) == 0; + + } + +inline bool IsAligned32 (const void *p) + { + + return (((uintptr) p) & 3) == 0; + + } + +inline bool IsAligned64 (const void *p) + { + + return (((uintptr) p) & 7) == 0; + + } + +inline bool IsAligned128 (const void *p) + { + + return (((uintptr) p) & 15) == 0; + + } + +/******************************************************************************/ + +// Converts from RGB values (range 0.0 to 1.0) to HSV values (range 0.0 to +// 6.0 for hue, and 0.0 to 1.0 for saturation and value). + +inline void DNG_RGBtoHSV (real32 r, + real32 g, + real32 b, + real32 &h, + real32 &s, + real32 &v) + { + + v = Max_real32 (r, Max_real32 (g, b)); + + real32 gap = v - Min_real32 (r, Min_real32 (g, b)); + + if (gap > 0.0f) + { + + if (r == v) + { + + h = (g - b) / gap; + + if (h < 0.0f) + { + h += 6.0f; + } + + } + + else if (g == v) + { + h = 2.0f + (b - r) / gap; + } + + else + { + h = 4.0f + (r - g) / gap; + } + + s = gap / v; + + } + + else + { + h = 0.0f; + s = 0.0f; + } + + } + +/*****************************************************************************/ + +// Converts from HSV values (range 0.0 to 6.0 for hue, and 0.0 to 1.0 for +// saturation and value) to RGB values (range 0.0 to 1.0). + +inline void DNG_HSVtoRGB (real32 h, + real32 s, + real32 v, + real32 &r, + real32 &g, + real32 &b) + { + + if (s > 0.0f) + { + + if (h < 0.0f) + h += 6.0f; + + if (h >= 6.0f) + h -= 6.0f; + + int32 i = (int32) h; + real32 f = h - (real32) i; + + real32 p = v * (1.0f - s); + + #define q (v * (1.0f - s * f)) + #define t (v * (1.0f - s * (1.0f - f))) + + switch (i) + { + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + case 5: r = v; g = p; b = q; break; + } + + #undef q + #undef t + + } + + else + { + r = v; + g = v; + b = v; + } + + } + +/******************************************************************************/ + +// High resolution timer, for code profiling. + +real64 TickTimeInSeconds (); + +// Lower resolution timer, but more stable. + +real64 TickCountInSeconds (); + +/******************************************************************************/ + +void DNGIncrementTimerLevel (); + +int32 DNGDecrementTimerLevel (); + +/******************************************************************************/ + +class dng_timer: private dng_uncopyable + { + + public: + + dng_timer (const char *message); + + ~dng_timer (); + + private: + + const char *fMessage; + + real64 fStartTime; + + }; + +/*****************************************************************************/ + +// Returns the maximum squared Euclidean distance from the specified point to the +// specified rectangle rect. + +real64 MaxSquaredDistancePointToRect (const dng_point_real64 &point, + const dng_rect_real64 &rect); + +/*****************************************************************************/ + +// Returns the maximum Euclidean distance from the specified point to the specified +// rectangle rect. + +real64 MaxDistancePointToRect (const dng_point_real64 &point, + const dng_rect_real64 &rect); + +/*****************************************************************************/ + +inline uint32 DNG_HalfToFloat (uint16 halfValue) + { + + int32 sign = (halfValue >> 15) & 0x00000001; + int32 exponent = (halfValue >> 10) & 0x0000001f; + int32 mantissa = halfValue & 0x000003ff; + + if (exponent == 0) + { + + if (mantissa == 0) + { + + // Plus or minus zero + + return (uint32) (sign << 31); + + } + + else + { + + // Denormalized number -- renormalize it + + while (!(mantissa & 0x00000400)) + { + mantissa <<= 1; + exponent -= 1; + } + + exponent += 1; + mantissa &= ~0x00000400; + + } + + } + + else if (exponent == 31) + { + + if (mantissa == 0) + { + + // Positive or negative infinity, convert to maximum (16 bit) values. + + return (uint32) ((sign << 31) | ((0x1eL + 127 - 15) << 23) | (0x3ffL << 13)); + + } + + else + { + + // Nan -- Just set to zero. + + return 0; + + } + + } + + // Normalized number + + exponent += (127 - 15); + mantissa <<= 13; + + // Assemble sign, exponent and mantissa. + + return (uint32) ((sign << 31) | (exponent << 23) | mantissa); + + } + +/*****************************************************************************/ + +inline uint16 DNG_FloatToHalf (uint32 i) + { + + int32 sign = (i >> 16) & 0x00008000; + int32 exponent = ((i >> 23) & 0x000000ff) - (127 - 15); + int32 mantissa = i & 0x007fffff; + + if (exponent <= 0) + { + + if (exponent < -10) + { + + // Zero or underflow to zero. + + return (uint16)sign; + + } + + // E is between -10 and 0. We convert f to a denormalized half. + + mantissa = (mantissa | 0x00800000) >> (1 - exponent); + + // Round to nearest, round "0.5" up. + // + // Rounding may cause the significand to overflow and make + // our number normalized. Because of the way a half's bits + // are laid out, we don't have to treat this case separately; + // the code below will handle it correctly. + + if (mantissa & 0x00001000) + mantissa += 0x00002000; + + // Assemble the half from sign, exponent (zero) and mantissa. + + return (uint16)(sign | (mantissa >> 13)); + + } + + else if (exponent == 0xff - (127 - 15)) + { + + if (mantissa == 0) + { + + // F is an infinity; convert f to a half + // infinity with the same sign as f. + + return (uint16)(sign | 0x7c00); + + } + + else + { + + // F is a NAN; produce a half NAN that preserves + // the sign bit and the 10 leftmost bits of the + // significand of f. + + return (uint16)(sign | 0x7c00 | (mantissa >> 13)); + + } + + } + + // E is greater than zero. F is a normalized float. + // We try to convert f to a normalized half. + + // Round to nearest, round "0.5" up + + if (mantissa & 0x00001000) + { + + mantissa += 0x00002000; + + if (mantissa & 0x00800000) + { + mantissa = 0; // overflow in significand, + exponent += 1; // adjust exponent + } + + } + + // Handle exponent overflow + + if (exponent > 30) + { + return (uint16)(sign | 0x7c00); // infinity with the same sign as f. + } + + // Assemble the half from sign, exponent and mantissa. + + return (uint16)(sign | (exponent << 10) | (mantissa >> 13)); + + } + +/*****************************************************************************/ + +inline uint32 DNG_FP24ToFloat (const uint8 *input) + { + + int32 sign = (input [0] >> 7) & 0x01; + int32 exponent = (input [0] ) & 0x7F; + int32 mantissa = (((int32) input [1]) << 8) | input[2]; + + if (exponent == 0) + { + + if (mantissa == 0) + { + + // Plus or minus zero + + return (uint32) (sign << 31); + + } + + else + { + + // Denormalized number -- renormalize it + + while (!(mantissa & 0x00010000)) + { + mantissa <<= 1; + exponent -= 1; + } + + exponent += 1; + mantissa &= ~0x00010000; + + } + + } + + else if (exponent == 127) + { + + if (mantissa == 0) + { + + // Positive or negative infinity, convert to maximum (24 bit) values. + + return (uint32) ((sign << 31) | ((0x7eL + 128 - 64) << 23) | (0xffffL << 7)); + + } + + else + { + + // Nan -- Just set to zero. + + return 0; + + } + + } + + // Normalized number + + exponent += (128 - 64); + mantissa <<= 7; + + // Assemble sign, exponent and mantissa. + + return (uint32) ((sign << 31) | (exponent << 23) | mantissa); + + } + +/*****************************************************************************/ + +inline void DNG_FloatToFP24 (uint32 input, uint8 *output) + { + + int32 exponent = (int32) ((input >> 23) & 0xFF) - 128; + int32 mantissa = input & 0x007FFFFF; + + if (exponent == 127) // infinity or NaN + { + + // Will the NaN alais to infinity? + + if (mantissa != 0x007FFFFF && ((mantissa >> 7) == 0xFFFF)) + { + + mantissa &= 0x003FFFFF; // knock out msb to make it a NaN + + } + + } + + else if (exponent > 63) // overflow, map to infinity + { + + exponent = 63; + mantissa = 0x007FFFFF; + + } + + else if (exponent <= -64) + { + + if (exponent >= -79) // encode as denorm + { + mantissa = (mantissa | 0x00800000) >> (-63 - exponent); + } + + else // underflow to zero + { + mantissa = 0; + } + + exponent = -64; + + } + + output [0] = (uint8)(((input >> 24) & 0x80) | (uint32) (exponent + 64)); + + output [1] = (mantissa >> 15) & 0x00FF; + output [2] = (mantissa >> 7) & 0x00FF; + + } + +/******************************************************************************/ + +// The following code was from PSDivide.h in Photoshop. + +// High order 32-bits of an unsigned 32 by 32 multiply. + +#ifndef MULUH + +#if defined(_X86_) && defined(_MSC_VER) + +inline uint32 Muluh86 (uint32 x, uint32 y) + { + uint32 result; + __asm + { + MOV EAX, x + MUL y + MOV result, EDX + } + return (result); + } + +#define MULUH Muluh86 + +#else + +#define MULUH(x,y) ((uint32) (((x) * (uint64) (y)) >> 32)) + +#endif + +#endif + +// High order 32-bits of an signed 32 by 32 multiply. + +#ifndef MULSH + +#if defined(_X86_) && defined(_MSC_VER) + +inline int32 Mulsh86 (int32 x, int32 y) + { + int32 result; + __asm + { + MOV EAX, x + IMUL y + MOV result, EDX + } + return (result); + } + +#define MULSH Mulsh86 + +#else + +#define MULSH(x,y) ((int32) (((x) * (int64) (y)) >> 32)) + +#endif + +#endif + +/******************************************************************************/ + +// Random number generator (identical to Apple's) for portable use. + +// This implements the "minimal standard random number generator" +// as proposed by Park and Miller in CACM October, 1988. +// It has a period of 2147483647 (0x7fffffff) + +// This is the ACM standard 30 bit generator: +// x' = (x * 16807) mod 2^31-1 + +DNG_ATTRIB_NO_SANITIZE("unsigned-integer-overflow") +inline uint32 DNG_Random (uint32 seed) + { + + // high = seed / 127773 + + uint32 temp = MULUH (0x069C16BD, seed); + uint32 high = (temp + ((seed - temp) >> 1)) >> 16; + + // low = seed % 127773 + + uint32 low = seed - high * 127773; + + // seed = (seed * 16807) % 2147483647 + + seed = 16807 * low - 2836 * high; + + if (seed & 0x80000000) + seed += 2147483647; + + return seed; + + } + +/*****************************************************************************/ + +class dng_dither: private dng_uncopyable + { + + public: + + static const uint32 kRNGBits = 7; + + static const uint32 kRNGSize = 1 << kRNGBits; + + static const uint32 kRNGMask = kRNGSize - 1; + + static const uint32 kRNGSize2D = kRNGSize * kRNGSize; + + private: + + dng_memory_data fNoiseBuffer; + + private: + + dng_dither (); + + public: + + static const dng_dither & Get (); + + public: + + const uint16 *NoiseBuffer16 () const + { + return fNoiseBuffer.Buffer_uint16 (); + } + + }; + +/*****************************************************************************/ + +void HistogramArea (dng_host &host, + const dng_image &image, + const dng_rect &area, + uint32 *hist, + uint32 histLimit, + uint32 plane = 0); + +/*****************************************************************************/ + +void LimitFloatBitDepth (dng_host &host, + const dng_image &srcImage, + dng_image &dstImage, + uint32 bitDepth, + real32 scale = 1.0f); + +/*****************************************************************************/ + +#if qMacOS + +/*****************************************************************************/ + +template +class CFReleaseHelper + { + + private: + + T fRef; + + public: + + CFReleaseHelper (T ref) + : fRef (ref) + { + } + + ~CFReleaseHelper () + { + if (fRef) + { + CFRelease (fRef); + } + } + + T Get () const + { + return fRef; + } + + }; + +/*****************************************************************************/ + +#endif // qMacOS + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_validate.cpp b/dng/dng_validate.cpp new file mode 100644 index 0000000..f6b71f0 --- /dev/null +++ b/dng/dng_validate.cpp @@ -0,0 +1,1038 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +// Process exit codes +// ------------------ +// +// As usual, 0 indicates success. +// +// If an exception occurs, the exit code will be equal to: +// +// DNG SDK error code - 100000 + 100 +// +// For example, the error dng_error_memory, which has a DNG SDK error code of +// 100005, is returned as an exit code of 105. +// +// This convention accounts for the fact that the shell truncates process exit +// codes to 8 bits and that the exit code 1 is used by ASAN to signal that a +// memory error occurred (so mapping the first DNG SDK error code to an exit +// code of 1 would not be a good idea). + +/*****************************************************************************/ + +#include "dng_color_space.h" +#include "dng_date_time.h" +#include "dng_exceptions.h" +#include "dng_file_stream.h" +#include "dng_globals.h" +#include "dng_host.h" +#include "dng_ifd.h" +#include "dng_image_writer.h" +#include "dng_info.h" +#include "dng_linearization_info.h" +#include "dng_mosaic_info.h" +#include "dng_negative.h" +#include "dng_preview.h" +#include "dng_render.h" +#include "dng_simple_image.h" +#include "dng_tag_codes.h" +#include "dng_tag_types.h" +#include "dng_tag_values.h" +#include "dng_xmp.h" +#include "dng_xmp_sdk.h" + +/*****************************************************************************/ + +#if qDNGValidateTarget + +/*****************************************************************************/ + +#define kDNGValidateVersion "1.5" + +/*****************************************************************************/ + +static bool gFourColorBayer = false; + +static int32 gMosaicPlane = -1; + +static bool gIgnoreEnhanced = false; + +static uint32 gPreferredSize = 0; +static uint32 gMinimumSize = 0; +static uint32 gMaximumSize = 0; + +static uint32 gProxyDNGSize = 0; + +static const dng_color_space *gFinalSpace = &dng_space_sRGB::Get (); + +static uint32 gFinalPixelType = ttByte; + +static dng_string gDumpStage1; +static dng_string gDumpStage2; +static dng_string gDumpStage3; +static dng_string gDumpTransparency; +static dng_string gDumpDepthMap; +static dng_string gDumpTIF; +static dng_string gDumpDNG; + +/*****************************************************************************/ + +static dng_error_code dng_validate (const char *filename) + { + + printf ("Validating \"%s\"...\n", filename); + + try + { + + dng_file_stream stream (filename); + + dng_host host; + + host.SetPreferredSize (gPreferredSize); + host.SetMinimumSize (gMinimumSize ); + host.SetMaximumSize (gMaximumSize ); + + host.ValidateSizes (); + + if (host.MinimumSize ()) + { + + host.SetForPreview (true); + + gDumpDNG.Clear (); + + } + + if (gDumpDNG.NotEmpty ()) + { + + host.SetSaveDNGVersion (dngVersion_SaveDefault); + + host.SetSaveLinearDNG (false); + + host.SetKeepOriginalFile (false); + + } + + // Read into the negative. + + AutoPtr negative; + + { + + dng_info info; + + info.Parse (host, stream); + + info.PostParse (host); + + if (!info.IsValidDNG ()) + { + return dng_error_bad_format; + } + + negative.Reset (host.Make_dng_negative ()); + + negative->Parse (host, stream, info); + + negative->PostParse (host, stream, info); + + if (info.fEnhancedIndex != -1 && !gIgnoreEnhanced) + { + + dng_timer timer ("Read enhanced image time"); + + negative->ReadEnhancedImage (host, stream, info); + + } + + else + { + + dng_timer timer ("Raw image read time"); + + negative->ReadStage1Image (host, stream, info); + + } + + if (info.fMaskIndex != -1) + { + + dng_timer timer ("Transparency mask read time"); + + negative->ReadTransparencyMask (host, stream, info); + + } + + if (info.fDepthIndex != -1) + { + + dng_timer timer ("Depth map read time"); + + negative->ReadDepthMap (host, stream, info); + + } + + negative->ValidateRawImageDigest (host); + + } + + // Option to write stage 1 image. + + if (gDumpStage1.NotEmpty ()) + { + + if (negative->Stage1Image ()) + { + + dng_file_stream stream2 (gDumpStage1.Get (), true); + + const dng_image &stage1 = *negative->Stage1Image (); + + dng_image_writer writer; + + writer.WriteTIFF (host, + stream2, + stage1, + stage1.Planes () >= 3 ? piRGB + : piBlackIsZero); + + } + + gDumpStage1.Clear (); + + } + + // Metadata. + + negative->SynchronizeMetadata (); + + // Build stage 2 image. + + if (negative->Stage1Image ()) + { + + dng_timer timer ("Linearization time"); + + negative->BuildStage2Image (host); + + } + + if (gDumpStage2.NotEmpty ()) + { + + dng_file_stream stream2 (gDumpStage2.Get (), true); + + if (negative->Stage2Image ()) + { + + const dng_image &stage2 = *negative->Stage2Image (); + + dng_image_writer writer; + + writer.WriteTIFF (host, + stream2, + stage2, + stage2.Planes () >= 3 ? piRGB + : piBlackIsZero); + + } + + gDumpStage2.Clear (); + + } + + // Four color Bayer option. + + if (gFourColorBayer) + { + negative->SetFourColorBayer (); + } + + // Build stage 3 image. + + if (negative->Stage2Image ()) + { + + dng_timer timer ("Interpolate time"); + + negative->BuildStage3Image (host, + gMosaicPlane); + + } + + else + { + + negative->ResizeTransparencyToMatchStage3 (host); + + negative->ResizeDepthToMatchStage3 (host); + + } + + // Convert to proxy, if requested. + + if (gProxyDNGSize) + { + + dng_timer timer ("ConvertToProxy time"); + + dng_image_writer writer; + + negative->ConvertToProxy (host, + writer, + gProxyDNGSize); + + } + + // Flatten transparency, if required. + + if (negative->NeedFlattenTransparency (host)) + { + + dng_timer timer ("FlattenTransparency time"); + + negative->FlattenTransparency (host); + + } + + if (gDumpStage3.NotEmpty ()) + { + + dng_file_stream stream2 (gDumpStage3.Get (), true); + + const dng_image &stage3 = *negative->Stage3Image (); + + dng_image_writer writer; + + writer.WriteTIFF (host, + stream2, + stage3, + stage3.Planes () >= 3 ? piRGB + : piBlackIsZero); + + gDumpStage3.Clear (); + + } + + if (gDumpTransparency.NotEmpty ()) + { + + if (negative->TransparencyMask ()) + { + + dng_file_stream stream2 (gDumpTransparency.Get (), true); + + const dng_image &transparencyMask = *negative->TransparencyMask (); + + dng_image_writer writer; + + writer.WriteTIFF (host, + stream2, + transparencyMask, + piBlackIsZero); + + } + + gDumpTransparency.Clear (); + + } + + if (gDumpDepthMap.NotEmpty ()) + { + + if (negative->HasDepthMap ()) + { + + dng_file_stream stream2 (gDumpDepthMap.Get (), true); + + const dng_image &depthMap = *negative->DepthMap (); + + dng_image_writer writer; + + writer.WriteTIFF (host, + stream2, + depthMap, + piBlackIsZero); + + } + + gDumpDepthMap.Clear (); + + } + + // Output DNG file if requested. + + if (gDumpDNG.NotEmpty ()) + { + + // Build the preview list. + + dng_preview_list previewList; + + dng_date_time_info dateTimeInfo; + + CurrentDateTimeAndZone (dateTimeInfo); + + for (uint32 previewIndex = 0; previewIndex < 2; previewIndex++) + { + + // Skip preview if writing a compresssed main image to save space + // in this example code. + + if (negative->RawJPEGImage () != NULL && previewIndex > 0) + { + break; + } + + // Report timing. + + dng_timer timer (previewIndex == 0 ? "Build thumbnail time" + : "Build preview time"); + + // Render a preview sized image. + + AutoPtr previewImage; + + { + + dng_render render (host, *negative); + + render.SetFinalSpace (negative->IsMonochrome () ? dng_space_GrayGamma22::Get () + : dng_space_sRGB ::Get ()); + + render.SetFinalPixelType (ttByte); + + render.SetMaximumSize (previewIndex == 0 ? 256 : 1024); + + previewImage.Reset (render.Render ()); + + } + + // Don't write the preview if it is same size as thumbnail. + + if (previewIndex > 0 && + Max_uint32 (previewImage->Bounds ().W (), + previewImage->Bounds ().H ()) <= 256) + { + break; + } + + // If we have compressed JPEG data, create a compressed thumbnail. Otherwise + // save a uncompressed thumbnail. + + bool useCompressedPreview = (negative->RawJPEGImage () != NULL) || + (previewIndex > 0); + + AutoPtr preview (useCompressedPreview ? + (dng_preview *) new dng_jpeg_preview : + (dng_preview *) new dng_image_preview); + + // Setup up preview info. + + preview->fInfo.fApplicationName .Set ("dng_validate"); + preview->fInfo.fApplicationVersion.Set (kDNGValidateVersion); + + preview->fInfo.fSettingsName.Set ("Default"); + + preview->fInfo.fColorSpace = previewImage->Planes () == 1 ? + previewColorSpace_GrayGamma22 : + previewColorSpace_sRGB; + + preview->fInfo.fDateTime = dateTimeInfo.Encode_ISO_8601 (); + + if (!useCompressedPreview) + { + + dng_image_preview *imagePreview = dynamic_cast (preview.Get ()); + + imagePreview->fImage.Reset (previewImage.Release ()); + + } + + else + { + + dng_jpeg_preview *jpegPreview = dynamic_cast (preview.Get ()); + + int32 quality = (previewIndex == 0 ? 8 : 5); + + dng_image_writer writer; + + writer.EncodeJPEGPreview (host, + *previewImage, + *jpegPreview, + quality); + + } + + previewList.Append (preview); + + } + + // Write DNG file. + + dng_file_stream stream2 (gDumpDNG.Get (), true); + + { + + dng_timer timer ("Write DNG time"); + + dng_image_writer writer; + + writer.WriteDNG (host, + stream2, + *negative.Get (), + &previewList, + dngVersion_Current, + false); + + } + + gDumpDNG.Clear (); + + } + + // Output TIF file if requested. + + if (gDumpTIF.NotEmpty ()) + { + + // Render final image. + + dng_render render (host, *negative); + + render.SetFinalSpace (*gFinalSpace ); + render.SetFinalPixelType (gFinalPixelType); + + if (host.MinimumSize ()) + { + + dng_point stage3Size = negative->Stage3Image ()->Size (); + + render.SetMaximumSize (Max_uint32 (stage3Size.v, + stage3Size.h)); + + } + + AutoPtr finalImage; + + { + + dng_timer timer ("Render time"); + + finalImage.Reset (render.Render ()); + + } + + finalImage->Rotate (negative->Orientation ()); + + // Now that Camera Raw supports non-raw formats, we should + // not keep any Camera Raw settings in the XMP around when + // writing rendered files. + + if (negative->GetXMP ()) + { + + negative->GetXMP ()->RemoveProperties (XMP_NS_CRS); + negative->GetXMP ()->RemoveProperties (XMP_NS_CRSS); + negative->GetXMP ()->RemoveProperties (XMP_NS_CRD); + + } + + // Write TIF file. + + dng_file_stream stream2 (gDumpTIF.Get (), true); + + { + + dng_timer timer ("Write TIFF time"); + + dng_image_writer writer; + + writer.WriteTIFF (host, + stream2, + *finalImage.Get (), + finalImage->Planes () >= 3 ? piRGB + : piBlackIsZero, + ccUncompressed, + negative.Get (), + &render.FinalSpace ()); + + } + + gDumpTIF.Clear (); + + } + + } + + catch (const dng_exception &except) + { + + return except.ErrorCode (); + + } + + catch (...) + { + + return dng_error_unknown; + + } + + printf ("Validation complete\n"); + + return dng_error_none; + + } + +/*****************************************************************************/ + +int main (int argc, char *argv []) + { + + try + { + + if (argc == 1) + { + + fprintf (stderr, + "\n" + "dng_validate, version " kDNGValidateVersion " " + #if qDNG64Bit + "(64-bit)" + #else + "(32-bit)" + #endif + "\n" + "Copyright 2005-2019 Adobe Systems, Inc.\n" + "\n" + "Usage: %s [options] file1 file2 ...\n" + "\n" + "Valid options:\n" + "-v Verbose mode\n" + "-d Dump line limit (implies -v)\n" + "-b4 Use four-color Bayer interpolation\n" + "-s Use this sample of multi-sample CFAs\n" + "-ignoreEnhanced Ignore the enhanced image IFD\n" + "-size Preferred preview image size\n" + "-min Minimum preview image size\n" + "-max Maximum preview image size\n" + "-proxy Target size for proxy DNG\n" + "-cs1 Color space: \"sRGB\" (default)\n" + "-cs2 Color space: \"Adobe RGB\"\n" + "-cs3 Color space: \"ProPhoto RGB\"\n" + "-cs4 Color space: \"ColorMatch RGB\"\n" + "-cs5 Color space: \"Gray Gamma 1.8\"\n" + "-cs6 Color space: \"Gray Gamma 2.2\"\n" + "-16 16-bits/channel output\n" + "-1 Write stage 1 image to \".tif\"\n" + "-2 Write stage 2 image to \".tif\"\n" + "-3 Write stage 3 image to \".tif\"\n" + "-transparency Write transparency mask to \".tif\"\n" + "-depthMap Write depth map to \".tif\"\n" + "-tif Write TIF image to \".tif\"\n" + "-dng Write DNG image to \".dng\"\n" + "\n", + argv [0]); + + return 1; + + } + + int index; + + for (index = 1; index < argc && argv [index] [0] == '-'; index++) + { + + dng_string option; + + option.Set (&argv [index] [1]); + + if (option.Matches ("v", true)) + { + gVerbose = true; + } + + else if (option.Matches ("d", true)) + { + + gVerbose = true; + + gDumpLineLimit = 0; + + if (index + 1 < argc) + { + gDumpLineLimit = atoi (argv [++index]); + } + + if (!gDumpLineLimit) + { + fprintf (stderr, "*** Invalid number after -d\n"); + return 1; + } + + } + + else if (option.Matches ("s", true)) + { + + if (index + 1 < argc) + { + gMosaicPlane = atoi (argv [++index]); + } + + else + { + fprintf (stderr, "*** Missing number after -s\n"); + return 1; + } + + } + + else if (option.Matches ("b4", true)) + { + gFourColorBayer = true; + } + + else if (option.Matches ("ignoreEnhanced", true)) + { + gIgnoreEnhanced = true; + } + + else if (option.Matches ("size", true)) + { + + if (index + 1 < argc) + { + gPreferredSize = (uint32) atoi (argv [++index]); + } + + else + { + fprintf (stderr, "*** Missing number after -size\n"); + return 1; + } + + } + + else if (option.Matches ("min", true)) + { + + if (index + 1 < argc) + { + gMinimumSize = (uint32) atoi (argv [++index]); + } + + else + { + fprintf (stderr, "*** Missing number after -min\n"); + return 1; + } + + } + + else if (option.Matches ("max", true)) + { + + if (index + 1 < argc) + { + gMaximumSize = (uint32) atoi (argv [++index]); + } + + else + { + fprintf (stderr, "*** Missing number after -max\n"); + return 1; + } + + } + + else if (option.Matches ("proxy", true)) + { + + if (index + 1 < argc) + { + gProxyDNGSize = (uint32) atoi (argv [++index]); + } + + else + { + fprintf (stderr, "*** Missing number after -proxy\n"); + return 1; + } + + } + + else if (option.Matches ("cs1", true)) + { + + gFinalSpace = &dng_space_sRGB::Get (); + + } + + else if (option.Matches ("cs2", true)) + { + + gFinalSpace = &dng_space_AdobeRGB::Get (); + + } + + else if (option.Matches ("cs3", true)) + { + + gFinalSpace = &dng_space_ProPhoto::Get (); + + } + + else if (option.Matches ("cs4", true)) + { + + gFinalSpace = &dng_space_ColorMatch::Get (); + + } + + else if (option.Matches ("cs5", true)) + { + + gFinalSpace = &dng_space_GrayGamma18::Get (); + + } + + else if (option.Matches ("cs6", true)) + { + + gFinalSpace = &dng_space_GrayGamma22::Get (); + + } + + else if (option.Matches ("16")) + { + + gFinalPixelType = ttShort; + + } + + else if (option.Matches ("1")) + { + + gDumpStage1.Clear (); + + if (index + 1 < argc) + { + gDumpStage1.Set (argv [++index]); + } + + if (gDumpStage1.IsEmpty () || gDumpStage1.StartsWith ("-")) + { + fprintf (stderr, "*** Missing file name after -1\n"); + return 1; + } + + if (!gDumpStage1.EndsWith (".tif")) + { + gDumpStage1.Append (".tif"); + } + + } + + else if (option.Matches ("2")) + { + + gDumpStage2.Clear (); + + if (index + 1 < argc) + { + gDumpStage2.Set (argv [++index]); + } + + if (gDumpStage2.IsEmpty () || gDumpStage2.StartsWith ("-")) + { + fprintf (stderr, "*** Missing file name after -2\n"); + return 1; + } + + if (!gDumpStage2.EndsWith (".tif")) + { + gDumpStage2.Append (".tif"); + } + + } + + else if (option.Matches ("3")) + { + + gDumpStage3.Clear (); + + if (index + 1 < argc) + { + gDumpStage3.Set (argv [++index]); + } + + if (gDumpStage3.IsEmpty () || gDumpStage3.StartsWith ("-")) + { + fprintf (stderr, "*** Missing file name after -3\n"); + return 1; + } + + if (!gDumpStage3.EndsWith (".tif")) + { + gDumpStage3.Append (".tif"); + } + + } + + else if (option.Matches ("transparency")) + { + + gDumpTransparency.Clear (); + + if (index + 1 < argc) + { + gDumpTransparency.Set (argv [++index]); + } + + if (gDumpTransparency.IsEmpty () || gDumpTransparency.StartsWith ("-")) + { + fprintf (stderr, "*** Missing file name after -transparency\n"); + return 1; + } + + if (!gDumpTransparency.EndsWith (".tif")) + { + gDumpTransparency.Append (".tif"); + } + + } + + else if (option.Matches ("depthMap")) + { + + gDumpDepthMap.Clear (); + + if (index + 1 < argc) + { + gDumpDepthMap.Set (argv [++index]); + } + + if (gDumpDepthMap.IsEmpty () || gDumpDepthMap.StartsWith ("-")) + { + fprintf (stderr, "*** Missing file name after -depthMap\n"); + return 1; + } + + if (!gDumpDepthMap.EndsWith (".tif")) + { + gDumpDepthMap.Append (".tif"); + } + + } + + else if (option.Matches ("tif", true)) + { + + gDumpTIF.Clear (); + + if (index + 1 < argc) + { + gDumpTIF.Set (argv [++index]); + } + + if (gDumpTIF.IsEmpty () || gDumpTIF.StartsWith ("-")) + { + fprintf (stderr, "*** Missing file name after -tif\n"); + return 1; + } + + if (!gDumpTIF.EndsWith (".tif")) + { + gDumpTIF.Append (".tif"); + } + + } + + else if (option.Matches ("dng", true)) + { + + gDumpDNG.Clear (); + + if (index + 1 < argc) + { + gDumpDNG.Set (argv [++index]); + } + + if (gDumpDNG.IsEmpty () || gDumpDNG.StartsWith ("-")) + { + fprintf (stderr, "*** Missing file name after -dng\n"); + return 1; + } + + if (!gDumpDNG.EndsWith (".dng")) + { + gDumpDNG.Append (".dng"); + } + + } + + else + { + fprintf (stderr, "*** Unknown option \"-%s\"\n", option.Get ()); + return 1; + } + + } + + if (index == argc) + { + fprintf (stderr, "*** No file specified\n"); + return 1; + } + + dng_xmp_sdk::InitializeSDK (); + + int result = 0; + + while (index < argc) + { + + dng_error_code error_code = dng_validate (argv [index++]); + + if (error_code != dng_error_none) + { + + result = error_code - dng_error_unknown + 100; + + } + + } + + dng_xmp_sdk::TerminateSDK (); + + return result; + + } + + catch (...) + { + + } + + fprintf (stderr, "*** Exception thrown in main routine\n"); + + return 1; + + } + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_xmp.cpp b/dng/dng_xmp.cpp new file mode 100644 index 0000000..4622672 --- /dev/null +++ b/dng/dng_xmp.cpp @@ -0,0 +1,4757 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_xmp.h" + +#include "dng_assertions.h" +#include "dng_date_time.h" +#include "dng_exceptions.h" +#include "dng_exif.h" +#include "dng_image_writer.h" +#include "dng_iptc.h" +#include "dng_negative.h" +#include "dng_string.h" +#include "dng_string_list.h" +#include "dng_utils.h" +#include "dng_xmp_sdk.h" + +/*****************************************************************************/ + +dng_xmp::dng_xmp (dng_memory_allocator &allocator) + + : fAllocator (allocator) + + , fSDK (NULL) + + { + + fSDK = new dng_xmp_sdk (); + + if (!fSDK) + { + ThrowMemoryFull (); + } + + } + +/*****************************************************************************/ + +dng_xmp::dng_xmp (const dng_xmp &xmp) + + : fAllocator (xmp.fAllocator) + + , fSDK (NULL) + + { + + fSDK = new dng_xmp_sdk (*xmp.fSDK); + + if (!fSDK) + { + ThrowMemoryFull (); + } + + } + +/*****************************************************************************/ + +dng_xmp::~dng_xmp () + { + + if (fSDK) + { + + delete fSDK; + + } + + } + +/*****************************************************************************/ + +dng_xmp * dng_xmp::Clone () const + { + + dng_xmp *result = new dng_xmp (*this); + + if (!result) + { + ThrowMemoryFull (); + } + + return result; + + } + +/*****************************************************************************/ + +void dng_xmp::TrimDecimal (char *s) + { + + uint32 len = (uint32) strlen (s); + + while (len > 0) + { + + if (s [len - 1] == '0') + s [--len] = 0; + + else + break; + + } + + if (len > 0) + { + + if (s [len - 1] == '.') + s [--len] = 0; + + } + + } + +/*****************************************************************************/ + +dng_string dng_xmp::EncodeFingerprint (const dng_fingerprint &f, + bool allowInvalid) + { + + dng_string result; + + if (f.IsValid () || allowInvalid) + { + + char s [dng_fingerprint::kDNGFingerprintSize * 2 + 1]; + + f.ToUtf8HexString (s); + + result.Set (s); + + } + + return result; + + } + +/*****************************************************************************/ + +dng_fingerprint dng_xmp::DecodeFingerprint (const dng_string &s) + { + + dng_fingerprint result; + + if (s.Length () == 32) + result.FromUtf8HexString (s.Get ()); + + return result; + + } + +/*****************************************************************************/ + +dng_string dng_xmp::EncodeGPSVersion (uint32 version) + { + + dng_string result; + + if (version) + { + + uint8 b0 = (uint8) (version >> 24); + uint8 b1 = (uint8) (version >> 16); + uint8 b2 = (uint8) (version >> 8); + uint8 b3 = (uint8) (version ); + + if (b0 <= 9 && b1 <= 9 && b2 <= 9 && b3 <= 9) + { + + char s [32]; + + sprintf (s, + "%u.%u.%u.%u", + (unsigned) b0, + (unsigned) b1, + (unsigned) b2, + (unsigned) b3); + + result.Set (s); + + } + + } + + return result; + + } + +/*****************************************************************************/ + +uint32 dng_xmp::DecodeGPSVersion (const dng_string &s) + { + + uint32 result = 0; + + if (s.Length () == 7) + { + + unsigned b0 = 0; + unsigned b1 = 0; + unsigned b2 = 0; + unsigned b3 = 0; + + if (sscanf (s.Get (), + "%u.%u.%u.%u", + &b0, + &b1, + &b2, + &b3) == 4) + { + + result = (b0 << 24) | + (b1 << 16) | + (b2 << 8) | + (b3 ); + + } + + } + + return result; + + } + +/*****************************************************************************/ + +dng_string dng_xmp::EncodeGPSCoordinate (const dng_string &ref, + const dng_urational *coord) + { + + dng_string result; + + if (ref.Length () == 1 && coord [0].IsValid () && + coord [1].IsValid ()) + { + + char refChar = ForceUppercase (ref.Get () [0]); + + if (refChar == 'N' || + refChar == 'S' || + refChar == 'E' || + refChar == 'W') + { + + char s [256]; + + // Use the seconds case if all three values are + // integers. + + if (coord [0].d == 1 && + coord [1].d == 1 && + coord [2].d == 1) + { + + sprintf (s, + "%u,%u,%u%c", + (unsigned) coord [0].n, + (unsigned) coord [1].n, + (unsigned) coord [2].n, + refChar); + + } + + // Else we need to use the fractional minutes case. + + else + { + + // Find value minutes. + + real64 x = coord [0].As_real64 () * 60.0 + + coord [1].As_real64 () + + coord [2].As_real64 () * (1.0 / 60.0); + + // Round to fractional seven decimal places. + + uint64 y = (uint64) Round_int64 (x * 10000000.0); + + // Split into degrees and minutes. + + uint32 d = (uint32) (y / (60 * 10000000)); + uint32 m = (uint32) (y % (60 * 10000000)); + + char min [32]; + + sprintf (min, "%.7f", m * (1.0 / 10000000.0)); + + TrimDecimal (min); + + sprintf (s, + "%u,%s%c", + (unsigned) d, + min, + refChar); + + } + + result.Set (s); + + } + + } + + return result; + + } + +/*****************************************************************************/ + +void dng_xmp::DecodeGPSCoordinate (const dng_string &s, + dng_string &ref, + dng_urational *coord) + { + + ref.Clear (); + + coord [0].Clear (); + coord [1].Clear (); + coord [2].Clear (); + + if (s.Length () > 1) + { + + char refChar = ForceUppercase (s.Get () [s.Length () - 1]); + + if (refChar == 'N' || + refChar == 'S' || + refChar == 'E' || + refChar == 'W') + { + + dng_string ss (s); + + ss.Truncate (ss.Length () - 1); + + ss.NormalizeAsCommaSeparatedNumbers (); + + int degrees = 0; + + real64 minutes = 0.0; + real64 seconds = 0.0; + + int count = sscanf (ss.Get (), + "%d,%lf,%lf", + °rees, + &minutes, + &seconds); + + if (count < 1) + { + return; + } + + // The degree, minute, second values should always be positive. + + if (degrees < 0 || minutes < 0 || seconds < 0) + { + return; + } + + coord [0] = dng_urational ((uint32) degrees, 1); + + if (count <= 2) + { + coord [1].Set_real64 (minutes, 10000000); + coord [2] = dng_urational (0, 1); + } + else + { + coord [1].Set_real64 (minutes, 1); + coord [2].Set_real64 (seconds, 100000); + } + + char r [2]; + + r [0] = refChar; + r [1] = 0; + + ref.Set (r); + + } + + } + + } + +/*****************************************************************************/ + +dng_string dng_xmp::EncodeGPSDateTime (const dng_string &dateStamp, + const dng_urational *timeStamp) + { + + dng_string result; + + if (timeStamp [0].IsValid () && + timeStamp [1].IsValid () && + timeStamp [2].IsValid ()) + { + + char s [256]; + + char sec [32]; + + sprintf (sec, + "%09.6f", + timeStamp [2].As_real64 ()); + + TrimDecimal (sec); + + int year = 0; + int month = 0; + int day = 0; + + if (dateStamp.NotEmpty ()) + { + + sscanf (dateStamp.Get (), + "%d:%d:%d", + &year, + &month, + &day); + + } + + if (year >= 1 && year <= 9999 && + month >= 1 && month <= 12 && + day >= 1 && day <= 31) + { + + sprintf (s, + "%04d-%02d-%02dT%02u:%02u:%sZ", + year, + month, + day, + (unsigned) Round_uint32 (timeStamp [0].As_real64 ()), + (unsigned) Round_uint32 (timeStamp [1].As_real64 ()), + sec); + + } + + else + { + + sprintf (s, + "%02u:%02u:%sZ", + (unsigned) Round_uint32 (timeStamp [0].As_real64 ()), + (unsigned) Round_uint32 (timeStamp [1].As_real64 ()), + sec); + + } + + result.Set (s); + + } + + return result; + + } + +/*****************************************************************************/ + +void dng_xmp::DecodeGPSDateTime (const dng_string &s, + dng_string &dateStamp, + dng_urational *timeStamp) + { + + dateStamp.Clear (); + + timeStamp [0].Clear (); + timeStamp [1].Clear (); + timeStamp [2].Clear (); + + if (s.NotEmpty ()) + { + + unsigned year = 0; + unsigned month = 0; + unsigned day = 0; + unsigned hour = 0; + unsigned minute = 0; + + double second = 0.0; + + if (sscanf (s.Get (), + "%u-%u-%uT%u:%u:%lf", + &year, + &month, + &day, + &hour, + &minute, + &second) == 6) + { + + if (year >= 1 && year <= 9999 && + month >= 1 && month <= 12 && + day >= 1 && day <= 31 ) + { + + char ss [64]; + + sprintf (ss, + "%04u:%02u:%02u", + year, + month, + day); + + dateStamp.Set (ss); + + } + + } + + else if (sscanf (s.Get (), + "%u:%u:%lf", + &hour, + &minute, + &second) != 3) + { + + return; + + } + + timeStamp [0] = dng_urational ((uint32) hour , 1); + timeStamp [1] = dng_urational ((uint32) minute, 1); + + timeStamp [2].Set_real64 (second, 1000); + + } + + } + +/*****************************************************************************/ + +void dng_xmp::Parse (dng_host &host, + const void *buffer, + uint32 count) + { + + fSDK->Parse (host, + (const char *) buffer, + count); + + } + +/*****************************************************************************/ + +dng_memory_block * dng_xmp::Serialize (bool asPacket, + uint32 targetBytes, + uint32 padBytes, + bool forJPEG, + bool compact) const + { + + return fSDK->Serialize (fAllocator, + asPacket, + targetBytes, + padBytes, + forJPEG, + compact); + + } + +/*****************************************************************************/ + +void dng_xmp::PackageForJPEG (AutoPtr &stdBlock, + AutoPtr &extBlock, + dng_string &extDigest) const + { + + fSDK->PackageForJPEG (fAllocator, + stdBlock, + extBlock, + extDigest); + + } + +/*****************************************************************************/ + +void dng_xmp::MergeFromJPEG (const dng_xmp &xmp) + { + + fSDK->MergeFromJPEG (xmp.fSDK); + + } + +/*****************************************************************************/ + +bool dng_xmp::HasMeta () const + { + + return fSDK->HasMeta (); + + } + +/*****************************************************************************/ + +void * dng_xmp::GetPrivateMeta () + { + + return fSDK->GetPrivateMeta (); + + } + +/*****************************************************************************/ + +bool dng_xmp::Exists (const char *ns, + const char *path) const + { + + return fSDK->Exists (ns, path); + + } + +/*****************************************************************************/ + +bool dng_xmp::HasNameSpace (const char *ns) const + { + + return fSDK->HasNameSpace (ns); + + } + +/*****************************************************************************/ + +bool dng_xmp::IteratePaths (IteratePathsCallback *callback, + void *callbackData, + const char *ns, + const char *path) + { + + return fSDK->IteratePaths (callback, callbackData, ns, path); + + } + +/*****************************************************************************/ + +void dng_xmp::Remove (const char *ns, + const char *path) + { + + fSDK->Remove (ns, path); + + } + +/*****************************************************************************/ + +void dng_xmp::RemoveProperties (const char *ns) + { + + fSDK->RemoveProperties (ns); + + } + +/*****************************************************************************/ + +void dng_xmp::RemoveEmptyStringOrArray (const char *ns, + const char *path) + { + + if (path == NULL || path [0] == 0) + { + return; + } + + if (fSDK->IsEmptyString (ns, path) || + fSDK->IsEmptyArray (ns, path)) + { + + Remove (ns, path); + + } + + } + +/*****************************************************************************/ + +static bool RemoveEmptyStringsAndArraysCallback (const char *ns, + const char *path, + void *callbackData) + { + + dng_xmp *xmp = (dng_xmp *) callbackData; + + xmp->RemoveEmptyStringOrArray (ns, path); + + return true; + + } + +/*****************************************************************************/ + +void dng_xmp::RemoveEmptyStringsAndArrays (const char *ns) + { + + IteratePaths (RemoveEmptyStringsAndArraysCallback, + (void *) this, + ns, + NULL); + + } + +/*****************************************************************************/ + +void dng_xmp::Set (const char *ns, + const char *path, + const char *text) + { + + fSDK->Set (ns, path, text); + + } + +/*****************************************************************************/ + +bool dng_xmp::GetString (const char *ns, + const char *path, + dng_string &s) const + { + + return fSDK->GetString (ns, path, s); + + } + +/*****************************************************************************/ + +void dng_xmp::SetString (const char *ns, + const char *path, + const dng_string &s) + { + + fSDK->SetString (ns, path, s); + + } + +/*****************************************************************************/ + +bool dng_xmp::SyncString (const char *ns, + const char *path, + dng_string &s, + uint32 options) + { + + bool isDefault = s.IsEmpty (); + + // Sync 1: Force XMP to match non-XMP. + + if (options & ignoreXMP) + { + + if (isDefault || (options & removeXMP)) + { + + Remove (ns, path); + + } + + else + { + + SetString (ns, path, s); + + } + + return false; + + } + + // Sync 2: From non-XMP to XMP if non-XMP is prefered. + + if ((options & preferNonXMP) && !isDefault) + { + + if (options & removeXMP) + { + + Remove (ns, path); + + } + + else + { + + SetString (ns, path, s); + + } + + return false; + + } + + // Sync 3: From XMP to non-XMP if XMP is prefered or default non-XMP. + + if ((options & preferXMP) || isDefault) + { + + if (GetString (ns, path, s)) + { + + if (options & removeXMP) + { + + Remove (ns, path); + + } + + return true; + + } + + } + + // Sync 4: From non-XMP to XMP. + + if (options & removeXMP) + { + + Remove (ns, path); + + } + + else if (!isDefault) + { + + SetString (ns, path, s); + + } + + return false; + + } + +/*****************************************************************************/ + +bool dng_xmp::GetStringList (const char *ns, + const char *path, + dng_string_list &list) const + { + + return fSDK->GetStringList (ns, path, list); + + } + +/*****************************************************************************/ + +void dng_xmp::SetStringList (const char *ns, + const char *path, + const dng_string_list &list, + bool isBag) + { + + fSDK->SetStringList (ns, path, list, isBag); + + } + +/*****************************************************************************/ + +void dng_xmp::SyncStringList (const char *ns, + const char *path, + dng_string_list &list, + bool isBag, + uint32 options) + { + + bool isDefault = (list.Count () == 0); + + // First make sure the XMP is not badly formatted, since + // this breaks some Photoshop logic. + + ValidateStringList (ns, path); + + // Sync 1: Force XMP to match non-XMP. + + if (options & ignoreXMP) + { + + if (isDefault) + { + + Remove (ns, path); + + } + + else + { + + SetStringList (ns, path, list, isBag); + + } + + return; + + } + + // Sync 2: From non-XMP to XMP if non-XMP is prefered. + + if ((options & preferNonXMP) && !isDefault) + { + + SetStringList (ns, path, list, isBag); + + return; + + } + + // Sync 3: From XMP to non-XMP if XMP is prefered or default non-XMP. + + if ((options & preferXMP) || isDefault) + { + + if (GetStringList (ns, path, list)) + { + + return; + + } + + } + + // Sync 4: From non-XMP to XMP. + + if (!isDefault) + { + + SetStringList (ns, path, list, isBag); + + } + + } + +/*****************************************************************************/ + +void dng_xmp::SetStructField (const char *ns, + const char *path, + const char *fieldNS, + const char *fieldName, + const dng_string &s) + { + + dng_string ss (s); + + ss.SetLineEndings ('\n'); + + ss.StripLowASCII (); + + fSDK->SetStructField (ns, path, fieldNS, fieldName, ss.Get ()); + + } + +/*****************************************************************************/ + +void dng_xmp::SetStructField (const char *ns, + const char *path, + const char *fieldNS, + const char *fieldName, + const char *s) + { + + fSDK->SetStructField (ns, path, fieldNS, fieldName, s); + + } + +/*****************************************************************************/ + +void dng_xmp::DeleteStructField (const char *ns, + const char *path, + const char *fieldNS, + const char *fieldName) + { + + fSDK->DeleteStructField (ns, path, fieldNS, fieldName); + + } + +/*****************************************************************************/ + +bool dng_xmp::GetStructField (const char *ns, + const char *path, + const char *fieldNS, + const char *fieldName, + dng_string &s) const + { + + return fSDK->GetStructField (ns, path, fieldNS, fieldName, s); + + } + +/*****************************************************************************/ + +void dng_xmp::SetAltLangDefault (const char *ns, + const char *path, + const dng_string &s) + { + + fSDK->SetAltLangDefault (ns, path, s); + + } + +/*****************************************************************************/ + +void dng_xmp::SetLocalString (const char *ns, + const char *path, + const dng_local_string &s) + { + + fSDK->SetLocalString (ns, path, s); + + } + +/*****************************************************************************/ + +bool dng_xmp::GetAltLangDefault (const char *ns, + const char *path, + dng_string &s, + bool silent) const + { + + return fSDK->GetAltLangDefault (ns, path, s, silent); + + } + +/*****************************************************************************/ + +bool dng_xmp::GetLocalString (const char *ns, + const char *path, + dng_local_string &s) const + { + + return fSDK->GetLocalString (ns, path, s); + + } + +/*****************************************************************************/ + +bool dng_xmp::SyncAltLangDefault (const char *ns, + const char *path, + dng_string &s, + uint32 options) + { + + bool isDefault = s.IsEmpty (); + + // Sync 1: Force XMP to match non-XMP. + + if (options & ignoreXMP) + { + + if (isDefault) + { + + Remove (ns, path); + + } + + else + { + + SetAltLangDefault (ns, path, s); + + } + + return false; + + } + + // Sync 2: From non-XMP to XMP if non-XMP is prefered. + + if ((options & preferNonXMP) && !isDefault) + { + + SetAltLangDefault (ns, path, s); + + return false; + + } + + // Sync 3: From XMP to non-XMP if XMP is prefered or default non-XMP. + + if ((options & preferXMP) || isDefault) + { + + if (GetAltLangDefault (ns, path, s)) + { + + return true; + + } + + } + + // Sync 4: From non-XMP to XMP. + + if (!isDefault) + { + + SetAltLangDefault (ns, path, s); + + } + + return false; + + } + +/*****************************************************************************/ + +bool dng_xmp::GetBoolean (const char *ns, + const char *path, + bool &x) const + { + + dng_string s; + + if (GetString (ns, path, s)) + { + + if (s.Matches ("True")) + { + + x = true; + + return true; + + } + + if (s.Matches ("False")) + { + + x = false; + + return true; + + } + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_xmp::SetBoolean (const char *ns, + const char *path, + bool x) + { + + Set (ns, path, x ? "True" : "False"); + + } + +/*****************************************************************************/ + +bool dng_xmp::Get_int32 (const char *ns, + const char *path, + int32 &x) const + { + + dng_string s; + + if (GetString (ns, path, s)) + { + + if (s.NotEmpty ()) + { + + int y = 0; + + if (sscanf (s.Get (), "%d", &y) == 1) + { + + x = y; + + return true; + + } + + } + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_xmp::Set_int32 (const char *ns, + const char *path, + int32 x, + bool usePlus) + { + + char s [64]; + + if (x > 0 && usePlus) + { + sprintf (s, "+%d", (int) x); + } + else + { + sprintf (s, "%d", (int) x); + } + + Set (ns, path, s); + + } + +/*****************************************************************************/ + +bool dng_xmp::Get_uint32 (const char *ns, + const char *path, + uint32 &x) const + { + + dng_string s; + + if (GetString (ns, path, s)) + { + + if (s.NotEmpty ()) + { + + unsigned y = 0; + + if (sscanf (s.Get (), "%u", &y) == 1) + { + + x = y; + + return true; + + } + + } + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_xmp::Set_uint32 (const char *ns, + const char *path, + uint32 x) + { + + char s [64]; + + sprintf (s, + "%u", + (unsigned) x); + + Set (ns, path, s); + + } + +/*****************************************************************************/ + +void dng_xmp::Sync_uint32 (const char *ns, + const char *path, + uint32 &x, + bool isDefault, + uint32 options) + { + + // Sync 1: Force XMP to match non-XMP. + + if (options & ignoreXMP) + { + + if (isDefault || (options & removeXMP)) + { + + Remove (ns, path); + + } + + else + { + + Set_uint32 (ns, path, x); + + } + + return; + + } + + // Sync 2: From non-XMP to XMP if non-XMP is prefered. + + if ((options & preferNonXMP) && !isDefault) + { + + if (options & removeXMP) + { + + Remove (ns, path); + + } + + else + { + + Set_uint32 (ns, path, x); + + } + + return; + + } + + // Sync 3: From XMP to non-XMP if XMP is prefered or default non-XMP. + + if ((options & preferXMP) || isDefault) + { + + if (Get_uint32 (ns, path, x)) + { + + if (options & removeXMP) + { + + Remove (ns, path); + + } + + return; + + } + + } + + // Sync 4: From non-XMP to XMP. + + if (options & removeXMP) + { + + Remove (ns, path); + + } + + else if (!isDefault) + { + + Set_uint32 (ns, path, x); + + } + + } + +/*****************************************************************************/ + +void dng_xmp::Sync_uint32_array (const char *ns, + const char *path, + uint32 *data, + uint32 &count, + uint32 maxCount, + uint32 options) + { + + dng_string_list list; + + for (uint32 j = 0; j < count; j++) + { + + char s [32]; + + sprintf (s, "%u", (unsigned) data [j]); + + dng_string ss; + + ss.Set (s); + + list.Append (ss); + + } + + SyncStringList (ns, + path, + list, + false, + options); + + count = 0; + + for (uint32 k = 0; k < maxCount; k++) + { + + data [k] = 0; + + if (k < list.Count ()) + { + + unsigned x = 0; + + if (sscanf (list [k].Get (), "%u", &x) == 1) + { + + data [count++] = x; + + } + + } + + } + + } + +/*****************************************************************************/ + +bool dng_xmp::Get_real64 (const char *ns, + const char *path, + real64 &x) const + { + + dng_string s; + + if (GetString (ns, path, s)) + { + + if (s.NotEmpty ()) + { + + double y = 0; + + if (sscanf (s.Get (), "%lf", &y) == 1) + { + + x = y; + + return true; + + } + + } + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_xmp::Set_real64 (const char *ns, + const char *path, + real64 x, + uint32 places, + bool trim, + bool usePlus) + { + + char s [64]; + + if (x > 0.0 && usePlus) + { + sprintf (s, "+%0.*f", (unsigned) places, (double) x); + } + else + { + sprintf (s, "%0.*f", (unsigned) places, (double) x); + } + + if (trim) + { + + while (s [strlen (s) - 1] == '0') + { + s [strlen (s) - 1] = 0; + } + + if (s [strlen (s) - 1] == '.') + { + s [strlen (s) - 1] = 0; + } + + } + + Set (ns, path, s); + + } + +/*****************************************************************************/ + +bool dng_xmp::Get_urational (const char *ns, + const char *path, + dng_urational &r) const + { + + dng_string s; + + if (GetString (ns, path, s)) + { + + if (s.NotEmpty ()) + { + + unsigned n = 0; + unsigned d = 0; + + if (sscanf (s.Get (), "%u/%u", &n, &d) == 2) + { + + if (d != 0) + { + + r = dng_urational (n, d); + + return true; + + } + + } + + } + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_xmp::Set_urational (const char *ns, + const char *path, + const dng_urational &r) + { + + char s [64]; + + sprintf (s, + "%u/%u", + (unsigned) r.n, + (unsigned) r.d); + + Set (ns, path, s); + + } + +/*****************************************************************************/ + +void dng_xmp::Sync_urational (const char *ns, + const char *path, + dng_urational &r, + uint32 options) + { + + bool isDefault = r.NotValid (); + + // Sync 1: Force XMP to match non-XMP. + + if (options & ignoreXMP) + { + + if (isDefault || (options & removeXMP)) + { + + Remove (ns, path); + + } + + else + { + + Set_urational (ns, path, r); + + } + + return; + + } + + // Sync 2: From non-XMP to XMP if non-XMP is prefered. + + if ((options & preferNonXMP) && !isDefault) + { + + if (options & removeXMP) + { + + Remove (ns, path); + + } + + else + { + + Set_urational (ns, path, r); + + } + + return; + + } + + // Sync 3: From XMP to non-XMP if XMP is prefered or default non-XMP. + + if ((options & preferXMP) || isDefault) + { + + if (Get_urational (ns, path, r)) + { + + if (options & removeXMP) + { + + Remove (ns, path); + + } + + return; + + } + + } + + // Sync 4: From non-XMP to XMP. + + if (options & removeXMP) + { + + Remove (ns, path); + + } + + else if (!isDefault) + { + + Set_urational (ns, path, r); + + } + + } + +/*****************************************************************************/ + +bool dng_xmp::Get_srational (const char *ns, + const char *path, + dng_srational &r) const + { + + dng_string s; + + if (GetString (ns, path, s)) + { + + if (s.NotEmpty ()) + { + + int n = 0; + int d = 0; + + if (sscanf (s.Get (), "%d/%d", &n, &d) == 2) + { + + if (d != 0) + { + + r = dng_srational (n, d); + + return true; + + } + + } + + } + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_xmp::Set_srational (const char *ns, + const char *path, + const dng_srational &r) + { + + char s [64]; + + sprintf (s, + "%d/%d", + (int) r.n, + (int) r.d); + + Set (ns, path, s); + + } + +/*****************************************************************************/ + +void dng_xmp::Sync_srational (const char *ns, + const char *path, + dng_srational &r, + uint32 options) + { + + bool isDefault = r.NotValid (); + + // Sync 1: Force XMP to match non-XMP. + + if (options & ignoreXMP) + { + + if (isDefault || (options & removeXMP)) + { + + Remove (ns, path); + + } + + else + { + + Set_srational (ns, path, r); + + } + + return; + + } + + // Sync 2: From non-XMP to XMP if non-XMP is prefered. + + if ((options & preferNonXMP) && !isDefault) + { + + if (options & removeXMP) + { + + Remove (ns, path); + + } + + else + { + + Set_srational (ns, path, r); + + } + + return; + + } + + // Sync 3: From XMP to non-XMP if XMP is prefered or default non-XMP. + + if ((options & preferXMP) || isDefault) + { + + if (Get_srational (ns, path, r)) + { + + if (options & removeXMP) + { + + Remove (ns, path); + + } + + return; + + } + + } + + // Sync 4: From non-XMP to XMP. + + if (options & removeXMP) + { + + Remove (ns, path); + + } + + else if (!isDefault) + { + + Set_srational (ns, path, r); + + } + + } + +/*****************************************************************************/ + +bool dng_xmp::GetFingerprint (const char *ns, + const char *path, + dng_fingerprint &print) const + { + + dng_string s; + + if (GetString (ns, path, s)) + { + + dng_fingerprint temp = DecodeFingerprint (s); + + if (temp.IsValid ()) + { + + print = temp; + + return true; + + } + + } + + return false; + + } + +/******************************************************************************/ + +void dng_xmp::SetFingerprint (const char *ns, + const char *tag, + const dng_fingerprint &print, + bool allowInvalid) + { + + dng_string s = EncodeFingerprint (print, allowInvalid); + + if (s.IsEmpty ()) + { + + Remove (ns, tag); + + } + + else + { + + SetString (ns, tag, s); + + } + + } + +/******************************************************************************/ + +void dng_xmp::SetVersion2to4 (const char *ns, + const char *path, + uint32 version) + { + + char buf [32]; + + if (version & 0x000000ff) + { + + // x.x.x.x + + sprintf (buf, + "%u.%u.%u.%u", + (unsigned) ((version >> 24) & 0xff), + (unsigned) ((version >> 16) & 0xff), + (unsigned) ((version >> 8) & 0xff), + (unsigned) ((version ) & 0xff)); + + } + + else if (version & 0x0000ff00) + { + + // x.x.x + + sprintf (buf, + "%u.%u.%u", + (unsigned) ((version >> 24) & 0xff), + (unsigned) ((version >> 16) & 0xff), + (unsigned) ((version >> 8) & 0xff)); + + } + + else + { + + // x.x + + sprintf (buf, + "%u.%u", + (unsigned) ((version >> 24) & 0xff), + (unsigned) ((version >> 16) & 0xff)); + + } + + Set (ns, path, buf); + + } + +/******************************************************************************/ + +dng_fingerprint dng_xmp::GetIPTCDigest () const + { + + dng_fingerprint digest; + + if (GetFingerprint (XMP_NS_PHOTOSHOP, + "LegacyIPTCDigest", + digest)) + { + + return digest; + + } + + return dng_fingerprint (); + + } + +/******************************************************************************/ + +void dng_xmp::SetIPTCDigest (dng_fingerprint &digest) + { + + SetFingerprint (XMP_NS_PHOTOSHOP, + "LegacyIPTCDigest", + digest); + + } + +/******************************************************************************/ + +void dng_xmp::ClearIPTCDigest () + { + + Remove (XMP_NS_PHOTOSHOP, "LegacyIPTCDigest"); + + } + +/*****************************************************************************/ + +void dng_xmp::SyncIPTC (dng_iptc &iptc, + uint32 options) + { + + SyncAltLangDefault (XMP_NS_DC, + "title", + iptc.fTitle, + options); + + SyncString (XMP_NS_PHOTOSHOP, + "Category", + iptc.fCategory, + options); + + { + + uint32 x = 0xFFFFFFFF; + + if (iptc.fUrgency >= 0) + { + + x = (uint32) iptc.fUrgency; + + } + + Sync_uint32 (XMP_NS_PHOTOSHOP, + "Urgency", + x, + x == 0xFFFFFFFF, + options); + + if (x <= 9) + { + + iptc.fUrgency = (int32) x; + + } + + } + + SyncStringList (XMP_NS_PHOTOSHOP, + "SupplementalCategories", + iptc.fSupplementalCategories, + true, + options); + + SyncStringList (XMP_NS_PHOTOSHOP, + "Keywords", + iptc.fKeywords, + true, + options); + + SyncString (XMP_NS_PHOTOSHOP, + "Instructions", + iptc.fInstructions, + options); + + { + + dng_string s = iptc.fDateTimeCreated.Encode_ISO_8601 (); + + if (SyncString (XMP_NS_PHOTOSHOP, + "DateCreated", + s, + options)) + { + + iptc.fDateTimeCreated.Decode_ISO_8601 (s.Get ()); + + } + + } + + { + + dng_string s = iptc.fDigitalCreationDateTime.Encode_ISO_8601 (); + + if (SyncString (XMP_NS_EXIF, + "DateTimeDigitized", + s, + options)) + { + + iptc.fDigitalCreationDateTime.Decode_ISO_8601 (s.Get ()); + + } + + } + + SyncStringList (XMP_NS_DC, + "creator", + iptc.fAuthors, + false, + options); + + SyncString (XMP_NS_PHOTOSHOP, + "AuthorsPosition", + iptc.fAuthorsPosition, + options); + + SyncString (XMP_NS_PHOTOSHOP, + "City", + iptc.fCity, + options); + + SyncString (XMP_NS_PHOTOSHOP, + "State", + iptc.fState, + options); + + SyncString (XMP_NS_PHOTOSHOP, + "Country", + iptc.fCountry, + options); + + SyncString (XMP_NS_IPTC, + "CountryCode", + iptc.fCountryCode, + options); + + SyncString (XMP_NS_IPTC, + "Location", + iptc.fLocation, + options); + + SyncString (XMP_NS_PHOTOSHOP, + "TransmissionReference", + iptc.fTransmissionReference, + options); + + SyncString (XMP_NS_PHOTOSHOP, + "Headline", + iptc.fHeadline, + options); + + SyncString (XMP_NS_PHOTOSHOP, + "Credit", + iptc.fCredit, + options); + + SyncString (XMP_NS_PHOTOSHOP, + "Source", + iptc.fSource, + options); + + SyncAltLangDefault (XMP_NS_DC, + "rights", + iptc.fCopyrightNotice, + options); + + SyncAltLangDefault (XMP_NS_DC, + "description", + iptc.fDescription, + options); + + SyncString (XMP_NS_PHOTOSHOP, + "CaptionWriter", + iptc.fDescriptionWriter, + options); + + } + +/*****************************************************************************/ + +void dng_xmp::IngestIPTC (dng_metadata &metadata, + bool xmpIsNewer) + { + + if (metadata.IPTCLength ()) + { + + // Parse the IPTC block. + + dng_iptc iptc; + + iptc.Parse (metadata.IPTCData (), + metadata.IPTCLength (), + metadata.IPTCOffset ()); + + // Compute fingerprint of IPTC data both ways, including and + // excluding the padding data. + + dng_fingerprint iptcDigest1 = metadata.IPTCDigest (true ); + dng_fingerprint iptcDigest2 = metadata.IPTCDigest (false); + + // See if there is an IPTC fingerprint stored in the XMP. + + dng_fingerprint xmpDigest = GetIPTCDigest (); + + if (xmpDigest.IsValid ()) + { + + // If they match, the XMP was already synced with this + // IPTC block, and we should not resync since it might + // overwrite changes in the XMP data. + + if (iptcDigest1 == xmpDigest) + { + + return; + + } + + // If it matches the incorrectly computed digest, skip + // the sync, but fix the digest in the XMP. + + if (iptcDigest2 == xmpDigest) + { + + SetIPTCDigest (iptcDigest1); + + return; + + } + + // Else the IPTC has changed, so force an update. + + xmpIsNewer = false; + + } + + else + { + + // There is no IPTC digest. Previously we would + // prefer the IPTC in this case, but the MWG suggests + // that we prefer the XMP in this case. + + xmpIsNewer = true; + + } + + // Remember the fingerprint of the IPTC we are syncing with. + + SetIPTCDigest (iptcDigest1); + + // Find the sync options. + + uint32 options = xmpIsNewer ? preferXMP + : preferNonXMP; + + // Synchronize the fields. + + SyncIPTC (iptc, options); + + } + + // After the IPTC data is moved to XMP, we don't need it anymore. + + metadata.ClearIPTC (); + + } + +/*****************************************************************************/ + +void dng_xmp::RebuildIPTC (dng_metadata &metadata, + dng_memory_allocator &allocator, + bool padForTIFF) + { + + // If there is no XMP, then there is no IPTC. + + if (!fSDK->HasMeta ()) + { + return; + } + + // Extract the legacy IPTC fields from the XMP data. + + dng_iptc iptc; + + SyncIPTC (iptc, preferXMP); + + // Build legacy IPTC record + + if (iptc.NotEmpty ()) + { + + AutoPtr block (iptc.Spool (allocator, + padForTIFF)); + + metadata.SetIPTC (block); + + } + + } + +/*****************************************************************************/ + +void dng_xmp::SyncFlash (uint32 &flashState, + uint32 &flashMask, + uint32 options) + { + + bool isDefault = (flashState == 0xFFFFFFFF); + + if ((options & ignoreXMP) || !isDefault) + { + + Remove (XMP_NS_EXIF, "Flash"); + + } + + if (!isDefault) + { + + fSDK->SetStructField (XMP_NS_EXIF, + "Flash", + XMP_NS_EXIF, + "Fired", + (flashState & 0x1) ? "True" : "False"); + + if (((flashMask >> 1) & 3) == 3) + { + + char s [8]; + + sprintf (s, "%u", (unsigned) ((flashState >> 1) & 3)); + + fSDK->SetStructField (XMP_NS_EXIF, + "Flash", + XMP_NS_EXIF, + "Return", + s); + + } + + if (((flashMask >> 3) & 3) == 3) + { + + char s [8]; + + sprintf (s, "%u", (unsigned) ((flashState >> 3) & 3)); + + fSDK->SetStructField (XMP_NS_EXIF, + "Flash", + XMP_NS_EXIF, + "Mode", + s); + + } + + if ((flashMask & (1 << 5)) != 0) + { + + fSDK->SetStructField (XMP_NS_EXIF, + "Flash", + XMP_NS_EXIF, + "Function", + (flashState & (1 << 5)) ? "True" : "False"); + + } + + if ((flashMask & (1 << 6)) != 0) + { + + fSDK->SetStructField (XMP_NS_EXIF, + "Flash", + XMP_NS_EXIF, + "RedEyeMode", + (flashState & (1 << 6)) ? "True" : "False"); + + } + + } + + else if (fSDK->Exists (XMP_NS_EXIF, "Flash")) + { + + dng_string s; + + if (fSDK->GetStructField (XMP_NS_EXIF, + "Flash", + XMP_NS_EXIF, + "Fired", + s)) + { + + flashState = 0; + flashMask = 1; + + if (s.Matches ("True")) + { + flashState |= 1; + } + + if (fSDK->GetStructField (XMP_NS_EXIF, + "Flash", + XMP_NS_EXIF, + "Return", + s)) + { + + unsigned x = 0; + + if (sscanf (s.Get (), "%u", &x) == 1 && x <= 3) + { + + flashState |= x << 1; + flashMask |= 3 << 1; + + } + + } + + if (fSDK->GetStructField (XMP_NS_EXIF, + "Flash", + XMP_NS_EXIF, + "Mode", + s)) + { + + unsigned x = 0; + + if (sscanf (s.Get (), "%u", &x) == 1 && x <= 3) + { + + flashState |= x << 3; + flashMask |= 3 << 3; + + } + + } + + if (fSDK->GetStructField (XMP_NS_EXIF, + "Flash", + XMP_NS_EXIF, + "Function", + s)) + { + + flashMask |= 1 << 5; + + if (s.Matches ("True")) + { + flashState |= 1 << 5; + } + + } + + if (fSDK->GetStructField (XMP_NS_EXIF, + "Flash", + XMP_NS_EXIF, + "RedEyeMode", + s)) + { + + flashMask |= 1 << 6; + + if (s.Matches ("True")) + { + flashState |= 1 << 6; + } + + } + + } + + } + + } + +/*****************************************************************************/ + +void dng_xmp::GenerateDefaultLensName (dng_exif &exif) + { + + // Generate default lens name from lens info if required. + // Ignore names names that end in "f/0.0" due to third party bug. + + if ((exif.fLensName.IsEmpty () || + exif.fLensName.EndsWith ("f/0.0")) && exif.fLensInfo [0].IsValid ()) + { + + char s [256]; + + real64 minFL = exif.fLensInfo [0].As_real64 (); + real64 maxFL = exif.fLensInfo [1].As_real64 (); + + // The f-stop numbers are optional. + + if (exif.fLensInfo [2].IsValid ()) + { + + real64 minFS = exif.fLensInfo [2].As_real64 (); + real64 maxFS = exif.fLensInfo [3].As_real64 (); + + if (minFL == maxFL) + sprintf (s, "%.1f mm f/%.1f", minFL, minFS); + + else if (minFS == maxFS) + sprintf (s, "%.1f-%.1f mm f/%.1f", minFL, maxFL, minFS); + + else + sprintf (s, "%.1f-%.1f mm f/%.1f-%.1f", minFL, maxFL, minFS, maxFS); + + } + + else + { + + if (minFL == maxFL) + sprintf (s, "%.1f mm", minFL); + + else + sprintf (s, "%.1f-%.1f mm", minFL, maxFL); + + } + + exif.fLensName.Set (s); + + SetString (XMP_NS_AUX, + "Lens", + exif.fLensName); + + // Don't generate exifEX for now. + + // SetString (XMP_NS_EXIFEX, + // "LensModel", + // exif.fLensName); + + } + + } + +/*****************************************************************************/ + +void dng_xmp::SyncLensName (dng_exif &exif) + { + + // EXIF lens names are sometimes missing or wrong (esp. when non-OEM lenses + // are used). So prefer the value from XMP. + + // Check XMP for the lens model in the aux namespace first. If not there, + // then check the exifEX namespace. + + if (!SyncString (XMP_NS_AUX, + "Lens", + exif.fLensName, + preferXMP)) + { + + SyncString (XMP_NS_EXIFEX, + "LensModel", + exif.fLensName, + preferXMP); + + } + + GenerateDefaultLensName (exif); + + } + +/*****************************************************************************/ + +void dng_xmp::SyncExif (dng_exif &exif, + const dng_exif *originalExif, + bool doingUpdateFromXMP, + bool removeFromXMP) + { + + DNG_ASSERT (!doingUpdateFromXMP || originalExif, + "Must have original EXIF if doingUpdateFromXMP"); + + // Default synchronization options for the read-only fields. + + uint32 readOnly = doingUpdateFromXMP ? ignoreXMP + : preferNonXMP; + + // Option for removable fields. + + uint32 removable = removeFromXMP ? removeXMP + : 0; + + // Make: + + SyncString (XMP_NS_TIFF, + "Make", + exif.fMake, + readOnly + removable); + + // Model: + + SyncString (XMP_NS_TIFF, + "Model", + exif.fModel, + readOnly + removable); + + // Exif version number: + + { + + // Find version number in XMP, if any. + + uint32 xmpVersion = 0; + + { + + dng_string s; + + if (GetString (XMP_NS_EXIF, "ExifVersion", s)) + { + + unsigned b0; + unsigned b1; + unsigned b2; + unsigned b3; + + if (sscanf (s.Get (), + "%1u%1u%1u%1u", + &b0, + &b1, + &b2, + &b3) == 4) + { + + if (b0 <= 9 && b1 <= 9 && b2 <= 9 && b3 <= 9) + { + + b0 += '0'; + b1 += '0'; + b2 += '0'; + b3 += '0'; + + xmpVersion = (b0 << 24) | + (b1 << 16) | + (b2 << 8) | + (b3 ); + + } + + } + + } + + } + + // Use maximum logic for merging. + + exif.fExifVersion = Max_uint32 (exif.fExifVersion, xmpVersion); + + // Provide default value for ExifVersion. + + if (!exif.fExifVersion) + { + exif.SetVersion0231 (); + } + + // Update XMP. + + dng_string xmpString; + + if (exif.fExifVersion) + { + + unsigned b0 = ((exif.fExifVersion >> 24) & 0x0FF) - '0'; + unsigned b1 = ((exif.fExifVersion >> 16) & 0x0FF) - '0'; + unsigned b2 = ((exif.fExifVersion >> 8) & 0x0FF) - '0'; + unsigned b3 = ((exif.fExifVersion ) & 0x0FF) - '0'; + + if (b0 <= 9 && b1 <= 9 && b2 <= 9 && b3 <= 9) + { + + char s [5]; + + sprintf (s, + "%1u%1u%1u%1u", + b0, + b1, + b2, + b3); + + xmpString.Set (s); + + } + + } + + if (removeFromXMP || xmpString.IsEmpty ()) + { + + Remove (XMP_NS_EXIF, "ExifVersion"); + + } + + else + { + + SetString (XMP_NS_EXIF, "ExifVersion", xmpString); + + } + + } + + // ExposureTime / ShutterSpeedValue: + + { + + // Process twice in case XMP contains only one of the + // two fields. + + for (uint32 pass = 0; pass < 2; pass++) + { + + dng_urational et = exif.fExposureTime; + + Sync_urational (XMP_NS_EXIF, + "ExposureTime", + et, + readOnly); + + if (et.IsValid ()) + { + + exif.SetExposureTime (et.As_real64 (), false); + + } + + dng_srational ss = exif.fShutterSpeedValue; + + Sync_srational (XMP_NS_EXIF, + "ShutterSpeedValue", + ss, + readOnly); + + if (ss.IsValid ()) + { + + exif.SetShutterSpeedValue (ss.As_real64 ()); + + } + + } + + if (removeFromXMP) + { + + Remove (XMP_NS_EXIF, "ExposureTime"); + + Remove (XMP_NS_EXIF, "ShutterSpeedValue"); + + } + + } + + // FNumber / ApertureValue: + + { + + for (uint32 pass = 0; pass < 2; pass++) + { + + dng_urational fs = exif.fFNumber; + + Sync_urational (XMP_NS_EXIF, + "FNumber", + fs, + readOnly); + + if (fs.IsValid ()) + { + + exif.SetFNumber (fs.As_real64 ()); + + } + + dng_urational av = exif.fApertureValue; + + Sync_urational (XMP_NS_EXIF, + "ApertureValue", + av, + readOnly); + + if (av.IsValid ()) + { + + exif.SetApertureValue (av.As_real64 ()); + + } + + } + + if (removeFromXMP) + { + + Remove (XMP_NS_EXIF, "FNumber"); + + Remove (XMP_NS_EXIF, "ApertureValue"); + + } + + } + + // Exposure program: + + Sync_uint32 (XMP_NS_EXIF, + "ExposureProgram", + exif.fExposureProgram, + exif.fExposureProgram == 0xFFFFFFFF, + readOnly + removable); + + // ISO Speed Ratings: + + { + + uint32 isoSpeedRatingsCount = 0; + + uint32 isoSpeedRatingsOptions = readOnly; + + uint32 oldISOSpeedRatings [3]; + + memcpy (oldISOSpeedRatings, + exif.fISOSpeedRatings, + sizeof (oldISOSpeedRatings)); + + bool checkXMPForHigherISO = false; + + for (uint32 j = 0; j < 3; j++) + { + + // Special case: the EXIF 2.2x standard represents ISO speed ratings with + // 2 bytes, which cannot hold ISO speed ratings above 65535 (e.g., + // 102400). If the EXIF ISO speed rating value is 65535, prefer the XMP + // ISOSpeedRatings tag value. + + if (exif.fISOSpeedRatings [j] == 65535) + { + + isoSpeedRatingsOptions = preferXMP; + + checkXMPForHigherISO = true; + + isoSpeedRatingsCount = 0; + + break; + + } + + else if (exif.fISOSpeedRatings [j] == 0) + { + break; + } + + isoSpeedRatingsCount++; + + } + + Sync_uint32_array (XMP_NS_EXIF, + "ISOSpeedRatings", + exif.fISOSpeedRatings, + isoSpeedRatingsCount, + 3, + isoSpeedRatingsOptions); + + // If the EXIF ISO was 65535 and we failed to find anything meaningful in the + // XMP, then we fall back to the EXIF ISO. + + if (checkXMPForHigherISO && (isoSpeedRatingsCount == 0)) + { + + memcpy (exif.fISOSpeedRatings, + oldISOSpeedRatings, + sizeof (oldISOSpeedRatings)); + + } + + // Only remove the ISO tag if there are not ratings over 65535. + + if (removeFromXMP) + { + + bool hasHighISO = false; + + for (uint32 j = 0; j < 3; j++) + { + + if (exif.fISOSpeedRatings [j] == 0) + { + break; + } + + hasHighISO = hasHighISO || (exif.fISOSpeedRatings [j] > 65535); + + } + + if (!hasHighISO) + { + + Remove (XMP_NS_EXIF, "ISOSpeedRatings"); + + } + + } + + } + + // SensitivityType: + + Sync_uint32 (XMP_NS_EXIF, + "SensitivityType", + exif.fSensitivityType, + exif.fSensitivityType == stUnknown, + readOnly + removable); + + // StandardOutputSensitivity: + + Sync_uint32 (XMP_NS_EXIF, + "StandardOutputSensitivity", + exif.fStandardOutputSensitivity, + exif.fStandardOutputSensitivity == 0, + readOnly + removable); + + // RecommendedExposureIndex: + + Sync_uint32 (XMP_NS_EXIF, + "RecommendedExposureIndex", + exif.fRecommendedExposureIndex, + exif.fRecommendedExposureIndex == 0, + readOnly + removable); + + // ISOSpeed: + + Sync_uint32 (XMP_NS_EXIF, + "ISOSpeed", + exif.fISOSpeed, + exif.fISOSpeed == 0, + readOnly + removable); + + // ISOSpeedLatitudeyyy: + + Sync_uint32 (XMP_NS_EXIF, + "ISOSpeedLatitudeyyy", + exif.fISOSpeedLatitudeyyy, + exif.fISOSpeedLatitudeyyy == 0, + readOnly + removable); + + // ISOSpeedLatitudezzz: + + Sync_uint32 (XMP_NS_EXIF, + "ISOSpeedLatitudezzz", + exif.fISOSpeedLatitudezzz, + exif.fISOSpeedLatitudezzz == 0, + readOnly + removable); + + // ExposureIndex: + + Sync_urational (XMP_NS_EXIF, + "ExposureIndex", + exif.fExposureIndex, + readOnly + removable); + + // Brightness Value: + + Sync_srational (XMP_NS_EXIF, + "BrightnessValue", + exif.fBrightnessValue, + readOnly + removable); + + // Exposure Bias: + + Sync_srational (XMP_NS_EXIF, + "ExposureBiasValue", + exif.fExposureBiasValue, + readOnly + removable); + + // Max Aperture: + + Sync_urational (XMP_NS_EXIF, + "MaxApertureValue", + exif.fMaxApertureValue, + readOnly + removable); + + // Subject Distance: + + Sync_urational (XMP_NS_EXIF, + "SubjectDistance", + exif.fSubjectDistance, + readOnly + removable); + + // Metering Mode: + + Sync_uint32 (XMP_NS_EXIF, + "MeteringMode", + exif.fMeteringMode, + exif.fMeteringMode == 0xFFFFFFFF, + readOnly + removable); + + // Light Source: + + Sync_uint32 (XMP_NS_EXIF, + "LightSource", + exif.fLightSource, + exif.fLightSource > 0x0FFFF, + readOnly + removable); + + // Flash State: + + SyncFlash (exif.fFlash, + exif.fFlashMask, + readOnly); + + if (removeFromXMP) + { + Remove (XMP_NS_EXIF, "Flash"); + } + + // Focal Length: + + Sync_urational (XMP_NS_EXIF, + "FocalLength", + exif.fFocalLength, + readOnly + removable); + + // Sensing Method. + + Sync_uint32 (XMP_NS_EXIF, + "SensingMethod", + exif.fSensingMethod, + exif.fSensingMethod > 0x0FFFF, + readOnly + removable); + + // File Source. + + Sync_uint32 (XMP_NS_EXIF, + "FileSource", + exif.fFileSource, + exif.fFileSource > 0x0FF, + readOnly + removable); + + // Scene Type. + + Sync_uint32 (XMP_NS_EXIF, + "SceneType", + exif.fSceneType, + exif.fSceneType > 0x0FF, + readOnly + removable); + + // Focal Length in 35mm Film: + + Sync_uint32 (XMP_NS_EXIF, + "FocalLengthIn35mmFilm", + exif.fFocalLengthIn35mmFilm, + exif.fFocalLengthIn35mmFilm == 0, + readOnly + removable); + + // Custom Rendered: + + Sync_uint32 (XMP_NS_EXIF, + "CustomRendered", + exif.fCustomRendered, + exif.fCustomRendered > 0x0FFFF, + readOnly + removable); + + // Exposure Mode: + + Sync_uint32 (XMP_NS_EXIF, + "ExposureMode", + exif.fExposureMode, + exif.fExposureMode > 0x0FFFF, + readOnly + removable); + + // White Balance: + + Sync_uint32 (XMP_NS_EXIF, + "WhiteBalance", + exif.fWhiteBalance, + exif.fWhiteBalance > 0x0FFFF, + readOnly + removable); + + // Scene Capture Type: + + Sync_uint32 (XMP_NS_EXIF, + "SceneCaptureType", + exif.fSceneCaptureType, + exif.fSceneCaptureType > 0x0FFFF, + readOnly + removable); + + // Gain Control: + + Sync_uint32 (XMP_NS_EXIF, + "GainControl", + exif.fGainControl, + exif.fGainControl > 0x0FFFF, + readOnly + removable); + + // Contrast: + + Sync_uint32 (XMP_NS_EXIF, + "Contrast", + exif.fContrast, + exif.fContrast > 0x0FFFF, + readOnly + removable); + + // Saturation: + + Sync_uint32 (XMP_NS_EXIF, + "Saturation", + exif.fSaturation, + exif.fSaturation > 0x0FFFF, + readOnly + removable); + + // Sharpness: + + Sync_uint32 (XMP_NS_EXIF, + "Sharpness", + exif.fSharpness, + exif.fSharpness > 0x0FFFF, + readOnly + removable); + + // Subject Distance Range: + + Sync_uint32 (XMP_NS_EXIF, + "SubjectDistanceRange", + exif.fSubjectDistanceRange, + exif.fSubjectDistanceRange > 0x0FFFF, + readOnly + removable); + + // Subject Area: + + Sync_uint32_array (XMP_NS_EXIF, + "SubjectArea", + exif.fSubjectArea, + exif.fSubjectAreaCount, + sizeof (exif.fSubjectArea ) / + sizeof (exif.fSubjectArea [0]), + readOnly); + + if (removeFromXMP) + { + Remove (XMP_NS_EXIF, "SubjectArea"); + } + + // Digital Zoom Ratio: + + Sync_urational (XMP_NS_EXIF, + "DigitalZoomRatio", + exif.fDigitalZoomRatio, + readOnly + removable); + + // Focal Plane Resolution: + + Sync_urational (XMP_NS_EXIF, + "FocalPlaneXResolution", + exif.fFocalPlaneXResolution, + readOnly + removable); + + Sync_urational (XMP_NS_EXIF, + "FocalPlaneYResolution", + exif.fFocalPlaneYResolution, + readOnly + removable); + + Sync_uint32 (XMP_NS_EXIF, + "FocalPlaneResolutionUnit", + exif.fFocalPlaneResolutionUnit, + exif.fFocalPlaneResolutionUnit > 0x0FFFF, + readOnly + removable); + + // ImageDescription: (XMP is is always preferred) + + if (fSDK->GetAltLangDefault (XMP_NS_DC, + "description", + exif.fImageDescription)) + + { + + } + + else if (doingUpdateFromXMP) + { + + exif.fImageDescription.Clear (); + + if (originalExif->fImageDescription.NotEmpty ()) + { + + fSDK->SetAltLangDefault (XMP_NS_DC, + "description", + dng_string ()); + + } + + } + + else if (exif.fImageDescription.NotEmpty ()) + { + + fSDK->SetAltLangDefault (XMP_NS_DC, + "description", + exif.fImageDescription); + + } + + // Artist: (XMP is is always preferred) + + { + + dng_string_list xmpList; + + if (fSDK->GetStringList (XMP_NS_DC, + "creator", + xmpList)) + { + + exif.fArtist.Clear (); + + if (xmpList.Count () > 0) + { + + uint32 j; + + uint32 bufferSize = xmpList.Count () * 4 + 1; + + for (j = 0; j < xmpList.Count (); j++) + { + + bufferSize += xmpList [j].Length () * 2; + + } + + dng_memory_data temp (bufferSize); + + char *t = temp.Buffer_char (); + + for (j = 0; j < xmpList.Count (); j++) + { + + const char *s = xmpList [j].Get (); + + bool needQuotes = xmpList [j].Contains ("; ") || + s [0] == '\"'; + + if (needQuotes) + { + *(t++) = '\"'; + } + + while (s [0] != 0) + { + + if (s [0] == '\"' && needQuotes) + { + *(t++) = '\"'; + } + + *(t++) = *(s++); + + } + + if (needQuotes) + { + *(t++) = '\"'; + } + + if (j != xmpList.Count () - 1) + { + *(t++) = ';'; + *(t++) = ' '; + } + else + { + *t = 0; + } + + } + + exif.fArtist.Set (temp.Buffer_char ()); + + } + + } + + else if (doingUpdateFromXMP) + { + + exif.fArtist.Clear (); + + if (originalExif->fArtist.NotEmpty ()) + { + + dng_string_list fakeList; + + fakeList.Append (dng_string ()); + + SetStringList (XMP_NS_DC, + "creator", + fakeList, + false); + + } + + } + + else if (exif.fArtist.NotEmpty ()) + { + + dng_string_list newList; + + dng_memory_data temp (exif.fArtist.Length () + 1); + + const char *s = exif.fArtist.Get (); + + char *t = temp.Buffer_char (); + + bool first = true; + + bool quoted = false; + + bool valid = true; + + while (s [0] != 0 && valid) + { + + if (first) + { + + if (s [0] == '\"') + { + + quoted = true; + + s++; + + } + + } + + first = false; + + if (quoted) + { + + if (s [0] == '\"' && + s [1] == '\"') + { + + s+= 2; + + *(t++) = '\"'; + + } + + else if (s [0] == '\"') + { + + s++; + + quoted = false; + + valid = valid && ((s [0] == 0) || ((s [0] == ';' && s [1] == ' '))); + + } + + else + { + + *(t++) = *(s++); + + } + + } + + else if (s [0] == ';' && + s [1] == ' ') + { + + s += 2; + + t [0] = 0; + + dng_string ss; + + ss.Set (temp.Buffer_char ()); + + newList.Append (ss); + + t = temp.Buffer_char (); + + first = true; + + } + + else + { + + *(t++) = *(s++); + + } + + } + + if (quoted) + { + + valid = false; + + } + + if (valid) + { + + if (t != temp.Buffer_char ()) + { + + t [0] = 0; + + dng_string ss; + + ss.Set (temp.Buffer_char ()); + + newList.Append (ss); + + } + + } + + else + { + + newList.Clear (); + + newList.Append (exif.fArtist); + + } + + SetStringList (XMP_NS_DC, + "creator", + newList, + false); + + } + + } + + // Software: (XMP is is always preferred) + + if (fSDK->GetString (XMP_NS_XAP, + "CreatorTool", + exif.fSoftware)) + + { + + } + + else if (doingUpdateFromXMP) + { + + exif.fSoftware.Clear (); + + if (originalExif->fSoftware.NotEmpty ()) + { + + fSDK->SetString (XMP_NS_XAP, + "CreatorTool", + dng_string ()); + + } + + } + + else if (exif.fSoftware.NotEmpty ()) + { + + fSDK->SetString (XMP_NS_XAP, + "CreatorTool", + exif.fSoftware); + + } + + // Copyright: (XMP is is always preferred) + + if (fSDK->GetAltLangDefault (XMP_NS_DC, + "rights", + exif.fCopyright)) + + { + + } + + else if (doingUpdateFromXMP) + { + + exif.fCopyright.Clear (); + + if (originalExif->fCopyright.NotEmpty ()) + { + + fSDK->SetAltLangDefault (XMP_NS_DC, + "rights", + dng_string ()); + + } + + } + + else if (exif.fCopyright.NotEmpty ()) + { + + fSDK->SetAltLangDefault (XMP_NS_DC, + "rights", + exif.fCopyright); + + } + + // Camera serial number private tag: + + SyncString (XMP_NS_AUX, + "SerialNumber", + exif.fCameraSerialNumber, + readOnly); + + // Lens Info: + + { + + dng_string s; + + if (exif.fLensInfo [0].IsValid ()) + { + + char ss [256]; + + sprintf (ss, + "%u/%u %u/%u %u/%u %u/%u", + (unsigned) exif.fLensInfo [0].n, + (unsigned) exif.fLensInfo [0].d, + (unsigned) exif.fLensInfo [1].n, + (unsigned) exif.fLensInfo [1].d, + (unsigned) exif.fLensInfo [2].n, + (unsigned) exif.fLensInfo [2].d, + (unsigned) exif.fLensInfo [3].n, + (unsigned) exif.fLensInfo [3].d); + + s.Set (ss); + + } + + // Check XMP for the lens specification in the aux namespace first. If + // not there, then check the exifEX namespace. + + SyncString (XMP_NS_AUX, + "LensInfo", + s, + readOnly); + + if (s.NotEmpty ()) + { + + unsigned n [4]; + unsigned d [4]; + + if (sscanf (s.Get (), + "%u/%u %u/%u %u/%u %u/%u", + &n [0], + &d [0], + &n [1], + &d [1], + &n [2], + &d [2], + &n [3], + &d [3]) == 8) + { + + for (uint32 j = 0; j < 4; j++) + { + + exif.fLensInfo [j] = dng_urational (n [j], d [j]); + + } + + } + + + } + + else + { + + // Not found in aux, so examine exifEX. + + dng_string_list strList; + + SyncStringList (XMP_NS_EXIFEX, + "LensSpecification", + strList, + false, + readOnly); + + if (strList.Count () == 4) + { + + const dng_string &s0 = strList [0]; + const dng_string &s1 = strList [1]; + const dng_string &s2 = strList [2]; + const dng_string &s3 = strList [3]; + + unsigned n [4]; + unsigned d [4]; + + if (sscanf (s0.Get (), "%u/%u", &n [0], &d [0]) == 2 && + sscanf (s1.Get (), "%u/%u", &n [1], &d [1]) == 2 && + sscanf (s2.Get (), "%u/%u", &n [2], &d [2]) == 2 && + sscanf (s3.Get (), "%u/%u", &n [3], &d [3]) == 2) + { + + for (uint32 j = 0; j < 4; j++) + { + + exif.fLensInfo [j] = dng_urational (n [j], d [j]); + + } + + } + + } + + } + + } + + // Lens name: + + SyncLensName (exif); + + // Lens ID: + + SyncString (XMP_NS_AUX, + "LensID", + exif.fLensID, + readOnly); + + // Lens Make: + + if (!SyncString (XMP_NS_EXIF, + "LensMake", + exif.fLensMake, + readOnly + removable)) + + { + + SyncString (XMP_NS_EXIFEX, + "LensMake", + exif.fLensMake, + readOnly + removable); + + } + + // Lens Serial Number: + + SyncString (XMP_NS_AUX, + "LensSerialNumber", + exif.fLensSerialNumber, + readOnly); + + // Image Number: + + Sync_uint32 (XMP_NS_AUX, + "ImageNumber", + exif.fImageNumber, + exif.fImageNumber == 0xFFFFFFFF, + preferXMP); // CR-4197237: Preserve aux:ImageNumber in XMP when Updating Metadata + + // User Comment: + + if (exif.fUserComment.NotEmpty ()) + { + + fSDK->SetAltLangDefault (XMP_NS_EXIF, + "UserComment", + exif.fUserComment); + + } + + else + { + + (void) fSDK->GetAltLangDefault (XMP_NS_EXIF, + "UserComment", + exif.fUserComment); + + } + + if (removeFromXMP) + { + Remove (XMP_NS_EXIF, "UserComment"); + } + + // Approximate focus distance: + + SyncApproximateFocusDistance (exif, + readOnly); + + // LensDistortInfo: + + { + + dng_string s; + + if (exif.HasLensDistortInfo ()) + { + + char ss [256]; + + sprintf (ss, + "%d/%d %d/%d %d/%d %d/%d", + (int) exif.fLensDistortInfo [0].n, + (int) exif.fLensDistortInfo [0].d, + (int) exif.fLensDistortInfo [1].n, + (int) exif.fLensDistortInfo [1].d, + (int) exif.fLensDistortInfo [2].n, + (int) exif.fLensDistortInfo [2].d, + (int) exif.fLensDistortInfo [3].n, + (int) exif.fLensDistortInfo [3].d); + + s.Set (ss); + + } + + SyncString (XMP_NS_AUX, + "LensDistortInfo", + s, + readOnly); + + if (s.NotEmpty ()) + { + + int n [4]; + int d [4]; + + if (sscanf (s.Get (), + "%d/%d %d/%d %d/%d %d/%d", + &n [0], + &d [0], + &n [1], + &d [1], + &n [2], + &d [2], + &n [3], + &d [3]) == 8) + { + + for (uint32 j = 0; j < 4; j++) + { + + exif.fLensDistortInfo [j] = dng_srational (n [j], d [j]); + + } + + } + + + } + + } + + // Flash Compensation: + + Sync_srational (XMP_NS_AUX, + "FlashCompensation", + exif.fFlashCompensation, + readOnly); + + // Owner Name: (allow XMP updates) + + SyncString (XMP_NS_AUX, + "OwnerName", + exif.fOwnerName, + preferXMP); + + // Firmware: + + SyncString (XMP_NS_AUX, + "Firmware", + exif.fFirmware, + readOnly); + + // Image Unique ID: + + { + + dng_string s = EncodeFingerprint (exif.fImageUniqueID); + + SyncString (XMP_NS_EXIF, + "ImageUniqueID", + s, + readOnly + removable); + + exif.fImageUniqueID = DecodeFingerprint (s); + + } + + // For the following GPS related fields, we offer an option to prefer XMP to + // the EXIF values. This is to allow the host app to modify the XMP for manual + // geo-tagging and overwrite the EXIF values during export. It also allows the user + // to correct the GPS values via changes in a sidecar XMP file, without modifying + // the original GPS data recorded in the raw file by the camera. + + bool preferGPSFromXMP = false; + + uint32 gpsSyncOption = preferNonXMP; + + #if qDNGPreferGPSMetadataFromXMP + preferGPSFromXMP = true; + #endif + + // Allow EXIF GPS to be updated via updates from XMP. + + if (doingUpdateFromXMP || preferGPSFromXMP) + { + + // Require that at least one basic GPS field exist in the + // XMP before overrriding the EXIF GPS fields. + + if (Exists (XMP_NS_EXIF, "GPSVersionID" ) || + Exists (XMP_NS_EXIF, "GPSLatitude" ) || + Exists (XMP_NS_EXIF, "GPSLongitude" ) || + Exists (XMP_NS_EXIF, "GPSAltitude" ) || + Exists (XMP_NS_EXIF, "GPSTimeStamp" ) || + Exists (XMP_NS_EXIF, "GPSProcessingMethod")) + { + + // Clear out the GPS info from the EXIF so it will + // replaced by the GPS info from the XMP. + + dng_exif blankExif; + + exif.CopyGPSFrom (blankExif); + + if (preferGPSFromXMP) + { + + gpsSyncOption = preferXMP; + + } + } + + } + + // GPS Version ID: + + { + + dng_string s = EncodeGPSVersion (exif.fGPSVersionID); + + if (SyncString (XMP_NS_EXIF, + "GPSVersionID", + s, + gpsSyncOption + removable)) + { + + exif.fGPSVersionID = DecodeGPSVersion (s); + + } + + } + + // GPS Latitude: + + { + + dng_string s = EncodeGPSCoordinate (exif.fGPSLatitudeRef, + exif.fGPSLatitude); + + if (SyncString (XMP_NS_EXIF, + "GPSLatitude", + s, + gpsSyncOption + removable)) + { + + DecodeGPSCoordinate (s, + exif.fGPSLatitudeRef, + exif.fGPSLatitude); + + } + + } + + // GPS Longitude: + + { + + dng_string s = EncodeGPSCoordinate (exif.fGPSLongitudeRef, + exif.fGPSLongitude); + + if (SyncString (XMP_NS_EXIF, + "GPSLongitude", + s, + gpsSyncOption + removable)) + { + + DecodeGPSCoordinate (s, + exif.fGPSLongitudeRef, + exif.fGPSLongitude); + + } + + } + + // Handle simple case of incorrectly written GPS altitude where someone didn't understand the GPSAltitudeRef and assumed the GPSAltitude RATIONAL is signed. + // Only handle this case as we do not want to misinterpret e.g. a fixed point representation of very high GPS altitudes. + + uint32 &altitudeRef = exif.fGPSAltitudeRef; + dng_urational &altitude = exif.fGPSAltitude; + + if (altitude.IsValid () && + (altitudeRef == 0 || altitudeRef == 0xFFFFFFFF)) // If the file contains a "below sea level" altitudeRef, assume the writing software is working according to the spec. + { + + if ((altitude.n & (1U << 31)) && + altitude.d < 7) // As the denominator increases, large numerator values become possibly valid distances. Pick a limit on the conservative side (approx 33e6m) to prevent misinterpretation. + // Noting that the normal case for this mistake has a denominator of 1 + { + + altitude.n = ~altitude.n + 1; + altitudeRef = 1; + + } + + } + + // GPS Altitude Reference: + + Sync_uint32 (XMP_NS_EXIF, + "GPSAltitudeRef", + altitudeRef, + altitudeRef == 0xFFFFFFFF, + gpsSyncOption + removable); + + // GPS Altitude: + + Sync_urational (XMP_NS_EXIF, + "GPSAltitude", + altitude, + gpsSyncOption + removable); + + // GPS Date/Time: + + { + + dng_string s = EncodeGPSDateTime (exif.fGPSDateStamp, + exif.fGPSTimeStamp); + + if (SyncString (XMP_NS_EXIF, + "GPSTimeStamp", + s, + preferNonXMP + removable)) + { + + DecodeGPSDateTime (s, + exif.fGPSDateStamp, + exif.fGPSTimeStamp); + + } + + } + + // GPS Satellites: + + SyncString (XMP_NS_EXIF, + "GPSSatellites", + exif.fGPSSatellites, + preferNonXMP + removable); + + // GPS Status: + + SyncString (XMP_NS_EXIF, + "GPSStatus", + exif.fGPSStatus, + preferNonXMP + removable); + + // GPS Measure Mode: + + SyncString (XMP_NS_EXIF, + "GPSMeasureMode", + exif.fGPSMeasureMode, + preferNonXMP + removable); + + // GPS DOP: + + Sync_urational (XMP_NS_EXIF, + "GPSDOP", + exif.fGPSDOP, + preferNonXMP + removable); + + // GPS Speed Reference: + + SyncString (XMP_NS_EXIF, + "GPSSpeedRef", + exif.fGPSSpeedRef, + preferNonXMP + removable); + + // GPS Speed: + + Sync_urational (XMP_NS_EXIF, + "GPSSpeed", + exif.fGPSSpeed, + preferNonXMP + removable); + + // GPS Track Reference: + + SyncString (XMP_NS_EXIF, + "GPSTrackRef", + exif.fGPSTrackRef, + preferNonXMP + removable); + + // GPS Track: + + Sync_urational (XMP_NS_EXIF, + "GPSTrack", + exif.fGPSTrack, + preferNonXMP + removable); + + // GPS Image Direction Reference: + + SyncString (XMP_NS_EXIF, + "GPSImgDirectionRef", + exif.fGPSImgDirectionRef, + preferNonXMP + removable); + + // GPS Image Direction: + + Sync_urational (XMP_NS_EXIF, + "GPSImgDirection", + exif.fGPSImgDirection, + preferNonXMP + removable); + + // GPS Map Datum: + + SyncString (XMP_NS_EXIF, + "GPSMapDatum", + exif.fGPSMapDatum, + preferNonXMP + removable); + + // GPS Destination Latitude: + + { + + dng_string s = EncodeGPSCoordinate (exif.fGPSDestLatitudeRef, + exif.fGPSDestLatitude); + + if (SyncString (XMP_NS_EXIF, + "GPSDestLatitude", + s, + preferNonXMP + removable)) + { + + DecodeGPSCoordinate (s, + exif.fGPSDestLatitudeRef, + exif.fGPSDestLatitude); + + } + + } + + // GPS Destination Longitude: + + { + + dng_string s = EncodeGPSCoordinate (exif.fGPSDestLongitudeRef, + exif.fGPSDestLongitude); + + if (SyncString (XMP_NS_EXIF, + "GPSDestLongitude", + s, + preferNonXMP + removable)) + { + + DecodeGPSCoordinate (s, + exif.fGPSDestLongitudeRef, + exif.fGPSDestLongitude); + + } + + } + + // GPS Destination Bearing Reference: + + SyncString (XMP_NS_EXIF, + "GPSDestBearingRef", + exif.fGPSDestBearingRef, + preferNonXMP + removable); + + // GPS Destination Bearing: + + Sync_urational (XMP_NS_EXIF, + "GPSDestBearing", + exif.fGPSDestBearing, + preferNonXMP + removable); + + // GPS Destination Distance Reference: + + SyncString (XMP_NS_EXIF, + "GPSDestDistanceRef", + exif.fGPSDestDistanceRef, + preferNonXMP + removable); + + // GPS Destination Distance: + + Sync_urational (XMP_NS_EXIF, + "GPSDestDistance", + exif.fGPSDestDistance, + preferNonXMP + removable); + + // GPS Processing Method: + + SyncString (XMP_NS_EXIF, + "GPSProcessingMethod", + exif.fGPSProcessingMethod, + preferNonXMP + removable); + + // GPS Area Information: + + SyncString (XMP_NS_EXIF, + "GPSAreaInformation", + exif.fGPSAreaInformation, + preferNonXMP + removable); + + // GPS Differential: + + Sync_uint32 (XMP_NS_EXIF, + "GPSDifferential", + exif.fGPSDifferential, + exif.fGPSDifferential == 0xFFFFFFFF, + preferNonXMP + removable); + + // GPS Horizontal Positioning Error: + + Sync_urational (XMP_NS_EXIF, + "GPSHPositioningError", + exif.fGPSHPositioningError, + preferNonXMP + removable); + + // Sync date/times. + + UpdateExifDates (exif, removeFromXMP); + + // EXIF 2.3.1 tags. + + Sync_srational (XMP_NS_EXIFEX, + "Temperature", + exif.fTemperature, + readOnly + removable); + + Sync_urational (XMP_NS_EXIFEX, + "Humidity", + exif.fHumidity, + readOnly + removable); + + Sync_urational (XMP_NS_EXIFEX, + "Pressure", + exif.fPressure, + readOnly + removable); + + Sync_srational (XMP_NS_EXIFEX, + "WaterDepth", + exif.fWaterDepth, + readOnly + removable); + + Sync_urational (XMP_NS_EXIFEX, + "Acceleration", + exif.fAcceleration, + readOnly + removable); + + Sync_srational (XMP_NS_EXIFEX, + "CameraElevationAngle", + exif.fCameraElevationAngle, + readOnly + removable); + + // We are syncing EXIF and XMP, but we are not updating the + // NativeDigest tags. It is better to just delete them than leave + // the stale values around. + + Remove (XMP_NS_EXIF, "NativeDigest"); + Remove (XMP_NS_TIFF, "NativeDigest"); + + // Fake EXIF fields. + + SyncAltLangDefault (XMP_NS_DC, + "title", + exif.fTitle, + preferXMP); + + } + +/*****************************************************************************/ + +void dng_xmp::SyncApproximateFocusDistance (dng_exif &exif, + const uint32 readOnly) + { + + Sync_urational (XMP_NS_AUX, + "ApproximateFocusDistance", + exif.fApproxFocusDistance, + readOnly); + + } + +/******************************************************************************/ + +void dng_xmp::ValidateStringList (const char *ns, + const char *path) + { + + fSDK->ValidateStringList (ns, path); + + } + +/******************************************************************************/ + +void dng_xmp::ValidateMetadata () + { + + // The following values should be arrays, but are not always. So + // fix them up because Photoshop sometimes has problems parsing invalid + // tags. + + ValidateStringList (XMP_NS_DC, "creator"); + + ValidateStringList (XMP_NS_PHOTOSHOP, "Keywords"); + ValidateStringList (XMP_NS_PHOTOSHOP, "SupplementalCategories"); + + } + +/******************************************************************************/ + +void dng_xmp::SyncExifDate (const char *ns, + const char *path, + dng_date_time_info &exifDateTime, + bool canRemoveFromXMP, + bool removeFromXMP, + const dng_time_zone &fakeTimeZone) + { + + dng_string s; + + // Find information on XMP side. + + dng_date_time_info xmpDateTime; + + if (GetString (ns, path, s)) + { + + if (s.IsEmpty ()) + { + + // XMP contains an NULL string. Clear EXIF date, + // and remove XMP tag if possible. + + exifDateTime.Clear (); + + if (canRemoveFromXMP && removeFromXMP) + { + Remove (ns, path); + } + + return; + + } + + xmpDateTime.Decode_ISO_8601 (s.Get ()); + + // If the time zone matches the fake time zone, + // ignore it on the XMP side. + + if (fakeTimeZone.IsValid () && + xmpDateTime.TimeZone ().IsValid () && + xmpDateTime.TimeZone ().OffsetMinutes () == fakeTimeZone.OffsetMinutes ()) + { + + xmpDateTime.ClearZone (); + + } + + } + + // If both are valid, we need to resolve. + + if (exifDateTime.IsValid () && xmpDateTime.IsValid ()) + { + + // Kludge: The Nikon D4 is writing date only date/times into XMP, so + // prefer the EXIF values if the XMP only contains a date. + + if (xmpDateTime.IsDateOnly ()) + { + + xmpDateTime = exifDateTime; + + } + + // Kludge: Nikon sometimes writes XMP values without a time zone + // but the EXIF contains a valid time zone. So in that case, + // prefer the EXIF. This case also deals with sidecar files + // created by pre-Exif 2.3.1 aware cr_sdk versions. + + else if (exifDateTime.DateTime () == xmpDateTime.DateTime () && + exifDateTime.TimeZone ().IsValid () && + !xmpDateTime.TimeZone ().IsValid ()) + { + + xmpDateTime = exifDateTime; + + } + + // Else assume that XMP is correct. + + else + { + + exifDateTime = xmpDateTime; + + } + + } + + // Else just pick the valid one. + + else if (xmpDateTime.IsValid ()) + { + + exifDateTime = xmpDateTime; + + } + + else if (exifDateTime.IsValid ()) + { + + xmpDateTime = exifDateTime; + + } + + // Else nothing is valid. + + else + { + + // Remove XMP side, if any. + + Remove (ns, path); + + return; + + } + + // Should we just remove the XMP version? + + if (canRemoveFromXMP && removeFromXMP) + { + + Remove (ns, path); + + } + + else + { + + s = exifDateTime.Encode_ISO_8601 (); + + SetString (ns, path, s); + + } + + } + +/******************************************************************************/ + +void dng_xmp::UpdateExifDates (dng_exif &exif, + bool removeFromXMP) + { + + // Kludge: Early versions of the XMP library did not support date + // encodings without explict time zones, so software had to fill in + // fake time zones on the XMP side. The usual way was to fill in + // local time zone for the date/time at the import location. + // Attempt to detect these cases and ignore the fake time zones. + + dng_time_zone fakeTimeZone; // Initialized to invalid + + if (!exif.AtLeastVersion0231 ()) // Real time zones supported in EXIF 2.3.1 + { + + // Look at DateTimeOriginal since it an EXIF only field (not aliased + // to other fields in XMP) + + dng_string s; + + if (GetString (XMP_NS_EXIF, "DateTimeOriginal", s) && s.NotEmpty ()) + { + + dng_date_time_info xmpDateTimeOriginal; + + xmpDateTimeOriginal.Decode_ISO_8601 (s.Get ()); + + // If this field has a time zone in XMP, it can only + // be fake. + + if (xmpDateTimeOriginal.TimeZone ().IsValid ()) + { + + fakeTimeZone = xmpDateTimeOriginal.TimeZone (); + + } + + } + + } + + // For the following three date/time fields, we generally prefer XMP to + // the EXIF values. This is to allow the user to correct the date/times + // via changes in a sidecar XMP file, without modifying the original + // raw file. + + // Modification Date/Time: + // exif.fDateTime + // kXMP_NS_XMP:"ModifyDate" & kXMP_NS_TIFF:"DateTime" are aliased + + SyncExifDate (XMP_NS_TIFF, + "DateTime", + exif.fDateTime, + false, // Cannot remove because aliased + removeFromXMP, + fakeTimeZone); + + // Original Date/Time: + // exif.fDateTimeOriginal + // IPTC: DateCreated + // XMP_NS_EXIF:"DateTimeOriginal" & XMP_NS_PHOTOSHOP:"DateCreated" + // Adobe has decided to keep the two XMP fields separate. + + { + + SyncExifDate (XMP_NS_EXIF, + "DateTimeOriginal", + exif.fDateTimeOriginal, + true, + removeFromXMP, + fakeTimeZone); + + // Sync the IPTC value to the EXIF value if only the EXIF + // value exists. + + if (exif.fDateTimeOriginal.IsValid ()) + { + + // See if the fake time zone was cloned into DateCreated + // field. + + bool forceUpdate = false; + + if (fakeTimeZone.IsValid ()) + { + + dng_string s; + + if (GetString (XMP_NS_PHOTOSHOP, "DateCreated", s) && s.NotEmpty ()) + { + + dng_date_time_info info; + + info.Decode_ISO_8601 (s.Get ()); + + if (info.DateTime () == exif.fDateTimeOriginal.DateTime ()) + { + + forceUpdate = true; + + } + + } + + } + + if (!Exists (XMP_NS_PHOTOSHOP, "DateCreated") || forceUpdate) + { + + dng_string s = exif.fDateTimeOriginal.Encode_ISO_8601 (); + + SetString (XMP_NS_PHOTOSHOP, "DateCreated", s); + + } + + } + + } + + // Date Time Digitized: + // XMP_NS_EXIF:"DateTimeDigitized" & kXMP_NS_XMP:"CreateDate" are aliased + + SyncExifDate (XMP_NS_EXIF, + "DateTimeDigitized", + exif.fDateTimeDigitized, + false, // Cannot remove because aliased + removeFromXMP, + fakeTimeZone); + + } + +/******************************************************************************/ + +void dng_xmp::UpdateDateTime (const dng_date_time_info &dt) + { + + dng_string s = dt.Encode_ISO_8601 (); + + SetString (XMP_NS_TIFF, + "DateTime", + s); + + } + +/******************************************************************************/ + +void dng_xmp::UpdateMetadataDate (const dng_date_time_info &dt) + { + + dng_string s = dt.Encode_ISO_8601 (); + + SetString (XMP_NS_XAP, + "MetadataDate", + s); + + } + +/*****************************************************************************/ + +bool dng_xmp::HasOrientation () const + { + + uint32 x = 0; + + if (Get_uint32 (XMP_NS_TIFF, + "Orientation", + x)) + { + + return (x >= 1) && (x <= 8); + + } + + return false; + + } + +/*****************************************************************************/ + +dng_orientation dng_xmp::GetOrientation () const + { + + dng_orientation result; + + uint32 x = 0; + + if (Get_uint32 (XMP_NS_TIFF, + "Orientation", + x)) + { + + if ((x >= 1) && (x <= 8)) + { + + result.SetTIFF (x); + + } + + } + + return result; + + } + +/******************************************************************************/ + +void dng_xmp::ClearOrientation () + { + + fSDK->Remove (XMP_NS_TIFF, "Orientation"); + + } + +/******************************************************************************/ + +void dng_xmp::SetOrientation (const dng_orientation &orientation) + { + + Set_uint32 (XMP_NS_TIFF, + "Orientation", + orientation.GetTIFF ()); + + } + +/*****************************************************************************/ + +void dng_xmp::SyncOrientation (dng_negative &negative, + bool xmpIsMaster) + { + + SyncOrientation (negative.Metadata (), xmpIsMaster); + + } + +/*****************************************************************************/ + +void dng_xmp::SyncOrientation (dng_metadata &metadata, + bool xmpIsMaster) + { + + // See if XMP contains the orientation. + + bool xmpHasOrientation = HasOrientation (); + + // See if XMP is the master value. + + if (xmpHasOrientation && (xmpIsMaster || !metadata.HasBaseOrientation ())) + { + + metadata.SetBaseOrientation (GetOrientation ()); + + } + + else + { + + SetOrientation (metadata.BaseOrientation ()); + + } + + } + +/******************************************************************************/ + +void dng_xmp::ClearImageInfo () + { + + Remove (XMP_NS_TIFF, "ImageWidth" ); + Remove (XMP_NS_TIFF, "ImageLength"); + + Remove (XMP_NS_EXIF, "PixelXDimension"); + Remove (XMP_NS_EXIF, "PixelYDimension"); + + Remove (XMP_NS_TIFF, "BitsPerSample"); + + Remove (XMP_NS_TIFF, "Compression"); + + Remove (XMP_NS_TIFF, "PhotometricInterpretation"); + + // "Orientation" is handled separately. + + Remove (XMP_NS_TIFF, "SamplesPerPixel"); + + Remove (XMP_NS_TIFF, "PlanarConfiguration"); + + Remove (XMP_NS_TIFF, "XResolution"); + Remove (XMP_NS_TIFF, "YResolution"); + + Remove (XMP_NS_TIFF, "ResolutionUnit"); + + Remove (XMP_NS_PHOTOSHOP, "ColorMode" ); + Remove (XMP_NS_PHOTOSHOP, "ICCProfile"); + + } + +/******************************************************************************/ + +void dng_xmp::SetImageSize (const dng_point &size) + { + + Set_uint32 (XMP_NS_TIFF, "ImageWidth" , size.h); + Set_uint32 (XMP_NS_TIFF, "ImageLength", size.v); + + // Mirror these values to the EXIF tags. + + Set_uint32 (XMP_NS_EXIF, "PixelXDimension" , size.h); + Set_uint32 (XMP_NS_EXIF, "PixelYDimension" , size.v); + + } + +/******************************************************************************/ + +void dng_xmp::SetSampleInfo (uint32 samplesPerPixel, + uint32 bitsPerSample) + { + + Set_uint32 (XMP_NS_TIFF, "SamplesPerPixel", samplesPerPixel); + + char s [32]; + + sprintf (s, "%u", (unsigned) bitsPerSample); + + dng_string ss; + + ss.Set (s); + + dng_string_list list; + + for (uint32 j = 0; j < samplesPerPixel; j++) + { + list.Append (ss); + } + + SetStringList (XMP_NS_TIFF, "BitsPerSample", list, false); + + } + +/******************************************************************************/ + +void dng_xmp::SetPhotometricInterpretation (uint32 pi) + { + + Set_uint32 (XMP_NS_TIFF, "PhotometricInterpretation", pi); + + } + +/******************************************************************************/ + +void dng_xmp::SetResolution (const dng_resolution &res) + { + + Set_urational (XMP_NS_TIFF, "XResolution", res.fXResolution); + Set_urational (XMP_NS_TIFF, "YResolution", res.fYResolution); + + Set_uint32 (XMP_NS_TIFF, "ResolutionUnit", res.fResolutionUnit); + + } + +/*****************************************************************************/ + +void dng_xmp::ComposeArrayItemPath (const char *ns, + const char *arrayName, + int32 itemNumber, + dng_string &s) const + { + + fSDK->ComposeArrayItemPath (ns, arrayName, itemNumber, s); + + } + +/*****************************************************************************/ + +void dng_xmp::ComposeStructFieldPath (const char *ns, + const char *structName, + const char *fieldNS, + const char *fieldName, + dng_string &s) const + { + + fSDK->ComposeStructFieldPath (ns, structName, fieldNS, fieldName, s); + + } + +/*****************************************************************************/ + +int32 dng_xmp::CountArrayItems (const char *ns, + const char *path) const + { + + return fSDK->CountArrayItems (ns, path); + + } + +/*****************************************************************************/ + +void dng_xmp::AppendArrayItem (const char *ns, + const char *arrayName, + const char *itemValue, + bool isBag, + bool propIsStruct) + { + + fSDK->AppendArrayItem (ns, + arrayName, + itemValue, + isBag, + propIsStruct); + } + +/*****************************************************************************/ + +#if qDNGXMPDocOps + +/*****************************************************************************/ + +void dng_xmp::DocOpsOpenXMP (const char *srcMIME) + { + + fSDK->DocOpsOpenXMP (srcMIME); + + } + +/*****************************************************************************/ + +void dng_xmp::DocOpsPrepareForSave (const char *srcMIME, + const char *dstMIME, + bool newPath) + { + + fSDK->DocOpsPrepareForSave (srcMIME, + dstMIME, + newPath); + + } + +/*****************************************************************************/ + +void dng_xmp::DocOpsUpdateMetadata (const char *srcMIME) + { + + fSDK->DocOpsUpdateMetadata (srcMIME); + + } + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_xmp.h b/dng/dng_xmp.h new file mode 100644 index 0000000..35a39aa --- /dev/null +++ b/dng/dng_xmp.h @@ -0,0 +1,425 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef __dng_xmp__ +#define __dng_xmp__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_types.h" +#include "dng_xmp_sdk.h" + +/*****************************************************************************/ + +class dng_xmp + { + + protected: + + // Sync option bits. + + enum + { + ignoreXMP = 1, // Force XMP values to match non-XMP + preferXMP = 2, // Prefer XMP values if conflict + preferNonXMP = 4, // Prefer non-XMP values if conflict + removeXMP = 8 // Remove XMP value after syncing + }; + + dng_memory_allocator &fAllocator; + + dng_xmp_sdk *fSDK; + + public: + + dng_xmp (dng_memory_allocator &allocator); + + dng_xmp (const dng_xmp &xmp); + + virtual ~dng_xmp (); + + virtual dng_xmp * Clone () const; + + dng_memory_allocator & Allocator () const + { + return fAllocator; + } + + void Parse (dng_host &host, + const void *buffer, + uint32 count); + + dng_memory_block * Serialize (bool asPacket = false, + uint32 targetBytes = 0, + uint32 padBytes = 4096, + bool forJPEG = false, + bool compact = true) const; + + // Kludge: Due to a bug in Premere Elements 9, we need to pass non-compact XMP + // to the host, until we drop support for this Premere version. This bug + // is fixed in Premere Elements 10 and later. + + dng_memory_block * SerializeNonCompact () const + { + return Serialize (false, + 0, + 4096, + false, + false); + } + + void PackageForJPEG (AutoPtr &stdBlock, + AutoPtr &extBlock, + dng_string &extDigest) const; + + void MergeFromJPEG (const dng_xmp &xmp); + + bool HasMeta () const; + + void RequireMeta () + { + fSDK->RequireMeta (); + } + + void * GetPrivateMeta (); + + bool Exists (const char *ns, + const char *path) const; + + bool HasNameSpace (const char *ns) const; + + bool IteratePaths (IteratePathsCallback *callback, + void *callbackData, + const char *ns = 0, + const char *path = 0); + + void Remove (const char *ns, + const char *path); + + void RemoveProperties (const char *ns); + + void RemoveEmptyStringOrArray (const char *ns, + const char *path); + + void RemoveEmptyStringsAndArrays (const char *ns = 0); + + void Set (const char *ns, + const char *path, + const char *text); + + bool GetString (const char *ns, + const char *path, + dng_string &s) const; + + void SetString (const char *ns, + const char *path, + const dng_string &s); + + bool GetStringList (const char *ns, + const char *path, + dng_string_list &list) const; + + void SetStringList (const char *ns, + const char *path, + const dng_string_list &list, + bool isBag = false); + + void SetStructField (const char *ns, + const char *path, + const char *fieldNS, + const char *fieldName, + const dng_string &s); + + void SetStructField (const char *ns, + const char *path, + const char *fieldNS, + const char *fieldName, + const char *s); + + void DeleteStructField (const char *ns, + const char *path, + const char *fieldNS, + const char *fieldName); + + bool GetStructField (const char *ns, + const char *path, + const char *fieldNS, + const char *fieldName, + dng_string &s) const; + + void SetAltLangDefault (const char *ns, + const char *path, + const dng_string &s); + + void SetLocalString (const char *ns, + const char *path, + const dng_local_string &s); + + bool GetAltLangDefault (const char *ns, + const char *path, + dng_string &s, + bool silent = false) const; + + bool GetLocalString (const char *ns, + const char *path, + dng_local_string &s) const; + + bool GetBoolean (const char *ns, + const char *path, + bool &x) const; + + void SetBoolean (const char *ns, + const char *path, + bool x); + + bool Get_int32 (const char *ns, + const char *path, + int32 &x) const; + + void Set_int32 (const char *ns, + const char *path, + int32 x, + bool usePlus = false); + + bool Get_uint32 (const char *ns, + const char *path, + uint32 &x) const; + + void Set_uint32 (const char *ns, + const char *path, + uint32 x); + + bool Get_real64 (const char *ns, + const char *path, + real64 &x) const; + + void Set_real64 (const char *ns, + const char *path, + real64 x, + uint32 places = 6, + bool trim = true, + bool usePlus = false); + + bool Get_urational (const char *ns, + const char *path, + dng_urational &r) const; + + void Set_urational (const char *ns, + const char *path, + const dng_urational &r); + + bool Get_srational (const char *ns, + const char *path, + dng_srational &r) const; + + void Set_srational (const char *ns, + const char *path, + const dng_srational &r); + + bool GetFingerprint (const char *ns, + const char *path, + dng_fingerprint &print) const; + + void SetFingerprint (const char *ns, + const char *path, + const dng_fingerprint &print, + bool allowInvalid = false); + + void SetVersion2to4 (const char *ns, + const char *path, + uint32 version); + + dng_fingerprint GetIPTCDigest () const; + + void SetIPTCDigest (dng_fingerprint &digest); + + void ClearIPTCDigest (); + + void IngestIPTC (dng_metadata &metadata, + bool xmpIsNewer = false); + + void RebuildIPTC (dng_metadata &metadata, + dng_memory_allocator &allocator, + bool padForTIFF); + + virtual void SyncExif (dng_exif &exif, + const dng_exif *originalExif = NULL, + bool doingUpdateFromXMP = false, + bool removeFromXMP = false); + + void ValidateStringList (const char *ns, + const char *path); + + void ValidateMetadata (); + + void UpdateDateTime (const dng_date_time_info &dt); + + void UpdateMetadataDate (const dng_date_time_info &dt); + + void UpdateExifDates (dng_exif &exif, + bool removeFromXMP = false); + + bool HasOrientation () const; + + dng_orientation GetOrientation () const; + + void ClearOrientation (); + + void SetOrientation (const dng_orientation &orientation); + + void SyncOrientation (dng_negative &negative, + bool xmpIsMaster); + // FIX_ME_API: Backwards compatibility + + void SyncOrientation (dng_metadata &metadata, + bool xmpIsMaster); + + void ClearImageInfo (); + + void SetImageSize (const dng_point &size); + + void SetSampleInfo (uint32 samplesPerPixel, + uint32 bitsPerSample); + + void SetPhotometricInterpretation (uint32 pi); + + void SetResolution (const dng_resolution &res); + + void ComposeArrayItemPath (const char *ns, + const char *arrayName, + int32 itemNumber, + dng_string &s) const; + + void ComposeStructFieldPath (const char *ns, + const char *structName, + const char *fieldNS, + const char *fieldName, + dng_string &s) const; + + int32 CountArrayItems (const char *ns, + const char *path) const; + + void AppendArrayItem (const char *ns, + const char *arrayName, + const char *itemValue, + bool isBag = true, + bool propIsStruct = false); + + static dng_string EncodeFingerprint (const dng_fingerprint &f, + bool allowInvalid = false); + + static dng_fingerprint DecodeFingerprint (const dng_string &s); + + #if qDNGXMPDocOps + + void DocOpsOpenXMP (const char *srcMIME); + + void DocOpsPrepareForSave (const char *srcMIME, + const char *dstMIME, + bool newPath = true); + + void DocOpsUpdateMetadata (const char *srcMIME); + + #endif + + protected: + + static void TrimDecimal (char *s); + + static dng_string EncodeGPSVersion (uint32 version); + + static uint32 DecodeGPSVersion (const dng_string &s); + + static dng_string EncodeGPSCoordinate (const dng_string &ref, + const dng_urational *coord); + + static void DecodeGPSCoordinate (const dng_string &s, + dng_string &ref, + dng_urational *coord); + + static dng_string EncodeGPSDateTime (const dng_string &dateStamp, + const dng_urational *timeStamp); + + static void DecodeGPSDateTime (const dng_string &s, + dng_string &dateStamp, + dng_urational *timeStamp); + + bool SyncString (const char *ns, + const char *path, + dng_string &s, + uint32 options = 0); + + void SyncStringList (const char *ns, + const char *path, + dng_string_list &list, + bool isBag = false, + uint32 options = 0); + + bool SyncAltLangDefault (const char *ns, + const char *path, + dng_string &s, + uint32 options = 0); + + void Sync_uint32 (const char *ns, + const char *path, + uint32 &x, + bool isDefault = false, + uint32 options = 0); + + void Sync_uint32_array (const char *ns, + const char *path, + uint32 *data, + uint32 &count, + uint32 maxCount, + uint32 options = 0); + + void Sync_urational (const char *ns, + const char *path, + dng_urational &r, + uint32 options = 0); + + void Sync_srational (const char *ns, + const char *path, + dng_srational &r, + uint32 options = 0); + + void SyncIPTC (dng_iptc &iptc, + uint32 options); + + void SyncFlash (uint32 &flashState, + uint32 &flashMask, + uint32 options); + + void SyncExifDate (const char *ns, + const char *path, + dng_date_time_info &exifDateTime, + bool canRemoveFromXMP, + bool removeFromXMP, + const dng_time_zone &fakeTimeZone); + + virtual void SyncApproximateFocusDistance (dng_exif &exif, + const uint32 readOnly); + + virtual void SyncLensName (dng_exif &exif); + + virtual void GenerateDefaultLensName (dng_exif &exif); + + private: + + // Hidden assignment operator. + + dng_xmp & operator= (const dng_xmp &xmp); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_xmp_sdk.cpp b/dng/dng_xmp_sdk.cpp new file mode 100644 index 0000000..c31795f --- /dev/null +++ b/dng/dng_xmp_sdk.cpp @@ -0,0 +1,1846 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_xmp_sdk.h" + +#include "dng_auto_ptr.h" +#include "dng_assertions.h" +#include "dng_exceptions.h" +#include "dng_flags.h" +#include "dng_host.h" +#include "dng_local_string.h" +#include "dng_memory.h" +#include "dng_string.h" +#include "dng_string_list.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +#if qMacOS +#ifndef MAC_ENV +#define MAC_ENV 1 +#endif +#endif + +#if qWinOS +#ifndef WIN_ENV +#define WIN_ENV 1 +#endif +#endif + +#include +#include + +#define TXMP_STRING_TYPE std::string + +#define XMP_INCLUDE_XMPFILES qDNGXMPFiles + +#define XMP_StaticBuild 1 + +#if qiPhone +#undef UNIX_ENV +#endif + +#include "XMP.incl_cpp" + +/*****************************************************************************/ + +const char *XMP_NS_TIFF = "http://ns.adobe.com/tiff/1.0/"; +const char *XMP_NS_EXIF = "http://ns.adobe.com/exif/1.0/"; +const char *XMP_NS_EXIFEX = "http://cipa.jp/exif/1.0/"; +const char *XMP_NS_PHOTOSHOP = "http://ns.adobe.com/photoshop/1.0/"; +const char *XMP_NS_XAP = "http://ns.adobe.com/xap/1.0/"; +const char *XMP_NS_XAP_RIGHTS = "http://ns.adobe.com/xap/1.0/rights/"; +const char *XMP_NS_DC = "http://purl.org/dc/elements/1.1/"; +const char *XMP_NS_XMP_NOTE = "http://ns.adobe.com/xmp/note/"; +const char *XMP_NS_MM = "http://ns.adobe.com/xap/1.0/mm/"; + +const char *XMP_NS_CRS = "http://ns.adobe.com/camera-raw-settings/1.0/"; +const char *XMP_NS_CRSS = "http://ns.adobe.com/camera-raw-saved-settings/1.0/"; +const char *XMP_NS_CRD = "http://ns.adobe.com/camera-raw-defaults/1.0/"; + +const char *XMP_NS_LCP = "http://ns.adobe.com/photoshop/1.0/camera-profile"; + +const char *XMP_NS_AUX = "http://ns.adobe.com/exif/1.0/aux/"; + +const char *XMP_NS_IPTC = "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/"; +const char *XMP_NS_IPTC_EXT = "http://iptc.org/std/Iptc4xmpExt/2008-02-29/"; + +const char *XMP_NS_CRX = "http://ns.adobe.com/lightroom-settings-experimental/1.0/"; + +const char *XMP_NS_DNG = "http://ns.adobe.com/dng/1.0/"; + +const char *XMP_NS_PANO = "http://ns.adobe.com/photoshop/1.0/panorama-profile"; + +/******************************************************************************/ + +#define CATCH_XMP_ALT(routine, fatal, silent)\ + \ + catch (std::bad_alloc &)\ + {\ + DNG_REPORT ("Info: XMP " routine " threw memory exception");\ + ThrowMemoryFull ();\ + }\ + \ + catch (XMP_Error &error)\ + {\ + const char *errMessage = error.GetErrMsg ();\ + if (errMessage && strlen (errMessage) <= 128)\ + {\ + if (!silent)\ + {\ + char errBuffer [256];\ + sprintf (errBuffer, "Info: XMP " routine " threw '%s' exception", errMessage);\ + DNG_REPORT (errBuffer);\ + }\ + }\ + else\ + {\ + DNG_REPORT ("Info: XMP " routine " threw unnamed exception");\ + }\ + if (fatal) ThrowProgramError ();\ + }\ + \ + catch (...)\ + {\ + DNG_REPORT ("Info: XMP " routine " threw unknown exception");\ + if (fatal) ThrowProgramError ();\ + } + +#define CATCH_XMP(routine, fatal) CATCH_XMP_ALT(routine, fatal, false) + +/*****************************************************************************/ + +class dng_xmp_private + { + + public: + + SXMPMeta *fMeta; + + dng_xmp_private () + : fMeta (NULL) + { + } + + dng_xmp_private (const dng_xmp_private &xmp); + + ~dng_xmp_private () + { + if (fMeta) + { + delete fMeta; + } + } + + private: + + // Hidden assignment operator. + + dng_xmp_private & operator= (const dng_xmp_private &xmp); + + }; + +/*****************************************************************************/ + +dng_xmp_private::dng_xmp_private (const dng_xmp_private &xmp) + + : fMeta (NULL) + + { + + if (xmp.fMeta) + { + + fMeta = new SXMPMeta (xmp.fMeta->Clone (0)); + + if (!fMeta) + { + ThrowMemoryFull (); + } + + } + + } + +/*****************************************************************************/ + +dng_xmp_sdk::dng_xmp_sdk () + + : fPrivate (NULL) + + { + + fPrivate = new dng_xmp_private; + + if (!fPrivate) + { + ThrowMemoryFull (); + } + + } + +/*****************************************************************************/ + +dng_xmp_sdk::dng_xmp_sdk (const dng_xmp_sdk &sdk) + + : fPrivate (NULL) + + { + + fPrivate = new dng_xmp_private (*sdk.fPrivate); + + if (!fPrivate) + { + ThrowMemoryFull (); + } + + } + +/*****************************************************************************/ + +dng_xmp_sdk::~dng_xmp_sdk () + { + + if (fPrivate) + { + delete fPrivate; + } + + } + +/*****************************************************************************/ + +static bool gInitializedXMP = false; + +/*****************************************************************************/ + +void dng_xmp_sdk::InitializeSDK (dng_xmp_namespace * extraNamespaces, + const char *software) + { + + if (!gInitializedXMP) + { + + try + { + + if (!SXMPMeta::Initialize ()) + { + ThrowProgramError (); + } + + // Register Lightroom beta settings namespace. + // We no longer read this, but we do need to register + // it so we can clean up this namespace when saving + // new settings. + + { + + TXMP_STRING_TYPE ss; + + SXMPMeta::RegisterNamespace (XMP_NS_CRX, + "crx", + &ss); + + } + + // Register CRSS snapshots namespace + + { + + TXMP_STRING_TYPE ss; + + SXMPMeta::RegisterNamespace (XMP_NS_CRSS, + "crss", + &ss); + + } + + // Register CRD defaults namespace + + { + + TXMP_STRING_TYPE ss; + + SXMPMeta::RegisterNamespace (XMP_NS_CRD, + "crd", + &ss); + + } + + // Register LCP (lens correction profiles) namespace + + { + + TXMP_STRING_TYPE ss; + + SXMPMeta::RegisterNamespace (XMP_NS_LCP, + "stCamera", + &ss); + + } + + // Register DNG format metadata namespace + + { + + TXMP_STRING_TYPE ss; + + SXMPMeta::RegisterNamespace (XMP_NS_DNG, + "dng", + &ss); + + } + + // Register panorama metadata namespace + + { + + TXMP_STRING_TYPE ss; + + SXMPMeta::RegisterNamespace (XMP_NS_PANO, + "panorama", + &ss); + + } + + // Register extra namespaces. + + if (extraNamespaces != NULL) + { + + for (; extraNamespaces->fullName != NULL; ++extraNamespaces) + { + + TXMP_STRING_TYPE ss; + + SXMPMeta::RegisterNamespace (extraNamespaces->fullName, + extraNamespaces->shortName, + &ss); + + } + + } + + #if qDNGXMPFiles + + #if qLinux || qAndroid + if (!SXMPFiles::Initialize (kXMPFiles_IgnoreLocalText)) + #else + if (!SXMPFiles::Initialize ()) + #endif + { + ThrowProgramError (); + } + + #endif + + #if qDNGXMPDocOps + + if (software) + { + + SXMPDocOps::SetAppName (software); + + } + + #else + + (void) software; + + #endif + + } + + CATCH_XMP ("Initialization", true) + + gInitializedXMP = true; + + } + + } + +/******************************************************************************/ + +void dng_xmp_sdk::TerminateSDK () + { + + if (gInitializedXMP) + { + + try + { + + #if qDNGXMPFiles + + SXMPFiles::Terminate (); + + #endif + + SXMPMeta::Terminate (); + + } + + catch (...) + { + + } + + gInitializedXMP = false; + + } + + } + +/******************************************************************************/ + +bool dng_xmp_sdk::HasMeta () const + { + + if (fPrivate->fMeta) + { + + return true; + + } + + return false; + + } + +/******************************************************************************/ + +void dng_xmp_sdk::ClearMeta () + { + + if (HasMeta ()) + { + + delete fPrivate->fMeta; + + fPrivate->fMeta = NULL; + + } + + } + +/******************************************************************************/ + +void dng_xmp_sdk::MakeMeta () + { + + ClearMeta (); + + InitializeSDK (); + + try + { + + fPrivate->fMeta = new SXMPMeta; + + if (!fPrivate->fMeta) + { + + ThrowMemoryFull (); + + } + + } + + CATCH_XMP ("MakeMeta", true) + + } + +/******************************************************************************/ + +void dng_xmp_sdk::NeedMeta () + { + + if (!HasMeta ()) + { + + MakeMeta (); + + } + + } + +/******************************************************************************/ + +void * dng_xmp_sdk::GetPrivateMeta () + { + + NeedMeta (); + + return (void *) fPrivate->fMeta; + + } + +/******************************************************************************/ + +void dng_xmp_sdk::Parse (dng_host &host, + const char *buffer, + uint32 count) + { + + MakeMeta (); + + try + { + + try + { + + fPrivate->fMeta->ParseFromBuffer (buffer, count); + + } + + CATCH_XMP ("ParseFromBuffer", true) + + } + + catch (dng_exception &except) + { + + ClearMeta (); + + if (host.IsTransientError (except.ErrorCode ())) + { + + throw; + + } + + ThrowBadFormat (); + + } + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::AppendArrayItem (const char *ns, + const char *arrayName, + const char *itemValue, + bool isBag, + bool propIsStruct) + { + + NeedMeta(); + + try + { + + fPrivate->fMeta->AppendArrayItem (ns, + arrayName, + isBag ? kXMP_PropValueIsArray + : kXMP_PropArrayIsOrdered, + itemValue, + propIsStruct ? kXMP_PropValueIsStruct + : 0); + + } + CATCH_XMP ("AppendArrayItem", true ) + + } + +/*****************************************************************************/ + +int32 dng_xmp_sdk::CountArrayItems (const char *ns, + const char *path) const + { + + if (HasMeta ()) + { + + try + { + + return fPrivate->fMeta->CountArrayItems (ns, path); + + } + + CATCH_XMP ("CountArrayItems", false) + + } + + return 0; + + } + +/*****************************************************************************/ + +bool dng_xmp_sdk::Exists (const char *ns, + const char *path) const + { + + if (HasMeta ()) + { + + try + { + + return fPrivate->fMeta->DoesPropertyExist (ns, path); + + } + + catch (...) + { + + // Does not exist... + + } + + } + + return false; + + } + +/*****************************************************************************/ + +bool dng_xmp_sdk::HasNameSpace (const char *ns) const + { + + bool result = false; + + if (HasMeta ()) + { + + try + { + + SXMPIterator iter (*fPrivate->fMeta, ns); + + TXMP_STRING_TYPE nsTemp; + TXMP_STRING_TYPE prop; + + if (iter.Next (&nsTemp, + &prop, + NULL, + NULL)) + { + + result = true; + + } + + } + + CATCH_XMP ("HasNameSpace", true) + + } + + return result; + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::Remove (const char *ns, + const char *path) + { + + if (HasMeta ()) + { + + try + { + + fPrivate->fMeta->DeleteProperty (ns, path); + + } + + CATCH_XMP ("DeleteProperty", false) + + } + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::RemoveProperties (const char *ns) + { + + if (HasMeta ()) + { + + try + { + + SXMPUtils::RemoveProperties (fPrivate->fMeta, + ns, + NULL, + kXMPUtil_DoAllProperties); + + } + + catch (...) + { + + } + + } + + } + +/*****************************************************************************/ + +bool dng_xmp_sdk::IsEmptyString (const char *ns, + const char *path) + { + + if (HasMeta ()) + { + + try + { + + TXMP_STRING_TYPE ss; + + XMP_OptionBits options = 0; + + if (fPrivate->fMeta->GetProperty (ns, + path, + &ss, + &options)) + { + + // Item must be simple. + + if (XMP_PropIsSimple (options)) + { + + // Check for null strings. + + return (ss.c_str () == 0 || + ss.c_str () [0] == 0); + + } + + } + + } + + CATCH_XMP ("IsEmptyString", false) + + } + + return false; + + } + +/*****************************************************************************/ + +bool dng_xmp_sdk::IsEmptyArray (const char *ns, + const char *path) + { + + if (HasMeta ()) + { + + try + { + + TXMP_STRING_TYPE ss; + + XMP_OptionBits options = 0; + + if (fPrivate->fMeta->GetProperty (ns, + path, + &ss, + &options)) + { + + if (XMP_PropIsArray (options)) + { + + if (fPrivate->fMeta->GetArrayItem (ns, + path, + 1, + &ss, + &options)) + { + + // If the first item is a null string... + + if (XMP_PropIsSimple (options)) + { + + if ((ss.c_str () == 0 || + ss.c_str () [0] == 0)) + { + + // And there is no second item. + + if (!fPrivate->fMeta->GetArrayItem (ns, + path, + 2, + &ss, + &options)) + { + + // Then we have an empty array. + + return true; + + } + + } + + } + + } + + else + { + + // Unable to get first item, so array is empty. + + return true; + + } + + } + + } + + } + + CATCH_XMP ("IsEmptyArray", false) + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::ComposeArrayItemPath (const char *ns, + const char *arrayName, + int32 index, + dng_string &s) const + { + + try + { + + std::string ss; + + SXMPUtils::ComposeArrayItemPath (ns, arrayName, index, &ss); + + s.Set (ss.c_str ()); + + return; + + } + + CATCH_XMP ("ComposeArrayItemPath", true) + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::ComposeStructFieldPath (const char *ns, + const char *structName, + const char *fieldNS, + const char *fieldName, + dng_string &s) const + { + + try + { + + std::string ss; + + SXMPUtils::ComposeStructFieldPath (ns, + structName, + fieldNS, + fieldName, + &ss); + + s.Set (ss.c_str ()); + + return; + + } + + CATCH_XMP ("ComposeStructFieldPath", true) + + } + +/*****************************************************************************/ + +bool dng_xmp_sdk::GetNamespacePrefix (const char *uri, + dng_string &s) const + { + + bool result = false; + + if (HasMeta ()) + { + + try + { + + std::string ss; + + fPrivate->fMeta->GetNamespacePrefix (uri, &ss); + + s.Set (ss.c_str ()); + + result = true; + + } + + CATCH_XMP ("GetNamespacePrefix", false) + + } + + return result; + + } + +/*****************************************************************************/ + +bool dng_xmp_sdk::GetString (const char *ns, + const char *path, + dng_string &s) const + { + + bool result = false; + + if (HasMeta ()) + { + + try + { + + TXMP_STRING_TYPE ss; + + if (fPrivate->fMeta->GetProperty (ns, path, &ss, NULL)) + { + + s.Set (ss.c_str ()); + + result = true; + + } + + } + + CATCH_XMP ("GetProperty", false) + + } + + return result; + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::ValidateStringList (const char *ns, + const char *path) + { + + if (Exists (ns, path)) + { + + bool bogus = true; + + try + { + + XMP_Index index = 1; + + TXMP_STRING_TYPE ss; + + while (fPrivate->fMeta->GetArrayItem (ns, + path, + index++, + &ss, + NULL)) + { + + } + + bogus = false; + + } + + catch (...) + { + + // Array is probably bogus. Don't need to report. + + } + + if (bogus) + { + + Remove (ns, path); + + } + + } + + } + +/*****************************************************************************/ + +bool dng_xmp_sdk::GetStringList (const char *ns, + const char *path, + dng_string_list &list) const + { + + bool result = false; + + if (HasMeta ()) + { + + try + { + + XMP_Index index = 1; + + TXMP_STRING_TYPE ss; + + while (fPrivate->fMeta->GetArrayItem (ns, + path, + index++, + &ss, + NULL)) + { + + dng_string s; + + s.Set (ss.c_str ()); + + list.Append (s); + + result = true; + + } + + } + + CATCH_XMP ("GetStringList", false) + + } + + return result; + + } + +/*****************************************************************************/ + +bool dng_xmp_sdk::GetAltLangDefault (const char *ns, + const char *path, + dng_string &s, + bool silent) const + { + + bool result = false; + + if (HasMeta ()) + { + + try + { + + TXMP_STRING_TYPE ss; + + if (fPrivate->fMeta->GetLocalizedText (ns, + path, + "x-default", + "x-default", + NULL, + &ss, + NULL)) + { + + s.Set (ss.c_str ()); + + result = true; + + } + + // Special Case: treat the following two representation equivalently. + // The first is an empty alt lang array; the second is an array with + // an empty item. It seems that xmp lib could be generating both under + // some circumstances! + // + // + // + // + // + // and + // + // + // + // + // + // + + else if (fPrivate->fMeta->GetProperty (ns, + path, + &ss, + NULL)) + { + + if (ss.empty ()) + { + + s.Clear (); + + result = true; + + } + + } + + } + + CATCH_XMP_ALT ("GetLocalizedText", false, silent) + + } + + return result; + + } + +/*****************************************************************************/ + +bool dng_xmp_sdk::GetLocalString (const char *ns, + const char *path, + dng_local_string &s) const + { + + dng_string defaultText; + + if (GetAltLangDefault (ns, path, defaultText, true)) + { + + s.SetDefaultText (defaultText); + + try + { + + int32 count = CountArrayItems (ns, path); + + if (count > 1) + { + + for (int32 index = 1; index <= count; index++) + { + + dng_string arrayItemPath; + + ComposeArrayItemPath (ns, + path, + index + 1, + arrayItemPath); + + TXMP_STRING_TYPE langS; + + if (fPrivate->fMeta->GetQualifier (ns, + arrayItemPath.Get (), + kXMP_NS_XML, + "lang", + &langS, + NULL)) + { + + dng_string language; + + language.Set (langS.c_str ()); + + if (language.IsEmpty () || + language.Matches ("x-default")) + { + continue; + } + + TXMP_STRING_TYPE tranS; + + if (fPrivate->fMeta->GetProperty (ns, + arrayItemPath.Get (), + &tranS, + NULL)) + { + + dng_string translation; + + translation.Set (tranS.c_str ()); + + s.AddTranslation (language, + translation); + + } + + } + + } + + } + + } + + CATCH_XMP ("GetLocalString", false) + + return true; + + } + + return false; + + } + +/*****************************************************************************/ + +bool dng_xmp_sdk::GetStructField (const char *ns, + const char *path, + const char *fieldNS, + const char *fieldName, + dng_string &s) const + { + + bool result = false; + + if (HasMeta ()) + { + + try + { + + TXMP_STRING_TYPE ss; + + if (fPrivate->fMeta->GetStructField (ns, + path, + fieldNS, + fieldName, + &ss, + NULL)) + { + + s.Set (ss.c_str ()); + + result = true; + + } + + } + + CATCH_XMP ("GetStructField", false) + + } + + return result; + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::Set (const char *ns, + const char *path, + const char *text) + { + + NeedMeta (); + + try + { + + fPrivate->fMeta->SetProperty (ns, path, text); + + return; + + } + + catch (...) + { + + // Failed for some reason. + + } + + // Remove existing value and try again. + + Remove (ns, path); + + try + { + + fPrivate->fMeta->SetProperty (ns, path, text); + + } + + CATCH_XMP ("SetProperty", true) + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::SetString (const char *ns, + const char *path, + const dng_string &s) + { + + dng_string ss (s); + + ss.SetLineEndings ('\n'); + + ss.StripLowASCII (); + + Set (ns, path, ss.Get ()); + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::SetStringList (const char *ns, + const char *path, + const dng_string_list &list, + bool isBag) + { + + // Remove any existing structure. + + Remove (ns, path); + + // If list is not empty, add the items. + + if (list.Count ()) + { + + NeedMeta (); + + for (uint32 index = 0; index < list.Count (); index++) + { + + dng_string s (list [index]); + + s.SetLineEndings ('\n'); + + s.StripLowASCII (); + + try + { + + fPrivate->fMeta->AppendArrayItem (ns, + path, + isBag ? kXMP_PropValueIsArray + : kXMP_PropArrayIsOrdered, + s.Get ()); + + } + + CATCH_XMP ("AppendArrayItem", true) + + } + + } + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::SetAltLangDefault (const char *ns, + const char *path, + const dng_string &s) + { + + NeedMeta (); + + Remove (ns, path); + + dng_string ss (s); + + ss.SetLineEndings ('\n'); + + ss.StripLowASCII (); + + try + { + + fPrivate->fMeta->SetLocalizedText (ns, + path, + "x-default", + "x-default", + ss.Get ()); + + } + + CATCH_XMP ("SetLocalizedText", true) + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::SetLocalString (const char *ns, + const char *path, + const dng_local_string &s) + { + + SetAltLangDefault (ns, path, s.DefaultText ()); + + try + { + + for (uint32 index = 0; index < s.TranslationCount (); index++) + { + + dng_string arrayItemPath; + + ComposeArrayItemPath (ns, + path, + index + 2, + arrayItemPath); + + fPrivate->fMeta->SetProperty (ns, + arrayItemPath.Get (), + s.Translation (index).Get ()); + + fPrivate->fMeta->SetQualifier (ns, + arrayItemPath.Get (), + kXMP_NS_XML, + "lang", + s.Language (index).Get (), + 0); + + } + + } + + CATCH_XMP ("SetLocalizedText", true) + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::SetStructField (const char *ns, + const char *path, + const char *fieldNS, + const char *fieldName, + const char *text) + { + + NeedMeta (); + + try + { + + fPrivate->fMeta->SetStructField (ns, + path, + fieldNS, + fieldName, + text); + + } + + CATCH_XMP ("SetStructField", true) + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::DeleteStructField (const char *ns, + const char *structName, + const char *fieldNS, + const char *fieldName) + { + + if (HasMeta ()) + { + + try + { + + fPrivate->fMeta->DeleteStructField (ns, structName, fieldNS, fieldName); + + } + + catch (...) + { + + } + + } + + } + +/*****************************************************************************/ + +dng_memory_block * dng_xmp_sdk::Serialize (dng_memory_allocator &allocator, + bool asPacket, + uint32 targetBytes, + uint32 padBytes, + bool forJPEG, + bool compact) const + { + + // The largest XMP packet you can embed in JPEG using normal methods: + + const uint32 kJPEG_XMP_Limit = 65504; + + if (HasMeta ()) + { + + TXMP_STRING_TYPE s; + + bool havePacket = false; + + // Note that the XMP lib is changing its default to compact format + // in the future, so the following line will need to change. + + uint32 formatOption = compact ? kXMP_UseCompactFormat : 0; + + if (asPacket && targetBytes) + { + + try + { + + fPrivate->fMeta->SerializeToBuffer (&s, + formatOption | kXMP_ExactPacketLength, + targetBytes, + "", + " "); + + havePacket = true; + + } + + catch (...) + { + + // Most likely the packet cannot fit in the target + // byte count. So try again without the limit. + + } + + } + + if (!havePacket) + { + + try + { + + fPrivate->fMeta->SerializeToBuffer (&s, + formatOption | + (asPacket ? 0 + : kXMP_OmitPacketWrapper), + (asPacket ? padBytes + : 0), + "", + " "); + + } + + CATCH_XMP ("SerializeToBuffer", true) + + } + + uint32 packetLen = (uint32) s.size (); + + if (forJPEG && asPacket && padBytes > 0 && targetBytes <= kJPEG_XMP_Limit && + packetLen > kJPEG_XMP_Limit) + { + + uint32 overLimitCount = packetLen - kJPEG_XMP_Limit; + + if (overLimitCount > padBytes) + { + padBytes = 0; + } + else + { + padBytes -= overLimitCount; + } + + try + { + + fPrivate->fMeta->SerializeToBuffer (&s, + formatOption, + padBytes, + "", + " "); + + } + + CATCH_XMP ("SerializeToBuffer", true) + + packetLen = (uint32) s.size (); + + } + + if (packetLen) + { + + AutoPtr buffer (allocator.Allocate (packetLen)); + + memcpy (buffer->Buffer (), s.c_str (), packetLen); + + return buffer.Release (); + + } + + } + + return NULL; + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::PackageForJPEG (dng_memory_allocator &allocator, + AutoPtr &stdBlock, + AutoPtr &extBlock, + dng_string &extDigest) const + { + + if (HasMeta ()) + { + + TXMP_STRING_TYPE stdStr; + TXMP_STRING_TYPE extStr; + TXMP_STRING_TYPE digestStr; + + try + { + + SXMPUtils::PackageForJPEG (*fPrivate->fMeta, + &stdStr, + &extStr, + &digestStr); + + } + + CATCH_XMP ("PackageForJPEG", true) + + uint32 stdLen = (uint32) stdStr.size (); + uint32 extLen = (uint32) extStr.size (); + + if (stdLen) + { + + stdBlock.Reset (allocator.Allocate (stdLen)); + + memcpy (stdBlock->Buffer (), stdStr.c_str (), stdLen); + + } + + if (extLen) + { + + extBlock.Reset (allocator.Allocate (extLen)); + + memcpy (extBlock->Buffer (), extStr.c_str (), extLen); + + if (digestStr.size () != 32) + { + ThrowProgramError (); + } + + extDigest.Set (digestStr.c_str ()); + + } + + } + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::MergeFromJPEG (const dng_xmp_sdk *xmp) + { + + if (xmp && xmp->HasMeta ()) + { + + NeedMeta (); + + try + { + + SXMPUtils::MergeFromJPEG (fPrivate->fMeta, + *xmp->fPrivate->fMeta); + + } + + CATCH_XMP ("MergeFromJPEG", true) + + } + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::ReplaceXMP (dng_xmp_sdk *xmp) + { + + ClearMeta (); + + if (xmp && xmp->HasMeta ()) + { + + fPrivate->fMeta = xmp->fPrivate->fMeta; + + xmp->fPrivate->fMeta = NULL; + + } + + } + +/*****************************************************************************/ + +bool dng_xmp_sdk::IteratePaths (IteratePathsCallback *callback, + void *callbackData, + const char* startingNS, + const char* startingPath) + { + + if (HasMeta ()) + { + + try + { + + SXMPIterator iter (*fPrivate->fMeta, startingNS, startingPath); + + TXMP_STRING_TYPE ns; + TXMP_STRING_TYPE prop; + + while (iter.Next (&ns, + &prop, + NULL, + NULL)) + { + + if (!callback (ns .c_str (), + prop.c_str (), + callbackData)) + { + + return false; + + } + + } + + } + + CATCH_XMP ("IteratePaths", true) + + } + + return true; + + } + +/*****************************************************************************/ + +#if qDNGXMPDocOps + +/*****************************************************************************/ + +void dng_xmp_sdk::DocOpsOpenXMP (const char *srcMIME) + { + + if (srcMIME [0]) + { + + NeedMeta (); + + try + { + + SXMPDocOps docOps; + + docOps.OpenXMP (fPrivate->fMeta, + srcMIME); + + } + + CATCH_XMP ("DocOpsOpenXMP", false) + + Set (XMP_NS_DC, + "format", + srcMIME); + + } + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::DocOpsPrepareForSave (const char *srcMIME, + const char *dstMIME, + bool newPath) + { + + NeedMeta (); + + try + { + + SXMPDocOps docOps; + + docOps.OpenXMP (fPrivate->fMeta, + srcMIME, + "old path"); + + docOps.NoteChange (kXMP_Part_All); + + docOps.PrepareForSave (dstMIME, + newPath ? "new path" : "old path"); + + } + + CATCH_XMP ("DocOpsPrepareForSave", false) + + Set (XMP_NS_DC, + "format", + dstMIME); + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::DocOpsUpdateMetadata (const char *srcMIME) + { + + NeedMeta (); + + try + { + + SXMPDocOps docOps; + + docOps.OpenXMP (fPrivate->fMeta, + srcMIME); + + docOps.NoteChange (kXMP_Part_Metadata); + + docOps.PrepareForSave (srcMIME); + + } + + CATCH_XMP ("DocOpsUpdateMetadata", false) + + } + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_xmp_sdk.h b/dng/dng_xmp_sdk.h new file mode 100644 index 0000000..539250a --- /dev/null +++ b/dng/dng_xmp_sdk.h @@ -0,0 +1,249 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#ifndef __dng_xmp_sdk__ +#define __dng_xmp_sdk__ + +/*****************************************************************************/ + +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_flags.h" +#include "dng_types.h" + +/*****************************************************************************/ + +extern const char *XMP_NS_TIFF; +extern const char *XMP_NS_EXIF; +extern const char *XMP_NS_EXIFEX; +extern const char *XMP_NS_PHOTOSHOP; +extern const char *XMP_NS_XAP; +extern const char *XMP_NS_XAP_RIGHTS; +extern const char *XMP_NS_DC; +extern const char *XMP_NS_XMP_NOTE; +extern const char *XMP_NS_MM; + +extern const char *XMP_NS_CRS; +extern const char *XMP_NS_CRSS; +extern const char *XMP_NS_CRD; + +extern const char *XMP_NS_LCP; + +extern const char *XMP_NS_AUX; + +extern const char *XMP_NS_IPTC; +extern const char *XMP_NS_IPTC_EXT; + +extern const char *XMP_NS_CRX; + +extern const char *XMP_NS_DNG; + +extern const char *XMP_NS_PANO; + +/*****************************************************************************/ + +class dng_xmp_private; + +/*****************************************************************************/ + +typedef bool (IteratePathsCallback) (const char *ns, + const char *path, + void *callbackData); + +/*****************************************************************************/ + +struct dng_xmp_namespace + { + const char * fullName; + const char * shortName; + }; + +/*****************************************************************************/ + +class dng_xmp_sdk + { + + private: + + dng_xmp_private *fPrivate; + + public: + + dng_xmp_sdk (); + + dng_xmp_sdk (const dng_xmp_sdk &sdk); + + virtual ~dng_xmp_sdk (); + + static void InitializeSDK (dng_xmp_namespace * extraNamespaces = NULL, + const char *software = NULL); + + static void TerminateSDK (); + + bool HasMeta () const; + + void RequireMeta () + { + NeedMeta (); + } + + void * GetPrivateMeta (); + + void Parse (dng_host &host, + const char *buffer, + uint32 count); + + bool Exists (const char *ns, + const char *path) const; + + void AppendArrayItem (const char *ns, + const char *arrayName, + const char *itemValue, + bool isBag = true, + bool propIsStruct = false); + + int32 CountArrayItems (const char *ns, + const char *path) const; + + bool HasNameSpace (const char *ns) const; + + void Remove (const char *ns, + const char *path); + + void RemoveProperties (const char *ns); + + bool IsEmptyString (const char *ns, + const char *path); + + bool IsEmptyArray (const char *ns, + const char *path); + + void ComposeArrayItemPath (const char *ns, + const char *arrayName, + int32 itemNumber, + dng_string &s) const; + + void ComposeStructFieldPath (const char *ns, + const char *structName, + const char *fieldNS, + const char *fieldName, + dng_string &s) const; + + bool GetNamespacePrefix (const char *uri, + dng_string &s) const; + + bool GetString (const char *ns, + const char *path, + dng_string &s) const; + + void ValidateStringList (const char *ns, + const char *path); + + bool GetStringList (const char *ns, + const char *path, + dng_string_list &list) const; + + bool GetAltLangDefault (const char *ns, + const char *path, + dng_string &s, + bool silent = false) const; + + bool GetLocalString (const char *ns, + const char *path, + dng_local_string &s) const; + + bool GetStructField (const char *ns, + const char *path, + const char *fieldNS, + const char *fieldName, + dng_string &s) const; + + void Set (const char *ns, + const char *path, + const char *text); + + void SetString (const char *ns, + const char *path, + const dng_string &s); + + void SetStringList (const char *ns, + const char *path, + const dng_string_list &list, + bool isBag); + + void SetAltLangDefault (const char *ns, + const char *path, + const dng_string &s); + + void SetLocalString (const char *ns, + const char *path, + const dng_local_string &s); + + void SetStructField (const char *ns, + const char *path, + const char *fieldNS, + const char *fieldName, + const char *text); + + void DeleteStructField (const char *ns, + const char *structName, + const char *fieldNS, + const char *fieldName); + + dng_memory_block * Serialize (dng_memory_allocator &allocator, + bool asPacket, + uint32 targetBytes, + uint32 padBytes, + bool forJPEG, + bool compact) const; + + void PackageForJPEG (dng_memory_allocator &allocator, + AutoPtr &stdBlock, + AutoPtr &extBlock, + dng_string &extDigest) const; + + void MergeFromJPEG (const dng_xmp_sdk *xmp); + + void ReplaceXMP (dng_xmp_sdk *xmp); + + bool IteratePaths (IteratePathsCallback *callback, + void *callbackData = NULL, + const char *startNS = 0, + const char *startingPath = 0); + + #if qDNGXMPDocOps + + void DocOpsOpenXMP (const char *srcMIME); + + void DocOpsPrepareForSave (const char *srcMIME, + const char *dstMIME, + bool newPath = true); + + void DocOpsUpdateMetadata (const char *srcMIME); + + #endif + + private: + + void ClearMeta (); + + void MakeMeta (); + + void NeedMeta (); + + // Hidden assignment operator. + + dng_xmp_sdk & operator= (const dng_xmp_sdk &sdk); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/dng/dng_xy_coord.cpp b/dng/dng_xy_coord.cpp new file mode 100644 index 0000000..3eeddb5 --- /dev/null +++ b/dng/dng_xy_coord.cpp @@ -0,0 +1,82 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +#include "dng_xy_coord.h" + +#include "dng_matrix.h" +#include "dng_utils.h" + +/******************************************************************************/ + +dng_xy_coord XYZtoXY (const dng_vector_3 &coord) + { + + real64 X = coord [0]; + real64 Y = coord [1]; + real64 Z = coord [2]; + + real64 total = X + Y + Z; + + if (total > 0.0) + { + + return dng_xy_coord (X / total, + Y / total); + + } + + return D50_xy_coord (); + + } + +/*****************************************************************************/ + +dng_vector_3 XYtoXYZ (const dng_xy_coord &coord) + { + + dng_xy_coord temp = coord; + + // Restrict xy coord to someplace inside the range of real xy coordinates. + // This prevents math from doing strange things when users specify + // extreme temperature/tint coordinates. + + temp.x = Pin_real64 (0.000001, temp.x, 0.999999); + temp.y = Pin_real64 (0.000001, temp.y, 0.999999); + + if (temp.x + temp.y > 0.999999) + { + real64 scale = 0.999999 / (temp.x + temp.y); + temp.x *= scale; + temp.y *= scale; + } + + return dng_vector_3 (temp.x / temp.y, + 1.0, + (1.0 - temp.x - temp.y) / temp.y); + + } + +/*****************************************************************************/ + +dng_xy_coord PCStoXY () + { + + return D50_xy_coord (); + + } + +/*****************************************************************************/ + +dng_vector_3 PCStoXYZ () + { + + return XYtoXYZ (PCStoXY ()); + + } + +/*****************************************************************************/ diff --git a/dng/dng_xy_coord.h b/dng/dng_xy_coord.h new file mode 100644 index 0000000..4c5a64d --- /dev/null +++ b/dng/dng_xy_coord.h @@ -0,0 +1,182 @@ +/*****************************************************************************/ +// Copyright 2006-2019 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/** \file + * Representation of colors in xy and XYZ coordinates. + */ + +/*****************************************************************************/ + +#ifndef __dng_xy_coord__ +#define __dng_xy_coord__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_types.h" + +/*****************************************************************************/ + +class dng_xy_coord + { + + public: + + real64 x; + real64 y; + + public: + + dng_xy_coord () + : x (0.0) + , y (0.0) + { + } + + dng_xy_coord (real64 xx, real64 yy) + : x (xx) + , y (yy) + { + } + + void Clear () + { + x = 0.0; + y = 0.0; + } + + bool IsValid () const + { + return x > 0.0 && + y > 0.0; + } + + bool NotValid () const + { + return !IsValid (); + } + + bool operator== (const dng_xy_coord &coord) const + { + return coord.x == x && + coord.y == y; + } + + bool operator!= (const dng_xy_coord &coord) const + { + return !(*this == coord); + } + + }; + +/*****************************************************************************/ + +inline dng_xy_coord operator+ (const dng_xy_coord &A, + const dng_xy_coord &B) + { + + dng_xy_coord C; + + C.x = A.x + B.x; + C.y = A.y + B.y; + + return C; + + } + +/*****************************************************************************/ + +inline dng_xy_coord operator- (const dng_xy_coord &A, + const dng_xy_coord &B) + { + + dng_xy_coord C; + + C.x = A.x - B.x; + C.y = A.y - B.y; + + return C; + + } + +/*****************************************************************************/ + +inline dng_xy_coord operator* (real64 scale, + const dng_xy_coord &A) + { + + dng_xy_coord B; + + B.x = A.x * scale; + B.y = A.y * scale; + + return B; + + } + +/******************************************************************************/ + +inline real64 operator* (const dng_xy_coord &A, + const dng_xy_coord &B) + { + + return A.x * B.x + + A.y * B.y; + + } + +/*****************************************************************************/ + +// Standard xy coordinate constants. + +inline dng_xy_coord StdA_xy_coord () + { + return dng_xy_coord (0.4476, 0.4074); + } + +inline dng_xy_coord D50_xy_coord () + { + return dng_xy_coord (0.3457, 0.3585); + } + +inline dng_xy_coord D55_xy_coord () + { + return dng_xy_coord (0.3324, 0.3474); + } + +inline dng_xy_coord D65_xy_coord () + { + return dng_xy_coord (0.3127, 0.3290); + } + +inline dng_xy_coord D75_xy_coord () + { + return dng_xy_coord (0.2990, 0.3149); + } + +/*****************************************************************************/ + +// Convert between xy coordinates and XYZ coordinates. + +dng_xy_coord XYZtoXY (const dng_vector_3 &coord); + +dng_vector_3 XYtoXYZ (const dng_xy_coord &coord); + +/*****************************************************************************/ + +// Returns the ICC XYZ profile connection space white point. + +dng_xy_coord PCStoXY (); + +dng_vector_3 PCStoXYZ (); + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/pictureConvert.pro b/pictureConvert.pro index c73aea4..f991848 100644 --- a/pictureConvert.pro +++ b/pictureConvert.pro @@ -20,8 +20,9 @@ win32-msvc* { win32-g++ { message("mingw") - INCLUDEPATH += C:\dev\3rdParty\exiv2\include C:\dev\3rdParty\libraw - LIBS += -LC:\dev\3rdParty\exiv2\lib -lexiv2.dll -LC:\dev\3rdParty\libraw\lib -lraw -lws2_32 + INCLUDEPATH += C:\dev\3rdParty\exiv2\include C:\dev\3rdParty\libraw xmp + LIBS += -LC:\dev\3rdParty\exiv2\lib -lexiv2.dll -LC:\dev\3rdParty\libraw\lib -lraw -lws2_32 -lz + QMAKE_CXXFLAGS += -DWIN_ENV -D_MSC_VER=1601 -DBUILDING_XMPCOMMON_LIB -DBUILDING_XMPCOMMON_AS_STATIC=1 -DBUILDING_XMPCORE_LIB } unix { @@ -44,22 +45,250 @@ DEFINES += APP_VERSION=\\\"$$VERSION\\\" #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ + cdcp.cpp \ cexif.cpp \ cexportdialog.cpp \ cimage.cpp \ csplashscreen.cpp \ ctreeview.cpp \ + dng/dng_1d_function.cpp \ + dng/dng_1d_table.cpp \ + dng/dng_abort_sniffer.cpp \ + dng/dng_area_task.cpp \ + dng/dng_bad_pixels.cpp \ + dng/dng_big_table.cpp \ + dng/dng_bottlenecks.cpp \ + dng/dng_camera_profile.cpp \ + dng/dng_color_space.cpp \ + dng/dng_color_spec.cpp \ + dng/dng_date_time.cpp \ + dng/dng_exceptions.cpp \ + dng/dng_exif.cpp \ + dng/dng_file_stream.cpp \ + dng/dng_filter_task.cpp \ + dng/dng_fingerprint.cpp \ + dng/dng_gain_map.cpp \ + dng/dng_globals.cpp \ + dng/dng_host.cpp \ + dng/dng_hue_sat_map.cpp \ + dng/dng_ifd.cpp \ + dng/dng_image.cpp \ + dng/dng_image_writer.cpp \ + dng/dng_info.cpp \ + dng/dng_iptc.cpp \ + dng/dng_jpeg_image.cpp \ + dng/dng_lens_correction.cpp \ + dng/dng_linearization_info.cpp \ + dng/dng_local_string.cpp \ + dng/dng_lossless_jpeg.cpp \ + dng/dng_matrix.cpp \ + dng/dng_memory.cpp \ + dng/dng_memory_stream.cpp \ + dng/dng_misc_opcodes.cpp \ + dng/dng_mosaic_info.cpp \ + dng/dng_mutex.cpp \ + dng/dng_negative.cpp \ + dng/dng_opcode_list.cpp \ + dng/dng_opcodes.cpp \ + dng/dng_orientation.cpp \ + dng/dng_parse_utils.cpp \ + dng/dng_pixel_buffer.cpp \ + dng/dng_point.cpp \ + dng/dng_preview.cpp \ + dng/dng_pthread.cpp \ + dng/dng_rational.cpp \ + dng/dng_read_image.cpp \ + dng/dng_rect.cpp \ + dng/dng_ref_counted_block.cpp \ + dng/dng_reference.cpp \ + dng/dng_render.cpp \ + dng/dng_resample.cpp \ + dng/dng_safe_arithmetic.cpp \ + dng/dng_shared.cpp \ + dng/dng_simple_image.cpp \ + dng/dng_spline.cpp \ + dng/dng_stream.cpp \ + dng/dng_string.cpp \ + dng/dng_string_list.cpp \ + dng/dng_tag_types.cpp \ + dng/dng_temperature.cpp \ + dng/dng_tile_iterator.cpp \ + dng/dng_tone_curve.cpp \ + dng/dng_utils.cpp \ + dng/dng_validate.cpp \ + dng/dng_xmp.cpp \ + dng/dng_xmp_sdk.cpp \ + dng/dng_xy_coord.cpp \ main.cpp \ - cmainwindow.cpp + cmainwindow.cpp \ + xmp/XMPCommon/source/IConfigurable.cpp \ + xmp/XMPCommon/source/IConfigurationManager.cpp \ + xmp/XMPCommon/source/IError.cpp \ + xmp/XMPCommon/source/IErrorNotifier.cpp \ + xmp/XMPCommon/source/IUTF8String.cpp \ + xmp/XMPCore/source/IArrayNode.cpp \ + xmp/XMPCore/source/IClientDOMParser.cpp \ + xmp/XMPCore/source/IClientDOMSerializer.cpp \ + xmp/XMPCore/source/ICompositeNode.cpp \ + xmp/XMPCore/source/ICoreConfigurationManager.cpp \ + xmp/XMPCore/source/ICoreObjectFactory.cpp \ + xmp/XMPCore/source/IDOMImplementationRegistry.cpp \ + xmp/XMPCore/source/IDOMParser.cpp \ + xmp/XMPCore/source/IDOMSerializer.cpp \ + xmp/XMPCore/source/IMetadata.cpp \ + xmp/XMPCore/source/INameSpacePrefixMap.cpp \ + xmp/XMPCore/source/INode.cpp \ + xmp/XMPCore/source/INodeIterator.cpp \ + xmp/XMPCore/source/IPath.cpp \ + xmp/XMPCore/source/IPathSegment.cpp \ + xmp/XMPCore/source/ISimpleNode.cpp \ + xmp/XMPCore/source/IStructureNode.cpp HEADERS += \ + cdcp.h \ cexif.h \ cexportdialog.h \ cimage.h \ csplashscreen.h \ cmainwindow.h \ common.h \ - ctreeview.h + ctreeview.h \ + dng/RawEnvironment.h \ + dng/dng_1d_function.h \ + dng/dng_1d_table.h \ + dng/dng_abort_sniffer.h \ + dng/dng_area_task.h \ + dng/dng_assertions.h \ + dng/dng_auto_ptr.h \ + dng/dng_bad_pixels.h \ + dng/dng_big_table.h \ + dng/dng_bottlenecks.h \ + dng/dng_camera_profile.h \ + dng/dng_classes.h \ + dng/dng_color_space.h \ + dng/dng_color_spec.h \ + dng/dng_date_time.h \ + dng/dng_errors.h \ + dng/dng_exceptions.h \ + dng/dng_exif.h \ + dng/dng_fast_module.h \ + dng/dng_file_stream.h \ + dng/dng_filter_task.h \ + dng/dng_fingerprint.h \ + dng/dng_flags.h \ + dng/dng_gain_map.h \ + dng/dng_globals.h \ + dng/dng_host.h \ + dng/dng_hue_sat_map.h \ + dng/dng_ifd.h \ + dng/dng_image.h \ + dng/dng_image_writer.h \ + dng/dng_info.h \ + dng/dng_iptc.h \ + dng/dng_jpeg_image.h \ + dng/dng_lens_correction.h \ + dng/dng_linearization_info.h \ + dng/dng_local_string.h \ + dng/dng_lossless_jpeg.h \ + dng/dng_matrix.h \ + dng/dng_memory.h \ + dng/dng_memory_stream.h \ + dng/dng_misc_opcodes.h \ + dng/dng_mosaic_info.h \ + dng/dng_mutex.h \ + dng/dng_negative.h \ + dng/dng_opcode_list.h \ + dng/dng_opcodes.h \ + dng/dng_orientation.h \ + dng/dng_parse_utils.h \ + dng/dng_pixel_buffer.h \ + dng/dng_point.h \ + dng/dng_preview.h \ + dng/dng_pthread.h \ + dng/dng_rational.h \ + dng/dng_read_image.h \ + dng/dng_rect.h \ + dng/dng_ref_counted_block.h \ + dng/dng_reference.h \ + dng/dng_render.h \ + dng/dng_resample.h \ + dng/dng_safe_arithmetic.h \ + dng/dng_sdk_limits.h \ + dng/dng_shared.h \ + dng/dng_simd_type.h \ + dng/dng_simple_image.h \ + dng/dng_spline.h \ + dng/dng_stream.h \ + dng/dng_string.h \ + dng/dng_string_list.h \ + dng/dng_tag_codes.h \ + dng/dng_tag_types.h \ + dng/dng_tag_values.h \ + dng/dng_temperature.h \ + dng/dng_tile_iterator.h \ + dng/dng_tone_curve.h \ + dng/dng_types.h \ + dng/dng_uncopyable.h \ + dng/dng_utils.h \ + dng/dng_xmp.h \ + dng/dng_xmp_sdk.h \ + dng/dng_xy_coord.h \ + xmp/TXMPFiles.hpp \ + xmp/TXMPIterator.hpp \ + xmp/TXMPMeta.hpp \ + xmp/TXMPUtils.hpp \ + xmp/XMP.hpp \ + xmp/XMP.incl_cpp \ + xmp/XMPCommon/Interfaces/BaseInterfaces/IConfigurable.h \ + xmp/XMPCommon/Interfaces/BaseInterfaces/ISharedObject.h \ + xmp/XMPCommon/Interfaces/BaseInterfaces/IThreadSafe.h \ + xmp/XMPCommon/Interfaces/BaseInterfaces/IVersionable.h \ + xmp/XMPCommon/Interfaces/IConfigurationManager.h \ + xmp/XMPCommon/Interfaces/IError.h \ + xmp/XMPCommon/Interfaces/IErrorNotifier.h \ + xmp/XMPCommon/Interfaces/IMemoryAllocator.h \ + xmp/XMPCommon/Interfaces/IObjectFactory.h \ + xmp/XMPCommon/Interfaces/IUTF8String.h \ + xmp/XMPCommon/Utilities/TWrapperFunctions.h \ + xmp/XMPCommon/Utilities/TWrapperFunctions2.h \ + xmp/XMPCommon/XMPCommonDefines.h \ + xmp/XMPCommon/XMPCommonErrorCodes.h \ + xmp/XMPCommon/XMPCommonFwdDeclarations.h \ + xmp/XMPCommon/XMPCommonLatestInterfaceVersions.h \ + xmp/XMPCore/Interfaces/IArrayNode.h \ + xmp/XMPCore/Interfaces/IClientDOMParser.h \ + xmp/XMPCore/Interfaces/IClientDOMSerializer.h \ + xmp/XMPCore/Interfaces/ICompositeNode.h \ + xmp/XMPCore/Interfaces/ICoreConfigurationManager.h \ + xmp/XMPCore/Interfaces/ICoreObjectFactory.h \ + xmp/XMPCore/Interfaces/IDOMImplementationRegistry.h \ + xmp/XMPCore/Interfaces/IDOMParser.h \ + xmp/XMPCore/Interfaces/IDOMSerializer.h \ + xmp/XMPCore/Interfaces/IMetadata.h \ + xmp/XMPCore/Interfaces/INameSpacePrefixMap.h \ + xmp/XMPCore/Interfaces/INode.h \ + xmp/XMPCore/Interfaces/INodeIterator.h \ + xmp/XMPCore/Interfaces/IPath.h \ + xmp/XMPCore/Interfaces/IPathSegment.h \ + xmp/XMPCore/Interfaces/ISimpleNode.h \ + xmp/XMPCore/Interfaces/IStructureNode.h \ + xmp/XMPCore/XMPCoreDefines.h \ + xmp/XMPCore/XMPCoreErrorCodes.h \ + xmp/XMPCore/XMPCoreFwdDeclarations.h \ + xmp/XMPCore/XMPCoreLatestInterfaceVersions.h \ + xmp/XMP_Const.h \ + xmp/XMP_Environment.h \ + xmp/XMP_IO.hpp \ + xmp/XMP_Version.h \ + xmp/client-glue/TXMPFiles.incl_cpp \ + xmp/client-glue/TXMPIterator.incl_cpp \ + xmp/client-glue/TXMPMeta.incl_cpp \ + xmp/client-glue/TXMPUtils.incl_cpp \ + xmp/client-glue/WXMPFiles.hpp \ + xmp/client-glue/WXMPIterator.hpp \ + xmp/client-glue/WXMPMeta.hpp \ + xmp/client-glue/WXMPUtils.hpp \ + xmp/client-glue/WXMP_Common.hpp FORMS += \ cexportdialog.ui \ diff --git a/xmp/TXMPFiles.hpp b/xmp/TXMPFiles.hpp new file mode 100644 index 0000000..aeecda9 --- /dev/null +++ b/xmp/TXMPFiles.hpp @@ -0,0 +1,854 @@ +#ifndef __TXMPFiles_hpp__ +#define __TXMPFiles_hpp__ 1 + +#if ( ! __XMP_hpp__ ) + #error "Do not directly include, use XMP.hpp" +#endif + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +// ================================================================================================= +/// \file TXMPFiles.hpp +/// \brief API for access to the main (document-level) metadata in a file_. +/// +/// The Adobe XMP Toolkit's file handling component, XMPFiles, is a front end to a set of +/// format-specific file handlers that support file I/O for XMP. The file handlers implement smart, +/// efficient support for those file formats for which the means to embed XMP is defined in the XMP +/// Specification. Where possible, this support allows: +/// \li Injection of XMP where none currently exists +/// \li Expansion of XMP without regard to existing padding +/// \li Reconciliation of the XMP and other legacy forms of metadata. +/// +/// \c TXMPFiles is designed for use by clients interested in the metadata and not in the primary +/// file content; the Adobe Bridge application is a typical example. \c TXMPFiles is not intended to +/// be appropriate for files authored by an application; that is, those files for which the +/// application has explicit knowledge of the file format. +// ================================================================================================= + + +// ================================================================================================= +/// \class TXMPFiles TXMPFiles.hpp +/// \brief API for access to the main (document-level) metadata in a file. +/// +/// \c TXMPFiles is a template class that provides the API for the Adobe XMP Toolkit's XMPFiles +/// component. This provides convenient access to the main, or document level, XMP for a file. Use +/// it to obtain metadata from a file, which you can then manipulate with the XMP Core component +/// (the classes \c TXMPMeta, \c TXMPUtils, and \c TXMPIterator); and to write new or changed +/// metadata back out to a file. +/// +/// The functions allow you to open a file, read and write the metadata, then close the file. +/// While open, portions of the file might be maintained in RAM data structures. Memory +/// usage can vary considerably depending onfile format and access options. +/// +/// A file can be opened for read-only or read-write access, with typical exclusion for both +/// modes. Errors result in the throw of an \c XMPError exception. +/// +/// \c TXMPFiles is the template class. It must be instantiated with a string class such as +/// \c std::string. Read the Toolkit Overview for information about the overall architecture of the XMP +/// API, and the documentation for \c XMP.hpp for specific instantiation instructions. +/// +/// Access these functions through the concrete class, \c SXMPFiles. +// ================================================================================================= + + +#if XMP_StaticBuild // ! Client XMP_IO objects can only be used in static builds. + #include "XMP_IO.hpp" +#endif + + +template +class TXMPFiles { + +public: + + // ============================================================================================= + /// \name Initialization and termination + /// @{ + /// + /// A \c TXMPFiles object must be initialized before use and can be terminated when done. + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetVersionInfo() retrieves version information for the XMPFiles component. + /// + /// Can be called before \c #Initialize(). This function is static; make the call directly from + /// the concrete class (\c SXMPFiles). + /// + /// @param versionInfo [out] A buffer in which to return the version information. + + static void GetVersionInfo ( XMP_VersionInfo * versionInfo ); + + // --------------------------------------------------------------------------------------------- + /// @brief Initializes the XMPFiles library; must be called before creating an \c SXMPFiles object. + /// + /// The main action is to activate the available smart file handlers. Must be called before + /// using any methods except \c GetVersionInfo(). + /// + /// This function is static; make the call directly from the concrete class (\c SXMPFiles). + /// + /// @return True on success. + + static bool Initialize(); + + // --------------------------------------------------------------------------------------------- + /// @brief Initializes the XMPFiles library; must be called before creating an \c SXMPFiles object. + /// + /// This overload of TXMPFiles::Initialize() accepts option bits to customize the initialization + /// actions. At this time no option is defined. + /// + /// The main action is to activate the available smart file handlers. Must be called before + /// using any methods except \c GetVersionInfo(). + /// + /// This function is static; make the call directly from the concrete class (\c SXMPFiles). + /// + /// @param options Option flags to control the initialization actions. + /// + /// @return True on success. + + static bool Initialize ( XMP_OptionBits options ); + + // --------------------------------------------------------------------------------------------- + /// @brief Initializes the XMPFiles library; must be called before creating an \c SXMPFiles object. + /// + /// This overload of TXMPFiles::Initialize() accepts plugin directory and name of the plug-ins + /// as a comma separated list to load the file handler plug-ins. If plugins == NULL, then all + /// plug-ins present in the plug-in directory will be loaded. + /// + /// The main action is to activate the available smart file handlers. Must be called before + /// using any methods except \c GetVersionInfo(). + /// + /// This function is static; make the call directly from the concrete class (\c SXMPFiles). + /// + /// @param pluginFolder Pugin directorty to load the file handler plug-ins. + /// @param plugins Comma sepearted list of plug-ins which should be loaded from the plug-in directory. + /// If plugin == NULL, then all plug-ins availbale in the plug-in directory will be loaded. + /// + /// @return True on success. + + static bool Initialize ( const char* pluginFolder, const char* plugins = NULL ); + + // --------------------------------------------------------------------------------------------- + /// @brief Initializes the XMPFiles library; must be called before creating an \c SXMPFiles object. + /// + /// This overload of TXMPFiles::Initialize( XMP_OptionBits options ) accepts plugin directory and + /// name of the plug-ins as a comma separated list to load the file handler plug-ins. + /// If plugins == NULL, then all plug-ins present in the plug-in directory will be loaded. + /// + /// The main action is to activate the available smart file handlers. Must be called before + /// using any methods except \c GetVersionInfo(). + /// + /// This function is static; make the call directly from the concrete class (\c SXMPFiles). + /// + /// @param options Option flags to control the initialization actions. + /// @param pluginFolder Pugin directorty to load the file handler plug-ins. + /// @param plugins Comma sepearted list of plug-ins which should be loaded from the plug-in directory. + /// If plugin == NULL, then all plug-ins availbale in the plug-in directory will be loaded. + /// + /// @return True on success. + + static bool Initialize ( XMP_OptionBits options, const char* pluginFolder, const char* plugins = NULL ); + + // --------------------------------------------------------------------------------------------- + /// @brief Terminates use of the XMPFiles library. + /// + /// Optional. Deallocates global data structures created by intialization. Its main action is to + /// deallocate heap-allocated global storage, for the benefit of client leak checkers. + /// + /// This function is static; make the call directly from the concrete class (\c SXMPFiles). + + static void Terminate(); + + /// @} + + // ============================================================================================= + /// \name Constructors and destructor + /// @{ + /// + /// The default constructor initializes an object that is associated with no file. The alternate + /// constructors call \c OpenFile(). + + // --------------------------------------------------------------------------------------------- + /// @brief Default constructor initializes an object that is associated with no file. + + TXMPFiles(); + + // --------------------------------------------------------------------------------------------- + /// @brief Destructor; typical virtual destructor. + /// + /// The destructor does not call \c CloseFile(); pending updates are lost when the destructor is run. + /// + /// @see \c OpenFile(), \c CloseFile() + + virtual ~TXMPFiles() throw(); + + // --------------------------------------------------------------------------------------------- + /// @brief Alternate constructor associates the new \c XMPFiles object with a specific file. + /// + /// Calls \c OpenFile() to open the specified file after performing a default construct. + /// + /// @param filePath The path for the file, specified as a nul-terminated UTF-8 string. + /// + /// @param format A format hint for the file, if known. + /// + /// @param openFlags Options for how the file is to be opened (for read or read/write, for + /// example). Use a logical OR of these bit-flag constants: + /// + /// \li \c #kXMPFiles_OpenForRead + /// \li \c #kXMPFiles_OpenForUpdate + /// \li \c #kXMPFiles_OpenOnlyXMP + /// \li \c #kXMPFiles_OpenStrictly + /// \li \c #kXMPFiles_OpenUseSmartHandler + /// \li \c #kXMPFiles_OpenUsePacketScanning + /// \li \c #kXMPFiles_OpenLimitedScanning + /// + /// @return The new \c TXMPFiles object. + + TXMPFiles ( XMP_StringPtr filePath, + XMP_FileFormat format = kXMP_UnknownFile, + XMP_OptionBits openFlags = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief Alternate constructor associates the new \c XMPFiles object with a specific file, + /// using a string object. + /// + /// Overloads the basic form of the function, allowing you to pass a string object + /// for the file path. It is otherwise identical; see details in the canonical form. + + TXMPFiles ( const tStringObj & filePath, + XMP_FileFormat format = kXMP_UnknownFile, + XMP_OptionBits openFlags = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief Copy constructor + /// + /// Increments an internal reference count but does not perform a deep copy. + /// + /// @param original The existing \c TXMPFiles object to copy. + /// + /// @return The new \c TXMPFiles object. + + TXMPFiles ( const TXMPFiles & original ); + + // --------------------------------------------------------------------------------------------- + /// @brief Assignment operator + /// + /// Increments an internal reference count but does not perform a deep copy. + /// + /// @param rhs The existing \c TXMPFiles object. + + void operator= ( const TXMPFiles & rhs ); + + // --------------------------------------------------------------------------------------------- + /// @brief Reconstructs a \c TXMPFiles object from an internal reference. + /// + /// This constructor creates a new \c TXMPFiles object that refers to the underlying reference + /// object of an existing \c TXMPFiles object. Use to safely pass \c SXMPFiles references across + /// DLL boundaries. + /// + /// @param xmpFilesObj The underlying reference object, obtained from some other XMP object + /// with \c TXMPFiles::GetInternalRef(). + /// + /// @return The new object. + + TXMPFiles ( XMPFilesRef xmpFilesObj ); + + // --------------------------------------------------------------------------------------------- + /// @brief GetInternalRef() retrieves an internal reference that can be safely passed across DLL + /// boundaries and reconstructed. + /// + /// Use with the reconstruction constructor to safely pass \c SXMPFiles references across DLL + /// boundaries where the clients might have used different string types when instantiating + /// \c TXMPFiles. + /// + /// @return The internal reference. + /// + /// @see \c TXMPMeta::GetInternalRef() for usage. + + XMPFilesRef GetInternalRef(); + + /// @} + + // ============================================================================================= + /// \name File handler information + /// @{ + /// + /// Call this static function from the concrete class, \c SXMPFiles, to obtain information about + /// the file handlers for the XMPFiles component. + + // --------------------------------------------------------------------------------------------- + /// @brief GetFormatInfo() reports what features are supported for a specific file format. + /// + /// The file handlers for different file formats vary considerably in what features they + /// support. Support depends on both the general capabilities of the format and the + /// implementation of the handler for that format. + /// + ///This function is static; make the call directly from the concrete class (\c SXMPFiles). + /// + /// @param format The file format whose support flags are desired. + /// + /// @param handlerFlags [out] A buffer in which to return a logical OR of option bit flags. + /// The following constants are defined: + /// + /// \li \c #kXMPFiles_CanInjectXMP - Can inject first-time XMP into an existing file. + /// \li \c #kXMPFiles_CanExpand - Can expand XMP or other metadata in an existing file. + /// \li \c #kXMPFiles_CanRewrite - Can copy one file to another, writing new metadata (as in SaveAs) + /// \li \c #kXMPFiles_CanReconcile - Supports reconciliation between XMP and other forms. + /// \li \c #kXMPFiles_AllowsOnlyXMP - Allows access to just the XMP, ignoring other forms. + /// This is only meaningful if \c #kXMPFiles_CanReconcile is set. + /// \li \c #kXMPFiles_ReturnsRawPacket - File handler returns raw XMP packet information and string. + /// + /// Even if \c #kXMPFiles_ReturnsRawPacket is set, the returned packet information might have an + /// offset of -1 to indicate an unknown offset. While all file handlers should be able to return + /// the raw packet, some might not know the offset of the packet within the file. This is + /// typical in cases where external libraries are used. These cases might not even allow return + /// of the raw packet. + /// + /// @return True if the format has explicit "smart" support, false if the format is handled by + /// the default packet scanning plus heuristics. */ + + + static bool GetFormatInfo ( XMP_FileFormat format, + XMP_OptionBits * handlerFlags = 0 ); + + /// @} + + // ============================================================================================= + /// \name File operations + /// @{ + /// + /// These functions allow you to open, close, and query files. + + // --------------------------------------------------------------------------------------------- + /// @brief \c CheckFileFormat() tries to determine the format of a file. + /// + /// Tries to determine the format of a file, returning an \c #XMP_FileFormat value. Uses the + /// same logic as \c OpenFile() to select a smart handler. + /// + /// @param filePath The path for the file, appropriate for the local operating system. Passed as + /// a nul-terminated UTF-8 string. The path is the same as would be passed to \c OpenFile. + /// + /// @return The file's format if a smart handler would be selected by \c OpenFile(), otherwise + /// \c #kXMP_UnknownFile. + + static XMP_FileFormat CheckFileFormat ( XMP_StringPtr filePath ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c CheckPackageFormat() tries to determine the format of a "package" folder. + /// + /// Tries to determine the format of a package, given the name of the top-level folder. Returns + /// an \c #XMP_FileFormat value. Examples of recognized packages include the video formats P2, + /// XDCAM, or Sony HDV. These packages contain collections of "clips", stored as multiple files + /// in specific subfolders. + /// + /// @param folderPath The path for the top-level folder, appropriate for the local operating + /// system. Passed as a nul-terminated UTF-8 string. This is not the same path you would pass to + /// \c OpenFile(). For example, the top-level path for a package might be ".../MyMovie", while + /// the path to a file you wish to open would be ".../MyMovie/SomeClip". + /// + /// @return The package's format if it can be determined, otherwise \c #kXMP_UnknownFile. + + static XMP_FileFormat CheckPackageFormat ( XMP_StringPtr folderPath ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetFileModDate() returns the last modification date of all files that are returned + /// by \c GetAssociatedResources() + /// + /// Returns the most recent O/S file modification date of all associated files. In the typical case + /// of a single file containing embedded XMP, returned date value is the modification date of the + /// same file. For sidecar and folder based video packages, returned date value is the modification + /// date of that associated file which was updated last. + /// + /// @param filePath A path exactly as would be passed to \c OpenFile. + /// + /// @param modDate A required pointer to return the last modification date. + /// + /// @param format A format hint as would be passed to \c OpenFile. + /// + /// @param options An optional set of option flags. The only defined one is \c kXMPFiles_ForceGivenHandler, + /// used to shortcut the handler selection logic if the caller is certain of the format. + /// + /// @return Returns true if the file path is valid to select a smart handler, false for an + /// invalid path or if fallback packet scanning would be selected. + + static bool GetFileModDate ( XMP_StringPtr filePath, + XMP_DateTime * modDate, + XMP_FileFormat * format = 0, + XMP_OptionBits options = 0 ); + + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetAssociatedResources() returns a list of files and folders associated to filePath. + /// + /// \c GetAssociatedResources is provided to locate all files that are associated to the given + /// filePath such as sidecar-based XMP or folder-based video packages.If a smart + /// handler can be selected (not fallback packet scanning) then a list of file/folder paths is + /// returned for the related files that can be safely copied/imported to a different location, + /// keeping intact metadata(XMP and non-XMP),content and the necessary folder structure of the + /// format. The necessary folder structure here is the structure that is needed to uniquely + /// identify a folder-based format.The filePath and format parameters are exactly as would be + /// used for OpenFile. In the simple embedded XMP case just one path is returned. In the simple + /// sidecar case one or two paths will be returned, one if there is no sidecar XMP and two if + /// sidecar XMP exists. For folder-based handlers paths to all associated files is returned, + /// including the files and folders necessary to identify the format.In general, all the returned + /// paths are existent.In case of folder based video formats the first associated resource in the + /// resourceList is the root folder. + /// + /// @param filePath A path exactly as would be passed to \c OpenFile. + /// + /// @param resourceList Address of a vector of strings to receive all associated resource paths. + /// + /// @param format A format hint as would be passed to \c OpenFile. + /// + /// @param options An optional set of option flags. The only defined one is \c kXMPFiles_ForceGivenHandler, + /// used to shortcut the handler selection logic if the caller is certain of the format. + /// + /// @return Returns true if the file path is valid to select a smart handler, false for an + /// invalid path or if fallback packet scanning would be selected. Can also return false for + /// unexpected errors that prevent knowledge of the file usage. + + static bool GetAssociatedResources ( XMP_StringPtr filePath, + std::vector* resourceList, + XMP_FileFormat format = kXMP_UnknownFile, + XMP_OptionBits options = 0); + + // --------------------------------------------------------------------------------------------- + /// @brief \c IsMetadataWritable() returns true if metadata can be updated for the given media path. + /// + /// \c IsMetadataWritable is provided to check if metadata can be updated or written to the format.In + /// the case of folder-based video formats only if all the metadata files can be written to, true is + /// returned.In other words, false is returned for a partial-write state of metadata files in + /// folder-based media formats. + /// + /// @param filePath A path exactly as would be passed to \c OpenFile. + /// + /// @param writable A pointer to the result flag. Is true if the metadata can be updated in the format, + /// otherwise false. + /// + /// @param format A format hint as would be passed to \c OpenFile. + /// + /// @param options An optional set of option flags. The only defined one is \c kXMPFiles_ForceGivenHandler, + /// used to shortcut the handler selection logic if the caller is certain of the format. + /// + /// @return Returns true if the file path is valid to select a smart handler, false for an + /// invalid path or if fallback packet scanning would be selected. + + static bool IsMetadataWritable (XMP_StringPtr filePath, + bool * writable, + XMP_FileFormat format = kXMP_UnknownFile, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c OpenFile() opens a file for metadata access. + /// + /// Opens a file for the requested forms of metadata access. Opening the file at a minimum + /// causes the raw XMP packet to be read from the file. If the file handler supports legacy + /// metadata reconciliation then legacy metadata is also read, unless \c #kXMPFiles_OpenOnlyXMP + /// is passed. + /// + /// If the file is opened for read-only access (passing \c #kXMPFiles_OpenForRead), the disk + /// file is closed immediately after reading the data from it; the \c XMPFiles object, however, + /// remains in the open state. You must call \c CloseFile() when finished using it. Other + /// methods, such as \c GetXMP(), can only be used between the \c OpenFile() and \c CloseFile() + /// calls. The \c XMPFiles destructor does not call \c CloseFile(); if you call it without + /// closing, any pending updates are lost. + /// + /// If the file is opened for update (passing \c #kXMPFiles_OpenForUpdate), the disk file + /// remains open until \c CloseFile() is called. The disk file is only updated once, when + /// \c CloseFile() is called, regardless of how many calls are made to \c PutXMP(). + /// + /// Typically, the XMP is not parsed and legacy reconciliation is not performed until \c GetXMP() + /// is called, but this is not guaranteed. Specific file handlers might do earlier parsing of + /// the XMP. Delayed parsing and early disk file close for read-only access are optimizations + /// to help clients implementing file browsers, so that they can access the file briefly + /// and possibly display a thumbnail, then postpone more expensive XMP processing until later. + /// + /// @param filePath The path for the file, appropriate for the local operating system. Passed as + /// a nul-terminated UTF-8 string. + /// + /// @param format The format of the file. If the format is unknown (\c #kXMP_UnknownFile) the + /// format is determined from the file content. The first handler to check is guessed from the + /// file's extension. Passing a specific format value is generally just a hint about what file + /// handler to try first (instead of the one based on the extension). If + /// \c #kXMPFiles_OpenStrictly is set, then any format other than \c #kXMP_UnknownFile requires + /// that the file actually be that format; otherwise an exception is thrown. + /// + /// @param openFlags A set of option flags that describe the desired access. By default (zero) + /// the file is opened for read-only access and the format handler decides on the level of + /// reconciliation that will be performed. A logical OR of these bit-flag constants: + /// + /// \li \c #kXMPFiles_OpenForRead - Open for read-only access. + /// \li \c #kXMPFiles_OpenForUpdate - Open for reading and writing. + /// \li \c #kXMPFiles_OpenOnlyXMP - Only the XMP is wanted, no reconciliation. + /// \li \c #kXMPFiles_OpenStrictly - Be strict about locating XMP and reconciling with other + /// forms. By default, a best effort is made to locate the correct XMP and to reconcile XMP + /// with other forms (if reconciliation is done). This option forces stricter rules, resulting + /// in exceptions for errors. The definition of strictness is specific to each handler, there + /// might be no difference. + /// \li \c #kXMPFiles_OpenUseSmartHandler - Require the use of a smart handler. + /// \li \c #kXMPFiles_OpenUsePacketScanning - Force packet scanning, do not use a smart handler. + /// \li \c #kXMPFiles_OptimizeFileLayout - When updating a file, spend the effort necessary + /// to optimize file layout. + /// + /// @return True if the file is succesfully opened and attached to a file handler. False for + /// anticipated problems, such as passing \c #kXMPFiles_OpenUseSmartHandler but not having an + /// appropriate smart handler. Throws an exception for serious problems. + + bool OpenFile ( XMP_StringPtr filePath, + XMP_FileFormat format = kXMP_UnknownFile, + XMP_OptionBits openFlags = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c OpenFile() opens a file for metadata access, using a string object + /// + /// Overloads the basic form of the function, allowing you to pass a string object for the file + /// path. It is otherwise identical; see details in the canonical form. + + bool OpenFile ( const tStringObj & filePath, + XMP_FileFormat format = kXMP_UnknownFile, + XMP_OptionBits openFlags = 0 ); + + #if XMP_StaticBuild // ! Client XMP_IO objects can only be used in static builds. + // --------------------------------------------------------------------------------------------- + /// @brief \c OpenFile() opens a client-provided XMP_IO object for metadata access. + /// + /// Alternative to the basic form of the function, allowing you to pass an XMP_IO object for + /// client-managed I/O. + /// + + bool OpenFile ( XMP_IO * clientIO, + XMP_FileFormat format = kXMP_UnknownFile, + XMP_OptionBits openFlags = 0 ); + #endif + + // --------------------------------------------------------------------------------------------- + /// @brief CloseFile() explicitly closes an opened file. + /// + /// Performs any necessary output to the file and closes it. Files that are opened for update + /// are written to only when closing. + /// + /// If the file is opened for read-only access (passing \c #kXMPFiles_OpenForRead), the disk + /// file is closed immediately after reading the data from it; the \c XMPFiles object, however, + /// remains in the open state. You must call \c CloseFile() when finished using it. Other + /// methods, such as \c GetXMP(), can only be used between the \c OpenFile() and \c CloseFile() + /// calls. The \c XMPFiles destructor does not call \c CloseFile(); if you call it without closing, + /// any pending updates are lost. + /// + /// If the file is opened for update (passing \c #kXMPFiles_OpenForUpdate), the disk file remains + /// open until \c CloseFile() is called. The disk file is only updated once, when \c CloseFile() + /// is called, regardless of how many calls are made to \c PutXMP(). + /// + /// @param closeFlags Option flags for optional closing actions. This bit-flag constant is + /// defined: + /// + /// \li \c #kXMPFiles_UpdateSafely - Write into a temporary file then swap for crash safety. + + void CloseFile ( XMP_OptionBits closeFlags = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetFileInfo() retrieves basic information about an opened file. + /// + /// @param filePath [out] A buffer in which to return the path passed to \c OpenFile(). Can be + /// null if value is not wanted. + /// + /// @param openFlags [out] A buffer in which to return the option flags passed to + /// \c OpenFile(). Can be null if value is not wanted. + /// + /// @param format [out] A buffer in which to return the file format. Can be null if value is not + /// wanted. + /// @param handlerFlags [out] A buffer in which to return the handler's capability flags. Can + /// be null if value is not wanted. + /// + /// @return True if the file object is in the open state; that is, \c OpenFile() has been called + /// but \c CloseFile() has not. False otherwise. Even if the file object is open, the actual + /// disk file might be closed in the host file-system sense; see \c OpenFile(). + + bool GetFileInfo ( tStringObj * filePath = 0, + XMP_OptionBits * openFlags = 0, + XMP_FileFormat * format = 0, + XMP_OptionBits * handlerFlags = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetAbortProc() registers a callback function used to check for a user-signaled abort. + /// + /// The specified procedure is called periodically to allow a user to cancel time-consuming + /// operations. The callback function should return true to signal an abort, which results in an + /// exception being thrown. + /// + /// @param abortProc The callback function. + /// + /// @param abortArg A pointer to caller-defined data to pass to the callback function. + + void SetAbortProc ( XMP_AbortProc abortProc, + void * abortArg ); + + /// @} + + // ============================================================================================= + /// \name Accessing metadata + /// @{ + /// + /// These functions allow you to retrieve XMP metadata from open files, so that you can use the + /// \c TXMPMeta API to manipulate it. The \c PutXMP() functions update the XMP packet in memory. + /// Changed XMP is not actually written out to the file until the file is closed. + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetXMP() retrieves the XMP metadata from an open file. + /// + /// The function reports whether XMP is present in the file; you can choose to retrieve any or + /// all of the parsed XMP, the raw XMP packet,or information about the raw XMP packet. The + /// options provided when the file was opened determine if reconciliation is done with other + /// forms of metadata. + /// + /// @param xmpObj [out] An XMP object in which to return the parsed XMP metadata. Can be null. + /// + /// @param xmpPacket [out] An string object in which to return the raw XMP packet as stored in + /// the file. Can be null. The encoding of the packet is given in the \c packetInfo. Returns an + /// empty string if the low level file handler does not provide the raw packet. + /// + /// @param packetInfo [out] An string object in which to return the location and form of the raw + /// XMP in the file. \c #XMP_PacketInfo::charForm and \c #XMP_PacketInfo::writeable reflect the + /// raw XMP in the file. The parsed XMP property values are always UTF-8. The writeable flag is + /// taken from the packet trailer; it applies only to "format ignorant" writing. The + /// \c #XMP_PacketInfo structure always reflects the state of the XMP in the file. The offset, + /// length, and character form do not change as a result of calling \c PutXMP() unless the file + /// is also written. Some file handlers might not return location or contents of the raw packet + /// string. To determine whether one does, check the \c #kXMPFiles_ReturnsRawPacket bit returned + /// by \c GetFormatInfo(). If the low-level file handler does not provide the raw packet + /// location, \c #XMP_PacketInfo::offset and \c #XMP_PacketInfo::length are both 0, + /// \c #XMP_PacketInfo::charForm is UTF-8, and \c #XMP_PacketInfo::writeable is false. + /// + /// @return True if the file has XMP, false otherwise. + + bool GetXMP ( SXMPMeta * xmpObj = 0, + tStringObj * xmpPacket = 0, + XMP_PacketInfo * packetInfo = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c PutXMP() updates the XMP metadata in this object without writing out the file. + /// + /// This function supplies new XMP for the file. However, the disk file is not written until the + /// object is closed with \c CloseFile(). The options provided when the file was opened + /// determine if reconciliation is done with other forms of metadata. + /// + /// @param xmpObj The new metadata as an XMP object. + + void PutXMP ( const SXMPMeta & xmpObj ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c PutXMP() updates the XMP metadata in this object without writing out the file, + /// using a string object for input. + /// + /// Overloads the basic form of the function, allowing you to pass the metadata as a string object + /// instead of an XMP object. It is otherwise identical; see details in the canonical form. + /// + /// @param xmpPacket The new metadata as a string object containing a complete XMP packet. + + void PutXMP ( const tStringObj & xmpPacket ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c PutXMP() updates the XMP metadata in this object without writing out the file, + /// using a string object and optional length. + /// + /// Overloads the basic form of the function, allowing you to pass the metadata as a string object + /// instead of an XMP object. It is otherwise identical; see details in the canonical form. + /// + /// @param xmpPacket The new metadata as a const char * string containing an XMP packet. + /// + /// @param xmpLength Optional. The number of bytes in the string. If not supplied, the string is + /// assumed to be nul-terminated. + + void PutXMP ( XMP_StringPtr xmpPacket, + XMP_StringLen xmpLength = kXMP_UseNullTermination ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c CanPutXMP() reports whether this file can be updated with a specific XMP packet. + /// + /// Use to determine if the file can probably be updated with a given set of XMP metadata. This + /// depends on the size of the packet, the options with which the file was opened, and the + /// capabilities of the handler for the file format. The function obtains the length of the + /// serialized packet for the provided XMP, but does not keep it or modify it, and does not + /// cause the file to be written when closed. This is implemented roughly as follows: + /// + ///
+    /// bool CanPutXMP ( XMP_StringPtr xmpPacket )
+    /// {
+    ///    XMP_FileFormat format;
+    ///    this->GetFileInfo ( 0, &format, 0 );
+    ///
+    ///    XMP_OptionBits formatFlags;
+    ///    GetFormatInfo ( format, &formatFlags );
+    ///
+    ///    if ( (formatFlags & kXMPFiles_CanInjectXMP) && (formatFlags & kXMPFiles_CanExpand) ) return true;
+    ///
+    ///    XMP_PacketInfo packetInfo;
+    ///    bool hasXMP = this->GetXMP ( 0, 0, &packetInfo );
+    ///
+    ///    if ( ! hasXMP ) {
+    ///       if ( formatFlags & kXMPFiles_CanInjectXMP ) return true;
+    ///    } else {
+    ///       if ( (formatFlags & kXMPFiles_CanExpand) ||
+    ///            (packetInfo.length >= strlen(xmpPacket)) ) return true;
+    ///    }
+    ///    return false;
+    /// }
+    /// 
+ /// + /// @param xmpObj The proposed new metadata as an XMP object. + + bool CanPutXMP ( const SXMPMeta & xmpObj ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c CanPutXMP() reports whether this file can be updated with a specific XMP packet, + /// passed in a string object. + /// + /// Overloads the basic form of the function, allowing you to pass the metadata as a string object + /// instead of an XMP object. It is otherwise identical; see details in the canonical form. + /// + /// @param xmpPacket The proposed new metadata as a string object containing an XMP packet. + + bool CanPutXMP ( const tStringObj & xmpPacket ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c CanPutXMP() reports whether this file can be updated with a specific XMP packet, + /// passed in a string object. + /// + /// Overloads the basic form of the function, allowing you to pass the metadata as a string object + /// instead of an XMP object. It is otherwise identical; see details in the canonical form. + /// + /// @param xmpPacket The proposed new metadata as a const char * string containing an XMP packet. + /// + /// @param xmpLength Optional. The number of bytes in the string. If not supplied, the string + /// is assumed to be nul-terminated. + + bool CanPutXMP ( XMP_StringPtr xmpPacket, + XMP_StringLen xmpLength = kXMP_UseNullTermination ); + + /// @} + + // ============================================================================================= + /// \name Progress notifications + /// @{ + /// + /// These functions allow track the progress of file operations. Initially only file updates are + /// tracked, these all occur within calls to SXMPFiles::CloseFile. There are no plans to track + /// other operations at this time. Tracking support must be added to specific file handlers, + /// there are no guarantees about which handlers will have support. To simplify the logic only + /// file writes will be estimated and measured. + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetDefaultProgressCallback() sets a global default for progress tracking. This is + /// used as a default for XMPFiles (library) objects created after the default is set. This does + /// not affect the callback for new SXMPFiles (client) objects with an existing XMPFiles object. + /// + /// @param proc The client's callback function. Can be zero to disable notifications. + /// + /// @param context A pointer used to carry client-private context. + /// + /// @param interval The desired number of seconds between notifications. Ideally the first + /// notification is sent after this interval, then at each following multiple of this interval. + /// + /// @param sendStartStop A Boolean value indicating if initial and final notifications are + /// wanted in addition to those at the reporting intervals. + + static void SetDefaultProgressCallback ( XMP_ProgressReportProc proc, void * context = 0, + float interval = 1.0, bool sendStartStop = false ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetProgressCallback() sets the progress notification callback for the associated + /// XMPFiles (library) object. + /// + /// @param proc The client's callback function. Can be zero to disable notifications. + /// + /// @param context A pointer used to carry client-private context. + /// + /// @param interval The desired number of seconds between notifications. Ideally the first + /// notification is sent after this interval, then at each following multiple of this interval. + /// + /// @param sendStartStop A Boolean value indicating if initial and final notifications are + /// wanted in addition to those at the reporting intervals. + + void SetProgressCallback ( XMP_ProgressReportProc proc, void * context = 0, + float interval = 1.0, bool sendStartStop = false ); + + /// @} + + // ============================================================================================= + // Error notifications + // =================== + + // --------------------------------------------------------------------------------------------- + /// \name Error notifications + /// @{ + /// + /// From the beginning through version 5.5, XMP Toolkit errors result in throwing an \c XMP_Error + /// exception. For the most part exceptions were thrown early and thus API calls aborted as soon + /// as an error was detected. Starting in version 5.5, support has been added for notifications + /// of errors arising in calls to \c TXMPFiles functions. + /// + /// A client can register an error notification callback function for a \c TXMPFile object. This + /// can be done as a global default or individually to each object. The global default applies + /// to all objects created after it is registered. Within the object there is no difference + /// between the global default or explicitly registered callback. The callback function returns + /// a \c bool value indicating if recovery should be attempted (true) or an exception thrown + /// (false). If no callback is registered, a best effort at recovery and continuation will be + /// made with an exception thrown if recovery is not possible. + /// + /// The number of notifications delivered for a given TXMPFiles object can be limited. This is + /// intended to reduce chatter from multiple or cascading errors. The limit is set when the + /// callback function is registered. This limits the number of notifications of the highest + /// severity delivered or less. If a higher severity error occurs, the counting starts again. + /// The limit and counting can be reset at any time, see \c ResetErrorCallbackLimit. + + // -------------------------------------------------------------------------------------------- + /// @brief SetDefaultErrorCallback() registers a global default error notification callback. + /// + /// @param proc The client's callback function. + /// + /// @param context Client-provided context for the callback. + /// + /// @param limit A limit on the number of notifications to be delivered. + + static void SetDefaultErrorCallback ( XMPFiles_ErrorCallbackProc proc, void* context = 0, XMP_Uns32 limit = 1 ); + + // -------------------------------------------------------------------------------------------- + /// @brief SetErrorCallback() registers an error notification callback. + /// + /// @param proc The client's callback function. + /// + /// @param context Client-provided context for the callback. + /// + /// @param limit A limit on the number of notifications to be delivered. + + void SetErrorCallback ( XMPFiles_ErrorCallbackProc proc, void* context = 0, XMP_Uns32 limit = 1 ); + + // -------------------------------------------------------------------------------------------- + /// @brief ResetErrorCallbackLimit() resets the error notification limit and counting. It has no + /// effect if an error notification callback function is not registered. + /// + /// @param limit A limit on the number of notifications to be delivered. + + void ResetErrorCallbackLimit ( XMP_Uns32 limit = 1 ); + + /// @} + + // ============================================================================================= + +private: + + XMPFilesRef xmpFilesRef; + + // These are used as callbacks from the library code to the client when returning values that + // involve heap allocations. This ensures the allocations occur within the client. + static void SetClientString ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen ); + static void SetClientStringVector ( void * clientPtr, XMP_StringPtr* arrayPtr, XMP_Uns32 stringCount ); +}; // class TXMPFiles + +// ================================================================================================= + +#endif // __TXMPFiles_hpp__ diff --git a/xmp/TXMPIterator.hpp b/xmp/TXMPIterator.hpp new file mode 100644 index 0000000..603db68 --- /dev/null +++ b/xmp/TXMPIterator.hpp @@ -0,0 +1,235 @@ +#ifndef __TXMPIterator_hpp__ +#define __TXMPIterator_hpp__ 1 + +#if ( ! __XMP_hpp__ ) + #error "Do not directly include, use XMP.hpp" +#endif + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +// ================================================================================================= +/// \file TXMPIterator.hpp +/// \brief API for access to the XMP Toolkit iteration services. +/// +/// \c TXMPIterator is the template class providing iteration services for the XMP Toolkit. It must +/// be instantiated with a string class such as \c std::string. See the instructions in XMP.hpp, and +/// the Overview for a discussion of the overall architecture of the XMP API. +// ================================================================================================= + +// ================================================================================================= +/// \class TXMPIterator TXMPIterator.hpp +/// @brief API for access to the XMP Toolkit iteration services. +/// +/// \c TXMPIterator provides a uniform means to iterate over the schema and properties within an XMP +/// object. \c TXMPIterator is a template class which must be instantiated with a string class such +/// as \c std::string. See the instructions in XMP.hpp, and the Overview for a discussion of the +/// overall architecture of the XMP API. Access these functions through the concrete class, +/// \c SXMPIterator. +/// +/// @note Only XMP object iteration is currently available. Future development may include iteration +/// over global tables, such as registered namespaces. +/// +/// To understand how iteration works, you should have a thorough understanding of the XMP data +/// tree, as described in the XMP Specification Part 1. You might also find it helpful to create +/// some complex XMP and examine the output of \c TXMPMeta::DumpObject(). +/// +/// \li The top of the XMP data tree is a single root node. This does not explicitly appear in the +/// dump and is never visited by an iterator; that is, it is never returned from +/// \c TXMPIterator::Next(). +/// +/// \li Beneath the root are schema nodes; these collect the top-level properties in the same +/// namespace. They are created and destroyed implicitly. +/// +/// \li Beneath the schema nodes are the property nodes. The nodes below a property node depend on +/// its type (simple, struct, or array) and whether it has qualifiers. +/// +/// A \c TXMPIterator constructor defines a starting point for the iteration, and options that +/// control how it proceeds. By default, iteration starts at the root and visits all nodes beneath +/// it in a depth-first manner. The root node iteself is not visited; the first visited node is a +/// schema node. You can provide a schema name or property path to select a different starting node. +/// By default, this visits the named root node first then all nodes beneath it in a depth-first +/// manner. +/// +/// The function \c TXMPIterator::Next() delivers the schema URI, path, and option flags for the +/// node being visited. If the node is simple, it also delivers the value. Qualifiers for this node +/// are visited next. The fields of a struct or items of an array are visited after the qualifiers +/// of the parent. +/// +/// You can specify options when contructing the iteration object to control how the iteration is +/// performed. +/// +/// \li \c #kXMP_IterJustChildren - Visit just the immediate children of the root. Skip the root +/// itself and all nodes below the immediate children. This omits the qualifiers of the immediate +/// children, the qualifier nodes being below what they qualify. +/// \li \c #kXMP_IterJustLeafNodes - Visit just the leaf property nodes and their qualifiers. +/// \li \c #kXMP_IterJustLeafName - Return just the leaf component of the node names. The default +/// is to return the full path name. +/// \li \c #kXMP_IterOmitQualifiers - Do not visit the qualifiers of a node. +// ================================================================================================= + +#include "client-glue/WXMPIterator.hpp" + +template class TXMPIterator { + +public: + + // --------------------------------------------------------------------------------------------- + /// @brief Assignment operator, assigns the internal ref and increments the ref count. + /// + /// Assigns the internal reference from an existing object and increments the reference count on + /// the underlying internal XMP iterator. + /// + /// @param rhs An existing iteration object. + + void operator= ( const TXMPIterator & rhs ); + + // --------------------------------------------------------------------------------------------- + /// @brief Copy constructor, creates a client object refering to the same internal object. + /// + /// Creates a new client iterator that refers to the same underlying iterator as an existing object. + /// + /// @param original An existing iteration object to copy. + + TXMPIterator ( const TXMPIterator & original ); + + // --------------------------------------------------------------------------------------------- + /// @brief Constructs an iterator for properties within a schema in an XMP object. + /// + /// See the class description for the general operation of an XMP object iterator. + /// Overloaded forms are provided to iterate the entire data tree, + /// a subtree rooted at a specific node, or properties within a specific schema. + /// + /// @param xmpObj The XMP object over which to iterate. + /// + /// @param schemaNS Optional schema namespace URI to restrict the iteration. To visit all of the + /// schema, pass 0 or the empty string "". + /// + /// @param propName Optional property name to restrict the iteration. May be an arbitrary path + /// expression. If provided, a schema URI must also be provided. To visit all properties, pass 0 + /// or the empty string "". + /// + /// @param options Option flags to control the iteration. A logical OR of these bit flag constants: + /// \li \c #kXMP_IterJustChildren - Visit only the immediate children of the root; default visits subtrees. + /// \li \c #kXMP_IterJustLeafNodes - Visit only the leaf nodes; default visits all nodes. + /// \li \c #kXMP_IterJustLeafName - Return just the leaf part of the path; default returns the full path. + /// \li \c #kXMP_IterOmitQualifiers - Omit all qualifiers. + /// + /// @return The new TXMPIterator object. + + TXMPIterator ( const TXMPMeta & xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief Constructs an iterator for a subtree of properties within an XMP object. + /// + /// See the class description for the general operation of an XMP object iterator. Overloaded + /// forms are provided to iterate the entire data tree, a subtree rooted at a specific node, or + /// properties within a specific schema. + /// + /// @param xmpObj The XMP object over which to iterate. + /// + /// @param schemaNS Optional schema namespace URI to restrict the iteration. To visit all of the + /// schema, pass 0 or the empty string "". + /// + /// @param options Option flags to control the iteration. A logical OR of these bit flag constants: + /// \li \c #kXMP_IterJustChildren - Visit only the immediate children of the root; default visits subtrees. + /// \li \c #kXMP_IterJustLeafNodes - Visit only the leaf nodes; default visits all nodes. + /// \li \c #kXMP_IterJustLeafName - Return just the leaf part of the path; default returns the full path. + /// \li \c #kXMP_IterOmitQualifiers - Omit all qualifiers. + /// + /// @return The new TXMPIterator object. + + TXMPIterator ( const TXMPMeta & xmpObj, + XMP_StringPtr schemaNS, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief Constructs an iterator for the entire data tree within an XMP object. + /// + /// See the class description for the general operation of an XMP object iterator. Overloaded + /// forms are provided to iterate the entire data tree, a subtree rooted at a specific node, or + /// properties within a specific schema. + /// + /// @param xmpObj The XMP object over which to iterate. + /// + /// @param options Option flags to control the iteration. A logical OR of these bit flag constants: + /// \li \c #kXMP_IterJustChildren - Visit only the immediate children of the root; default visits subtrees. + /// \li \c #kXMP_IterJustLeafNodes - Visit only the leaf nodes; default visits all nodes. + /// \li \c #kXMP_IterJustLeafName - Return just the leaf part of the path; default returns the full path. + /// \li \c #kXMP_IterOmitQualifiers - Omit all qualifiers. + /// + /// @return The new \c TXMPIterator object. + + TXMPIterator ( const TXMPMeta & xmpObj, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief Constructs an iterator for the global tables of the XMP toolkit. Not implemented. + + TXMPIterator ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_OptionBits options ); + + // --------------------------------------------------------------------------------------------- + /// @brief Destructor, typical virtual destructor. + + virtual ~TXMPIterator() throw(); + + // --------------------------------------------------------------------------------------------- + /// @brief \c Next() visits the next node in the iteration. + /// + /// Proceeds to the next node according to the options specified on creation of this object, and + /// delivers the schema URI, path, and option flags for the node being visited. If the node is + /// simple, it also delivers the value. + /// + /// @param schemaNS [out] A string object in which to return the assigned the schema namespace + /// URI of the current property. Can be null if the value is not wanted. + /// + /// @param propPath [out] A string object in which to return the XPath name of the current + /// property. Can be null if the value is not wanted. + /// + /// @param propValue [out] A string object in which to return the value of the current + /// property. Can be null if the value is not wanted. + /// + /// @param options [out] A buffer in which to return the flags describing the current property, + /// which are a logical OR of \c #XMP_OptionBits bit-flag constants. + /// + /// @return True if there was another node to visit, false if the iteration is complete. + + bool Next ( tStringObj * schemaNS = 0, + tStringObj * propPath = 0, + tStringObj * propValue = 0, + XMP_OptionBits * options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c Skip() skips some portion of the remaining iterations. + /// + /// @param options Option flags to control the iteration, a logical OR of these bit-flag + /// constants: + /// \li \c #kXMP_IterSkipSubtree - Skip the subtree below the current node. + /// \li \c #kXMP_IterSkipSiblings - Skip the subtree below and remaining siblings of the current node. + + void Skip ( XMP_OptionBits options ); + +private: + + XMPIteratorRef iterRef; + + TXMPIterator(); // ! Hidden, must choose property or table iteration. + + static void SetClientString ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen ); + +}; // class TXMPIterator + +// ================================================================================================= + +#endif // __TXMPIterator_hpp__ diff --git a/xmp/TXMPMeta.hpp b/xmp/TXMPMeta.hpp new file mode 100644 index 0000000..5a3e576 --- /dev/null +++ b/xmp/TXMPMeta.hpp @@ -0,0 +1,1756 @@ +#ifndef __TXMPMeta_hpp__ +#define __TXMPMeta_hpp__ 1 + +#if ( ! __XMP_hpp__ ) + #error "Do not directly include, use XMP.hpp" +#endif + +#include "XMPCore/XMPCoreDefines.h" +#if ENABLE_CPP_DOM_MODEL + #include "XMPCore/XMPCoreFwdDeclarations.h" +#endif + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +// ================================================================================================= +/// \file TXMPMeta.hpp +/// \brief API for access to the XMP Toolkit core services. +/// +/// \c TXMPMeta is the template class providing the core services of the XMP Toolkit. It must be +/// instantiated with a string class such as \c std::string. Read the Toolkit Overview for +/// information about the overall architecture of the XMP API, and the documentation for \c XMP.hpp +/// for specific instantiation instructions. Please that you MUST NOT derive a class from this class, +/// consider this class FINAL, use it directly. [1279031] +/// +/// Access these functions through the concrete class, \c SXMPMeta. +// ================================================================================================= + +// ================================================================================================= +/// \class TXMPMeta TXMPMeta.hpp +/// \brief API for access to the XMP Toolkit core services. +/// +/// \c TXMPMeta is the template class providing the core services of the XMP Toolkit. It should be +/// instantiated with a string class such as \c std::string. Read the Toolkit Overview for +/// information about the overall architecture of the XMP API, and the documentation for \c XMP.hpp +/// for specific instantiation instructions. +/// +/// Access these functions through the concrete class, \c SXMPMeta. +/// +/// You can create \c TXMPMeta objects (also called XMP objects) from metadata that you construct, +/// or that you obtain from files using the XMP Toolkit's XMPFiles component; see \c TXMPFiles.hpp. +// ================================================================================================= + +template class TXMPIterator; +template class TXMPUtils; + +// ------------------------------------------------------------------------------------------------- + +template class TXMPMeta { + +public: + + // ============================================================================================= + // Initialization and termination + // ============================== + + // --------------------------------------------------------------------------------------------- + /// \name Initialization and termination + /// + /// @{ + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetVersionInfo() retrieves runtime version information. + /// + /// The header \c XMPVersion.hpp defines a static version number for the XMP Toolkit, which + /// describes the version of the API used at client compile time. It is not necessarily the same + /// as the runtime version. Do not base runtime decisions on the static version alone; you can, + /// however, compare the runtime and static versions. + /// + /// This function is static; make the call directly from the concrete class (\c SXMPMeta). The + /// function can be called before calling \c TXMPMeta::Initialize(). + /// + /// @param info [out] A buffer in which to return the version information. + + static void GetVersionInfo ( XMP_VersionInfo * info ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c Initialize() explicitly initializes the XMP Toolkit before use. */ + + /// Initializes the XMP Toolkit. + /// + /// Call this function before making any other calls to the \c TXMPMeta functions, except + /// \c TXMPMeta::GetVersionInfo(). + /// + /// This function is static; make the call directly from the concrete class (\c SXMPMeta). + /// + /// @return True on success. */ + static bool Initialize(); + // --------------------------------------------------------------------------------------------- + /// @brief \c Terminate() explicitly terminates usage of the XMP Toolkit. + /// + /// Frees structures created on initialization. + /// + /// This function is static; make the call directly from the concrete class (\c SXMPMeta). + + static void Terminate(); + + /// @} + + // ============================================================================================= + // Constuctors and destructor + // ========================== + + // --------------------------------------------------------------------------------------------- + /// \name Constructors and destructor + /// @{ + + // --------------------------------------------------------------------------------------------- + /// @brief Default constructor, creates an empty object. + /// + /// The default constructor creates a new empty \c TXMPMeta object. + /// + /// @return The new object. */ + TXMPMeta(); + + // --------------------------------------------------------------------------------------------- + /// @brief Copy constructor, creates a client object refering to the same internal object. + /// + /// The copy constructor creates a new \c TXMPMeta object that refers to the same internal XMP + /// object. as an existing \c TXMPMeta object. + /// + /// @param original The object to copy. + /// + /// @return The new object. */ + + TXMPMeta ( const TXMPMeta & original ); + + // --------------------------------------------------------------------------------------------- + /// @brief Assignment operator, assigns the internal reference and increments the reference count. + /// + /// The assignment operator assigns the internal ref from the rhs object and increments the + /// reference count on the underlying internal XMP object. + + void operator= ( const TXMPMeta & rhs ); + + // --------------------------------------------------------------------------------------------- + /// @brief Reconstructs an XMP object from an internal reference. + /// + /// This constructor creates a new \c TXMPMeta object that refers to the underlying reference object + /// of an existing \c TXMPMeta object. Use to safely pass XMP objects across DLL boundaries. + /// + /// @param xmpRef The underlying reference object, obtained from some other XMP object with + /// \c TXMPMeta::GetInternalRef(). + /// + /// @return The new object. + + TXMPMeta ( XMPMetaRef xmpRef ); + + // --------------------------------------------------------------------------------------------- + /// @brief Constructs an object and parse one buffer of RDF into it. + /// + /// This constructor creates a new \c TXMPMeta object and populates it with metadata from a + /// buffer containing serialized RDF. This buffer must be a complete RDF parse stream. + /// + /// The result of passing serialized data to this function is identical to creating an empty + /// object then calling \c TXMPMeta::ParseFromBuffer(). To use the constructor, however, the RDF + /// must be complete. If you need to parse data from multiple buffers, create an empty object + /// and use \c TXMPMeta::ParseFromBuffer(). + /// + /// @param buffer A pointer to the buffer of RDF to be parsed. Can be null if the length is 0; + /// in this case, the function creates an empty object. + /// + /// @param xmpSize The length in bytes of the buffer. + /// + /// @return The new object. + + TXMPMeta ( XMP_StringPtr buffer, + XMP_StringLen xmpSize ); + + // --------------------------------------------------------------------------------------------- + /// @brief Destructor, typical virtual destructor. */ + virtual ~TXMPMeta() throw(); + + /// @} + + // ============================================================================================= + // Global state functions + // ====================== + + // --------------------------------------------------------------------------------------------- + /// \name Global option flags + /// @{ + /// Global option flags affect the overall behavior of the XMP Toolkit. The available options + /// will be declared in \c XMP_Const.h. There are none in this version of the Toolkit. + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetGlobalOptions() retrieves the set of global option flags. There are none in + /// this version of the Toolkit. + /// + /// This function is static; you can make the call from the class without instantiating it. + /// + /// @return A logical OR of global option bit-flag constants. + + static XMP_OptionBits GetGlobalOptions(); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetGlobalOptions() updates the set of global option flags. There are none in this + /// version of the Toolkit. + /// + /// The entire set is replaced with the new values. If only one flag is to be modified, use + /// \c TXMPMeta::GetGlobalOptions() to obtain the current set, modify the desired flag, then use + /// this function to reset the value. + /// + /// This function is static; you can make the call from the class without instantiating it. + /// + /// @param options A logical OR of global option bit-flag constants. + + static void SetGlobalOptions ( XMP_OptionBits options ); + + /// @} + + // --------------------------------------------------------------------------------------------- + /// \name Internal data structure dump utilities + /// @{ + /// + /// These are debugging utilities that dump internal data structures, to be handled by + /// client-defined callback described in \c XMP_Const.h. + /// + /// @see Member function \c TXMPMeta::DumpObject() + + // --------------------------------------------------------------------------------------------- + /// @brief \c DumpNamespaces() sends the list of registered namespace URIs and prefixes to a handler. + /// + /// For debugging. Invokes a client-defined callback for each line of output. + /// + /// This function is static; make the call directly from the concrete class (\c SXMPMeta). + /// + /// @param outProc The client-defined procedure to handle each line of output. + /// + /// @param clientData A pointer to client-defined data to pass to the handler. + /// + /// @return A success-fail status value, returned from the handler. Zero is success, failure + /// values are client-defined. + + static XMP_Status DumpNamespaces ( XMP_TextOutputProc outProc, + void * clientData ); + + /// @} + + // --------------------------------------------------------------------------------------------- + /// \name Namespace Functions + /// @{ + /// + /// Namespaces must be registered before use in namespace URI parameters or path expressions. + /// Within the XMP Toolkit the registered namespace URIs and prefixes must be unique. Additional + /// namespaces encountered when parsing RDF are automatically registered. + /// + /// The namespace URI should always end in an XML name separator such as '/' or '#'. This is + /// because some forms of RDF shorthand catenate a namespace URI with an element name to form a + /// new URI. + + // --------------------------------------------------------------------------------------------- + /// @brief \c RegisterNamespace() registers a namespace URI with a suggested prefix. + /// + /// If the URI is not registered but the suggested prefix is in use, a unique prefix is created + /// from the suggested one. The actual registered prefix is returned. The function result tells + /// if the registered prefix is the suggested one. It is not an error if the URI is already + /// registered, regardless of the prefix. + /// + /// This function is static; make the call directly from the concrete class (\c SXMPMeta). + /// + /// @param namespaceURI The URI for the namespace. Must be a valid XML URI. + /// + /// @param suggestedPrefix The suggested prefix to be used if the URI is not yet registered. + /// Must be a valid XML name. + /// + /// @param registeredPrefix [out] A string object in which to return the prefix actually + /// registered for this URI. + /// + /// @return True if the registered prefix matches the suggested prefix. + /// + /// @note No checking is done on either the URI or the prefix. */ + + static bool RegisterNamespace ( XMP_StringPtr namespaceURI, + XMP_StringPtr suggestedPrefix, + tStringObj * registeredPrefix ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetNamespacePrefix() obtains the prefix for a registered namespace URI, and + /// reports whether the URI is registered. + /// + /// This function is static; make the call directly from the concrete class (\c SXMPMeta). + /// + /// @param namespaceURI The URI for the namespace. Must not be null or the empty string. It is + /// not an error if the namespace URI is not registered. + /// + /// @param namespacePrefix [out] A string object in which to return the prefix registered for + /// this URI, with a terminating colon character, ':'. If the namespace is not registered, this + /// string is not modified. + /// + /// @return True if the namespace URI is registered. + + static bool GetNamespacePrefix ( XMP_StringPtr namespaceURI, + tStringObj * namespacePrefix ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetNamespaceURI() obtains the URI for a registered namespace prefix, and reports + /// whether the prefix is registered. + /// + /// This function is static; make the call directly from the concrete class (\c SXMPMeta). + /// + /// @param namespacePrefix The prefix for the namespace. Must not be null or the empty string. + /// It is not an error if the namespace prefix is not registered. + /// + /// @param namespaceURI [out] A string object in which to return the URI registered for this + /// prefix. If the prefix is not registered, this string is not modified. + /// + /// @return True if the namespace prefix is registered. + + static bool GetNamespaceURI ( XMP_StringPtr namespacePrefix, + tStringObj * namespaceURI ); + + // --------------------------------------------------------------------------------------------- + /// @brief Not implemented. + /// + /// Deletes a namespace from the registry. Does nothing if the URI is not registered, or if the + /// parameter is null or the empty string. + /// + /// This function is static; make the call directly from the concrete class (\c SXMPMeta). + /// + /// @param namespaceURI The URI for the namespace. + + static void DeleteNamespace ( XMP_StringPtr namespaceURI ); + + /// @} + + // ============================================================================================= + // Basic property manipulation functions + // ===================================== + + // *** Should add discussion of schemaNS and propName prefix usage. + + // --------------------------------------------------------------------------------------------- + /// \name Accessing property values + /// @{ + /// + /// The property value accessors all take a property specification; the top level namespace URI + /// (the "schema" namespace) and the basic name of the property being referenced. See the + /// introductory discussion of path expression usage for more information. + /// + /// The accessor functions return true if the specified property exists. If it does, output + /// parameters return the value (if any) and option flags describing the property. The option + /// bit-flag constants that describe properties are \c kXMP_PropXx and + /// \c kXMP_ArrayIsXx. See \c #kXMP_PropValueIsURI and following, and macros \c #XMP_PropIsSimple + /// and following in \c XMP_Const.h. If the property exists and has a value, it is returned as a + /// Unicode string in UTF-8 encoding. Arrays and the non-leaf levels of structs do not have + /// values. + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetProperty() reports whether a property exists, and retrieves its value. + /// + /// This is the simplest property accessor. Use this to retrieve the values of top-level simple + /// properties, or after using the path composition functions in \c TXMPUtils. + /// + /// When specifying a namespace and path (in this and all other accessors): + /// \li If a namespace URI is specified, it must be for a registered namespace. + /// \li If the namespace is specified only by a prefix in the property name path, + /// it must be a registered prefix. + /// \li If both a URI and path prefix are present, they must be corresponding + /// parts of a registered namespace. + /// + /// @param schemaNS The namespace URI for the property. The URI must be for a registered + /// namespace. Must not be null or the empty string. + /// + /// @param propName The name of the property. Can be a general path expression, must not be null + /// or the empty string. The first component can be a namespace prefix; if present without a + /// \c schemaNS value, the prefix specifies the namespace. The prefix must be for a registered + /// namespace, and if a namespace URI is specified, must match the registered prefix for that + /// namespace. + /// + /// @param propValue [out] A string object in which to return the value of the property, if the + /// property exists and has a value. Arrays and non-leaf levels of structs do not have values. + /// Can be null if the value is not wanted. + /// + /// @param options A buffer in which to return option flags describing the property. Can be null + /// if the flags are not wanted. + /// + /// @return True if the property exists. + + bool GetProperty ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + tStringObj * propValue, + XMP_OptionBits * options ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetArrayItem() provides access to items within an array. + /// + /// Reports whether the item exists; if it does, and if it has a value, the function retrieves + /// the value. Items are accessed by an integer index, where the first item has index 1. + /// + /// @param schemaNS The namespace URI for the array; see \c GetProperty(). + /// + /// @param arrayName The name of the array. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param itemIndex The 1-based index of the desired item. Use the macro \c #kXMP_ArrayLastItem + /// to specify the last existing array item. + /// + /// @param itemValue [out] A string object in which to return the value of the array item, if it + /// has a value. Arrays and non-leaf levels of structs do not have values. Can be null if the + /// value is not wanted. + /// + /// @param options [out] A buffer in which to return the option flags describing the array item. + /// Can be null if the flags are not wanted. + /// + /// @return True if the array item exists. + + bool GetArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + tStringObj * itemValue, + XMP_OptionBits * options ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetStructField() provides access to fields within a nested structure. + /// + /// Reports whether the field exists; if it does, and if it has a value, the function retrieves + /// the value. + /// + /// @param schemaNS The namespace URI for the struct; see \c GetProperty(). + /// + /// @param structName The name of the struct. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param fieldNS The namespace URI for the field. Same URI and prefix usage as the \c schemaNS + /// and \c structName parameters. + /// + /// @param fieldName The name of the field. Must be a single XML name, must not be null or the + /// empty string. Same URI and prefix usage as the \c schemaNS and \c structName parameters. + /// + /// @param fieldValue [out] A string object in which to return the value of the field, if the + /// field has a value. Arrays and non-leaf levels of structs do not have values. Can be null if + /// the value is not wanted. + /// + /// @param options [out] A buffer in which to return the option flags describing the field. Can + /// be null if the flags are not wanted. + /// + /// @return True if the field exists. + + bool GetStructField ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + tStringObj * fieldValue, + XMP_OptionBits * options ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetQualifier() provides access to a qualifier attached to a property. + /// + /// @note In this version of the Toolkit, qualifiers are supported only for simple leaf properties. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property to which the qualifier is attached. Can be a + /// general path expression, must not be null or the empty string; see \c GetProperty() for + /// namespace prefix usage. + /// + /// @param qualNS The namespace URI for the qualifier. Same URI and prefix usage as the + /// \c schemaNS and \c propName parameters. + /// + /// @param qualName The name of the qualifier. Must be a single XML name, must not be null or + /// the empty string. Same URI and prefix usage as the \c schemaNS and \c propName parameters. + /// + /// @param qualValue [out] A string object in which to return the value of the qualifier, if the + /// qualifier has a value. Arrays and non-leaf levels of structs do not have values. Can be null + /// if the value is not wanted. + /// + /// @param options [out] A buffer in which to return the option flags describing the qualifier. + /// Can be null if the flags are not wanted. + /// + /// @return True if the qualifier exists. + + bool GetQualifier ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + tStringObj * qualValue, + XMP_OptionBits * options ) const; + + /// @} + + // ============================================================================================= + + // --------------------------------------------------------------------------------------------- + /// \name Creating properties and setting their values + /// @{ + /// + /// These functions all take a property specification; the top level namespace URI (the "schema" + /// namespace) and the basic name of the property being referenced. See the introductory + /// discussion of path expression usage for more information. + /// + /// All of the functions take a UTF-8 encoded Unicode string for the property value. Arrays and + /// non-leaf levels of structs do not have values. The value can be passed as an + /// \c #XMP_StringPtr (a pointer to a null-terminated string), or as a string object + /// (\c tStringObj). + + /// Each function takes an options flag that describes the property. You can use these functions + /// to create empty arrays and structs by setting appropriate option flags. When you assign a + /// value, all levels of a struct that are implicit in the assignment are created if necessary. + /// \c TXMPMeta::AppendArrayItem() implicitly creates the named array if necessary. + /// + /// The allowed option bit-flags include: + /// \li \c #kXMP_PropValueIsStruct - Can be used to create an empty struct. + /// A struct is implicitly created when the first field is set. + /// \li \c #kXMP_PropValueIsArray - By default, a general unordered array (bag). + /// \li \c #kXMP_PropArrayIsOrdered - An ordered array. + /// \li \c #kXMP_PropArrayIsAlternate - An alternative array. + /// \li \c #kXMP_PropArrayIsAltText - An alt-text array. Each array element must + /// be a simple property with an \c xml:lang attribute. + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetProperty() creates or sets a property value. + /// + /// This is the simplest property setter. Use it for top-level simple properties, or after using + /// the path composition functions in \c TXMPUtils. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param propValue The new value, a pointer to a null terminated UTF-8 string. Must be null + /// for arrays and non-leaf levels of structs that do not have values. + /// + /// @param options Option flags describing the property; a logical OR of allowed bit-flag + /// constants; see \c #kXMP_PropValueIsStruct and following. Must match the type of a property + /// that already exists. + + void SetProperty ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr propValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetProperty() creates or sets a property value using a string object. + /// + /// Overloads the basic form of the function, allowing you to pass a string object + /// for the item value. It is otherwise identical; see details in the canonical form. + + void SetProperty ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + const tStringObj & propValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetArrayItem() creates or sets the value of an item within an array. + /// + /// Items are accessed by an integer index, where the first item has index 1. This function + /// creates the item if necessary, but the array itself must already exist Use + /// \c AppendArrayItem() to create arrays. A new item is automatically appended if the index is the + /// array size plus 1. To insert a new item before or after an existing item, use option flags. + /// + /// Use \c TXMPUtils::ComposeArrayItemPath() to create a complex path. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param arrayName The name of the array. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param itemIndex The 1-based index of the desired item. Use the macro \c #kXMP_ArrayLastItem + /// to specify the last existing array item. + /// + /// @param itemValue The new item value, a null-terminated UTF-8 string, if the array item has a + /// value. + /// + /// @param options Option flags describing the array type and insertion location for a new item; + /// a logical OR of allowed bit-flag constants. The type, if specified, must match the existing + /// array type, \c #kXMP_PropArrayIsOrdered, \c #kXMP_PropArrayIsAlternate, or + /// \c #kXMP_PropArrayIsAltText. Default (0 or \c #kXMP_NoOptions) matches the existing array type. + /// + /// To insert a new item before or after the specified index, set flag \c #kXMP_InsertBeforeItem + /// or \c #kXMP_InsertAfterItem. + + void SetArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + XMP_StringPtr itemValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetArrayItem() creates or sets the value of an item within an array using a string object. + /// + /// Overloads the basic form of the function, allowing you to pass a string object in which to + /// return the item value. It is otherwise identical; see details in the canonical form. + + void SetArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + const tStringObj & itemValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c AppendArrayItem() adds an item to an array, creating the array if necessary. + /// + /// This function simplifies construction of an array by not requiring that you pre-create an + /// empty array. The array that is assigned is created automatically if it does not yet exist. + /// If the array exists, it must have the form specified by the options. Each call appends a new + /// item to the array. + /// + /// Use \c TXMPUtils::ComposeArrayItemPath() to create a complex path. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param arrayName The name of the array. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param arrayOptions Option flags describing the array type to create; a logical OR of + /// allowed bit-flag constants, \c #kXMP_PropArrayIsOrdered, \c #kXMP_PropArrayIsAlternate, or + /// \c #kXMP_PropArrayIsAltText. If the array exists, must match the existing array type or be + /// null (0 or \c #kXMP_NoOptions). + /// + /// @param itemValue The new item value, a null-terminated UTF-8 string, if the array item has a + /// value. + /// + /// @param itemOptions Option flags describing the item type to create; one of the bit-flag + /// constants \c #kXMP_PropValueIsArray or \c #kXMP_PropValueIsStruct to create a complex array + /// item. + + void AppendArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits arrayOptions, + XMP_StringPtr itemValue, + XMP_OptionBits itemOptions = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c AppendArrayItem() adds an item to an array using a string object value, creating + /// the array if necessary. + /// + /// Overloads the basic form of the function, allowing you to pass a string object in which to + /// return the item value. It is otherwise identical; see details in the canonical form. + + void AppendArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits arrayOptions, + const tStringObj & itemValue, + XMP_OptionBits itemOptions = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetStructField() creates or sets the value of a field within a nested structure. + /// + /// Use this to set a value within an existing structure, create a new field within an existing + /// structure, or create an empty structure of any depth. If you set a field in a structure that + /// does not exist, the structure is automatically created. + /// + /// Use \c TXMPUtils::ComposeStructFieldPath() to create a complex path. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param structName The name of the struct. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param fieldNS The namespace URI for the field. Same namespace and prefix usage as + /// \c GetProperty(). + /// + /// @param fieldName The name of the field. Must be a single XML name, must not be null or the + /// empty string. Same namespace and prefix usage as \c GetProperty(). + /// + /// @param fieldValue The new value, a null-terminated UTF-8 string, if the field has a value. + /// Null to create a new, empty struct or empty field in an existing struct. + /// + /// @param options Option flags describing the property, in which the bit-flag + /// \c #kXMP_PropValueIsStruct must be set to create a struct. + + void SetStructField ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + XMP_StringPtr fieldValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetStructField() creates or sets the value of a field within a nested structure, + /// using a string object. + /// + /// Overloads the basic form of the function, allowing you to pass a string object in which to + /// return the field value. It is otherwise identical; see details in the canonical form. + + void SetStructField ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + const tStringObj & fieldValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetQualifier() creates or sets a qualifier attached to a property. + /// + /// Use this to set a value for an existing qualifier, or create a new qualifier. <> Use + /// \c TXMPUtils::ComposeQualifierPath() to create a complex path. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property to which the qualifier is attached. Can be a + /// general path expression, must not be null or the empty string; see \c GetProperty() for + /// namespace prefix usage. + /// + /// @param qualNS The namespace URI for the qualifier. Same namespace and prefix usage as + /// \c GetProperty(). + /// + /// @param qualName The name of the qualifier. Must be a single XML name, must not be null or + /// the empty string. Same namespace and prefix usage as \c GetProperty(). + /// + /// @param qualValue The new value, a null-terminated UTF-8 string, if the qualifier has a + /// value. Null to create a new, empty qualifier. + /// + /// @param options Option flags describing the <>, a logical OR + /// of property-type bit-flag constants. Use the macro \c #XMP_PropIsQualifier to create a + /// qualifier. <> + + void SetQualifier ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + XMP_StringPtr qualValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetQualifier() creates or sets a qualifier attached to a property using a string object. + /// + /// Overloads the basic form of the function, allowing you to pass a string object + /// for the qualifier value. It is otherwise identical; see details in the canonical form. + + void SetQualifier ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + const tStringObj & qualValue, + XMP_OptionBits options = 0 ); + + /// @} + + // ============================================================================================= + + // --------------------------------------------------------------------------------------------- + /// \name Detecting and deleting properties. + /// @{ + /// + /// The namespace URI and prefix usage for property specifiers in these functions is the same as + /// for \c TXMPMeta::GetProperty(). + + // --------------------------------------------------------------------------------------------- + /// @brief \c DeleteProperty() deletes an XMP subtree rooted at a given property. + /// + /// It is not an error if the property does not exist. + /// + /// @param schemaNS The namespace URI for the property; see \c GetProperty(). + /// + /// @param propName The name of the property; see \c GetProperty(). + + void DeleteProperty ( XMP_StringPtr schemaNS, + XMP_StringPtr propName ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c DeleteArrayItem() deletes an XMP subtree rooted at a given array item. + /// + /// It is not an error if the array item does not exist. Use + /// \c TXMPUtils::ComposeArrayItemPath() to create a complex path. + /// + /// @param schemaNS The namespace URI for the array; see \c GetProperty(). + /// + /// @param arrayName The name of the array. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param itemIndex The 1-based index of the desired item. Use the macro \c #kXMP_ArrayLastItem + /// to specify the last existing array item. + + void DeleteArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c DeleteStructField() deletes an XMP subtree rooted at a given struct field. + /// + /// It is not an error if the field does not exist. + /// + /// @param schemaNS The namespace URI for the struct; see \c GetProperty(). + /// + /// @param structName The name of the struct. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param fieldNS The namespace URI for the field. Same namespace and prefix usage as + /// \c GetProperty(). + /// + /// @param fieldName The name of the field. Must be a single XML name, must not be null or the + /// empty string. Same namespace and prefix usage as \c GetProperty(). + + void DeleteStructField ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c DeleteQualifier() deletes an XMP subtree rooted at a given qualifier. + /// + /// It is not an error if the qualifier does not exist. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property to which the qualifier is attached. Can be a + /// general path expression, must not be null or the empty string; see \c GetProperty() for + /// namespace prefix usage. + /// + /// @param qualNS The namespace URI for the qualifier. Same namespace and prefix usage as + /// \c GetProperty(). + /// + /// @param qualName The name of the qualifier. Must be a single XML name, must not be null or + /// the empty string. Same namespace and prefix usage as \c GetProperty(). + + void DeleteQualifier ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c DoesPropertyExist() reports whether a property currently exists. + /// + /// @param schemaNS The namespace URI for the property; see \c GetProperty(). + /// + /// @param propName The name of the property; see \c GetProperty(). + /// + /// @return True if the property exists. + + bool DoesPropertyExist ( XMP_StringPtr schemaNS, + XMP_StringPtr propName ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c DoesArrayItemExist() reports whether an array item currently exists. + /// + /// Use \c TXMPUtils::ComposeArrayItemPath() to create a complex path. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param arrayName The name of the array. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param itemIndex The 1-based index of the desired item. Use the macro \c #kXMP_ArrayLastItem + /// to specify the last existing array item. + /// + /// @return True if the array item exists. + + bool DoesArrayItemExist ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c DoesStructFieldExist() reports whether a struct field currently exists. + /// + /// Use \c TXMPUtils::ComposeStructFieldPath() to create a complex path. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param structName The name of the struct. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param fieldNS The namespace URI for the field. Same namespace and prefix usage as + /// \c GetProperty(). + /// + /// @param fieldName The name of the field. Must be a single XML name, must not be null or the + /// empty string. Same namespace and prefix usage as \c GetProperty(). + /// + /// @return True if the field exists. + + bool DoesStructFieldExist ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c DoesQualifierExist() reports whether a qualifier currently exists. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property to which the qualifier is attached. Can be a + /// general path expression, must not be null or the empty string; see \c GetProperty() for + /// namespace prefix usage. + /// + /// @param qualNS The namespace URI for the qualifier. Same namespace and prefix usage as + /// \c GetProperty(). + /// + /// @param qualName The name of the qualifier. Must be a single XML name, must not be null or + /// the empty string. Same namespace and prefix usage as \c GetProperty(). + /// + /// @return True if the qualifier exists. + + bool DoesQualifierExist ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName ) const; + + /// @} + + // ============================================================================================= + // Specialized Get and Set functions + // ============================================================================================= + + // --------------------------------------------------------------------------------------------- + /// \name Accessing properties as binary values. + /// @{ + /// + /// These are very similar to \c TXMPMeta::GetProperty() and \c TXMPMeta::SetProperty(), except + /// that the value is returned or provided in binary form instead of as a UTF-8 string. + /// \c TXMPUtils provides functions for converting between binary and string values. + /// Use the path composition functions in \c TXMPUtils to compose complex path expressions + /// for fields or items in nested structures or arrays, or for qualifiers. + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetProperty_Bool() retrieves the value of a Boolean property as a C++ bool. + /// + /// Reports whether a property exists, and retrieves its binary value and property type information. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param propValue [out] A buffer in which to return the binary value. Can be null if the + /// value is not wanted. Must be null for arrays and non-leaf levels of structs that do not have + /// values. + /// + /// @param options [out] A buffer in which to return the option flags describing the property, a + /// logical OR of allowed bit-flag constants; see \c #kXMP_PropValueIsStruct and following. Can + /// be null if flags are not wanted. + /// + /// @return True if the property exists. + + bool GetProperty_Bool ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + bool * propValue, + XMP_OptionBits * options ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetProperty_Int() retrieves the value of an integer property as a C long integer. + /// + /// Reports whether a property exists, and retrieves its binary value and property type information. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param propValue [out] A buffer in which to return the binary value. Can be null if the + /// value is not wanted. Must be null for arrays and non-leaf levels of structs that do not have + /// values. + /// + /// @param options [out] A buffer in which to return the option flags describing the property, a + /// logical OR of allowed bit-flag constants; see \c #kXMP_PropValueIsStruct and following. Can + /// be null if flags are not wanted. + /// + /// @return True if the property exists. + + bool GetProperty_Int ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int32 * propValue, + XMP_OptionBits * options ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetProperty_Int64() retrieves the value of an integer property as a C long long integer. + /// + /// Reports whether a property exists, and retrieves its binary value and property type information. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param propValue [out] A buffer in which to return the binary value. Can be null if the + /// value is not wanted. Must be null for arrays and non-leaf levels of structs that do not have + /// values. + /// + /// @param options [out] A buffer in which to return the option flags describing the property, a + /// logical OR of allowed bit-flag constants; see \c #kXMP_PropValueIsStruct and following. Can + /// be null if flags are not wanted. + /// + /// @return True if the property exists. + + bool GetProperty_Int64 ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int64 * propValue, + XMP_OptionBits * options ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetProperty_Float() retrieves the value of a floating-point property as a C double float. + /// + /// Reports whether a property exists, and retrieves its binary value and property type information. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param propValue [out] A buffer in which to return the binary value. Can be null if the + /// value is not wanted. Must be null for arrays and non-leaf levels of structs that do not have + /// values. + /// + /// @param options [out] A buffer in which to return the option flags describing the property, a + /// logical OR of allowed bit-flag constants; see \c #kXMP_PropValueIsStruct and following. Can + /// be null if flags are not wanted. + /// + /// @return True if the property exists. + + bool GetProperty_Float ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + double * propValue, + XMP_OptionBits * options ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetProperty_Date() retrieves the value of a date-time property as an \c #XMP_DateTime structure. + /// + /// Reports whether a property exists, and retrieves its binary value and property type information. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param propValue [out] A buffer in which to return the binary value. Can be null if the + /// value is not wanted. Must be null for arrays and non-leaf levels of structs that do not have + /// values. + /// + /// @param options [out] A buffer in which to return the option flags describing the property, a + /// logical OR of allowed bit-flag constants; see \c #kXMP_PropValueIsStruct and following. Can + /// be null if flags are not wanted. + /// + /// @return True if the property exists. + + bool GetProperty_Date ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_DateTime * propValue, + XMP_OptionBits * options ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetProperty_Bool() sets the value of a Boolean property using a C++ bool. + /// + /// Sets a property with a binary value, creating it if necessary. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param propValue The new binary value. Can be null if creating the property. Must be null + /// for arrays and non-leaf levels of structs that do not have values. + /// + /// @param options Option flags describing the property; a logical OR of allowed bit-flag + /// constants; see \c #kXMP_PropValueIsStruct and following. Must match the type of a property + /// that already exists. + + void SetProperty_Bool ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + bool propValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetProperty_Int() sets the value of an integer property using a C long integer. + /// + /// Sets a property with a binary value, creating it if necessary. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param propValue The new binary value. Can be null if creating the property. Must be null + /// for arrays and non-leaf levels of structs that do not have values. + /// + /// @param options Option flags describing the property; a logical OR of allowed bit-flag + /// constants; see \c #kXMP_PropValueIsStruct and following. Must match the type of a property + /// that already exists. + + void SetProperty_Int ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int32 propValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetProperty_Int64() sets the value of an integer property using a C long long integer. + /// + /// Sets a property with a binary value, creating it if necessary. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param propValue The new binary value. Can be null if creating the property. Must be null + /// for arrays and non-leaf levels of structs that do not have values. + /// + /// @param options Option flags describing the property; a logical OR of allowed bit-flag + /// constants; see \c #kXMP_PropValueIsStruct and following. Must match the type of a property + /// that already exists. + + void SetProperty_Int64 ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int64 propValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetProperty_Float() sets the value of a floating-point property using a C double float. + /// + /// Sets a property with a binary value, creating it if necessary. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param propValue The new binary value. Can be null if creating the property. Must be null + /// for arrays and non-leaf levels of structs that do not have values. + /// + /// @param options Option flags describing the property; a logical OR of allowed bit-flag + /// constants; see \c #kXMP_PropValueIsStruct and following. Must match the type of a property + /// that already exists. + + void SetProperty_Float ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + double propValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetProperty_Date() sets the value of a date/time property using an \c #XMP_DateTime structure. + /// + /// Sets a property with a binary value, creating it if necessary. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param propValue The new binary value. Can be null if creating the property. Must be null + /// for arrays and non-leaf levels of structs that do not have values. + /// + /// @param options Option flags describing the property; a logical OR of allowed bit-flag + /// constants; see \c #kXMP_PropValueIsStruct and following. Must match the type of a property + /// that already exists. + + void SetProperty_Date ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + const XMP_DateTime & propValue, + XMP_OptionBits options = 0 ); + + /// @} + // ============================================================================================= + /// \name Accessing localized text (alt-text) properties. + /// @{ + /// + /// Localized text properties are stored in alt-text arrays. They allow multiple concurrent + /// localizations of a property value, for example a document title or copyright in several + /// languages. + /// + /// These functions provide convenient support for localized text properties, including a + /// number of special and obscure aspects. The most important aspect of these functions is that + /// they select an appropriate array item based on one or two RFC 3066 language tags. One of + /// these languages, the "specific" language, is preferred and selected if there is an exact + /// match. For many languages it is also possible to define a "generic" language that can be + /// used if there is no specific language match. The generic language must be a valid RFC 3066 + /// primary subtag, or the empty string. + /// + /// For example, a specific language of "en-US" should be used in the US, and a specific + /// language of "en-UK" should be used in England. It is also appropriate to use "en" as the + /// generic language in each case. If a US document goes to England, the "en-US" title is + /// selected by using the "en" generic language and the "en-UK" specific language. + /// + /// It is considered poor practice, but allowed, to pass a specific language that is just an + /// RFC 3066 primary tag. For example "en" is not a good specific language, it should only be + /// used as a generic language. Passing "i" or "x" as the generic language is also considered + /// poor practice but allowed. + /// + /// Advice from the W3C about the use of RFC 3066 language tags can be found at: + /// \li http://www.w3.org/International/articles/language-tags/ + /// + /// \note RFC 3066 language tags must be treated in a case insensitive manner. The XMP toolkit + /// does this by normalizing their capitalization: + /// \li The primary subtag is lower case, the suggested practice of ISO 639. + /// \li All 2 letter secondary subtags are upper case, the suggested practice of ISO 3166. + /// \li All other subtags are lower case. + /// + /// The XMP specification defines an artificial language, "x-default", that is used to + /// explicitly denote a default item in an alt-text array. The XMP toolkit normalizes alt-text + /// arrays such that the x-default item is the first item. The \c SetLocalizedText() function + /// has several special features related to the x-default item, see its description for details. + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetLocalizedText() retrieves information about a selected item in an alt-text array. + /// + /// The array item is selected according to these rules: + /// \li Look for an exact match with the specific language. + /// \li If a generic language is given, look for a partial match. + /// \li Look for an x-default item. + /// \li Choose the first item. + /// + /// A partial match with the generic language is where the start of the item's language matches + /// the generic string and the next character is '-'. An exact match is also recognized as a + /// degenerate case. + /// + /// You can pass "x-default" as the specific language. In this case, selection of an + /// \c x-default item is an exact match by the first rule, not a selection by the 3rd rule. The + /// last 2 rules are fallbacks used when the specific and generic languages fail to produce a + /// match. + /// + /// The return value reports whether a match was successfully made. + /// + /// @param schemaNS The namespace URI for the alt-text array; see \c GetProperty(). + /// + /// @param altTextName The name of the alt-text array. Can be a general path expression, must + /// not be null or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param genericLang The name of the generic language as an RFC 3066 primary subtag. Can be + /// null or the empty string if no generic language is wanted. + /// + /// @param specificLang The name of the specific language as an RFC 3066 tag, or "x-default". + /// Must not be null or the empty string. + /// + /// @param actualLang [out] A string object in which to return the language of the selected + /// array item, if an appropriate array item is found. Can be null if the language is not wanted. + /// + /// @param itemValue [out] A string object in which to return the value of the array item, if an + /// appropriate array item is found. Can be null if the value is not wanted. + /// + /// @param options A buffer in which to return the option flags that describe the array item, if + /// an appropriate array item is found. Can be null if the flags are not wanted. + /// + /// @return True if an appropriate array item exists. + + bool GetLocalizedText ( XMP_StringPtr schemaNS, + XMP_StringPtr altTextName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang, + tStringObj * actualLang, + tStringObj * itemValue, + XMP_OptionBits * options ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetLocalizedText() modifies the value of a selected item in an alt-text array. + /// + /// Creates an appropriate array item if necessary, and handles special cases for the x-default + /// item. + /// + /// The array item is selected according to these rules: + /// \li Look for an exact match with the specific language. + /// \li If a generic language is given, look for a partial match. + /// \li Look for an x-default item. + /// \li Choose the first item. + /// + /// A partial match with the generic language is where the start of the item's language matches + /// the generic string and the next character is '-'. An exact match is also recognized as a + /// degenerate case. + /// + /// You can pass "x-default" as the specific language. In this case, selection of an + /// \c x-default item is an exact match by the first rule, not a selection by the 3rd rule. The + /// last 2 rules are fallbacks used when the specific and generic languages fail to produce a + /// match. + /// + /// Item values are modified according to these rules: + /// + /// \li If the selected item is from a match with the specific language, the value of that + /// item is modified. If the existing value of that item matches the existing value of the + /// x-default item, the x-default item is also modified. If the array only has 1 existing item + /// (which is not x-default), an x-default item is added with the given value. + /// + /// \li If the selected item is from a match with the generic language and there are no other + /// generic matches, the value of that item is modified. If the existing value of that item + /// matches the existing value of the x-default item, the x-default item is also modified. If + /// the array only has 1 existing item (which is not x-default), an x-default item is added + /// with the given value. + /// + /// \li If the selected item is from a partial match with the generic language and there are + /// other partial matches, a new item is created for the specific language. The x-default item + /// is not modified. + /// + /// \li If the selected item is from the last 2 rules then a new item is created for the + /// specific language. If the array only had an x-default item, the x-default item is also + /// modified. If the array was empty, items are created for the specific language and + /// x-default. + /// + /// @param schemaNS The namespace URI for the alt-text array; see \c GetProperty(). + /// + /// @param altTextName The name of the alt-text array. Can be a general path expression, must + /// not be null or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param genericLang The name of the generic language as an RFC 3066 primary subtag. Can be + /// null or the empty string if no generic language is wanted. + /// + /// @param specificLang The name of the specific language as an RFC 3066 tag, or "x-default". + /// Must not be null or the empty string. + /// + /// @param itemValue The new value for the matching array item, specified as a null-terminated + /// UTF-8 string. + /// + /// @param options Option flags, none currently defined. + + void SetLocalizedText ( XMP_StringPtr schemaNS, + XMP_StringPtr altTextName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang, + XMP_StringPtr itemValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetLocalizedText() modifies the value of a selected item in an alt-text array using + /// a string object. + /// + /// Creates an appropriate array item if necessary, and handles special cases for the x-default + /// item. + /// + /// The array item is selected according to these rules: + /// \li Look for an exact match with the specific language. + /// \li If a generic language is given, look for a partial match. + /// \li Look for an x-default item. + /// \li Choose the first item. + /// + /// A partial match with the generic language is where the start of the item's language matches + /// the generic string and the next character is '-'. An exact match is also recognized as a + /// degenerate case. + /// + /// You can pass "x-default" as the specific language. In this case, selection of an \c x-default + /// item is an exact match by the first rule, not a selection by the 3rd rule. The last 2 rules + /// are fallbacks used when the specific and generic languages fail to produce a match. + /// + /// Item values are modified according to these rules: + /// + /// \li If the selected item is from a match with the specific language, the value of that + /// item is modified. If the existing value of that item matches the existing value of the + /// x-default item, the x-default item is also modified. If the array only has 1 existing item + /// (which is not x-default), an x-default item is added with the given value. + /// + /// \li If the selected item is from a match with the generic language and there are no other + /// generic matches, the value of that item is modified. If the existing value of that item + /// matches the existing value of the x-default item, the x-default item is also modified. If + /// the array only has 1 existing item (which is not x-default), an x-default item is added + /// with the given value. + /// + /// \li If the selected item is from a partial match with the generic language and there are + /// other partial matches, a new item is created for the specific language. The x-default item + /// is not modified. + /// + /// \li If the selected item is from the last 2 rules then a new item is created for the + /// specific language. If the array only had an x-default item, the x-default item is also + /// modified. If the array was empty, items are created for the specific language and + /// x-default. + /// + /// @param schemaNS The namespace URI for the alt-text array; see \c GetProperty(). + /// + /// @param altTextName The name of the alt-text array. Can be a general path expression, must + /// not be null or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param genericLang The name of the generic language as an RFC 3066 primary subtag. Can be + /// null or the empty string if no generic language is wanted. + /// + /// @param specificLang The name of the specific language as an RFC 3066 tag, or "x-default". + /// Must not be null or the empty string. + /// + /// @param itemValue The new value for the matching array item, specified as a string object. + /// + /// @param options Option flags, none currently defined. + + void SetLocalizedText ( XMP_StringPtr schemaNS, + XMP_StringPtr altTextName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang, + const tStringObj & itemValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c DeleteLocalizedText() deletes specific language alternatives from an alt-text array. + /// + /// The rules for finding the language value to delete are similar to those for \c #SetLocalizedText(). + /// + /// @param schemaNS The namespace URI for the alt-text array; see \c #GetProperty(). + /// + /// @param altTextName The name of the alt-text array. Can be a general path expression, must + /// not be null or the empty string; see \c #GetProperty() for namespace prefix usage. + /// + /// @param genericLang The name of the generic language as an RFC 3066 primary subtag. Can be + /// null or the empty string if no generic language is wanted. + /// + /// @param specificLang The name of the specific language as an RFC 3066 tag, or "x-default". + /// Must not be null or the empty string. + /// + void + DeleteLocalizedText ( XMP_StringPtr schemaNS, + XMP_StringPtr altTextName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang ); + + /// @} + + // ============================================================================================= + /// \name Creating and reading serialized RDF. + /// @{ + /// + /// The metadata contained in an XMP object must be serialized as RDF for storage in an XMP + /// packet and output to a file. Similarly, metadata in the form of serialized RDF (such as + /// metadata read from a file using \c TXMPFiles) must be parsed into an XMP object for + /// manipulation with the XMP Toolkit. + /// + /// These functions support parsing serialized RDF into an XMP object, and serializing an XMP + /// object into RDF. The input for parsing can be any valid Unicode encoding. ISO Latin-1 is + /// also recognized, but its use is strongly discouraged. Serialization is always as UTF-8. + + // --------------------------------------------------------------------------------------------- + /// @brief \c ParseFromBuffer() parses RDF from a series of input buffers into this XMP object. + /// + /// Use this to convert metadata from serialized RDF form (as, for example, read from an XMP + /// packet embedded in a file) into an XMP object that you can manipulate with the XMP Toolkit. + /// If this XMP object is empty and the input buffer contains a complete XMP packet, this is the + /// same as creating a new XMP object from that buffer with the constructor. + /// + /// You can use this function to combine multiple buffers into a single metadata tree. To + /// terminate an input loop conveniently, pass the option \c #kXMP_ParseMoreBuffers for all + /// real input, then make a final call with a zero length and \c #kXMP_NoOptions. The buffers + /// can be any length. The buffer boundaries need not respect XML tokens or even Unicode + /// characters. + /// + /// @param buffer A pointer to a buffer of input. Can be null if \c bufferSize is 0. + /// + /// @param bufferSize The length of the input buffer in bytes. Zero is a valid value. + /// + /// @param options An options flag that controls how the parse operation is performed. A logical + /// OR of these bit-flag constants: + /// \li \c #kXMP_ParseMoreBuffers - This is not the last buffer of input, more calls follow. + /// \li \c #kXMP_RequireXMPMeta - The \c x:xmpmeta XML element is required around \c rdf:RDF. + /// + /// @see \c TXMPFiles::GetXMP() + + void ParseFromBuffer ( XMP_StringPtr buffer, + XMP_StringLen bufferSize, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SerializeToBuffer() serializes metadata in this XMP object into a string as RDF. + /// + /// Use this to prepare metadata for storage as an XMP packet embedded in a file. See \c TXMPFiles::PutXMP(). + /// + /// @param rdfString [out] A string object in which to return the serialized RDF. Must not be null. + /// + /// @param options An options flag that controls how the serialization operation is performed. + /// The specified options must be logically consistent; an exception is thrown if they are not. + /// A logical OR of these bit-flag constants: + /// \li \c kXMP_OmitPacketWrapper - Do not include an XML packet wrapper. This cannot be + /// specified together with \c #kXMP_ReadOnlyPacket, \c #kXMP_IncludeThumbnailPad, or + /// \c #kXMP_ExactPacketLength. + /// \li \c kXMP_ReadOnlyPacket - Create a read-only XML packet wapper. Cannot be specified + /// together with \c kXMP_OmitPacketWrapper. + /// \li \c kXMP_UseCompactFormat - Use a highly compact RDF syntax and layout. + /// \li \c kXMP_IncludeThumbnailPad - Include typical space for a JPEG thumbnail in the + /// padding if no \c xmp:Thumbnails property is present. Cannot be specified together with + /// \c kXMP_OmitPacketWrapper. + /// \li \c kXMP_ExactPacketLength - The padding parameter provides the overall packet length. + /// The actual amount of padding is computed. An exception is thrown if the packet exceeds + /// this length with no padding. Cannot be specified together with + /// \c kXMP_OmitPacketWrapper. + /// + /// In addition to the above options, you can include one of the following encoding options: + /// \li \c #kXMP_EncodeUTF8 - Encode as UTF-8, the default. + /// \li \c #kXMP_EncodeUTF16Big - Encode as big-endian UTF-16. + /// \li \c #kXMP_EncodeUTF16Little - Encode as little-endian UTF-16. + /// \li \c #kXMP_EncodeUTF32Big - Encode as big-endian UTF-32. + /// \li \c #kXMP_EncodeUTF32Little - Encode as little-endian UTF-32. + /// + /// @param padding The amount of padding to be added if a writeable XML packet is created. If + /// zero (the default) an appropriate amount of padding is computed. + /// + /// @param newline The string to be used as a line terminator. If empty, defaults to linefeed, + /// U+000A, the standard XML newline. + /// + /// @param indent The string to be used for each level of indentation in the serialized RDF. If + /// empty, defaults to two ASCII spaces, U+0020. + /// + /// @param baseIndent The number of levels of indentation to be used for the outermost XML + /// element in the serialized RDF. This is convenient when embedding the RDF in other text. + + void SerializeToBuffer ( tStringObj * rdfString, + XMP_OptionBits options, + XMP_StringLen padding, + XMP_StringPtr newline, + XMP_StringPtr indent = "", + XMP_Index baseIndent = 0 ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c SerializeToBuffer() serializes metadata in this XMP object into a string as RDF. + /// + /// This simpler form of the function uses default values for the \c newline, \c indent, and + /// \c baseIndent parameters. + /// + /// @param rdfString [out] A string object in which to return the serialized RDF. Must not be null. + /// + /// @param options An options flag that controls how the serialization operation is performed. + /// The specified options must be logically consistent; an exception is thrown if they are not. + /// A logical OR of these bit-flag constants: + /// \li \c kXMP_OmitPacketWrapper - Do not include an XML packet wrapper. This cannot be + /// specified together with \c #kXMP_ReadOnlyPacket, \c #kXMP_IncludeThumbnailPad, or + /// \c #kXMP_ExactPacketLength. + /// \li \c kXMP_ReadOnlyPacket - Create a read-only XML packet wapper. Cannot be specified + /// together with \c kXMP_OmitPacketWrapper. + /// \li \c kXMP_UseCompactFormat - Use a highly compact RDF syntax and layout. + /// \li \c kXMP_IncludeThumbnailPad - Include typical space for a JPEG thumbnail in the + /// padding if no \c xmp:Thumbnails property is present. Cannot be specified together with + /// \c kXMP_OmitPacketWrapper. + /// \li \c kXMP_ExactPacketLength - The padding parameter provides the overall packet length. + /// The actual amount of padding is computed. An exception is thrown if the packet exceeds + /// this length with no padding. Cannot be specified together with + /// \c kXMP_OmitPacketWrapper. + /// + /// In addition to the above options, you can include one of the following encoding options: + /// \li \c #kXMP_EncodeUTF8 - Encode as UTF-8, the default. + /// \li \c #kXMP_EncodeUTF16Big - Encode as big-endian UTF-16. + /// \li \c #kXMP_EncodeUTF16Little - Encode as little-endian UTF-16. + /// \li \c #kXMP_EncodeUTF32Big - Encode as big-endian UTF-32. + /// \li \c #kXMP_EncodeUTF32Little - Encode as little-endian UTF-32. + /// + /// @param padding The amount of padding to be added if a writeable XML packet is created. + /// If zero (the default) an appropriate amount of padding is computed. + + void SerializeToBuffer ( tStringObj * rdfString, + XMP_OptionBits options = 0, + XMP_StringLen padding = 0 ) const; + + /// @} + // ============================================================================================= + // Miscellaneous Member Functions + // ============================== + + // --------------------------------------------------------------------------------------------- + /// \name Helper functions. + /// @{ + + // --------------------------------------------------------------------------------------------- + /// @brief Retrieves an internal reference that can be safely passed across DLL boundaries and + /// reconstructed. + /// + /// The \c TXMPMeta class is a normal C++ template, it is instantiated and local to each client + /// executable, as are the other \c TXMP* classes. Different clients might not use the same + /// string type to instantiate \c TXMPMeta. + /// + /// Because of this you should not pass \c SXMPMeta objects, or pointers to \c SXMPMeta objects, + /// across DLL boundaries. Use this function to obtain a safe internal reference that you can + /// pass, then construct a local object on the callee side. This construction does not create a + /// cloned XMP tree, it is the same underlying XMP object safely wrapped in each client's + /// \c SXMPMeta object. + /// + /// Use this function and the associated constructor like this: + /// \li The callee's header contains: + ///
+    /// CalleeMethod ( XMPMetaRef xmpRef );
+    /// 
+ /// + /// \li The caller's code contains: + ///
+    /// SXMPMeta callerXMP;
+    /// CalleeMethod ( callerXMP.GetInternalRef() );
+    /// 
+ /// + /// \li The callee's code contains: + ///
+    /// SXMPMeta calleeXMP ( xmpRef );
+    /// 
+ /// + /// @return The reference object. + + XMPMetaRef GetInternalRef() const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetObjectName() retrieves the client-assigned name of this XMP object. + /// + /// Assign this name with \c SetObjectName(). + /// + /// @param name [out] A string object in which to return the name. + + void GetObjectName ( tStringObj * name ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetObjectName() assigns a name to this XMP object. + /// + /// Retrieve this client-assigned name with \c GetObjectName(). + /// + /// @param name The name as a null-terminated UTF-8 string. + + void SetObjectName ( XMP_StringPtr name ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetObjectName() assigns a name to this XMP object. + /// + /// Retrieve this client-assigned name with \c GetObjectName(). + /// + /// @param name The name as a string object. + + void SetObjectName ( tStringObj name ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c Sort() sorts the data model tree of an XMP object. + /// + /// Use this function to sort the data model of an XMP object into a canonical order. This can + /// be convenient when comparing data models, (e.g. by text comparison of DumpObject output). + /// + /// At the top level the namespaces are sorted by their prefixes. Within a namespace, the top + /// level properties are sorted by name. Within a struct, the fields are sorted by their + /// qualified name, i.e. their XML prefix:local form. Unordered arrays of simple items are + /// sorted by value. Language Alternative arrays are sorted by the xml:lang qualifiers, with + /// the "x-default" item placed first. + + void Sort(); + + // --------------------------------------------------------------------------------------------- + /// @brief \c Erase() restores the object to a "just constructed" state. + + void Erase(); + + // --------------------------------------------------------------------------------------------- + /// @brief \c Clone() creates a deep copy of an XMP object. + /// + /// Use this function to copy an entire XMP metadata tree. Assignment and copy constructors only + /// increment a reference count, they do not do a deep copy. This function returns an object, + /// not a pointer. The following shows correct usage: + /// + ///
+    /// SXMPMeta * clone1 = new SXMPMeta ( sourceXMP.Clone() );  // This works.
+    /// SXMPMeta   clone2 ( sourceXMP.Clone );  	// This works also. (Not a pointer.)
+    /// 
+ /// The \c clone2 example does not use an explicit pointer. + /// This is good for local usage, protecting against memory leaks. + /// + /// This is an example of incorrect usage: + ///
+    /// SXMPMeta * clone3 = &sourceXMP.Clone();		// ! This does not work!
+    /// 
+ /// The assignment to \c clone3 creates a temporary object, initializes it with the clone, + /// assigns the address of the temporary to \c clone3, then deletes the temporary. + /// + /// @param options Option flags, not currently defined.. + /// + /// @return An XMP object cloned from the original. + + TXMPMeta Clone ( XMP_OptionBits options = 0 ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c CountArrayItems() reports the number of items currently defined in an array. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param arrayName The name of the array. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @return The number of items. + + XMP_Index CountArrayItems ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c DumpObject() outputs the content of an XMP object to a callback handler for debugging. + /// + /// Invokes a client-defined callback for each line of output. + /// + /// @param outProc The client-defined procedure to handle each line of output. + /// + /// @param clientData A pointer to client-defined data to pass to the handler. + /// + /// @return A success-fail status value, returned from the handler. Zero is success, failure + /// values are client-defined. + /// + /// @see Static function \c DumpNamespaces() + + XMP_Status DumpObject ( XMP_TextOutputProc outProc, + void * clientData ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief Not implemented + XMP_OptionBits GetObjectOptions() const; + + // --------------------------------------------------------------------------------------------- + /// \brief Not implemented + void SetObjectOptions ( XMP_OptionBits options ); + + /// @} + + // ============================================================================================= + // Error notifications + // =================== + + // --------------------------------------------------------------------------------------------- + /// \name Error notifications + /// @{ + /// + /// From the beginning through version 5.5, XMP Tookit errors result in throwing an \c XMP_Error + /// exception. For the most part exceptions were thrown early and thus API calls aborted as soon + /// as an error was detected. Starting in version 5.5, support has been added for notifications + /// of errors arising in calls to \c TXMPMeta functions. + /// + /// A client can register an error notification callback function for a \c TXMPMeta object. This + /// can be done as a global default or individually to each object. The global default applies + /// to all objects created after it is registered. Within the object there is no difference + /// between the global default or explicitly registered callback. The callback function returns + /// a \c bool value indicating if recovery should be attempted (true) or an exception thrown + /// (false). If no callback is registered, a best effort at recovery and continuation will be + /// made with an exception thrown if recovery is not possible. + /// + /// The number of notifications delivered for a given TXMPMeta object can be limited. This is + /// intended to reduce chatter from multiple or cascading errors. The limit is set when the + /// callback function is registered. This limits the number of notifications of the highest + /// severity delivered or less. If a higher severity error occurs, the counting starts again. + /// The limit and counting can be reset at any time, see \c ResetErrorCallbackLimit. + + // -------------------------------------------------------------------------------------------- + /// @brief SetDefaultErrorCallback() registers a global default error notification callback. + /// + /// @param proc The client's callback function. + /// + /// @param context Client-provided context for the callback. + /// + /// @param limit A limit on the number of notifications to be delivered. + + static void SetDefaultErrorCallback ( XMPMeta_ErrorCallbackProc proc, void* context = 0, XMP_Uns32 limit = 1 ); + + // -------------------------------------------------------------------------------------------- + /// @brief SetErrorCallback() registers an error notification callback. + /// + /// @param proc The client's callback function. + /// + /// @param context Client-provided context for the callback. + /// + /// @param limit A limit on the number of notifications to be delivered. + + void SetErrorCallback ( XMPMeta_ErrorCallbackProc proc, void* context = 0, XMP_Uns32 limit = 1 ); + + // -------------------------------------------------------------------------------------------- + /// @brief ResetErrorCallbackLimit() resets the error notification limit and counting. It has no + /// effect if an error notification callback function is not registered. + /// + /// @param limit A limit on the number of notifications to be delivered. + + void ResetErrorCallbackLimit ( XMP_Uns32 limit = 1 ); + + /// @} + + // ============================================================================================= + + XMPMetaRef xmpRef; // *** Should be private, see below. + +private: + +#if 0 // *** VS.Net and gcc seem to not handle the friend declarations properly. + friend class TXMPIterator ; + friend class TXMPUtils ; +#endif + + static void SetClientString ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen ); + +}; // class TXMPMeta + +#endif // __TXMPMeta_hpp__ diff --git a/xmp/TXMPUtils.hpp b/xmp/TXMPUtils.hpp new file mode 100644 index 0000000..79ade07 --- /dev/null +++ b/xmp/TXMPUtils.hpp @@ -0,0 +1,967 @@ +#ifndef __TXMPUtils_hpp__ +#define __TXMPUtils_hpp__ 1 + +#if ( ! __XMP_hpp__ ) + #error "Do not directly include, use XMP.hpp" +#endif + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +// ================================================================================================= +/// \file TXMPUtils.hpp +/// \brief API for access to the XMP Toolkit utility services. +/// +/// \c TXMPUtils is the template class providing utility services for the XMP Toolkit. It must be +/// instantiated with a string class such as \c std::string. See the instructions in XMP.hpp, and +/// the Overview for a discussion of the overall architecture of the XMP API. +// ================================================================================================= + +// ================================================================================================= +/// \class TXMPUtils TXMPUtils.hpp +/// @brief API for access to the XMP Toolkit utility services. +/// +/// \c TXMPUtils is a template class which must be instantiated with a string class such as +/// \c std::string. See the instructions in XMP.hpp, and the Overview for a discussion of the overall +/// architecture of the XMP API. +/// +/// This class defines helper functions that support the basic metadata manipulation provided by +/// \c TXMPMeta. All of the functions are static; that is, you call them directly from the concrete +/// class (\c SXMPUtils), which is never itself instantiated. +/// +/// General categories of utilities include: +/// +/// \li Composing complex path expressions, which you can then pass to the property access +/// functions in \c TXMPMeta +/// \li Converting between binary and string forms of property values +/// \li Manipulating date/time values +/// \li Encoding and decoding base-64 strings +/// \li JPEG file handling +/// \li Editing aids for creating a user interface for the XMP Toolkit +// ================================================================================================= + +template class TXMPUtils { + +public: + + // ============================================================================================= + // No constructors or destructor declared or needed + // ================================================ + + // ============================================================================================ + /// \name Path composition + /// @{ + /// + /// These functions provide support for composing path expressions to deeply nested properties. + /// The functions in \c TXMPMeta such as \c TXMPMeta::GetProperty(), + /// \c TXMPMeta::GetArrayItem(), and \c TXMPMeta::GetStructField() provide easy access to top level + /// simple properties, items in top level arrays, and fields of top level structs. They are + /// not as convenient for more complex things, such as fields several levels deep in a complex + /// struct, or fields within an array of structs, or items of an array that is a field of a + /// struct. You can use these utility functions to compose these paths, which you can then pass + /// to the property access functions. You can also compose paths to top-level array items or + /// struct fields so that you can use the binary accessors such as + /// \c TXMPMeta::GetProperty_Int(). + /// + /// You can use these functions is to compose a complete path expression, or all but the last + /// component. For example, suppose you have a property that is an array of integers within a + /// struct. You can access one of the array items like this: + /// + ///
+    ///   SXMPUtils::ComposeStructFieldPath ( schemaNS, "Struct", fieldNS, "Array", &path );
+    ///   SXMPUtils::ComposeArrayItemPath ( schemaNS, path, index, &path );
+    ///   exists = xmpObj.GetProperty_Int ( schemaNS, path, &value, &options );
+    /// 
+ /// + /// You could also use this code if you want the string form of the integer: + /// + ///
+    ///   SXMPUtils::ComposeStructFieldPath ( schemaNS, "Struct", fieldNS, "Array", &path );
+    ///   xmpObj.GetArrayItem ( schemaNS, path, index, &value, &options );
+    /// 
+ /// + /// \note It might look confusing that the \c schemaNS is passed in all of the calls above. This + /// is because the XMP Toolkit keeps the top-level "schema" namespace separate from the rest of + /// the path expression. + + // --------------------------------------------------------------------------------------------- + /// @brief \c ComposeArrayItemPath() composes the path expression for an item in an array. + /// + /// The returned string is in the form ns:arrayName[i], where "ns" is the prefix for + /// the specified namespace, and "i" is the decimal representation of specified item index. + /// If the last item was specified, the path is ns:arrayName[last()]. + /// + /// @param schemaNS The namespace URI for the array; see \c GetProperty(). + /// + /// @param arrayName The name of the array. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param itemIndex The 1-based index of the desired item. Use the macro + /// \c #kXMP_ArrayLastItem to specify the last existing array item. + /// + /// @param fullPath [out] A string in which to return the composed path. + + static void ComposeArrayItemPath ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + tStringObj * fullPath ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ComposeStructFieldPath() composes the path expression for a field in a struct. + /// + /// The returned string is in the form ns:structName/fNS:fieldName, where "ns" is the + /// prefix for the schema namespace, and "fNS" is the prefix for field namespace. + /// + /// @param schemaNS The namespace URI for the struct; see \c GetProperty(). + /// + /// @param structName The name of the struct. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param fieldNS The namespace URI for the field. Same URI and prefix usage as the + /// \c schemaNS and \c structName parameters. + /// + /// @param fieldName The name of the field. Must be a single XML name, must not be null or the + /// empty string. Same URI and prefix usage as the \c schemaNS and \c structName parameters. + /// + /// @param fullPath [out] A string in which to return the composed path. + + static void ComposeStructFieldPath ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + tStringObj * fullPath ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ComposeQualifierPath() composes the path expression for a qualifier. + /// + /// The returned string is in the form ns:propName/?qNS:qualName, where "ns" is the + /// prefix for the schema namespace, and "qNS" is the prefix for the qualifier namespace. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property to which the qualifier is attached. Can be a + /// general path expression, must not be null or the empty string; see \c GetProperty() for + /// namespace prefix usage. + /// + /// @param qualNS The namespace URI for the qualifier. Same URI and prefix usage as the + /// \c schemaNS and \c propName parameters. + /// + /// @param qualName The name of the qualifier. Must be a single XML name, must not be null or the + /// empty string. Same URI and prefix usage as the \c schemaNS and \c propName parameters. + /// + /// @param fullPath [out] A string in which to return the composed path. + + static void ComposeQualifierPath ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + tStringObj * fullPath ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ComposeLangSelector() composes the path expression to select an alternate item by language. + /// + /// Path syntax allows two forms of "content addressing" to select an item in an array of + /// alternatives. The form used in this function lets you select an item in an alt-text array + /// based on the value of its \c xml:lang qualifier. The other form of content addressing is + /// shown in \c ComposeFieldSelector(). + /// + /// The returned string is in the form ns:arrayName[\@xml:lang='langName'], where + /// "ns" is the prefix for the schema namespace + /// + /// This function provides a path expression that is explicitly and only for a specific + /// language. In most cases, \c TXMPMeta::SetLocalizedText() and \c TXMPMeta::GetLocalizedText() + /// are preferred, because they provide extra logic to choose the appropriate language and + /// maintain consistency with the 'x-default' value. + /// + /// @param schemaNS The namespace URI for the array; see \c GetProperty(). + /// + /// @param arrayName The name of the array. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param langName The RFC 3066 code for the desired language, as a null-terminated UTF-8 string. + /// + /// @param fullPath [out] A string in which to return the composed path. + + static void ComposeLangSelector ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr langName, + tStringObj * fullPath ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ComposeLangSelector() composes a path expression to select an alternate item by language. + /// + /// Path syntax allows two forms of "content addressing" to select an item in an array of + /// alternatives. The form used in this function lets you select an item in an alt-text array + /// based on the value of its \c xml:lang qualifier. The other form of content addressing is + /// shown in \c ComposeFieldSelector(). + /// + /// The returned string is in the form ns:arrayName[\@xml:lang='langName'], where + /// "ns" is the prefix for the schema namespace + /// + /// This function provides a path expression that is explicitly and only for a specific + /// language. In most cases, \c TXMPMeta::SetLocalizedText() and \c TXMPMeta::GetLocalizedText() + /// are preferred, because they provide extra logic to choose the appropriate language and + /// maintain consistency with the 'x-default' value. + /// + /// @param schemaNS The namespace URI for the array; see \c GetProperty(). + /// + /// @param arrayName The name of the array. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param langName The RFC 3066 code for the desired language, as a string object. + /// + /// @param fullPath [out] A string in which to return the composed path. + + static void ComposeLangSelector ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + const tStringObj & langName, + tStringObj * fullPath ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ComposeFieldSelector() composes a path expression to select an alternate item by a field's value. + /// + /// Path syntax allows two forms of "content addressing" to select an item in an array of + /// alternatives. The form used in this function lets you select an item in an array of structs + /// based on the value of one of the fields in the structs. The other form of content addressing + /// is shown in \c ComposeLangSelector(). + /// + /// For example, consider a simple struct that has two fields, the name of a city and the URI of + /// an FTP site in that city. Use this to create an array of download alternatives. You can show + /// the user a popup built from the values of the city fields, then get the corresponding URI as + /// follows: + ///
+    ///   ComposeFieldSelector ( schemaNS, "Downloads", fieldNS, "City", chosenCity, &path );
+    ///   exists = GetStructField ( schemaNS, path, fieldNS, "URI", &uri );
+    /// 
+ /// + /// The returned string is in the form ns:arrayName[fNS:fieldName='fieldValue'], where + /// "ns" is the prefix for the schema namespace and "fNS" is the prefix for the field namespace. + /// + /// @param schemaNS The namespace URI for the array; see \c GetProperty(). + /// + /// @param arrayName The name of the array. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param fieldNS The namespace URI for the field used as the selector. Same URI and prefix + /// usage as the \c schemaNS and \c arrayName parameters. + /// + /// @param fieldName The name of the field used as the selector. Must be a single XML name, must + /// not be null or the empty string. It must be the name of a field that is itself simple. + /// + /// @param fieldValue The desired value of the field, specified as a null-terminated UTF-8 string. + /// + /// @param fullPath [out] A string in which to return the composed path. + + static void ComposeFieldSelector ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + XMP_StringPtr fieldValue, + tStringObj * fullPath ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ComposeFieldSelector() composes a path expression to select an alternate item by a field's value. + /// + /// Path syntax allows two forms of "content addressing" to select an item in an array of + /// alternatives. The form used in this function lets you select an item in an array of structs + /// based on the value of one of the fields in the structs. The other form of content addressing + /// is shown in \c ComposeLangSelector(). + /// + /// For example, consider a simple struct that has two fields, the name of a city and the URI of + /// an FTP site in that city. Use this to create an array of download alternatives. You can show + /// the user a popup built from the values of the city fields, then get the corresponding URI as + /// follows: + ///
+    ///   ComposeFieldSelector ( schemaNS, "Downloads", fieldNS, "City", chosenCity, &path );
+    ///   exists = GetStructField ( schemaNS, path, fieldNS, "URI", &uri );
+    /// 
+ /// + /// The returned string is in the form ns:arrayName[fNS:fieldName='fieldValue'], where + /// "ns" is the prefix for the schema namespace and "fNS" is the prefix for the field namespace. + /// + /// @param schemaNS The namespace URI for the array; see \c GetProperty(). + /// + /// @param arrayName The name of the array. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param fieldNS The namespace URI for the field used as the selector. Same URI and prefix + /// usage as the \c schemaNS and \c arrayName parameters. + /// + /// @param fieldName The name of the field used as the selector. Must be a single XML name, must + /// not be null or the empty string. It must be the name of a field that is itself simple. + /// + /// @param fieldValue The desired value of the field, specified as a string object. + /// + /// @param fullPath [out] A string in which to return the composed path. + + static void ComposeFieldSelector ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + const tStringObj & fieldValue, + tStringObj * fullPath ); + + /// @} + + // ============================================================================================= + /// \name Conversion between binary types and strings + /// @{ + /// + /// The main accessors in \c TXMPMeta set and retrieve property values as strings. additional + /// functions, such as \c TXMPMeta::SetPropertyInt(), set and retrieve property values as + /// explicit binary data types. Use these functions to convert between binary and string + /// values. + /// + /// Strings can be specified as null-terminated UTF-8 (\c #XMP_StringPtr), or as string + /// objects (\c tStringObj) of the type declared when instantiating the XMP classes; see + /// \c XMP.hpp. Alternate forms of each conversion function allow either type of string. + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertFromBool() converts a Boolean value to a string. + /// + /// The string values of Booleans are returned by the macros \c #kXMP_TrueStr and + /// \c #kXMP_FalseStr in \c XMP_Const.h. + /// + /// @param binValue The Boolean value to be converted. + /// + /// @param strValue [out] A buffer in which to return the string representation of the value. + + static void ConvertFromBool ( bool binValue, + tStringObj * strValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertFromInt() converts a 32-bit integer value to a string. + /// + /// @param binValue The integer value to be converted. + /// + /// @param format Optional. A C \c sprintf format for the conversion. Default is "%d". + /// + /// @param strValue [out] A buffer in which to return the string representation of the value. + + static void ConvertFromInt ( long binValue, + XMP_StringPtr format, + tStringObj * strValue ); + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertFromInt64() converts a 64-bit integer value to a string. + /// + /// @param binValue The integer value to be converted. + /// + /// @param format Optional. A C \c sprintf format for the conversion. Default is "%d". + /// + /// @param strValue [out] A buffer in which to return the string representation of the value. + + static void ConvertFromInt64 ( long long binValue, + XMP_StringPtr format, + tStringObj * strValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertFromFloat() converts a floating-point value to a string. + /// + /// @param binValue The floating-point value to be converted. + /// + /// @param format Optional. A C \c sprintf format for the conversion. Default is "%d". + /// + /// @param strValue [out] A buffer in which to return the string representation of the value. + + static void ConvertFromFloat ( double binValue, + XMP_StringPtr format, + tStringObj * strValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertFromDate() converts a date/time value to a string. + /// + /// Formats a date according to the ISO 8601 profile in http://www.w3.org/TR/NOTE-datetime: + ///
+    ///   YYYY
+    ///   YYYY-MM
+    ///   YYYY-MM-DD
+    ///   YYYY-MM-DDThh:mmTZD
+    ///   YYYY-MM-DDThh:mm:ssTZD
+    ///   YYYY-MM-DDThh:mm:ss.sTZD
+    /// 
+ /// + /// \c YYYY = four-digit year, formatted as "%.4d"
+ /// \c MM = two-digit month (01=January)
+ /// \c DD = two-digit day of month (01 through 31)
+ /// \c hh = two digits of hour (00 through 23)
+ /// \c mm = two digits of minute (00 through 59)
+ /// \c ss = two digits of second (00 through 59)
+ /// \c s = one or more digits representing a decimal fraction of a second
+ /// \c TZD = time zone designator (Z or +hh:mm or -hh:mm) + /// + /// Time-only input is allowed where the year, month, and day are all zero. This is output as + /// "0000-00-00...". + /// + /// @note ISO 8601 does not allow years less than 1000 or greater than 9999. This API allows + /// any year, even negative ones. The W3C profile also requires a time zone designator if a time + /// is present, this API treats the time zone designator as optional. The XMP_DateTime type has + /// an explicit notion of zone-less time. + /// + /// @param binValue The date/time value to be converted. + /// + /// @param strValue [out] A buffer in which to return the ISO 8601 string representation of the date/time. + + static void ConvertFromDate ( const XMP_DateTime & binValue, + tStringObj * strValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertToBool() converts a string to a Boolean value. + /// + /// The preferred strings are those returned by the macros \c #kXMP_TrueStr and \c #kXMP_FalseStr. + /// If these do not match, the function does a case insensitive comparison, then simply 't' or 'f', + /// and finally non-zero and zero integer representations. + /// + /// @param strValue The string representation of the value, specified as a null-terminated UTF-8 string. + /// + /// @return The appropriate C++ bool value for the string. + + static bool ConvertToBool ( XMP_StringPtr strValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertToBool() converts a string to a Boolean value. + /// + /// Overloads the basic form of the function, allowing you to pass a string object, + /// rather than a const * char. It is otherwise identical; see details in the canonical form. + /// + /// @param strValue The string representation of the value, specified as a string object. + /// + /// @return The appropriate C++ bool value for the string. + + static bool ConvertToBool ( const tStringObj & strValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertToInt() converts a string to a 32-bit integer value. + /// + /// @param strValue The string representation of the value, specified as a null-terminated UTF-8 string. + /// + /// @return The 32-bit integer value. + + static long ConvertToInt ( XMP_StringPtr strValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertToInt() converts a string to a 32-bit integer value. + /// + /// Overloads the basic form of the function, allowing you to pass a string object, + /// rather than a const * char. It is otherwise identical. + /// + /// @param strValue The string representation of the value, specified as a string object. + /// + /// @return The 32-bit integer value. + + static long ConvertToInt ( const tStringObj & strValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertToInt64() converts a string to a 64-bit integer value. + /// + /// @param strValue The string representation of the value, specified as a null-terminated UTF-8 string. + /// + /// @return The 64-bit integer value. + + static long long ConvertToInt64 ( XMP_StringPtr strValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertToInt64() converts a string to a 64-bit integer value. + /// + /// Overloads the basic form of the function, allowing you to pass a string object, + /// rather than a const * char. It is otherwise identical. + /// + /// @param strValue The string representation of the value, specified as a string object. + /// + /// @return The 64-bit integer value. + + static long long ConvertToInt64 ( const tStringObj & strValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertToFloat() converts a string to a floating-point value. + /// + /// @param strValue The string representation of the value, specified as a null-terminated UTF-8 string. + /// + /// @return The floating-point value. + + static double ConvertToFloat ( XMP_StringPtr strValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertToFloat() converts a string to a floating-point value. + /// + /// Overloads the basic form of the function, allowing you to pass a string object, + /// rather than a const * char. It is otherwise identical. + /// + /// @param strValue The string representation of the value, specified as a string object. + /// + /// @return The floating-point value. + + static double ConvertToFloat ( const tStringObj & strValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertToDate() converts a string to a date/time value. + /// + /// Parses a date according to the ISO 8601 profile in http://www.w3.org/TR/NOTE-datetime: + ///
+    ///   YYYY
+    ///   YYYY-MM
+    ///   YYYY-MM-DD
+    ///   YYYY-MM-DDThh:mmTZD
+    ///   YYYY-MM-DDThh:mm:ssTZD
+    ///   YYYY-MM-DDThh:mm:ss.sTZD
+    /// 
+ /// + /// \c YYYY = four-digit year, formatted as "%.4d"
+ /// \c MM = two-digit month (01=January)
+ /// \c DD = two-digit day of month (01 through 31)
+ /// \c hh = two digits of hour (00 through 23)
+ /// \c mm = two digits of minute (00 through 59)
+ /// \c ss = two digits of second (00 through 59)
+ /// \c s = one or more digits representing a decimal fraction of a second
+ /// \c TZD = time zone designator (Z or +hh:mm or -hh:mm) + /// + /// A missing date portion or missing TZD are tolerated. A missing date value can begin with + /// "Thh:" or "hh:"; the year, month, and day are all set to zero in the \c #XMP_DateTime value. + /// A missing TZD is assumed to be UTC. + /// + /// @note ISO 8601 does not allow years less than 1000 or greater than 9999. This API allows + /// any year, even negative ones. The W3C profile also requires a time zone designator if a time + /// is present, this API treats the time zone designator as optional. The XMP_DateTime type has + /// an explicit notion of zone-less time. + /// + /// @param strValue The ISO 8601 string representation of the date/time, specified as a + /// null-terminated UTF-8 string. + /// + /// @param binValue [out] A buffer in which to return the binary date/time value. + + static void ConvertToDate ( XMP_StringPtr strValue, + XMP_DateTime * binValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertToDate() converts a string to a date/time value. + /// + /// Overloads the basic form of the function, allowing you to pass a string object, + /// rather than a const * char. It is otherwise identical. + /// See details for the canonical form. + /// + /// + /// @param strValue The ISO 8601 string representation of the date/time, specified as a string + /// object. + /// + /// @param binValue [out] A buffer in which to return the binary date/time value. + + static void ConvertToDate ( const tStringObj & strValue, + XMP_DateTime * binValue ); + + /// @} + + // ============================================================================================= + /// \name Date-time manipulation + /// @{ + /// + /// In addition to the type-conversion functions that convert between strings and binary + /// date-time values, these functions create, manipulate, and compare date-time values. + + // --------------------------------------------------------------------------------------------- + /// @brief \c CurrentDateTime() obtains the current date and time. + /// + /// Creates and returns a binary \c #XMP_DateTime value. The returned time is UTC, properly + /// adjusted for the local time zone. The resolution of the time is not guaranteed to be finer + /// than seconds. + /// + /// @param time [out] A buffer in which to return the date/time value. + + static void CurrentDateTime ( XMP_DateTime * time ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetTimeZone() sets the time zone in a date/time value to the local time zone. + /// + /// Any existing time zone value is replaced. The other date/time fields are not adjusted in any way. + /// + /// @param time A pointer to the date-time value, which is modified in place. + + static void SetTimeZone ( XMP_DateTime * time ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertToUTCTime() ensures that a time is UTC. + /// + /// If the time zone is not UTC, the time is adjusted and the time zone set to be UTC. The value + /// is not modified if the time zone is already UTC or if the value has no time zone. + /// + /// @param time A pointer to the date-time value, which is modified in place. + + static void ConvertToUTCTime ( XMP_DateTime * time ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertToLocalTime() ensures that a time is local. + /// + /// If the time zone is not the local zone, the time is adjusted and the time zone set to be local. + /// The value is not modified if the time zone is already the local zone or if the value has no + /// time zone. + /// + /// @param time A pointer to the date-time value, which is modified in place. + + static void ConvertToLocalTime ( XMP_DateTime * time ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c CompareDateTime() compares the order of two date/time values. + /// + /// Both values are treated as in the same time zone if either has no time zone. + /// + /// @param left The left-side date/time value. + /// + /// @param right The right-side date/time value. + /// + /// @return An integer indicating the order: + /// \li -1 if left is earlier than right + /// \li 0 if left matches right + /// \li +1 if left is later than right + + static int CompareDateTime ( const XMP_DateTime & left, + const XMP_DateTime & right ); + + /// @} + + // ============================================================================================= + /// \name Base64 encoding and decoding + /// @{ + /// + /// These functions convert between raw data values and Base64-encoded strings. + + // --------------------------------------------------------------------------------------------- + /// @brief \c EncodeToBase64() converts a raw data value to a Base64-encoded string. + /// + /// @param rawStr An \c #XMP_StringPtr (char *) string containing the raw data to be converted. + /// + /// @param rawLen The number of characters of raw data to be converted. + /// + /// @param encodedStr [out] A string object in which to return the encoded string. + + static void EncodeToBase64 ( XMP_StringPtr rawStr, + XMP_StringLen rawLen, + tStringObj * encodedStr ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c EncodeToBase64() converts a raw data value passed in a string object to a Base64-encoded string. + /// + /// Overloads the basic form of the function, allowing you to pass a string object as input. + /// It is otherwise identical. + /// + /// @param rawStr A string object containing the raw data to be converted. + /// + /// @param encodedStr [out] A string object in which to return the encoded string. + + static void EncodeToBase64 ( const tStringObj & rawStr, + tStringObj * encodedStr ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c DecodeFromBase64() Decodes a Base64-encoded string to raw data. + /// + /// @param encodedStr An \c #XMP_StringPtr (char *) string containing the encoded data to be converted. + /// + /// @param encodedLen The number of characters of raw data to be converted. + /// + /// @param rawStr [out] A string object in which to return the decoded data. + + static void DecodeFromBase64 ( XMP_StringPtr encodedStr, + XMP_StringLen encodedLen, + tStringObj * rawStr ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c DecodeFromBase64() Decodes a Base64-encoded string, passed as a string object, to raw data. + /// + /// Overloads the basic form of the function, allowing you to pass a string object as input. + /// It is otherwise identical. + /// + /// @param encodedStr An string object containing the encoded data to be converted. + /// + /// @param rawStr [out] A string object in which to return the decoded data. + + static void DecodeFromBase64 ( const tStringObj & encodedStr, + tStringObj * rawStr ); + + /// @} + + // ============================================================================================= + // ============================================================================================= + /// \name JPEG file handling + /// @{ + /// + /// These functions support the partitioning of XMP in JPEG files into standard and extended + /// portions in order to work around the 64KB size limit of JPEG marker segments. + /// + /// @note (Doc note) Add detail about how to write out and read back extended data + + // --------------------------------------------------------------------------------------------- + /// @brief \c PackageForJPEG() creates XMP serializations appropriate for a JPEG file. + /// + /// The standard XMP in a JPEG file is limited to 64K bytes. This function serializes the XMP + /// metadata in an XMP object into a string of RDF (see \c TXMPMeta::SerializeToBuffer()). If + /// the data does not fit into the 64K byte limit, it creates a second packet string with the + /// extended data. + /// + /// @param xmpObj The XMP object containing the metadata. + /// + /// @param standardXMP [out] A string object in which to return the full standard XMP packet. + /// + /// @param extendedXMP [out] A string object in which to return the serialized extended XMP, + /// empty if not needed. + /// + /// @param extendedDigest [out] A string object in which to return an MD5 digest of the serialized + /// extended XMP, empty if not needed. + /// + /// @see \c MergeFromJPEG() + + static void PackageForJPEG ( const TXMPMeta & xmpObj, + tStringObj * standardXMP, + tStringObj * extendedXMP, + tStringObj * extendedDigest ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c MergeFromJPEG() merges standard and extended XMP retrieved from a JPEG file. + /// + /// When an extended partition stores properties that do not fit into the JPEG file limitation + /// of 64K bytes, this function integrates those properties back into the same XMP object with + /// those from the standard XMP packet. + /// + /// @param fullXMP [in, out] An XMP object which the caller has initialized from the standard + /// XMP packet in a JPEG file. The extended XMP is added to this object. + /// + /// @param extendedXMP An XMP object which the caller has initialized from the extended XMP + /// packet in a JPEG file. + /// + /// @see \c PackageForJPEG() + + static void MergeFromJPEG ( TXMPMeta * fullXMP, + const TXMPMeta & extendedXMP ); + + /// @} + + // ============================================================================================= + /// \name Editing utilities + /// @{ + /// + /// These functions are useful in implementing a user interface for editing XMP. They + /// convert sets of property values to and from displayable and manipulable strings, and perform + /// operations on sets of metadata, such as those available from the File Info dialog box. + + // --------------------------------------------------------------------------------------------- + /// @brief \c CatenateArrayItems() creates a single edit string from a set of array item values. + /// + /// Collects the values of all items in an array into a single string, using a specified + /// separation string. Each item in the specified array must be a simple string value. + /// + /// @param xmpObj The XMP object containing the array to be catenated. + /// + /// @param schemaNS The schema namespace URI for the array. Must not be null or the empty string. + /// + /// @param arrayName The name of the array. May be a general path expression, must not be null + /// or the empty string. + /// + /// @param separator The string with which to separate the items in the catenated string. + /// Defaults to "; ", ASCII semicolon and space (U+003B, U+0020). + /// + /// @param quotes The character or characters to use as quotes around array items that contain a + /// separator. Defaults to the double-quote character ("), ASCII quote (U+0022). + /// + /// @param options Option flags to control the catenation. <> + /// + /// @param catedStr [out] A string object in which to return the catenated array items. + /// + /// @see \c SeparateArrayItems() + + static void CatenateArrayItems ( const TXMPMeta & xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr separator, + XMP_StringPtr quotes, + XMP_OptionBits options, + tStringObj * catedStr ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SeparateArrayItems() updates an array from a concatenated edit string of values. + /// + /// This reverses the action of \c CatenateArrayItems(), separating out individual array items + /// from the edit string and updating the array with the new values. Each item in the array must + /// be a simple string value. + /// + /// @param xmpObj The XMP object containing the array to be updated. + /// + /// @param schemaNS The schema namespace URI for the array. Must not be null or the empty string. + /// + /// @param arrayName The name of the array. May be a general path expression, must not be null + /// or the empty string. + /// + /// @param options Option flags to control the separation. <> + /// + /// @param catedStr The concatenated array items, as created by \c CatenateArrayItems(), + /// specified as a null-terminated UTF-8 string. + + static void SeparateArrayItems ( TXMPMeta * xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits options, + XMP_StringPtr catedStr ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SeparateArrayItems() updates an array from a concatenated edit string of values. + /// + /// Overloads the basic form of the function, allowing you to pass a string object in which + /// to return the concatenated string. It is otherwise identical; see details for the canonical form. + /// + + static void SeparateArrayItems ( TXMPMeta * xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits options, + const tStringObj & catedStr ); + + /// @brief \c ApplyTemplate() modifies a working XMP object according to a template object. + /// + /// The XMP template can be used to add, replace or delete properties from the working XMP object. + /// This function replaces the previous \c AppendProperties() function, which is no longer available. + /// The actions that you specify determine how the template is applied. Each action can be applied + /// individually or combined; if you do not specify any actions, the properties and values in the working + /// XMP object do not change. + /// + /// These actions are available: + /// \li Clear (\c #kXMPTemplate_ClearUnnamedProperties): Deletes top-level properties. + /// Any top-level property that is present in the template (even with empty value) + /// is retained. All other top-level properties in the working object are deleted. + /// + /// \li Add (\c #kXMPTemplate_AddNewProperties): Adds new properties to the working object if the + /// template properties have values. See additional detail below. + /// + /// \li Replace (\c #kXMPTemplate_ReplaceExistingProperties): Replaces the values of existing top-level + /// properties in the working XMP if the value forms match those in the template. Properties + /// with empty values in the template are ignored. If combined with Clear or Add actions, + /// those take precedence; values are cleared or added, rather than replaced. + /// + /// \li Replace/Delete empty (\c #kXMPTemplate_ReplaceWithDeleteEmpty): Replaces values in the same way + /// as the simple Replace action, and also deletes properties if the value in the template is empty. + /// If combined with Clear or Add actions, those take precedence; values are cleared or added, + /// rather than replaced. + /// + /// \li Include internal (\c #kXMPTemplate_IncludeInternalProperties): Performs specified action + /// on internal properties as well as external properties. By default, internal properties + /// are ignored for all actions. + /// + /// The Add behavior depends on the type of property: + ///
    + ///
  • If a top-level property is not in the working XMP, and has a value in the template, + /// the property and value are added. Empty properties are not added.
  • + ///
  • If a property is in both the working XMP and template, the value forms must match, otherwise + /// the template is ignored for that property.
  • + ///
  • If a struct is present in both the working XMP and template, the individual fields of the + /// template struct are added as appropriate; that is, the logic is recursively applied to the fields. + /// Struct values are equivalent if they have the same fields with equivalent values.
  • + ///
  • If an array is present in both the working XMP and template, items from the template are + /// added if the value forms match. Array values match if they have sets of equivalent items, + /// regardless of order.
  • + ///
  • Alt-text arrays use the \c xml:lang qualifier as a key, adding languages that are missing.
  • + ///
+ /// Array item checking is n-squared; this can be time-intensive if the Replace option is + /// not specified. Each source item is checked to see if it already exists in the destination, + /// without regard to order or duplicates. Simple items are compared by value and \c xml:lang + /// qualifier; other qualifiers are ignored. Structs are recursively compared by field names, + /// without regard to field order. Arrays are compared by recursively comparing all items. + + /// @param workingXMP The destination XMP object. + /// + /// @param templateXMP The template to apply to the destination XMP object. + /// + /// @param actions Option flags to control the copying. If none are specified, the properties and values + /// in the working XMP do not change. A logical OR of these bit-flag constants: + /// \li \c #kXMPTemplate_ClearUnnamedProperties -- Delete anything that is not in the template + /// \li \c #kXMPTemplate_AddNewProperties -- Add properties; see detailed description. + /// \li \c #kXMPTemplate_ReplaceExistingProperties -- Replace the values of existing properties. + /// \li \c #kXMPTemplate_ReplaceWithDeleteEmpty -- Replace the values of existing properties + /// and delete properties if the new value is empty. + /// \li \c #kXMPTemplate_IncludeInternalProperties -- Operate on internal properties as well as + /// external properties. + /// + + static void ApplyTemplate ( TXMPMeta * workingXMP, + const TXMPMeta & templateXMP, + XMP_OptionBits actions ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c RemoveProperties() removes multiple properties from an XMP object. + /// + /// The operation depends on how the namespace and property are specified: + /// + /// \li Non-empty \c schemaNS and \c propName - The named property is removed if it is an + /// external property, or if the \c #kXMPUtil_DoAllProperties option flag is set. It does not + /// matter whether the named property is an actual property or an alias. + /// + /// \li Non-empty \c schemaNS and empty \c propName - All external properties in the named + /// schema are removed. Internal properties are also removed if the + /// \c #kXMPUtil_DoAllProperties option flag is set. In addition, aliases from the named schema + /// are removed if the \c #kXMPUtil_IncludeAliases option flag is set. + /// + /// \li Empty \c schemaNS and empty \c propName - All external properties in all schemas are + /// removed. Internal properties are also removed if the \c #kXMPUtil_DoAllProperties option + /// flag is set. Aliases are handled implicitly, because the associated actuals are removed or + /// not. + /// + /// \li It is an error to pass an empty \c schemaNS and non-empty \c propName. + /// + /// @param xmpObj The XMP object containing the properties to be removed. + /// + /// @param schemaNS Optional schema namespace URI for the properties to be removed. + /// + /// @param propName Optional path expression for the property to be removed. + /// + /// @param options Option flags to control the deletion operation. A logical OR of these + /// bit-flag constants: + /// \li \c #kXMPUtil_DoAllProperties - Delete internal properties in addition to external properties. + /// \li \c #kXMPUtil_IncludeAliases - Include aliases if the schema is explicitly specified. + + static void RemoveProperties ( TXMPMeta * xmpObj, + XMP_StringPtr schemaNS = 0, + XMP_StringPtr propName = 0, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c DuplicateSubtree() replicates a subtree from one XMP object into another. + /// + /// The destination can be a different namespace and root location in the same object, or the + /// same or a different location in another XMP object. + /// + /// @param source The source XMP object. + /// + /// @param dest The destination XMP object. + /// + /// @param sourceNS The schema namespace URI for the source subtree. + /// + /// @param sourceRoot The root location for the source subtree. Can be a general path expression, + /// must not be null or the empty string. + /// + /// @param destNS The schema namespace URI for the destination. Defaults to the source namespace. + /// + /// @param destRoot The root location for the destination. Can be a general path expression. + /// Defaults to the source location. + /// + /// @param options Option flags to control the operation. <> + + static void DuplicateSubtree ( const TXMPMeta & source, + TXMPMeta * dest, + XMP_StringPtr sourceNS, + XMP_StringPtr sourceRoot, + XMP_StringPtr destNS = 0, + XMP_StringPtr destRoot = 0, + XMP_OptionBits options = 0 ); + + /// @} + + // ============================================================================================= + +private: + + static void SetClientString ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen ); + +}; // class TXMPUtils + +// ================================================================================================= + +#endif // __TXMPUtils_hpp__ diff --git a/xmp/XMP.hpp b/xmp/XMP.hpp new file mode 100644 index 0000000..8ec39eb --- /dev/null +++ b/xmp/XMP.hpp @@ -0,0 +1,98 @@ +#ifndef __XMP_hpp__ +#define __XMP_hpp__ 1 + +// ================================================================================================= +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +// ================================================================================================ +/// \file XMP.hpp +/// \brief Overall header file for the XMP Toolkit +/// +/// This is an overall header file, the only one that C++ clients should include. +/// +/// The full client API is in the \c TXMPMeta.hpp, \c TXMPIterator.hpp, \c TXMPUtils.hpp headers. +/// Read these for information, but do not include them directly. The \c TXMP... classes are C++ +/// template classes that must be instantiated with a string class such as \c std::string. The +/// string class is used to return text strings for property values, serialized XMP, and so on. +/// Clients must also compile \c XMP.incl_cpp to ensure that all client-side glue code is generated. +/// This should be done by including it in exactly one client source file. +/// +/// There are two C preprocessor macros that simplify use of the templates: +/// +/// \li \c TXMP_STRING_TYPE - Define this as the string class to use with the template. You will get +/// the template headers included and typedefs (\c SXMPMeta, and so on) to use in your code. +/// +/// \li \c TXMP_EXPAND_INLINE - Define this as 1 if you want to have the template functions expanded +/// inline in your code. Leave it undefined, or defined as 0, to use out-of-line instantiations of +/// the template functions. Compiling \c XMP.incl_cpp generates explicit out-of-line +/// instantiations if \c TXMP_EXPAND_INLINE is off. +/// +/// The template parameter, class \c tStringObj, must have the following member functions (which +/// match those for \c std::string): +/// +///
+///  tStringObj& assign ( const char * str, size_t len )
+///  size_t size() const
+///  const char * c_str() const
+/// 
+/// +/// The string class must be suitable for at least UTF-8. This is the encoding used for all general +/// values, and is the default encoding for serialized XMP. The string type must also be suitable +/// for UTF-16 or UTF-32 if those serialization encodings are used. This mainly means tolerating +/// embedded 0 bytes, which \c std::string does. +// ================================================================================================ + +/// /c XMP_Environment.h must be the first included header. +#include "XMP_Environment.h" + +#include "XMP_Version.h" +#include "XMP_Const.h" + +#if XMP_WinBuild + #if XMP_DebugBuild + #pragma warning ( push, 4 ) + #else + #pragma warning ( push, 3 ) + #endif + #pragma warning ( disable : 4702 ) // unreachable code + #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning) +#endif + +#if defined ( TXMP_STRING_TYPE ) + + #include "TXMPMeta.hpp" + #include "TXMPIterator.hpp" + #include "TXMPUtils.hpp" + typedef class TXMPMeta SXMPMeta; // For client convenience. + typedef class TXMPIterator SXMPIterator; + typedef class TXMPUtils SXMPUtils; + #if TXMP_EXPAND_INLINE + #error "TXMP_EXPAND_INLINE is not working at present. Please don't use it." + #include "client-glue/TXMPMeta.incl_cpp" + #include "client-glue/TXMPIterator.incl_cpp" + #include "client-glue/TXMPUtils.incl_cpp" + #include "client-glue/TXMPFiles.incl_cpp" + #endif + + #if XMP_INCLUDE_XMPFILES + #include "TXMPFiles.hpp" // ! Needs typedef for SXMPMeta. + typedef class TXMPFiles SXMPFiles; + #if TXMP_EXPAND_INLINE + #include "client-glue/TXMPFiles.incl_cpp" + #endif + #endif + +#endif // TXMP_STRING_TYPE + +#if XMP_WinBuild + #pragma warning ( pop ) +#endif + +// ================================================================================================= + +#endif // __XMP_hpp__ diff --git a/xmp/XMP.incl_cpp b/xmp/XMP.incl_cpp new file mode 100644 index 0000000..fe2a6fa --- /dev/null +++ b/xmp/XMP.incl_cpp @@ -0,0 +1,69 @@ +#ifndef __XMP_incl_cpp__ +#define __XMP_incl_cpp__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +// ================================================================================================ +/// \file XMP.incl_cpp +/// \brief Overall client glue file for the XMP toolkit. +/// +/// This is an overall client source file of XMP toolkit glue, the only XMP-specific one that +/// clients should build in projects. This ensures that all of the client-side glue code for the +/// XMP toolkit gets compiled. +/// +/// You cannot compile this file directly, because the template's string type must be declared and +/// only the client can do that. Instead, include this in some other source file. For example, +/// to use std::string you only need these two lines: +/// +/// \code +/// #include +/// #include "XMP.incl_cpp" +/// \endcode + + +#include "XMP.hpp" // ! This must be the first include! + +#define XMP_ClientBuild 1 + +#if XMP_WinBuild + #if XMP_DebugBuild + #pragma warning ( push, 4 ) + #else + #pragma warning ( push, 3 ) + #endif + + #pragma warning ( disable : 4127 ) // conditional expression is constant + #pragma warning ( disable : 4189 ) // local variable is initialized but not referenced + #pragma warning ( disable : 4702 ) // unreachable code + #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning) +#endif + +#if defined ( TXMP_STRING_TYPE ) && (! TXMP_EXPAND_INLINE) + + // We're using a single out of line instantiation. Do it here. + + #include "client-glue/TXMPMeta.incl_cpp" + #include "client-glue/TXMPIterator.incl_cpp" + #include "client-glue/TXMPUtils.incl_cpp" + template class TXMPMeta ; + template class TXMPIterator ; + template class TXMPUtils ; + #if XMP_INCLUDE_XMPFILES + #include "client-glue/TXMPFiles.incl_cpp" + template class TXMPFiles ; + #endif + +#endif + +#if XMP_WinBuild + #pragma warning ( pop ) +#endif + +#endif // __XMP_incl_cpp__ diff --git a/xmp/XMPCommon/Interfaces/BaseInterfaces/IConfigurable.h b/xmp/XMPCommon/Interfaces/BaseInterfaces/IConfigurable.h new file mode 100644 index 0000000..e7ba7c6 --- /dev/null +++ b/xmp/XMPCommon/Interfaces/BaseInterfaces/IConfigurable.h @@ -0,0 +1,225 @@ +//! +//! @file IConfigurable.h +//! + +#ifndef IConfigurable_h__ +#define IConfigurable_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCommon/XMPCommonFwdDeclarations.h" + +namespace AdobeXMPCommon { + + //! + //! \brief Interface that allows to attach various key-value parameters to the underlying object. + //! + //! \details Key is an unsigned 64-bit integer value which can be a char buffer of eight characters also. + //! \note For all value types except user data ( const void * ) or char buffer ( const char * ) a copy is made + //! and is stored, so the scope is maintained internally. But for user data ( const void * ) or char buffer + //! ( const char * ) its clients responsibility to make sure these pointers remain valid through out the life + //! span of the object or objects derived from it. + //! + class XMP_PUBLIC IConfigurable + { + public: + + //! + //! @brief Indicates various types of parameter values. + //! + typedef enum { + //!< Data type is none. + kDTNone = 0, + //!< Data type is boolean. + kDTBool = 1 << 0, + //!< Data type is unsigned 64 bit integer. + kDTUint64 = 1 << 1, + //!< Data type is signed 64 bit integer. + kDTInt64 = 1 << 2, + //!< Data type is character. + kDTChar = 1 << 3, + //!< Data type is double. + kDTDouble = 1 << 4, + //!< Data type is char buffer. + kDTConstCharBuffer = 1 << 5, + //!< Data type is user data ( pointer to const void ). + kDTConstVoidPtr = 1 << 6, + + //!< Maximum value this enum can hold. + kDTAll = 0xFFFFFFFF + } eDataType; + + //! + //! @{ + //! @brief Add/Change a value of a parameter. + //! \param[in] key An unsigned 64 bit integer value indicating the key. + //! \param[in] value New value of the parameter. + //! \attention Error is thrown in case + //! - the previous type of value associated with key is of different type. + //! - the type of value associated with key is not as expected. + //! + virtual void APICALL SetParameter( const uint64 & key, bool value ) = 0; + virtual void APICALL SetParameter( const uint64 & key, uint64 value ) = 0; + virtual void APICALL SetParameter( const uint64 & key, int64 value ) = 0; + virtual void APICALL SetParameter( const uint64 & key, double value ) = 0; + virtual void APICALL SetParameter( const uint64 & key, char value ) = 0; + virtual void APICALL SetParameter( const uint64 & key, const char * value ) = 0; + virtual void APICALL SetParameter( const uint64 & key, const void * value ) = 0; + //! @} + + //! + //! @brief Removes a particular parameter if present. + //! \param[in] key An unsigned 64 bit integer value indicating the key. + //! \return True in case key was present and is deleted. + //! \attention Error is thrown in case + //! - key is a must have for the underlying object. + //! + virtual bool APICALL RemoveParameter( const uint64 & key ) = 0; + + //! + //! @{ + //! @brief Get the value of a parameter if present. + //! \param[in] key An unsigned 64 bit integer value indicating the key. + //! \param[out] value The value of the parameter. + //! \return false if no such parameter is present, otherwise true. + //! \attention Error is thrown in case the type of the parameter is not + //! the one client is asking for. + //! + virtual bool APICALL GetParameter( const uint64 & key, bool & value ) const = 0; + virtual bool APICALL GetParameter( const uint64 & key, uint64 & value ) const = 0; + virtual bool APICALL GetParameter( const uint64 & key, int64 & value ) const = 0; + virtual bool APICALL GetParameter( const uint64 & key, double & value ) const = 0; + virtual bool APICALL GetParameter( const uint64 & key, char & value ) const = 0; + virtual bool APICALL GetParameter( const uint64 & key, const char * & value ) const = 0; + virtual bool APICALL GetParameter( const uint64 & key, const void * & value ) const = 0; + //! @} + + //! + //! @brief Get all the keys of the parameters associated with the object. + //! \details Provide a std::vector containing the keys of all the parameters associated with the object. + //! \return A std::vector of unsigned 64 bit integers. + //! + virtual std::vector< uint64 > APICALL GetAllParameters() const = 0; + + //! + //! @brief Get the number of parameters associated with the object. + //! + virtual sizet APICALL Size() const __NOTHROW__ = 0; + + //! + //! @brief Get the value type of a particular parameter. + //! \param[in] key An unsigned 64 bit integer value indicating the key. + //! \return A value of type eDataType indicating the type of value the parameter is supposed to hold. + //! \note return kDTNone in case no such key is associated with the object. + //! + virtual eDataType APICALL GetDataType( const uint64 & key ) const = 0; + + //! + //! @brief Utility function to convert character buffer ( maximum of 8 characters ) to uint64 representation. + //! \param[in] key A pointer to const char buffer, maximum characters used are 8 provided there is no + //! null character present in the buffer between 1st to 8 characters, otherwise characters upto NULL + //! character (excluding NULL) are read. + //! \return A 64-bit unsigned integer representing the first 8 characters of the character buffer. + //! \note Return 0 in case key is NULL. + //! + static uint64 ConvertCharBufferToUint64( const char * key ) { + uint64 keyAsuint64 = 0; + if ( key ) { + for ( int i = 0; i < 8 && key[ i ] != '\0'; i++ ) { + keyAsuint64 = keyAsuint64 << 8; + keyAsuint64 += ( unsigned char ) key[ i ]; + } + } + return keyAsuint64; + } + + //! + //! @brief A union data type to store all kind of values. + //! + union CombinedDataValue { + bool boolValue; + uint32 uint32Value; + uint64 uint64Value; + int64 int64Value; + double doubleValue; + char charValue; + const char * constCharPtrValue; + const void * constVoidPtrValue; + }; + + protected: + //! \cond XMP_INTERNAL_DOCUMENTATION + // all safe functions + virtual void APICALL setParameter( const uint64 & key, uint32 dataType, const CombinedDataValue & dataValue, pcIError_base & error ) __NOTHROW__ = 0; + virtual uint32 APICALL removeParameter( const uint64 & key, pcIError_base & error ) __NOTHROW__ = 0; + virtual uint32 APICALL getParameter( const uint64 & key, uint32 dataType, CombinedDataValue & value, pcIError_base & error ) const __NOTHROW__ = 0; + virtual void APICALL getAllParameters( uint64 * array, sizet count ) const __NOTHROW__ = 0; + virtual uint32 APICALL getDataType( const uint64 & key, pcIError_base & error ) const __NOTHROW__ = 0; + //! \endcond + + protected: + //! + //! protected Virtual Destructor + //! + virtual ~IConfigurable() __NOTHROW__ {}; + + friend class IConfigurableProxy; + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + }; + +//! \cond XMP_INTERNAL_DOCUMENTATION +#if !BUILDING_XMPCOMMON_LIB && !SOURCE_COMPILING_XMP_ALL + + class IConfigurableProxy + : public virtual IConfigurable { + public: + IConfigurableProxy( pIConfigurable configurable ); + virtual void APICALL SetParameter( const uint64 & key, bool value ); + virtual void APICALL SetParameter( const uint64 & key, uint64 value ); + virtual void APICALL SetParameter( const uint64 & key, int64 value ); + virtual void APICALL SetParameter( const uint64 & key, double value ); + virtual void APICALL SetParameter( const uint64 & key, char value ); + virtual void APICALL SetParameter( const uint64 & key, const char * value ); + virtual void APICALL SetParameter( const uint64 & key, const void * value ); + virtual void APICALL setParameter( const uint64 & key, uint32 dataType, const CombinedDataValue & dataValue, pcIError_base & error ) __NOTHROW__; + + virtual bool APICALL RemoveParameter( const uint64 & key ); + virtual uint32 APICALL removeParameter( const uint64 & key, pcIError_base & error ) __NOTHROW__; + + virtual bool APICALL GetParameter( const uint64 & key, bool & value ) const; + virtual bool APICALL GetParameter( const uint64 & key, uint64 & value ) const; + virtual bool APICALL GetParameter( const uint64 & key, int64 & value ) const; + virtual bool APICALL GetParameter( const uint64 & key, double & value ) const; + virtual bool APICALL GetParameter( const uint64 & key, char & value ) const; + virtual bool APICALL GetParameter( const uint64 & key, const char * & value ) const; + virtual bool APICALL GetParameter( const uint64 & key, const void * & value ) const; + virtual uint32 APICALL getParameter( const uint64 & key, uint32 dataType, CombinedDataValue & value, pcIError_base & error ) const __NOTHROW__; + + virtual std::vector< uint64 > APICALL GetAllParameters() const; + virtual void APICALL getAllParameters( uint64 * array, sizet count ) const __NOTHROW__; + + virtual sizet APICALL Size() const __NOTHROW__; + + virtual eDataType APICALL GetDataType( const uint64 & key ) const; + virtual uint32 APICALL getDataType( const uint64 & key, pcIError_base & error ) const __NOTHROW__; + + protected: + pIConfigurable mConfigurableRawPtr; + }; + +#endif // !BUILDING_XMPCOMMON_LIB && !SOURCE_COMPILING_XMP_ALL +//! \endcond + +} + +#endif // IConfigurable_h__ diff --git a/xmp/XMPCommon/Interfaces/BaseInterfaces/ISharedObject.h b/xmp/XMPCommon/Interfaces/BaseInterfaces/ISharedObject.h new file mode 100644 index 0000000..4b5f86f --- /dev/null +++ b/xmp/XMPCommon/Interfaces/BaseInterfaces/ISharedObject.h @@ -0,0 +1,71 @@ +#ifndef __ISharedObject_h__ +#define __ISharedObject_h__ 1 + +// ================================================================================================= +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCommon/XMPCommonFwdDeclarations.h" + +namespace AdobeXMPCommon { + + //! + //! \brief Interface that serves as the base interface of all the externally exposed interfaces. + //! \details This allows all interfaces to be used as shared pointers so as to reduce the burden of + //! scope management from the client or library code. It makes the heap allocated object to be self manged in + //! in terms of memory and life. This provides functions so as to inform the actual object when a shared pointer + //! is created or destroyed and appropriately release the memory during the last call to Release. + //! \attention Supports Multi-threading at object level through use of Atomic Variables. + //! \note Any interface which inherits from this needs to make sure that its destructor is declared protected + //! so that unknowingly also client of this object cannot call delete on the object. + //! + + class XMP_PUBLIC ISharedObject { + public: + + //! + //! @brief Called by the clients of the object to indicate that he has acquired the shared ownership of the object. + //! + virtual void APICALL Acquire() const __NOTHROW__ = 0; + + //! + //! @brief Called by the clients of the object to indicate he has released his shared ownership of the object. + //! If this being the last client than this function should call Destroy to delete and release the memory. + //! + virtual void APICALL Release() const __NOTHROW__ = 0; + + //! @{ + //! \cond XMP_INTERNAL_DOCUMENTATION + //! Return the pointer to the internal Shared Object interface + //! \return either a const or non const pointer to internal ISharedObject_I interface. + virtual AdobeXMPCommon_Int::pISharedObject_I APICALL GetISharedObject_I() __NOTHROW__ = 0; + + XMP_PRIVATE AdobeXMPCommon_Int::pcISharedObject_I GetISharedObject_I() const __NOTHROW__ { + return const_cast< ISharedObject * >( this )->GetISharedObject_I(); + } + //! \endcond + //! @} + + protected: + + //! + //! protected virtual destructor. + //! + virtual ~ISharedObject() __NOTHROW__ = 0; + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + + REQ_FRIEND_CLASS_DECLARATION(); + }; + + inline ISharedObject::~ISharedObject() __NOTHROW__ { } + +}; + +#endif // __ISharedObject_h__ diff --git a/xmp/XMPCommon/Interfaces/BaseInterfaces/IThreadSafe.h b/xmp/XMPCommon/Interfaces/BaseInterfaces/IThreadSafe.h new file mode 100644 index 0000000..b0ed4b5 --- /dev/null +++ b/xmp/XMPCommon/Interfaces/BaseInterfaces/IThreadSafe.h @@ -0,0 +1,75 @@ +//! @file IThreadSafe.h +#ifndef IThreadSafe_h__ +#define IThreadSafe_h__ 1 + +// +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= +// + +#include "XMPCommon/XMPCommonFwdDeclarations.h" + +namespace AdobeXMPCommon { + + //! + //! \brief Interface that serves as the base interface for all the externally exposed interfaces which needs to provide client configurable thread safety. + //! + //! \attention In case client has disabled thread safety at the module level these functions will + //! have no use. + //! \note By default all the objects created are not thread safe. + //! + class XMP_PUBLIC IThreadSafe + { + public: + + //! + //! @brief Enables the thread safety on an object. + //! @details After calling this function the object can be used across multiple threads. + //! + virtual void APICALL EnableThreadSafety() const __NOTHROW__ = 0; + + //! + //! @brief Disables the thread safety on an object. + //! @details After calling this function the object should not be used across multiple threads. + //! + virtual void APICALL DisableThreadSafety() const __NOTHROW__ = 0; + + //! + //! @brief Informs whether object can be used across multiple threads or not. + //! \returns bool value; true in case object can be used across multiple threads, false + //! otherwise. + //! + virtual bool APICALL IsThreadSafe() const = 0; + + //! @{ + //! \cond XMP_INTERNAL_DOCUMENTATION + //! @brief Return the pointer to the internal Thread Safe interface + //! \return either a const or non const pointer to internal IThreadSafe_I interface. + virtual AdobeXMPCommon_Int::pIThreadSafe_I APICALL GetIThreadSafe_I() __NOTHROW__ = 0; + + XMP_PRIVATE AdobeXMPCommon_Int::pcIThreadSafe_I GetIThreadSafe_I() const __NOTHROW__ { + return const_cast< IThreadSafe * >( this )->GetIThreadSafe_I(); + } + //! \endcond + //! @} + + protected: + //! \cond XMP_INTERNAL_DOCUMENTATION + //! @brief all safe functions + virtual uint32 APICALL isThreadSafe() const __NOTHROW__ = 0; + //! \endcond + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + }; + +} +#endif // IThreadSafe_h__ diff --git a/xmp/XMPCommon/Interfaces/BaseInterfaces/IVersionable.h b/xmp/XMPCommon/Interfaces/BaseInterfaces/IVersionable.h new file mode 100644 index 0000000..ec60747 --- /dev/null +++ b/xmp/XMPCommon/Interfaces/BaseInterfaces/IVersionable.h @@ -0,0 +1,71 @@ +#ifndef IVersionable_h__ +#define IVersionable_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCommon/XMPCommonFwdDeclarations.h" + +namespace AdobeXMPCommon { + + //! + //! \brief Interface that serves as the base interface for all the externally exposed interfaces + //! which needs to provide evolving versions of the interface. + //! @details Provide pointer to interface requested by client. + //! Requirements on the class type + //! -# Need to implement a function GetInterfaceID() returning a unique id for the + //! interface. Only required to be implemented in first version of the interface. + //! -# Need to implement a function GetVersionNumber() returning the version of the + //! interface. Required to implemented by each version of the interface. + //! + + class XMP_PUBLIC IVersionable { + public: + + //! + //! @brief Get the raw pointer to an interface object implementing the requested version. + //! \return a raw pointer to an interface object implementing the requested version. + //! \attention In case a particular version number is not supported than an error is + //! thrown. + //! + template< typename requestedInterface > + XMP_PRIVATE requestedInterface * GetInterfacePointer() { + pvoid ptr = GetInterfacePointer( requestedInterface::GetInterfaceID(), + requestedInterface::GetInterfaceVersion() ); + return static_cast< requestedInterface * >( ptr ); + } + + //! + //! @brief Get the raw pointer to a const interface object implementing the requested version. + //! \return a raw pointer to a const interface object implementing the requested version. + //! \attention In case a particular version number is not supported than an error is + //! thrown. + //! + template< typename requestedInterface > + XMP_PRIVATE const requestedInterface * GetInterfacePointer() const { + return const_cast< IVersionable * >( this )->GetInterfacePointer< requestedInterface >(); + } + + protected: + virtual ~IVersionable() {} + //! \cond XMP_INTERNAL_DOCUMENTATION + virtual pvoid APICALL GetInterfacePointer( uint64 interfaceID, uint32 interfaceVersion ) = 0; + // all safe functions + virtual pvoid APICALL getInterfacePointer( uint64 interfaceID, uint32 interfaceVersion, pcIError_base & error ) __NOTHROW__ = 0; + //! \endcond + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + }; + +} + +#endif // IVersionable_h__ diff --git a/xmp/XMPCommon/Interfaces/IConfigurationManager.h b/xmp/XMPCommon/Interfaces/IConfigurationManager.h new file mode 100644 index 0000000..5581749 --- /dev/null +++ b/xmp/XMPCommon/Interfaces/IConfigurationManager.h @@ -0,0 +1,167 @@ +//! @file IConfigurationManager.h + +#ifndef IConfigurationManager_h__ +#define IConfigurationManager_h__ 1 + +// +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= +// + +#include "XMPCommon/Interfaces/BaseInterfaces/ISharedObject.h" +#include "XMPCommon/Interfaces/BaseInterfaces/IVersionable.h" + +#include "XMPCommon/XMPCommonFwdDeclarations.h" + +namespace AdobeXMPCommon { + + //! + //! @class IConfigurationManager_v1 + //! \brief Version1 of the interface that represents configuration settings controllable by the client. + //! \details Provides functions through which client can plug in its own memory allocators, error notifiers. + //! \attention Not Thread Safe as this functionality is generally used at the initialization phase. + //! + class XMP_PUBLIC IConfigurationManager_v1 + : public virtual ISharedObject + , public virtual IVersionable + { + public: + + //! + //! @brief Allows the client to plug in its own memory allocation procedures which will be used to allocate/deallocate memory from the heap. + //! + //! \param[in] memoryAllocator A pointer to an object of type AdobeXMPCommon::IMemoryAllocator. + //! NULL pointer will switch to default allocator built in the library. + //! \return A value of bool type; true means successful and false otherwise. + //! + virtual bool APICALL RegisterMemoryAllocator( pIMemoryAllocator memoryAllocator ) = 0; + + //! + //! @brief Allows the client to plug in its own error notification procedures which will be used to + //! inform client about various warnings and errors. + //! \param[in] clientErrorNotifier A pointer to an object of type AdobeXMPCommon::IErrorNotifier. NULL + //! pointer means client no longer wants to be notified of any warnings or errors. + //! \return a value of bool type; true means successful and false otherwise. + //! + virtual bool APICALL RegisterErrorNotifier( pIErrorNotifier_base clientErrorNotifier ) = 0; + + //! + //! @brief Allows the client to disable the support for multi threading inside the library. + //! By default library supports multi-threading. + //! \return A value of bool type; true means successful and false otherwise. + //! + virtual bool APICALL DisableMultiThreading() = 0; + + //! + //! @brief Returns whether library supports multi threading or not + //! \return A value of bool type; true means it supports multi threading and false otherwise. + //! + virtual bool APICALL IsMultiThreaded() const = 0; + + //! + //! \cond XMP_INTERNAL_DOCUMENTATION + //! @{ + //! @brief Returns the actual raw pointer from the shared pointer, which can be a shared pointer of a proxy class. + //! \return Either a const or non const pointer to IConfigurationManager interface. + //! + virtual pIConfigurationManager APICALL GetActualIConfigurationManager() __NOTHROW__ = 0; + XMP_PRIVATE pcIConfigurationManager GetActualIConfigurationManager() const __NOTHROW__ { + return const_cast< IConfigurationManager_v1 * >( this )->GetActualIConfigurationManager(); + } + //! + //! @} + + //! + //! @{ + //! @brief Convert raw pointer to shared pointer. + //! @detail The raw pointer is of version 1 interface where as the returned shared pointer depends on the version client is interested in. + //! + //! \return Shared pointer to const or non constant IConfigurationManager interface. + //! + XMP_PRIVATE static spIConfigurationManager MakeShared( pIConfigurationManager_base ptr ); + XMP_PRIVATE static spcIConfigurationManager MakeShared( pcIConfigurationManager_base ptr ) { + return MakeShared( const_cast< pIConfigurationManager_base >( ptr ) ); + } + //! + //! @} + + //! + //! @brief Return the unique ID assigned to the interface. + //! \return 64 bit unsigned integer representing the unique ID assigned to the interface. + //! + XMP_PRIVATE static uint64 GetInterfaceID() { return kIConfigurationManagerID; } + + //! + //! @brief Returns the version of the interface. + //! \return 32 bit unsigned integer representing the version of the interface. + //! + XMP_PRIVATE static uint32 GetInterfaceVersion() { return 1; } + //! \endcond + + virtual ~IConfigurationManager_v1() __NOTHROW__ {} + + protected: + + //! \cond XMP_INTERNAL_DOCUMENTATION + virtual uint32 APICALL registerMemoryAllocator( pIMemoryAllocator_base memoryAllocator, pcIError_base & error ) __NOTHROW__ = 0; + virtual uint32 APICALL registerErrorNotifier( pIErrorNotifier_base clientErrorNotifier, pcIError_base & error ) __NOTHROW__ = 0; + virtual uint32 APICALL disableMultiThreading( pcIError_base & error ) __NOTHROW__ = 0; + virtual uint32 APICALL isMultiThreaded( pcIError_base & error ) const __NOTHROW__ = 0; + //! \endcond + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + //! \endcond + }; +} + + +#if !BUILDING_XMPCOMMON_LIB && !SOURCE_COMPILING_XMP_ALL + +namespace AdobeXMPCommon { + class IConfigurationManagerProxy + : public virtual IConfigurationManager + { + private: + pIConfigurationManager mRawPtr; + + public: + IConfigurationManagerProxy( pIConfigurationManager ptr ) + : mRawPtr( ptr ) + { + mRawPtr->Acquire(); + } + + ~IConfigurationManagerProxy() __NOTHROW__ { mRawPtr->Release(); } + pIConfigurationManager APICALL GetActualIConfigurationManager() __NOTHROW__ { return mRawPtr; } + AdobeXMPCommon_Int::pISharedObject_I APICALL GetISharedObject_I() __NOTHROW__ { return mRawPtr->GetISharedObject_I(); } + + void APICALL Acquire() const __NOTHROW__; + void APICALL Release() const __NOTHROW__; + pvoid APICALL GetInterfacePointer( uint64 interfaceID, uint32 interfaceVersion ); + virtual bool APICALL RegisterMemoryAllocator( pIMemoryAllocator memoryAllocator ); + virtual bool APICALL RegisterErrorNotifier( pIErrorNotifier_base clientErrorNotifier ); + virtual bool APICALL DisableMultiThreading(); + virtual bool APICALL IsMultiThreaded() const; + + protected: + virtual uint32 APICALL registerMemoryAllocator( pIMemoryAllocator_base memoryAllocator, pcIError_base & error ) __NOTHROW__; + virtual uint32 APICALL registerErrorNotifier( pIErrorNotifier_base clientErrorNotifier, pcIError_base & error ) __NOTHROW__; + virtual uint32 APICALL disableMultiThreading( pcIError_base & error ) __NOTHROW__; + virtual uint32 APICALL isMultiThreaded( pcIError_base & error ) const __NOTHROW__; + pvoid APICALL getInterfacePointer( uint64 interfaceID, uint32 interfaceVersion, pcIError_base & error ) __NOTHROW__; + }; + +} + +#endif // BUILDING_XMPCOMMON_LIB && !SOURCE_COMPILING_XMP_LIB + +#endif // IConfigurationManager_h__ diff --git a/xmp/XMPCommon/Interfaces/IError.h b/xmp/XMPCommon/Interfaces/IError.h new file mode 100644 index 0000000..15955ba --- /dev/null +++ b/xmp/XMPCommon/Interfaces/IError.h @@ -0,0 +1,377 @@ +//! @file IError.h +#ifndef __IError_h__ +#define __IError_h__ 1 + +// ================================================================================================= +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================ + +#include "XMPCommon/Interfaces/BaseInterfaces/ISharedObject.h" +#include "XMPCommon/Interfaces/BaseInterfaces/IVersionable.h" + +#include "XMPCommon/XMPCommonFwdDeclarations.h" + +namespace AdobeXMPCommon { + + //! + //! @brief Version1 of the interface that represents an error/warning encountered during processing. + //! \details Provides all the functions to get required information regarding error scenario. + //! \attention Do Not support Multi-threading at object level. + //! \attention Multi-threading not required since clients will only be provided const objects. + //! + class XMP_PUBLIC IError_v1 + : public virtual ISharedObject + , public virtual IVersionable + { + public: + //! + //! @brief Indicates various types of errors. + //! + typedef enum { + //! No severity, not to be used. + kESNone = 0, + + //! Recovery is possible, client can choose to ignore and let library continue with the best possible way. + kESWarning = 1 << 0, + + //! Recovery is not possible, an exception of type pcIError_base will be thrown aborting the API call. + kESOperationFatal = 1 << 1, + + //! Recovery is not possible, an exception of type pcIError_base will be thrown, client should abort the process. + kESProcessFatal = 1 << 2, + + // Add new severities here + + //! Maximum value this enum can hold, should be treated as invalid value. + kESMaxValue = 1 << 31, + + //! Union of all severities + kESAll = kAllBits, + } eErrorSeverity; + + + //! + //! @brief Indicates various types of error domains. + //! + typedef enum { + //! No Domain + kEDNone = 0, + + //! Indicates error related to general conditions. + kEDGeneral = 1, + + //! Indicates error related to memory allocation-deallocation conditions. + kEDMemoryManagement = 2, + + //! Indicates error related to configurable APIs. + kEDConfigurable = 3, + + //! Indicates error releated to multithreading. + kEDMultiThreading = 4, + + //! Indicates error related to XMP Data Model Management. + kEDDataModel = 100, + + //! Indicates error related to XMP Parsing. + kEDParser = 101, + + //! Indicates error related to XMP Serializing. + kEDSerializer = 102, + + + //! Indicates error related to dealing with XMP in various file formats. + kEDXMPFiles = 200, + + + //! Indicates error related to Conflict Identification. + kEDConflictIdentification = 400, + + //! Indicates error related to Conflict Resolution. + kEDConflictResolution = 500, + + //! Indicates error related to 3 Way Merge. + kEDThreeWayMerge = 600, + + //! Indicates error related to Generic Strategy Database + kEDGenericStrategyDatabase = 601, + + //! Indicates error related to Asset Management. + kEDAssetManagement = 10000, + + //! Maximum value this enum can hold, should be treated as invalid value. + kEDMaxValue = kMaxEnumValue + } eErrorDomain; + + typedef uint32 eErrorCode; + + //! + //! @brief Get the error code. + //! \return An object of type #eErrorCode indicating the error code. + //! + virtual eErrorCode APICALL GetCode() const = 0; + + //! + //! @brief Get the error domain. + //! \return An object of type #eErrorDomain indicating the error domain. + //! + virtual eErrorDomain APICALL GetDomain() const = 0; + + //! + //! @brief Get the error severity. + //! \return An object of type #eErrorSeverity indicating the severity of error. + //! + virtual eErrorSeverity APICALL GetSeverity() const = 0; + + //! + //! @brief Get the error message. + //! \details Error message contains a descriptive string, for debugging use only. It must not be shown to users + //! in a final product. It is written for developers, not users, and never localized. + //! \return A shared pointer to const AdobeXMPCommon::IUTF8String object containing message string. + //! + virtual spcIUTF8String APICALL GetMessage() const = 0; + + //! + //! @brief Get the location of the error origin. + //! \return A shared pointer to const AdobeXMPCommon::IUTF8String object containing location as like file name + //! and line number. + //! \note For debugging use only. + //! + virtual spcIUTF8String APICALL GetLocation() const = 0; + + //! + //! @brief Get the value of a parameter at a particular index. + //! \details Based on each error condition various parameters are stored along with the error object. Clients can + //! one by one get access to each parameter that can be later used for debugging. + //! \param[in] index A value of \#AdobeXMPCommon::sizet indicating the index of the parameter client is + //! interested in retrieving. + //! \return A shared pointer to const \#AdobeXMPCommon::IUTF8String object containing some string. + //! \attention Throws \#AdobeXMPCommon::pcIError_base in case index is out of bounds. + //! \note For debugging use only. + //! + virtual spcIUTF8String APICALL GetParameter( sizet index ) const = 0; + + //! + //! @brief Get the count of parameters. + //! \return An object of type \#AdobeXMPCommon::sizet containing the count of paramaters associated with the error object. + //! + virtual sizet APICALL GetParametersCount() const __NOTHROW__ = 0; + + //! + //! @{ + //! @brief Get the next error in the chain. + //! \return A pointer to const/non-const \#AdobeXMPCommon::IError object which is the next error in the chain. + //! \note Return an invalid shared pointer in case it is the last error object in the chain. + //! + virtual spIError APICALL GetNextError() = 0; + + XMP_PRIVATE spcIError GetNextError() const { + return const_cast< IError_v1 * >( this )->GetNextError(); + } + //! @} + + //! + //! @brief Set the next error in the chain. + //! \param[in] error A pointer to \#AdobeXMP::IError object which will be the next error in the chain. + //! \return A pointer to \#AdobeXMPCommon::IError object which is the current next error in the chain. + //! \note Return an invalid pointer in case there is no current next error in the chain. + //! + virtual spIError APICALL SetNextError( const spIError & error ) = 0; + + //! + //! @brief Set the error message. + //! \param[in] message Pointer to a constant char buffer containing message. It can be null terminated or not. + //! NULL pointer will be treated as empty message string. + //! \param[in] len A value of \#AdobeXMPCommon::sizet indicating the length in case message is not null + //! terminated. In case message is null terminated it can be set to its default value ( npos ). + //! + virtual void APICALL SetMessage( const char * message, sizet len = npos ) __NOTHROW__ = 0; + + //! + //! @brief Set the location of origin of error. + //! \param[in] fileName Pointer to a null terminated char buffer containing the file name from which the error + //! originated. NULL pointer will be treated as empty fileName. + //! \param[in] lineNumber A value of \#AdobeXMPCommon::sizet indicating the line in source file from which the error + //! originated. + //! + virtual void APICALL SetLocation( const char * fileName, sizet lineNumber ) __NOTHROW__ = 0; + + //! + //! @brief Appends a parameter to the list of parameters. + //! \param[in] parameter Pointer to a constant char buffer containing parameter. It can be null terminated or not. + //! NULL pointer will be treated as empty message string. + //! \param[in] len A value of AdobeXMPCommon::sizet indicating the length in case parameter is not null + //! terminated. In case parameter is null terminated it can be set to its default value ( npos ). + //! + virtual void APICALL AppendParameter( const char * parameter, sizet len = npos ) __NOTHROW__ = 0; + + //! + //! @brief Appends an address as a string to the list of parameters. + //! \param[in] addressParameter A value of void * type containing the address of the location to be used as parameter. + //! + virtual void APICALL AppendParameter( void * addressParameter ) __NOTHROW__ = 0; + + //! + //! @brief Appends a 32 bit unsigned integer value as a string to the list of parameters. + //! \param[in] integerValue A value of AdobeXMPCommon::uint32 type containing the integral value to be used as parameter. + //! + virtual void APICALL AppendParameter( const uint32 & integerValue ) __NOTHROW__ = 0; + + //! + //! @brief Appends a 64 bit unsigned integer value as a string to the list of parameters. + //! \param[in] integerValue A value of AdobeXMPCommon::uint64 type containing the integral value to be used as parameter. + //! + virtual void APICALL AppendParameter( const uint64 & integerValue ) __NOTHROW__ = 0; + + //! + //! @brief Appends a 32 bit integer value as a string to the list of parameters. + //! \param[in] integerValue A value of AdobeXMPCommon::int32 type containing the integral value to be used as parameter. + //! + virtual void APICALL AppendParameter( const int32 & integerValue ) __NOTHROW__ = 0; + + //! + //! @brief Appends a 64 bit integer value as a string to the list of parameters. + //! \param[in] integerValue A value of AdobeXMPCommon::uint64 type containing the integral value to be used as parameter. + //! + virtual void APICALL AppendParameter( const int64 & integerValue ) __NOTHROW__ = 0; + + //! + //! @brief Appends a floating value as a string to the list of parameters. + //! \param[in] floatValue A value of float type containing the floating value to be used as parameter. + //! + virtual void APICALL AppendParameter( const float & floatValue ) __NOTHROW__ = 0; + + //! + //! @brief Appends a double floating value as a string to the list of parameters. + //! \param[in] doubleValue A value of double type containing the floating value to be used as parameter. + //! + virtual void APICALL AppendParameter( const double & doubleValue ) __NOTHROW__ = 0; + + //! + //! @brief Appends a boolean value as a string to the list of parameters. + //! \param[in] booleanValue A value of bool type containing the boolean value to be used as parameter. + //! + virtual void APICALL AppendParameter( bool booleanValue ) __NOTHROW__ = 0; + + //! + //! @brief Creates an error object. + //! \param[in] objFactory A pointer to IObjectFactory object. + //! \param[in] errDomain A value of #eErrorDomain indicating the error domain. + //! \param[in] errCode A value of #eErrorCode indicating the error code. + //! \param[in] errSeverity A value of #eErrorSeverity indicating the severity of the error. + //! \return A shared pointer to an object of IError_v1. + //! + static spIError CreateError( pIObjectFactory objFactory, eErrorDomain errDomain, + eErrorCode errCode, eErrorSeverity errSeverity ); + + //! + //! \cond XMP_INTERNAL_DOCUMENTATION + //! @{ + //! @brief Return the actual raw pointer from the pointer available to client, which can be of a proxy class. + //! \return Either a const or non const pointer to IError interface. + //! + virtual pIError APICALL GetActualIError() __NOTHROW__ = 0; + XMP_PRIVATE pcIError GetActualIError() const __NOTHROW__ { + return const_cast< IError_v1 * >( this )->GetActualIError(); + } + //! + //! @} + + //! + //! @{ + //! @brief Convert raw pointer to shared pointer. + //! @details The raw pointer is of version 1 interface where as the returned shared pointer depends on the version client who is interested in. + //! + //! \return Shared pointer to const or non constant IError interface. + //! + XMP_PRIVATE static spIError MakeShared( pIError_base ptr ); + XMP_PRIVATE static spcIError MakeShared( pcIError_base ptr ) { + return MakeShared( const_cast< pIError_base >( ptr ) ); + } + //! + //! @} + + //! + //! @brief Returns the unique ID assigned to the interface. + //! \return 64 bit unsigned integer representing the unique ID assigned to the interface. + //! + XMP_PRIVATE static uint64 GetInterfaceID() { return kIErrorID; } + + //! + //! @brief Returns the version of the interface. + //! \return 32 bit unsigned integer representing the version of the interface. + //! + XMP_PRIVATE static uint32 GetInterfaceVersion() { return 1; } + //! \endcond + + protected: + //! + //! Destructor + //! + virtual ~IError_v1() __NOTHROW__ {} + + //! \cond XMP_INTERNAL_DOCUMENTATION + virtual uint32 APICALL getCode( pcIError_base & error ) const __NOTHROW__ = 0; + virtual uint32 APICALL getDomain( pcIError_base & error ) const __NOTHROW__ = 0; + virtual uint32 APICALL getSeverity( pcIError_base & error ) const __NOTHROW__ = 0; + virtual pcIUTF8String_base APICALL getMessage( pcIError_base & error ) const __NOTHROW__ = 0; + virtual pcIUTF8String_base APICALL getLocation( pcIError_base & error ) const __NOTHROW__ = 0; + virtual pcIUTF8String_base APICALL getParameter( sizet index, pcIError_base & error ) const __NOTHROW__ = 0; + virtual pIError_base APICALL getNextError( pcIError_base & error ) __NOTHROW__ = 0; + virtual pIError_base APICALL setNextError( pIError_base nextError, pcIError_base & error ) __NOTHROW__ = 0; + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + //! \endcond + }; + + //! + //! @brief A function pointer to report back errors and warnings to the library encountered during the serialization operation. + //! @details Based on the error condition library can return 0 or non zero to indicate that a particular warning can be ignored + //! and operation can continue. + //! \param[in] errorDomain An unsigned 32 bit integer indicating the domain of the error. + //! \param[in] errorCode An unsigned 32 bit integer indicating the code of the error. + //! \param[in] errorSeverity An unsigned 32 bit integer indicating the severity of the error. + //! \param[out] error A reference to a pointer to const IError object which will be filled with the error object in case of any error. + //! \return non zero value indicating that process can continue ignoring the warning, otherwise return 0 to indicate it should stop immediately. + //! + typedef uint32( *ReportErrorAndContinueABISafeProc )( uint32 errorDomain, uint32 errorCode, uint32 errorSeverity, const char * message, pcIError_base & error ); + + //! + //! @brief A Function object used by the client to report back and warnings to the library encountered during the serialization operation. + //! @details Based on the error condition library can return 0 or non zero to indicate that a particular warning can be ignored + //! and operation can continue. + //! + class ReportErrorAndContinueFunctor { + public: + ReportErrorAndContinueFunctor( ReportErrorAndContinueABISafeProc safeProc ) + : mSafeProc( safeProc ) {} + + //! + //! \param[in] errorDomain A value of \#IError_v1::eErrorDomain indicating the domain of the error. + //! \param[in] errorCode A value of \#IError_v1::eErrorCode indicating the code of the error. + //! \param[in] errorSeverity A value of \#IError_v1::eErrorSeverity indicating the severity of the error. + //! \param[in] message Pointer to a constant char buffer containing message. + //! \return true value indicating that process can continue ignoring the warning, otherwise return false to indicate it should stop immediately. + //! + bool operator()( IError_v1::eErrorDomain errorDomain, IError_v1::eErrorCode errorCode, IError_v1::eErrorSeverity errorSeverity, const char * message ) { + pcIError_base error( NULL ); + auto retValue = mSafeProc( static_cast< uint32 >( errorDomain ), static_cast< uint32 >( errorCode ), static_cast< uint32 >( errorSeverity ), message, error ); + if ( error ) + throw IError_v1::MakeShared( error ); + return retValue != 0 ? true : false; + } + + protected: + ReportErrorAndContinueABISafeProc mSafeProc; + }; + + +} + +#endif // __IError_h__ diff --git a/xmp/XMPCommon/Interfaces/IErrorNotifier.h b/xmp/XMPCommon/Interfaces/IErrorNotifier.h new file mode 100644 index 0000000..7d17721 --- /dev/null +++ b/xmp/XMPCommon/Interfaces/IErrorNotifier.h @@ -0,0 +1,51 @@ +#ifndef IErrorNotifier_h__ +#define IErrorNotifier_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCommon/XMPCommonFwdDeclarations.h" + +namespace AdobeXMPCommon { + + //! + //! \brief Version1 of the interface that represents an interface to be implemented by client + //! in case he is interested in getting notifications with respect to errors/warnings encountered + //! by library. + //! \details In case client is interested in error notifications he can implement this interface + //! and register the same with the \#AdobeXMPCommon::IConfigurationManager. For every warning or error + //! encountered the NotifyError function will be called by the library. In case of warnings ( indicated + //! by the severity of the error ) the client has the option to continue ignoring the warning by returning + //! true else he can return false and the warning will be thrown aborting the current operation. + //! + class XMP_PUBLIC IErrorNotifier_v1 + { + public: + //! + //! @brief Called by the library to notify the client about the warning/error. + //! \param[in] error const pointer to a \#AdobeXMPCommon::IError. Client can use the information + //! in the error to decide what should be the future course of action. + //! \return A value of bool type that will indicate the future course of action. + //! + virtual bool APICALL Notify( const spcIError & error ) = 0; + + protected: + //! \cond XMP_INTERNAL_DOCUMENTATION + virtual uint32 APICALL notify( pcIError_base error, uint32 & exceptionThrown ) __NOTHROW__; + //! \endcond + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + //! \endcond + }; +} + +#endif // IErrorNotifier_h__ diff --git a/xmp/XMPCommon/Interfaces/IMemoryAllocator.h b/xmp/XMPCommon/Interfaces/IMemoryAllocator.h new file mode 100644 index 0000000..c2cdef3 --- /dev/null +++ b/xmp/XMPCommon/Interfaces/IMemoryAllocator.h @@ -0,0 +1,64 @@ +#ifndef IMemoryAllocator_h__ +#define IMemoryAllocator_h__ 1 + +// ================================================================================================= +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCommon/Interfaces/BaseInterfaces/IVersionable.h" + +#include "XMPCommon/XMPCommonFwdDeclarations.h" + +namespace AdobeXMPCommon { + + //! + //! \brief Version1 of the interface that represents an interface to be implemented by client in case + //! he is interested in controlling the memory allocation and deallocation on the heap. + //! \details In case client is interested in controlling the memory allocation and deallocation on + //! the heap he can implement this interface and register the same with the + //! \#AdobeXMPCommon::IConfigurationManager. For every request of memory allocation or deallocation on + //! the heap corresponding function will be called by the library. + //! \attention Support for Multi threading is under clients hand. + //! + class XMP_PUBLIC IMemoryAllocator_v1 + { + public: + //! + //! @brief Called by the library whenever it needs some space on the heap. + //! \param[in] size A value of type \#AdobeXMPCommon::sizet indicating the number of bytes + //! required by the library on the heap. + //! \return A pointer to memory location on the heap. + //! + virtual void * APICALL allocate( sizet size ) __NOTHROW__ = 0; + + //! + //! @brief Called by the library whenever there is no further need for a previously allocated space on the heap. + //! \param[in] ptr A pointer to a memory location which is no longer needed. + //! + virtual void APICALL deallocate( void * ptr ) __NOTHROW__ = 0; + + //! + //! @brief Called by the library whenever it needs to expand or contract some space already allocated on + //! the heap, preserving the contents. + //! \param[in] ptr A pointer to a memory location which was previously allocated on the heap. + //! \param[in] size A value of type \#AdobeXMPCommon::sizet indicating the new number of bytes + //! required by the library on the heap. + //! \return A pointer to memory location on the heap which is of new size and previous contents are + //! preserved. + //! + virtual void * APICALL reallocate( void * ptr, sizet size ) __NOTHROW__ = 0; + + protected: + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + }; +}; + +#endif // IMemoryAllocator_h__ diff --git a/xmp/XMPCommon/Interfaces/IObjectFactory.h b/xmp/XMPCommon/Interfaces/IObjectFactory.h new file mode 100644 index 0000000..0dd86b9 --- /dev/null +++ b/xmp/XMPCommon/Interfaces/IObjectFactory.h @@ -0,0 +1,90 @@ +#ifndef IObjectFactory_h__ +#define IObjectFactory_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCommon/XMPCommonFwdDeclarations.h" +#include "XMPCommon/Interfaces/BaseInterfaces/IVersionable.h" + +namespace AdobeXMPCommon { + + //! + //! \brief Version1 of a interface that represents a factory to create various artifacts defined within + //! AdobeXMPCommon namespace. + //! @details Provides all the functions to create instances of various artifacts defined with AdobeXMPCommon namespace. This + //! is the interface through which clients of the library actually get access to all other interfaces. + //! + + class XMP_PUBLIC IObjectFactory_v1 + : public IVersionable + { + public: + + //! + //! @brief Creates an IUTF8String object. + //! \param[in] buf pointer to a constant char buffer containing content. It can be null + //! terminated or not. NULL pointer will be treated as empty string. + //! \param[in] count A value of \#AdobeXMPCommon::sizet indicating the length in case buf is not null + //! terminated. In case buf is null terminated it can be set to npos. + //! \param[out] error A reference to a pointer to const IError object which will be filled with the error object in case of any error. + //! \attention The returned pointer is allocated on heap by the module so client is responsible for its release. + //! They should call Release once they no longer need this object. + //! + virtual pIUTF8String_base APICALL CreateUTF8String( const char * buf, sizet count, pcIError_base & error ) __NOTHROW__ = 0; + + //! + //! @brief Creates an IError object. + //! \param[in] domain An unsigned 32 bit integer value representing the error domain. + //! \param[in] code An unsigned 32 bit integer value representing the error code. + //! \param[in] severity An unsigned 32 bit integer value representing the severity of the error. + //! \param[out] error A reference to a pointer to const IError object which will be filled with the error object in case of any error. + //! \attention The returned pointer is allocated on heap by the module so client is responsible for its release. + //! They should call Release once they no longer need this object. + //! + virtual pIError_base APICALL CreateError( uint32 domain, uint32 code, uint32 severity, pcIError_base & error ) __NOTHROW__ = 0; + + //! + //! @{ + //! @details Convert raw pointer to shared pointer. The raw pointer is of version 1 interface + //! where as the returned returned pointer depends on the version client is interested in. + //! \return Raw pointer to const or non constant IObjectFactory interface. + //! + XMP_PRIVATE static pIObjectFactory MakeObjectFactory( pIObjectFactory_base ptr ) { + return IObjectFactory::GetInterfaceVersion() > 1 ? ptr->GetInterfacePointer< IObjectFactory >() : ptr; + } + XMP_PRIVATE static pcIObjectFactory MakeObjectFactory( pcIObjectFactory_base ptr ) { + return MakeObjectFactory( const_cast< pIObjectFactory_base >( ptr ) ); + } + //! + //! @} + + //! + //! @brief Returns the unique ID assigned to the interface. + //! \return 64 bit unsigned integer representing the unique ID assigned to the interface. + //! + XMP_PRIVATE static uint64 GetInterfaceID() { return kIObjectFactoryID; } + + //! + //! @brief Returns the version of the interface. + //! \return 32 bit unsigned integer representing the version of the interface. + //! + XMP_PRIVATE static uint32 GetInterfaceVersion() { return 1; } + + virtual ~IObjectFactory_v1() __NOTHROW__ {}; + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + }; + +} + +#endif // IObjectFactory_h__ diff --git a/xmp/XMPCommon/Interfaces/IUTF8String.h b/xmp/XMPCommon/Interfaces/IUTF8String.h new file mode 100644 index 0000000..e1cc517 --- /dev/null +++ b/xmp/XMPCommon/Interfaces/IUTF8String.h @@ -0,0 +1,504 @@ +#ifndef __IUTF8String_h__ +#define __IUTF8String_h__ 1 + +// ================================================================================================= +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCommon/Interfaces/BaseInterfaces/ISharedObject.h" +#include "XMPCommon/Interfaces/BaseInterfaces/IVersionable.h" + +namespace AdobeXMPCommon { + using AdobeXMPCommon::npos; + + //! + //! @brief Version1 of the interface that represents an UTF8String. + //! @details Provides all the functions to access properties of the string object, appends or assigns content + //! to the existing string objects and clones existing string objects. + //! + class XMP_PUBLIC IUTF8String_v1 + : public virtual ISharedObject + , public virtual IVersionable + { + public: + //! + //! @brief Appends more content into the existing string object through a pointer to char buffer. + //! \param[in] buf pointer to a constant char buffer containing new content. It can be null + //! terminated or not. NULL pointer will be treated as empty string. + //! \param[in] count A value of \#AdobeXMPCommon::sizet indicating the length in case buf is not null + //! terminated. In case buf is null terminated it can be set to npos. + //! \return The shared pointer to itself of type \#AdobeXMPCommon::IUTF8String. + //! + virtual spIUTF8String APICALL append( const char * buf, sizet count ) = 0; + + //! + //! @brief Appends the contents of another string into the existing string. + //! \param[in] src Shared pointer to const \#AdobeXMPCommon::IUTF8String whose contents will be + //! appended to existing content in the object. Invalid shared pointer will be treated as empty string. + //! \param[in] srcPos A value of \#AdobeXMPCommon::sizet indicating the position of the first character + //! in src that is inserted into the object as a substring. + //! \param[in] count A value of \#AdobeXMPCommon::sizet indicating the length of the substring to be copied + //! (if the string is shorter, as many characters as possible are copied). A value of npos indicates all + //! characters until the end of src. + //! \return The shared pointer to itself of type \#AdobeXMPCommon::IUTF8String. + //! \attention Error is thrown in case + //! - srcPos is greater than length of src. + //! + virtual spIUTF8String APICALL append( const spcIUTF8String & src, sizet srcPos = 0, sizet count = npos ) = 0; + + //! + //! @brief Overwrites new string content into the existing string object through a pointer to char buffer. + //! \param[in] buf pointer to a constant char buffer containing new content. It can be null + //! terminated or not. NULL pointer will be treated as empty string. + //! \param[in] count A value of \#AdobeXMPCommon::sizet indicating the length in case buf is not null + //! terminated. In case buf is null terminated it can be set to npos. + //! \return The shared pointer to itself of type \#AdobeXMPCommon::IUTF8String. + //! + virtual spIUTF8String APICALL assign( const char * buf, sizet count ) = 0; + + //! + //! @brief Overwrites the contents with contents of another string. + //! \param[in] src shared pointer to const \#AdobeXMPCommon::IUTF8String whose contents will + //! overwrite existing content in the object. Invalid shared pointer will be treated as empty string. + //! \param[in] srcPos A value of \#AdobeXMPCommon::sizet indicating the position of the first character + //! in src that is inserted into the object as a substring. + //! \param[in] count A value of \#AdobeXMPCommon::sizet indicating the length of the substring to be copied. + //! A value of npos indicates all characters until the end of src. If this is greater than the available + //! characters in the substring then copying is limited to the number of available characters. + //! \return The shared pointer to itself of type \#AdobeXMPCommon::IUTF8String. + //! \attention Error is thrown in case + //! - srcPos is greater than length of src. + //! + virtual spIUTF8String APICALL assign( const spcIUTF8String & src, sizet srcPos = 0, sizet count = npos ) = 0; + + //! + //! @brief Inserts additional characters into the string right before the character indicated by pos. + //! \param[in] pos Insertion point: The new contents are inserted before the character at position pos. + //! \param[in] buf pointer to a constant char buffer containing new content. It can be null + //! terminated or not. NULL pointer will be treated as empty string. + //! \param[in] count A value of \#AdobeXMPCommon::sizet indicating the length in case buf is not null + //! terminated. In case buf is null terminated it can be set to npos. + //! \return The shared pointer to itself of type \#AdobeXMPCommon::IUTF8String. + //! \\attention Error is thrown in case + //! - pos is greater than the object's length. + //! + virtual spIUTF8String APICALL insert( sizet pos, const char * buf, sizet count ) = 0; + + //! + //! @brief Inserts additional characters into the string right before the character indicated by pos. + //! \param[in] pos Insertion point: The new contents are inserted before the character at position pos. + //! \param[in] src shared pointer to const \#AdobeXMPCommon::IUTF8String whose contents will + //! be copied and pushed into the object. Invalid shared pointer will be treated as empty string. + //! \param[in] srcPos A value of \#AdobeXMPCommon::sizet indicating the position of the first character + //! in src that is inserted into the object as a substring. + //! \param[in] count A value of \#AdobeXMPCommon::sizet indicating the length of the substring to be copied + //! (if the string is shorter, as many characters as possible are copied). A value of npos indicates all + //! characters until the end of src. If this is greater than the available characters in the substring + //! then copying is limited to the number of available characters. + //! \return The shared pointer to itself of type \#AdobeXMPCommon::IUTF8String. + //! \attention Error is thrown in case + //! - srcPos is greater than length of src. + //! - pos is greater than the object's length. + //! + virtual spIUTF8String APICALL insert( sizet pos, const spcIUTF8String & src, sizet srcPos = 0, sizet count = npos ) = 0; + + //! + //! @brief Erases part of the string, reducing its length. + //! \param[in] pos Position of the first character to be erased. + //! If this is greater than the string length, nothing is erased. + //! \param[in] count Number of characters to erase (if the string is shorter, as many characters as + //! possible are erased). A value of npos indicates all characters until the end of the string. + //! \return The shared pointer to itself of type \#AdobeXMPCommon::IUTF8String. + //! \attention Error is thrown in case + //! - pos is greater than the object's length. + //! + virtual spIUTF8String APICALL erase( sizet pos = 0, sizet count = npos ) = 0; + + //! + //! @brief Resizes the string to the length of n characters. + //! /param[in] n New string length, expressed in number of characters. + //! \note If n is smaller than the current string length, the current value is shortened + //! to its first size character, removing the characters beyond the nth. If n is greater than the + //! current string length, the current content is extended by inserting at the end as many NULL + //! characters as needed to reach a size of n. + //! \attention Error is thrown in case + //! - n is greater than max_size + //! - allocation fails + //! + virtual void APICALL resize( sizet n ) = 0 ; + + //! + //! @brief Replace portion of string. + //! \param[in] pos Position of the first character to be replaced. + //! \param[in] count Number of characters to replace (if the string is shorter, as many characters as possible + //! are replaced). A value of npos indicates all characters until the end of the string. + //! \param[in] buf pointer to a constant char buffer containing new content. It can be null + //! terminated or not. NULL pointer will be treated as empty string. + //! \param[in] srcCount A value of \#AdobeXMPCommon::sizet indicating the length in case buf is not null + //! terminated. In case buf is null terminated it can be set to npos. + //! \return The shared pointer to itself of type \#AdobeXMPCommon::IUTF8String. + //! \attention Error is thrown in case + //! - pos is greater than the object's length. + //! + virtual spIUTF8String APICALL replace( sizet pos, sizet count, const char * buf, sizet srcCount ) = 0; + + //! + //! @brief Replace portion of string. + //! \param[in] pos Position of the first character to be replaced. + //! \param[in] count Number of characters to replace (if the string is shorter, as many characters as possible + //! are replaced). A value of npos indicates all characters until the end of the string. + //! \param[in] src Shared pointer to const \#AdobeXMPCommon::IUTF8String whose contents will + //! be copied and pushed into the object. Invalid shared pointer will be treated as empty string. + //! \param[in] srcPos Position of the first character in str that is copied to the object as replacement. + //! \param[in] srcCount Length of the substring to be copied (if the string is shorter, as many characters + //! as possible are copied). A value of npos indicates all characters until the end of str. + //! \return The shared pointer to itself of type \#AdobeXMPCommon::IUTF8String. + //! \attention Error is thrown in case + //! - srcPos is greater than length of src. + //! - pos is greater than the object's length. + //! + virtual spIUTF8String APICALL replace( sizet pos, sizet count, const spcIUTF8String & src, sizet srcPos = 0, sizet srcCount = npos ) = 0; + + //! + //! @brief Copy sequence of characters from string. + //! @details Copies a substring of the current value of the string object into the array. This substring + //! contains the len characters that start at position pos. + //! \param[in,out] buf Pointer to an array of characters. The array shall contain enough storage for the copied + //! characters. + //! \param[in] len Number of characters to copy (if the string is shorter, as many characters as possible are + //! copied). + //! \param[in] pos Position of the first character to be copied. + //! \return The number of characters copied to the array. This may be equal to count or to size() - pos. + //! \note The function does not append a null character at the end of the copied content. + //! \attention Error is thrown in case + //! - pos is greater than the object's length. + //! + virtual sizet APICALL copy( char * buf, sizet len, sizet pos = 0 ) const = 0; + + //! + //! @brief Find content in string. + //! @details Searches the string for the first occurrence of the sequence specified by its arguments. When pos + //! is specified, the search only includes characters at or after position pos, ignoring any possible + //! occurrences that include characters before pos. + //! \param[in] buf pointer to a constant char buffer containing content to be matched. It can be null + //! terminated or not. NULL pointer will be treated as empty string. + //! \param[in] pos Position of the first character in the string to be considered in the search. + //! If this is greater than the string length, the function never finds matches. + //! \return The position of the first character of the first match. If no matches were found, the function + //! returns npos. + //! + sizet find( const char * buf, sizet pos = 0 ) const { + return find( buf, pos, npos ); + } + + //! + //! @brief Find content in string. + //! @details Searches the string for the first occurrence of the sequence specified by its arguments. When pos + //! is specified, the search only includes characters at or after position pos, ignoring any possible + //! occurrences that include characters before pos. + //! \param[in] buf pointer to a constant char buffer containing content to be matched. It can be null + //! terminated or not. NULL pointer will be treated as empty string. + //! \param[in] pos Position of the first character in the string to be considered in the search. + //! If this is greater than the string length, the function never finds matches. + //! \param[in] count Length of sequence of characters to match. + //! \return The position of the first character of the first match. If no matches were found, the function + //! returns npos. + //! + virtual sizet APICALL find( const char * buf, sizet pos, sizet count ) const = 0; + + + // + //! @brief Find content in string. + //! @details Searches the string for the first occurrence of the sequence specified by its arguments. When pos + //! is specified, the search only includes characters at or after position pos, ignoring any possible + //! occurrences that include characters before pos. + //! \param[in] src shared pointer to const \#AdobeXMPCommon::IUTF8String containing content to be matched. Invalid + //! shared pointer will be treated as empty string. + //! \param[in] pos Position of the first character in the string to be considered in the search. + //! If this is greater than the string length, the function never finds matches. + //! \param[in] count Length of sequence of characters to match. + //! \return The position of the first character of the first match. If no matches were found, the function + //! returns npos. + //! + virtual sizet APICALL find( const spcIUTF8String & src, sizet pos = 0, sizet count = npos ) const = 0; + + //! + // @{ + //! @brief Find last occurrence of content in string. + //! @details Searches the string for the last occurrence of the sequence specified by its arguments. When pos + //! is specified, the search only includes sequences of characters that begin at or before position pos, + //! ignoring any possible match beginning after pos. + //! \param[in] buf pointer to a constant char buffer containing content to be matched. It can be null + //! terminated or not. NULL pointer will be treated as empty string. + //! \param[in] pos Position of the last character in the string to be considered as the beginning of a match. + //! Any value greater or equal than the string length (including npos) means that the entire string is + //! searched. + //! \return The position of the fist character of the last match. If no matches were found, the function + //! returns npos. + //! + sizet rfind( const char * buf, sizet pos = npos ) const { + return rfind( buf, pos, npos ); + } + virtual sizet APICALL rfind( const char * buf, sizet pos, sizet count ) const = 0; + //@} + //! + + //! + //! @brief Find last occurrence of content in string. + //! @details Searches the string for the last occurrence of the sequence specified by its arguments. When pos + //! is specified, the search only includes sequences of characters that begin at or before position pos, + //! ignoring any possible match beginning after pos. + //! \param[in] src shared pointer to const \#AdobeXMPCommon::IUTF8String containing content to be matched. Invalid + //! shared pointer will be treated as empty string. + //! \param[in] pos Position of the last character in the string to be considered as the beginning of a match. + //! Any value greater or equal than the string length (including npos) means that the entire string is + //! searched. + //! \param[in] count Length of sequence of characters to match. + //! \return The position of the fist character of the last match. If no matches were found, the function + //! returns npos. + //! + virtual sizet APICALL rfind( const spcIUTF8String & src, sizet pos = npos, sizet count = npos ) const = 0; + + //! + //! @{ + //! @brief Compare strings. + //! @details Compares the value of the string object (or a substring) to the sequence of characters specified by its + //! arguments. + //! \param[in] buf pointer to a constant char buffer containing content to be compared. It can be null + //! terminated or not. NULL pointer will be treated as empty string. + //! + //! \return Returns a signed integral indicating the relation between the strings + //! | value | relation between compared string and comparing string | + //! | :---: | :-----------------------------------------------------| + //! | 0 | They compare equal | + //! | <0 | Either the value of the first character that does not match is lower in the compared string, or all compared characters match but the compared string is shorter. | + //! | >0 | Either the value of the first character that does not match is greater in the compared string, or all compared characters match but the compared string is longer. | + //! \attention Error is thrown in case + //! - pos is greater than the object's length. + //! + int32 compare( const char * buf ) const { + return compare( 0, size(), buf, npos ); + } + //! @brief Compare strings. + //! @details Compares the value of the string object (or a substring) to the sequence of characters specified by its + //! arguments. + //! \param[in] buf pointer to a constant char buffer containing content to be compared. It can be null + //! terminated or not. NULL pointer will be treated as empty string. + //! \param[in] pos Position of the first character in the compared string. If this is greater than the string + //! length, it is treated as empty string. + //! \param[in] len Length of compared string (if the string is shorter, as many characters as possible). + //! A value of npos indicates all characters until the end of the string. + //! + //! \return Returns a signed integral indicating the relation between the strings + //! | value | relation between compared string and comparing string | + //! | :---: | :-----------------------------------------------------| + //! | 0 | They compare equal | + //! | <0 | Either the value of the first character that does not match is lower in the compared string, or all compared characters match but the compared string is shorter. | + //! | >0 | Either the value of the first character that does not match is greater in the compared string, or all compared characters match but the compared string is longer. | + //! \attention Error is thrown in case + //! - pos is greater than the object's length. + //! + int32 compare( sizet pos, sizet len, const char * buf ) const { + return compare( pos, len, buf, npos ); + } + //!@brief Compare strings. + //! @details Compares the value of the string object (or a substring) to the sequence of characters specified by its + //! arguments. + //! \param[in] buf pointer to a constant char buffer containing content to be compared. It can be null + //! terminated or not. NULL pointer will be treated as empty string. + //! \param[in] pos Position of the first character in the compared string. If this is greater than the string + //! length, it is treated as empty string. + //! \param[in] len Length of compared string (if the string is shorter, as many characters as possible). + //! A value of npos indicates all characters until the end of the string. + //! \param[in] count Number of characters to compare. + //! + //! \return Returns a signed integral indicating the relation between the strings + //! | value | relation between compared string and comparing string | + //! | :---: | :-----------------------------------------------------| + //! | 0 | They compare equal | + //! | <0 | Either the value of the first character that does not match is lower in the compared string, or all compared characters match but the compared string is shorter. | + //! | >0 | Either the value of the first character that does not match is greater in the compared string, or all compared characters match but the compared string is longer. | + //! \attention Error is thrown in case + //! - pos is greater than the object's length. + //! + virtual int32 APICALL compare( sizet pos, sizet len, const char * buf, sizet count ) const = 0; + + + //! + //! @brief Compare strings. + //! @details Compares the value of the string object (or a substring) to the contents of an string or substring object + //! specified by its arguments. + //! \param[in] str shared pointer to const \#AdobeXMPCommon::IUTF8String containing content to be compared. + //! Invalid shared pointer will be treated as empty string. + //! \return Returns a signed integral indicating the relation between the strings + //! | value | relation between compared string and comparing string | + //! | :---: | :-----------------------------------------------------| + //! | 0 | They compare equal | + //! | <0 | Either the value of the first character that does not match is lower in the compared string, or all compared characters match but the compared string is shorter. | + //! | >0 | Either the value of the first character that does not match is greater in the compared string, or all compared characters match but the compared string is longer. | + //! \attention Error is thrown in case + //! - strPos is greater than length of str. + //! - pos is greater than the object's length. + //! + int32 compare( const spcIUTF8String & str ) const { + return compare( 0, size(), str, 0, str->size() ); + } + //! + //! @brief Compare strings. + //! @details Compares the value of the string object (or a substring) to the contents of an string or substring object + //! specified by its arguments. + //! \param[in] pos Position of the first character in the compared string. + //! \param[in] len Length of compared string (if the string is shorter, as many characters as possible). + //! A value of npos indicates all characters until the end of the string. + //! \param[in] str shared pointer to const \#AdobeXMPCommon::IUTF8String containing content to be compared. + //! Invalid shared pointer will be treated as empty string. + //! \param[in] strPos Position of the first character in the comparing string. + //! \param[in] strLen Length of comparing string (if the string is shorter, as many characters as possible). + //! + //! \return Returns a signed integral indicating the relation between the strings + //! | value | relation between compared string and comparing string | + //! | :---: | :-----------------------------------------------------| + //! | 0 | They compare equal | + //! | <0 | Either the value of the first character that does not match is lower in the compared string, or all compared characters match but the compared string is shorter. | + //! | >0 | Either the value of the first character that does not match is greater in the compared string, or all compared characters match but the compared string is longer. | + //! \attention Error is thrown in case + //! - strPos is greater than length of str. + //! - pos is greater than the object's length. + //! + virtual int32 APICALL compare( sizet pos, sizet len, const spcIUTF8String & str, sizet strPos = 0, sizet strLen = npos ) const = 0; + + //! + //! @brief Returns a new string object which contains a sub string of the actual string object. + //! \param[in] pos Position of the first character to be copied. If this is greater than the string length, then + //! nothing is copied. + //! \param[in] count Number of characters to copy (if the string is shorter, as many characters as possible are + //! copied). + //! \return A shared pointer to AdobeXMPCommon::IUTF8String which is exact replica of the current object. + //! \attention Error is thrown in case + //! - pos is greater than the object's length. + //! - allocation fails + //! + virtual spIUTF8String APICALL substr( sizet pos = 0, sizet count = npos ) const = 0; + + //! + //! @brief Indicates whether the string object is empty or not. + //! \return A value of type bool; true in case the contents of the string object is empty. + //! + virtual bool APICALL empty() const = 0; + + //! + //! @brief Provides access to the actual location where contents of string are stored. + //! \return A pointer to a buffer of const chars containing the contents of the string object. + //! + virtual const char * APICALL c_str() const __NOTHROW__ = 0; + + //! + //! @brief Clears the contents of the string object. + //! + virtual void APICALL clear() __NOTHROW__ = 0; + + //! + //! @brief Indicates the number of bytes used by the contents of the string object. + //! \return An object of type \#AdobeXMPCommon::sizet containing the number of bytes used to store the contents fo the string object. + //! + virtual sizet APICALL size() const __NOTHROW__ = 0; + + //! + //! \cond XMP_INTERNAL_DOCUMENTATION + //! @{ + //! @brief Returns the actual raw pointer from the shared pointer, which can be a shared pointer of a proxy class. + //! \return Either a const or non const pointer to IUTF8String interface. + //! + virtual pIUTF8String APICALL GetActualIUTF8String() __NOTHROW__ = 0; + XMP_PRIVATE pcIUTF8String GetActualIUTF8String() const __NOTHROW__ { + return const_cast< IUTF8String_v1 * >( this )->GetActualIUTF8String(); + } + //! + //! @} + + //! + //! @{ + //! @brief Convert raw pointer to shared pointer. The raw pointer is of version 1 interface + //! where as the returned shared pointer depends on the version client is interested in. + //! \return Shared pointer to const or non constant interface. + //! + XMP_PRIVATE static spIUTF8String MakeShared( pIUTF8String_base ptr ); + XMP_PRIVATE static spcIUTF8String MakeShared( pcIUTF8String_base ptr ) { + return MakeShared( const_cast< pIUTF8String_base >( ptr ) ); + } + //! + //! @} + + //! + //! @brief Returns the unique ID assigned to the interface. + //! \return 64 bit unsigned integer representing the unique ID assigned to the interface. + //! + XMP_PRIVATE static uint64 GetInterfaceID() { return kIUTF8StringID; } + + //! + //! @brief Returns the version of the interface. + //! \return 32 bit unsigned integer representing the version of the interface. + //! + XMP_PRIVATE static uint32 GetInterfaceVersion() { return 1; } + //! \endcond + + // static factory functions + + //! + //! @brief Creates an empty IUTF8String object. + //! \param[in] objFactory A pointer to \#AdobeXMPCommon::IObjectFactory object. + //! \return A shared pointer to an empty IUTF8String object + //! + XMP_PRIVATE static spIUTF8String CreateUTF8String( pIObjectFactory objFactory ); + + //! + //! @brief Creates an IUTF8String object whose initial contents are copied from a char buffer. + //! \param[in] objFactory A pointer to \#AdobeXMPCommon::IObjectFactory object. + //! \param[in] buf pointer to a constant char buffer containing content. It can be null + //! terminated or not. NULL pointer will be treated as empty string. + //! \param[in] count A value of \#AdobeXMPCommon::sizet indicating the length in case buf is not null + //! terminated. In case buf is null terminated it can be set to npos. + //! \return A shared pointer to a newly created \#AdobeXMPCommon::IUTF8String object + //! + XMP_PRIVATE static spIUTF8String CreateUTF8String( pIObjectFactory objFactory, const char * buf, sizet count ); + + protected: + //! + //! Destructor + //! + virtual ~IUTF8String_v1() __NOTHROW__ {} + + //! \cond XMP_INTERNAL_DOCUMENTATION + virtual pIUTF8String_base APICALL assign( const char * buffer, sizet count, pcIError_base & error ) __NOTHROW__ = 0; + virtual pIUTF8String_base APICALL assign( pcIUTF8String_base str, sizet srcPos, sizet count, pcIError_base & error ) __NOTHROW__ = 0; + virtual pIUTF8String_base APICALL append( const char * buffer, sizet count, pcIError_base & error ) __NOTHROW__ = 0; + virtual pIUTF8String_base APICALL append( pcIUTF8String_base str, sizet srcPos, sizet count, pcIError_base & error ) __NOTHROW__ = 0; + virtual pIUTF8String_base APICALL insert( sizet pos, const char * buf, sizet count, pcIError_base & error ) __NOTHROW__ = 0; + virtual pIUTF8String_base APICALL insert( sizet pos, pcIUTF8String_base src, sizet srcPos, sizet count, pcIError_base & error ) __NOTHROW__ = 0; + virtual pIUTF8String_base APICALL erase( sizet pos, sizet count, pcIError_base & error ) __NOTHROW__ = 0; + virtual void APICALL resize( sizet n, pcIError_base & error ) __NOTHROW__ = 0; + virtual pIUTF8String_base APICALL replace( sizet pos, sizet count, const char * buf, sizet srcCount, pcIError_base & error ) __NOTHROW__ = 0; + virtual pIUTF8String_base APICALL replace( sizet pos, sizet count, pcIUTF8String_base src, sizet srcPos, sizet srcCount, pcIError_base & error ) __NOTHROW__ = 0; + virtual sizet APICALL copy( char * buf, sizet len, sizet pos, pcIError_base & error ) const __NOTHROW__ = 0; + virtual sizet APICALL find( const char * buf, sizet pos, sizet count, pcIError_base & error ) const __NOTHROW__ = 0; + virtual sizet APICALL find( pcIUTF8String_base src, sizet pos, sizet count, pcIError_base & error ) const __NOTHROW__ = 0; + virtual sizet APICALL rfind( const char * buf, sizet pos, sizet count, pcIError_base & error ) const __NOTHROW__ = 0; + virtual sizet APICALL rfind( pcIUTF8String_base src, sizet pos, sizet count, pcIError_base & error ) const __NOTHROW__ = 0; + virtual int32 APICALL compare( sizet pos, sizet len, const char * buf, sizet count, pcIError_base & error ) const __NOTHROW__ = 0; + virtual int32 APICALL compare( sizet pos, sizet len, pcIUTF8String_base str, sizet strPos, sizet strLen, pcIError_base & error ) const __NOTHROW__ = 0; + virtual pIUTF8String_base APICALL substr( sizet pos, sizet count, pcIError_base & error ) const __NOTHROW__ = 0; + virtual uint32 APICALL empty( pcIError_base & error ) const __NOTHROW__ = 0; + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + //! \endcond + }; +} + +#endif // __IUTF8String_h__ diff --git a/xmp/XMPCommon/Utilities/TWrapperFunctions.h b/xmp/XMPCommon/Utilities/TWrapperFunctions.h new file mode 100644 index 0000000..41af71e --- /dev/null +++ b/xmp/XMPCommon/Utilities/TWrapperFunctions.h @@ -0,0 +1,252 @@ +#ifndef TWrapperFunctions_h__ +#define TWrapperFunctions_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCommon/Interfaces/IError.h" + +#if SUPPORT_VARIADIC_TEMPLATES +//! \cond XMP_INTERNAL_DOCUMENTATION +namespace AdobeXMPCommon { + + //! + //! @{ + //! template functions taking care of all the functions where return types of client exposed function as well as + //! DLL/library safe function are void. + //! \details It calls the underlying DLL/library safe function and in case any error is reported back through + //! pointer, it throws the error to the upper functions to handle. + //! \param[in] ptr non const pointer to an object of className itself ( most probably this pointer ). + //! \param[in] Func function pointer to non const member function of className accepting 0 or n number of variables + //! anad a reference to a pointer to const #AdobeXMPCommon::IError_base object and returning void. + //! \param[in] Vs n number of variables of varying/same type. + //! \note These takes care of non const functions. + //! + template < typename className > + void CallSafeFunctionReturningVoid( className * ptr, + void ( APICALL className::*Func )( pcIError_base & ) ) + { + pcIError_base error( NULL ); + ( ptr->*Func )( error ); + if ( error ) throw IError::MakeShared( error ); + } + + template< typename className, typename ... Ts > + void CallSafeFunctionReturningVoid( className * ptr, + void ( APICALL className::*Func )( Ts ..., pcIError_base & ), Ts ... Vs ) + { + pcIError_base error( NULL ); + ( ptr->*Func )( Vs ..., error ); + if ( error ) throw IError::MakeShared( error ); + } + //! + //! @} + + //! + //! @{ + //! template functions taking care of all the functions where return types of client exposed function as well as + //! DLL/library safe function are void. + //! \details It calls the underlying DLL/library safe function and in case any error is reported back through + //! pointer, it throws the error to the upper functions to handle. + //! \param[in] ptr const pointer to a const object of className itself ( most probably this pointer ). + //! \param[in] Func function pointer to const member function of className accepting 0 or n number of variables + //! and a reference to a pointer to const #AdobeXMPCommon::IError_base object and returning void. + //! \param[in] Vs n number of variables of varying/same type. + //! \note These takes care of const functions. + //! + template < typename className > + void CallConstSafeFunctionReturningVoid( const className * const ptr, + void ( APICALL className::*Func )( pcIError_base & ) const ) + { + pcIError_base error( NULL ); + ( ptr->*Func )( error ); + if ( error ) throw IError::MakeShared( error ); + } + + template < typename className, typename ... Ts > + void CallConstSafeFunctionReturningVoid( const className * const ptr, + void ( APICALL className::*Func )( Ts ..., pcIError_base & ) const, Ts ... Vs ) + { + pcIError_base error( NULL ); + ( ptr->*Func )( Vs ..., error ); + if ( error ) throw IError::MakeShared( error ); + } + //! + //! @} + + //! + //! @{ + //! template functions taking care of all the functions where return types of client exposed function as well as + //! DLL/library safe function are nor void type neither shared pointers. + //! \details It calls the underlying DLL/library safe function and in case any error is reported back through + //! pointer, it throws the error to the upper function to handle. + //! \param[in] ptr non const pointer to an object of className itself ( most probably this pointer ). + //! \param[in] Func function pointer to non const member function of className accepting 0 or n number of variables + //! and a reference to a pointer to const #AdobeXMPCommon::IError_base object and returning value of type internalReturnType. + //! \param[in] Vs n number of variables of varying/same type. + //! These take care of non const functions. + //! + template < typename className, typename returnType, typename internalReturnType > + returnType CallSafeFunction( className * ptr, + internalReturnType ( APICALL className::*Func )( pcIError_base & ) ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = (ptr->*Func)( error ); + if ( error ) throw IError::MakeShared( error ); + #if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4800 ) + #endif + return static_cast< returnType >( returnValue ); + #if XMP_WinBuild + #pragma warning( pop ) + #endif + } + + template < typename className, typename returnType, typename internalReturnType, typename ... Ts > + returnType CallSafeFunction( className * ptr, + internalReturnType ( APICALL className::*Func )( Ts ..., pcIError_base & ), Ts ... Vs ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = (ptr->*Func)( Vs ..., error ); + if ( error ) throw IError::MakeShared( error ); + #if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4800 ) + #endif + return static_cast< returnType >( returnValue ); + #if XMP_WinBuild + #pragma warning( pop ) + #endif + } + //! + //! @} + + //! + //! @{ + //! template functions taking care of all the functions where return types of client exposed function as well as + //! DLL/library safe function are nor void type neither shared pointers. + //! \details It calls the underlying DLL/library safe function and in case any error is reported back through + //! pointer, it throws the error to the upper function to handle. + //! \param[in] ptr const pointer to a const object of className itself ( most probably this pointer ). + //! \param[in] Func function pointer to const member function of className accepting 0 or n number of variables + //! and a reference to a pointer to const #AdobeXMPCommon::IError_base object and returning value of type internalReturnType. + //! \param[in] Vs n number of variables of varying/same type. + //! These take care of const functions. + //! + template < typename className, typename returnType, typename internalReturnType > + returnType CallConstSafeFunction( const className * const ptr, + internalReturnType ( APICALL className::*Func )( pcIError_base & ) const ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = (ptr->*Func)( error ); + if ( error ) throw IError::MakeShared( error ); + #if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4800 ) + #endif + return static_cast< returnType >( returnValue ); + #if XMP_WinBuild + #pragma warning( pop ) + #endif + } + + template < typename className, typename returnType, typename internalReturnType, typename ... Ts > + returnType CallConstSafeFunction( const className * const ptr, + internalReturnType ( APICALL className::*Func )( Ts ..., pcIError_base & ) const, Ts ... Vs ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = (ptr->*Func)( Vs ..., error ); + if ( error ) throw IError::MakeShared( error ); + #if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4800 ) + #endif + return static_cast< returnType >( returnValue ); + #if XMP_WinBuild + #pragma warning( pop ) + #endif + } + //! + //! @} + + //! + //! @{ + //! template functions taking care of all the functions where return types of client exposed function is a + //! shared pointer. + //! \details It calls the underlying DLL/library safe function and in case any error is reported back through + //! pointer, it throws the error to the upper function to handle. + //! \param[in] ptr non const pointer to an object of className itself ( most probably this pointer ). + //! \param[in] Func function pointer to non const member function of className accepting 0 or n number of variables + //! and a reference to a pointer to const #AdobeXMPCommon::IError_base object and returning internalReturnType. + //! \param[in] Vs n number of variables of varying/same type. + //! These take care of non const functions. + //! + template < typename className, typename internalReturnType, typename sharedPointerType > + shared_ptr< sharedPointerType > CallSafeFunctionReturningPointer( className * ptr, + internalReturnType ( APICALL className::*Func )( pcIError_base & ) ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = (ptr->*Func)( error ); + if ( error ) throw IError::MakeShared( error ); + return sharedPointerType::MakeShared( returnValue ); + } + + template < typename className, typename internalReturnType, typename sharedPointerType, typename ... Ts > + shared_ptr< sharedPointerType > CallSafeFunctionReturningPointer( className * ptr, + internalReturnType ( APICALL className::*Func )( Ts ..., pcIError_base & ), Ts ... Vs ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = ( ptr->*Func )( Vs ..., error ); + if ( error ) throw IError::MakeShared( error ); + return sharedPointerType::MakeShared( returnValue ); + } + //! + //! @} + + //! + //! @{ + //! template functions taking care of all the functions where return types of client exposed function is a + //! shared pointer. + //! \details It calls the underlying DLL/library safe function and in case any error is reported back through + //! pointer, it throws the error to the upper function to handle. + //! \param[in] ptr const pointer to a const object of className itself ( most probably this pointer ). + //! \param[in] Func function pointer to const member function of className accepting 0 or n number of variables + //! and a reference to a pointer to const #AdobeXMPCommon::IError_base object and returning internalReturnType. + //! \param[in] Vs n number of variables of varying/same type. + //! These take care of const functions. + //! + template < typename className, typename internalReturnType, typename sharedPointerType > + shared_ptr< sharedPointerType > CallConstSafeFunctionReturningPointer( const className * const ptr, + internalReturnType ( APICALL className::*Func )( pcIError_base & ) const ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = (ptr->*Func)( error ); + if ( error ) throw IError::MakeShared( error ); + return sharedPointerType::MakeShared( returnValue ); + } + + template < typename className, typename internalReturnType, typename sharedPointerType, typename ... Ts > + shared_ptr< sharedPointerType > CallConstSafeFunctionReturningPointer( const className * const ptr, + internalReturnType ( APICALL className::*Func )( Ts ..., pcIError_base & ) const, Ts ... Vs ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = ( ptr->*Func )( Vs ..., error ); + if ( error ) throw IError::MakeShared( error ); + return sharedPointerType::MakeShared( returnValue ); + } + //! + //! @} +} + +//! \endcond +#else + #include "XMPCommon/Utilities/TWrapperFunctions2.h" +#endif +#endif // TWrapperFunctions_h__ diff --git a/xmp/XMPCommon/Utilities/TWrapperFunctions2.h b/xmp/XMPCommon/Utilities/TWrapperFunctions2.h new file mode 100644 index 0000000..a6dc4a6 --- /dev/null +++ b/xmp/XMPCommon/Utilities/TWrapperFunctions2.h @@ -0,0 +1,554 @@ +#ifndef TWrapperFunctions2_h__ +#define TWrapperFunctions2_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +//! \cond XMP_INTERNAL_DOCUMENTATION +namespace AdobeXMPCommon { + template < typename className > + void CallSafeFunctionReturningVoid( className * ptr, + void ( APICALL className::*Func )( pcIError_base & ) ) + { + pcIError_base error( NULL ); + ( ptr->*Func )( error ); + if ( error ) throw IError::MakeShared( error ); + } + + template< typename className, typename t1 > + void CallSafeFunctionReturningVoid( className * ptr, + void ( APICALL className::*Func )( t1, pcIError_base & ), t1 v1 ) + { + pcIError_base error( NULL ); + ( ptr->*Func )( v1, error ); + if ( error ) throw IError::MakeShared( error ); + } + + template< typename className, typename t1, typename t2 > + void CallSafeFunctionReturningVoid( className * ptr, + void ( APICALL className::*Func )( t1, t2, pcIError_base & ), t1 v1, t2 v2 ) + { + pcIError_base error( NULL ); + ( ptr->*Func )( v1, v2, error ); + if ( error ) throw IError::MakeShared( error ); + } + + template< typename className, typename t1, typename t2, typename t3 > + void CallSafeFunctionReturningVoid( className * ptr, + void ( APICALL className::*Func )( t1, t2, t3, pcIError_base & ), t1 v1, t2 v2, t3 v3 ) + { + pcIError_base error( NULL ); + ( ptr->*Func )( v1, v2, v3, error ); + if ( error ) throw IError::MakeShared( error ); + } + + template< typename className, typename t1, typename t2, typename t3, typename t4 > + void CallSafeFunctionReturningVoid( className * ptr, + void ( APICALL className::*Func )( t1, t2, t3, t4, pcIError_base & ), t1 v1, t2 v2, t3 v3, t4 v4 ) + { + pcIError_base error( NULL ); + ( ptr->*Func )( v1, v2, v3, v4, error ); + if ( error ) throw IError::MakeShared( error ); + } + + template< typename className, typename t1, typename t2, typename t3, typename t4, typename t5 > + void CallSafeFunctionReturningVoid( className * ptr, + void ( APICALL className::*Func )( t1, t2, t3, t4, t5, pcIError_base & ), t1 v1, t2 v2, t3 v3, t4 v4, t5 v5 ) + { + pcIError_base error( NULL ); + ( ptr->*Func )( v1, v2, v3, v4, v5, error ); + if ( error ) throw IError::MakeShared( error ); + } + + template< typename className, typename t1, typename t2, typename t3, typename t4, typename t5, typename t6 > + void CallSafeFunctionReturningVoid( className * ptr, + void ( APICALL className::*Func )( t1, t2, t3, t4, t5, t6, pcIError_base & ), t1 v1, t2 v2, t3 v3, t4 v4, t5 v5, t6 v6 ) + { + pcIError_base error( NULL ); + ( ptr->*Func )( v1, v2, v3, v4, v5, error ); + if ( error ) throw IError::MakeShared( error ); + } + + template < typename className > + void CallConstSafeFunctionReturningVoid( const className * const ptr, + void ( APICALL className::*Func )( pcIError_base & ) const ) + { + pcIError_base error( NULL ); + ( ptr->*Func )( error ); + if ( error ) throw IError::MakeShared( error ); + } + + template < typename className, typename t1 > + void CallConstSafeFunctionReturningVoid( const className * const ptr, + void ( APICALL className::*Func )( t1, pcIError_base & ) const, t1 v1 ) + { + pcIError_base error( NULL ); + ( ptr->*Func )( v1, error ); + if ( error ) throw IError::MakeShared( error ); + } + + template < typename className, typename t1, typename t2 > + void CallConstSafeFunctionReturningVoid( const className * const ptr, + void ( APICALL className::*Func )( t1, t2, pcIError_base & ) const, t1 v1, t2 v2 ) + { + pcIError_base error( NULL ); + ( ptr->*Func )( v1, v2, error ); + if ( error ) throw IError::MakeShared( error ); + } + + template < typename className, typename t1, typename t2, typename t3 > + void CallConstSafeFunctionReturningVoid( const className * const ptr, + void ( APICALL className::*Func )( t1, t2, t3, pcIError_base & ) const, t1 v1, t2 v2, t3 v3 ) + { + pcIError_base error( NULL ); + ( ptr->*Func )( v1, v2, v3, error ); + if ( error ) throw IError::MakeShared( error ); + } + + template < typename className, typename t1, typename t2, typename t3, typename t4 > + void CallConstSafeFunctionReturningVoid( const className * const ptr, + void ( APICALL className::*Func )( t1, t2, t3, t4, pcIError_base & ) const, t1 v1, t2 v2, t3 v3, t4 v4 ) + { + pcIError_base error( NULL ); + ( ptr->*Func )( v1, v2, v3, v4, error ); + if ( error ) throw IError::MakeShared( error ); + } + + template < typename className, typename t1, typename t2, typename t3, typename t4, typename t5 > + void CallConstSafeFunctionReturningVoid( const className * const ptr, + void ( APICALL className::*Func )( t1, t2, t3, t4, t5, pcIError_base & ) const, t1 v1, t2 v2, t3 v3, t4 v4, t5 v5 ) + { + pcIError_base error( NULL ); + ( ptr->*Func )( v1, v2, v3, v4, v5, error ); + if ( error ) throw IError::MakeShared( error ); + } + + + template < typename className, typename returnType, typename internalReturnType > + returnType CallSafeFunction( className * ptr, + internalReturnType ( APICALL className::*Func )( pcIError_base & ) ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = (ptr->*Func)( error ); + if ( error ) throw IError::MakeShared( error ); + #if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4800 ) + #endif + return static_cast< returnType >( returnValue ); + #if XMP_WinBuild + #pragma warning( pop ) + #endif + } + + template < typename className, typename returnType, typename internalReturnType, typename t1 > + returnType CallSafeFunction( className * ptr, + internalReturnType ( APICALL className::*Func )( t1, pcIError_base & ), t1 v1 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = (ptr->*Func)( v1, error ); + if ( error ) throw IError::MakeShared( error ); + #if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4800 ) + #endif + return static_cast< returnType >( returnValue ); + #if XMP_WinBuild + #pragma warning( pop ) + #endif + } + + template < typename className, typename returnType, typename internalReturnType, typename t1, typename t2 > + returnType CallSafeFunction( className * ptr, + internalReturnType ( APICALL className::*Func )( t1, t2, pcIError_base & ), t1 v1, t2 v2 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = (ptr->*Func)( v1, v2, error ); + if ( error ) throw IError::MakeShared( error ); + #if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4800 ) + #endif + return static_cast< returnType >( returnValue ); + #if XMP_WinBuild + #pragma warning( pop ) + #endif + } + + template < typename className, typename returnType, typename internalReturnType, typename t1, typename t2, typename t3 > + returnType CallSafeFunction( className * ptr, + internalReturnType( APICALL className::*Func )( t1, t2, t3, pcIError_base & ), t1 v1, t2 v2, t3 v3 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = ( ptr->*Func )( v1, v2, v3, error ); + if ( error ) throw IError::MakeShared( error ); + #if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4800 ) + #endif + return static_cast< returnType >( returnValue ); + #if XMP_WinBuild + #pragma warning( pop ) + #endif + } + + template < typename className, typename returnType, typename internalReturnType, typename t1, typename t2, typename t3, typename t4 > + returnType CallSafeFunction( className * ptr, + internalReturnType( APICALL className::*Func )( t1, t2, t3, t4, pcIError_base & ), t1 v1, t2 v2, t3 v3, t4 v4 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = ( ptr->*Func )( v1, v2, v3, v4, error ); + if ( error ) throw IError::MakeShared( error ); + #if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4800 ) + #endif + return static_cast< returnType >( returnValue ); + #if XMP_WinBuild + #pragma warning( pop ) + #endif + } + + template < typename className, typename returnType, typename internalReturnType, typename t1, typename t2, typename t3, typename t4, typename t5 > + returnType CallSafeFunction( className * ptr, + internalReturnType( APICALL className::*Func )( t1, t2, t3, t4, t5, pcIError_base & ), t1 v1, t2 v2, t3 v3, t4 v4, t5 v5 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = ( ptr->*Func )( v1, v2, v3, v4, v5, error ); + if ( error ) throw IError::MakeShared( error ); + #if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4800 ) + #endif + return static_cast< returnType >( returnValue ); + #if XMP_WinBuild + #pragma warning( pop ) + #endif + } + + // Fixing AML build on mac + + template < typename className, typename returnType, typename internalReturnType, typename t1, typename t2, typename t3, typename t4, typename t5, typename t6 > + returnType CallSafeFunction( className * ptr, + internalReturnType( APICALL className::*Func )( t1, t2, t3, t4, t5, t6, pcIError_base & ), t1 v1, t2 v2, t3 v3, t4 v4, t5 v5, t6 v6 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = ( ptr->*Func )( v1, v2, v3, v4, v5, v6, error ); + if ( error ) throw IError::MakeShared( error ); + #if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4800 ) + #endif + return static_cast< returnType >( returnValue ); + #if XMP_WinBuild + #pragma warning( pop ) + #endif + } + + template < typename className, typename returnType, typename internalReturnType > + returnType CallConstSafeFunction( const className * const ptr, + internalReturnType ( APICALL className::*Func )( pcIError_base & ) const ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = (ptr->*Func)( error ); + if ( error ) throw IError::MakeShared( error ); + #if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4800 ) + #endif + return static_cast< returnType >( returnValue ); + #if XMP_WinBuild + #pragma warning( pop ) + #endif + } + + template < typename className, typename returnType, typename internalReturnType, typename t1 > + returnType CallConstSafeFunction( const className * const ptr, + internalReturnType ( APICALL className::*Func )( t1, pcIError_base & ) const, t1 v1 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = (ptr->*Func)( v1, error ); + if ( error ) throw IError::MakeShared( error ); + #if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4800 ) + #endif + return static_cast< returnType >( returnValue ); + #if XMP_WinBuild + #pragma warning( pop ) + #endif + } + + template < typename className, typename returnType, typename internalReturnType, typename t1, typename t2 > + returnType CallConstSafeFunction( const className * const ptr, + internalReturnType ( APICALL className::*Func )( t1, t2, pcIError_base & ) const, t1 v1, t2 v2 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = (ptr->*Func)( v1, v2, error ); + if ( error ) throw IError::MakeShared( error ); + #if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4800 ) + #endif + return static_cast< returnType >( returnValue ); + #if XMP_WinBuild + #pragma warning( pop ) + #endif + } + + template < typename className, typename returnType, typename internalReturnType, typename t1, typename t2, typename t3 > + returnType CallConstSafeFunction( const className * const ptr, + internalReturnType ( APICALL className::*Func )( t1, t2, t3, pcIError_base & ) const, t1 v1, t2 v2, t3 v3 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = (ptr->*Func)( v1, v2, v3, error ); + if ( error ) throw IError::MakeShared( error ); + #if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4800 ) + #endif + return static_cast< returnType >( returnValue ); + #if XMP_WinBuild + #pragma warning( pop ) + #endif + } + + template < typename className, typename returnType, typename internalReturnType, typename t1, typename t2, typename t3, typename t4 > + returnType CallConstSafeFunction( const className * const ptr, + internalReturnType ( APICALL className::*Func )( t1, t2, t3, t4, pcIError_base & ) const, t1 v1, t2 v2, t3 v3, t4 v4 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = (ptr->*Func)( v1, v2, v3, v4, error ); + if ( error ) throw IError::MakeShared( error ); + #if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4800 ) + #endif + return static_cast< returnType >( returnValue ); + #if XMP_WinBuild + #pragma warning( pop ) + #endif + } + + template < typename className, typename returnType, typename internalReturnType, typename t1, typename t2, typename t3, typename t4, typename t5 > + returnType CallConstSafeFunction( const className * const ptr, + internalReturnType ( APICALL className::*Func )( t1, t2, t3, t4, t5, pcIError_base & ) const, t1 v1, t2 v2, t3 v3, t4 v4, t5 v5 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = (ptr->*Func)( v1, v2, v3, v4, v5, error ); + if ( error ) throw IError::MakeShared( error ); + #if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4800 ) + #endif + return static_cast< returnType >( returnValue ); + #if XMP_WinBuild + #pragma warning( pop ) + #endif + } + + //Fixing AML build on mac + + template < typename className, typename returnType, typename internalReturnType, typename t1, typename t2, typename t3, typename t4, typename t5, typename t6 > + returnType CallConstSafeFunction( const className * const ptr, + internalReturnType ( APICALL className::*Func )( t1, t2, t3, t4, t5, t6, pcIError_base & ) const, t1 v1, t2 v2, t3 v3, t4 v4, t5 v5, t6 v6 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = (ptr->*Func)( v1, v2, v3, v4, v5, v6, error ); + if ( error ) throw IError::MakeShared( error ); + #if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4800 ) + #endif + return static_cast< returnType >( returnValue ); + #if XMP_WinBuild + #pragma warning( pop ) + #endif + } + + template < typename className, typename returnType, typename internalReturnType, typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7 > + returnType CallConstSafeFunction( const className * const ptr, + internalReturnType ( APICALL className::*Func )( t1, t2, t3, t4, t5, t6, t7, pcIError_base & ) const, t1 v1, t2 v2, t3 v3, t4 v4, t5 v5, t6 v6, t7 v7 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = (ptr->*Func)( v1, v2, v3, v4, v5, v6, v7, error ); + if ( error ) throw IError::MakeShared( error ); + #if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4800 ) + #endif + return static_cast< returnType >( returnValue ); + #if XMP_WinBuild + #pragma warning( pop ) + #endif + } + + + template < typename className, typename returnType, typename internalReturnType, typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8, typename t9 > + returnType CallConstSafeFunction( const className * const ptr, + internalReturnType ( APICALL className::*Func )( t1, t2, t3, t4, t5, t6, t7, t8, t9, pcIError_base & ) const, t1 v1, t2 v2, t3 v3, t4 v4, t5 v5, t6 v6, t7 v7, t8 v8, t9 v9 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = (ptr->*Func)( v1, v2, v3, v4, v5, v6, v7, v8, v9, error ); + if ( error ) throw IError::MakeShared( error ); + #if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4800 ) + #endif + return static_cast< returnType >( returnValue ); + #if XMP_WinBuild + #pragma warning( pop ) + #endif + } + + + + template < typename className, typename internalReturnType, typename sharedPointerType > + shared_ptr< sharedPointerType > CallSafeFunctionReturningPointer( className * ptr, + internalReturnType( APICALL className::*Func )( pcIError_base & ) ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = ( ptr->*Func )( error ); + if ( error ) throw IError::MakeShared( error ); + return sharedPointerType::MakeShared( returnValue ); + } + + template < typename className, typename internalReturnType, typename sharedPointerType, typename t1 > + shared_ptr< sharedPointerType > CallSafeFunctionReturningPointer( className * ptr, + internalReturnType( APICALL className::*Func )( t1, pcIError_base & ), t1 v1 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = ( ptr->*Func )( v1, error ); + if ( error ) throw IError::MakeShared( error ); + return sharedPointerType::MakeShared( returnValue ); + } + + template < typename className, typename internalReturnType, typename sharedPointerType, typename t1, typename t2 > + shared_ptr< sharedPointerType > CallSafeFunctionReturningPointer( className * ptr, + internalReturnType( APICALL className::*Func )( t1, t2, pcIError_base & ), t1 v1, t2 v2 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = ( ptr->*Func )( v1, v2, error ); + if ( error ) throw IError::MakeShared( error ); + return sharedPointerType::MakeShared( returnValue ); + } + + template < typename className, typename internalReturnType, typename sharedPointerType, typename t1, typename t2, typename t3 > + shared_ptr< sharedPointerType > CallSafeFunctionReturningPointer( className * ptr, + internalReturnType( APICALL className::*Func )( t1, t2, t3, pcIError_base & ), t1 v1, t2 v2, t3 v3 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = ( ptr->*Func )( v1, v2, v3, error ); + if ( error ) throw IError::MakeShared( error ); + return sharedPointerType::MakeShared( returnValue ); + } + + template < typename className, typename internalReturnType, typename sharedPointerType, typename t1, typename t2, typename t3, typename t4 > + shared_ptr< sharedPointerType > CallSafeFunctionReturningPointer( className * ptr, + internalReturnType( APICALL className::*Func )( t1, t2, t3, t4, pcIError_base & ), t1 v1, t2 v2, t3 v3, t4 v4 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = ( ptr->*Func )( v1, v2, v3, v4, error ); + if ( error ) throw IError::MakeShared( error ); + return sharedPointerType::MakeShared( returnValue ); + } + + template < typename className, typename internalReturnType, typename sharedPointerType, typename t1, typename t2, typename t3, typename t4, typename t5 > + shared_ptr< sharedPointerType > CallSafeFunctionReturningPointer( className * ptr, + internalReturnType( APICALL className::*Func )( t1, t2, t3, t4, t5, pcIError_base & ), t1 v1, t2 v2, t3 v3, t4 v4, t5 v5 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = ( ptr->*Func )( v1, v2, v3, v4, v5, error ); + if ( error ) throw IError::MakeShared( error ); + return sharedPointerType::MakeShared( returnValue ); + } + + template < typename className, typename internalReturnType, typename sharedPointerType, typename t1, typename t2, typename t3, typename t4, typename t5, typename t6 > + shared_ptr< sharedPointerType > CallSafeFunctionReturningPointer( className * ptr, + internalReturnType( APICALL className::*Func )( t1, t2, t3, t4, t5, t6, pcIError_base & ), t1 v1, t2 v2, t3 v3, t4 v4, t5 v5, t6 v6 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = ( ptr->*Func )( v1, v2, v3, v4, v5, v6, error ); + if ( error ) throw IError::MakeShared( error ); + return sharedPointerType::MakeShared( returnValue ); + } + + template < typename className, typename internalReturnType, typename sharedPointerType > + shared_ptr< sharedPointerType > CallConstSafeFunctionReturningPointer( const className * const ptr, + internalReturnType( APICALL className::*Func )( pcIError_base & ) const ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = ( ptr->*Func )( error ); + if ( error ) throw IError::MakeShared( error ); + return sharedPointerType::MakeShared( returnValue ); + } + + template < typename className, typename internalReturnType, typename sharedPointerType, typename t1 > + shared_ptr< sharedPointerType > CallConstSafeFunctionReturningPointer( const className * const ptr, + internalReturnType( APICALL className::*Func )( t1, pcIError_base & ) const, t1 v1 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = ( ptr->*Func )( v1, error ); + if ( error ) throw IError::MakeShared( error ); + return sharedPointerType::MakeShared( returnValue ); + } + + template < typename className, typename internalReturnType, typename sharedPointerType, typename t1, typename t2 > + shared_ptr< sharedPointerType > CallConstSafeFunctionReturningPointer( const className * const ptr, + internalReturnType( APICALL className::*Func )( t1, t2, pcIError_base & ) const, t1 v1, t2 v2 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = ( ptr->*Func )( v1, v2, error ); + if ( error ) throw IError::MakeShared( error ); + return sharedPointerType::MakeShared( returnValue ); + } + + template < typename className, typename internalReturnType, typename sharedPointerType, typename t1, typename t2, typename t3 > + shared_ptr< sharedPointerType > CallConstSafeFunctionReturningPointer( const className * const ptr, + internalReturnType( APICALL className::*Func )( t1, t2, t3, pcIError_base & ) const, t1 v1, t2 v2, t3 v3 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = ( ptr->*Func )( v1, v2, v3, error ); + if ( error ) throw IError::MakeShared( error ); + return sharedPointerType::MakeShared( returnValue ); + } + + template < typename className, typename internalReturnType, typename sharedPointerType, typename t1, typename t2, typename t3, typename t4 > + shared_ptr< sharedPointerType > CallConstSafeFunctionReturningPointer( const className * const ptr, + internalReturnType( APICALL className::*Func )( t1, t2, t3, t4, pcIError_base & ) const, t1 v1, t2 v2, t3 v3, t4 v4 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = ( ptr->*Func )( v1, v2, v3, v4, error ); + if ( error ) throw IError::MakeShared( error ); + return sharedPointerType::MakeShared( returnValue ); + } + + template < typename className, typename internalReturnType, typename sharedPointerType, typename t1, typename t2, typename t3, typename t4, typename t5 > + shared_ptr< sharedPointerType > CallConstSafeFunctionReturningPointer( const className * const ptr, + internalReturnType( APICALL className::*Func )( t1, t2, t3, t4, t5, pcIError_base & ) const, t1 v1, t2 v2, t3 v3, t4 v4, t5 v5 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = ( ptr->*Func )( v1, v2, v3, v4, v5, error ); + if ( error ) throw IError::MakeShared( error ); + return sharedPointerType::MakeShared( returnValue ); + } + + template < typename className, typename internalReturnType, typename sharedPointerType, typename t1, typename t2, typename t3, typename t4, typename t5, typename t6 > + shared_ptr< sharedPointerType > CallConstSafeFunctionReturningPointer( const className * const ptr, + internalReturnType( APICALL className::*Func )( t1, t2, t3, t4, t5, t6, pcIError_base & ) const, t1 v1, t2 v2, t3 v3, t4 v4, t5 v5, t6 v6 ) + { + pcIError_base error( NULL ); + internalReturnType returnValue = ( ptr->*Func )( v1, v2, v3, v4, v5, v6, error ); + if ( error ) throw IError::MakeShared( error ); + return sharedPointerType::MakeShared( returnValue ); + } +} +//! \endcond +#endif // TWrapperFunctions2_h__ + diff --git a/xmp/XMPCommon/XMPCommonDefines.h b/xmp/XMPCommon/XMPCommonDefines.h new file mode 100644 index 0000000..d63de35 --- /dev/null +++ b/xmp/XMPCommon/XMPCommonDefines.h @@ -0,0 +1,166 @@ +#ifndef __XMPCommonDefines_h__ +#define __XMPCommonDefines_h__ 1 + +// ================================================================================================= +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +// ================================================================================================= +// XMP_CommonDefines.h - Common Defines across all the XMP Components +// ================================================================ +// +// This header defines common definitions to be used across all the XMP Components. +// +// ================================================================================================= + +// ================================================================================================= +// All Platform Settings +// =========================== +#include "XMP_Environment.h" +#if !XMP_WinBuild + #include +#endif + // ================================================================================================= + // Macintosh Specific Settings + // =========================== + #if XMP_MacBuild + #define SUPPORT_STD_ATOMIC_IMPLEMENTATION 0 + #ifdef _LIBCPP_VERSION + #define SUPPORT_SHARED_POINTERS_IN_TR1 0 + #define SUPPORT_SHARED_POINTERS_IN_STD 1 + #else + #define SUPPORT_SHARED_POINTERS_IN_TR1 1 + #define SUPPORT_SHARED_POINTERS_IN_STD 0 + #endif + #define SUPPORT_SHARED_POINTERS_WITH_ALLOCATORS 0 + #define BAD_EXCEPTION_SUPPORT_STRINGS 0 + #define VECTOR_SUPPORT_CONST_ITERATOR_FUNCTIONS 0 + #define SUPPORT_VARIADIC_TEMPLATES 0 + #endif + + // ================================================================================================= + // IOS Specific Settings + // =========================== + #if XMP_iOSBuild + #define SUPPORT_STD_ATOMIC_IMPLEMENTATION 0 + #ifdef _LIBCPP_VERSION + #define SUPPORT_SHARED_POINTERS_IN_TR1 0 + #define SUPPORT_SHARED_POINTERS_IN_STD 1 + #else + #define SUPPORT_SHARED_POINTERS_IN_TR1 1 + #define SUPPORT_SHARED_POINTERS_IN_STD 0 + #endif + #define SUPPORT_SHARED_POINTERS_WITH_ALLOCATORS 0 + #define BAD_EXCEPTION_SUPPORT_STRINGS 0 + #define VECTOR_SUPPORT_CONST_ITERATOR_FUNCTIONS 0 + #define SUPPORT_VARIADIC_TEMPLATES 0 + #endif + + // ================================================================================================= + // Windows Specific Settings + // ========================= + #if XMP_WinBuild + #define SUPPORT_SHARED_POINTERS_WITH_ALLOCATORS 1 + #if _MSC_VER <= 1600 + #define SUPPORT_STD_ATOMIC_IMPLEMENTATION 0 + #define SUPPORT_SHARED_POINTERS_IN_TR1 1 + #define SUPPORT_SHARED_POINTERS_IN_STD 0 + #else + #define SUPPORT_STD_ATOMIC_IMPLEMENTATION 1 + #define SUPPORT_SHARED_POINTERS_IN_TR1 0 + #define SUPPORT_SHARED_POINTERS_IN_STD 1 + #endif + #define BAD_EXCEPTION_SUPPORT_STRINGS 1 + #define VECTOR_SUPPORT_CONST_ITERATOR_FUNCTIONS 1 + #endif + + // ================================================================================================= + // UNIX Specific Settings + // ====================== + #if XMP_UNIXBuild + #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) + #if GCC_VERSION >= 40800 + #define SUPPORT_STD_ATOMIC_IMPLEMENTATION 1 + #else + #define REQ_FRIEND_CLASS_DECLARATION() template friend class std::_Sp_counted_ptr; + #define SUPPORT_STD_ATOMIC_IMPLEMENTATION 0 + #endif + + #define SUPPORT_SHARED_POINTERS_IN_TR1 0 + #define SUPPORT_SHARED_POINTERS_IN_STD 1 + #define SUPPORT_SHARED_POINTERS_WITH_ALLOCATORS 0 + #define BAD_EXCEPTION_SUPPORT_STRINGS 0 + #define VECTOR_SUPPORT_CONST_ITERATOR_FUNCTIONS 1 + #define SUPPORT_DYNAMIC_CAST_OPTIMIZATION 0 + #define SUPPORT_VARIADIC_TEMPLATES 0 + #endif + + #ifndef SUPPORT_VARIADIC_TEMPLATES + #define SUPPORT_VARIADIC_TEMPLATES 1 + #endif + + #ifndef REQ_FRIEND_CLASS_DECLARATION + #define REQ_FRIEND_CLASS_DECLARATION() + #endif + + #define JOIN_CLASSNAME_WITH_VERSION_NUMBER_INT(x,y) x ## _v ## y + #define JOIN_CLASSNAME_WITH_VERSION_NUMBER(x,y) JOIN_CLASSNAME_WITH_VERSION_NUMBER_INT(x,y) + #define BASE_CLASS(classNameWithoutVersionNumber, versionNumber) JOIN_CLASSNAME_WITH_VERSION_NUMBER(classNameWithoutVersionNumber, versionNumber) + #define EXPAND_MACRO(X) X + #define QUOTEME2(X) #X + #define QUOTEME(X) QUOTEME2(X) + + #define __NOTHROW__ throw() + + #if SOURCE_COMPILING_XMP_ALL || SOURCE_COMPILING_XMPCORE_LIB || SOURCE_COMPILING_XMPCOMPAREANDMERGE_LIB || SOURCE_COMPILING_XMPEXTENSIONS_LIB + #define SOURCE_COMPILING_XMPCOMMON_LIB 1 + #else + #define SOURCE_COMPILING_XMPCOMMON_LIB 0 + #endif + + #ifndef BUILDING_XMPCOMMON_LIB + #define BUILDING_XMPCOMMON_LIB 0 + #endif + + #if BUILDING_XMPCOMMON_LIB + #if !BUILDING_XMPCOMMON_AS_STATIC && !BUILDING_XMPCOMMON_AS_DYNAMIC + #error "Define either BUILDING_XMPCOMMON_AS_STATIC as 1 or BUILDING_XMPCOMMON_AS_DYNAMIC as 1" + #endif + #endif + + #ifndef __XMP_Const_h__ + #include "XMP_Const.h" + #endif + + namespace AdobeXMPCommon { + + typedef XMP_Int64 int64; + typedef XMP_Uns64 uint64; + typedef XMP_Int32 int32; + typedef XMP_Uns32 uint32; + #if !XMP_64 + typedef uint32 sizet; + #else + typedef uint64 sizet; + #endif + + const sizet kMaxSize ( ( sizet ) -1 ); + const sizet npos ( kMaxSize ); + + // force an enum type to be represented in 32 bits + static const uint32 kMaxEnumValue ( Max_XMP_Uns32 ); + static const uint32 kAllBits ( 0xFFFFFFFF ); + + // unique ids for the interfaces defined in the namespace + static const uint64 kIErrorID ( 0x6e4572726f722020 /* nError */ ); + static const uint64 kIUTF8StringID ( 0x6e55544638537472 /* nUTF8Str */ ); + static const uint64 kIObjectFactoryID ( 0x6e4f626a46616374 /* nObjFact */ ); + static const uint64 kIErrorNotifierID ( 0x6e4572724e6f7466 /* nErrNotf */ ); + static const uint64 kIConfigurationManagerID ( 0x6e436f6e664d6772 /* nConfMgr */ ); + } // namespace AdobeXMPCommon + +#endif // __XMPCommonDefines_h__ diff --git a/xmp/XMPCommon/XMPCommonErrorCodes.h b/xmp/XMPCommon/XMPCommonErrorCodes.h new file mode 100644 index 0000000..22997da --- /dev/null +++ b/xmp/XMPCommon/XMPCommonErrorCodes.h @@ -0,0 +1,114 @@ +#ifndef XMPCommonErrorCodes_h__ +#define XMPCommonErrorCodes_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCommon/XMPCommonDefines.h" + +namespace AdobeXMPCommon { + + //! + //! @brief Indicates various types of error codes within General Domain. + //! + typedef enum { + //! Indicates no error + kGECNone = 0, + + //! Indicates that parameters passed to function are not as expected. + kGECParametersNotAsExpected = 1, + + //! Indicates that version expected by client is not available in the library. + kGECVersionUnavailable = 2, + + //! Indicates that some assertion has failed. + kGECAssertionFailure = 3, + + //! Indicates logic failure. + kGECLogicalError = 4, + + //! Indicates index provided is out of bounds. + kGECIndexOutOfBounds = 5, + + //! Indicates an internal failure. + kGECInternalFailure = 6, + + //! Indicates a call to deprecated function. + kGECDeprecatedFunctionCall = 7, + + //! Indicates an external failure. + kGECExternalFailure = 8, + + //! Indicates an unknown failure. + kGECUnknownFailure = 9, + + //! Indicates an error due to User Abort. + kGECUserAbort = 10, + + //! Indicates a particular interface is not available. + kGECInterfaceUnavailable = 11, + + //! Indicates that client code has thrown some exception. + kGECClientThrownExceptionCaught = 100, + + //! Indicates that standard exception has occurred. + kGECStandardException = 101, + + //! Indicates that some unknown exception has occurred. + kGECUnknownExceptionCaught = 200, + + //! Indicates that functionality is not yet implemented. + kGECNotImplemented = 10000, + + //! Maximum value this enum can hold, should be treated as invalid value. + kGECMaxValue = kMaxEnumValue + } eGeneralErrorCode; + + //! + //! @brief Indicates various types of error codes within Memory Management domain. + //! + typedef enum { + //! Indicates no error + kMMECNone = 0, + + //! Indicates that allocation has failed. + kMMECAllocationFailure = 1, + + //! Maximum value this enum can hold, should be treated as invalid value. + kMMECMaxValue = kMaxEnumValue + } eMemoryManagementErrorCode; + + //! + //! @brief Indicates various types of error codes within Configurable domain. + //! + typedef enum { + //! Indicates no error. + kCECNone = 0, + + //! Indicates that key is not supported by the object. + kCECKeyNotSupported = 1, + + //! Indicates different type of value provided than the one supported for a key. + kCECValueTypeNotSupported = 2, + + //! Indicates that different value type is previously stored for a key. + kCECPreviousTypeDifferent = 3, + + //! Indicates the type of value stored for a key is different than what client is asking for. + kCECValueTypeMismatch = 4, + + //! Indicates an invalid value is provided. + kCECValueNotSupported = 5, + + //! Maximum value this enum can hold, should be treated as invalid value. + kCECodeMaxValue = 0xFFFFFFFF + } eConfigurableErrorCode; +} + +#endif // XMPCommonErrorCodes_h__ diff --git a/xmp/XMPCommon/XMPCommonFwdDeclarations.h b/xmp/XMPCommon/XMPCommonFwdDeclarations.h new file mode 100644 index 0000000..dce21ec --- /dev/null +++ b/xmp/XMPCommon/XMPCommonFwdDeclarations.h @@ -0,0 +1,158 @@ +#ifndef __XMPCommonFwdDeclarations_h__ +#define __XMPCommonFwdDeclarations_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCommon/XMPCommonDefines.h" +#include "XMPCommon/XMPCommonLatestInterfaceVersions.h" +#include + +#if SUPPORT_SHARED_POINTERS_IN_STD + #include + #include +#elif SUPPORT_SHARED_POINTERS_IN_TR1 + #if XMP_WinBuild + #include + #else + #include + #include + #endif +#else + #error "location of shared pointer stuff is unknown" +#endif + +namespace AdobeXMPCommon { + + #if SUPPORT_SHARED_POINTERS_IN_STD + using std::shared_ptr; + using std::enable_shared_from_this; + using std::mem_fn; + #elif SUPPORT_SHARED_POINTERS_IN_TR1 + using std::tr1::shared_ptr; + using std::tr1::enable_shared_from_this; + using std::tr1::mem_fn; + #endif + + // void + typedef void * pvoid; + typedef const void * pcvoid; + typedef shared_ptr< void > spvoid; + typedef shared_ptr< const void * > spcvoid; + + // IObjectFactory + class IObjectFactory_v1; + typedef IObjectFactory_v1 IObjectFactory_base; + typedef IObjectFactory_v1 * pIObjectFactory_base; + typedef const IObjectFactory_v1 * pcIObjectFactory_base; + typedef BASE_CLASS( IObjectFactory, IOBJECTFACTORY_VERSION ) IObjectFactory; + typedef IObjectFactory * pIObjectFactory; + typedef const IObjectFactory * pcIObjectFactory; + + // IError + class IError_v1; + typedef IError_v1 IError_base; + typedef IError_v1 * pIError_base; + typedef const IError_v1 * pcIError_base; + typedef BASE_CLASS( IError, IERROR_VERSION ) IError; + typedef IError * pIError; + typedef const IError * pcIError; + typedef shared_ptr< IError > spIError; + typedef shared_ptr< const IError > spcIError; + + // IUTF8String + class IUTF8String_v1; + typedef IUTF8String_v1 IUTF8String_base; + typedef IUTF8String_v1 * pIUTF8String_base; + typedef const IUTF8String_v1 * pcIUTF8String_base; + typedef BASE_CLASS( IUTF8String, IUTF8STRING_VERSION ) IUTF8String; + typedef IUTF8String * pIUTF8String; + typedef const IUTF8String * pcIUTF8String; + typedef shared_ptr< IUTF8String > spIUTF8String; + typedef shared_ptr< const IUTF8String > spcIUTF8String; + + // IMemoryAllocator + class IMemoryAllocator_v1; + typedef IMemoryAllocator_v1 IMemoryAllocator_base; + typedef IMemoryAllocator_v1 * pIMemoryAllocator_base; + typedef const IMemoryAllocator_v1 * pcIMemoryAllocator_base; + typedef BASE_CLASS( IMemoryAllocator, IMEMORYALLOCATOR_VERSION ) IMemoryAllocator; + typedef IMemoryAllocator * pIMemoryAllocator; + typedef const IMemoryAllocator * pcIMemoryAllocator; + typedef shared_ptr< IMemoryAllocator > spIMemoryAllocator; + typedef shared_ptr< const IMemoryAllocator > spcIMemoryAllocator; + + // IErrorNotifier + class IErrorNotifier_v1; + typedef IErrorNotifier_v1 IErrorNotifier_base; + typedef IErrorNotifier_v1 * pIErrorNotifier_base; + typedef const IErrorNotifier_v1 * pcIErrorNotifier_base; + typedef BASE_CLASS( IErrorNotifier, IERRORNOTIFIER_VERSION ) IErrorNotifier; + typedef IErrorNotifier * pIErrorNotifier; + typedef const IErrorNotifier * pcIErrorNotifier; + typedef shared_ptr< IErrorNotifier > spIErrorNotifier; + typedef shared_ptr< const IErrorNotifier > spcIErrorNotifier; + + // IConfigurationManager + class IConfigurationManager_v1; + typedef IConfigurationManager_v1 IConfigurationManager_base; + typedef IConfigurationManager_v1 * pIConfigurationManager_base; + typedef const IConfigurationManager_v1 * pcIConfigurationManager_base; + typedef BASE_CLASS( IConfigurationManager, ICONFIGURATIONMANAGER_VERSION ) IConfigurationManager; + typedef IConfigurationManager * pIConfigurationManager; + typedef const IConfigurationManager * pcIConfigurationManager; + typedef shared_ptr< IConfigurationManager > spIConfigurationManager; + typedef shared_ptr< const IConfigurationManager > spcIConfigurationManager; + + // IConfigurable + class IConfigurable; + typedef IConfigurable * pIConfigurable; + typedef const IConfigurable * pcIConfigurable; + + // typedefs for vectors and their corresponding shared pointers. + typedef std::vector< spIUTF8String > IUTF8Strings; + typedef std::vector< spcIUTF8String > cIUTF8Strings; + typedef shared_ptr< IUTF8Strings > spIUTF8Strings; + typedef shared_ptr< cIUTF8Strings > spcIUTF8Strings; + typedef shared_ptr< const IUTF8Strings > spIUTF8Strings_const; + typedef shared_ptr< const cIUTF8Strings > spcIUTF8Strings_const; + + //! + //! @brief A function pointer to get the memory allocated from the library. + //! \param[in] size a value indicating the number of bytes to be allocated. + //! \return a pointer to memory allocated by the library. + //! \note NULL value is returned in case memory allocation fails. + //! + typedef void * ( *MemAllocateProc )( sizet sz ); + void * MemAllocate( sizet size ) __NOTHROW__; + + //! + //! @brief A function pointer to get the memory freed from the library. + //! \param[in] ptr address of the memory location to be freed. + //! + typedef void( *MemReleaseProc )( void * ptr ); + void MemRelease( void * ptr ) __NOTHROW__; + +} // namespace AdobeXMPCommon + +namespace AdobeXMPCommon_Int { + + // ISharedObject_I + class ISharedObject_I; + typedef ISharedObject_I * pISharedObject_I; + typedef const ISharedObject_I * pcISharedObject_I; + + // IThreadSafe_I + class IThreadSafe_I; + typedef IThreadSafe_I * pIThreadSafe_I; + typedef const IThreadSafe_I * pcIThreadSafe_I; + +} + +#endif // __XMPCommonFwdDeclarations_h__ diff --git a/xmp/XMPCommon/XMPCommonLatestInterfaceVersions.h b/xmp/XMPCommon/XMPCommonLatestInterfaceVersions.h new file mode 100644 index 0000000..d7f5add --- /dev/null +++ b/xmp/XMPCommon/XMPCommonLatestInterfaceVersions.h @@ -0,0 +1,51 @@ +#ifndef XMPCommonLatestInterfaceVersions_h__ +#define XMPCommonLatestInterfaceVersions_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +//! +//!@brief Macro to include a client file through with client can control the interface versions he wants to stick with +//!if not the latest ones. +//! +#if !SOURCE_COMPILING_XMPCOMMON_LIB + #ifdef XMPCOMMON_CLIENT_VERSION_NUMBER_FILE + #include QUOTEME(XMPCOMMON_CLIENT_VERSION_NUMBER_FILE) + #endif +#endif + +#ifndef IOBJECTFACTORY_VERSION + #define IOBJECTFACTORY_VERSION 1 +#endif + +#ifndef IERROR_VERSION + #define IERROR_VERSION 1 +#endif + +#ifndef IUTF8STRING_VERSION + #define IUTF8STRING_VERSION 1 +#endif + +#ifndef IMEMORYALLOCATOR_VERSION + #define IMEMORYALLOCATOR_VERSION 1 +#endif + +#ifndef IERRORNOTIFIER_VERSION + #define IERRORNOTIFIER_VERSION 1 +#endif + +#ifndef ICONFIGURATIONMANAGER_VERSION + #define ICONFIGURATIONMANAGER_VERSION 1 +#endif + +#ifndef ICONFIGURABLE_VERSION + #define ICONFIGURABLE_VERSION 1 +#endif + +#endif // XMPCommonLatestInterfaceVersions_h__ diff --git a/xmp/XMPCommon/source/IConfigurable.cpp b/xmp/XMPCommon/source/IConfigurable.cpp new file mode 100644 index 0000000..fb8e2f3 --- /dev/null +++ b/xmp/XMPCommon/source/IConfigurable.cpp @@ -0,0 +1,204 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCommon/Interfaces/BaseInterfaces/IConfigurable.h" + +#if !BUILDING_XMPCOMMON_LIB && !SOURCE_COMPILING_XMP_ALL + +#include "XMPCommon/Utilities/TWrapperFunctions.h" +#include + +namespace AdobeXMPCommon { + + IConfigurableProxy::IConfigurableProxy( pIConfigurable configurable ) + : mConfigurableRawPtr( configurable ) { } + + void APICALL IConfigurableProxy::SetParameter( const uint64 & key, bool value ) { + CombinedDataValue combinedValue; + combinedValue.uint32Value = value ? 1 : 0; + CallSafeFunctionReturningVoid< IConfigurable, const uint64 &, uint32, const CombinedDataValue & + >( + mConfigurableRawPtr, &IConfigurable::setParameter, key, static_cast< uint32 >( IConfigurable::kDTBool ), combinedValue + ); + } + + void APICALL IConfigurableProxy::setParameter( const uint64 & key, uint32 dataType, const CombinedDataValue & dataValue, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mConfigurableRawPtr->setParameter( key, dataType, dataValue, error ); + } + + bool APICALL IConfigurableProxy::RemoveParameter( const uint64 & key ) { + return CallSafeFunction< IConfigurable, bool, uint32, const uint64 & >( + mConfigurableRawPtr, &IConfigurable::removeParameter, key ); + } + + uint32 APICALL IConfigurableProxy::removeParameter( const uint64 & key, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mConfigurableRawPtr->removeParameter( key, error ); + } + + bool APICALL IConfigurableProxy::GetParameter( const uint64 & key, bool & value ) const { + CombinedDataValue combinedValue; + bool returnValue = CallConstSafeFunction< IConfigurable, bool, uint32, const uint64 &, uint32, CombinedDataValue & + >( + mConfigurableRawPtr, &IConfigurable::getParameter, key, static_cast< uint32 >( IConfigurable::kDTBool ), combinedValue + ); + value = combinedValue.uint32Value != 0 ? 1 : 0; + return returnValue; + } + + uint32 APICALL IConfigurableProxy::getParameter( const uint64 & key, uint32 dataType, CombinedDataValue & value, pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mConfigurableRawPtr->getParameter( key, dataType, value, error ); + } + + std::vector< uint64 > APICALL IConfigurableProxy::GetAllParameters() const { + sizet nElements = mConfigurableRawPtr->Size(); + std::vector< uint64 > vec( nElements ); + if ( nElements > 0 ) + mConfigurableRawPtr->getAllParameters( vec.data(), nElements ); + return vec; + } + + void APICALL IConfigurableProxy::getAllParameters( uint64 * array, sizet count ) const __NOTHROW__ { + assert( false ); + return mConfigurableRawPtr->getAllParameters( array, count ); + } + + sizet APICALL IConfigurableProxy::Size() const __NOTHROW__ { + return mConfigurableRawPtr->Size(); + } + + IConfigurable::eDataType APICALL IConfigurableProxy::GetDataType( const uint64 & key ) const { + return CallConstSafeFunction< IConfigurable, eDataType, uint32, const uint64 & >( + mConfigurableRawPtr, &IConfigurable::getDataType, key ); + } + + uint32 APICALL IConfigurableProxy::getDataType( const uint64 & key, pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mConfigurableRawPtr->getDataType( key, error ); + } + + bool APICALL IConfigurableProxy::GetParameter( const uint64 & key, const void * & value ) const { + CombinedDataValue combinedValue; + bool returnValue = CallConstSafeFunction< IConfigurable, bool, uint32, const uint64 &, uint32, CombinedDataValue & + >( + mConfigurableRawPtr, &IConfigurable::getParameter, key, static_cast< uint32 >( IConfigurable::kDTConstVoidPtr ), combinedValue + ); + value = combinedValue.constVoidPtrValue; + return returnValue; + } + + bool APICALL IConfigurableProxy::GetParameter( const uint64 & key, const char * & value ) const { + CombinedDataValue combinedValue; + bool returnValue = CallConstSafeFunction< IConfigurable, bool, uint32, const uint64 &, uint32, CombinedDataValue & + >( + mConfigurableRawPtr, &IConfigurable::getParameter, key, static_cast< uint32 >( IConfigurable::kDTConstCharBuffer ), combinedValue + ); + value = combinedValue.constCharPtrValue; + return returnValue; + } + + bool APICALL IConfigurableProxy::GetParameter( const uint64 & key, char & value ) const { + CombinedDataValue combinedValue; + bool returnValue = CallConstSafeFunction< IConfigurable, bool, uint32, const uint64 &, uint32, CombinedDataValue & + >( + mConfigurableRawPtr, &IConfigurable::getParameter, key, static_cast< uint32 >( IConfigurable::kDTChar ), combinedValue + ); + value = combinedValue.charValue; + return returnValue; + } + + bool APICALL IConfigurableProxy::GetParameter( const uint64 & key, double & value ) const { + CombinedDataValue combinedValue; + bool returnValue = CallConstSafeFunction< IConfigurable, bool, uint32, const uint64 &, uint32, CombinedDataValue & + >( + mConfigurableRawPtr, &IConfigurable::getParameter, key, static_cast< uint32 >( IConfigurable::kDTDouble ), combinedValue + ); + value = combinedValue.doubleValue; + return returnValue; + } + + bool APICALL IConfigurableProxy::GetParameter( const uint64 & key, int64 & value ) const { + CombinedDataValue combinedValue; + bool returnValue = CallConstSafeFunction< IConfigurable, bool, uint32, const uint64 &, uint32, CombinedDataValue & + >( + mConfigurableRawPtr, &IConfigurable::getParameter, key, static_cast< uint32 >( IConfigurable::kDTInt64 ), combinedValue + ); + value = combinedValue.int64Value; + return returnValue; + } + + bool APICALL IConfigurableProxy::GetParameter( const uint64 & key, uint64 & value ) const { + CombinedDataValue combinedValue; + bool returnValue = CallConstSafeFunction< IConfigurable, bool, uint32, const uint64 &, uint32, CombinedDataValue & + >( + mConfigurableRawPtr, &IConfigurable::getParameter, key, static_cast< uint32 >( IConfigurable::kDTUint64 ), combinedValue + ); + value = combinedValue.uint64Value; + return returnValue; + } + + void APICALL IConfigurableProxy::SetParameter( const uint64 & key, const void * value ) { + CombinedDataValue combinedValue; + combinedValue.constVoidPtrValue= value; + return CallSafeFunctionReturningVoid< IConfigurable, const uint64 &, uint32, const CombinedDataValue & + >( + mConfigurableRawPtr, &IConfigurable::setParameter, key, static_cast< uint32 >( IConfigurable::kDTConstVoidPtr ), combinedValue + ); + } + + void APICALL IConfigurableProxy::SetParameter( const uint64 & key, const char * value ) { + CombinedDataValue combinedValue; + combinedValue.constCharPtrValue = value; + return CallSafeFunctionReturningVoid< IConfigurable, const uint64 &, uint32, const CombinedDataValue & + >( + mConfigurableRawPtr, &IConfigurable::setParameter, key, static_cast< uint32 >( IConfigurable::kDTConstCharBuffer ), combinedValue + ); + } + + void APICALL IConfigurableProxy::SetParameter( const uint64 & key, char value ) { + CombinedDataValue combinedValue; + combinedValue.charValue = value; + return CallSafeFunctionReturningVoid< IConfigurable, const uint64 &, uint32, const CombinedDataValue & + >( + mConfigurableRawPtr, &IConfigurable::setParameter, key, static_cast< uint32 >( IConfigurable::kDTChar ), combinedValue + ); + } + + void APICALL IConfigurableProxy::SetParameter( const uint64 & key, double value ) { + CombinedDataValue combinedValue; + combinedValue.doubleValue = value; + return CallSafeFunctionReturningVoid< IConfigurable, const uint64 &, uint32, const CombinedDataValue & + >( + mConfigurableRawPtr, &IConfigurable::setParameter, key, static_cast< uint32 >( IConfigurable::kDTDouble ), combinedValue + ); + } + + void APICALL IConfigurableProxy::SetParameter( const uint64 & key, int64 value ) { + CombinedDataValue combinedValue; + combinedValue.int64Value = value; + return CallSafeFunctionReturningVoid< IConfigurable, const uint64 &, uint32, const CombinedDataValue & + >( + mConfigurableRawPtr, &IConfigurable::setParameter, key, static_cast< uint32 >( IConfigurable::kDTInt64 ), combinedValue + ); + } + + void APICALL IConfigurableProxy::SetParameter( const uint64 & key, uint64 value ) { + CombinedDataValue combinedValue; + combinedValue.uint64Value = value; + return CallSafeFunctionReturningVoid< IConfigurable, const uint64 &, uint32, const CombinedDataValue & + >( + mConfigurableRawPtr, &IConfigurable::setParameter, key, static_cast< uint32 >( IConfigurable::kDTUint64 ), combinedValue + ); + } + +} + +#endif // !BUILDING_XMPCOMMON_LIB && !SOURCE_COMPILING_XMP_ALL diff --git a/xmp/XMPCommon/source/IConfigurationManager.cpp b/xmp/XMPCommon/source/IConfigurationManager.cpp new file mode 100644 index 0000000..0cfdab1 --- /dev/null +++ b/xmp/XMPCommon/source/IConfigurationManager.cpp @@ -0,0 +1,92 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#define FRIEND_CLASS_DECLARATION() friend class IConfigurationManagerProxy; + +#include "XMPCommon/Interfaces/IConfigurationManager.h" + +#if !BUILDING_XMPCOMMON_LIB && !SOURCE_COMPILING_XMP_ALL + +#include "XMPCommon/Utilities/TWrapperFunctions.h" +#include "XMPCommon/Interfaces/IError.h" + +#include + +namespace AdobeXMPCommon { + + void APICALL IConfigurationManagerProxy::Acquire() const __NOTHROW__ { + assert( false ); + } + + void APICALL IConfigurationManagerProxy::Release() const __NOTHROW__ { + assert( false ); + } + + pvoid APICALL IConfigurationManagerProxy::getInterfacePointer( uint64 interfaceID, uint32 interfaceVersion, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->getInterfacePointer( interfaceID, interfaceVersion, error ); + } + + pvoid APICALL IConfigurationManagerProxy::GetInterfacePointer( uint64 interfaceID, uint32 interfaceVersion ) { + return AdobeXMPCommon::CallSafeFunction< + IVersionable, pvoid, pvoid, uint64, uint32 + >( mRawPtr, &IVersionable::getInterfacePointer, interfaceID, interfaceVersion ); + } + + uint32 APICALL IConfigurationManagerProxy::registerMemoryAllocator( pIMemoryAllocator_base memoryAllocator, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->registerMemoryAllocator( memoryAllocator, error ); + } + + bool APICALL IConfigurationManagerProxy::RegisterMemoryAllocator( pIMemoryAllocator memoryAllocator ) { + return CallSafeFunction< IConfigurationManager, bool, uint32, pIMemoryAllocator_base >( + mRawPtr, &IConfigurationManager::registerMemoryAllocator, memoryAllocator ); + } + + uint32 APICALL IConfigurationManagerProxy::registerErrorNotifier( pIErrorNotifier_base clientErrorNotifier, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->registerErrorNotifier( clientErrorNotifier, error ); + } + + bool APICALL IConfigurationManagerProxy::RegisterErrorNotifier( pIErrorNotifier_base clientErrorNotifier ) { + return CallSafeFunction< IConfigurationManager, bool, uint32, pIErrorNotifier_base >( + mRawPtr, &IConfigurationManager::registerErrorNotifier, clientErrorNotifier ); + } + + uint32 APICALL IConfigurationManagerProxy::disableMultiThreading( pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->disableMultiThreading( error ); + } + + bool APICALL IConfigurationManagerProxy::DisableMultiThreading() { + return CallSafeFunction< IConfigurationManager, bool, uint32 >( + mRawPtr, &IConfigurationManager::disableMultiThreading ); + } + + uint32 APICALL IConfigurationManagerProxy::isMultiThreaded( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->isMultiThreaded( error ); + } + + bool APICALL IConfigurationManagerProxy::IsMultiThreaded() const { + return CallConstSafeFunction< IConfigurationManager, bool, uint32 >( + mRawPtr, &IConfigurationManager::isMultiThreaded ); + } + + spIConfigurationManager IConfigurationManager_v1::MakeShared( pIConfigurationManager_base ptr ) { + if ( !ptr ) return spIConfigurationManager(); + pIConfigurationManager p = IConfigurationManager::GetInterfaceVersion() > 1 ? + ptr->GetInterfacePointer< IConfigurationManager >() : ptr; + return shared_ptr< IConfigurationManager >( new IConfigurationManagerProxy( p ) ); + } + +} + +#endif // !BUILDING_XMPCOMMON_LIB && !SOURCE_COMPILING_XMP_LIB + diff --git a/xmp/XMPCommon/source/IError.cpp b/xmp/XMPCommon/source/IError.cpp new file mode 100644 index 0000000..8f763d3 --- /dev/null +++ b/xmp/XMPCommon/source/IError.cpp @@ -0,0 +1,207 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#define FRIEND_CLASS_DECLARATION() friend class IErrorProxy; + +#include "XMPCommon/Interfaces/IError.h" + +#if !BUILDING_XMPCOMMON_LIB && !SOURCE_COMPILING_XMP_ALL + +#include "XMPCommon/Utilities/TWrapperFunctions.h" +#include "XMPCommon/Interfaces/IUTF8String.h" +#include "XMPCommon/Interfaces/IObjectFactory.h" + +#include + +namespace AdobeXMPCommon { + class IErrorProxy + : public virtual IError + { + private: + pIError mRawPtr; + + public: + IErrorProxy( pIError ptr ) + : mRawPtr( ptr ) + { + mRawPtr->Acquire(); + } + + ~IErrorProxy() __NOTHROW__ { mRawPtr->Release(); } + + pIError APICALL GetActualIError() __NOTHROW__ { return mRawPtr; } + + void APICALL Acquire() const __NOTHROW__ { assert( false ); } + + void APICALL Release() const __NOTHROW__ { assert( false ); } + + pvoid APICALL GetInterfacePointer( uint64 interfaceID, uint32 interfaceVersion ) { + return CallSafeFunction< + IVersionable, pvoid, pvoid, uint64, uint32 + >( mRawPtr, &IVersionable::getInterfacePointer, interfaceID, interfaceVersion ); + } + + pvoid APICALL getInterfacePointer( uint64 interfaceID, uint32 interfaceVersion, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->getInterfacePointer( interfaceID, interfaceVersion, error ); + } + + eErrorCode APICALL GetCode() const { + return CallConstSafeFunction< IError, eErrorCode, uint32 >( + mRawPtr, &IError::getCode ); + } + + uint32 APICALL getCode( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getCode( error ); + } + + eErrorDomain APICALL GetDomain() const { + return CallConstSafeFunction< IError, eErrorDomain, uint32 >( + mRawPtr, &IError::getDomain ); + } + + uint32 APICALL getDomain( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getDomain( error ); + } + + eErrorSeverity APICALL GetSeverity() const { + return CallConstSafeFunction< IError, eErrorSeverity, uint32 >( + mRawPtr, &IError::getSeverity ); + } + + uint32 APICALL getSeverity( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getSeverity( error ); + } + + spcIUTF8String APICALL GetMessage() const { + return CallConstSafeFunctionReturningPointer< IError, pcIUTF8String_base, const IUTF8String >( + mRawPtr, &IError::getMessage ); + } + + pcIUTF8String_base APICALL getMessage( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getMessage( error ); + } + + spcIUTF8String APICALL GetLocation() const { + return CallConstSafeFunctionReturningPointer< IError, pcIUTF8String_base, const IUTF8String >( + mRawPtr, &IError::getLocation ); + } + + pcIUTF8String_base APICALL getLocation( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getLocation( error ); + } + + spcIUTF8String APICALL GetParameter( sizet index ) const { + return CallConstSafeFunctionReturningPointer< IError, pcIUTF8String_base, const IUTF8String, sizet >( + mRawPtr, &IError::getParameter, index ); + } + + pcIUTF8String_base APICALL getParameter( sizet index, pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getParameter( index, error ); + } + + sizet APICALL GetParametersCount() const __NOTHROW__ { + return mRawPtr->GetParametersCount(); + } + + spIError APICALL GetNextError() { + pcIError_base error( NULL ); + pIError_base ptr = mRawPtr->getNextError( error ); + if ( error ) throw MakeShared( error ); + return MakeShared( ptr ); + } + + pIError_base APICALL getNextError( pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->getNextError( error ); + } + + spIError APICALL SetNextError( const spIError & nextError ) { + pcIError_base error( NULL ); + pIError_base ptr = mRawPtr->setNextError( nextError ? nextError->GetActualIError() : NULL, error ); + if ( error ) throw MakeShared( error ); + return MakeShared( ptr ); + } + + pIError_base APICALL setNextError( pIError_base nextError, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->setNextError( nextError, error ); + } + + void APICALL SetMessage( const char * message, sizet len ) __NOTHROW__ { + mRawPtr->SetMessage( message, len ); + } + + void APICALL SetLocation( const char * fileName, sizet lineNumber ) __NOTHROW__ { + mRawPtr->SetLocation( fileName, lineNumber ); + } + + void APICALL AppendParameter( const char * parameter, sizet len ) __NOTHROW__ { + mRawPtr->AppendParameter( parameter, len ); + } + + void APICALL AppendParameter( void * addressParameter ) __NOTHROW__ { + return mRawPtr->AppendParameter( addressParameter ); + } + + void APICALL AppendParameter( const uint32 & integerValue ) __NOTHROW__ { + return mRawPtr->AppendParameter( integerValue ); + } + + void APICALL AppendParameter( const uint64 & integerValue ) __NOTHROW__ { + return mRawPtr->AppendParameter( integerValue ); + } + + void APICALL AppendParameter( const int32 & integerValue ) __NOTHROW__ { + return mRawPtr->AppendParameter( integerValue ); + } + + void APICALL AppendParameter( const int64 & integerValue ) __NOTHROW__ { + return mRawPtr->AppendParameter( integerValue ); + } + + void APICALL AppendParameter( const float & floatValue ) __NOTHROW__ { + return mRawPtr->AppendParameter( floatValue ); + } + + void APICALL AppendParameter( const double & doubleValue ) __NOTHROW__ { + return mRawPtr->AppendParameter( doubleValue ); + } + + void APICALL AppendParameter( bool booleanValue ) __NOTHROW__ { + return mRawPtr->AppendParameter( booleanValue ); + } + + AdobeXMPCommon_Int::pISharedObject_I APICALL GetISharedObject_I() __NOTHROW__ { + return mRawPtr->GetISharedObject_I(); + } + + }; + + spIError IError_v1::CreateError( pIObjectFactory objFactory, eErrorDomain errDomain, eErrorCode errCode, eErrorSeverity errSeverity ) { + pIError_base temp = CallSafeFunction< IObjectFactory_base, pIError_base, pIError_base, uint32, uint32, uint32 >( + objFactory, &IObjectFactory_base::CreateError, static_cast< uint32 >( errDomain ), + static_cast< uint32 >( errCode ), static_cast< uint32 >( errSeverity ) ); + return MakeShared( temp ); + } + + spIError IError_v1::MakeShared( pIError_base ptr ) { + if ( !ptr ) return spIError(); + pIError p = IError::GetInterfaceVersion() > 1 ? ptr->GetInterfacePointer() : ptr; + return shared_ptr< IError >( new IErrorProxy( p ) ); + } +} + +#endif // !BUILDING_XMPCOMMON_LIB && !SOURCE_COMPILING_XMP_ALL diff --git a/xmp/XMPCommon/source/IErrorNotifier.cpp b/xmp/XMPCommon/source/IErrorNotifier.cpp new file mode 100644 index 0000000..acc8486 --- /dev/null +++ b/xmp/XMPCommon/source/IErrorNotifier.cpp @@ -0,0 +1,26 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCommon/Interfaces/IErrorNotifier.h" +#include "XMPCommon/Interfaces/IError.h" + +namespace AdobeXMPCommon { + uint32 APICALL IErrorNotifier_v1::notify( pcIError_base error, uint32 & exceptionThrown ) __NOTHROW__ { + exceptionThrown = 0; + bool retValue( false ); + try { + retValue = Notify( IError::MakeShared( error ) ); + } catch ( ... ) { + exceptionThrown = 1; + } + return retValue ? 1 : 0; + } + +} + diff --git a/xmp/XMPCommon/source/IUTF8String.cpp b/xmp/XMPCommon/source/IUTF8String.cpp new file mode 100644 index 0000000..5f24b0e --- /dev/null +++ b/xmp/XMPCommon/source/IUTF8String.cpp @@ -0,0 +1,299 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#define FRIEND_CLASS_DECLARATION() friend class IUTF8StringProxy; + +#include "XMPCommon/Interfaces/IUTF8String.h" + +#if !BUILDING_XMPCOMMON_LIB && !SOURCE_COMPILING_XMP_ALL + +#include "XMPCommon/Utilities/TWrapperFunctions.h" +#include "XMPCommon/Interfaces/IObjectFactory.h" + +#include + +namespace AdobeXMPCommon { + + class IUTF8StringProxy + : public virtual IUTF8String + , public enable_shared_from_this< IUTF8StringProxy > + { + private: + pIUTF8String mRawPtr; + + public: + IUTF8StringProxy( pIUTF8String ptr ) + : mRawPtr( ptr ) + { + mRawPtr->Acquire(); + } + + ~IUTF8StringProxy() __NOTHROW__ { mRawPtr->Release(); } + + pIUTF8String APICALL GetActualIUTF8String() __NOTHROW__ { return mRawPtr; } + + void APICALL Acquire() const __NOTHROW__ { assert( false ); } + + void APICALL Release() const __NOTHROW__ { assert( false ); } + + spIUTF8String APICALL append( const char * buf, sizet count ) { + CallSafeFunction< IUTF8String, pIUTF8String_base, pIUTF8String_base, const char *, sizet >( + mRawPtr, &IUTF8String::append, buf, count )->Release(); + return shared_from_this(); + } + + spIUTF8String APICALL append( const spcIUTF8String & src, sizet srcPos, sizet count ) { + CallSafeFunction< IUTF8String, pIUTF8String_base, pIUTF8String_base, pcIUTF8String_base, sizet, sizet >( + mRawPtr, &IUTF8String::append, src ? src->GetActualIUTF8String() : NULL, srcPos, count )->Release(); + return shared_from_this(); + } + + pIUTF8String_base APICALL append( const char * buffer, sizet count, pcIError_base & error ) __NOTHROW__ { + assert( false ); + mRawPtr->append( buffer, count, error ); + return this; + } + + pIUTF8String_base APICALL append( pcIUTF8String_base str, sizet srcPos, sizet count, pcIError_base & error ) __NOTHROW__ { + assert( false ); + mRawPtr->append( str, srcPos, count, error ); + return this; + } + + spIUTF8String APICALL assign( const char * buf, sizet count ) { + CallSafeFunction< IUTF8String, pIUTF8String_base, pIUTF8String_base, const char *, sizet >( + mRawPtr, &IUTF8String::assign, buf, count )->Release(); + return shared_from_this(); + } + + spIUTF8String APICALL assign( const spcIUTF8String & src, sizet srcPos, sizet count ) { + CallSafeFunction< IUTF8String, pIUTF8String_base, pIUTF8String_base, pcIUTF8String_base, sizet, sizet >( + mRawPtr, &IUTF8String::assign, src ? src->GetActualIUTF8String() : NULL, srcPos, count )->Release(); + return shared_from_this(); + } + + pIUTF8String_base APICALL assign( const char * buffer, sizet count, pcIError_base & error ) __NOTHROW__ { + assert( false ); + mRawPtr->assign( buffer, count, error ); + return this; + } + + pIUTF8String_base APICALL assign( pcIUTF8String_base str, sizet srcPos, sizet count, pcIError_base & error ) __NOTHROW__ { + assert( false ); + mRawPtr->assign( str, srcPos, count, error ); + return this; + } + + spIUTF8String APICALL insert( sizet pos, const char * buf, sizet count ) { + CallSafeFunction< IUTF8String, pIUTF8String_base, pIUTF8String_base, sizet, const char *, sizet >( + mRawPtr, &IUTF8String::insert, pos, buf, count )->Release(); + return shared_from_this(); + } + + spIUTF8String APICALL insert( sizet pos, const spcIUTF8String & src, sizet srcPos, sizet count ) { + CallSafeFunction< IUTF8String, pIUTF8String_base, pIUTF8String_base, sizet, pcIUTF8String_base, sizet, sizet >( + mRawPtr, &IUTF8String::insert, pos, src ? src->GetActualIUTF8String() : NULL, srcPos, count )->Release(); + return shared_from_this(); + } + + pIUTF8String_base APICALL insert( sizet pos, const char * buf, sizet count, pcIError_base & error ) __NOTHROW__ { + assert( false ); + mRawPtr->insert( pos, buf, count, error ); + return this; + } + + pIUTF8String_base APICALL insert( sizet pos, pcIUTF8String_base src, sizet srcPos, sizet count, pcIError_base & error ) __NOTHROW__ { + assert( false ); + mRawPtr->insert( pos, src, srcPos, count, error ); + return this; + } + + spIUTF8String APICALL erase( sizet pos, sizet count ) { + CallSafeFunction< IUTF8String, pIUTF8String_base, pIUTF8String_base, sizet, sizet >( + mRawPtr, &IUTF8String::erase, pos, count )->Release(); + return shared_from_this(); + } + + pIUTF8String_base APICALL erase( sizet pos, sizet count, pcIError_base & error ) __NOTHROW__ { + assert( false ); + mRawPtr->erase( pos, count, error ); + return this; + } + + void APICALL resize( sizet n ) { + CallSafeFunctionReturningVoid< IUTF8String, sizet >( + mRawPtr, &IUTF8String::resize, n ); + } + + virtual void APICALL resize( sizet n, pcIError_base & error ) __NOTHROW__ { + assert( false ); + mRawPtr->resize( n, error ); + } + + spIUTF8String APICALL replace( sizet pos, sizet count, const char * buf, sizet srcCount ) { + CallSafeFunction< IUTF8String, pIUTF8String_base, pIUTF8String_base, sizet, sizet, const char *, sizet >( + mRawPtr, &IUTF8String::replace, pos, count, buf, srcCount )->Release(); + return shared_from_this(); + } + + spIUTF8String APICALL replace( sizet pos, sizet count, const spcIUTF8String & src, sizet srcPos, sizet srcCount ) { + CallSafeFunction< IUTF8String, pIUTF8String_base, pIUTF8String_base, sizet, sizet, pcIUTF8String_base, sizet, sizet >( + mRawPtr, &IUTF8String::replace, pos, count, src ? src->GetActualIUTF8String() : NULL, srcPos, srcCount )->Release(); + return shared_from_this(); + } + + pIUTF8String_base APICALL replace( sizet pos, sizet count, const char * buf, sizet srcCount, pcIError_base & error ) __NOTHROW__ { + assert( false ); + mRawPtr->replace( pos, count, buf, srcCount, error ); + return this; + } + + pIUTF8String_base APICALL replace( sizet pos, sizet count, pcIUTF8String_base src, sizet srcPos, sizet srcCount, pcIError_base & error ) __NOTHROW__ { + assert( false ); + mRawPtr->replace( pos, count, src, srcPos, srcCount, error ); + return this; + } + + sizet APICALL copy( char * buf, sizet len, sizet pos ) const { + return CallConstSafeFunction< IUTF8String, sizet, sizet, char *, sizet, sizet >( + mRawPtr, &IUTF8String::copy, buf, len, pos ); + } + + sizet APICALL copy( char * buf, sizet len, sizet pos, pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->copy( buf, len, pos, error ); + } + + sizet APICALL find( const char * buf, sizet pos, sizet count ) const { + return CallConstSafeFunction< IUTF8String, sizet, sizet, const char *, sizet, sizet >( + mRawPtr, &IUTF8String::find, buf, pos, count ); + } + + sizet APICALL find( const spcIUTF8String & src, sizet pos, sizet count ) const { + return CallConstSafeFunction< IUTF8String, sizet, sizet, pcIUTF8String_base, sizet, sizet >( + mRawPtr, &IUTF8String::find, src ? src->GetActualIUTF8String() : NULL, pos, count ); + } + + sizet APICALL find( const char * buf, sizet pos, sizet count, pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->find( buf, pos, count, error ); + } + + sizet APICALL find( pcIUTF8String_base src, sizet pos, sizet count, pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->find( src, pos, count, error ); + } + + sizet APICALL rfind( const char * buf, sizet pos, sizet count ) const { + return CallConstSafeFunction< IUTF8String, sizet, sizet, const char *, sizet, sizet >( + mRawPtr, &IUTF8String::rfind, buf, pos, count ); + } + + sizet APICALL rfind( const spcIUTF8String & src, sizet pos, sizet count ) const { + return CallConstSafeFunction< IUTF8String, sizet, sizet, pcIUTF8String_base, sizet, sizet >( + mRawPtr, &IUTF8String::rfind, src ? src->GetActualIUTF8String() : NULL, pos, count ); + } + + sizet APICALL rfind( const char * buf, sizet pos, sizet count, pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->rfind( buf, pos, count, error ); + } + + sizet APICALL rfind( pcIUTF8String_base src, sizet pos, sizet count, pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->rfind( src, pos, count, error ); + } + + int32 APICALL compare( sizet pos, sizet len, const char * buf, sizet count ) const { + return CallConstSafeFunction< IUTF8String, int32, int32, sizet, sizet, const char *, sizet >( + mRawPtr, &IUTF8String::compare, pos, len, buf, count ); + } + + int32 APICALL compare( sizet pos, sizet len, const spcIUTF8String & str, sizet strPos, sizet strLen ) const { + return CallConstSafeFunction< IUTF8String, int32, int32, sizet, sizet, pcIUTF8String_base, sizet, sizet >( + mRawPtr, &IUTF8String::compare, pos, len, str ? str->GetActualIUTF8String() : NULL, strPos, strLen ); + } + + int32 APICALL compare( sizet pos, sizet len, const char * buf, sizet count, pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->compare( pos, len, buf, count, error ); + } + + int32 APICALL compare( sizet pos, sizet len, pcIUTF8String_base str, sizet strPos, sizet strLen, pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->compare( pos, len, str, strPos, strLen, error ); + } + + spIUTF8String APICALL substr( sizet pos, sizet count ) const { + return CallConstSafeFunctionReturningPointer< IUTF8String, pIUTF8String_base, IUTF8String, sizet, sizet >( + mRawPtr, &IUTF8String::substr, pos, count ); + } + + pIUTF8String_base APICALL substr( sizet pos, sizet count, pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->substr( pos, count, error ); + } + + bool APICALL empty() const { + return CallConstSafeFunction< IUTF8String, bool, uint32 >( + mRawPtr, &IUTF8String::empty ); + } + + uint32 APICALL empty( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->empty(); + } + + const char * APICALL c_str() const __NOTHROW__ { + return mRawPtr->c_str(); + } + + void APICALL clear() __NOTHROW__ { + mRawPtr->clear(); + } + + sizet APICALL size() const __NOTHROW__ { + return mRawPtr->size(); + } + + AdobeXMPCommon_Int::pISharedObject_I APICALL GetISharedObject_I() __NOTHROW__ { + return mRawPtr->GetISharedObject_I(); + } + + pvoid APICALL GetInterfacePointer( uint64 interfaceID, uint32 interfaceVersion ) { + return CallSafeFunction< IVersionable, pvoid, pvoid, uint64, uint32 >( + mRawPtr, &IVersionable::getInterfacePointer, interfaceID, interfaceVersion ); + } + + pvoid APICALL getInterfacePointer( uint64 interfaceID, uint32 interfaceVersion, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->getInterfacePointer( interfaceID, interfaceVersion, error ); + } + + }; + + spIUTF8String IUTF8String_v1::MakeShared( pIUTF8String_base ptr ) { + if ( !ptr ) return spIUTF8String(); + pIUTF8String p = IUTF8String::GetInterfaceVersion() > 1 ? ptr->GetInterfacePointer< IUTF8String >() : ptr; + return shared_ptr< IUTF8StringProxy >( new IUTF8StringProxy( p ) ); + } + + spIUTF8String IUTF8String_v1::CreateUTF8String( pIObjectFactory objFactory ) { + return CallSafeFunctionReturningPointer< IObjectFactory, pIUTF8String_base, IUTF8String, const char *, sizet >( + objFactory, &IObjectFactory::CreateUTF8String, NULL, ( sizet ) 0 ); + } + + spIUTF8String IUTF8String_v1::CreateUTF8String( pIObjectFactory objFactory, const char * buf, sizet count ) { + return CallSafeFunctionReturningPointer< IObjectFactory, pIUTF8String_base, IUTF8String, const char *, sizet >( + objFactory, &IObjectFactory::CreateUTF8String, buf, count ); + } + +} + +#endif // BUILDING_XMPCOMMON_LIB && !SOURCE_COMPILING_XMP_ALL diff --git a/xmp/XMPCore/Interfaces/IArrayNode.h b/xmp/XMPCore/Interfaces/IArrayNode.h new file mode 100644 index 0000000..dd18976 --- /dev/null +++ b/xmp/XMPCore/Interfaces/IArrayNode.h @@ -0,0 +1,303 @@ +#ifndef __IArrayNode_h__ +#define __IArrayNode_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + + +#include "XMPCore/Interfaces/ICompositeNode.h" + +namespace AdobeXMPCore { + + //! + //! \brief Version1 of the interface that represents an Array Node of XMP DOM. + //! \details Provides all the functions to get and set various properties of the array node. + //! \attention Support multi threading through locks but can be enabled/disabled by the client. By default + //! every object created does not support multi-threading. + //! \note The index of the array is 1-based. + //! + class XMP_PUBLIC IArrayNode_v1 + : public virtual ICompositeNode_v1 + { + public: + + //! + //! @brief Indicates different kinds of array forms possible in XMP i.e, unordered, ordered and alternative. + //! + typedef enum { + //! unknown array form, should be used as invalid value. + kAFNone = 0, + + //! Array contains entries which are unordered. + kAFUnordered = 1 << 0, + + //! Array contains entries which are ordered. + kAFOrdered = 1 << 1, + + //! Array contains entries which are ordered plus default value should be the top one. + kAFAlternative = 1 << 2, + + //! Maximum value this enum can hold, should be treated as invalid value + kAFAll = kAllBits + } eArrayForm; + + //! + //! @brief Get the type of array. + //! \return a value of type #eArrayForm indicating the type of array. + //! + virtual eArrayForm APICALL GetArrayForm() const = 0; + + //! + //! @brief Get the type of child nodes. + //! \return a value of type #eNodeType indicating the type of child nodes array can hold. + //! \note An empty array will return \#INode_v1::kNTAll indicating that right now it can hold any type of node. + //! + virtual eNodeType APICALL GetChildNodeType() const = 0; + + //! + //! @{ + //! @brief Get the node at the specified index. + //! \param[in] index an object of type \#sizet indicating the index of the node client who is interested in. + //! \return A shared pointer to const or non const \#INode object containing node. + //! \note In case no node exists at the given index an invalid shared pointer is returned. + //! \note The index of an array is 1-based. + //! + virtual spINode APICALL GetNodeAtIndex( sizet index ) = 0; + XMP_PRIVATE spcINode GetNodeAtIndex( sizet index ) const { + return const_cast< IArrayNode_v1 * >( this )->GetNodeAtIndex( index ); + } + //! @} + + //! + //! @{ + //! @brief Get the node at the specified index as simple node, if possible. + //! \param[in] index An object of type \#sizet indicating the index of the node client is interested in. + //! \return A shared pointer to const or non const \#ISimpleNode object containing node. + //! \note In case no node exists at the given index an invalid shared pointer is returned. + //! \note The index of an array is 1-based. + //! \attention Error is thrown in case + //! - a child exists at the given index but is not a simple node. + //! + XMP_PRIVATE spcISimpleNode GetSimpleNodeAtIndex( sizet index ) const { + auto node = GetNodeAtIndex( index ); + if ( node ) return node->ConvertToSimpleNode(); + return spcISimpleNode(); + } + + XMP_PRIVATE spISimpleNode GetSimpleNodeAtIndex( sizet index ) { + auto node = GetNodeAtIndex( index ); + if ( node ) return node->ConvertToSimpleNode(); + return spISimpleNode(); + } + //! @} + + //! + //! @{ + //! @brief Get the node at the specified index as structure node, if possible. + //! \param[in] index An object of type \#sizet indicating the index of the node client is interested in. + //! \return A shared pointer to const or non const \#IStructureNode object containing node. + //! \note In case no node exists at the given index an invalid shared pointer is returned. + //! \note The index of an array is 1-based. + //! \attention Error is thrown in case + //! - a child exists at the given index but is not a structure node. + //! + XMP_PRIVATE spcIStructureNode GetStructureNodeAtIndex( sizet index ) const { + auto node = GetNodeAtIndex( index ); + if ( node ) return node->ConvertToStructureNode(); + return spcIStructureNode(); + } + + XMP_PRIVATE spIStructureNode GetStructureNodeAtIndex( sizet index ) { + auto node = GetNodeAtIndex( index ); + if ( node ) return node->ConvertToStructureNode(); + return spIStructureNode(); + } + //! @} + + //! + //! @{ + //! @brief Get the node at the specified index as an array node, if possible. + //! \param[in] index an object of type \#sizet indicating the index of the node client is interested in. + //! \return a shared pointer to const or non const \#IArrayNode object containing node. + //! \note In case no node exists at the given index an invalid shared pointer is returned. + //! \note The index of an array is 1-based. + //! \attention Error is thrown in case + //! - a child exists at the given index but is not an array node. + //! + XMP_PRIVATE spcIArrayNode GetArrayNodeAtIndex( sizet index ) const { + auto node = GetNodeAtIndex( index ); + if ( node ) return node->ConvertToArrayNode(); + return spcIArrayNode(); + } + + XMP_PRIVATE spIArrayNode GetArrayNodeAtIndex( sizet index ) { + auto node = GetNodeAtIndex( index ); + if ( node ) return node->ConvertToArrayNode(); + return spIArrayNode(); + } + //! @} + + //! + //! @brief Inserts a given node at the specified index. + //! \param[in] node Shared pointer to an object of \#INode containing the node to be inserted at the specified index. + //! \param[in] index An object of type sizet indicating the index where the node should + //! be inserted. + //! \note The index of an array is 1-based. + //! \attention Error is thrown in following cases: + //! -# given node is invalid. + //! -# type of given node is not same as other child items of the array node. + //! -# given node is already a child of some other node. + //! -# given index is less than 1 or greater than current child count + 1. + //! + virtual void APICALL InsertNodeAtIndex( const spINode & node, sizet index ) = 0; + + //! + //! @brief Replaces an existing node with the given node at the specified index. + //! \param[in] node Shared pointer to an object of \#INode containing the node to be inserted at the specified index. + //! \param[in] index An object of type \#sizet indicating the index from where the node should be replaced. + //! \return A shared pointer to the node replaced with the new node. + //! \note The index of an array is 1-based. + //! \attention Error is thrown in following cases: + //! -# Given node is invalid. + //! -# Type of given node is not same as other child items of the array node. + //! -# Given node is already a child of some other node. + //! -# Given index is less than 1 or greater than current child count. + //! -# No node exists at the requested index. + //! + virtual spINode APICALL ReplaceNodeAtIndex( const spINode & node, sizet index ) = 0; + + //! + //! @brief Remove the node at the specified index. + //! \param[in] index An object of type \#sizet indicating the index from where the node should be removed. + //! \note The index of an array is 1-based. + //! \return A shared pointer to \#INode object containing node which is removed from the tree. + //! \note In case no node exists at the given index an invalid shared pointer is returned. + //! + virtual spINode APICALL RemoveNodeAtIndex( sizet index ) = 0; + + //! + //! \cond XMP_INTERNAL_DOCUMENTATION + + //! + //! @{ + //! @brief Returns the actual raw pointer from the shared pointer, which can be a shared pointer of a proxy class. + //! \return Either a const or non const pointer to IArrayNode interface. + //! + virtual pIArrayNode APICALL GetActualIArrayNode() __NOTHROW__ = 0; + XMP_PRIVATE pcIArrayNode GetActualIArrayNode() const __NOTHROW__ { + return const_cast< IArrayNode_v1 * >( this )->GetActualIArrayNode(); + } + //! + //! @} + + //! + //! @{ + //! @brief Return the pointer to internal interfaces. + //! \return either a const or non const pointer to IArrayNode_I interface. + //! + virtual AdobeXMPCore_Int::pIArrayNode_I APICALL GetIArrayNode_I() __NOTHROW__ = 0; + + XMP_PRIVATE AdobeXMPCore_Int::pcIArrayNode_I GetIArrayNode_I() const __NOTHROW__ { + return const_cast< IArrayNode_v1 * >( this )->GetIArrayNode_I(); + } + //! + //! @} + + //! + //! @{ + //! @brief Converts raw pointer to shared pointer. + //! @details The raw pointer is of version 1 interface + //! where as the returned shared pointer depends on the version client is interested in. + //! \return Shared pointer to const or non constant interface. + //! + XMP_PRIVATE static spIArrayNode MakeShared( pIArrayNode_base ptr ); + XMP_PRIVATE static spcIArrayNode MakeShared( pcIArrayNode_base ptr ) { + return MakeShared( const_cast< pIArrayNode_base >( ptr ) ); + } + //! + //! @} + + //! + //! return The unique ID assigned to the interface. + //! \return 64 bit unsigned integer representing the unique ID assigned to the interface. + //! + XMP_PRIVATE static uint64 GetInterfaceID() { return kIArrayNodeID; } + + //! + //! @brief Returns the version of the interface. + //! \return 32 bit unsigned integer representing the version of the interface. + //! + XMP_PRIVATE static uint32 GetInterfaceVersion() { return 1; } + //! \endcond + + // Factories to create the array node + + //! + //! @brief Creates an unordered array node which is not part of any metadata document. + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the array node. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated, set it to \#AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the array node. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to \#AdobeXMPCommon::npos. + //! \return A shared pointer to a \#IArrayNode object. + //! \attention Error is thrown in the following cases: + //! -# nameSpace is NULL or its contents are empty. + //! -# name is NULL or its contents are empty. + //! + XMP_PRIVATE static spIArrayNode CreateUnorderedArrayNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ); + + //! + //! @brief Creates an ordered array node which is not part of any metadata document. + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the array node. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the array node. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to \#AdobeXMPCommon::npos. + //! \return A shared pointer to a \#IArrayNode object. + //! \attention Error is thrown in the following cases: + //! -# nameSpace is NULL or its contents are empty. + //! -# name is NULL or its contents are empty. + //! + XMP_PRIVATE static spIArrayNode CreateOrderedArrayNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ); + + //! + //! @brief Creates an alternative array node which is not part of any metadata document. + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the array node. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the array node. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to \#AdobeXMPCommon::npos. + //! \return A shared pointer to a \#IArrayNode object. + //! \attention Error is thrown in the following cases: + //! -# nameSpace is NULL or its contents are empty. + //! -# name is NULL or its contents are empty. + //! + XMP_PRIVATE static spIArrayNode CreateAlternativeArrayNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ); + + protected: + //! + //! Destructor + //! + virtual ~IArrayNode_v1() __NOTHROW__ {} + + //! \cond XMP_INTERNAL_DOCUMENTATION + virtual uint32 APICALL getArrayForm( pcIError_base & error ) const __NOTHROW__ = 0; + virtual uint32 APICALL getChildNodeType( pcIError_base & error ) const __NOTHROW__ = 0; + virtual pINode_base APICALL getNodeAtIndex( sizet index, pcIError_base & error ) __NOTHROW__ = 0; + virtual void APICALL insertNodeAtIndex( pINode_base node, sizet index, pcIError_base & error ) __NOTHROW__ = 0; + virtual pINode_base APICALL replaceNodeAtIndex( pINode_base node, sizet index, pcIError_base & error ) __NOTHROW__ = 0; + virtual pINode_base APICALL removeNodeAtIndex( sizet index, pcIError_base & error ) __NOTHROW__ = 0; + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + //! \endcond + + }; +} + +#endif // __IArrayNode_h__ diff --git a/xmp/XMPCore/Interfaces/IClientDOMParser.h b/xmp/XMPCore/Interfaces/IClientDOMParser.h new file mode 100644 index 0000000..ff27e00 --- /dev/null +++ b/xmp/XMPCore/Interfaces/IClientDOMParser.h @@ -0,0 +1,88 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCore/XMPCoreFwdDeclarations.h" +#include "XMPCommon/Interfaces/IError.h" +#include "XMPCommon/Interfaces/BaseInterfaces/IConfigurable.h" +#include "XMPCore/XMPCoreErrorCodes.h" + +namespace AdobeXMPCore { + + //! + //! @brief Version 1 of the interface that supports parsing by the client supplied parser of the XMP Data Model. + //! @details Provides functions to parse the XMP Data Model. + //! Thread safety is controllable by the client. + //! + class XMP_PUBLIC IClientDOMParser_v1 + { + public: + + //! + //! @brief Parse the contents present in the buffer taking into account the configuration parameters. + //! \param[in] buffer Pointer to a constant char buffer containing serialized XMP Data Model. + //! \param[in] bufferLength Number of characters in buffer. In case name is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] configurationParameters An object of type \#AdobeXMPCommon::IConfigurable containing all the configuration parameters requested by client + //! to be taken care of while parsing. + //! \param[in] proc A function pointer to be used by the parse operation to report back any encountered errors/warnings. + //! \return A shared pointer to \#INode object containing all the parsed XMP Data Model. + //! + virtual spINode APICALL Parse( const char * buffer, sizet bufferLength, pcIConfigurable configurationParameters, ReportErrorAndContinueFunctor proc ) = 0; + + //! + //! @brief Indicates whether object supports case sensitive keys or not. + //! \return True in case object supports case sensitive keys, false otherwise. + //! \note Default implementation makes keys case insensitive. + //! + virtual bool APICALL AreKeysCaseSensitive() const { return false; } + + //! + //! @brief Initialize the default configuration parameters. + //! @details The object needs to fill the default configuration parameters supported by it. + //! \param[in] configurationParameters an empty object of type \#AdobeXMPCommon::IConfigurable. + //! \note default implementation does not fill anything in the configuration parameters. + //! + virtual void APICALL Initialize( pIConfigurable configurationParameters ) {}; + + //! + //! @brief Validate the data type and value for a parameter. + //! \param[in] key An unsigned 64 bit integer value indicating the key. + //! \param[in] dataType A value of type \#AdobeXMPCommon::IConfigurable::eDataType indicating the type of value the parameter holds. + //! \param[in] dataValue A value of \#AdobeXMPCommon::IConfigurable::CombinedDataValue indicating the value the parameter holds. + //! \return An error code in case there is something wrong with the combination, otherwise returns \ p0-\#AdobeXMPCommon::eCECNone. + //! \note Default implementation validates all the keys + dataTypes + dataValue combinations. + //! + virtual eConfigurableErrorCode APICALL Validate( const uint64 & key, IConfigurable::eDataType dataType, const IConfigurable::CombinedDataValue & dataValue ) { + return kCECNone; + } + + //! + //! @brief Called by the library when the object is no longer required by it and client can free up the resources or memory associated with the object. + //! + virtual void APICALL Release() const __NOTHROW__ = 0; + + protected: + //! + //! Destructor + //! + virtual ~IClientDOMParser_v1() {} + + //! \cond XMP_INTERNAL_DOCUMENTATION + virtual pINode_base APICALL parse( const char * buffer, sizet bufferLength, pcIConfigurable configurationParameters, ReportErrorAndContinueABISafeProc proc, pcIError_base & error, uint32 & unknownExceptionCaught ) __NOTHROW__; + virtual uint32 APICALL areKeysCaseSensitive( pcIError_base & error, uint32 & unknownExceptionCaught ) const __NOTHROW__; + virtual void APICALL initialize( pIConfigurable configurationParameters, pcIError_base & error, uint32 & unknownExceptionCaught ) __NOTHROW__; + virtual uint32 APICALL validate( const uint64 & key, uint32 dataType, const IConfigurable::CombinedDataValue & dataValue, pcIError_base & error, uint32 & unknownExceptionCaught ) __NOTHROW__; + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + //! \endcond + + }; +} diff --git a/xmp/XMPCore/Interfaces/IClientDOMSerializer.h b/xmp/XMPCore/Interfaces/IClientDOMSerializer.h new file mode 100644 index 0000000..06558cd --- /dev/null +++ b/xmp/XMPCore/Interfaces/IClientDOMSerializer.h @@ -0,0 +1,94 @@ +#ifndef IClientDOMSerializer_h__ +#define IClientDOMSerializer_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCore/XMPCoreFwdDeclarations.h" +#include "XMPCommon/Interfaces/IError.h" +#include "XMPCommon/Interfaces/BaseInterfaces/IConfigurable.h" +#include "XMPCommon/XMPCommonErrorCodes.h" + +namespace AdobeXMPCore { + + //! + //! @brief Version 1 of the interface that supports serializing by the client supplied serializer of the XMP Data Model. + //! @details Provides functions to serialize the XMP Data Model. + //! Thread safety is controllable by the client. + //! + class XMP_PUBLIC IClientDOMSerializer_v1 + { + public: + + //! + //! @brief Serialize the XMP Data Model taking into account the configuration parameters. + //! \param[in] node The node to be serialized. + //! \param[in] nameSpacePrefixMap An object of type \#INameSpacePrefixMap which contains preferred prefixes for namespaces. + //! \param[in] configurationParameters An object of type #AdobeXMPCommon::IConfigurable containing all the configuration parameters requested by client + //! to be taken care of while serializing. + //! \param[in] functor A function object to be used by the serializing operation to report back any encountered errors/warnings. + //! \param[out] string A shared pointer to an IUTF8String object which should be filled with the serialized form of XMP Data Model. + //! + virtual void APICALL Serialize( const spINode & node, const spcINameSpacePrefixMap & nameSpacePrefixMap, pcIConfigurable configurationParameters, + ReportErrorAndContinueFunctor functor, const spIUTF8String & string ) = 0; + + //! + //! @brief Indicates whether object supports case sensitive keys or not. + //! \return True in case object supports case sensitive keys, false otherwise. + //! \note Default implementation makes keys case insensitive. + //! + virtual bool APICALL AreKeysCaseSensitive() const { return false; } + + //! + //! @brief Initializes the default configuration parameters. + //! The object needs to fill the default configuration parameters supported by it. + //! \param[in] configurationParameters An empty object of type #AdobeXMPCommon::IConfigurable. + //! \note Default implementation does not fill anything in the configuration parameters. + //! + virtual void APICALL Initialize( pIConfigurable configurationParameters ) {}; + + //! + //! @brief Validate the data type and value for a parameter. + //! \param[in] key An unsigned 64 bit integer value indicating the key. + //! \param[in] dataType A value of type #AdobeXMPCommon::IConfigurable::eDataType indicating the type of value the parameter holds. + //! \param[in] dataValue A value of \#AdobeXMPCommon::IConfigurable::CombinedDataValue indicating the value the parameter holds. + //! \return An error code in case there is something wrong with the combination, otherwise returns \#AdobeXMPCommon::eCECNone. + //! \note Default implementation validates all the keys + dataTypes + dataValue combinations. + //! + virtual eConfigurableErrorCode APICALL Validate( const uint64 & key, IConfigurable::eDataType dataType, const IConfigurable::CombinedDataValue & dataValue ) { + return kCECNone; + } + + //! + //! @brief Called by the library when the object is no longer required by it and client can free up the resources or memory associated with the object. + //! + virtual void APICALL Release() const __NOTHROW__ = 0; + + protected: + //! + //! Destructor + //! + virtual ~IClientDOMSerializer_v1() {} + + //! \cond XMP_INTERNAL_DOCUMENTATION + virtual void APICALL serialize( pINode_base node, pcINameSpacePrefixMap_base nameSpacePrefixMap, pcIConfigurable configurationParameters, ReportErrorAndContinueABISafeProc proc, pIUTF8String_base string, pcIError_base & error, uint32 & unknownExceptionCaught ) __NOTHROW__; + virtual uint32 APICALL areKeysCaseSensitive( pcIError_base & error, uint32 & unknownExceptionCaught ) const __NOTHROW__; + virtual void APICALL initialize( pIConfigurable configurationParameters, pcIError_base & error, uint32 & unknownExceptionCaught ) __NOTHROW__; + virtual uint32 APICALL validate( const uint64 & key, uint32 dataType, const IConfigurable::CombinedDataValue & dataValue, pcIError_base & error, uint32 & unknownExceptionCaught ) __NOTHROW__; + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + //! \endcond + + }; +} + +#endif // IClientDOMSerializer_h__ diff --git a/xmp/XMPCore/Interfaces/ICompositeNode.h b/xmp/XMPCore/Interfaces/ICompositeNode.h new file mode 100644 index 0000000..4754ed5 --- /dev/null +++ b/xmp/XMPCore/Interfaces/ICompositeNode.h @@ -0,0 +1,331 @@ +#ifndef __ICompositeNode_h__ +#define __ICompositeNode_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCore/Interfaces/INode.h" +#include "XMPCommon/Interfaces/BaseInterfaces/IVersionable.h" + +namespace AdobeXMPCore { + + //! + //! \brief Version1 of the interface that serves as a base interface to all composite types of nodes + //! in the XMP DOM ( like Arrays and Structures ). + //! \details Provides all the functions to get various properties of the composite node. + //! \attention Support multi threading through locks but can be enabled/disabled by the client. By default + //! every object created does not support multi-threading. + //! + class XMP_PUBLIC ICompositeNode_v1 + : public virtual INode_v1 + { + public: + + //! + //! @brief Get the node type specified by the path relative to the composite node. + //! \param[in] path Shared pointer to a const \#AdobeXMPCore::IPath object containing the relative path + //! from the node to the node client is interested in. + //! \return The type of the node. + //! \note In case no node exists at the given path a value \#eNodeType::kNTNone is returned. + //! + virtual eNodeType APICALL GetNodeTypeAtPath( const spcIPath & path ) const = 0; + + //! + //! @{ + //! @brief Get the node specified by the path relative to the composite node. + //! \param[in] path Shared pointer to a const \#AdobeXMPCore::IPath object containing the relative path + //! from the node to the node client is interested in. + //! \return A shared pointer to either a const or non const \#AdobeXMPCore::INode object containing node. + //! \note In case no node exists at the given path an invalid shared pointer is returned. + //! + XMP_PRIVATE spcINode GetNodeAtPath( const spcIPath & path ) const { + return const_cast< ICompositeNode_v1 * >( this )->GetNodeAtPath( path ); + } + virtual spINode APICALL GetNodeAtPath( const spcIPath & path ) = 0; + //! @} + + //! + //! @brief Appends a given node as the child of the node. + //! @details In case of array node it is appended at the last + //! and in case of structure node qualified name of the node to be inserted determines its position. + //! \param[in] node Shared pointer to an object of \#AdobeXMPCore::INode containing the node to be + //! appended as the last child. + //! \note This operation is not currently implemented for the ICompositeNode interface. + //! \attention Error is thrown in following cases: + //! -# provided node is invalid. + //! -# type of given node is not same as other child items of the array node. + //! -# given node is already a child of some other node. + //! -# composite node already has a child node with the same qualified name in case of structure node. + //! + virtual void APICALL AppendNode( const spINode & node ) = 0; + + //! + //! @brief Inserts a given node at the path relative to the composite node. + //! \param[in] node Shared pointer to an object of \#AdobeXMPCore::INode containing the node to be + //! inserted at the specified relative path. + //! \param[in] path Shared pointer to a const \#AdobeXMPCore::IPath object containing the relative path. + //! \note All the hierarchy of nodes is created if not present. + //! \note This operation is not currently implemented for the ICompositeNode interface. + //! \attention Error is thrown in following cases: + //! -# given node is invalid. + //! -# type of given node is not same as other child items of the array node. + //! -# given node is already a child of some other node. + //! -# given path is invalid or logically incorrect. + //! -# type of given node is not suitable for the destination location. + //! -# a node already exists at the specified path. + //! + virtual void APICALL InsertNodeAtPath( const spINode & node, const spcIPath & path ) = 0; + + //! + //! @brief Replaces an existing node with the given node at the path relative to the composite node.. + //! \param[in] node Shared pointer to an object of \#AdobeXMPCore::INode. + //! \param[in] path Shared pointer to a const \#AdobeXMPCore::IPath object containing the relative path. + //! \return a Shared pointer to the node being replaced. + //! \note This operation is not currently implemented for the ICompositeNode interface. + //! \attention Error is thrown in following cases: + //! -# given node is invalid. + //! -# type of given node is not same as other child items of the array node. + //! -# given node is already a child of some other node. + //! -# given index is less than 1 or greater than current child count. + //! -# type of given node is not suitable for the destination location. + //! -# no node exists at the specified path. + //! + virtual spINode APICALL ReplaceNodeAtPath( const spINode & node, const spcIPath & path ) = 0; + + //! + //! @brief Removes the node specified by the path relative to the composite node. + //! \param[in] path Shared pointer to a const \#AdobeXMPCore::IPath object containing the relative path + //! from the node to the node client is interested in. + //! \return A shared pointer to \#AdobeXMPCore::INode object containing node which is removed from the tree. + //! \note In case no node exists at the given path an invalid shared pointer is returned. + //! + virtual spINode APICALL RemoveNodeAtPath( const spcIPath & path ) = 0; + + //! + //! @{ + //! @brief Get an iterator object to iterate over all the child nodes of the composite node. + //! \return a shared pointer to a const or non const \#INodeIterator object. + //! + virtual spINodeIterator APICALL Iterator() = 0; + XMP_PRIVATE spcINodeIterator Iterator() const { + return const_cast< ICompositeNode_v1 * >( this )->Iterator(); + } + // @} + + //! + //! @brief Get the count of child nodes of the composite node. + //! \return an object of type \#AdobeXMPCommon::sizet containing the count of children of the node. + //! + virtual sizet APICALL ChildCount() const __NOTHROW__ = 0; + + // Wrapper non virtual functions + + //! + //! @{ + //! @brief Get a simple node specified by the path relative to the node. + //! \param[in] path Shared pointer to a const \#AdobeXMPCore::IPath object containing the relative path + //! from the node to the node client is interested in. + //! \return A shared pointer to const or non const \#ISimpleNode object containing node. + //! \note In case no node exists at the given path an invalid shared pointer is returned. + //! \attention Error is thrown in case + //! - a node exists at the given path but is not a simple node. + //! + XMP_PRIVATE spcISimpleNode GetSimpleNodeAtPath( const spcIPath & path ) const { + auto node = GetNodeAtPath( path ); + if ( node ) return node->ConvertToSimpleNode(); + return spcISimpleNode(); + } + + XMP_PRIVATE spISimpleNode GetSimpleNodeAtPath( const spcIPath & path ) { + auto node = GetNodeAtPath( path ); + if ( node ) return node->ConvertToSimpleNode(); + return spISimpleNode(); + } + //! @} + + //! + //! @{ + //! @brief Get a structure node specified by the path relative to the node. + //! \param[in] path Shared pointer to a const \#AdobeXMPCore::IPath object containing the relative path + //! from the node to the node client is interested in. + //! \return A shared pointer to const or non const \#IStructureNode object containing node. + //! \note In case no node exists at the given path an invalid shared pointer is returned. + //! \attention Error is thrown in case + //! - a node exists at the given path but is not a structure node. + //! + XMP_PRIVATE spcIStructureNode GetStructureNodeAtPath( const spcIPath & path ) const { + auto node = GetNodeAtPath( path ); + if ( node ) return node->ConvertToStructureNode(); + return spcIStructureNode(); + } + + XMP_PRIVATE spIStructureNode GetStructureNodeAtPath( const spcIPath & path ) { + auto node = GetNodeAtPath( path ); + if ( node ) return node->ConvertToStructureNode(); + return spIStructureNode(); + } + // !@} + + //! + //! @{ + //! @brief Get an array node specified by the path relative to the node. + //! \param[in] path Shared pointer to a const \#AdobeXMPCore::IPath object containing the relative path + //! from the node to the node client is interested in. + //! \return A shared pointer to const or non const \#IArrayNode object containing node. + //! \note In case no node exists at the given path an invalid shared pointer is returned. + //! \attention Error is thrown in case + //! - a node exists at the given path but is not an array node. + //! + XMP_PRIVATE spcIArrayNode GetArrayNodeAtPath( const spcIPath & path ) const { + auto node = GetNodeAtPath( path ); + if ( node ) return node->ConvertToArrayNode(); + return spcIArrayNode(); + } + + XMP_PRIVATE spIArrayNode GetArrayNodeAtPath( const spcIPath & path ) { + auto node = GetNodeAtPath( path ); + if ( node ) return node->ConvertToArrayNode(); + return spIArrayNode(); + } + // !@} + + //! + //! \cond XMP_INTERNAL_DOCUMENTATION + + //! + //! @{ + //! @brief Returns the actual raw pointer from the shared pointer, which can be a shared pointer of a proxy class. + //! \return Either a const or non const pointer to ICompositeNode interface. + //! + virtual pICompositeNode APICALL GetActualICompositeNode() __NOTHROW__ = 0; + XMP_PRIVATE pcICompositeNode GetActualICompositeNode() const __NOTHROW__ { + return const_cast< ICompositeNode_v1 * >( this )->GetActualICompositeNode(); + } + //! + //! @} + + //! + //! @{ + //! @brief Returns the pointer to internal interfaces. + //! \return Either a const or non const pointer to ICompositeNode_I interface. + //! + virtual AdobeXMPCore_Int::pICompositeNode_I APICALL GetICompositeNode_I() __NOTHROW__ = 0; + + XMP_PRIVATE AdobeXMPCore_Int::pcICompositeNode_I GetICompositeNode_I() const __NOTHROW__ { + return const_cast< ICompositeNode_v1 * >( this )->GetICompositeNode_I(); + } + //! + //! @} + + //! + //! @{ + //! @brief Converts raw pointer to shared pointer. + //! @details The raw pointer is of version 1 interface + //! where as the returned shared pointer depends on the version client is interested in. + //! \return Shared pointer to const or non constant interface. + //! + XMP_PRIVATE static spICompositeNode MakeShared( pICompositeNode_base ptr ); + XMP_PRIVATE static spcICompositeNode MakeShared( pcICompositeNode_base ptr ) { + return const_cast< ICompositeNode_v1 * >( ptr )->MakeShared( ptr ); + } + //! + //! @} + + //! + //! @brief Returns the unique ID assigned to the interface. + //! \return 64 bit unsigned integer representing the unique ID assigned to the interface. + //! + XMP_PRIVATE static uint64 GetInterfaceID() { return kICompositeNodeID; } + + //! + //! @brief Returns the version of the interface. + //! \return 32 bit unsigned integer representing the version of the interface. + //! + XMP_PRIVATE static uint32 GetInterfaceVersion() { return 1; } + //! \endcond + + protected: + //! + //! Destructor + //! + virtual ~ICompositeNode_v1() __NOTHROW__ {} + + //! \cond XMP_INTERNAL_DOCUMENTATION + virtual uint32 APICALL getNodeTypeAtPath( pcIPath_base path, pcIError_base & error ) const __NOTHROW__ = 0; + virtual pINode_base APICALL getNodeAtPath( pcIPath_base path, pcIError_base & error ) __NOTHROW__ = 0; + virtual void APICALL appendNode( pINode_base node, pcIError_base & error ) __NOTHROW__ = 0; + virtual void APICALL insertNodeAtPath( pINode_base node, pcIPath_base path, pcIError_base & error ) __NOTHROW__ = 0; + virtual pINode_base APICALL replaceNodeAtPath( pINode_base node, pcIPath_base path, pcIError_base & error ) __NOTHROW__ = 0; + virtual pINode_base APICALL removeNodeAtPath( pcIPath_base path, pcIError_base & error ) __NOTHROW__ = 0; + virtual pINodeIterator_base APICALL iterator( pcIError_base & error ) __NOTHROW__ = 0; + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + //! \endcond + + }; +} + +//! \cond XMP_INTERNAL_DOCUMENTATION + +#if !BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB + +namespace AdobeXMPCore { + +#if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4250 ) +#endif + + class ICompositeNodeProxy + : public virtual ICompositeNode + , public virtual INodeProxy + { + private: + pICompositeNode mRawPtr; + + public: + ICompositeNodeProxy( pICompositeNode ptr ); + ~ICompositeNodeProxy() __NOTHROW__ ; + + pICompositeNode APICALL GetActualICompositeNode() __NOTHROW__; + AdobeXMPCore_Int::pICompositeNode_I APICALL GetICompositeNode_I() __NOTHROW__; + + virtual eNodeType APICALL GetNodeTypeAtPath( const spcIPath & path ) const; + virtual spINode APICALL GetNodeAtPath( const spcIPath & path ); + virtual void APICALL AppendNode( const spINode & node ); + virtual void APICALL InsertNodeAtPath( const spINode & node, const spcIPath & path ); + virtual spINode APICALL ReplaceNodeAtPath( const spINode & node, const spcIPath & path ); + virtual spINode APICALL RemoveNodeAtPath( const spcIPath & path ); + virtual spINodeIterator APICALL Iterator(); + virtual sizet APICALL ChildCount() const __NOTHROW__; + + protected: + virtual uint32 APICALL getNodeTypeAtPath( pcIPath_base path, pcIError_base & error ) const __NOTHROW__; + virtual pINode_base APICALL getNodeAtPath( pcIPath_base path, pcIError_base & error ) __NOTHROW__; + virtual void APICALL appendNode( pINode_base node, pcIError_base & error ) __NOTHROW__; + virtual void APICALL insertNodeAtPath( pINode_base node, pcIPath_base path, pcIError_base & error ) __NOTHROW__; + virtual pINode_base APICALL replaceNodeAtPath( pINode_base node, pcIPath_base path, pcIError_base & error ) __NOTHROW__; + virtual pINode_base APICALL removeNodeAtPath( pcIPath_base path, pcIError_base & error ) __NOTHROW__; + virtual pINodeIterator_base APICALL iterator( pcIError_base & error ) __NOTHROW__; + + }; + +#if XMP_WinBuild + #pragma warning( pop ) +#endif + +} + +#endif // BUILDING_XMPCORE_LIB +//! \endcond + +#endif // __ICompositeNode_h__ diff --git a/xmp/XMPCore/Interfaces/ICoreConfigurationManager.h b/xmp/XMPCore/Interfaces/ICoreConfigurationManager.h new file mode 100644 index 0000000..98b09a0 --- /dev/null +++ b/xmp/XMPCore/Interfaces/ICoreConfigurationManager.h @@ -0,0 +1,107 @@ +#ifndef ICoreConfigurationManager_h__ +#define ICoreConfigurationManager_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCore/XMPCoreFwdDeclarations.h" +#include "XMPCommon/Interfaces/IConfigurationManager.h" + +namespace AdobeXMPCore { + //! + //! @brief Version1 of the interface that represents configuration settings controllable by the client. + //! \details Provides functions through which client can plug in its own memory allocators, error notifiers. + //! \attention Not Thread Safe as this functionality is generally used at the initialization phase. + //! + class XMP_PUBLIC ICoreConfigurationManager_v1 + : public virtual IConfigurationManager_v1 + { + public: + + //! + //! \cond XMP_INTERNAL_DOCUMENTATION + + //! + //! @{ + //! @brief Returns the actual raw pointer from the shared pointer, which can be a shared pointer of a proxy class. + //! \return Either a const or non const pointer to ICoreConfigurationManager interface. + //! + virtual pICoreConfigurationManager APICALL GetActualICoreConfigurationManager() __NOTHROW__ = 0; + XMP_PRIVATE pcICoreConfigurationManager GetActualICoreConfigurationManager() const __NOTHROW__ { + return const_cast< ICoreConfigurationManager_v1 * >( this )->GetActualICoreConfigurationManager(); + } + //! + //! @} + + //! + //! @{ + //! @brief Returns the pointer to internal interfaces. + //! \return Either a const or non const pointer to ICoreConfigurationManager_I interface. + //! + virtual AdobeXMPCore_Int::pICoreConfigurationManager_I APICALL GetICoreConfigurationManager_I() __NOTHROW__ = 0; + + XMP_PRIVATE AdobeXMPCore_Int::pcICoreConfigurationManager_I GetICoreConfigurationManager_I() const __NOTHROW__ { + return const_cast< ICoreConfigurationManager_v1 * >( this )->GetICoreConfigurationManager_I(); + } + //! + //! @} + + //! + //! @{ + //! @brief Converts raw pointer to shared pointer. The raw pointer is of version 1 interface + //! where as the returned shared pointer depends on the version client is interested in. + //! \return Shared pointer to const or non constant interface. + //! + XMP_PRIVATE static spICoreConfigurationManager MakeShared( pICoreConfigurationManager_base ptr ); + XMP_PRIVATE static spcICoreConfigurationManager MakeShared( pcICoreConfigurationManager_base ptr ) { + return MakeShared( const_cast< pICoreConfigurationManager_base >( ptr ) ); + } + //! + //! @} + + //! + //! @brief Return the unique ID assigned to the interface. + //! \return 64 bit unsigned integer representing the unique ID assigned to the interface. + //! + XMP_PRIVATE static uint64 GetInterfaceID() { return kICoreConfigurationManagerID; } + + //! + //! @brief Returns the version of the interface. + //! \return 32 bit unsigned integer representing the version of the interface. + //! + XMP_PRIVATE static uint32 GetInterfaceVersion() { return 1; } + //! \endcond + + // static factory functions + + //! + //! @brief Get the configuration manager object associated with XMPCore library.. + //! \return A shared pointer to an object of \#ICoreConfigurationManager. + //! + XMP_PRIVATE static spICoreConfigurationManager GetCoreConfigurationManager(); + + + protected: + //! + //! Destructor + //! + virtual ~ICoreConfigurationManager_v1() __NOTHROW__ {} + + //! \cond XMP_INTERNAL_DOCUMENTATION + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + //! \endcond + + }; +} +#endif // ICoreConfigurationManager_h__ + diff --git a/xmp/XMPCore/Interfaces/ICoreObjectFactory.h b/xmp/XMPCore/Interfaces/ICoreObjectFactory.h new file mode 100644 index 0000000..234a685 --- /dev/null +++ b/xmp/XMPCore/Interfaces/ICoreObjectFactory.h @@ -0,0 +1,264 @@ +#ifndef ICoreObjectFactory_h__ +#define ICoreObjectFactory_h__ 1 + +// ================================================================================================= +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCore/XMPCoreFwdDeclarations.h" +#include "XMPCommon/Interfaces/IObjectFactory.h" + +namespace AdobeXMPCore { + + //! + //! @brief Version1 of a interface that represents a factory to create various artifacts of XMP DOM like array, + //! structure, path etc. + //! + //! @details Provides all the functions to create instances of various artifacts of XMP DOM and return them as shared pointers + //! to the clients. This is the interface through which clients of the library actually get access to all other interfaces. + //! + + class XMP_PUBLIC ICoreObjectFactory_v1 + : public virtual IObjectFactory_v1 + { + public: + + //! + //! @brief Creates an empty name space prefix map. + //! \param[out] error A reference to a pointer to const IError object which will be filled with the error object in case of any error. + //! \return A pointer to an empty \#INameSpacePrefixMap_v1 object. + //! + virtual pINameSpacePrefixMap_base APICALL CreateNameSpacePrefixMap( pcIError_base & error ) __NOTHROW__ = 0; + + //! + //! @brief Provides the default mapping of prefix string and nameSpace strings used by XMPCore. + //! \param[out] error A reference to a pointer to const IError object which will be filled with the error object in case of any error. + //! \return A pointer to const \#INameSpacePrefixMap_v1 object containing all the mappings used as default by the XMPCore. + //! + virtual pcINameSpacePrefixMap_base APICALL GetDefaultNameSpacePrefixMap( pcIError_base & error ) __NOTHROW__ = 0; + + //! + //! @brief Creates a normal property path segment.These are essentially all properties (simple, struct and arrays). + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the property. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the property. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[out] error A reference to a pointer to const IError object which will be filled with the error object in case of any error. + //! \return A pointer to const \#IPathSegment_v1. + //! + virtual pcIPathSegment_base APICALL CreatePropertyPathSegment( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, pcIError_base & error ) __NOTHROW__ = 0; + + //! + //! @brief Creates an array index path segment that denotes a specific element of an array. + //! @details Such segments do not have an own name and inherits the namespace from the Array property itself. + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the property. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] index An object of type \#AdobeXMP::sizet containting the index of the array element. + //! \param[out] error A reference to a pointer to const IError object which will be filled with the error object in case of any error. + //! \return A pointer to const \#IPathSegment_v1. + //! \attention Throws \#AdobeXMP::pcIError in case + //! - pointers to const char buffers are NULL, + //! - their content is empty. + //! + virtual pcIPathSegment_base APICALL CreateArrayIndexPathSegment( const char * nameSpace, sizet nameSpaceLength, sizet index, pcIError_base & error ) __NOTHROW__ = 0; + + //! + //! @brief Creates a Qualifier path segment, which behaves like a normal property + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the property. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the property. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[out] error A reference to a pointer to const IError object which will be filled with the error object in case of any error. + //! \return A pointer to const \#IPathSegment_v1. + //! + virtual pcIPathSegment_base APICALL CreateQualifierPathSegment( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, pcIError_base & error ) __NOTHROW__ = 0; + + //! + //! @brief Creates a path segment that selects a specific qualifier by its value. + //! For example a specific language in a alternative array of languages. + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the property. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the property. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] value Pointer to a constant char buffer containing value of the language (xml:lang) + //! \param[in] valueLength Number of characters in value. In case value is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[out] error A reference to a pointer to const IError object which will be filled with the error object in case of any error. + //! \return A pointer to const \#IPathSegment_v1. + //! + virtual pcIPathSegment_base APICALL CreateQualifierSelectorPathSegment( const char * nameSpace, sizet nameSpaceLength, const char * name, + sizet nameLength, const char * value, sizet valueLength, pcIError_base & error ) __NOTHROW__ = 0; + + //! + //! @brief Creates an empty IPath object. + //! \param[out] error A reference to a pointer to const IError object which will be filled with the error object in case of any error. + //! \return A pointer to an empty \#IPath_v1 object + //! + virtual pIPath_base APICALL CreatePath( pcIError_base & error ) __NOTHROW__ = 0; + + //! + //! @brief Creates a path from a char buffer which contains the serialized path. + //! \param[in] path Pointer to a const char buffer containing serialized form of the path. + //! \param[in] pathLength Number of characters in the path. In case path in null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] map A pointer to a const \#IXMPNameSpacePrefixMap_v1 object which will contain the mapping for nameSpaces to prefixes. + //! \param[out] error A reference to a pointer to const IError object which will be filled with the error object in case of any error. + //! \return A pointer to a \#IPath_v1 object. + //! + virtual pIPath_base APICALL ParsePath( const char * path, sizet pathLength, pcINameSpacePrefixMap_base map, pcIError_base & error ) __NOTHROW__ = 0; + + //! + //! @brief Creates a simple property node which is not part of any metadata document. + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the simple node. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the simple node. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] value Pointer to a constant char buffer containing value of the simple node. + //! \param[in] valueLength Number of characters in value. In case name is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[out] error A reference to a pointer to const IError object which will be filled with the error object in case of any error. + //! \return A pointer to a \#ISimpleNode_v1 object. + //! + virtual pISimpleNode_base APICALL CreateSimpleNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, + const char * value, sizet valueLength, pcIError_base & error ) __NOTHROW__ = 0; + + //! + //! @brief Creates an array node which is not part of any metadata document. + //! \param[in] arrayForm A value indicating the array type + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the array node. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the array node. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[out] error A reference to a pointer to const IError object which will be filled with the error object in case of any error. + //! \return A pointer to a \#IArrayNode_v1 object. + //! + virtual pIArrayNode_base APICALL CreateArrayNode( uint32 arrayForm, const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, pcIError_base & error ) __NOTHROW__ = 0; + + //! + //! @brief Creates a structure node which is not part of any metadata document. + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the structure node. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the structure node. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[out] error A reference to a pointer to const IError object which will be filled with the error object in case of any error. + //! \return A pointer to a \#IStructureNode_v1 object. + virtual pIStructureNode_base APICALL CreateStructureNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, pcIError_base & error ) __NOTHROW__ = 0; + + //! + //! @brief Creates an empty IMetadata object. + //! \param[out] error A reference to a pointer to const IError object which will be filled with the error object in case of any error. + //! \return a pointer to an empty \#IMetadata_v1 object. + //! + virtual pIMetadata_base APICALL CreateMetadata( pcIError_base & error ) __NOTHROW__ = 0; + + //! + //! @brief Provides the reference to the database of Serializers and Parsers available with the library. + //! \param[out] error A reference to a pointer to const IError object which will be filled with the error object in case of any error. + //! \return a pointer to \#IDOMImplementationRegistry_base object containing all the entries for serailizers and parsers. + //! + virtual pIDOMImplementationRegistry_base APICALL GetDOMImplementationRegistry( pcIError_base & error ) __NOTHROW__ = 0; + + //! + //! @brief Provides access to the configuration manager of the library. + //! \param[out] error A reference to a pointer to const IError object which will be filled with the error object in case of any error. + //! \return A pointer to \#ICoreConfigurationManager_base object. + //! + virtual pICoreConfigurationManager_base APICALL GetCoreConfigurationManager( pcIError_base & error ) __NOTHROW__ = 0; + + //! + //! \cond XMP_INTERNAL_DOCUMENTATION + + //! + //! @{ + //! @brief Returns the actual raw pointer from the shared pointer, which can be a shared pointer of a proxy class. + //! \return Either a const or non const pointer to ICoreObjectFactory interface. + //! + virtual pICoreObjectFactory APICALL GetActualICoreObjectFactory() __NOTHROW__ = 0; + XMP_PRIVATE pcICoreObjectFactory GetActualICoreObjectFactory() const __NOTHROW__ { + return const_cast< ICoreObjectFactory_v1 * >( this )->GetActualICoreObjectFactory(); + } + //! + //! @} + + //! + //! @{ + //! @brief Returns the pointer to internal interfaces. + //! \return Either a const or non const pointer to ICoreObjectFactory_I interface. + //! + virtual AdobeXMPCore_Int::pICoreObjectFactory_I APICALL GetICoreObjectFactory_I() __NOTHROW__ = 0; + + XMP_PRIVATE AdobeXMPCore_Int::pcICoreObjectFactory_I GetICoreObjectFactory_I() const __NOTHROW__ { + return const_cast< ICoreObjectFactory_v1 * >( this )->GetICoreObjectFactory_I(); + } + //! + //! @} + + //! + //! @{ + //! @brief Converts raw pointer to base version to pointer to client interested version. + //! @details The raw pointer is of version 1 interface where as the returned pointer depends on the version client is interested in. + //! \return Pointer to const or non constant interface. + //! + XMP_PRIVATE static pICoreObjectFactory MakeCoreObjectFactory( pICoreObjectFactory_base ptr ); + XMP_PRIVATE static pcICoreObjectFactory MakeCoreObjectFactory( pcICoreObjectFactory_base ptr ) { + return MakeCoreObjectFactory( const_cast< pcICoreObjectFactory_base >( ptr ) ); + } + //! + //! @} + + //! + //! @brief Returns the unique ID assigned to the interface. + //! \return 64 bit unsigned integer representing the unique ID assigned to the interface. + //! + XMP_PRIVATE static uint64 GetInterfaceID() { return kICoreObjectFactoryID; } + + //! + //! @brief returns the version of the interface. + //! \return 32 bit unsigned integer representing the version of the interface. + //! + XMP_PRIVATE static uint32 GetInterfaceVersion() { return 1; } + //! \endcond + + //! + //! @brief Gets an object of ICoreObjectFactory. + //! \return A pointer to an ICoreObjectFactory object. + //! + XMP_PRIVATE static pICoreObjectFactory GetCoreObjectFactory(); + + //! + //! @{ + //! @brief Sets up the core object factory. + //! \param[in] coreObjectFactory A pointer to an \#ICoreObjectFactory_v1 object. + //! \note coreObjectFactory is an optional parameter and only required for clients who don't directly link with the library + //! but want to use its functionality. + //! + #if LINKING_XMPCORE_LIB + XMP_PRIVATE static void SetupCoreObjectFactory(); + #else + XMP_PRIVATE static void SetupCoreObjectFactory( pICoreObjectFactory_base coreObjectFactory ); + #endif + //! @} + + //! + //! @brief Destroy everything related to core object factory. + //! + XMP_PRIVATE static void DestroyCoreObjectFactory(); + + protected: + //! + //! Destructor + //! + virtual ~ICoreObjectFactory_v1() __NOTHROW__ {} + + //! \cond XMP_INTERNAL_DOCUMENTATION + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + //! \endcond + + }; +} +#endif // ICoreObjectFactory_h__ diff --git a/xmp/XMPCore/Interfaces/IDOMImplementationRegistry.h b/xmp/XMPCore/Interfaces/IDOMImplementationRegistry.h new file mode 100644 index 0000000..7a732bd --- /dev/null +++ b/xmp/XMPCore/Interfaces/IDOMImplementationRegistry.h @@ -0,0 +1,150 @@ +#ifndef IDOMImplementationRegistry_h__ +#define IDOMImplementationRegistry_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCore/XMPCoreFwdDeclarations.h" +#include "XMPCommon/Interfaces/BaseInterfaces/ISharedObject.h" +#include "XMPCommon/Interfaces/BaseInterfaces/IVersionable.h" + +namespace AdobeXMPCore { + + //! + //! \brief Version1 of the interface that serves as a database/registry of all the parsers and + //! serializers available with the XMPCore library. + //! \details Provides all the functions to + //! -# get registered serializers and parsers from the database. + //! -# add client defined serializers and parsers to the database. + //! \attention Support multi threading if library is configured to support multi-threading by default. + //! \note By default following keys are registered by default with the database by the library: + //! -# rdf + //! + class XMP_PUBLIC IDOMImplementationRegistry_v1 + : public virtual ISharedObject + , public virtual IVersionable + { + public: + + //! + //! @brief Gets a parser corresponding to the key and returns to the client for usage. + //! \param[in] key Pointer to a const NULL terminated char buffer containing key of the parser in the database. + //! \return A shared pointer to a \#IDOMParser object. + //! \note In case the key is not present in the database an invalid shared pointer will be returned. + //! \note key is case sensitive. + //! + virtual spIDOMParser APICALL GetParser( const char * key ) const = 0; + + //! + //! @brief Gets a serializer corresponding to the key and returns to the client for usage. + //! \param[in] key Pointer to a const NULL terminated char buffer containing key of the serializer in the database. + //! \return A shared pointer to a \#IDOMSerializer object. + //! \note In case the key is not present in the database an invalid shared pointer will be returned. + //! \note key is case sensitive. + //! + virtual spIDOMSerializer APICALL GetSerializer( const char * key ) const = 0; + + //! + //! @brief Registers a parser with the database along with the key. + //! \param[in] key Pointer to a const NULL terminated char buffer containing key of the parser to be used while registering. + //! \param[in] parser A pointer to \#IClientDOMParser object to be registered with the database + //! \return True in case parser is successfully registered, false otherwise like in case key is already registered. + //! + virtual bool APICALL RegisterParser( const char * key, pIClientDOMParser_base parser ) = 0; + + //! + //! @brief Registers a serializer with the database along with the key. + //! \param[in] key Pointer to a const NULL terminated char buffer containing key of the serializer to be used while registering. + //! \param[in] serializer A pointer to \#IClientDOMSerializer object to be registered with the database. + //! \return True in case serializer is successfully registered, false otherwise like in case key is already registered. + //! + virtual bool APICALL RegisterSerializer( const char * key, pIClientDOMSerializer_base serializer ) = 0; + + //! + //! @brief Provides the reference to the database of Serializers and Parsers available with the library. + //! \return A shared pointer to \#IDOMImplementationRegistry object containing all the entries for serailizers and parsers. + //! + XMP_PRIVATE static spIDOMImplementationRegistry GetDOMImplementationRegistry(); + + //! + //! \cond XMP_INTERNAL_DOCUMENTATION + + //! + //! @{ + //! @brief Returns the actual raw pointer from the shared pointer, which can be a shared pointer of a proxy class. + //! \return Either a const or non const pointer to IDOMImplementationRegistry interface. + //! + virtual pIDOMImplementationRegistry APICALL GetActualIDOMImplementationRegistry() __NOTHROW__ = 0; + XMP_PRIVATE pcIDOMImplementationRegistry GetActualIDOMImplementationRegistry() const __NOTHROW__ { + return const_cast< IDOMImplementationRegistry_v1 * >( this )->GetActualIDOMImplementationRegistry(); + } + //! + //! @} + + //! + //! @{ + //! @brief Returns the pointer to internal interfaces. + //! \return Either a const or non const pointer to IDOMImplementationRegistry_I interface. + //! + virtual AdobeXMPCore_Int::pIDOMImplementationRegistry_I APICALL GetIDOMImplementationRegistry_I() __NOTHROW__ = 0; + + XMP_PRIVATE AdobeXMPCore_Int::pcIDOMImplementationRegistry_I GetIDOMImplementationRegistry_I() const __NOTHROW__ { + return const_cast< IDOMImplementationRegistry_v1 * >( this )->GetIDOMImplementationRegistry_I(); + } + //! + //! @} + + //! + //! @{ + //! @brief Converts raw pointer to shared pointer. The raw pointer is of version 1 interface + //! where as the returned shared pointer depends on the version client is interested in. + //! \return Shared pointer to const or non constant interface. + //! + XMP_PRIVATE static spIDOMImplementationRegistry MakeShared( pIDOMImplementationRegistry_base ptr ); + XMP_PRIVATE static spcIDOMImplementationRegistry MakeShared( pcIDOMImplementationRegistry_base ptr ) { + return MakeShared( const_cast< pIDOMImplementationRegistry_base >( ptr ) ); + } + //! + //! @} + + //! + //! @brief Returns the unique ID assigned to the interface. + //! \return 64 bit unsigned integer representing the unique ID assigned to the interface. + //! + XMP_PRIVATE static uint64 GetInterfaceID() { return kIDOMImplementationRegistryID; } + + //! + //! @brief Returns the version of the interface. + //! \return 32 bit unsigned integer representing the version of the interface. + //! + XMP_PRIVATE static uint32 GetInterfaceVersion() { return 1; } + //! \endcond + + protected: + //! + //! Destructor + //! + virtual ~IDOMImplementationRegistry_v1() __NOTHROW__ {} + + //! \cond XMP_INTERNAL_DOCUMENTATION + virtual pIDOMParser_base APICALL getParser( const char * key, pcIError_base & error ) const __NOTHROW__ = 0; + virtual pIDOMSerializer_base APICALL getSerializer( const char * key, pcIError_base & error ) const __NOTHROW__ = 0; + virtual uint32 APICALL registerParser( const char * key, pIClientDOMParser_base parser, pcIError_base & error ) __NOTHROW__ = 0; + virtual uint32 APICALL registerSerializer( const char * key, pIClientDOMSerializer_base serializer, pcIError_base & error ) __NOTHROW__= 0; + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + //! \endcond + + }; +} + +#endif // IDOMImplementationRegistry_h__ diff --git a/xmp/XMPCore/Interfaces/IDOMParser.h b/xmp/XMPCore/Interfaces/IDOMParser.h new file mode 100644 index 0000000..b33ff17 --- /dev/null +++ b/xmp/XMPCore/Interfaces/IDOMParser.h @@ -0,0 +1,181 @@ +#ifndef IDOMParser_h__ +#define IDOMParser_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCore/XMPCoreFwdDeclarations.h" +#include "XMPCommon/Interfaces/BaseInterfaces/ISharedObject.h" +#include "XMPCommon/Interfaces/BaseInterfaces/IVersionable.h" +#include "XMPCommon/Interfaces/BaseInterfaces/IConfigurable.h" + +namespace AdobeXMPCore { + + //! + //! @brief Version 1 of the interface that supports parsing of the XMP Data Model. + //! @details Provides all functions to parse the buffer as well as to configure the parser. + //! + class XMP_PUBLIC IDOMParser_v1 + : public virtual IConfigurable + , public virtual ISharedObject + , public virtual IVersionable + { + public: + + //! + //! @brief Indicates various types of operations possible while parsing with some node as the context. + //! + typedef enum { + //! @brief Append all the nodes parsed from the buffer as the child of the context node. + //! \attention Error is thrown in case + //! - Context Node is invalid. + //! - Context Node is not array or structure node. + //! - Context Node is a structure node but a child node with the same qualified name is already present. + //! - Context Node is an array node but the type of any parsed node is not same as that of other existing nodes in the array. + //! + kATAppendAsChildren = 0, + + //! @brief Replaces the children of the context node with nodes parsed from the buffer. + //! \attention Error is thrown in case + //! - Context Node is invalid. + //! - Context Node is not array or structure node. + //! - Context Node is a structure node but a child node with the same qualified name is not already present. + //! - Context Node is an array node but the type of all parsed nodes are not same. + //! + kATReplaceChildren = 1, + + //! @brief Either append all the nodes parsed from the buffer as the child/children of the context node. + //! \attention Error is thrown in case + //! - Context Node is invalid. + //! - Context Node is not array or structure node. + //! - Context Node is an array node but the type of all parsed nodes are not same. + //! - If a structure node is the parsed node, it is appended if it already not present, otherwise it is replaced. + //! - If an array node is the parsed node, it is appended if it already not present, otherwise it is removed. + kATAppendOrReplaceChildren = 2, + + //! @brief Treats all the parsed nodes as the siblings of the context node and place them before the context node, if possible. + //! \attention Error is thrown in case + //! - Context Node is invalid. + //! - parent of the Context Node is not an array node. + //! - The type of any parsed nodes is not same as that of other existing nodes in the array. + kATInsertBefore = 3, + + //! @brief Treats all the parsed nodes as the siblings of the context node and place them after the context node, if possible. + //! \attention Error is thrown in case + //! - Context Node is invalid. + //! - parent of the Context Node is not an array node. + //! - The type of any parsed nodes is not same as that of other existing nodes in the array. + kATInsertAfter = 4, + + //! @brief Replaces the context node and insert the node parsed from the buffer in its place. + //! \attention Error is thrown in case + //! - type of node returned after parsing in not of type which is compatible with the Context Node. + kATReplace = 5, + } eActionType; + + //! + //! @brief Parses the buffer contents and creates an XMP DOM node. + //! \param[in] buffer Pointer to a constant char buffer containing serialized XMP Data Model. + //! \param[in] bufferLength Number of characters in buffer. In case name is null terminated set it to \#AdobeXMPCommon::npos. + //! \return A shared pointer to an object of \#IMetadata containing all the information parsed from the buffer. + //! + virtual spIMetadata APICALL Parse( const char * buffer, sizet bufferLength ) = 0; + + //! + //! @brief Parse the buffer contents and populate the provided node . + //! \param[in] buffer Pointer to a constant char buffer containing serialized XMP Data Model. + //! \param[in] bufferLength Number of characters in buffer. In case name is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] actionType Value indicating how the parsed content and context node should be used. + //! \param[in,out] node The context node to be used base on the actionType. + //! + virtual void APICALL ParseWithSpecificAction( const char * buffer, sizet bufferLength, eActionType actionType, spINode & node ) = 0; + + //! + //! @brief Virtual copy constructor. + //! @details Creates an exact replica of the object. + //! \return A shared pointer to an object of \#IDOMParser which is the exact replica of the current serializer. + //! + virtual spIDOMParser APICALL Clone() const = 0; + + //! + //! \cond XMP_INTERNAL_DOCUMENTATION + + //! + //! @{ + //! @brief Returns the actual raw pointer from the shared pointer, which can be a shared pointer of a proxy class. + //! \return Either a const or non const pointer to IDOMParser interface. + //! + virtual pIDOMParser APICALL GetActualIDOMParser() __NOTHROW__ = 0; + XMP_PRIVATE pcIDOMParser GetActualIDOMParser() const __NOTHROW__ { + return const_cast< IDOMParser_v1 * >( this )->GetActualIDOMParser(); + } + //! + //! @} + + //! + //! @{ + //! @brief Returns the pointer to internal interfaces. + //! \return Either a const or non const pointer to IDOMParser_I interface. + //! + virtual AdobeXMPCore_Int::pIDOMParser_I APICALL GetIDOMParser_I() __NOTHROW__ = 0; + + XMP_PRIVATE AdobeXMPCore_Int::pcIDOMParser_I GetIDOMParser_I() const __NOTHROW__ { + return const_cast< IDOMParser_v1 * >( this )->GetIDOMParser_I(); + } + //! + //! @} + + //! + //! @{ + //! @brief Converts raw pointer to shared pointer. + //! @details The raw pointer is of version 1 interface where as the returned shared pointer depends on the version client is interested in. + //! + //! \return Shared pointer to const or non constant interface. + //! + XMP_PRIVATE static spIDOMParser MakeShared( pIDOMParser_base ptr ); + XMP_PRIVATE static spcIDOMParser MakeShared( pcIDOMParser_base ptr ) { + return MakeShared( const_cast< pIDOMParser_base >( ptr ) ); + } + //! + //! @} + + //! + //! return The unique ID assigned to the interface. + //! \return 64 bit unsigned integer representing the unique ID assigned to the interface. + //! + XMP_PRIVATE static uint64 GetInterfaceID() { return kIDOMParserID; } + + //! + //! return The version of the interface. + //! \return 32 bit unsigned integer representing the version of the interface. + //! + XMP_PRIVATE static uint32 GetInterfaceVersion() { return 1; } + //! \endcond + + protected: + //! + //! Destructor + //! + virtual ~IDOMParser_v1() __NOTHROW__ {} + + //! \cond XMP_INTERNAL_DOCUMENTATION + virtual pIMetadata_base APICALL parse( const char * buffer, sizet bufferLength, pcIError_base & error ) __NOTHROW__ = 0; + virtual void APICALL parseWithSpecificAction( const char * buffer, sizet bufferLength, uint32 actionType, pINode_base node, pcIError_base & error ) __NOTHROW__ = 0; + virtual pIDOMParser_base APICALL clone( pcIError_base & error ) const __NOTHROW__ = 0; + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + //! \endcond + + }; +} + +#endif // IDOMParser_h__ diff --git a/xmp/XMPCore/Interfaces/IDOMSerializer.h b/xmp/XMPCore/Interfaces/IDOMSerializer.h new file mode 100644 index 0000000..fe4d9fc --- /dev/null +++ b/xmp/XMPCore/Interfaces/IDOMSerializer.h @@ -0,0 +1,120 @@ +#ifndef IDOMSerializer_h__ +#define IDOMSerializer_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCore/XMPCoreFwdDeclarations.h" +#include "XMPCommon/Interfaces/BaseInterfaces/ISharedObject.h" +#include "XMPCommon/Interfaces/BaseInterfaces/IVersionable.h" +#include "XMPCommon/Interfaces/BaseInterfaces/IConfigurable.h" + +namespace AdobeXMPCore { + //! + //! @brief Version1 of the interface that represents an object that can serialize an XMP Data Model to a buffer. + //! Provides the functions to serialize the XMP Data Model. + //! + class XMP_PUBLIC IDOMSerializer_v1 + : public virtual IConfigurable + , public virtual ISharedObject + , public virtual IVersionable + { + public: + + //! + //! @brief Serializes the given XMP Node into an IUTF8String object. + //! \param[in] node An object of type \#INode which needs to be serialized. + //! \param[in] nameSpacePrefixMap An object of type \#INameSpacePrefixMap which contains preferred prefixes for namespaces. + //! \return An object of \#AdobeXMPCommon::IUTF8String type containing the serialized form of the node. + //! + virtual spIUTF8String APICALL Serialize( const spINode & node, const spcINameSpacePrefixMap & nameSpacePrefixMap = spcINameSpacePrefixMap() ) = 0; + + //! + //! @brief Virtual copy constructor. + //! Creates an exact replica of the object. + //! \return A shared pointer to an object of \#IDOMSerializer which is the exact replica of the current serializer. + //! + virtual spIDOMSerializer APICALL Clone() const = 0; + + //! + //! \cond XMP_INTERNAL_DOCUMENTATION + + //! + //! @{ + //! @brief Returns the actual raw pointer from the shared pointer, which can be a shared pointer of a proxy class. + //! \return Either a const or non const pointer to IDOMSerializer interface. + //! + virtual pIDOMSerializer APICALL GetActualIDOMSerializer() __NOTHROW__ = 0; + XMP_PRIVATE pcIDOMSerializer GetActualIDOMSerializer() const __NOTHROW__ { + return const_cast< IDOMSerializer_v1 * >( this )->GetActualIDOMSerializer(); + } + //! + //! @} + + //! + //! @{ + //! @brief Returns the pointer to internal interfaces. + //! \return Either a const or non const pointer to IDOMSerializer_I interface. + //! + virtual AdobeXMPCore_Int::pIDOMSerializer_I APICALL GetIDOMSerializer_I() __NOTHROW__ = 0; + + XMP_PRIVATE AdobeXMPCore_Int::pcIDOMSerializer_I GetIDOMSerializer_I() const __NOTHROW__ { + return const_cast< IDOMSerializer_v1 * >( this )->GetIDOMSerializer_I(); + } + //! + //! @} + + //! + //! @{ + //! @brief Converts raw pointer to shared pointer. The raw pointer is of version 1 interface + //! where as the returned shared pointer depends on the version client is interested in. + //! \return Shared pointer to const or non constant interface. + //! + XMP_PRIVATE static spIDOMSerializer MakeShared( pIDOMSerializer_base ptr ); + XMP_PRIVATE static spcIDOMSerializer MakeShared( pcIDOMSerializer_base ptr ) { + return MakeShared( const_cast< pIDOMSerializer_base >( ptr ) ); + } + + //! + //! @} + + //! + //! @brief Returns the unique ID assigned to the interface. + //! \return 64 bit unsigned integer representing the unique ID assigned to the interface. + //! + XMP_PRIVATE static uint64 GetInterfaceID() { return kIDOMSerializerID; } + + //! + //! @brief Returns the version of the interface. + //! \return 32 bit unsigned integer representing the version of the interface. + //! + XMP_PRIVATE static uint32 GetInterfaceVersion() { return 1; } + //! \endcond + + protected: + //! + //! Destructor + //! + virtual ~IDOMSerializer_v1() __NOTHROW__ {} + + //! \cond XMP_INTERNAL_DOCUMENTATION + virtual pIUTF8String_base APICALL serialize( pINode_base node, pcINameSpacePrefixMap_base map, pcIError_base & error ) __NOTHROW__ = 0; + virtual pIDOMSerializer_base APICALL clone( pcIError_base & error ) const __NOTHROW__ = 0; + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + //! \endcond + + }; +} + +#endif // IDOMSerializer_h__ + diff --git a/xmp/XMPCore/Interfaces/IMetadata.h b/xmp/XMPCore/Interfaces/IMetadata.h new file mode 100644 index 0000000..74015a6 --- /dev/null +++ b/xmp/XMPCore/Interfaces/IMetadata.h @@ -0,0 +1,145 @@ +#ifndef __IMetadata_h__ +#define __IMetadata_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCore/Interfaces/IStructureNode.h" + +namespace AdobeXMPCore { + + //! + //! \brief Version1 of the interface that represents the whole xmp metadata for an asset. + //! @details Provides all the functions to add or remove nodes to and from metadata. + //! \attention Support multi threading through locks but can be enabled/disabled by the client. By default + //! every object created does not support multi-threading. + //! + + class XMP_PUBLIC IMetadata_v1 + : public virtual IStructureNode_v1 + { + public: + //! + + //! @brief Gets the about URI string for the XMP metadata. + //! \return A shared pointer to const \#AdobeXMPCommon::IUTF8String object containing URI string. + //! \note By default this is an empty string. + //! + virtual spcIUTF8String APICALL GetAboutURI() const = 0; + + //! + //! @brief Sets the about URI string for the XMP metadata. + //! \param[in] uri Pointer to a constant char buffer containing uri string. + //! \param[in] uriLength Number of characters in uri. In case name is null terminated set it to \#AdobeXMPCommon::npos. + //! + virtual void APICALL SetAboutURI( const char * uri, sizet uriLength ) __NOTHROW__ = 0; + + //! + //! @brief Enables support for a particular feature. + //! \param[in] key A const char buffer containing key for the feature. + //! \param[in] keyLength Number of characters in key. + //! \note Following keys are supported: + //! - alias Enable support for aliases on the metadata object. + //! + virtual void APICALL EnableFeature( const char * key, sizet keyLength ) const __NOTHROW__ = 0; + + //! + //! @brief Disables support for a particular feature. + //! \param[in] key A const char buffer containing key for the feature. + //! \param[in] keyLength Number of characters in key. + //! + virtual void APICALL DisableFeature( const char * key, sizet keyLength ) const __NOTHROW__ = 0; + + //! \cond XMP_INTERNAL_DOCUMENTATION + + //! + //! @{ + //! @brief Returns the actual raw pointer from the shared pointer, which can be a shared pointer of a proxy class. + //! \return Either a const or non const pointer to IMetadata interface. + //! + virtual pIMetadata APICALL GetActualIMetadata() __NOTHROW__ = 0; + XMP_PRIVATE pcIMetadata GetActualIMetadata() const __NOTHROW__ { + return const_cast< IMetadata_v1 * >( this )->GetActualIMetadata(); + } + //! + //! @} + + //! + //! @{ + //! @brief Returns the pointer to internal interfaces. + //! \return Either a const or non const pointer to IMetadata_I interface. + //! + virtual AdobeXMPCore_Int::pIMetadata_I APICALL GetIMetadata_I() __NOTHROW__ = 0; + + XMP_PRIVATE AdobeXMPCore_Int::pcIMetadata_I GetIMetadata_I() const __NOTHROW__ { + return const_cast< IMetadata_v1 * >( this )->GetIMetadata_I(); + } + //! + //! @} + + //! + //! @{ + //! @brief Converts raw pointer to shared pointer. The raw pointer is of version 1 interface + //! where as the returned shared pointer depends on the version client is interested in. + //! \return Shared pointer to const or non constant interface. + //! + XMP_PRIVATE static spIMetadata MakeShared( pIMetadata_base ptr ); + XMP_PRIVATE static spcIMetadata MakeShared( pcIMetadata_base ptr ) { + return MakeShared( const_cast< pIMetadata_base >( ptr ) ); + } + //! + //! @} + + //! + //! @brief Returns the unique ID assigned to the interface. + //! \return 64 bit unsigned integer representing the unique ID assigned to the interface. + //! + XMP_PRIVATE static uint64 GetInterfaceID() { return kIMetadataID; } + + //! + //! @brief Returns the version of the interface. + //! \return 32 bit unsigned integer representing the version of the interface. + //! + XMP_PRIVATE static uint32 GetInterfaceVersion() { return 1; } + //! \endcond + + // static factory functions + + //! + //! @brief Creates an empty IMetadata object. + //! \return A shared pointer to an empty \#IMetadata object. + //! + XMP_PRIVATE static spIMetadata CreateMetadata(); + + protected: + //! + //! Destructor + //! + virtual ~IMetadata_v1() __NOTHROW__ {} + + //! Hiding some functions from derived classes + using INode_v1::GetParent; + using INode_v1::GetNameSpace; + using INode_v1::SetNameSpace; + using INode_v1::GetName; + using INode_v1::SetName; + + //! \cond XMP_INTERNAL_DOCUMENTATION + virtual pcIUTF8String_base APICALL getAboutURI( pcIError_base & error ) const __NOTHROW__ = 0; + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + //! \endcond + + }; +} + +#endif // __IMetadata_h__ diff --git a/xmp/XMPCore/Interfaces/INameSpacePrefixMap.h b/xmp/XMPCore/Interfaces/INameSpacePrefixMap.h new file mode 100644 index 0000000..9aa606a --- /dev/null +++ b/xmp/XMPCore/Interfaces/INameSpacePrefixMap.h @@ -0,0 +1,236 @@ +#ifndef INameSpacePrefixMap_h__ +#define INameSpacePrefixMap_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCore/XMPCoreFwdDeclarations.h" +#include "XMPCommon/Interfaces/BaseInterfaces/ISharedObject.h" +#include "XMPCommon/Interfaces/BaseInterfaces/IVersionable.h" +#include "XMPCommon/Interfaces/BaseInterfaces/IThreadSafe.h" + +namespace AdobeXMPCore { + + //! + //! \brief Version1 of the interface that represents map where each entry consists of prefix string + //! as the key and corresponding nameSpace string as its value. + //! \details Provides all the functions to get/set the entries inside the map. + //! \attention Supports Multi-threading at object level through locks. + //! + class XMP_PUBLIC INameSpacePrefixMap_v1 + : public virtual ISharedObject + , public virtual IVersionable + , public virtual IThreadSafe + { + public: + + //! + //! @brief Adds a new pair of prefix string and its corresponding nameSpace string or replace an existing entry. + //! \param[in] prefix Pointer to a constant char buffer containing prefix string. + //! \param[in] prefixLength Number of characters in prefix. In case prefix is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] nameSpace Pointer to a constant char buffer containing nameSpace string. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to \#AdobeXMPCommon::npos. + //! \return A bool type object containing true in case operation was successful, false otherwise. + //! \note Raises warning in case of + //! - prefix or nameSpace are null pointers + //! - prefixLength or nameSpaceLength is 0. + //! + virtual bool APICALL Insert( const char * prefix, sizet prefixLength, const char * nameSpace, sizet nameSpaceLength ) = 0; + + //! + //! @brief Finds the prefix string in the map and removes an entry corresponding to it in the map. + //! \param[in] prefix Pointer to a const char buffer containing prefix string. + //! \param[in] prefixLength Number of characters in prefix. In case prefix is null terminated set it to \#AdobeXMPCommon::npos. + //! \return A bool type object containing true in case entry was found and then deleted from the map, false otherwise. + //! \note Raises warning in case of + //! - prefix is null pointer, or + //! - prefixLength is 0. + //! \attention Throws AdobeXMPCommon::pcIError in case of failure in removing or searching process. + //! + virtual bool APICALL RemovePrefix( const char * prefix, sizet prefixLength ) = 0; + + //! + //! @brief Finds the nameSpace string in the map and removes an entry corresponding to it in the map. + //! \param[in] nameSpace Pointer to a constant char buffer containing nameSpace string. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to \#AdobeXMPCommon::npos. + //! \return A bool type object containing true in case entry was found and then deleted from the map, false otherwise. + //! \note Raises warning in case of + //! - nameSpace is null pointer, or + //! - nameSpaceLength is 0. + //! \attention Throws AdobeXMPCommon::pcIError in case of failure in removing or searching process. + //! + virtual bool APICALL RemoveNameSpace( const char * nameSpace, sizet nameSpaceLength ) = 0; + + //! + //! @brief Checks for the existence of a particular prefix in the map. + //! \param[in] prefix Pointer to a const char buffer containing prefix string. + //! \param[in] prefixLength Number of characters in prefix. In case prefix is null terminated set it to \#AdobeXMPCommon::npos. + //! \return A bool type object containing true in case there is an entry present corresponding to the prefix string, otherwise false. + //! \note Raises warning in case of + //! - prefix is null pointer, or + //! - prefixLength is 0. + //! + virtual bool APICALL IsPrefixPresent( const char * prefix, sizet prefixLength ) const = 0; + + //! + //! @brief Checks for the existence of a particular nameSpace in the map. + //! \param[in] nameSpace Pointer to a constant char buffer containing nameSpace string. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to \#AdobeXMPCommon::npos. + //! \return A bool type object containing true in case there is an entry present corresponding to the nameSpace string, otherwise false. + //! \note Raises warning in case of + //! - nameSpace is null pointer, or + //! - nameSpaceLength is 0. + //! + virtual bool APICALL IsNameSpacePresent( const char * nameSpace, sizet nameSpaceLength ) const = 0; + + //! + //! @brief Gets the nameSpace string corresponding to the prefix string. + //! \param[in] prefix Pointer to a const char buffer containing prefix string. + //! \param[in] prefixLength Number of characters in prefix. In case prefix is null terminated set it to \#AdobeXMPCommon::npos. + //! \return A shared pointer to const \#AdobeXMPCommon::IUTF8String object containing nameSpace string corresponding to + //! prefix string if a mapping exists, otherwise invalid shared pointer is returned. + //! \note Raises warning in case of + //! - prefix is null pointer, or + //! - prefixLength is 0. + //! + virtual spcIUTF8String APICALL GetNameSpace( const char * prefix, sizet prefixLength ) const = 0; + + //! + //! @brief Get the prefix string corresponding to the nameSpace string. + //! \param[in] nameSpace Pointer to a constant char buffer containing nameSpace string. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to \#AdobeXMPCommon::npos. + //! \return A shared pointer to const \#AdobeXMPCommon::IUTF8String object containing prefix string corresponding to + //! nameSpace string if a mapping exists, otherwise invalid shared pointer is returned. + //! \note Raises warning in case of + //! - nameSpace is null pointer, or + //! - nameSpaceLength is 0. + //! + virtual spcIUTF8String APICALL GetPrefix( const char * nameSpace, sizet nameSpaceLength ) const = 0; + + //! + //! @brief To get the total number of entries in the map. + //! \return An object of type \#AdobeXMPCommon::sizet containing the count of entries in the map. + //! + virtual sizet APICALL Size() const __NOTHROW__ = 0; + + //! + //! @brief To check whether the map is empty or not. + //! \return True in case map is empty; false otherwise. + //! + bool IsEmpty() const __NOTHROW__; + + //! + //! @brief Clear all the entries in the map. + //! + virtual void APICALL Clear() __NOTHROW__ = 0; + + //! + //! \brief Virtual Copy Constructor. + //! \details Makes an another object which is exact replica of the existing object. + // \return A shared pointer to INameSpacePrefixMap which is exact replica of the current object. + //! + virtual spINameSpacePrefixMap APICALL Clone() const = 0; + + //! + //! \cond XMP_INTERNAL_DOCUMENTATION + + //! + //! @{ + //! @brief Returns the actual raw pointer from the shared pointer, which can be a shared pointer of a proxy class. + //! \return Either a const or non const pointer to INameSpacePrefixMap interface. + //! + virtual pINameSpacePrefixMap APICALL GetActualINameSpacePrefixMap() __NOTHROW__ = 0; + XMP_PRIVATE pcINameSpacePrefixMap GetActualINameSpacePrefixMap() const __NOTHROW__ { + return const_cast< INameSpacePrefixMap_v1 * >( this )->GetActualINameSpacePrefixMap(); + } + //! + //! @} + + //! + //! @{ + //! @brief Returns the pointer to internal interfaces. + //! \return Either a const or non const pointer to INameSpacePrefixMap_I interface. + //! + virtual AdobeXMPCore_Int::pINameSpacePrefixMap_I APICALL GetINameSpacePrefixMap_I() __NOTHROW__ = 0; + + XMP_PRIVATE AdobeXMPCore_Int::pcINameSpacePrefixMap_I GetINameSpacePrefixMap_I() const __NOTHROW__ { + return const_cast< INameSpacePrefixMap_v1 * >( this )->GetINameSpacePrefixMap_I(); + } + //! + //! @} + + //! + //! @{ + //! @brief Converts raw pointer to shared pointer. The raw pointer is of version 1 interface + //! where as the returned shared pointer depends on the version client is interested in. + //! \return shared pointer to const or non constant interface. + //! + XMP_PRIVATE static spINameSpacePrefixMap MakeShared( pINameSpacePrefixMap_base ptr ); + XMP_PRIVATE static spcINameSpacePrefixMap MakeShared( pcINameSpacePrefixMap_base ptr ) { + return MakeShared( const_cast< pINameSpacePrefixMap_base >( ptr ) ); + } + //! + //! @} + + //! + //! @brief Returns the unique ID assigned to the interface. + //! \return 64 bit unsigned integer representing the unique ID assigned to the interface. + //! + XMP_PRIVATE static uint64 GetInterfaceID() { return kINameSpacePrefixMapID; } + + //! + //! @brief returns the version of the interface. + //! \return 32 bit unsigned integer representing the version of the interface. + //! + XMP_PRIVATE static uint32 GetInterfaceVersion() { return 1; } + + //! \endcond + + + // static factory functions + + //! + //! @brief Provides the default mapping of prefix string and nameSpace strings used by XMPCore. + //! \return A shared pointer to const INameSpacePrefixMap object containing all the mappings used + //! as default by the XMPCore. + //! + XMP_PRIVATE static spcINameSpacePrefixMap GetDefaultNameSpacePrefixMap(); + + //! + //! @brief Creates an empty namespace - prefix map and returns it to the client as a shared pointer. + //! \return A shared pointer to an empty INameSpacePrefixMap object. + //! + XMP_PRIVATE static spINameSpacePrefixMap CreateNameSpacePrefixMap(); + + protected: + //! + //! Destructor + //! + virtual ~INameSpacePrefixMap_v1() __NOTHROW__ {} + + //! \cond XMP_INTERNAL_DOCUMENTATION + const uint32 kPrefixIsParameter = 0; + const uint32 kNameSpaceIsParameter = 1; + + virtual uint32 APICALL insert( const char * prefix, sizet prefixLength, const char * nameSpace, sizet nameSpaceLength, pcIError_base & error ) __NOTHROW__ = 0; + virtual uint32 APICALL remove( uint32 keyType, const char * key, sizet keyLength, pcIError_base & error ) __NOTHROW__ = 0; + virtual uint32 APICALL isPresent( uint32 keyType, const char * key, sizet keyLength, pcIError_base & error ) const __NOTHROW__ = 0; + virtual pcIUTF8String_base APICALL get( uint32 keyType, const char * key, sizet keyLength, pcIError_base & error ) const __NOTHROW__ = 0; + virtual pINameSpacePrefixMap_base APICALL clone( pcIError_base & error ) const __NOTHROW__ = 0; + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + //! \endcond + + }; +} + +#endif // INameSpacePrefixMap_h__ diff --git a/xmp/XMPCore/Interfaces/INode.h b/xmp/XMPCore/Interfaces/INode.h new file mode 100644 index 0000000..59b64bb --- /dev/null +++ b/xmp/XMPCore/Interfaces/INode.h @@ -0,0 +1,582 @@ +#ifndef __INode_h__ +#define __INode_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCore/XMPCoreFwdDeclarations.h" +#include "XMPCommon/Interfaces/BaseInterfaces/ISharedObject.h" +#include "XMPCommon/Interfaces/BaseInterfaces/IVersionable.h" +#include "XMPCommon/Interfaces/BaseInterfaces/IThreadSafe.h" + +namespace AdobeXMPCore { + + //! + //! \brief Version1 of the interface that serves as a base interface to all types of nodes in the XMP DOM. + //! \details Provides all the functions to get various properties of the node. + //! \attention Support multi threading through locks but can be enabled/disabled by the client. By default + //! every object created does not support multi-threading. + //! + class XMP_PUBLIC INode_v1 + : public virtual ISharedObject + , public virtual IVersionable + , public virtual IThreadSafe + { + public: + + //! + //! @brief Indicates various types of node available in XMP Data Model like simple, array and structure. + //! + typedef enum { + //! Indicates none, to be used as invalid type. + kNTNone = 0, + + //! XMP Node is of Simple Node type (key value pair). + kNTSimple = 1, + + //! XMP Node is of Array type. Indexing start from 1. + kNTArray = 1 << 1, + + //! XMP Node is of structure type. + kNTStructure = 1 << 2, + + //! XMP Node of any type + kNTAll = kAllBits + } eNodeType; + + //! + //! @brief Gets the node type of the node. + //! \return An object of type \#eNodeType indicating the type of the node. + //! + virtual eNodeType APICALL GetNodeType() const = 0; + + //! + //! @brief Gets the node type of the node's parent. + //! \return An object of type \#eNodeType indicating the type of the node. + //! \note \#eNodeType::kNTNone is returned in case node has no parent. + //! + virtual eNodeType APICALL GetParentNodeType() const = 0; + + //! + //! @{ + //! @brief Gets the parent node of the node. + //! \return Either a const or non const pointer to INode interface. + //! \return A shared pointer to either a const or non const \#AdobeXMPCore::INode representing the parent of the node. + //! \note Returns an invalid shared pointer in case the node is a root node or it is not part of tree. + //! + XMP_PRIVATE spcINode GetParent() const { + return const_cast< INode_v1 * >( this )->GetParent(); + } + virtual spINode APICALL GetParent() = 0; + //! + //! @} + + //! + //! @brief Changes the local name of the node. + //! \param[in] name Pointer to a constant char buffer containing local name of the node. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to \#AdobeXMPCommon::npos. + //! \attention Error can be thrown in case + //! - name is NULL pointer or its contents are empty + //! - name is not valid XML property name. + //! - Sibling with the same combination of name and nameSpace is present. + //! + virtual void APICALL SetName( const char * name, sizet nameLength ) = 0; + + //! + //! @brief Gets the local name of the node. + //! \return a shared pointer to const \#AdobeXMPCommon::IUTF8String representing the name of the node. + //! + virtual spcIUTF8String APICALL GetName() const = 0; + + //! + //! @brief Changes the name space of the node. + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the node. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to \#AdobeXMPCommon::npos. + //! \attention Error can be thrown in case + //! - nameSpace is NULL pointer or its contents are empty + //! - Sibling with the same combination of name and nameSpace is present. + //! + virtual void APICALL SetNameSpace( const char * nameSpace, sizet nameSpaceLength ) = 0; + + //! + //! @brief Gets the name space of the node. + //! \return A shared pointer to const \#AdobeXMPCommon::IUTF8String representing the name space of the node. + //! + virtual spcIUTF8String APICALL GetNameSpace() const = 0; + + //! + //! @brief Gets the path of the node from the root of the metadata. + //! \return A shared pointer to \#AdobeXMPCore::IPath representing the path of the node. + //! + virtual spIPath APICALL GetPath() const = 0; + + //! + //! @brief Gets the count of the qualifiers attached with the node + //! \return An object of type \#AdobeXMPCommon::sizet containing the count of qualifiers attached with the node. + //! + virtual sizet APICALL QualifiersCount() const __NOTHROW__ = 0; + + //! + //! @{ + //! Get an iterator object to iterate over all the qualifier nodes attached to the composite node. + //! \return A shared pointer to a const or non const \#INodeIterator object. + //! + XMP_PRIVATE spcINodeIterator QualifiersIterator() const { + return const_cast< INode_v1 * >( this )->QualifiersIterator(); + } + virtual spINodeIterator APICALL QualifiersIterator() = 0; + //! @} + + //! + //! @brief Gets the type of the node's qualifier having specified namespace and name. + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the qualifier node. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the qualifier node. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to \#AdobeXMPCommon::npos. + //! \return An object of type \#eNodeType indicating the type of the node's qualifier. + //! \note In case no qualifier exists with the specified nameSpace and name combination then an \#eNodeType::kNTNode is returned. + //! + virtual eNodeType APICALL GetQualifierNodeType( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) const = 0; + + //! + //! @{ + //! @brief Gets the qualifier of the node having specified namespace and name. + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the qualifier node. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the qualifier node. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to \#AdobeXMPCommon::npos. + //! \return A shared pointer to either a const or const qualifier node. + //! \note In case no qualifier exists with the specified nameSpace and name combination then an invalid shared pointer + //! is returned. + //! + XMP_PRIVATE spcINode GetQualifier( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) const { + return const_cast< INode_v1 * >( this )->GetQualifier( nameSpace, nameSpaceLength, name, nameLength ); + } + virtual spINode APICALL GetQualifier( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) = 0; + //! @} + + //! + //! @{ + //! @brief Get the node's qualifier having specified name space and name as simple node. + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the qualifier node. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the qualifier node. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to \#AdobeXMPCommon::npos. + //! \return A shared pointer to const or non const \#ISimpleNode object containing qualifier. + //! \note In case no qualifier exists with the specified nameSpace and name combination then an invalid shared pointer + //! is returned. + //! \attention Error is thrown in case + //! - a qualifier exists with the specified nameSpace and name combination but is not a simple node. + //! + XMP_PRIVATE spcISimpleNode GetSimpleQualifier( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) const { + auto node = GetQualifier( nameSpace, nameSpaceLength, name, nameLength ); + if ( node ) return node->ConvertToSimpleNode(); + return spcISimpleNode(); + } + + XMP_PRIVATE spISimpleNode GetSimpleQualifier( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) { + auto node = GetQualifier( nameSpace, nameSpaceLength, name, nameLength ); + if ( node ) return node->ConvertToSimpleNode(); + return spISimpleNode(); + } + //! @} + + //! + //! @{ + //! @brief Get the node's qualifier having specified name space and name as structure node. + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the qualifier node. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the qualifier node. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to \#AdobeXMPCommon::npos. + //! \return A shared pointer to const or non const \#IStructureNode object containing qualifier. + //! \note In case no qualifier exists with the specified nameSpace and name combination then an invalid shared pointer + //! is returned. + //! \attention Error is thrown in case + //! - a qualifier exists with the specified nameSpace and name combination but is not a structure node. + //! + XMP_PRIVATE spcIStructureNode GetStructureQualifier( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) const { + auto node = GetQualifier( nameSpace, nameSpaceLength, name, nameLength ); + if ( node ) return node->ConvertToStructureNode(); + return spcIStructureNode(); + } + + XMP_PRIVATE spIStructureNode GetStructureQualifier( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) { + auto node = GetQualifier( nameSpace, nameSpaceLength, name, nameLength ); + if ( node ) return node->ConvertToStructureNode(); + return spIStructureNode(); + } + //! @} + + //! + //! @{ + //! @brief Get the node's qualifier having specified name space and name as an array node. + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the qualifier node. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the qualifier node. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to \#AdobeXMPCommon::npos. + //! \return A shared pointer to const or non const \#ISimpleNode object containing qualifier. + //! \note In case no qualifier exists with the specified nameSpace and name combination then an invalid shared pointer + //! is returned. + //! \attention Error is thrown in case + //! - a qualifier exists with the specified nameSpace and name combination but is not an array node. + //! + XMP_PRIVATE spcIArrayNode GetArrayQualifier( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) const { + auto node = GetQualifier( nameSpace, nameSpaceLength, name, nameLength ); + if ( node ) return node->ConvertToArrayNode(); + return spcIArrayNode(); + } + + XMP_PRIVATE spIArrayNode GetArrayQualifier( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) { + auto node = GetQualifier( nameSpace, nameSpaceLength, name, nameLength ); + if ( node ) return node->ConvertToArrayNode(); + return spIArrayNode(); + } + //! @} + + //! + //! @brief Inserts a given qualifier node. + //! \param[in] node Shared pointer to an object of \#AdobeXMPCore::INode representing the qualifier node to be inserted. + //! \attention Error is thrown in following cases: + //! - given qualifier node is invalid. + //! - given qualifier node is already a child of some other node. + //! - there exists a qualifier node with the same nameSpace and name combination. + //! - parent or any ancestor node is a qualifier node. + //! + virtual void APICALL InsertQualifier( const spINode & node ) = 0; + + //! + //! @brief Replaces a given qualifier node. + //! \param[in] node Shared pointer to an object of \#AdobeXMPCore::INode representing the qualifier node to be inserted. + //! \return A shared pointer to a qualifier node which is being replaced. + //! \attention Error is thrown in following cases: + //! -# given qualifier node is invalid. + //! -# given qualifier node is already a child of some other node. + //! -# there exists no qualifier node with the same nameSpace and name combination. + //! \note Warning is raised in case the type of the old existing node is not same as that of new node. + //! + virtual spINode APICALL ReplaceQualifier( const spINode & node ) = 0; + + //! + //! @brief Removes the qualifier node with the specified nameSpace and name. + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the qualifier node. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the qualifier node. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to \#AdobeXMPCommon::npos. + //! \return A shared pointer to \#AdobeXMPCore::INode object representing qualifier node which is removed from the node. + //! \note In case no qualifier node exists at the given index an invalid shared pointer is returned. + //! + virtual spINode APICALL RemoveQualifier( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) = 0; + + //! + //! @brief Indicates whether the node is a direct child of an array node. + //! \return A bool value; true in case the node is a direct child of an array node, false otherwise. + //! + virtual bool APICALL IsArrayItem() const = 0; + + //! + //! @brief Indicates whether the node is a qualifier node. + //! \return A bool value; true in case the node is a qualifier node, false otherwise. + //! + virtual bool APICALL IsQualifierNode() const = 0; + + //! + //! @brief Returns the index of the node in case it is an array item. + //! \return The index of the node, in case it is an array item, otherwise returns 0. + //! + virtual sizet APICALL GetIndex() const = 0; + + //! + //! @brief Indicates whether the node has any qualifiers associated with it. + //! \return A bool value; true in case the node has any qualifier associated with it, false otherwise. + //! + virtual bool APICALL HasQualifiers() const = 0; + + //! + //! @brief Returns whether the node has any content or not. + //! return A bool value indicating the presence of contents in the node apart from qualifiers. + //! + virtual bool APICALL HasContent() const = 0; + + //! + //! @brief Returns whether the node is empty. + //! return A bool value indicating whether the contents and qualifiers of a node are empty. + //! + virtual bool APICALL IsEmpty() const = 0; + + //! + //! @brief Returns the status about any change done to the node or its children or qualifiers. + //! returns a bool value indicating whether some changes have been performed on the node or its children or qualifiers. + //! for a simple node, true will be returned in scenarios like when the node's value or qualifiers are modified. + //! for an array node or a structure node, true will be returned in scenarios like when the node's children or the node's qualifiers are modified. + //! + virtual bool APICALL HasChanged() const = 0; + + //! + //! @brief Acknowledges that changes for the node and its children and qualifiers have been taken care of. + //! for a simple node, changes that will be acknowledged in scenarios like when the node's value or node's qualifiers were modified. + //! for an array node or a structure node, changes will be acknowledged in scenarios like when the node's children were or the node's qualifiers were modified. + //! + virtual void APICALL AcknowledgeChanges() const __NOTHROW__ = 0; + + //! + //! @brief Clear the contents of the node. + //! \param[in] contents A bool value controlling whether contents of the node should be cleared or not. + //! \param[in] qualifiers A bool value controlling whether qualifiers of the node should be cleared or not. + //! + virtual void APICALL Clear( bool contents = true, bool qualifiers = true ) = 0; + + //! + //! @{ + //! @brief Converts Node to a simple node, if possible. + //! \return Shared pointer to const or non const ISimpleNode object. An empty simple node is thrown in case actual node is not a simple node. + //! + XMP_PRIVATE spcISimpleNode ConvertToSimpleNode() const { + return const_cast< INode_v1 * >( this )->ConvertToSimpleNode(); + } + virtual spISimpleNode APICALL ConvertToSimpleNode() = 0; + //! @} + + //! + //! @{ + //! @brief Converts Node to a structure node type, if possible. + //! \return Shared pointer to const or non const IStructureNode object. An empty structure node is thrown in case actual node is not a structure node. + //! + XMP_PRIVATE spcIStructureNode ConvertToStructureNode() const { + return const_cast< INode_v1 * >( this )->ConvertToStructureNode(); + } + virtual spIStructureNode APICALL ConvertToStructureNode() = 0; + //! @} + + //! + //! @{ + //! @brief Converts Node to an array node type, if possible. + //! \return Shared pointer to const or non const IArrayNode object. An empty array node is thrown in case actual node is not an array node. + //! + XMP_PRIVATE spcIArrayNode ConvertToArrayNode() const { + return const_cast< INode_v1 * >( this )->ConvertToArrayNode(); + } + virtual spIArrayNode APICALL ConvertToArrayNode() = 0; + //! @} + + //! + //! @{ + //! @brief Converts Node to a metadata node type, if possible. + //! \return Shared pointer to const or non const IMetadata object. An empty metadata node is thrown in case actual node is not a metadata node. + //! + XMP_PRIVATE spcIMetadata ConvertToMetadata() const { + return const_cast< INode_v1 * >( this )->ConvertToMetadata(); + } + virtual spIMetadata APICALL ConvertToMetadata() = 0; + //! @} + + //! + //! @brief Virtual copy constructor + //! @details Clones the node creating an exact replica of the node which is not part of any metadata tree. + //! \param[in] ignoreEmptyNodes A bool value controlling whether to clone empty nodes or not. + //! \param[in] ignoreNodesWithOnlyQualifiers A bool value controlling whether presence of only qualifiers should mark + //! node as non empty. + //! \return A shared pointer to newly created replica of the node. + //! + virtual spINode APICALL Clone( bool ignoreEmptyNodes = false, bool ignoreNodesWithOnlyQualifiers = false ) const = 0; + + //! + //! \cond XMP_INTERNAL_DOCUMENTATION + + //! + //! @{ + //! @brief Returns the actual raw pointer from the shared pointer, which can be a shared pointer of a proxy class. + //! \return Either a const or non const pointer to INode interface. + //! + virtual pINode APICALL GetActualINode() __NOTHROW__ = 0; + + XMP_PRIVATE pcINode GetActualINode() const __NOTHROW__ { + return const_cast< INode_v1 * >( this )->GetActualINode(); + } + //! + //! @} + + //! + //! @{ + //! @brief Returns the pointer to internal interfaces. + //! \return Either a const or non const pointer to INode_I interface. + //! + virtual AdobeXMPCore_Int::pINode_I APICALL GetINode_I() __NOTHROW__ = 0; + + XMP_PRIVATE AdobeXMPCore_Int::pcINode_I GetINode_I() const __NOTHROW__ { + return const_cast< INode_v1 * >( this )->GetINode_I(); + } + //! + //! @} + + //! + //! @{ + //! @brief Converts raw pointer to shared pointer. The raw pointer is of version 1 interface + //! where as the returned shared pointer depends on the version client is interested in. + //! \return Shared pointer to const or non constant interface. + //! + XMP_PRIVATE static spINode MakeShared( pINode_base ptr ); + XMP_PRIVATE static spcINode MakeShared( pcINode_base ptr ) { + return MakeShared( const_cast< pINode_base >( ptr ) ); + } + //! + //! @} + + //! + //! @brief Returns the unique ID assigned to the interface. + //! \return 64 bit unsigned integer representing the unique ID assigned to the interface. + //! + XMP_PRIVATE static uint64 GetInterfaceID() { return kINodeID; } + + //! + //! @brief Returns the version of the interface. + //! \return 32 bit unsigned integer representing the version of the interface. + //! + XMP_PRIVATE static uint32 GetInterfaceVersion() { return 1; } + //! \endcond + + protected: + //! + //! Destructor + //! + virtual ~INode_v1() __NOTHROW__ {} + + //! \cond XMP_INTERNAL_DOCUMENTATION + virtual uint32 APICALL getParentNodeType( pcIError_base & error ) const __NOTHROW__ = 0; + virtual pINode_base APICALL getParent( pcIError_base & error ) __NOTHROW__ = 0; + virtual void APICALL setName( const char * name, sizet nameLength, pcIError_base & error ) __NOTHROW__ = 0; + virtual pcIUTF8String_base APICALL getName( pcIError_base & error ) const __NOTHROW__ = 0; + virtual void APICALL setNameSpace( const char * nameSpace, sizet nameSpaceLength, pcIError_base & error ) __NOTHROW__ = 0; + virtual pcIUTF8String_base APICALL getNameSpace( pcIError_base & error ) const __NOTHROW__ = 0; + virtual pIPath_base APICALL getPath( pcIError_base & error ) const __NOTHROW__ = 0; + virtual pINodeIterator_base APICALL qualifiersIterator( pcIError_base & error ) __NOTHROW__ = 0; + virtual uint32 APICALL getQualifierNodeType( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, pcIError_base & error ) const __NOTHROW__ = 0; + virtual pINode_base APICALL getQualifier( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, pcIError_base & error ) __NOTHROW__ = 0; + virtual void APICALL insertQualifier( pINode_base base, pcIError_base & error ) __NOTHROW__ = 0; + virtual pINode_base APICALL replaceQualifier( pINode_base node, pcIError_base & error ) __NOTHROW__ = 0; + virtual pINode_base APICALL removeQualifier( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, pcIError_base & error ) __NOTHROW__ = 0; + virtual uint32 APICALL getNodeType( pcIError_base & error ) const __NOTHROW__ = 0; + virtual uint32 APICALL isArrayItem( pcIError_base & error ) const __NOTHROW__ = 0; + virtual uint32 APICALL isQualifierNode( pcIError_base & error ) const __NOTHROW__ = 0; + virtual sizet APICALL getIndex( pcIError_base & error ) const __NOTHROW__ = 0; + virtual uint32 APICALL hasQualifiers( pcIError_base & error ) const __NOTHROW__ = 0; + virtual uint32 APICALL hasContent( pcIError_base & error ) const __NOTHROW__ = 0; + virtual uint32 APICALL isEmpty( pcIError_base & error ) const __NOTHROW__ = 0; + virtual uint32 APICALL hasChanged( pcIError_base & error ) const __NOTHROW__ = 0; + virtual void APICALL clear( uint32 contents, uint32 qualifiers, pcIError_base & error ) __NOTHROW__ = 0; + virtual pINode_base APICALL clone( uint32 igoreEmptyNodes, uint32 ignoreNodesWithOnlyQualifiers, pcIError_base & error ) const __NOTHROW__ = 0; + virtual pISimpleNode_base APICALL convertToSimpleNode( pcIError_base & error ) __NOTHROW__ = 0; + virtual pIStructureNode_base APICALL convertToStructureNode( pcIError_base & error ) __NOTHROW__ = 0; + virtual pIArrayNode_base APICALL convertToArrayNode( pcIError_base & error ) __NOTHROW__ = 0; + virtual pIMetadata_base APICALL convertToMetadata( pcIError_base & error ) __NOTHROW__ = 0; + + //! @} + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + //! \endcond + + }; +} + +//! \cond XMP_INTERNAL_DOCUMENTATION +#if !BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB + namespace AdobeXMPCore { + + class INodeProxy + : public virtual INode + { + private: + pINode mRawPtr; + + public: + INodeProxy( pINode ptr ); + ~INodeProxy() __NOTHROW__ ; + + pINode APICALL GetActualINode() __NOTHROW__; + void APICALL Acquire() const __NOTHROW__; + void APICALL Release() const __NOTHROW__; + + AdobeXMPCommon_Int::pISharedObject_I APICALL GetISharedObject_I() __NOTHROW__; + AdobeXMPCore_Int::pINode_I APICALL GetINode_I() __NOTHROW__; + + pvoid APICALL GetInterfacePointer( uint64 interfaceID, uint32 interfaceVersion ); + + virtual eNodeType APICALL GetParentNodeType() const; + virtual spINode APICALL GetParent(); + virtual void APICALL SetName( const char * name, sizet nameLength ); + virtual spcIUTF8String APICALL GetName() const; + virtual void APICALL SetNameSpace( const char * nameSpace, sizet nameSpaceLength ); + virtual spcIUTF8String APICALL GetNameSpace() const; + virtual spIPath APICALL GetPath() const; + virtual sizet APICALL QualifiersCount() const __NOTHROW__; + virtual spINodeIterator APICALL QualifiersIterator(); + virtual eNodeType APICALL GetQualifierNodeType( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) const; + virtual spINode APICALL GetQualifier( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ); + virtual void APICALL InsertQualifier( const spINode & node ); + virtual spINode APICALL ReplaceQualifier( const spINode & node ); + virtual spINode APICALL RemoveQualifier( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ); + virtual eNodeType APICALL GetNodeType() const; + virtual bool APICALL IsArrayItem() const; + virtual bool APICALL IsQualifierNode() const; + virtual sizet APICALL GetIndex() const; + virtual bool APICALL HasQualifiers() const; + virtual bool APICALL HasContent() const; + virtual bool APICALL IsEmpty() const; + virtual bool APICALL HasChanged() const; + virtual void APICALL AcknowledgeChanges() const __NOTHROW__; + virtual void APICALL Clear( bool contents, bool qualifiers ); + virtual spINode APICALL Clone( bool ignoreEmptyNodes, bool ignoreNodesWithOnlyQualifiers ) const; + virtual void APICALL EnableThreadSafety() const __NOTHROW__; + virtual void APICALL DisableThreadSafety() const __NOTHROW__; + virtual bool APICALL IsThreadSafe() const; + virtual AdobeXMPCommon_Int::pIThreadSafe_I APICALL GetIThreadSafe_I() __NOTHROW__; + virtual spISimpleNode APICALL ConvertToSimpleNode(); + virtual spIStructureNode APICALL ConvertToStructureNode(); + virtual spIArrayNode APICALL ConvertToArrayNode(); + virtual spIMetadata APICALL ConvertToMetadata(); + + protected: + virtual pINode_base APICALL getParent( pcIError_base & error ) __NOTHROW__; + virtual void APICALL setName( const char * name, sizet nameLength, pcIError_base & error ) __NOTHROW__; + virtual pcIUTF8String_base APICALL getName( pcIError_base & error ) const __NOTHROW__; + virtual void APICALL setNameSpace( const char * nameSpace, sizet nameSpaceLength, pcIError_base & error ) __NOTHROW__; + virtual pcIUTF8String_base APICALL getNameSpace( pcIError_base & error ) const __NOTHROW__; + virtual pIPath_base APICALL getPath( pcIError_base & error ) const __NOTHROW__; + virtual pINodeIterator_base APICALL qualifiersIterator( pcIError_base & error ) __NOTHROW__; + virtual pINode_base APICALL getQualifier( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, pcIError_base & error ) __NOTHROW__; + virtual void APICALL insertQualifier( pINode_base base, pcIError_base & error ) __NOTHROW__; + virtual pINode_base APICALL replaceQualifier( pINode_base node, pcIError_base & error ) __NOTHROW__; + virtual pINode_base APICALL removeQualifier( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, pcIError_base & error ) __NOTHROW__; + virtual uint32 APICALL getNodeType( pcIError_base & error ) const __NOTHROW__; + virtual uint32 APICALL isArrayItem( pcIError_base & error ) const __NOTHROW__; + virtual uint32 APICALL isQualifierNode( pcIError_base & error ) const __NOTHROW__; + virtual sizet APICALL getIndex( pcIError_base & error ) const __NOTHROW__; + virtual uint32 APICALL hasQualifiers( pcIError_base & error ) const __NOTHROW__; + virtual uint32 APICALL hasContent( pcIError_base & error ) const __NOTHROW__; + virtual uint32 APICALL isEmpty( pcIError_base & error ) const __NOTHROW__; + virtual uint32 APICALL hasChanged( pcIError_base & error ) const __NOTHROW__; + virtual void APICALL clear( uint32 contents, uint32 qualifiers, pcIError_base & error ) __NOTHROW__; + virtual pINode_base APICALL clone( uint32 igoreEmptyNodes, uint32 ignoreNodesWithOnlyQualifiers, pcIError_base & error ) const __NOTHROW__; + virtual uint32 APICALL isThreadSafe() const __NOTHROW__; + virtual pISimpleNode_base APICALL convertToSimpleNode( pcIError_base & error ) __NOTHROW__; + virtual pIStructureNode_base APICALL convertToStructureNode( pcIError_base & error ) __NOTHROW__; + virtual pIArrayNode_base APICALL convertToArrayNode( pcIError_base & error ) __NOTHROW__; + virtual pIMetadata_base APICALL convertToMetadata( pcIError_base & error ) __NOTHROW__; + virtual uint32 APICALL getParentNodeType( pcIError_base & error ) const __NOTHROW__; + virtual uint32 APICALL getQualifierNodeType( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, pcIError_base & error ) const __NOTHROW__; + pvoid APICALL getInterfacePointer( uint64 interfaceID, uint32 interfaceVersion, pcIError_base & error ) __NOTHROW__; + }; + +} +#endif // !BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB +//! \endcond + +#endif // __INode_h__ diff --git a/xmp/XMPCore/Interfaces/INodeIterator.h b/xmp/XMPCore/Interfaces/INodeIterator.h new file mode 100644 index 0000000..93bb194 --- /dev/null +++ b/xmp/XMPCore/Interfaces/INodeIterator.h @@ -0,0 +1,193 @@ +#ifndef __INodeIterator_h__ +#define __INodeIterator_h__ 1 + + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCore/XMPCoreFwdDeclarations.h" +#include "XMPCommon/Interfaces/BaseInterfaces/ISharedObject.h" +#include "XMPCore/Interfaces/INode.h" +#include "XMPCommon/Interfaces/BaseInterfaces/IVersionable.h" + +namespace AdobeXMPCore { + + //! + //! @brief Interface that represents an iterator over the mutable children of a XMP DOM Node. + //! \note Iterators are valid as long as their are no changes performed on the node. In case there are some + //! changes performed on the node then the behavior is undefined. + //! + class XMP_PUBLIC INodeIterator_v1 + : public virtual ISharedObject + , public virtual IVersionable + { + public: + + //! + //! @brief Gets the type of the node currently pointed by the iterator. + //! \return A value of type \#INode_v1::eNodeType indicating the type of the node currently pointed by the iterator. + //! + virtual INode_v1::eNodeType APICALL GetNodeType() const = 0; + + //! + //! @{ + //! @brief Gets the node currently pointed by the iterator. + //! \return A shared pointer to a const or non const object of type \#INode. + //! \note In case iterator has gone beyond its limit, an invalid shared pointer is returned + //! + virtual spINode APICALL GetNode() = 0; + XMP_PRIVATE spcINode APICALL GetNode() const { + return const_cast< INodeIterator * >( this )->GetNode(); + }; + //! @} + + //! + //! @{ + //! @brief Gets the iterator's currently pointed node as simple node, if possible. + //! \return A shared pointer to a const or non const object of type \#ISimpleNode. + //! \attention Error is thrown in case + //! - iterator's currently pointed node is valid but is not a simple node. + //! \note In case iterator has gone beyond its limit, an invalid shared pointer is returned. + //! + XMP_PRIVATE spISimpleNode GetSimpleNode() { + auto node = GetNode(); + if ( node ) return node->ConvertToSimpleNode(); + return spISimpleNode(); + } + + XMP_PRIVATE spcISimpleNode GetSimpleNode() const { + return const_cast< INodeIterator * >( this )->GetSimpleNode(); + } + //! @} + + //! + //! @brief Gets the iterator's currently pointed node as structure node, if possible. + //! \return A shared pointer to a const or non const object of type \#IStructureNode. + //! \attention Error is thrown in case + //! - iterator's currently pointed node is valid but is not a structure node. + //! \note In case iterator has gone beyond its limit, an invalid shared pointer is returned. + //! + XMP_PRIVATE spIStructureNode GetStructureNode() { + auto node = GetNode(); + if ( node ) return node->ConvertToStructureNode(); + return spIStructureNode(); + } + + XMP_PRIVATE spcIStructureNode GetStructureNode() const { + return const_cast< INodeIterator * >( this )->GetStructureNode(); + } + //! @} + + //! + //! @brief Gets the iterator's currently pointed node as an array node, if possible. + //! \return A shared pointer to a const or non const object of type \#IArrayNode. + //! \attention Error is thrown in case + //! - iterator's currently pointed node is valid but is not an array node. + //! \note In case iterator has gone beyond its limit, an invalid shared pointer is returned. + //! + XMP_PRIVATE spIArrayNode GetArrayNode() { + auto node = GetNode(); + if ( node ) return node->ConvertToArrayNode(); + return spIArrayNode(); + } + + XMP_PRIVATE spcIArrayNode GetArrayNode() const { + return const_cast< INodeIterator * >( this )->GetArrayNode(); + } + //! @} + + //! + //! @{ + //! @brief Advances iterator by one position. + //! \return A shared pointer to a const or non object of type \#INodeIterator. + //! \note Returned shared pointer is invalid in case the current node is the last one. + //! + virtual spINodeIterator APICALL Next() = 0; + XMP_PRIVATE spcINodeIterator APICALL Next() const { + return const_cast< INodeIterator * >( this )->Next(); + } + //! @} + + + //! + //! \cond XMP_INTERNAL_DOCUMENTATION + + //! + //! @{ + //! @brief Returns the actual raw pointer from the shared pointer, which can be a shared pointer of a proxy class. + //! \return Either a const or non const pointer to INodeIterator interface. + //! + virtual pINodeIterator APICALL GetActualINodeIterator() __NOTHROW__ = 0; + XMP_PRIVATE pcINodeIterator GetActualINodeIterator() const __NOTHROW__ { + return const_cast< INodeIterator * >( this )->GetActualINodeIterator(); + } + //! + //! @} + + //! + //! @{ + //! @brief Returns the pointer to internal interfaces. + //! \return Either a const or non const pointer to INodeIterator_I interface. + //! + virtual AdobeXMPCore_Int::pINodeIterator_I APICALL GetINodeIterator_I() __NOTHROW__ = 0; + XMP_PRIVATE AdobeXMPCore_Int::pcINodeIterator_I GetINodeIterator_I() const __NOTHROW__ { + return const_cast< INodeIterator * >( this )->GetINodeIterator_I(); + } + //! + //! @} + + //! + //! @{ + //! @brief Converts raw pointer to shared pointer. + //! \return Shared pointer to const or non constant interface. + //! + XMP_PRIVATE static spINodeIterator MakeShared( pINodeIterator ptr ); + XMP_PRIVATE static spcINodeIterator MakeShared( pcINodeIterator ptr ) { + return MakeShared( const_cast< pINodeIterator >( ptr ) ); + } + //! + //! @} + + //! + //! @} + + //! + //! return the unique ID assigned to the interface. + //! \return 64 bit unsigned integer representing the unique ID assigned to the interface. + //! + XMP_PRIVATE static uint64 GetInterfaceID() { return kINodeIteratorID; } + + //! + //! return the version of the interface. + //! \return 32 bit unsigned integer representing the version of the interface. + //! + XMP_PRIVATE static uint32 GetInterfaceVersion() { return 1; } + //! \endcond + + protected: + //! + //! Destructor + //! + virtual ~INodeIterator_v1() __NOTHROW__ {} + + //! \cond XMP_INTERNAL_DOCUMENTATION + virtual uint32 APICALL getNodeType( pcIError_base & error ) const __NOTHROW__ = 0; + virtual pINode_base APICALL getNode( pcIError_base & error ) __NOTHROW__ = 0; + virtual pINodeIterator_base APICALL next( pcIError_base & error ) __NOTHROW__ = 0; + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + //! \endcond + + }; +} + +#endif // __INodeIterator_h__ diff --git a/xmp/XMPCore/Interfaces/IPath.h b/xmp/XMPCore/Interfaces/IPath.h new file mode 100644 index 0000000..7f55853 --- /dev/null +++ b/xmp/XMPCore/Interfaces/IPath.h @@ -0,0 +1,212 @@ +#ifndef __IPath_h__ +#define __IPath_h__ 1 + +// ================================================================================================= +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCore/XMPCoreFwdDeclarations.h" +#include "XMPCommon/Interfaces/BaseInterfaces/ISharedObject.h" +#include "XMPCommon/Interfaces/BaseInterfaces/IVersionable.h" + +namespace AdobeXMPCore { + + //! + //! @brief Version1 of the interface that provides an easy iterative description of a specific path into the XMP tree. + //! @details Path consists of multiple path segments in an order and each \#IPathSegment represents one segment + //! of the path into the XMP tree. + //! Provides all the functions to create path and get the various properties of a path. + //! \attention Do not support multi-threading. + //! \note Index in the path are 1-based. + //! + class XMP_PUBLIC IPath_v1 + : public virtual ISharedObject + , public virtual IVersionable + { + public: + + //! + //! @brief Registers a map of namespace and prefix with the object. + //! This map will be used during serialization and parsing. + //! \param[in] map A shared pointer of an object \#AdobeXMPCore::INameSpacePrefixMap. + //! \return A shared pointer to the const map registered previously with the object. + //! + virtual spcINameSpacePrefixMap APICALL RegisterNameSpacePrefixMap( const spcINameSpacePrefixMap & map ) = 0; + + //! + //! @brief Serializes the IPath object to a utf8 string representation. This will produce either a long form of the path using + //! the full namespace strings or short form of the path using the prefix for the namespace. + //! \param[in] map A shared pointer to a const \#AdobeXMPCore::INameSpacePrefixMap object which can contain the + //! mapping for nameSpaces to prefixes. They will take precedence over the map registered with the object. + //! \return A shard pointer to \#AdobeXMPCommon::IUTF8String object containing serialized form of path. + //! \note In case map is not a valid shared pointer all the mappings will be picked from the map registered with the object. + //! If neither a map is registered nor it is provided in the arguments then it will serialize to long form of the path. + //! \attention Error will be thrown in case + //! - no prefix exists for a namespace used in the path. + //! + virtual spIUTF8String APICALL Serialize( const spcINameSpacePrefixMap & map = spcINameSpacePrefixMap() ) const = 0; + + //! + //! @brief Appends a path segment to the path. + //! \param[in] segment A shared pointer to a const \#AdobeXMPCore::IPathSegment object. + //! \attention Error is thrown in case + //! - segment is not a valid shared pointer. + //! + virtual void APICALL AppendPathSegment( const spcIPathSegment & segment ) = 0; + + //! + //! @brief Removes a path segment from the path. + //! \param[in] index Indicates the index of the path segment to be removed. + //! \returns A shared pointer to the const path segment removed. + //! \attention Error is thrown in case + //! - index is out of bounds. + //! + virtual spcIPathSegment APICALL RemovePathSegment( sizet index ) = 0; + + //! + //! @brief Gets the path segment at a particular index in the path + //! \param[in] index Indicates the index for the path segment in the path. + //! \return A shared pointer to a const path segment. + //! \attention Error is thrown in case + //! -index is out of bounds. + //! + virtual spcIPathSegment APICALL GetPathSegment( sizet index ) const = 0; + + //! + //! @brief Gets the number of the path segments in the path. + //! \return The count of the path segments in the path. + //! + virtual sizet APICALL Size() const __NOTHROW__ = 0; + + //! + //! @brief To check whether path is empty or not. + //! \returns A bool object indicating true in case the path is empty (no path segment) + //! + XMP_PRIVATE bool IsEmpty() const { + return Size() == 0; + } + + //! + //! @brief Clears the path by removing all the path segments from it + //! + virtual void APICALL Clear() __NOTHROW__ = 0; + + //! + //! @brief Gets a new path having a selected range of path segments + //! \param[in] startingIndex Indicates the starting index of the path segment to be part of the returned path object. Default value is 1. + //! \param[in] countOfSegments Indicates the count of the path segments to be part of the returned path object starting from startingIndex. + //! Default value is \#AdobeXMPCommon::kMaxSize. + //! \note In case countOfSegments is more than the number of segments available in the path object starting from the starting index + //! then internally it is truncated to the number of path segments available. + //! \attention Error is thrown in case + //! - startingIndex is more than the count of segments in the object. + //! + virtual spIPath APICALL Clone( sizet startingIndex = 1, sizet countOfSegments = kMaxSize ) const = 0; + + //! + //! \cond XMP_INTERNAL_DOCUMENTATION + + //! + //! @{ + //! @brief Returns the actual raw pointer from the shared pointer, which can be a shared pointer of a proxy class. + //! \return Either a const or non const pointer to IPath interface. + //! + virtual pIPath APICALL GetActualIPath() __NOTHROW__ = 0; + XMP_PRIVATE pcIPath GetActualIPath() const __NOTHROW__ { + return const_cast< IPath_v1 * >( this )->GetActualIPath(); + } + //! + //! @} + + //! + //! @{ + //! @brief Returns the pointer to internal interfaces. + //! \return Either a const or non const pointer to IPath_I interface. + //! + virtual AdobeXMPCore_Int::pIPath_I APICALL GetIPath_I() __NOTHROW__ = 0; + + XMP_PRIVATE AdobeXMPCore_Int::pcIPath_I GetIPath_I() const __NOTHROW__ { + return const_cast< IPath_v1 * >( this )->GetIPath_I(); + } + //! + //! @} + + //! + //! @{ + //! @brief Converts raw pointer to shared pointer. The raw pointer is of version 1 interface + //! where as the returned shared pointer depends on the version client is interested in. + //! \return Shared pointer to const or non constant interface. + //! + XMP_PRIVATE static spIPath MakeShared( pIPath_base ptr ); + XMP_PRIVATE static spcIPath MakeShared( pcIPath_base ptr ) { + return MakeShared( const_cast< pIPath_base >( ptr ) ); + } + //! + //! @} + + //! + //! @brief Returns the unique ID assigned to the interface. + //! \return 64 bit unsigned integer representing the unique ID assigned to the interface. + //! + XMP_PRIVATE static uint64 GetInterfaceID() { return kIPathID; } + + //! + //! @brief Returns the version of the interface. + //! \return 32 bit unsigned integer representing the version of the interface. + //! + XMP_PRIVATE static uint32 GetInterfaceVersion() { return 1; } + //! \endcond + + // static factory functions + + //! + //! @brief Creates an empty IPath object. + //! \return a shared pointer to an empty IPath object + //! + XMP_PRIVATE static spIPath CreatePath(); + + //! + //! @brief Creates a path from a char buffer which contains the serialized path. + //! \param[in] path Pointer to a const char buffer containing serialized form of the path. + //! \param[in] pathLength Number of characters in the path. In case path in null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] map A shared pointer to a const \#AdobeXMPCore::IXMPNameSpacePrefixMap object which will contain the + //! mapping from nameSpaces to prefixes. + //! \return A shared pointer to a \#AdobeXMPCore::IPath object. + //! \note In case the serializedPath is NULL or the contents are empty then it will result in an empty path. + //! \note This operation is currently not implemented for the IPath interface. + //! \attention Error is thrown in case + //! - no mapping exists for a prefix to name space. + //! - path contains invalid data. + //! + XMP_PRIVATE static spIPath ParsePath( const char * path, sizet pathLength, const spcINameSpacePrefixMap & map ); + + + protected: + //! + //! Destructor + //! + virtual ~IPath_v1() __NOTHROW__ {} + + //! \cond XMP_INTERNAL_DOCUMENTATION + virtual pcINameSpacePrefixMap_base APICALL registerNameSpacePrefixMap( pcINameSpacePrefixMap_base map, pcIError_base & error ) __NOTHROW__ = 0; + virtual pIUTF8String_base APICALL serialize( pcINameSpacePrefixMap_base map, pcIError_base & error ) const __NOTHROW__ = 0; + virtual void APICALL appendPathSegment( pcIPathSegment_base segment, pcIError_base & error ) __NOTHROW__ = 0; + virtual pcIPathSegment_base APICALL removePathSegment( sizet index, pcIError_base & error ) __NOTHROW__ = 0; + virtual pcIPathSegment_base APICALL getPathSegment( sizet index, pcIError_base & error ) const __NOTHROW__ = 0; + virtual pIPath_base APICALL clone( sizet startingIndex, sizet countOfSegemetns, pcIError_base & error ) const __NOTHROW__ = 0; + + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + //! \endcond + + }; +} + +#endif // __IPath_h__ diff --git a/xmp/XMPCore/Interfaces/IPathSegment.h b/xmp/XMPCore/Interfaces/IPathSegment.h new file mode 100644 index 0000000..9dd9ac9 --- /dev/null +++ b/xmp/XMPCore/Interfaces/IPathSegment.h @@ -0,0 +1,225 @@ +#ifndef __IPathSegment_h__ +#define __IPathSegment_h__ 1 + +// ================================================================================================= +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCore/XMPCoreFwdDeclarations.h" +#include "XMPCommon/Interfaces/BaseInterfaces/ISharedObject.h" +#include "XMPCommon/Interfaces/BaseInterfaces/IVersionable.h" + +namespace AdobeXMPCore { + + //! + //! @brief Version1 of the interface that represents one segment in a path to a node into the XMP tree. + //! @details Provides all the functions to access properties of the path segment and factory functions + //! to create various kinds of path segments. + //! \attention Not thread safe and not required as only read only access is provided to client. + //! + class XMP_PUBLIC IPathSegment_v1 + : public virtual ISharedObject + , public virtual IVersionable + { + public: + + //! + //! @brief This enumeration represents the types of a path segment. + //! + typedef enum { + //! None type + kPSTNone = 0, + + //! Any property that consists of namespace and a localName + kPSTProperty = 1, + + //! An array index which does not have a namespace or localName itself + kPSTArrayIndex = 1 << 1, + + //! A qualifier of a property, also consists of namespace and localName + kPSTQualifier = 1 << 2, + + //! Selects a specific qualifier by its value (e.g. specific language) + kPSTQualifierSelector = 1 << 3, + + //! Represents all property types + kPSTAll = kAllBits + } ePathSegmentType; + + //! + //! @brief Gets the name space of the path segment. + //! \return A shared pointer to const \#AdobeXMPCommon::IUTF8String object representing namespace of the path segment. + //! + virtual spcIUTF8String APICALL GetNameSpace() const = 0; + + //! + //! @brief Gets the name of the path segment. + //! \return A shared pointer to const \#AdobeXMPCommon::IUTF8String object containing name of the path segment. In case + //! path segment has no name space, an invalid shared pointer is returned. + //! + virtual spcIUTF8String APICALL GetName() const = 0; + + //! + //! @brief Gets the type of the path segment. + //! \return An object of type \#ePathSegmentType representing type of the path segment. + //! + virtual ePathSegmentType APICALL GetType() const = 0; + + //! + //! @brief Gets the index of the array type path segment. + //! \return An objet of type \#AdobeXMPCommon::sizet object representing index of the array type path segment. In case + //! path segment is not of type kPSTArrayIndex, it returns \#AdobeXMPCommon::kMaxSize. + //! + virtual sizet APICALL GetIndex() const __NOTHROW__ = 0; + + //! + //! @brief Gets the value of the qualifier type path segment. + //! \return A shared pointer to const \#AdobeXMP::IUTF8String object representing value of the qualifier type path segment. + //! In case path segment is not of type kPSTQualifier an invalid shared pointer is returned. + //! + virtual spcIUTF8String APICALL GetValue() const = 0; + + //! + //! \cond XMP_INTERNAL_DOCUMENTATION + + //! + //! @{ + //! @brief Returns the actual raw pointer from the shared pointer, which can be a shared pointer of a proxy class. + //! \return Either a const or non const pointer to IPathSegment interface. + //! + virtual pIPathSegment APICALL GetActualIPathSegment() __NOTHROW__ = 0; + XMP_PRIVATE pcIPathSegment GetActualIPathSegment() const __NOTHROW__ { + return const_cast< IPathSegment_v1 * >( this )->GetActualIPathSegment(); + } + //! + //! @} + + //! + //! @{ + //! @brief Returns the pointer to internal interfaces. + //! \return Either a const or non const pointer to IPathSegment_I interface. + //! + virtual AdobeXMPCore_Int::pIPathSegment_I APICALL GetIPathSegment_I() __NOTHROW__ = 0; + + XMP_PRIVATE AdobeXMPCore_Int::pcIPathSegment_I GetIPathSegment_I() const __NOTHROW__ { + return const_cast< IPathSegment_v1 * >( this )->GetIPathSegment_I(); + } + //! + //! @} + + //! + //! @{ + //! @brief Converts raw pointer to shared pointer. + //! @details The raw pointer is of version 1 interface + //! where as the returned shared pointer depends on the version client is interested in. + //! \return Shared pointer to const or non constant interface. + //! + XMP_PRIVATE static spIPathSegment MakeShared( pIPathSegment_base ptr ); + XMP_PRIVATE static spcIPathSegment MakeShared( pcIPathSegment_base ptr ) { + return MakeShared( const_cast< pIPathSegment_base >( ptr ) ); + } + //! + //! @} + + //! + //! @brief Returns the unique ID assigned to the interface. + //! \return 64 bit unsigned integer representing the unique ID assigned to the interface. + //! + XMP_PRIVATE static uint64 GetInterfaceID() { return kIPathSegmentID; } + + //! + //! @brief Returns the version of the interface. + //! \return 32 bit unsigned integer representing the version of the interface. + //! + XMP_PRIVATE static uint32 GetInterfaceVersion() { return 1; } + //! \endcond + + // static factory functions + + // Factories to create the specific segments + + //! + //! @brief Creates a normal property path segment.These are essentially all properties (simple, struct and arrays). + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the property. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the property. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to \#AdobeXMPCommon::npos. + //! \return A shared pointer to const \#IPathSegment. + //! \attention Throws \#AdobeXMPCommon::pcIError in case + //! - pointers to const char buffers are NULL, + //! - their content is empty. + //! + XMP_PRIVATE static spcIPathSegment CreatePropertyPathSegment( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ); + + //! + //! @brief Creates an array index path segment that denotes a specific element of an array. + //! @details Such segments do not have an own name and inherits the namespace from the Array property itself. + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the property. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] index An object of type \#AdobeXMP::sizet containting the index of the array element. + //! \return A shared pointer to const \#IPathSegment. + //! \attention Throws \#AdobeXMP::pcIError in case + //! - pointers to const char buffers are NULL, + //! - their content is empty. + //! + //! + XMP_PRIVATE static spcIPathSegment CreateArrayIndexPathSegment( const char * nameSpace, sizet nameSpaceLength, sizet index ); + + //! + //! @brief Creates a Qualifier path segment, which behaves like a normal property + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the property. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the property. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to \#AdobeXMPCommon::npos. + //! \return A shared pointer to const \#IPathSegment. + //! \attention Throws \#AdobeXMPCommon::pcIError in case + //! - pointers to const char buffers are NULL, + //! - their content is empty. + //! + XMP_PRIVATE static spcIPathSegment CreateQualifierPathSegment( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ); + + //! + //! @brief Creates a path segment that selects a specific qualifier by its value. + //! For example a specific language in a alternative array of languages. + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the property. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the property. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to \#AdobeXMPCommon::npos. + //! \param[in] value Pointer to a constant char buffer containing value of the language (xml:lang) + //! \param[in] valueLength Number of characters in value. In case value is null terminated set it to \#AdobeXMPCommon::npos. + //! \return A shared pointer to const \#IPathSegment. + //! \attention Throws #AdobeXMPCommon::pcIError in case + //! - pointers to const char buffers are NULL, + //! - their content is empty. + //! + XMP_PRIVATE static spcIPathSegment CreateQualifierSelectorPathSegment( const char * nameSpace, sizet nameSpaceLength, const char * name, + sizet nameLength, const char * value, sizet valueLength ); + + + + protected: + //! + //! Destructor + //! + virtual ~IPathSegment_v1() __NOTHROW__ {} + + //! \cond XMP_INTERNAL_DOCUMENTATION + virtual pcIUTF8String_base APICALL getNameSpace( pcIError_base & error ) const __NOTHROW__ = 0; + virtual pcIUTF8String_base APICALL getName( pcIError_base & error ) const __NOTHROW__ = 0; + virtual uint32 APICALL getType( pcIError_base & error ) const __NOTHROW__ = 0; + virtual pcIUTF8String_base APICALL getValue( pcIError_base & error ) const __NOTHROW__ = 0; + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + //! \endcond + + }; +} + +#endif // __IPathSegment_h__ diff --git a/xmp/XMPCore/Interfaces/ISimpleNode.h b/xmp/XMPCore/Interfaces/ISimpleNode.h new file mode 100644 index 0000000..8d154d3 --- /dev/null +++ b/xmp/XMPCore/Interfaces/ISimpleNode.h @@ -0,0 +1,150 @@ +#ifndef __ISimpleNode_h__ +#define __ISimpleNode_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCore/Interfaces/INode.h" + +namespace AdobeXMPCore { + + //! + //! \brief Version1 of the interface that represents a Simple Property Node of XMP DOM. + //! \details Provides all the functions to get and set various properties of the simple node. + //! \attention Support multi threading through locks but can be enabled/disabled by the client. By default + //! every object created does not support multi-threading. + //! + class XMP_PUBLIC ISimpleNode_v1 + : public virtual INode_v1 + { + public: + + //! + //! @brief Gets the value of the simple property node. + //! \return A shared pointer to const AdobeXMPCommon::IUTF8String object containing value string + //! of the simple property node. + //! + virtual spcIUTF8String APICALL GetValue() const = 0; + + //! + //! @brief Changes the value string of the simple property node. + //! \param[in] value Pointer to a constant char buffer containing value of the simple node. + //! \param[in] valueLength Number of characters in value. In case name is null terminated set it to AdobeXMPCommon::npos. + //! \note In case the value is null pointer or its contents are empty than the value is set to empty string. + //! + virtual void APICALL SetValue( const char * value, sizet valueLength ) = 0; + + //! + //! @brief Indicates whether the simple property node is of URI type. + //! \return A bool value; true in case the simple node is of URI type, false otherwise. + //! + virtual bool APICALL IsURIType() const = 0; + + //! + //! @brief Controls whether the type of simple property node should be of IsURI type or not. + //! \param[in] isURI A bool value controlling the IsURI type of the simple property node + //! + virtual void APICALL SetURIType( bool isURI ) = 0; + + // Factories to create the simple node + + //! + //! @brief Creates a simple property node which is not part of any metadata document. + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the simple node. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the simple node. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to AdobeXMPCommon::npos. + //! \param[in] value Pointer to a constant char buffer containing value of the simple node. + //! \param[in] valueLength Number of characters in value. In case name is null terminated set it to AdobeXMPCommon::npos. + //! \return A shared pointer to a AdobeXMPCore::ISimpleNode object. + //! \attention Error is thrown in case + //! - nameSpace or name are NULL pointers, or + //! - their contents are empty. + //! \note In case the value is a null pointer or its contents are empty than the value is set to empty string. + //! + XMP_PRIVATE static spISimpleNode CreateSimpleNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, + const char * value = NULL, sizet valueLength = AdobeXMPCommon::npos ); + + + //! + //! \cond XMP_INTERNAL_DOCUMENTATION + + //! + //! @{ + //! @brief Returns the actual raw pointer from the shared pointer, which can be a shared pointer of a proxy class. + //! \return Either a const or non const pointer to ISimpleNode interface. + //! + virtual pISimpleNode APICALL GetActualISimpleNode() __NOTHROW__ = 0; + XMP_PRIVATE pcISimpleNode GetActualISimpleNode() const __NOTHROW__ { + return const_cast< ISimpleNode_v1 * >( this )->GetActualISimpleNode(); + } + //! + //! @} + + //! + //! @{ + //! @brief Returns the pointer to internal interfaces. + //! \return Either a const or non const pointer to ISimpleNode_I interface. + //! + virtual AdobeXMPCore_Int::pISimpleNode_I APICALL GetISimpleNode_I() __NOTHROW__ = 0; + + XMP_PRIVATE AdobeXMPCore_Int::pcISimpleNode_I GetISimpleNode_I() const __NOTHROW__ { + return const_cast< ISimpleNode_v1 * >( this )->GetISimpleNode_I(); + } + //! + //! @} + + //! + //! @{ + //! @brief Converts raw pointer to shared pointer. The raw pointer is of version 1 interface + //! where as the returned shared pointer depends on the version client is interested in. + //! \return Shared pointer to const or non constant interface. + //! + XMP_PRIVATE static spISimpleNode MakeShared( pISimpleNode_base ptr ); + XMP_PRIVATE static spcISimpleNode MakeShared( pcISimpleNode_base ptr ) { + return MakeShared( const_cast< pISimpleNode_base >( ptr ) ); + } + //! + //! @} + + //! + //! @brief Returns the unique ID assigned to the interface. + //! \return 64 bit unsigned integer representing the unique ID assigned to the interface. + //! + XMP_PRIVATE static uint64 GetInterfaceID() { return kISimpleNodeID; } + + //! + //! @brief Returns the version of the interface. + //! \return 32 bit unsigned integer representing the version of the interface. + //! + XMP_PRIVATE static uint32 GetInterfaceVersion() { return 1; } + //! \endcond + + protected: + //! + //! Destructor + //! + virtual ~ISimpleNode_v1() __NOTHROW__ {} + + //! \cond XMP_INTERNAL_DOCUMENTATION + virtual pcIUTF8String_base APICALL getValue( pcIError_base & error ) const __NOTHROW__ = 0; + virtual void APICALL setValue( const char * value, sizet valueLength, pcIError_base & error ) __NOTHROW__ = 0; + virtual uint32 APICALL isURIType( pcIError_base & error ) const __NOTHROW__ = 0; + virtual void APICALL setURIType( uint32 isURI, pcIError_base & error ) __NOTHROW__ = 0; + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + //! \endcond + + }; +} + +#endif // __ISimpleNode_h__ diff --git a/xmp/XMPCore/Interfaces/IStructureNode.h b/xmp/XMPCore/Interfaces/IStructureNode.h new file mode 100644 index 0000000..5df1f08 --- /dev/null +++ b/xmp/XMPCore/Interfaces/IStructureNode.h @@ -0,0 +1,306 @@ +#ifndef IStructureNode_h__ +#define IStructureNode_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCore/Interfaces/ICompositeNode.h" + +namespace AdobeXMPCore { + + //! + //! @brief Version1 of the interface that represents a structure Node of XMP DOM. + //! \details Provides all the functions to get and set various properties of the structure node. + //! \attention Support multi threading through locks but can be enabled/disabled by the client. By default + //! every object created does not support multi-threading. + //! + class XMP_PUBLIC IStructureNode_v1 + : public virtual ICompositeNode_v1 + { + public: + + //! + //! @brief Gets the type of the node's child having specified namespace and name. + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the child node. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the child node. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to AdobeXMPCommon::npos. + //! \return An object of type #eNodeType indicating the type of the node's child. + //! \note In case no child exists with the specified nameSpace and name combination then an eNodeType::kNTNone is returned. + //! + virtual eNodeType APICALL GetChildNodeType( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) const = 0; + + + //! + //! @{ + //! @brief Gets the child of the node having specified namespace and name. + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the child node. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the child node. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to AdobeXMPCommon::npos. + //! \return A shared pointer to either a const or const child node. + //! \note In case no child exists with the specified nameSpace and name combination then an invalid shared pointer + //! is returned. + //! + XMP_PRIVATE spcINode GetNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) const { + return const_cast< IStructureNode_v1 * >( this )->GetNode( nameSpace, nameSpaceLength, name, nameLength ); + } + virtual spINode APICALL GetNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) = 0; + //! @} + + //! + //! @{ + //! @brief Gets the node's child having specified name space and name as simple node. + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the child node. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the child node. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to AdobeXMPCommon::npos. + //! \return A shared pointer to const or non const ISimpleNode object containing child. + //! \note In case no child exists with the specified nameSpace and name combination then an invalid shared pointer + //! is returned. + //! \attention Error is thrown in case + //! - a child exists with the specified nameSpace and name combination but is not a simple node. + //! + XMP_PRIVATE spcISimpleNode GetSimpleNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) const { + auto node = GetNode( nameSpace, nameSpaceLength, name, nameLength ); + if ( node ) return node->ConvertToSimpleNode(); + return spcISimpleNode(); + } + + XMP_PRIVATE spISimpleNode GetSimpleNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) { + auto node = GetNode( nameSpace, nameSpaceLength, name, nameLength ); + if ( node ) return node->ConvertToSimpleNode(); + return spISimpleNode(); + } + //! @} + + //! + //! @{ + //! @brief Gets the node's child having specified name space and name as structure node. + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the child node. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the child node. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to AdobeXMPCommon::npos. + //! \return A shared pointer to const or non const IStructureNode object containing child. + //! \note In case no child exists with the specified nameSpace and name combination then an invalid shared pointer + //! is returned. + //! \attention Error is thrown in case + //! - a child exists with the specified nameSpace and name combination but is not a structure node. + //! + XMP_PRIVATE spcIStructureNode GetStructureNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) const { + auto node = GetNode( nameSpace, nameSpaceLength, name, nameLength ); + if ( node ) return node->ConvertToStructureNode(); + return spcIStructureNode(); + } + + XMP_PRIVATE spIStructureNode GetStructureNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) { + auto node = GetNode( nameSpace, nameSpaceLength, name, nameLength ); + if ( node ) return node->ConvertToStructureNode(); + return spIStructureNode(); + } + //! @} + + //! + //! @{ + //! @brief Gets the node's child having specified name space and name as an array node. + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the child node. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the child node. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to AdobeXMPCommon::npos. + //! \return A shared pointer to const or non const ISimpleNode object containing child. + //! \note In case no child exists with the specified nameSpace and name combination then an invalid shared pointer + //! is returned. + //! \attention Error is thrown in case + //! - a child exists with the specified nameSpace and name combination but is not an array node. + //! + XMP_PRIVATE spcIArrayNode GetArrayNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) const { + auto node = GetNode( nameSpace, nameSpaceLength, name, nameLength ); + if ( node ) return node->ConvertToArrayNode(); + return spcIArrayNode(); + } + + XMP_PRIVATE spIArrayNode GetArrayNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) { + auto node = GetNode( nameSpace, nameSpaceLength, name, nameLength ); + if ( node ) return node->ConvertToArrayNode(); + return spIArrayNode(); + } + //! @} + + //! + //! @brief Inserts a given node. + //! \param[in] node Shared pointer to an object of AdobeXMPCore::INode containing the node to be inserted. + //! \attention Error is thrown in following cases: + //! -# given node is invalid. + //! -# given node is already a child of some other node. + //! -# there exists a node with the same nameSpace and name combination. + //! + virtual void APICALL InsertNode( const spINode & node ) = 0; + + //! + //! @brief Replaces a given node. + //! \param[in] node Shared pointer to an object of AdobeXMPCore::INode. + //! \return A shared pointer to the node being replaced. + //! \attention Error is thrown in following cases: + //! -# given node is invalid. + //! -# given node is already a child of some other node. + //! -# there exists no node with the same nameSpace and name combination. + //! \note Type of the old existing node may/may not be same as that of new node. + //! + virtual spINode APICALL ReplaceNode( const spINode & node ) = 0; + + //! + //! @brief Removes the node with the specified nameSpace and name. + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the child node. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the child node. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to AdobeXMPCommon::npos. + //! \return A shared pointer to AdobeXMPCore::INode object containing node which is removed from the tree. + //! \note In case no node exists with the given nameSpace and name combination an invalid shared pointer is returned. + //! + virtual spINode APICALL RemoveNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) = 0; + + //! + //! \cond XMP_INTERNAL_DOCUMENTATION + + //! + //! @{ + //! @brief Returns the actual raw pointer from the shared pointer, which can be a shared pointer of a proxy class. + //! \return Either a const or non const pointer to IStructureNode interface. + //! + virtual pIStructureNode APICALL GetActualIStructureNode() __NOTHROW__ = 0; + XMP_PRIVATE pcIStructureNode GetActualIStructureNode() const __NOTHROW__ { + return const_cast< IStructureNode_v1 * >( this )->GetActualIStructureNode(); + } + //! + //! @} + + //! + //! @{ + //! @brief Returns the pointer to internal interfaces. + //! \return Either a const or non const pointer to IStructureNode_I interface. + //! + virtual AdobeXMPCore_Int::pIStructureNode_I APICALL GetIStructureNode_I() __NOTHROW__ = 0; + + XMP_PRIVATE AdobeXMPCore_Int::pcIStructureNode_I GetIStructureNode_I() const __NOTHROW__ { + return const_cast< IStructureNode_v1 * >( this )->GetIStructureNode_I(); + } + //! + //! @} + + //! + //! @{ + //! @brief Converts raw pointer to shared pointer. The raw pointer is of version 1 interface + //! where as the returned shared pointer depends on the version client is interested in. + //! \return Shared pointer to const or non constant interface. + //! + XMP_PRIVATE static spIStructureNode MakeShared( pIStructureNode_base ptr ); + XMP_PRIVATE static spcIStructureNode MakeShared( pcIStructureNode_base ptr ) { + return MakeShared( const_cast< pIStructureNode_base >( ptr ) ); + } + //! + //! @} + + //! + //! @brief Returns the unique ID assigned to the interface. + //! \return 64 bit unsigned integer representing the unique ID assigned to the interface. + //! + XMP_PRIVATE static uint64 GetInterfaceID() { return kIStructureNodeID; } + + //! + //! @brief Returns the version of the interface. + //! \return 32 bit unsigned integer representing the version of the interface. + //! + XMP_PRIVATE static uint32 GetInterfaceVersion() { return 1; } + //! \endcond + + // Factories to create the structure node + + //! + //! @brief Creates a structure node which is not part of any metadata document. + //! \param[in] nameSpace Pointer to a constant char buffer containing name space URI of the structure node. + //! \param[in] nameSpaceLength Number of characters in nameSpace. In case nameSpace is null terminated set it to AdobeXMPCommon::npos. + //! \param[in] name Pointer to a constant char buffer containing local name of the structure node. + //! \param[in] nameLength Number of characters in name. In case name is null terminated set it to AdobeXMPCommon::npos. + //! \return A shared pointer to a AdobeXMPCore::IStructureNode object. + //! \attention Error is thrown in the following cases: + //! -# nameSpace is NULL or its contents are empty. + //! -# name is NULL or its contents are empty. + //! + XMP_PRIVATE static spIStructureNode CreateStructureNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ); + + protected: + //! + //! Destructor + //! + virtual ~IStructureNode_v1() __NOTHROW__ {} + + //! \cond XMP_INTERNAL_DOCUMENTATION + virtual uint32 APICALL getChildNodeType( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, pcIError_base & error ) const __NOTHROW__ = 0; + virtual pINode_base APICALL getNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, pcIError_base & error ) __NOTHROW__ = 0; + virtual void APICALL insertNode( pINode_base node, pcIError_base & error ) __NOTHROW__ = 0; + virtual pINode_base APICALL replaceNode( pINode_base node, pcIError_base & error ) __NOTHROW__ = 0; + virtual pINode_base APICALL removeNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, pcIError_base & error ) __NOTHROW__ = 0; + + #ifdef FRIEND_CLASS_DECLARATION + FRIEND_CLASS_DECLARATION(); + #endif + REQ_FRIEND_CLASS_DECLARATION(); + //! \endcond + + }; +} + +//! \cond XMP_INTERNAL_DOCUMENTATION +#if !BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB + +namespace AdobeXMPCore { + +#if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4250 ) +#endif + + class IStructureNodeProxy + : public virtual IStructureNode + , public virtual ICompositeNodeProxy + { + private: + pIStructureNode mRawPtr; + + public: + IStructureNodeProxy( pIStructureNode ptr ); + ~IStructureNodeProxy() __NOTHROW__ ; + + AdobeXMPCore_Int::pIStructureNode_I APICALL GetIStructureNode_I() __NOTHROW__; + virtual pIStructureNode APICALL GetActualIStructureNode() __NOTHROW__; + + virtual eNodeType APICALL GetChildNodeType( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) const; + virtual spINode APICALL GetNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ); + virtual void APICALL InsertNode( const spINode & node ); + virtual spINode APICALL ReplaceNode( const spINode & node ); + virtual spINode APICALL RemoveNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ); + + protected: + virtual uint32 APICALL getChildNodeType( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, pcIError_base & error ) const __NOTHROW__; + virtual pINode_base APICALL getNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, pcIError_base & error ) __NOTHROW__; + virtual void APICALL insertNode( pINode_base node, pcIError_base & error ) __NOTHROW__; + virtual pINode_base APICALL replaceNode( pINode_base node, pcIError_base & error ) __NOTHROW__; + virtual pINode_base APICALL removeNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, pcIError_base & error ) __NOTHROW__; + }; + +#if XMP_WinBuild + #pragma warning( pop ) +#endif + +} + +#endif // BUILDING_XMPCORE_LIB +//! \endcond + +#endif // IStructureNode_h__ diff --git a/xmp/XMPCore/XMPCoreDefines.h b/xmp/XMPCore/XMPCoreDefines.h new file mode 100644 index 0000000..fbe9af4 --- /dev/null +++ b/xmp/XMPCore/XMPCoreDefines.h @@ -0,0 +1,86 @@ +#ifndef XMPCoreDefines_h__ +#define XMPCoreDefines_h__ 1 + +// ================================================================================================= +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +// ================================================================================================= +// XMPCoreDefines.h - Common Defines for XMP Core component +// ================================================================ +// +// This header defines common definitions to be used in XMP Core component. +// +// ================================================================================================= + +// ================================================================================================= +// All Platform Settings +// =========================== +#include "XMPCommon/XMPCommonDefines.h" + +#ifndef ENABLE_CPP_DOM_MODEL +// ================================================================================================= +// Macintosh Specific Settings +// =========================== +#if XMP_MacBuild + #define ENABLE_CPP_DOM_MODEL 0 +#endif + +// ================================================================================================= +// IOS Specific Settings +// =========================== +#if XMP_iOSBuild + #define ENABLE_CPP_DOM_MODEL 0 +#endif + +// ================================================================================================= +// Windows Specific Settings +// ========================= +#if XMP_WinBuild + #define ENABLE_CPP_DOM_MODEL 0 +#endif + +// ================================================================================================= +// UNIX Specific Settings +// ====================== +#if XMP_UNIXBuild +#define ENABLE_CPP_DOM_MODEL 0 +#endif +#endif // ENABLE_CPP_DOM_MODEL + +#ifndef ENABLE_CPP_DOM_MODEL + #define ENABLE_CPP_DOM_MODEL 0 +#endif + +#if ENABLE_CPP_DOM_MODEL + + #if SOURCE_COMPILING_XMP_ALL + #define SOURCE_COMPILING_XMPCORE_LIB 1 + #endif + + #ifndef SOURCE_COMPILING_XMPCORE_LIB + #define SOURCE_COMPILING_XMPCORE_LIB 0 + #endif + + #ifndef BUILDING_XMPCORE_LIB + #define BUILDING_XMPCORE_LIB 0 + #endif + + #if BUILDING_XMPCORE_LIB + #if !BUILDING_XMPCORE_AS_STATIC && !BUILDING_XMPCORE_AS_DYNAMIC + #error "Define either BUILDING_XMPCORE_AS_STATIC as 1 or BUILDING_XMPCORE_AS_DYNAMIC as 1" + #endif + #endif + + #ifndef LINKING_XMPCORE_LIB + #define LINKING_XMPCORE_LIB 1 + #endif + + namespace AdobeXMPCore {}; +#endif // ENABLE_CPP_DOM_MODEL + +#endif // XMPCoreDefines_h__ diff --git a/xmp/XMPCore/XMPCoreErrorCodes.h b/xmp/XMPCore/XMPCoreErrorCodes.h new file mode 100644 index 0000000..63c2f55 --- /dev/null +++ b/xmp/XMPCore/XMPCoreErrorCodes.h @@ -0,0 +1,116 @@ +#ifndef XMPCoreErrorCodes_h__ +#define XMPCoreErrorCodes_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCore/XMPCoreDefines.h" +#include "XMPCommon/XMPCommonErrorCodes.h" + +namespace AdobeXMPCore { + + typedef enum { + //! Indicates no error. + kDMECNone = 0, + + //! Indicates that IXMPNameSpacePrefixMap has an entry missing. + kDMECNameSpacePrefixMapEntryMissing = 1, + + //! Indicates that a different type of node is present than one user is expecting + kDMECDifferentNodeTypePresent = 2, + + //! Indicates that node is already a child of another parent. + kDMECNodeAlreadyAChild = 3, + + //! Indicates a node with the same qualified name or index already exists. + kDMECNodeAlreadyExists = 4, + + //! Indicates no such node exists. + kDMECNoSuchNodeExists = 5, + + //! Indicates current array element type is not same as that of other child items + kDMECArrayItemTypeDifferent = 6, + + //! Indicates invalid path segment inside a path. + kDMECInvalidPathSegment = 7, + + //! Indicates Bad schema parameter + kDMECBadSchema = 101, + + //! Indicates Bad XPath parameter + kDMECBadXPath = 102, + + //! Indicates Bad options parameter + kDMECBadOptions = 103, + + //! Indicates Bad iteration position + kDMECBadIterPosition = 104, + + //! Indicates Unicode error + kDMECBadUnicode = 105, + + //! Indicates XMP format error + kDMECValidationError = 106, + + //! Indicates Empty iterator + kDMECEmptyIterator = 107, + + //! Maximum value this enum can hold, should be treated as invalid value. + kDMECMaxValue = kMaxEnumValue + } eDataModelErrorCode; + + //! + //! @brief Indicates various errors encountered during parsing. + //! + typedef enum { + //! Indicates no error. + kPECNone = 0, + + //! Indicates XML parsing error. + kPECBadXML = 1, + + //! RDF format error + kPECBadRDF = 2, + + //! XMP format error + kPECBadXMP = 3, + + //! Context Node is invalid + kPECInvalidContextNode = 4, + + //! Context Node is not a composite node + kPECContextNodeIsNonComposite = 5, + + //! Parent of Context Node is not an array node + kPECContextNodeParentIsNonArray = 6, + + //! Maximum value this enum can hold, should be treated as invalid value. + kPECMaxValue = kMaxEnumValue + } eParserErrorCode; + + //! + //! @brief Indicates various errors encountered during serialization. + //! + typedef enum { + //! Indicates no error. + kSECNone = 0, + + //! Indicates serialization failed to achieve size requirement. + kSECSizeExceed = 1, + + //! Indicates un registered namespace encountered during serialization. + kSECUnRegisteredNameSpace = 2, + + //! Maximum value this enum can hold, should be treated as invalid value. + kSECMaxValue = kMaxEnumValue + + } eSerializerErrorCode; +} + +#endif // XMPCoreErrorCodes_h__ diff --git a/xmp/XMPCore/XMPCoreFwdDeclarations.h b/xmp/XMPCore/XMPCoreFwdDeclarations.h new file mode 100644 index 0000000..65a38cc --- /dev/null +++ b/xmp/XMPCore/XMPCoreFwdDeclarations.h @@ -0,0 +1,308 @@ +#ifndef XMPCoreFwdDeclarations_h__ +#define XMPCoreFwdDeclarations_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + + +#include "XMPCore/XMPCoreDefines.h" +#include "XMPCommon/XMPCommonFwdDeclarations.h" +#include "XMPCore/XMPCoreLatestInterfaceVersions.h" + +namespace AdobeXMPCore { + using namespace AdobeXMPCommon; + + // INameSpacePrefixMap + class INameSpacePrefixMap_v1; + typedef INameSpacePrefixMap_v1 INameSpacePrefixMap_base; + typedef INameSpacePrefixMap_v1 * pINameSpacePrefixMap_base; + typedef const INameSpacePrefixMap_v1 * pcINameSpacePrefixMap_base; + typedef BASE_CLASS( INameSpacePrefixMap, INAMESPACEPREFIXMAP_VERSION ) INameSpacePrefixMap; + typedef INameSpacePrefixMap * pINameSpacePrefixMap; + typedef const INameSpacePrefixMap * pcINameSpacePrefixMap; + typedef shared_ptr< INameSpacePrefixMap > spINameSpacePrefixMap; + typedef shared_ptr< const INameSpacePrefixMap > spcINameSpacePrefixMap; + static const uint64 kINameSpacePrefixMapID ( 0x634e5350724d6170 /* cNSPrMap */ ); + + // IPathSegment + class IPathSegment_v1; + typedef IPathSegment_v1 IPathSegment_base; + typedef IPathSegment_v1 * pIPathSegment_base; + typedef const IPathSegment_v1 * pcIPathSegment_base; + typedef BASE_CLASS( IPathSegment, IPATHSEGMENT_VERSION ) IPathSegment; + typedef IPathSegment * pIPathSegment; + typedef const IPathSegment * pcIPathSegment; + typedef shared_ptr< IPathSegment > spIPathSegment; + typedef shared_ptr< const IPathSegment > spcIPathSegment; + static const uint64 kIPathSegmentID ( 0x6350617468536567 /* cPathSeg */ ); + + // IPath + class IPath_v1; + typedef IPath_v1 IPath_base; + typedef IPath_v1 * pIPath_base; + typedef const IPath_v1 * pcIPath_base; + typedef BASE_CLASS( IPath, IPATH_VERSION ) IPath; + typedef IPath * pIPath; + typedef const IPath * pcIPath; + typedef shared_ptr< IPath > spIPath; + typedef shared_ptr< const IPath > spcIPath; + static const uint64 kIPathID ( 0x6350617468202020 /* cPath */ ); + + // INode + class INode_v1; + typedef INode_v1 INode_base; + typedef INode_v1 * pINode_base; + typedef const INode_v1 * pcINode_base; + typedef BASE_CLASS( INode, INODE_VERSION ) INode; + typedef INode * pINode; + typedef const INode * pcINode; + typedef shared_ptr< INode > spINode; + typedef shared_ptr< const INode > spcINode; + static const uint64 kINodeID ( 0x634e6f6465202020 /* cNode */ ); + + // ISimpleNode + class ISimpleNode_v1; + typedef ISimpleNode_v1 ISimpleNode_base; + typedef ISimpleNode_v1 * pISimpleNode_base; + typedef const ISimpleNode_v1 * pcISimpleNode_base; + typedef BASE_CLASS( ISimpleNode, ISIMPLENODE_VERSION ) ISimpleNode; + typedef ISimpleNode * pISimpleNode; + typedef const ISimpleNode * pcISimpleNode; + typedef shared_ptr< ISimpleNode > spISimpleNode; + typedef shared_ptr< const ISimpleNode > spcISimpleNode; + static const uint64 kISimpleNodeID ( 0x63536d6c4e6f6465 /* cSmlNode */ ); + + // ICompositeNode + class ICompositeNode_v1; + typedef ICompositeNode_v1 ICompositeNode_base; + typedef ICompositeNode_v1 * pICompositeNode_base; + typedef const ICompositeNode_v1 * pcICompositeNode_base; + typedef BASE_CLASS( ICompositeNode, ICOMPOSITENODE_VERSION ) ICompositeNode; + typedef ICompositeNode * pICompositeNode; + typedef const ICompositeNode * pcICompositeNode; + typedef shared_ptr< ICompositeNode > spICompositeNode; + typedef shared_ptr< const ICompositeNode > spcICompositeNode; + static const uint64 kICompositeNodeID ( 0x63436d704e6f6465 /* cCmpNode */ ); + + // IStructureNode + class IStructureNode_v1; + typedef IStructureNode_v1 IStructureNode_base; + typedef IStructureNode_v1 * pIStructureNode_base; + typedef const IStructureNode_v1 * pcIStructureNode_base; + typedef BASE_CLASS( IStructureNode, ISTRUCTURENODE_VERSION ) IStructureNode; + typedef IStructureNode * pIStructureNode; + typedef const IStructureNode * pcIStructureNode; + typedef shared_ptr< IStructureNode > spIStructureNode; + typedef shared_ptr< const IStructureNode > spcIStructureNode; + static const uint64 kIStructureNodeID ( 0x635374724e6f6465 /* cStrNode */ ); + + // IArrayNode + class IArrayNode_v1; + typedef IArrayNode_v1 IArrayNode_base; + typedef IArrayNode_v1 * pIArrayNode_base; + typedef const IArrayNode_v1 * pcIArrayNode_base; + typedef BASE_CLASS( IArrayNode, IARRAYNODE_VERSION ) IArrayNode; + typedef IArrayNode * pIArrayNode; + typedef const IArrayNode * pcIArrayNode; + typedef shared_ptr< IArrayNode > spIArrayNode; + typedef shared_ptr< const IArrayNode > spcIArrayNode; + static const uint64 kIArrayNodeID ( 0x634172724e6f6465 /* cArrNode */ ); + + // INodeIterator + class INodeIterator_v1; + typedef INodeIterator_v1 INodeIterator_base; + typedef INodeIterator_v1 * pINodeIterator_base; + typedef const INodeIterator_v1 * pcINodeIterator_base; + typedef BASE_CLASS(INodeIterator, INODEITERATOR_VERSION) INodeIterator; + typedef INodeIterator * pINodeIterator; + typedef const INodeIterator * pcINodeIterator; + typedef shared_ptr< INodeIterator > spINodeIterator; + typedef shared_ptr< const INodeIterator > spcINodeIterator; + static const uint64 kINodeIteratorID (0x634e6f6465497420 /* cNodeIt */); + + // IMetadata + class IMetadata_v1; + typedef IMetadata_v1 IMetadata_base; + typedef IMetadata_v1 * pIMetadata_base; + typedef const IMetadata_v1 * pcIMetadata_base; + typedef BASE_CLASS( IMetadata, IMETADATA_VERSION ) IMetadata; + typedef IMetadata * pIMetadata; + typedef const IMetadata * pcIMetadata; + typedef shared_ptr< IMetadata > spIMetadata; + typedef shared_ptr< const IMetadata > spcIMetadata; + static const uint64 kIMetadataID ( 0x634d657461646174 /* cMetadat */ ); + + // IClientDOMParser + class IClientDOMParser_v1; + typedef IClientDOMParser_v1 IClientDOMParser_base; + typedef IClientDOMParser_v1 * pIClientDOMParser_base; + typedef const IClientDOMParser_v1 * pcIClientDOMParser_base; + typedef BASE_CLASS( IClientDOMParser, ICLIENTDOMPARSER_VERSION ) IClientDOMParser; + typedef IClientDOMParser * pIClientDOMParser; + typedef const IClientDOMParser * pcIClientDOMParser; + + // IClientDOMSerializer + class IClientDOMSerializer_v1; + typedef IClientDOMSerializer_v1 IClientDOMSerializer_base; + typedef IClientDOMSerializer_v1 * pIClientDOMSerializer_base; + typedef const IClientDOMSerializer_v1 * pcIClientDOMSerializer_base; + typedef BASE_CLASS( IClientDOMSerializer, ICLIENTDOMSERIALIZER_VERSION ) IClientDOMSerializer; + typedef IClientDOMSerializer * pIClientDOMSerializer; + typedef const IClientDOMSerializer * pcIClientDOMSerializer; + + // IDOMParser + class IDOMParser_v1; + typedef IDOMParser_v1 IDOMParser_base; + typedef IDOMParser_v1 * pIDOMParser_base; + typedef const IDOMParser_v1 * pcIDOMParser_base; + typedef BASE_CLASS( IDOMParser, IDOMPARSER_VERSION ) IDOMParser; + typedef IDOMParser * pIDOMParser; + typedef const IDOMParser * pcIDOMParser; + typedef shared_ptr< IDOMParser > spIDOMParser; + typedef shared_ptr< const IDOMParser > spcIDOMParser; + static const uint64 kIDOMParserID ( 0x63444f4d50727372 /* cDOMPrsr */ ); + + // IDOMSerializer + class IDOMSerializer_v1; + typedef IDOMSerializer_v1 IDOMSerializer_base; + typedef IDOMSerializer_v1 * pIDOMSerializer_base; + typedef const IDOMSerializer_v1 * pcIDOMSerializer_base; + typedef BASE_CLASS( IDOMSerializer, IDOMSERIALIZER_VERSION ) IDOMSerializer; + typedef IDOMSerializer * pIDOMSerializer; + typedef const IDOMSerializer * pcIDOMSerializer; + typedef shared_ptr< IDOMSerializer > spIDOMSerializer; + typedef shared_ptr< const IDOMSerializer > spcIDOMSerializer; + static const uint64 kIDOMSerializerID ( 0x63444f4d53726c7a /* cDOMSrlz */ ); + + // IDOMImplementationRegistry + class IDOMImplementationRegistry_v1; + typedef IDOMImplementationRegistry_v1 IDOMImplementationRegistry_base; + typedef IDOMImplementationRegistry_v1 * pIDOMImplementationRegistry_base; + typedef const IDOMImplementationRegistry_v1 * pcIDOMImplementationRegistry_base; + typedef BASE_CLASS( IDOMImplementationRegistry, IDOMIMPLEMENTATIONREGISTRY_VERSION) IDOMImplementationRegistry; + typedef IDOMImplementationRegistry * pIDOMImplementationRegistry; + typedef const IDOMImplementationRegistry * pcIDOMImplementationRegistry; + typedef shared_ptr< IDOMImplementationRegistry > spIDOMImplementationRegistry; + typedef shared_ptr< const IDOMImplementationRegistry > spcIDOMImplementationRegistry; + static const uint64 kIDOMImplementationRegistryID ( 0x63444f4d52677374 /* cDOMRgst */ ); + + // ICoreObjectFactory + class ICoreObjectFactory_v1; + typedef ICoreObjectFactory_v1 ICoreObjectFactory_base; + typedef ICoreObjectFactory_v1 * pICoreObjectFactory_base; + typedef const ICoreObjectFactory_v1 * pcICoreObjectFactory_base; + typedef BASE_CLASS( ICoreObjectFactory, ICOREOBJECTFACTORY_VERSION ) ICoreObjectFactory; + typedef ICoreObjectFactory * pICoreObjectFactory; + typedef const ICoreObjectFactory * pcICoreObjectFactory; + static const uint64 kICoreObjectFactoryID ( 0x634f626a46616374 /* cObjFact */ ); + + // ICoreConfigurationManager + class ICoreConfigurationManager_v1; + typedef ICoreConfigurationManager_v1 ICoreConfigurationManager_base; + typedef ICoreConfigurationManager_v1 * pICoreConfigurationManager_base; + typedef const ICoreConfigurationManager_v1 * pcICoreConfigurationManager_base; + typedef BASE_CLASS( ICoreConfigurationManager, ICORECONFIGURATIONMANAGER_VERSION ) ICoreConfigurationManager; + typedef ICoreConfigurationManager * pICoreConfigurationManager; + typedef const ICoreConfigurationManager * pcICoreConfigurationManager; + typedef shared_ptr< ICoreConfigurationManager > spICoreConfigurationManager; + typedef shared_ptr< const ICoreConfigurationManager > spcICoreConfigurationManager; + static const uint64 kICoreConfigurationManagerID ( 0x63436f6e664d6772 /* cConfMgr */ ); + +} + +namespace AdobeXMPCore_Int { + + // INameSpacePrefixMap_I + class INameSpacePrefixMap_I; + typedef INameSpacePrefixMap_I * pINameSpacePrefixMap_I; + typedef const INameSpacePrefixMap_I * pcINameSpacePrefixMap_I; + + // IPathSegment_I + class IPathSegment_I; + typedef IPathSegment_I * pIPathSegment_I; + typedef const IPathSegment_I * pcIPathSegment_I; + + // IPath_I + class IPath_I; + typedef IPath_I * pIPath_I; + typedef const IPath_I * pcIPath_I; + + // INode_I + class INode_I; + typedef INode_I * pINode_I; + typedef const INode_I * pcINode_I; + + // ISimpleNode_I + class ISimpleNode_I; + typedef ISimpleNode_I * pISimpleNode_I; + typedef const ISimpleNode_I * pcISimpleNode_I; + + // ICompositeNode_I + class ICompositeNode_I; + typedef ICompositeNode_I * pICompositeNode_I; + typedef const ICompositeNode_I * pcICompositeNode_I; + + // IStructureNode_I + class IStructureNode_I; + typedef IStructureNode_I * pIStructureNode_I; + typedef const IStructureNode_I * pcIStructureNode_I; + + // IArrayNode_I + class IArrayNode_I; + typedef IArrayNode_I * pIArrayNode_I; + typedef const IArrayNode_I * pcIArrayNode_I; + + // INodeIterator_I + class INodeIterator_I; + typedef INodeIterator_I * pINodeIterator_I; + typedef const INodeIterator_I * pcINodeIterator_I; + + // IMetadata_I + class IMetadata_I; + typedef IMetadata_I * pIMetadata_I; + typedef const IMetadata_I * pcIMetadata_I; + + // IClientDOMParser_I + class IClientDOMParser_I; + typedef IClientDOMParser_I * pIClientDOMParser_I; + typedef const IClientDOMParser_I * pcIClientDOMParser_I; + + // IClientDOMSerializer_I + class IClientDOMSerializer_I; + typedef IClientDOMSerializer_I * pIClientDOMSerializer_I; + typedef const IClientDOMSerializer_I * pcIClientDOMSerializer_I; + + // IDOMParser_I + class IDOMParser_I; + typedef IDOMParser_I * pIDOMParser_I; + typedef const IDOMParser_I * pcIDOMParser_I; + + // IDOMSerializer_I + class IDOMSerializer_I; + typedef IDOMSerializer_I * pIDOMSerializer_I; + typedef const IDOMSerializer_I * pcIDOMSerializer_I; + + // IDOMImplementationRegistry_I + class IDOMImplementationRegistry_I; + typedef IDOMImplementationRegistry_I * pIDOMImplementationRegistry_I; + typedef const IDOMImplementationRegistry_I * pcIDOMImplementationRegistry_I; + + // ICoreObjectFactory_I + class ICoreObjectFactory_I; + typedef ICoreObjectFactory_I * pICoreObjectFactory_I; + typedef const ICoreObjectFactory_I * pcICoreObjectFactory_I; + + // ICoreConfigurationManager_I + class ICoreConfigurationManager_I; + typedef ICoreConfigurationManager_I * pICoreConfigurationManager_I; + typedef const ICoreConfigurationManager_I * pcICoreConfigurationManager_I; +} + +#endif // XMPCoreFwdDeclarations_h__ + diff --git a/xmp/XMPCore/XMPCoreLatestInterfaceVersions.h b/xmp/XMPCore/XMPCoreLatestInterfaceVersions.h new file mode 100644 index 0000000..b5b54bf --- /dev/null +++ b/xmp/XMPCore/XMPCoreLatestInterfaceVersions.h @@ -0,0 +1,92 @@ +#ifndef XMPCoreLatestInterfaceVersions_h__ +#define XMPCoreLatestInterfaceVersions_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +//! +//! @brief Macro to include a client file through with client can control the interface versions he wants to stick with +//! if not the latest ones. +//! +#if !SOURCE_COMPILING_XMPCORE_LIB + #ifdef XMPCORE_CLIENT_VERSION_NUMBER_FILE + #include QUOTEME(XMPCORE_CLIENT_VERSION_NUMBER_FILE) + #endif +#endif + +#ifndef INAMESPACEPREFIXMAP_VERSION + #define INAMESPACEPREFIXMAP_VERSION 1 +#endif + +#ifndef IPATHSEGMENT_VERSION + #define IPATHSEGMENT_VERSION 1 +#endif + +#ifndef IPATH_VERSION + #define IPATH_VERSION 1 +#endif + +#ifndef INODE_VERSION + #define INODE_VERSION 1 +#endif + +#ifndef INODEITERATOR_VERSION + #define INODEITERATOR_VERSION 1 +#endif + +#ifndef ISIMPLENODE_VERSION + #define ISIMPLENODE_VERSION 1 +#endif + +#ifndef ICOMPOSITENODE_VERSION + #define ICOMPOSITENODE_VERSION 1 +#endif + +#ifndef ISTRUCTURENODE_VERSION + #define ISTRUCTURENODE_VERSION 1 +#endif + +#ifndef IARRAYNODE_VERSION + #define IARRAYNODE_VERSION 1 +#endif + +#ifndef IMETADATA_VERSION + #define IMETADATA_VERSION 1 +#endif + +#ifndef ICLIENTDOMPARSER_VERSION + #define ICLIENTDOMPARSER_VERSION 1 +#endif + +#ifndef ICLIENTDOMSERIALIZER_VERSION + #define ICLIENTDOMSERIALIZER_VERSION 1 +#endif + +#ifndef IDOMPARSER_VERSION + #define IDOMPARSER_VERSION 1 +#endif + +#ifndef IDOMSERIALIZER_VERSION + #define IDOMSERIALIZER_VERSION 1 +#endif + +#ifndef IDOMIMPLEMENTATIONREGISTRY_VERSION + #define IDOMIMPLEMENTATIONREGISTRY_VERSION 1 +#endif + +#ifndef ICOREOBJECTFACTORY_VERSION + #define ICOREOBJECTFACTORY_VERSION 1 +#endif + +#ifndef ICORECONFIGURATIONMANAGER_VERSION + #define ICORECONFIGURATIONMANAGER_VERSION 1 +#endif + +#endif // XMPCoreLatestInterfaceVersions_h__ + diff --git a/xmp/XMPCore/source/IArrayNode.cpp b/xmp/XMPCore/source/IArrayNode.cpp new file mode 100644 index 0000000..12fd0da --- /dev/null +++ b/xmp/XMPCore/source/IArrayNode.cpp @@ -0,0 +1,163 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +namespace AdobeXMPCore { + class IArrayNodeProxy; +} + +#define FRIEND_CLASS_DECLARATION() friend class AdobeXMPCore::IArrayNodeProxy; + +#include "XMPCore/Interfaces/IArrayNode.h" + +#if !BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB + +#include "XMPCommon/Utilities/TWrapperFunctions.h" +#include "XMPCore/Interfaces/ICoreObjectFactory.h" + +#include + +namespace AdobeXMPCore { + +#if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4250 ) +#endif + + class IArrayNodeProxy + : public virtual IArrayNode + , public virtual ICompositeNodeProxy + { + private: + pIArrayNode mRawPtr; + + public: + IArrayNodeProxy( pIArrayNode ptr ) + : mRawPtr( ptr ) + , ICompositeNodeProxy( ptr ) + , INodeProxy( ptr ) {} + + ~IArrayNodeProxy() __NOTHROW__ {} + + pIArrayNode APICALL GetActualIArrayNode() __NOTHROW__ { return mRawPtr; } + + void APICALL Acquire() const __NOTHROW__ { assert( false ); } + + void APICALL Release() const __NOTHROW__ { assert( false ); } + + AdobeXMPCommon_Int::pISharedObject_I APICALL GetISharedObject_I() __NOTHROW__ { + return mRawPtr->GetISharedObject_I(); + } + + AdobeXMPCore_Int::pIArrayNode_I APICALL GetIArrayNode_I() __NOTHROW__ { + return mRawPtr->GetIArrayNode_I(); + } + + pvoid APICALL GetInterfacePointer( uint64 interfaceID, uint32 interfaceVersion ) { + return CallSafeFunction< IVersionable, pvoid, pvoid, uint64, uint32 >( + mRawPtr, &IVersionable::getInterfacePointer, interfaceID, interfaceVersion ); + } + + pvoid APICALL getInterfacePointer( uint64 interfaceID, uint32 interfaceVersion, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->getInterfacePointer( interfaceID, interfaceVersion, error ); + } + + virtual eArrayForm APICALL GetArrayForm() const { + return CallConstSafeFunction< IArrayNode_v1, eArrayForm, uint32 >( + mRawPtr, &IArrayNode_v1::getArrayForm ); + } + + virtual eNodeType APICALL GetChildNodeType() const { + return CallConstSafeFunction< IArrayNode_v1, eNodeType, uint32 >( + mRawPtr, &IArrayNode_v1::getChildNodeType ); + } + + virtual spINode APICALL GetNodeAtIndex( sizet index ) { + return CallSafeFunctionReturningPointer< IArrayNode_v1, pINode_base, INode, sizet >( + mRawPtr, &IArrayNode_v1::getNodeAtIndex, index ); + } + + virtual void APICALL InsertNodeAtIndex( const spINode & node, sizet index ) { + return CallSafeFunctionReturningVoid< IArrayNode_v1, pINode_base, sizet >( + mRawPtr, &IArrayNode_v1::insertNodeAtIndex, node ? node->GetActualINode() : NULL, index ); + } + + virtual spINode APICALL RemoveNodeAtIndex( sizet index ) { + return CallSafeFunctionReturningPointer< IArrayNode_v1, pINode_base, INode, sizet >( + mRawPtr, &IArrayNode_v1::removeNodeAtIndex, index ); + } + + virtual spINode APICALL ReplaceNodeAtIndex( const spINode & node, sizet index ) { + return CallSafeFunctionReturningPointer< IArrayNode_v1, pINode_base, INode, pINode_base, sizet >( + mRawPtr, &IArrayNode_v1::replaceNodeAtIndex, node ? node->GetActualINode() :NULL, index ); + } + + virtual uint32 APICALL getArrayForm( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getArrayForm( error ); + } + + virtual pINode_base APICALL getNodeAtIndex( sizet index, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->getNodeAtIndex( index, error ); + } + + virtual void APICALL insertNodeAtIndex( pINode_base node, sizet index, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->insertNodeAtIndex( node, index, error ); + } + + virtual pINode_base APICALL removeNodeAtIndex( sizet index, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->removeNodeAtIndex( index, error ); + } + + virtual pINode_base APICALL replaceNodeAtIndex( pINode_base node, sizet index, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->replaceNodeAtIndex( node, index, error ); + } + + virtual uint32 APICALL getChildNodeType( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getChildNodeType( error ); + } + + }; + +#if XMP_WinBuild + #pragma warning( pop ) +#endif + + spIArrayNode IArrayNode_v1::MakeShared( pIArrayNode_base ptr ) { + if ( !ptr ) return spIArrayNode(); + pIArrayNode p = IArrayNode::GetInterfaceVersion() > 1 ? ptr->GetInterfacePointer< IArrayNode >() : ptr; + return shared_ptr< IArrayNode >( new IArrayNodeProxy( p ) ); + } + + spIArrayNode IArrayNode_v1::CreateUnorderedArrayNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) { + return CallSafeFunctionReturningPointer< ICoreObjectFactory, pIArrayNode_base, IArrayNode, uint32, const char *, sizet, const char *, sizet >( + ICoreObjectFactory::GetCoreObjectFactory(), &ICoreObjectFactory::CreateArrayNode, static_cast< uint32 >( IArrayNode::kAFUnordered ), + nameSpace, nameSpaceLength, name, nameLength ); + } + + spIArrayNode IArrayNode_v1::CreateOrderedArrayNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) { + return CallSafeFunctionReturningPointer< ICoreObjectFactory, pIArrayNode_base, IArrayNode, uint32, const char *, sizet, const char *, sizet >( + ICoreObjectFactory::GetCoreObjectFactory(), &ICoreObjectFactory::CreateArrayNode, static_cast< uint32 >( IArrayNode::kAFOrdered ), + nameSpace, nameSpaceLength, name, nameLength ); + } + + spIArrayNode IArrayNode_v1::CreateAlternativeArrayNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) { + return CallSafeFunctionReturningPointer< ICoreObjectFactory, pIArrayNode_base, IArrayNode, uint32, const char *, sizet, const char *, sizet >( + ICoreObjectFactory::GetCoreObjectFactory(), &ICoreObjectFactory::CreateArrayNode, static_cast< uint32 >( IArrayNode::kAFAlternative ), + nameSpace, nameSpaceLength, name, nameLength ); + } + +} + +#endif // BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB diff --git a/xmp/XMPCore/source/IClientDOMParser.cpp b/xmp/XMPCore/source/IClientDOMParser.cpp new file mode 100644 index 0000000..c476782 --- /dev/null +++ b/xmp/XMPCore/source/IClientDOMParser.cpp @@ -0,0 +1,76 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCore/Interfaces/IClientDOMParser.h" +#include "XMPCore/Interfaces/INode.h" + +#if 1//!BUILDING_XMPCORE_LIB +namespace AdobeXMPCore { + + pINode_base APICALL IClientDOMParser_v1::parse( const char * buffer, sizet bufferLength, pcIConfigurable configurationParameters, ReportErrorAndContinueABISafeProc proc, pcIError_base & error, uint32 & unknownExceptionCaught ) __NOTHROW__ { + unknownExceptionCaught = 0; + error = NULL; + try { + auto node = Parse( buffer, bufferLength, configurationParameters, proc ); + if ( node ) { + node->Acquire(); + return node->GetActualINode(); + } + return NULL; + } catch ( spcIError err ) { + error = err->GetActualIError(); + error->Acquire(); + } catch ( ... ) { + unknownExceptionCaught = 1; + } + return NULL; + } + + uint32 APICALL IClientDOMParser_v1::areKeysCaseSensitive( pcIError_base & error, uint32 & unknownExceptionCaught ) const __NOTHROW__ { + unknownExceptionCaught = 0; + error = NULL; + try { + return AreKeysCaseSensitive() ? 1 : 0; + } catch ( spcIError err ) { + error = err->GetActualIError(); + error->Acquire(); + } catch ( ... ) { + unknownExceptionCaught = 1; + } + return 0; + } + + void APICALL IClientDOMParser_v1::initialize( pIConfigurable configurationParameters, pcIError_base & error, uint32 & unknownExceptionCaught ) __NOTHROW__ { + unknownExceptionCaught = 0; + error = NULL; + try { + Initialize( configurationParameters ); + } catch ( spcIError err ) { + error = err->GetActualIError(); + error->Acquire(); + } catch ( ... ) { + unknownExceptionCaught = 1; + } + } + + uint32 APICALL IClientDOMParser_v1::validate( const uint64 & key, uint32 dataType, const IConfigurable::CombinedDataValue & dataValue, pcIError_base & error, uint32 & unknownExceptionCaught ) __NOTHROW__ { + unknownExceptionCaught = 0; + error = NULL; + try { + return static_cast< uint32 >( Validate( key, static_cast< IConfigurable::eDataType >( dataType ), dataValue ) ); + } catch ( spcIError err ) { + error = err->GetActualIError(); + error->Acquire(); + } catch ( ... ) { + unknownExceptionCaught = 1; + } + return 0; + } +} +#endif // !BUILDING_XMPCORE_LIB diff --git a/xmp/XMPCore/source/IClientDOMSerializer.cpp b/xmp/XMPCore/source/IClientDOMSerializer.cpp new file mode 100644 index 0000000..f1f9f52 --- /dev/null +++ b/xmp/XMPCore/source/IClientDOMSerializer.cpp @@ -0,0 +1,73 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCore/Interfaces/IClientDOMSerializer.h" +#include "XMPCore/Interfaces/INode.h" +#include "XMPCommon/Interfaces/IUTF8String.h" +#include "XMPCore/Interfaces/INameSpacePrefixMap.h" + +#if !BUILDING_XMPCORE_LIB +namespace AdobeXMPCore { + + void APICALL IClientDOMSerializer_v1::serialize( pINode_base node, pcINameSpacePrefixMap_base map, pcIConfigurable configurationParameters, ReportErrorAndContinueABISafeProc proc, pIUTF8String_base string, pcIError_base & error, uint32 & unknownErrorThrown ) __NOTHROW__ { + unknownErrorThrown = 0; + error = NULL; + try { + Serialize( INode::MakeShared( node ), INameSpacePrefixMap::MakeShared( map ), configurationParameters, proc, IUTF8String::MakeShared( string ) ); + } catch ( spcIError err ) { + error = err->GetActualIError(); + error->Acquire(); + } catch ( ... ) { + unknownErrorThrown = 1; + } + } + + uint32 APICALL IClientDOMSerializer_v1::areKeysCaseSensitive( pcIError_base & error, uint32 & unknownExceptionCaught ) const __NOTHROW__ { + unknownExceptionCaught = 0; + error = NULL; + try { + return AreKeysCaseSensitive() ? 1 : 0; + } catch ( spcIError err ) { + error = err->GetActualIError(); + error->Acquire(); + } catch ( ... ) { + unknownExceptionCaught = 1; + } + return 0; + } + + void APICALL IClientDOMSerializer_v1::initialize( pIConfigurable configurationParameters, pcIError_base & error, uint32 & unknownExceptionCaught ) __NOTHROW__ { + unknownExceptionCaught = 0; + error = NULL; + try { + Initialize( configurationParameters ); + } catch ( spcIError err ) { + error = err->GetActualIError(); + error->Acquire(); + } catch ( ... ) { + unknownExceptionCaught = 1; + } + } + + uint32 APICALL IClientDOMSerializer_v1::validate( const uint64 & key, uint32 dataType, const IConfigurable::CombinedDataValue & dataValue, pcIError_base & error, uint32 & unknownExceptionCaught ) __NOTHROW__ { + unknownExceptionCaught = 0; + error = NULL; + try { + return static_cast< uint32 >( Validate( key, static_cast< IConfigurable::eDataType >( dataType ), dataValue ) ); + } catch ( spcIError err ) { + error = err->GetActualIError(); + error->Acquire(); + } catch ( ... ) { + unknownExceptionCaught = 1; + } + return 0; + } + +} +#endif // !BUILDING_XMPCORE_LIB diff --git a/xmp/XMPCore/source/ICompositeNode.cpp b/xmp/XMPCore/source/ICompositeNode.cpp new file mode 100644 index 0000000..603501b --- /dev/null +++ b/xmp/XMPCore/source/ICompositeNode.cpp @@ -0,0 +1,121 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +namespace AdobeXMPCore { + class ICompositeNodeProxy; +} + +#define FRIEND_CLASS_DECLARATION() friend class AdobeXMPCore::ICompositeNodeProxy; + +#include "XMPCore/Interfaces/ICompositeNode.h" + +#if !BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB + +#include "XMPCommon/Utilities/TWrapperFunctions.h" +#include +#include "XMPCore/Interfaces/IPath.h" +#include "XMPCore/Interfaces/INodeIterator.h" + +namespace AdobeXMPCore { + ICompositeNodeProxy::ICompositeNodeProxy( pICompositeNode ptr ) + : mRawPtr( ptr ) + , INodeProxy( ptr ) {} + + ICompositeNodeProxy::~ICompositeNodeProxy() __NOTHROW__ {} + + pICompositeNode APICALL ICompositeNodeProxy::GetActualICompositeNode() __NOTHROW__ { return mRawPtr; } + + AdobeXMPCore_Int::pICompositeNode_I APICALL ICompositeNodeProxy::GetICompositeNode_I() __NOTHROW__ { + return mRawPtr->GetICompositeNode_I(); + } + + INode_v1::eNodeType APICALL ICompositeNodeProxy::GetNodeTypeAtPath( const spcIPath & path ) const { + return CallConstSafeFunction< ICompositeNode_v1, eNodeType, uint32, pcIPath_base >( + mRawPtr, &ICompositeNode_v1::getNodeTypeAtPath, path ? path->GetActualIPath() : NULL ); + } + + spINode APICALL ICompositeNodeProxy::GetNodeAtPath( const spcIPath & path ) { + return CallSafeFunctionReturningPointer< ICompositeNode_v1, pINode_base, INode, pcIPath_base >( + mRawPtr, &ICompositeNode_v1::getNodeAtPath, path ? path->GetActualIPath() : NULL ); + } + + void APICALL ICompositeNodeProxy::AppendNode( const spINode & node ) { + return CallSafeFunctionReturningVoid< ICompositeNode_v1, pINode_base >( + mRawPtr, &ICompositeNode_v1::appendNode, node ? node->GetActualINode() : NULL ); + } + + void APICALL ICompositeNodeProxy::InsertNodeAtPath( const spINode & node, const spcIPath & path ) { + return CallSafeFunctionReturningVoid< ICompositeNode_v1, pINode_base, pcIPath_base >( + mRawPtr, &ICompositeNode_v1::insertNodeAtPath, node ? node->GetActualINode() : NULL, path ? path->GetActualIPath() : NULL ); + } + + spINode APICALL ICompositeNodeProxy::ReplaceNodeAtPath( const spINode & node, const spcIPath & path ) { + return CallSafeFunctionReturningPointer< ICompositeNode_v1, pINode_base, INode, pINode_base, pcIPath_base >( + mRawPtr, &ICompositeNode_v1::replaceNodeAtPath, node ? node->GetActualINode() : NULL, path ? path->GetActualIPath() : NULL ); + } + + spINode APICALL ICompositeNodeProxy::RemoveNodeAtPath( const spcIPath & path ) { + return CallSafeFunctionReturningPointer< ICompositeNode_v1, pINode_base, INode, pcIPath_base >( + mRawPtr, &ICompositeNode_v1::removeNodeAtPath, path ? path->GetActualIPath() : NULL ); + } + + spINodeIterator APICALL ICompositeNodeProxy::Iterator() { + return CallSafeFunctionReturningPointer< ICompositeNode_v1, pINodeIterator_base, INodeIterator >( + mRawPtr, &ICompositeNode_v1::iterator ); + } + + sizet APICALL ICompositeNodeProxy::ChildCount() const __NOTHROW__ { + return mRawPtr->ChildCount(); + } + + uint32 APICALL ICompositeNodeProxy::getNodeTypeAtPath( pcIPath_base path, pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getNodeTypeAtPath( path, error ); + } + + pINode_base APICALL ICompositeNodeProxy::getNodeAtPath( pcIPath_base path, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->getNodeAtPath( path, error ); + } + + void APICALL ICompositeNodeProxy::appendNode( pINode_base node, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->appendNode( node, error ); + } + + void APICALL ICompositeNodeProxy::insertNodeAtPath( pINode_base node, pcIPath_base path, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->insertNodeAtPath( node, path, error ); + } + + pINode_base APICALL ICompositeNodeProxy::replaceNodeAtPath( pINode_base node, pcIPath_base path, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->replaceNodeAtPath( node, path, error ); + } + + pINode_base APICALL ICompositeNodeProxy::removeNodeAtPath( pcIPath_base path, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->removeNodeAtPath( path, error ); + } + + pINodeIterator_base APICALL ICompositeNodeProxy::iterator( pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->iterator( error ); + } + + spICompositeNode ICompositeNode_v1::MakeShared( pICompositeNode_base ptr ) { + if ( !ptr ) return spICompositeNode(); + pICompositeNode p = ICompositeNode::GetInterfaceVersion() > 1 ? + ptr->GetInterfacePointer< ICompositeNode >() : ptr; + return shared_ptr< ICompositeNode >( new ICompositeNodeProxy( p ) ); + } + +} + +#endif // BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB diff --git a/xmp/XMPCore/source/ICoreConfigurationManager.cpp b/xmp/XMPCore/source/ICoreConfigurationManager.cpp new file mode 100644 index 0000000..af4f74c --- /dev/null +++ b/xmp/XMPCore/source/ICoreConfigurationManager.cpp @@ -0,0 +1,88 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +namespace AdobeXMPCore { + class ICoreConfigurationManagerProxy; +} + +#define FRIEND_CLASS_DECLARATION() friend class AdobeXMPCore::ICoreConfigurationManagerProxy; + +#include "XMPCore/Interfaces/ICoreConfigurationManager.h" + +#if !BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB + +#include "XMPCommon/Utilities/TWrapperFunctions.h" +#include "XMPCore/Interfaces/ICoreObjectFactory.h" +#include + +namespace AdobeXMPCore { + +#if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4250 ) +#endif + + class ICoreConfigurationManagerProxy + : public virtual ICoreConfigurationManager + , public virtual IConfigurationManagerProxy + { + private: + pICoreConfigurationManager mRawPtr; + + public: + ICoreConfigurationManagerProxy( pICoreConfigurationManager ptr ) + : IConfigurationManagerProxy( ptr ) + , mRawPtr( ptr ) {} + + ~ICoreConfigurationManagerProxy() __NOTHROW__ {} + + pICoreConfigurationManager APICALL GetActualICoreConfigurationManager() __NOTHROW__ { return mRawPtr; } + + void APICALL Acquire() const __NOTHROW__ { assert( false ); } + + void APICALL Release() const __NOTHROW__ { assert( false ); } + + AdobeXMPCommon_Int::pISharedObject_I APICALL GetISharedObject_I() __NOTHROW__ { + return mRawPtr->GetISharedObject_I(); + } + + AdobeXMPCore_Int::pICoreConfigurationManager_I APICALL GetICoreConfigurationManager_I() __NOTHROW__ { + return mRawPtr->GetICoreConfigurationManager_I(); + } + + pvoid APICALL GetInterfacePointer( uint64 interfaceID, uint32 interfaceVersion ) { + return CallSafeFunction< IVersionable, pvoid, pvoid, uint64, uint32 >( + mRawPtr, &IVersionable::getInterfacePointer, interfaceID, interfaceVersion ); + } + + pvoid APICALL getInterfacePointer( uint64 interfaceID, uint32 interfaceVersion, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->getInterfacePointer( interfaceID, interfaceVersion, error ); + } + + }; + + spICoreConfigurationManager ICoreConfigurationManager_v1::MakeShared( pICoreConfigurationManager_base ptr ) { + if ( !ptr ) return spICoreConfigurationManager(); + pICoreConfigurationManager p = ICoreConfigurationManager::GetInterfaceVersion() > 1 ? ptr->GetInterfacePointer< ICoreConfigurationManager >() : ptr; + return shared_ptr< ICoreConfigurationManager >( new ICoreConfigurationManagerProxy( p ) ); + } + + spICoreConfigurationManager ICoreConfigurationManager_v1::GetCoreConfigurationManager() { + return CallSafeFunctionReturningPointer< ICoreObjectFactory, pICoreConfigurationManager_base, ICoreConfigurationManager >( + ICoreObjectFactory::GetCoreObjectFactory(), &ICoreObjectFactory::GetCoreConfigurationManager ); + } + +#if XMP_WinBuild + #pragma warning( pop ) +#endif + +} + +#endif // !BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB diff --git a/xmp/XMPCore/source/ICoreObjectFactory.cpp b/xmp/XMPCore/source/ICoreObjectFactory.cpp new file mode 100644 index 0000000..b7e12e8 --- /dev/null +++ b/xmp/XMPCore/source/ICoreObjectFactory.cpp @@ -0,0 +1,83 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPCore/Interfaces/ICoreObjectFactory.h" + +#if !BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB + +#include "XMPCommon/Utilities/TWrapperFunctions.h" +#include + +#if LINKING_XMPCORE_LIB + extern "C" AdobeXMPCore::pICoreObjectFactory_base WXMPMeta_GetXMPDOMFactoryInstance_1(); +#endif + +namespace AdobeXMPCore { + + pICoreObjectFactory ICoreObjectFactory_v1::MakeCoreObjectFactory( pICoreObjectFactory_base ptr ) { + if ( ICoreObjectFactory::GetInterfaceVersion() == 1 ) + return ptr; + else + return ptr->GetInterfacePointer< ICoreObjectFactory >(); + } + +#if LINKING_XMPCORE_LIB + static pICoreObjectFactory ManageCoreObjectFactory( bool destroy = false ) { + static pICoreObjectFactory sCoreObjectFactoryPtr( NULL ); + if ( destroy && sCoreObjectFactoryPtr ) { + sCoreObjectFactoryPtr = NULL; + return sCoreObjectFactoryPtr; + } + + if ( !sCoreObjectFactoryPtr ) { + if ( ICoreObjectFactory::GetInterfaceVersion() != 1 ) + sCoreObjectFactoryPtr = WXMPMeta_GetXMPDOMFactoryInstance_1()->GetInterfacePointer< ICoreObjectFactory >(); + else + sCoreObjectFactoryPtr = WXMPMeta_GetXMPDOMFactoryInstance_1(); + } + return sCoreObjectFactoryPtr; + } + + + void ICoreObjectFactory_v1::SetupCoreObjectFactory() { + ManageCoreObjectFactory(); + } +#else + static pICoreObjectFactory ManageCoreObjectFactory( bool destroy = false, pICoreObjectFactory_base coreObjectFactory = NULL ) { + static pICoreObjectFactory sCoreObjectFactoryPtr( NULL ); + if ( destroy && sCoreObjectFactoryPtr ) { + sCoreObjectFactoryPtr = NULL; + return sCoreObjectFactoryPtr; + } + + if ( !sCoreObjectFactoryPtr && coreObjectFactory ) { + if ( ICoreObjectFactory::GetInterfaceVersion() != 1 ) + sCoreObjectFactoryPtr = coreObjectFactory->GetInterfacePointer< ICoreObjectFactory >(); + else + sCoreObjectFactoryPtr = coreObjectFactory; + } + return sCoreObjectFactoryPtr; + } + + void ICoreObjectFactory_v1::SetupCoreObjectFactory( pICoreObjectFactory_base coreObjectFactory ) { + ManageCoreObjectFactory( false, coreObjectFactory ); + } +#endif + + pICoreObjectFactory ICoreObjectFactory_v1::GetCoreObjectFactory() { + return ManageCoreObjectFactory(); + } + + void ICoreObjectFactory_v1::DestroyCoreObjectFactory() { + ManageCoreObjectFactory( true ); + } + +} + +#endif // BUILDING_XMPCORE_LIB diff --git a/xmp/XMPCore/source/IDOMImplementationRegistry.cpp b/xmp/XMPCore/source/IDOMImplementationRegistry.cpp new file mode 100644 index 0000000..35934e2 --- /dev/null +++ b/xmp/XMPCore/source/IDOMImplementationRegistry.cpp @@ -0,0 +1,122 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +namespace AdobeXMPCore { + class IDOMImplementationRegistryProxy; +} + +#define FRIEND_CLASS_DECLARATION() friend class AdobeXMPCore::IDOMImplementationRegistryProxy; + +#include "XMPCore/Interfaces/IDOMImplementationRegistry.h" + +#if !BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB + +#include "XMPCommon/Utilities/TWrapperFunctions.h" +#include +#include "XMPCore/Interfaces/IDOMParser.h" +#include "XMPCore/Interfaces/IDOMSerializer.h" +#include "XMPCore/Interfaces/ICoreObjectFactory.h" + +namespace AdobeXMPCore { + + class IDOMImplementationRegistryProxy + : public virtual IDOMImplementationRegistry + { + private: + pIDOMImplementationRegistry mRawPtr; + + public: + IDOMImplementationRegistryProxy( pIDOMImplementationRegistry ptr ) + : mRawPtr( ptr ) + { + mRawPtr->Acquire(); + } + + ~IDOMImplementationRegistryProxy() __NOTHROW__ { mRawPtr->Release(); } + + pIDOMImplementationRegistry APICALL GetActualIDOMImplementationRegistry() __NOTHROW__ { return mRawPtr; } + + void APICALL Acquire() const __NOTHROW__ { assert( false ); } + + void APICALL Release() const __NOTHROW__ { assert( false ); } + + AdobeXMPCommon_Int::pISharedObject_I APICALL GetISharedObject_I() __NOTHROW__ { + return mRawPtr->GetISharedObject_I(); + } + + AdobeXMPCore_Int::pIDOMImplementationRegistry_I APICALL GetIDOMImplementationRegistry_I() __NOTHROW__ { + return mRawPtr->GetIDOMImplementationRegistry_I(); + } + + pvoid APICALL GetInterfacePointer( uint64 interfaceID, uint32 interfaceVersion ) { + return CallSafeFunction< IVersionable, pvoid, pvoid, uint64, uint32 >( + mRawPtr, &IVersionable::getInterfacePointer, interfaceID, interfaceVersion ); + } + + pvoid APICALL getInterfacePointer( uint64 interfaceID, uint32 interfaceVersion, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->getInterfacePointer( interfaceID, interfaceVersion, error ); + } + + virtual spIDOMParser APICALL GetParser( const char * key ) const { + return CallConstSafeFunctionReturningPointer< IDOMImplementationRegistry, pIDOMParser_base, IDOMParser, const char * >( + mRawPtr, &IDOMImplementationRegistry::getParser, key ); + } + + virtual spIDOMSerializer APICALL GetSerializer( const char * key ) const { + return CallConstSafeFunctionReturningPointer< IDOMImplementationRegistry, pIDOMSerializer_base, IDOMSerializer, const char * >( + mRawPtr, &IDOMImplementationRegistry::getSerializer, key ); + } + + virtual bool APICALL RegisterParser( const char * key, pIClientDOMParser_base parser ) { + return CallSafeFunction< IDOMImplementationRegistry, bool, uint32, const char *, pIClientDOMParser_base >( + mRawPtr, &IDOMImplementationRegistry::registerParser, key, parser ); + } + + virtual bool APICALL RegisterSerializer( const char * key, pIClientDOMSerializer_base serializer ) { + return CallSafeFunction< IDOMImplementationRegistry, bool, uint32, const char *, pIClientDOMSerializer_base >( + mRawPtr, &IDOMImplementationRegistry::registerSerializer, key, serializer ); + } + + virtual pIDOMParser_base APICALL getParser( const char * key, pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getParser( key, error ); + } + + virtual pIDOMSerializer_base APICALL getSerializer( const char * key, pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getSerializer( key, error ); + } + + virtual uint32 APICALL registerParser( const char * key, pIClientDOMParser_base parser, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->registerParser( key, parser, error ); + } + + virtual uint32 APICALL registerSerializer( const char * key, pIClientDOMSerializer_base serializer, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->registerSerializer( key, serializer, error ); + } + + }; + + spIDOMImplementationRegistry IDOMImplementationRegistry_v1::GetDOMImplementationRegistry() { + return CallSafeFunctionReturningPointer< ICoreObjectFactory, pIDOMImplementationRegistry_base, IDOMImplementationRegistry >( + ICoreObjectFactory::GetCoreObjectFactory(), &ICoreObjectFactory::GetDOMImplementationRegistry ); + } + + spIDOMImplementationRegistry IDOMImplementationRegistry_v1::MakeShared( pIDOMImplementationRegistry_base ptr ) { + if ( !ptr ) return spIDOMImplementationRegistry(); + pIDOMImplementationRegistry p = IDOMImplementationRegistry::GetInterfaceVersion() > 1 ? ptr->GetInterfacePointer< IDOMImplementationRegistry >() : ptr; + return shared_ptr< IDOMImplementationRegistry >( new IDOMImplementationRegistryProxy( p ) ); + } + +} + +#endif // BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB diff --git a/xmp/XMPCore/source/IDOMParser.cpp b/xmp/XMPCore/source/IDOMParser.cpp new file mode 100644 index 0000000..a077a5c --- /dev/null +++ b/xmp/XMPCore/source/IDOMParser.cpp @@ -0,0 +1,116 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +namespace AdobeXMPCore { + class IDOMParserProxy; +} + +#define FRIEND_CLASS_DECLARATION() friend class AdobeXMPCore::IDOMParserProxy; + +#include "XMPCore/Interfaces/IDOMParser.h" + +#if !BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB + +#include "XMPCommon/Utilities/TWrapperFunctions.h" +#include +#include "XMPCore/Interfaces/IMetadata.h" + +namespace AdobeXMPCore { + +#if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4250 ) +#endif + + class IDOMParserProxy + : public virtual IDOMParser + , public virtual IConfigurableProxy + { + private: + pIDOMParser mRawPtr; + + public: + IDOMParserProxy( pIDOMParser ptr ) + : IConfigurableProxy( ptr ) + , mRawPtr( ptr ) + { + mRawPtr->Acquire(); + } + + ~IDOMParserProxy() __NOTHROW__ { mRawPtr->Release(); } + + pIDOMParser APICALL GetActualIDOMParser() __NOTHROW__ { return mRawPtr; } + + void APICALL Acquire() const __NOTHROW__ { assert( false ); } + + void APICALL Release() const __NOTHROW__ { assert( false ); } + + AdobeXMPCommon_Int::pISharedObject_I APICALL GetISharedObject_I() __NOTHROW__ { + return mRawPtr->GetISharedObject_I(); + } + + AdobeXMPCore_Int::pIDOMParser_I APICALL GetIDOMParser_I() __NOTHROW__ { + return mRawPtr->GetIDOMParser_I(); + } + + pvoid APICALL GetInterfacePointer( uint64 interfaceID, uint32 interfaceVersion ) { + return CallSafeFunction< IVersionable, pvoid, pvoid, uint64, uint32 >( + mRawPtr, &IVersionable::getInterfacePointer, interfaceID, interfaceVersion ); + } + + pvoid APICALL getInterfacePointer( uint64 interfaceID, uint32 interfaceVersion, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->getInterfacePointer( interfaceID, interfaceVersion, error ); + } + + virtual spIDOMParser APICALL Clone() const { + return CallConstSafeFunctionReturningPointer< IDOMParser, pIDOMParser_base, IDOMParser >( + mRawPtr, &IDOMParser::clone ); + } + + virtual spIMetadata APICALL Parse( const char * buffer, sizet bufferLength ) { + return CallSafeFunctionReturningPointer< IDOMParser, pIMetadata_base, IMetadata, const char *, sizet >( + mRawPtr, &IDOMParser::parse, buffer, bufferLength ); + } + + virtual void APICALL ParseWithSpecificAction( const char * buffer, sizet bufferLength, eActionType actionType, spINode & node ) { + return CallSafeFunctionReturningVoid< IDOMParser, const char *, sizet, uint32, pINode_base >( + mRawPtr, &IDOMParser::parseWithSpecificAction, buffer, bufferLength, static_cast< uint32 >( actionType ), node ? node->GetActualINode() : NULL ); + } + + virtual pIDOMParser_base APICALL clone( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->clone( error ); + } + + virtual pIMetadata_base APICALL parse( const char * buffer, sizet bufferLength, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->parse( buffer, bufferLength, error ); + } + + virtual void APICALL parseWithSpecificAction( const char * buffer, sizet bufferLength, uint32 actionType, pINode_base node, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->parseWithSpecificAction( buffer, bufferLength, actionType, node, error ); + } + + }; + +#if XMP_WinBuild + #pragma warning( pop ) +#endif + + spIDOMParser IDOMParser_v1::MakeShared( pIDOMParser_base ptr ) { + if ( !ptr ) return spIDOMParser(); + pIDOMParser p = IDOMParser::GetInterfaceVersion() > 1 ? ptr->GetInterfacePointer< IDOMParser >() : ptr; + return shared_ptr< IDOMParser >( new IDOMParserProxy( p ) ); + } + +} + +#endif // BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB diff --git a/xmp/XMPCore/source/IDOMSerializer.cpp b/xmp/XMPCore/source/IDOMSerializer.cpp new file mode 100644 index 0000000..481477a --- /dev/null +++ b/xmp/XMPCore/source/IDOMSerializer.cpp @@ -0,0 +1,109 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +namespace AdobeXMPCore { + class IDOMSerializerProxy; +} + +#define FRIEND_CLASS_DECLARATION() friend class AdobeXMPCore::IDOMSerializerProxy; + +#include "XMPCore/Interfaces/IDOMSerializer.h" + +#if !BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB + +#include "XMPCommon/Utilities/TWrapperFunctions.h" +#include "XMPCommon/Interfaces/IUTF8String.h" +#include "XMPCore/Interfaces/INode.h" +#include "XMPCore/Interfaces/INameSpacePrefixMap.h" + +#include + +namespace AdobeXMPCore { + +#if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4250 ) +#endif + + class IDOMSerializerProxy + : public virtual IDOMSerializer + , public virtual IConfigurableProxy + { + private: + pIDOMSerializer mRawPtr; + + public: + IDOMSerializerProxy( pIDOMSerializer ptr ) + : IConfigurableProxy( ptr ) + , mRawPtr( ptr ) + { + mRawPtr->Acquire(); + } + + ~IDOMSerializerProxy() __NOTHROW__ { mRawPtr->Release(); } + + pIDOMSerializer APICALL GetActualIDOMSerializer() __NOTHROW__ { return mRawPtr; } + + void APICALL Acquire() const __NOTHROW__ { assert( false ); } + + void APICALL Release() const __NOTHROW__ { assert( false ); } + + AdobeXMPCommon_Int::pISharedObject_I APICALL GetISharedObject_I() __NOTHROW__ { + return mRawPtr->GetISharedObject_I(); + } + + AdobeXMPCore_Int::pIDOMSerializer_I APICALL GetIDOMSerializer_I() __NOTHROW__ { + return mRawPtr->GetIDOMSerializer_I(); + } + + pvoid APICALL GetInterfacePointer( uint64 interfaceID, uint32 interfaceVersion ) { + return CallSafeFunction< IVersionable, pvoid, pvoid, uint64, uint32 >( + mRawPtr, &IVersionable::getInterfacePointer, interfaceID, interfaceVersion ); + } + + pvoid APICALL getInterfacePointer( uint64 interfaceID, uint32 interfaceVersion, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->getInterfacePointer( interfaceID, interfaceVersion, error ); + } + + virtual spIDOMSerializer APICALL Clone() const { + return CallConstSafeFunctionReturningPointer< IDOMSerializer, pIDOMSerializer_base, IDOMSerializer >( + mRawPtr, &IDOMSerializer_v1::clone ); + } + + virtual spIUTF8String APICALL Serialize( const spINode & node, const spcINameSpacePrefixMap & map ) { + return CallSafeFunctionReturningPointer< IDOMSerializer, pIUTF8String_base, IUTF8String, pINode_base, pcINameSpacePrefixMap_base >( + mRawPtr, &IDOMSerializer_v1::serialize, node ? node->GetActualINode() : NULL , map ? map->GetActualINameSpacePrefixMap() : NULL ); + } + + virtual pIDOMSerializer_base APICALL clone( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->clone( error ); + } + + virtual pIUTF8String_base APICALL serialize( pINode_base node, pcINameSpacePrefixMap_base map, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->serialize( node, map, error ); + } + + }; + +#if XMP_WinBuild + #pragma warning( pop ) +#endif + + spIDOMSerializer IDOMSerializer_v1::MakeShared( pIDOMSerializer_base ptr ) { + if ( !ptr ) return spIDOMSerializer(); + pIDOMSerializer p = ptr->GetInterfacePointer< IDOMSerializer >(); + return shared_ptr< IDOMSerializer >( new IDOMSerializerProxy( p ) ); + } + +} + +#endif // !BUILDING_XMPCORE_LIB diff --git a/xmp/XMPCore/source/IMetadata.cpp b/xmp/XMPCore/source/IMetadata.cpp new file mode 100644 index 0000000..9b4eef5 --- /dev/null +++ b/xmp/XMPCore/source/IMetadata.cpp @@ -0,0 +1,113 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +namespace AdobeXMPCore { + class IMetadataProxy; +} + +#define FRIEND_CLASS_DECLARATION() friend class AdobeXMPCore::IMetadataProxy; + +#include "XMPCore/Interfaces/IMetadata.h" + +#if !BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB + +#include "XMPCommon/Utilities/TWrapperFunctions.h" +#include "XMPCommon/Interfaces/IUTF8String.h" +#include "XMPCore/Interfaces/ICoreObjectFactory.h" + +#include + +namespace AdobeXMPCore { + +#if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4250 ) +#endif + + class IMetadataProxy + : public virtual IMetadata + , public virtual IStructureNodeProxy + { + private: + pIMetadata mRawPtr; + + public: + IMetadataProxy( pIMetadata ptr ) + : mRawPtr( ptr ) + , IStructureNodeProxy( ptr ) + , ICompositeNodeProxy( ptr ) + , INodeProxy( ptr ) {} + + ~IMetadataProxy() __NOTHROW__ {} + + pIMetadata APICALL GetActualIMetadata() __NOTHROW__ { return mRawPtr; } + + void APICALL Acquire() const __NOTHROW__ { assert( false ); } + + void APICALL Release() const __NOTHROW__ { assert( false ); } + + AdobeXMPCommon_Int::pISharedObject_I APICALL GetISharedObject_I() __NOTHROW__ { + return mRawPtr->GetISharedObject_I(); + } + + AdobeXMPCore_Int::pIMetadata_I APICALL GetIMetadata_I() __NOTHROW__ { + return mRawPtr->GetIMetadata_I(); + } + + pvoid APICALL GetInterfacePointer( uint64 interfaceID, uint32 interfaceVersion ) { + return CallSafeFunction< IVersionable, pvoid, pvoid, uint64, uint32 >( + mRawPtr, &IVersionable::getInterfacePointer, interfaceID, interfaceVersion ); + } + + pvoid APICALL getInterfacePointer( uint64 interfaceID, uint32 interfaceVersion, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->getInterfacePointer( interfaceID, interfaceVersion, error ); + } + + virtual spcIUTF8String APICALL GetAboutURI() const { + return CallConstSafeFunctionReturningPointer< IMetadata_v1, pcIUTF8String_base, const IUTF8String >( + mRawPtr, &IMetadata_v1::getAboutURI ); + } + + virtual void APICALL SetAboutURI( const char * uri, sizet uriLength ) __NOTHROW__ { + mRawPtr->SetAboutURI( uri, uriLength ); + } + + virtual pcIUTF8String_base APICALL getAboutURI( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getAboutURI( error ); + } + + virtual void APICALL EnableFeature( const char * key, sizet keyLength ) const __NOTHROW__ { + return mRawPtr->EnableFeature( key, keyLength ); + } + + virtual void APICALL DisableFeature( const char * key, sizet keyLength ) const __NOTHROW__ { + return mRawPtr->DisableFeature( key, keyLength ); + } + }; + +#if XMP_WinBuild + #pragma warning( pop ) +#endif + + spIMetadata IMetadata_v1::MakeShared( pIMetadata_base ptr ) { + if ( !ptr ) return spIMetadata(); + pIMetadata p = IMetadata::GetInterfaceVersion() > 1 ? ptr->GetInterfacePointer< IMetadata >() : ptr; + return shared_ptr< IMetadata >( new IMetadataProxy( p ) ); + } + + spIMetadata IMetadata_v1::CreateMetadata() { + return CallSafeFunctionReturningPointer< ICoreObjectFactory, pIMetadata, IMetadata >( + ICoreObjectFactory::GetCoreObjectFactory(), &ICoreObjectFactory::CreateMetadata ); + } + +} + +#endif // BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB diff --git a/xmp/XMPCore/source/INameSpacePrefixMap.cpp b/xmp/XMPCore/source/INameSpacePrefixMap.cpp new file mode 100644 index 0000000..8dee5b7 --- /dev/null +++ b/xmp/XMPCore/source/INameSpacePrefixMap.cpp @@ -0,0 +1,187 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +namespace AdobeXMPCore { + class INameSpacePrefixMapProxy; +} + +#define FRIEND_CLASS_DECLARATION() friend class AdobeXMPCore::INameSpacePrefixMapProxy; + +#include "XMPCore/Interfaces/INameSpacePrefixMap.h" +#include "XMPCore/Interfaces/ICoreObjectFactory.h" + +#if !BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB + +#include "XMPCommon/Utilities/TWrapperFunctions.h" +#include "XMPCommon/Interfaces/IUTF8String.h" + +#include + +namespace AdobeXMPCore { + + + bool INameSpacePrefixMap::IsEmpty() const __NOTHROW__{ + return this->Size() == 0; + } + + class INameSpacePrefixMapProxy + : public virtual INameSpacePrefixMap + { + private: + pINameSpacePrefixMap mRawPtr; + + public: + INameSpacePrefixMapProxy( pINameSpacePrefixMap ptr ) + : mRawPtr( ptr ) + { + mRawPtr->Acquire(); + } + + ~INameSpacePrefixMapProxy() __NOTHROW__ { mRawPtr->Release(); } + + pINameSpacePrefixMap APICALL GetActualINameSpacePrefixMap() __NOTHROW__ { return mRawPtr; } + + void APICALL Acquire() const __NOTHROW__ { assert( false ); } + + void APICALL Release() const __NOTHROW__ { assert( false ); } + + AdobeXMPCommon_Int::pISharedObject_I APICALL GetISharedObject_I() __NOTHROW__ { + return mRawPtr->GetISharedObject_I(); + } + + AdobeXMPCore_Int::pINameSpacePrefixMap_I APICALL GetINameSpacePrefixMap_I() __NOTHROW__ { + return mRawPtr->GetINameSpacePrefixMap_I(); + } + + pvoid APICALL GetInterfacePointer( uint64 interfaceID, uint32 interfaceVersion ) { + return CallSafeFunction< IVersionable, pvoid, pvoid, uint64, uint32 >( + mRawPtr, &IVersionable::getInterfacePointer, interfaceID, interfaceVersion ); + } + + pvoid APICALL getInterfacePointer( uint64 interfaceID, uint32 interfaceVersion, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->getInterfacePointer( interfaceID, interfaceVersion, error ); + } + + virtual bool APICALL Insert( const char * prefix, sizet prefixLength, const char * nameSpace, sizet nameSpaceLength ) { + return CallSafeFunction< INameSpacePrefixMap_v1, bool, uint32, const char *, sizet, const char *, sizet >( + mRawPtr, &INameSpacePrefixMap_v1::insert, prefix, prefixLength, nameSpace, nameSpaceLength ); + } + + virtual uint32 APICALL insert( const char * prefix, sizet prefixLength, const char * nameSpace, sizet nameSpaceLength, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->insert( prefix, prefixLength, nameSpace, nameSpaceLength, error ); + } + + virtual bool APICALL RemovePrefix( const char * prefix, sizet prefixLength ) { + return CallSafeFunction< INameSpacePrefixMap_v1, bool, uint32, uint32, const char *, sizet >( + mRawPtr, &INameSpacePrefixMap_v1::remove, kPrefixIsParameter, prefix, prefixLength ); + } + + virtual bool APICALL RemoveNameSpace( const char * nameSpace, sizet nameSpaceLength ) { + return CallSafeFunction< INameSpacePrefixMap_v1, bool, uint32, uint32, const char *, sizet >( + mRawPtr, &INameSpacePrefixMap_v1::remove, kNameSpaceIsParameter, nameSpace, nameSpaceLength ); + } + + virtual bool APICALL IsPrefixPresent( const char * prefix, sizet prefixLength ) const { + return CallConstSafeFunction< INameSpacePrefixMap_v1, bool, uint32, uint32, const char *, sizet >( + mRawPtr, &INameSpacePrefixMap_v1::isPresent, kPrefixIsParameter, prefix, prefixLength ); + } + + virtual bool APICALL IsNameSpacePresent( const char * nameSpace, sizet nameSpaceLength ) const { + return CallConstSafeFunction< INameSpacePrefixMap_v1, bool, uint32, uint32, const char *, sizet >( + mRawPtr, &INameSpacePrefixMap_v1::isPresent, kNameSpaceIsParameter, nameSpace, nameSpaceLength ); + } + + virtual spcIUTF8String APICALL GetNameSpace( const char * prefix, sizet prefixLength ) const { + return CallConstSafeFunctionReturningPointer< INameSpacePrefixMap_v1, pcIUTF8String_base, const IUTF8String, uint32, const char *, sizet >( + mRawPtr, &INameSpacePrefixMap_v1::get, kPrefixIsParameter, prefix, prefixLength ); + } + + virtual spcIUTF8String APICALL GetPrefix( const char * nameSpace, sizet nameSpaceLength ) const { + return CallConstSafeFunctionReturningPointer< INameSpacePrefixMap_v1, pcIUTF8String_base, const IUTF8String, uint32, const char *, sizet >( + mRawPtr, &INameSpacePrefixMap_v1::get, kNameSpaceIsParameter, nameSpace, nameSpaceLength ); + } + + virtual sizet APICALL Size() const __NOTHROW__ { + return mRawPtr->Size(); + } + + virtual void APICALL Clear() __NOTHROW__ { + return mRawPtr->Clear(); + } + + virtual spINameSpacePrefixMap APICALL Clone() const { + return CallConstSafeFunctionReturningPointer< INameSpacePrefixMap_v1, pINameSpacePrefixMap_base, INameSpacePrefixMap >( + mRawPtr, &INameSpacePrefixMap_v1::clone ); + } + + virtual pINameSpacePrefixMap_base APICALL clone( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->clone( error ); + } + + virtual uint32 APICALL remove( uint32 keyType, const char * key, sizet keyLength, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->remove( keyType, key, keyLength, error ); + } + + virtual uint32 APICALL isPresent( uint32 keyType, const char * key, sizet keyLength, pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->isPresent( keyType, key, keyLength, error ); + } + + virtual pcIUTF8String_base APICALL get( uint32 keyType, const char * key, sizet keyLength, pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->get( keyType, key, keyLength, error ); + } + + virtual void APICALL EnableThreadSafety() const __NOTHROW__ { + return mRawPtr->EnableThreadSafety(); + } + + virtual void APICALL DisableThreadSafety() const __NOTHROW__ { + return mRawPtr->DisableThreadSafety(); + } + + virtual bool APICALL IsThreadSafe() const { + return mRawPtr->isThreadSafe() != 0; + } + + virtual uint32 APICALL isThreadSafe( ) const __NOTHROW__ { + assert( false ); + return mRawPtr->isThreadSafe(); + } + + virtual AdobeXMPCommon_Int::pIThreadSafe_I APICALL GetIThreadSafe_I() __NOTHROW__ override { + return mRawPtr->GetIThreadSafe_I(); + } + + }; + + spINameSpacePrefixMap INameSpacePrefixMap_v1::MakeShared( pINameSpacePrefixMap_base ptr ) { + if ( !ptr ) return spINameSpacePrefixMap(); + pINameSpacePrefixMap p = INameSpacePrefixMap::GetInterfaceVersion() > 1 ? + ptr->GetInterfacePointer< INameSpacePrefixMap >() : ptr; + return shared_ptr< INameSpacePrefixMap >( new INameSpacePrefixMapProxy( p ) ); + } + + spINameSpacePrefixMap INameSpacePrefixMap_v1::CreateNameSpacePrefixMap() { + return CallSafeFunctionReturningPointer< ICoreObjectFactory, pINameSpacePrefixMap_base, INameSpacePrefixMap >( + ICoreObjectFactory::GetCoreObjectFactory(), &ICoreObjectFactory::CreateNameSpacePrefixMap ); + } + + spcINameSpacePrefixMap INameSpacePrefixMap_v1::GetDefaultNameSpacePrefixMap() { + return CallSafeFunctionReturningPointer< ICoreObjectFactory, pcINameSpacePrefixMap_base, const INameSpacePrefixMap >( + ICoreObjectFactory::GetCoreObjectFactory(), &ICoreObjectFactory::GetDefaultNameSpacePrefixMap ); + } + +} + +#endif // BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB diff --git a/xmp/XMPCore/source/INode.cpp b/xmp/XMPCore/source/INode.cpp new file mode 100644 index 0000000..9d0126c --- /dev/null +++ b/xmp/XMPCore/source/INode.cpp @@ -0,0 +1,377 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +namespace AdobeXMPCore { + class INodeProxy; +} + +#define FRIEND_CLASS_DECLARATION() friend class AdobeXMPCore::INodeProxy; + +#include "XMPCore/Interfaces/INode.h" + +#if !BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB + +#include "XMPCommon/Utilities/TWrapperFunctions.h" +#include "XMPCommon/Interfaces/IUTF8String.h" +#include "XMPCore/Interfaces/IPath.h" +#include "XMPCore/Interfaces/ISimpleNode.h" +#include "XMPCore/Interfaces/INodeIterator.h" +#include "XMPCore/Interfaces/IArrayNode.h" +#include "XMPCore/Interfaces/IMetadata.h" + +#include + +namespace AdobeXMPCore { + + INodeProxy::INodeProxy( pINode ptr ) : mRawPtr( ptr ) { + mRawPtr->Acquire(); + } + + INodeProxy::~INodeProxy() __NOTHROW__ { + mRawPtr->Release(); + } + + pINode APICALL INodeProxy::GetActualINode() __NOTHROW__ { + return mRawPtr; + } + + void APICALL INodeProxy::Acquire() const __NOTHROW__ { + assert( false ); + } + + void APICALL INodeProxy::Release() const __NOTHROW__ { + assert( false ); + } + + AdobeXMPCommon_Int::pISharedObject_I APICALL INodeProxy::GetISharedObject_I() __NOTHROW__ { + return mRawPtr->GetISharedObject_I(); + } + + AdobeXMPCore_Int::pINode_I APICALL INodeProxy::GetINode_I() __NOTHROW__ { + return mRawPtr->GetINode_I(); + } + + pvoid APICALL INodeProxy::getInterfacePointer( uint64 interfaceID, uint32 interfaceVersion, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->getInterfacePointer( interfaceID, interfaceVersion, error ); + } + + spINode APICALL INodeProxy::GetParent() { + return CallSafeFunctionReturningPointer< INode_v1, pINode_base, INode >( + mRawPtr, &INode_v1::getParent ); + } + + void APICALL INodeProxy::SetName( const char * name, sizet nameLength ) { + return CallSafeFunctionReturningVoid< INode_v1, const char *, sizet >( + mRawPtr, &INode_v1::setName, name, nameLength ); + } + + spcIUTF8String APICALL INodeProxy::GetName() const { + return CallConstSafeFunctionReturningPointer< INode_v1, pcIUTF8String_base, const IUTF8String >( + mRawPtr, &INode_v1::getName ); + } + + void APICALL INodeProxy::SetNameSpace( const char * nameSpace, sizet nameSpaceLength ) { + return CallSafeFunctionReturningVoid< INode_v1, const char *, sizet >( + mRawPtr, &INode_v1::setNameSpace, nameSpace, nameSpaceLength ); + + } + + spcIUTF8String APICALL INodeProxy::GetNameSpace() const { + return CallConstSafeFunctionReturningPointer< INode_v1, pcIUTF8String_base, const IUTF8String >( + mRawPtr, &INode_v1::getNameSpace ); + } + + spIPath APICALL INodeProxy::GetPath() const { + return CallConstSafeFunctionReturningPointer< INode_v1, pIPath_base, IPath >( + mRawPtr, &INode_v1::getPath ); + } + + sizet APICALL INodeProxy::QualifiersCount() const __NOTHROW__ { + return mRawPtr->QualifiersCount(); + } + + spINodeIterator APICALL INodeProxy::QualifiersIterator() { + return CallSafeFunctionReturningPointer< INode_v1, pINodeIterator_base, INodeIterator >( + mRawPtr, &INode_v1::qualifiersIterator ); + } + + spINode APICALL INodeProxy::GetQualifier( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) { + return CallSafeFunctionReturningPointer< INode_v1, pINode_base, INode, const char *, sizet, const char *, sizet >( + mRawPtr, &INode_v1::getQualifier, nameSpace, nameSpaceLength, name, nameLength ); + } + + void APICALL INodeProxy::InsertQualifier( const spINode & node ) { + return CallSafeFunctionReturningVoid< INode_v1, pINode_base >( + mRawPtr, &INode_v1::insertQualifier, node ? node->GetActualINode() : NULL ); + } + + spINode APICALL INodeProxy::ReplaceQualifier( const spINode & node ) { + return CallSafeFunctionReturningPointer< INode_v1, pINode_base, INode, pINode_base >( + mRawPtr, &INode_v1::replaceQualifier, node ? node->GetActualINode() : NULL ); + } + + spINode APICALL INodeProxy::RemoveQualifier( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) { + return CallSafeFunctionReturningPointer< INode_v1, pINode_base, INode, const char *, sizet, const char *, sizet >( + mRawPtr, &INode_v1::removeQualifier, nameSpace, nameSpaceLength, name, nameLength ); + } + + INode_v1::eNodeType APICALL INodeProxy::GetNodeType() const { + return CallConstSafeFunction< INode_v1, eNodeType, uint32 >( + mRawPtr, &INode_v1::getNodeType ); + } + + bool APICALL INodeProxy::IsArrayItem() const { + return CallConstSafeFunction< INode_v1, bool, uint32 >( + mRawPtr, &INode_v1::isArrayItem ); + } + + bool APICALL INodeProxy::IsQualifierNode() const { + return CallConstSafeFunction< INode_v1, bool, uint32 >( + mRawPtr, &INode_v1::isQualifierNode ); + } + + sizet APICALL INodeProxy::GetIndex() const { + return CallConstSafeFunction< INode_v1, sizet, sizet >( + mRawPtr, &INode_v1::getIndex ); + } + + bool APICALL INodeProxy::HasQualifiers() const { + return CallConstSafeFunction< INode_v1, bool, uint32 >( + mRawPtr, &INode_v1::hasQualifiers ); + } + + bool APICALL INodeProxy::HasContent() const { + return CallConstSafeFunction< INode_v1, bool, uint32 >( + mRawPtr, &INode_v1::hasContent ); + } + + bool APICALL INodeProxy::IsEmpty() const { + return CallConstSafeFunction< INode_v1, bool, uint32 >( + mRawPtr, &INode_v1::isEmpty ); + } + + bool APICALL INodeProxy::HasChanged() const { + return CallConstSafeFunction< INode_v1, bool, uint32 >( + mRawPtr, &INode_v1::hasChanged ); + } + + void APICALL INodeProxy::AcknowledgeChanges() const __NOTHROW__ { + return mRawPtr->AcknowledgeChanges( ); + } + + void APICALL INodeProxy::Clear( bool contents, bool qualifiers ) { + return CallSafeFunctionReturningVoid< INode_v1, uint32, uint32 >( + mRawPtr, &INode_v1::clear, static_cast< uint32 >( contents ), static_cast< uint32 >( qualifiers ) ); + } + + spINode APICALL INodeProxy::Clone( bool ignoreEmptyNodes, bool ignoreNodesWithOnlyQualifiers ) const { + return CallConstSafeFunctionReturningPointer< INode_v1, pINode_base, INode, uint32, uint32 >( + mRawPtr, &INode_v1::clone, static_cast< uint32 >( ignoreEmptyNodes ), static_cast< uint32 >( ignoreNodesWithOnlyQualifiers ) ); + } + + pINode_base APICALL INodeProxy::getParent( pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->getParent( error ); + } + + void APICALL INodeProxy::setName( const char * name, sizet nameLength, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->setName( name, nameLength, error ); + } + + AdobeXMPCommon::pcIUTF8String_base APICALL INodeProxy::getName( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getName( error ); + } + + void APICALL INodeProxy::setNameSpace( const char * nameSpace, sizet nameSpaceLength, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->setNameSpace( nameSpace, nameSpaceLength, error ); + } + + pcIUTF8String_base APICALL INodeProxy::getNameSpace( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getNameSpace( error ); + } + + pIPath_base APICALL INodeProxy::getPath( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getPath( error ); + } + + pINodeIterator_base APICALL INodeProxy::qualifiersIterator( pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->qualifiersIterator( error ); + } + + pINode_base APICALL INodeProxy::getQualifier( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->getQualifier( nameSpace, nameSpaceLength, name, nameLength, error ); + } + + void APICALL INodeProxy::insertQualifier( pINode_base base, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->insertQualifier( base, error ); + } + + pINode_base APICALL INodeProxy::replaceQualifier( pINode_base node, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->replaceQualifier( node, error ); + } + + pINode_base APICALL INodeProxy::removeQualifier( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->removeQualifier( nameSpace, nameSpaceLength, name, nameLength, error ); + } + + uint32 APICALL INodeProxy::getNodeType( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getNodeType( error ); + } + + uint32 APICALL INodeProxy::isArrayItem( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->isArrayItem( error ); + } + + uint32 APICALL INodeProxy::isQualifierNode( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->isQualifierNode( error ); + } + + sizet APICALL INodeProxy::getIndex( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getIndex( error ); + } + + uint32 APICALL INodeProxy::hasQualifiers( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->hasQualifiers( error ); + } + + uint32 APICALL INodeProxy::hasContent( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->hasContent( error ); + } + + uint32 APICALL INodeProxy::isEmpty( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->isEmpty( error ); + } + + uint32 APICALL INodeProxy::hasChanged( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->hasChanged( error ); + } + + void APICALL INodeProxy::clear( uint32 contents, uint32 qualifiers, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->clear( contents, qualifiers, error ); + } + + pINode_base APICALL INodeProxy::clone( uint32 igoreEmptyNodes, uint32 ignoreNodesWithOnlyQualifiers, pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->clone( igoreEmptyNodes, ignoreNodesWithOnlyQualifiers, error ); + } + + void APICALL INodeProxy::EnableThreadSafety() const __NOTHROW__ { + return mRawPtr->EnableThreadSafety( ); + } + + void APICALL INodeProxy::DisableThreadSafety() const __NOTHROW__ { + return mRawPtr->DisableThreadSafety( ); + } + + bool APICALL INodeProxy::IsThreadSafe() const { + return mRawPtr->isThreadSafe() != 0; + } + + uint32 APICALL INodeProxy::isThreadSafe() const __NOTHROW__ { + assert( false ); + return mRawPtr->isThreadSafe(); + } + + spISimpleNode APICALL INodeProxy::ConvertToSimpleNode() { + return CallSafeFunctionReturningPointer< INode_v1, pISimpleNode_base, ISimpleNode >( + mRawPtr, &INode_v1::convertToSimpleNode ); + } + + spIStructureNode APICALL INodeProxy::ConvertToStructureNode() { + return CallSafeFunctionReturningPointer< INode_v1, pIStructureNode_base, IStructureNode >( + mRawPtr, &INode_v1::convertToStructureNode ); + } + + spIArrayNode APICALL INodeProxy::ConvertToArrayNode() { + return CallSafeFunctionReturningPointer< INode_v1, pIArrayNode_base, IArrayNode >( + mRawPtr, &INode_v1::convertToArrayNode ); + } + + spIMetadata APICALL INodeProxy::ConvertToMetadata() { + return CallSafeFunctionReturningPointer< INode_v1, pIMetadata_base, IMetadata >( + mRawPtr, &INode_v1::convertToMetadata ); + } + + pISimpleNode_base APICALL INodeProxy::convertToSimpleNode( pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->convertToSimpleNode( error ); + } + + pIStructureNode_base APICALL INodeProxy::convertToStructureNode( pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->convertToStructureNode( error ); } + + pIArrayNode_base APICALL INodeProxy::convertToArrayNode( pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->convertToArrayNode( error ); + } + + pIMetadata_base APICALL INodeProxy::convertToMetadata( pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->convertToMetadata( error ); + } + + uint32 APICALL INodeProxy::getParentNodeType( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getParentNodeType( error ); + } + + uint32 APICALL INodeProxy::getQualifierNodeType( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getQualifierNodeType( nameSpace, nameSpaceLength, name, nameLength, error ); + } + + INode_v1::eNodeType APICALL INodeProxy::GetParentNodeType() const { + return CallConstSafeFunction< INode_v1, eNodeType, uint32 >( + mRawPtr, &INode_v1::getParentNodeType ); + } + + INode_v1::eNodeType APICALL INodeProxy::GetQualifierNodeType( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) const { + return CallConstSafeFunction< INode_v1, eNodeType, uint32, const char *, sizet, const char *, sizet >( + mRawPtr, &INode_v1::getQualifierNodeType, nameSpace, nameSpaceLength, name, nameLength ); + } + + AdobeXMPCommon_Int::pIThreadSafe_I APICALL INodeProxy::GetIThreadSafe_I() __NOTHROW__ { + return mRawPtr->GetIThreadSafe_I( ); + } + + pvoid APICALL INodeProxy::GetInterfacePointer( uint64 interfaceID, uint32 interfaceVersion ) { + return CallSafeFunction< IVersionable, pvoid, pvoid, uint64, uint32 >( + mRawPtr, &IVersionable::getInterfacePointer, interfaceID, interfaceVersion ); + } + + spINode INode_v1::MakeShared( pINode_base ptr ) { + if ( !ptr ) return spINode(); + pINode p = INode::GetInterfaceVersion() > 1 ? ptr->GetInterfacePointer< INode >() : ptr; + return shared_ptr< INode >( new INodeProxy( p ) ); + } + +} + +#endif // !BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB diff --git a/xmp/XMPCore/source/INodeIterator.cpp b/xmp/XMPCore/source/INodeIterator.cpp new file mode 100644 index 0000000..f2f43a3 --- /dev/null +++ b/xmp/XMPCore/source/INodeIterator.cpp @@ -0,0 +1,105 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +namespace AdobeXMPCore { + class INodeIteratorProxy; +} + +#define FRIEND_CLASS_DECLARATION() friend class AdobeXMPCore::INodeIteratorProxy; + +#include "XMPCore/Interfaces/INodeIterator.h" + +#if !BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB + +#include "XMPCommon/Utilities/TWrapperFunctions.h" +#include + +namespace AdobeXMPCore { + + class INodeIteratorProxy + : public virtual INodeIterator + { + private: + pINodeIterator mRawPtr; + + public: + INodeIteratorProxy( pINodeIterator ptr ) + : mRawPtr( ptr ) + { + mRawPtr->Acquire(); + } + + ~INodeIteratorProxy() __NOTHROW__ { mRawPtr->Release(); } + + pINodeIterator APICALL GetActualINodeIterator() __NOTHROW__ { return mRawPtr; } + + void APICALL Acquire() const __NOTHROW__ { assert( false ); } + + void APICALL Release() const __NOTHROW__ { assert( false ); } + + AdobeXMPCommon_Int::pISharedObject_I APICALL GetISharedObject_I() __NOTHROW__ { + return mRawPtr->GetISharedObject_I(); + } + + AdobeXMPCore_Int::pINodeIterator_I APICALL GetINodeIterator_I() __NOTHROW__ { + return mRawPtr->GetINodeIterator_I(); + } + + pvoid APICALL getInterfacePointer( uint64 interfaceID, uint32 interfaceVersion, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->getInterfacePointer( interfaceID, interfaceVersion, error ); + } + + virtual spINode APICALL GetNode() { + return CallSafeFunctionReturningPointer< INodeIterator_v1, pINode_base, INode >( + mRawPtr, &INodeIterator_v1::getNode ); + } + + virtual INode_v1::eNodeType APICALL GetNodeType() const { + return CallConstSafeFunction< INodeIterator_v1, INode_v1::eNodeType, uint32 >( + mRawPtr, &INodeIterator_v1::getNodeType ); + } + + virtual spINodeIterator APICALL Next() { + return CallSafeFunctionReturningPointer< INodeIterator_v1, pINodeIterator, INodeIterator >( + mRawPtr, &INodeIterator_v1::next ); + } + + virtual uint32 APICALL getNodeType( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getNodeType( error ); + } + + virtual pINode_base APICALL getNode( pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->getNode( error ); + } + + virtual pINodeIterator_base APICALL next( pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->next( error ); + } + + pvoid APICALL GetInterfacePointer( uint64 interfaceID, uint32 interfaceVersion ) { + return CallSafeFunction< IVersionable, pvoid, pvoid, uint64, uint32 >( + mRawPtr, &IVersionable::getInterfacePointer, interfaceID, interfaceVersion ); + } + + }; + + spINodeIterator INodeIterator_v1::MakeShared( pINodeIterator_base ptr ) { + if ( !ptr ) return spINodeIterator(); + //return shared_ptr< INodeIterator >( new INodeIteratorProxy( ptr ) ); + pINodeIterator p = INodeIterator::GetInterfaceVersion() > 1 ? ptr->GetInterfacePointer< INodeIterator >() : ptr; + return shared_ptr< INodeIterator >( new INodeIteratorProxy( p ) ); + } + +} + +#endif // BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB diff --git a/xmp/XMPCore/source/IPath.cpp b/xmp/XMPCore/source/IPath.cpp new file mode 100644 index 0000000..f0b99c3 --- /dev/null +++ b/xmp/XMPCore/source/IPath.cpp @@ -0,0 +1,157 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +namespace AdobeXMPCore { + class IPathProxy; +} + +#define FRIEND_CLASS_DECLARATION() friend class AdobeXMPCore::IPathProxy; + +#include "XMPCore/Interfaces/IPath.h" + +#if !BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB + +#include "XMPCommon/Utilities/TWrapperFunctions.h" +#include "XMPCore/Interfaces/INameSpacePrefixMap.h" +#include "XMPCommon/Interfaces/IUTF8String.h" +#include "XMPCore/Interfaces/IPathSegment.h" +#include "XMPCore/Interfaces/ICoreObjectFactory.h" + +#include + +namespace AdobeXMPCore { + + class IPathProxy + : public virtual IPath + { + private: + pIPath mRawPtr; + + public: + IPathProxy( pIPath ptr ) + : mRawPtr( ptr ) + { + mRawPtr->Acquire(); + } + + ~IPathProxy() __NOTHROW__ { mRawPtr->Release(); } + + pIPath APICALL GetActualIPath() __NOTHROW__ { return mRawPtr; } + + void APICALL Acquire() const __NOTHROW__ { assert( false ); } + + void APICALL Release() const __NOTHROW__ { assert( false ); } + + AdobeXMPCommon_Int::pISharedObject_I APICALL GetISharedObject_I() __NOTHROW__ { + return mRawPtr->GetISharedObject_I(); + } + + AdobeXMPCore_Int::pIPath_I APICALL GetIPath_I() __NOTHROW__ { + return mRawPtr->GetIPath_I(); + } + + pvoid APICALL GetInterfacePointer( uint64 interfaceID, uint32 interfaceVersion ) { + return CallSafeFunction< IVersionable, pvoid, pvoid, uint64, uint32 >( + mRawPtr, &IVersionable::getInterfacePointer, interfaceID, interfaceVersion ); + } + + pvoid APICALL getInterfacePointer( uint64 interfaceID, uint32 interfaceVersion, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->getInterfacePointer( interfaceID, interfaceVersion, error ); + } + + virtual spcINameSpacePrefixMap APICALL RegisterNameSpacePrefixMap( const spcINameSpacePrefixMap & map ) { + return CallSafeFunctionReturningPointer< IPath_v1, pcINameSpacePrefixMap_base, const INameSpacePrefixMap, pcINameSpacePrefixMap_base >( + mRawPtr, &IPath_v1::registerNameSpacePrefixMap, map ? map->GetActualINameSpacePrefixMap() : NULL ); + } + + virtual pcINameSpacePrefixMap_base APICALL registerNameSpacePrefixMap( pcINameSpacePrefixMap_base map, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->registerNameSpacePrefixMap( map, error ); + } + + virtual spIUTF8String APICALL Serialize( const spcINameSpacePrefixMap & map ) const { + return CallConstSafeFunctionReturningPointer< IPath_v1, pIUTF8String_base, IUTF8String, pcINameSpacePrefixMap_base >( + mRawPtr, &IPath_v1::serialize, map ? map->GetActualINameSpacePrefixMap() : NULL ); + } + + virtual pIUTF8String_base APICALL serialize( pcINameSpacePrefixMap_base map, pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->serialize( map, error ); + } + + virtual void APICALL AppendPathSegment( const spcIPathSegment & segment ) { + return CallSafeFunctionReturningVoid< IPath_v1, pcIPathSegment_base >( + mRawPtr, &IPath_v1::appendPathSegment, segment ? segment->GetActualIPathSegment() : NULL ); + } + + virtual void APICALL appendPathSegment( pcIPathSegment_base segment, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->appendPathSegment( segment, error ); + } + + virtual spcIPathSegment APICALL RemovePathSegment( sizet index ) { + return CallSafeFunctionReturningPointer< IPath_v1, pcIPathSegment_base, const IPathSegment, sizet >( + mRawPtr, &IPath_v1::removePathSegment, index ); + } + + virtual pcIPathSegment_base APICALL removePathSegment( sizet index, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->removePathSegment( index, error ); + } + + virtual spcIPathSegment APICALL GetPathSegment( sizet index ) const { + return CallConstSafeFunctionReturningPointer< IPath_v1, pcIPathSegment_base, const IPathSegment, sizet >( + mRawPtr, &IPath_v1::getPathSegment, index ); + } + + virtual pcIPathSegment_base APICALL getPathSegment( sizet index, pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getPathSegment( index, error ); + } + + virtual sizet APICALL Size() const __NOTHROW__ { + return mRawPtr->Size( ); + } + + virtual void APICALL Clear() __NOTHROW__ { + return mRawPtr->Clear( ); + } + + virtual spIPath APICALL Clone( sizet startingIndex, sizet countOfSegments ) const { + return CallConstSafeFunctionReturningPointer< IPath_v1, pIPath_base, IPath, sizet, sizet >( + mRawPtr, &IPath_v1::clone, startingIndex, countOfSegments ); + } + + virtual pIPath_base APICALL clone( sizet startingIndex, sizet countOfSegemetns, pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->clone( startingIndex, countOfSegemetns, error ); + } + + }; + + spIPath IPath_v1::MakeShared( pIPath_base ptr ) { + if ( !ptr ) return spIPath(); + pIPath p = IPath::GetInterfaceVersion() > 1 ? ptr->GetInterfacePointer< IPath >() : ptr; + return shared_ptr< IPath >( new IPathProxy( p ) ); + } + + spIPath IPath_v1::CreatePath() { + return CallSafeFunctionReturningPointer< ICoreObjectFactory, pIPath_base, IPath >( + ICoreObjectFactory::GetCoreObjectFactory(), &ICoreObjectFactory::CreatePath ); + } + + spIPath IPath_v1::ParsePath( const char * path, sizet pathLength, const spcINameSpacePrefixMap & map ) { + return CallSafeFunctionReturningPointer< ICoreObjectFactory, pIPath_base, IPath, const char *, sizet, pcINameSpacePrefixMap_base >( + ICoreObjectFactory::GetCoreObjectFactory(), &ICoreObjectFactory::ParsePath, path, pathLength, map ? map->GetActualINameSpacePrefixMap() : NULL ); + } + +} + +#endif // BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB diff --git a/xmp/XMPCore/source/IPathSegment.cpp b/xmp/XMPCore/source/IPathSegment.cpp new file mode 100644 index 0000000..4228492 --- /dev/null +++ b/xmp/XMPCore/source/IPathSegment.cpp @@ -0,0 +1,146 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +namespace AdobeXMPCore { + class IPathSegmentProxy; +} + +#define FRIEND_CLASS_DECLARATION() friend class AdobeXMPCore::IPathSegmentProxy; + +#include "XMPCore/Interfaces/IPathSegment.h" + +#if !BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB + +#include "XMPCommon/Utilities/TWrapperFunctions.h" +#include "XMPCommon/Interfaces/IUTF8String.h" +#include "XMPCommon/Interfaces/IError.h" +#include "XMPCore/Interfaces/ICoreObjectFactory.h" + +#include + +namespace AdobeXMPCore { + + class IPathSegmentProxy + : public virtual IPathSegment + { + private: + pIPathSegment mRawPtr; + + public: + IPathSegmentProxy( pIPathSegment ptr ) + : mRawPtr( ptr ) + { + mRawPtr->Acquire(); + } + + ~IPathSegmentProxy() __NOTHROW__ { mRawPtr->Release(); } + + pIPathSegment APICALL GetActualIPathSegment() __NOTHROW__ { return mRawPtr; } + + void APICALL Acquire() const __NOTHROW__ { assert( false ); } + + void APICALL Release() const __NOTHROW__ { assert( false ); } + + AdobeXMPCommon_Int::pISharedObject_I APICALL GetISharedObject_I() __NOTHROW__ { + return mRawPtr->GetISharedObject_I(); + } + + AdobeXMPCore_Int::pIPathSegment_I APICALL GetIPathSegment_I() __NOTHROW__ { + return mRawPtr->GetIPathSegment_I(); + } + + pvoid APICALL GetInterfacePointer( uint64 interfaceID, uint32 interfaceVersion ) { + return CallSafeFunction< IVersionable, pvoid, pvoid, uint64, uint32 >( + mRawPtr, &IVersionable::getInterfacePointer, interfaceID, interfaceVersion ); + } + + pvoid APICALL getInterfacePointer( uint64 interfaceID, uint32 interfaceVersion, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->getInterfacePointer( interfaceID, interfaceVersion, error ); + } + + virtual spcIUTF8String APICALL GetNameSpace() const { + return CallConstSafeFunctionReturningPointer< IPathSegment_v1, pcIUTF8String_base, const IUTF8String >( + mRawPtr, &IPathSegment_v1::getNameSpace ); + } + + virtual pcIUTF8String_base APICALL getNameSpace( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getNameSpace( error ); + } + + virtual spcIUTF8String APICALL GetName() const { + return CallConstSafeFunctionReturningPointer< IPathSegment_v1, pcIUTF8String_base, const IUTF8String >( + mRawPtr, &IPathSegment_v1::getName ); + } + + virtual pcIUTF8String_base APICALL getName( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getName( error ); + } + + virtual ePathSegmentType APICALL GetType() const { + return CallConstSafeFunction< IPathSegment_v1, ePathSegmentType, uint32 >( + mRawPtr, &IPathSegment_v1::getType ); + } + + virtual uint32 APICALL getType( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getType( error ); + } + + virtual sizet APICALL GetIndex() const __NOTHROW__ { + return mRawPtr->GetIndex(); + } + + virtual spcIUTF8String APICALL GetValue() const { + return CallConstSafeFunctionReturningPointer< IPathSegment_v1, pcIUTF8String_base, const IUTF8String >( + mRawPtr, &IPathSegment_v1::getValue ); + } + + virtual pcIUTF8String_base APICALL getValue( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getValue( error ); + } + + }; + + spIPathSegment IPathSegment_v1::MakeShared( pIPathSegment_base ptr ) { + if ( !ptr ) return spIPathSegment(); + pIPathSegment p = IPathSegment::GetInterfaceVersion() > 1 ? + ptr->GetInterfacePointer< IPathSegment >() : ptr; + return shared_ptr< IPathSegment >( new IPathSegmentProxy( p ) ); + } + + spcIPathSegment IPathSegment_v1::CreatePropertyPathSegment( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) { + return CallSafeFunctionReturningPointer< ICoreObjectFactory, pcIPathSegment_base, const IPathSegment, const char *, sizet, const char *, sizet >( + ICoreObjectFactory::GetCoreObjectFactory(), &ICoreObjectFactory::CreatePropertyPathSegment, nameSpace, nameSpaceLength, name, nameLength ); + } + + spcIPathSegment IPathSegment_v1::CreateArrayIndexPathSegment( const char * nameSpace, sizet nameSpaceLength, sizet index ) { + return CallSafeFunctionReturningPointer< ICoreObjectFactory, pcIPathSegment_base, const IPathSegment, const char *, sizet, sizet >( + ICoreObjectFactory::GetCoreObjectFactory(), &ICoreObjectFactory::CreateArrayIndexPathSegment, nameSpace, nameSpaceLength, index ); + } + + spcIPathSegment IPathSegment_v1::CreateQualifierPathSegment( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) { + return CallSafeFunctionReturningPointer< ICoreObjectFactory, pcIPathSegment_base, const IPathSegment, const char *, sizet, const char *, sizet >( + ICoreObjectFactory::GetCoreObjectFactory(), &ICoreObjectFactory::CreateQualifierPathSegment, nameSpace, nameSpaceLength, name, nameLength ); + } + + spcIPathSegment IPathSegment_v1::CreateQualifierSelectorPathSegment( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, + const char * value, sizet valueLength ) + { + return CallSafeFunctionReturningPointer< ICoreObjectFactory, pcIPathSegment_base, const IPathSegment, const char *, sizet, const char *, sizet, const char *, sizet >( + ICoreObjectFactory::GetCoreObjectFactory(), &ICoreObjectFactory::CreateQualifierSelectorPathSegment, nameSpace, nameSpaceLength, name, nameLength, value, valueLength ); + } + +} + +#endif // BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB + diff --git a/xmp/XMPCore/source/ISimpleNode.cpp b/xmp/XMPCore/source/ISimpleNode.cpp new file mode 100644 index 0000000..170192f --- /dev/null +++ b/xmp/XMPCore/source/ISimpleNode.cpp @@ -0,0 +1,111 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +namespace AdobeXMPCore { + class ISimpleNodeProxy; +} + +#define FRIEND_CLASS_DECLARATION() friend class AdobeXMPCore::ISimpleNodeProxy; + +#include "XMPCore/Interfaces/ISimpleNode.h" + +#if !BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB + +#include "XMPCommon/Utilities/TWrapperFunctions.h" +#include "XMPCommon/Interfaces/IUTF8String.h" +#include "XMPCore/Interfaces/ICoreObjectFactory.h" + +#include + +namespace AdobeXMPCore { + +#if XMP_WinBuild + #pragma warning( push ) + #pragma warning( disable : 4250 ) +#endif + + class ISimpleNodeProxy + : public virtual ISimpleNode + , public virtual INodeProxy + { + private: + pISimpleNode mRawPtr; + + public: + ISimpleNodeProxy( pISimpleNode ptr ) + : mRawPtr( ptr ) + , INodeProxy( ptr ) {} + + ~ISimpleNodeProxy() __NOTHROW__ {} + + pISimpleNode APICALL GetActualISimpleNode() __NOTHROW__ { return mRawPtr; } + + AdobeXMPCore_Int::pISimpleNode_I APICALL GetISimpleNode_I() __NOTHROW__ { + return mRawPtr->GetISimpleNode_I(); + } + + virtual spcIUTF8String APICALL GetValue() const { + return CallConstSafeFunctionReturningPointer< ISimpleNode_v1, pcIUTF8String_base, const IUTF8String >( + mRawPtr, &ISimpleNode_v1::getValue ); + } + + virtual void APICALL SetValue( const char * value, sizet valueLength ) { + return CallSafeFunctionReturningVoid< ISimpleNode_v1, const char *, sizet >( + mRawPtr, &ISimpleNode_v1::setValue, value, valueLength ); + } + + virtual bool APICALL IsURIType() const { + return CallConstSafeFunction< ISimpleNode_v1, bool, uint32 >( + mRawPtr, &ISimpleNode_v1::isURIType ); + } + + virtual void APICALL SetURIType( bool isURI ) { + return CallSafeFunctionReturningVoid< ISimpleNode_v1, uint32 >( + mRawPtr, &ISimpleNode_v1::setURIType, isURI ? 1 : 0 ); + } + + virtual pcIUTF8String_base APICALL getValue( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getValue( error ); + } + + virtual void APICALL setValue( const char * value, sizet valueLength, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->setValue( value, valueLength, error ); + } + + virtual uint32 APICALL isURIType( pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->isURIType( error ); + } + + virtual void APICALL setURIType( uint32 isURI, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->setURIType( isURI, error ); + } + + }; + + spISimpleNode ISimpleNode_v1::CreateSimpleNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, const char * value, sizet valueLength ) { + return CallSafeFunctionReturningPointer< ICoreObjectFactory, pISimpleNode_base, ISimpleNode, const char *, sizet, const char *, sizet, const char *, sizet >( + ICoreObjectFactory::GetCoreObjectFactory(), &ICoreObjectFactory::CreateSimpleNode, nameSpace, nameSpaceLength, name, nameLength, value, valueLength ); + } + + spISimpleNode ISimpleNode_v1::MakeShared( pISimpleNode_base ptr ) { + if ( !ptr ) return spISimpleNode(); + pISimpleNode p = ISimpleNode::GetInterfaceVersion() > 1 ? ptr->GetInterfacePointer< ISimpleNode >() : ptr; + return shared_ptr< ISimpleNode >( new ISimpleNodeProxy( p ) ); + } + +#if XMP_WinBuild + #pragma warning( pop ) +#endif +} + +#endif // !BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB diff --git a/xmp/XMPCore/source/IStructureNode.cpp b/xmp/XMPCore/source/IStructureNode.cpp new file mode 100644 index 0000000..32aa76d --- /dev/null +++ b/xmp/XMPCore/source/IStructureNode.cpp @@ -0,0 +1,104 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +namespace AdobeXMPCore { + class IStructureNodeProxy; +} + +#define FRIEND_CLASS_DECLARATION() friend class AdobeXMPCore::IStructureNodeProxy; + +#include "XMPCore/Interfaces/IStructureNode.h" + +#if !BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB + +#include "XMPCommon/Utilities/TWrapperFunctions.h" +#include "XMPCore/Interfaces/ICoreObjectFactory.h" +#include + +namespace AdobeXMPCore { + + + IStructureNodeProxy::IStructureNodeProxy( pIStructureNode ptr ) : mRawPtr( ptr ) + , ICompositeNodeProxy( ptr ) + , INodeProxy( ptr ) { } + + IStructureNodeProxy::~IStructureNodeProxy() __NOTHROW__ { } + + AdobeXMPCore_Int::pIStructureNode_I APICALL IStructureNodeProxy::GetIStructureNode_I() __NOTHROW__ { + return mRawPtr->GetIStructureNode_I(); + } + + INode_v1::eNodeType APICALL IStructureNodeProxy::GetChildNodeType( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) const { + return CallConstSafeFunction< IStructureNode_v1, eNodeType, uint32, const char *, sizet, const char *, sizet >( + mRawPtr, &IStructureNode_v1::getChildNodeType, nameSpace, nameSpaceLength, name, nameLength ); + } + + spINode APICALL IStructureNodeProxy::GetNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) { + return CallSafeFunctionReturningPointer< IStructureNode_v1, pINode_base, INode, const char *, sizet, const char *, sizet >( + mRawPtr, &IStructureNode_v1::getNode, nameSpace, nameSpaceLength, name, nameLength ); + } + + void APICALL IStructureNodeProxy::InsertNode( const spINode & node ) { + return CallSafeFunctionReturningVoid< IStructureNode_v1, pINode_base >( + mRawPtr, &IStructureNode_v1::insertNode, node ? node->GetActualINode() : NULL ); + } + + spINode APICALL IStructureNodeProxy::ReplaceNode( const spINode & node ) { + return CallSafeFunctionReturningPointer< IStructureNode_v1, pINode_base, INode, pINode_base >( + mRawPtr, &IStructureNode_v1::replaceNode, node ? node->GetActualINode() : NULL ); + } + + spINode APICALL IStructureNodeProxy::RemoveNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) { + return CallSafeFunctionReturningPointer< IStructureNode_v1, pINode_base, INode, const char *, sizet, const char *, sizet >( + mRawPtr, &IStructureNode_v1::removeNode, nameSpace, nameSpaceLength, name, nameLength ); + } + + uint32 APICALL IStructureNodeProxy::getChildNodeType( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, pcIError_base & error ) const __NOTHROW__ { + assert( false ); + return mRawPtr->getChildNodeType( nameSpace, nameSpaceLength, name, nameLength, error ); + } + + pINode_base APICALL IStructureNodeProxy::getNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->getNode( nameSpace, nameSpaceLength, name, nameLength, error ); + } + + void APICALL IStructureNodeProxy::insertNode( pINode_base node, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->insertNode( node, error ); + } + + pINode_base APICALL IStructureNodeProxy::replaceNode( pINode_base node, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->replaceNode( node, error ); + } + + pINode_base APICALL IStructureNodeProxy::removeNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength, pcIError_base & error ) __NOTHROW__ { + assert( false ); + return mRawPtr->removeNode( nameSpace, nameSpaceLength, name, nameLength, error ); + } + + pIStructureNode APICALL IStructureNodeProxy::GetActualIStructureNode() __NOTHROW__ { + return mRawPtr; + } + + spIStructureNode IStructureNode_v1::MakeShared( pIStructureNode_base ptr ) { + if ( !ptr ) return spIStructureNode(); + pIStructureNode p = IStructureNode::GetInterfaceVersion() > 1 ? ptr->GetInterfacePointer< IStructureNode >() : ptr; + return shared_ptr< IStructureNode >( new IStructureNodeProxy( p ) ); + } + + spIStructureNode IStructureNode_v1::CreateStructureNode( const char * nameSpace, sizet nameSpaceLength, const char * name, sizet nameLength ) { + return CallSafeFunctionReturningPointer< ICoreObjectFactory, pIStructureNode_base, IStructureNode, const char *, sizet, const char *, sizet >( + ICoreObjectFactory::GetCoreObjectFactory(), &ICoreObjectFactory::CreateStructureNode, nameSpace, nameSpaceLength, name, nameLength ); + } + +} + +#endif // BUILDING_XMPCORE_LIB && !SOURCE_COMPILING_XMPCORE_LIB diff --git a/xmp/XMP_Const.h b/xmp/XMP_Const.h new file mode 100644 index 0000000..144baec --- /dev/null +++ b/xmp/XMP_Const.h @@ -0,0 +1,1569 @@ +#ifndef __XMP_Const_h__ +#define __XMP_Const_h__ 1 + +// ================================================================================================= +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMP_Environment.h" + + #include + +#if XMP_MacBuild | XMP_iOSBuild // ! No stdint.h on Windows and some UNIXes. + #include +#endif +#if XMP_UNIXBuild // hopefully an inttypes.h on all UNIXes... + #include +#endif + +#ifndef XMP_MARKER_EXTENSIBILITY_BACKWARD_COMPATIBILITY + #define XMP_MARKER_EXTENSIBILITY_BACKWARD_COMPATIBILITY 1 +#endif + +#if __cplusplus +extern "C" { +#endif + +// ================================================================================================= +/// \file XMP_Const.h +/// \brief Common C/C++ types and constants for the XMP toolkit. +// ================================================================================================= + +// ================================================================================================= +// Basic types and constants +// ========================= + +// The XMP_... types are used on the off chance that the ..._t types present a problem. In that +// case only the declarations of the XMP_... types needs to change, not all of the uses. These +// types are used where fixed sizes are required in order to have a known ABI for a DLL build. + +#if XMP_MacBuild | XMP_iOSBuild + + typedef int8_t XMP_Int8; + typedef int16_t XMP_Int16; + typedef int32_t XMP_Int32; + typedef int64_t XMP_Int64; + + typedef uint8_t XMP_Uns8; + typedef uint16_t XMP_Uns16; + typedef uint32_t XMP_Uns32; + typedef uint64_t XMP_Uns64; + +#elif XMP_WinBuild + + typedef signed char XMP_Int8; + typedef signed short XMP_Int16; + typedef signed long XMP_Int32; + typedef signed long long XMP_Int64; + + typedef unsigned char XMP_Uns8; + typedef unsigned short XMP_Uns16; + typedef unsigned long XMP_Uns32; + typedef unsigned long long XMP_Uns64; + +#elif XMP_UNIXBuild + + #if ! XMP_64 + + typedef signed char XMP_Int8; + typedef signed short XMP_Int16; + typedef signed long XMP_Int32; + typedef signed long long XMP_Int64; + + typedef unsigned char XMP_Uns8; + typedef unsigned short XMP_Uns16; + typedef unsigned long XMP_Uns32; + typedef unsigned long long XMP_Uns64; + + #else + + typedef signed char XMP_Int8; + typedef signed short XMP_Int16; + typedef signed int XMP_Int32; + typedef signed long long XMP_Int64; + + typedef unsigned char XMP_Uns8; + typedef unsigned short XMP_Uns16; + typedef unsigned int XMP_Uns32; + typedef unsigned long long XMP_Uns64; + + #endif + +#else + + #error "XMP environment error - must define one of XMP_MacBuild, XMP_WinBuild, XMP_UNIXBuild or XMP_iOSBuild" + +#endif + +typedef XMP_Uns8 XMP_Bool; + +const XMP_Uns8 kXMP_Bool_False = 0; + +#define ConvertXMP_BoolToBool(a) (a) != kXMP_Bool_False +#define ConvertBoolToXMP_Bool(a) (a) ? !kXMP_Bool_False : kXMP_Bool_False + +static const XMP_Uns8 Min_XMP_Uns8 = ( (XMP_Uns8) 0x00 ); +static const XMP_Uns8 Max_XMP_Uns8 = ( (XMP_Uns8) 0xFF ); +static const XMP_Uns16 Min_XMP_Uns16 = ( (XMP_Uns16) 0x00 ); +static const XMP_Uns16 Max_XMP_Uns16 = ( (XMP_Uns16) 0xFFFF ); +static const XMP_Uns32 Min_XMP_Uns32 = ( (XMP_Uns32) 0x00 ); +static const XMP_Uns32 Max_XMP_Uns32 = ( (XMP_Uns32) 0xFFFFFFFF ); +static const XMP_Uns64 Min_XMP_Uns64 = ( (XMP_Uns64) 0x00 ); +static const XMP_Uns64 Max_XMP_Uns64 = ( (XMP_Uns64) 0xFFFFFFFFFFFFFFFFLL ); + +static const XMP_Int8 Min_XMP_Int8 = ( (XMP_Int8) 0x80 ); +static const XMP_Int8 Max_XMP_Int8 = ( (XMP_Int8) 0x7F ); +static const XMP_Int16 Min_XMP_Int16 = ( (XMP_Int16) 0x8000 ); +static const XMP_Int16 Max_XMP_Int16 = ( (XMP_Int16) 0x7FFF ); +static const XMP_Int32 Min_XMP_Int32 = ( (XMP_Int32) 0x80000000 ); +static const XMP_Int32 Max_XMP_Int32 = ( (XMP_Int32) 0x7FFFFFFF ); +static const XMP_Int64 Min_XMP_Int64 = ( (XMP_Int64) 0x8000000000000000LL ); +static const XMP_Int64 Max_XMP_Int64 = ( (XMP_Int64) 0x7FFFFFFFFFFFFFFFLL ); + + +/// @brief An "ABI safe" pointer to the internal part of an XMP object. Use to pass an XMP object across +/// client DLL boundaries. See \c TXMPMeta::GetInternalRef(). +typedef struct __XMPMeta__ * XMPMetaRef; + +/// @brief An "ABI safe" pointer to the internal part of an XMP iteration object. Use to pass an XMP +/// iteration object across client DLL boundaries. See \c TXMPIterator. +typedef struct __XMPIterator__ * XMPIteratorRef; + +/// @brief An "ABI safe" pointer to the internal part of an XMP document operations object. Use to pass an +/// XMP document operations object across client DLL boundaries. See \c TXMPDocOps. +typedef struct __XMPDocOps__ * XMPDocOpsRef; + +/// @brief An "ABI safe" pointer to the internal part of an XMP file-handling object. Use to pass an XMP +/// file-handling object across client DLL boundaries. See \c TXMPFiles. +typedef struct __XMPFiles__ * XMPFilesRef; + +// ================================================================================================= + +/// \name General scalar types and constants +/// @{ + +/// \typedef XMP_StringPtr +/// \brief The type for input string parameters. A const char *, a null-terminated UTF-8 +/// string. + +/// \typedef XMP_StringLen +/// \brief The type for string length parameters. A 32-bit unsigned integer, as big as will be +/// practically needed. + +/// \typedef XMP_Index +/// \brief The type for offsets and indices. A 32-bit signed integer. It is signed to allow -1 for +/// loop termination. + +/// \typedef XMP_OptionBits +/// \brief The type for a collection of 32 flag bits. +/// @details Individual flags are defined as enum value bit +/// masks; see \c #kXMP_PropValueIsURI and following. A number of macros provide common set or set +/// operations, such as \c XMP_PropIsSimple. For other tests use an expression like options & +/// kXMP_. When passing multiple option flags use the bitwise-OR operator. '|', +/// not the arithmatic plus, '+'. + +typedef const char * XMP_StringPtr; // Points to a null terminated UTF-8 string. +typedef XMP_Uns32 XMP_StringLen; +typedef XMP_Int32 XMP_Index; // Signed, sometimes -1 is handy. +typedef XMP_Uns32 XMP_OptionBits; // Used as 32 individual bits. + +/// \def kXMP_TrueStr +/// \brief The canonical true string value for Booleans in serialized XMP. +/// +/// Code that converts from string to bool should be case insensitive, and also allow "1". + +/// \def kXMP_FalseStr +/// \brief The canonical false string value for Booleans in serialized XMP. +/// +/// Code that converts from string to bool should be case insensitive, and also allow "0". + +#define kXMP_TrueStr "True" // Serialized XMP spellings, not for the type bool. +#define kXMP_FalseStr "False" + +///@brief Type for yes/no/maybe answers. The values are picked to allow Boolean-like usage. The yes +///values are true (non-zero), the no value is false (zero). +enum { + /// The part or parts have definitely changed. + kXMPTS_Yes = 1, + /// The part or parts have definitely not changed. + kXMPTS_No = 0, + /// The part or parts might, or might not, have changed. + kXMPTS_Maybe = -1 +}; +typedef XMP_Int8 XMP_TriState; + +/// @} + +// ================================================================================================= + +/// \struct XMP_DateTime +/// \brief The expanded type for a date and time. +/// +/// Dates and time in the serialized XMP are ISO 8601 strings. The \c XMP_DateTime struct allows +/// easy conversion with other formats. +/// +/// All of the fields are 32 bit, even though most could be 8 bit. This avoids overflow when doing +/// carries for arithmetic or normalization. All fields have signed values for the same reasons. +/// +/// Date-time values are occasionally used with only a date or only a time component. A date without +/// a time has zeros in the \c XMP_DateTime struct for all time fields. A time without a date has +/// zeros for all date fields (year, month, and day). +/// +/// \c TXMPUtils provides utility functions for manipulating date-time values. +/// +/// @see \c TXMPUtils::ConvertToDate(), \c TXMPUtils::ConvertFromDate(), +/// \c TXMPUtils::CompareDateTime(), \c TXMPUtils::ConvertToLocalTime(), +/// \c TXMPUtils::ConvertToUTCTime(), \c TXMPUtils::CurrentDateTime(), +/// \c TXMPUtils::SetTimeZone() + +struct XMP_DateTime { + + /// The year, can be negative. + XMP_Int32 year; + + /// The month in the range 1..12. + XMP_Int32 month; + + /// The day of the month in the range 1..31. + XMP_Int32 day; + + /// The hour in the range 0..23. + XMP_Int32 hour; + + /// The minute in the range 0..59. + XMP_Int32 minute; + + /// The second in the range 0..59. + XMP_Int32 second; + + /// Is the date portion meaningful? + XMP_Bool hasDate; + + /// Is the time portion meaningful? + XMP_Bool hasTime; + + /// Is the time zone meaningful? + XMP_Bool hasTimeZone; + + /// The "sign" of the time zone, \c #kXMP_TimeIsUTC (0) means UTC, \c #kXMP_TimeWestOfUTC (-1) + /// is west, \c #kXMP_TimeEastOfUTC (+1) is east. + XMP_Int8 tzSign; + + /// The time zone hour in the range 0..23. + XMP_Int32 tzHour; + + /// The time zone minute in the range 0..59. + XMP_Int32 tzMinute; + + /// Nanoseconds within a second, often left as zero. + XMP_Int32 nanoSecond; + + #if __cplusplus + XMP_DateTime() : year(0), month(0), day(0), hour(0), minute(0), second(0), + hasDate(false),hasTime(false), hasTimeZone(false), tzSign(0), tzHour(0), tzMinute(0), nanoSecond(0){}; + #endif + +}; + +/// Constant values for \c XMP_DateTime::tzSign field. +enum { + /// Time zone is west of UTC. + kXMP_TimeWestOfUTC = -1, + /// UTC time. + kXMP_TimeIsUTC = 0, + /// Time zone is east of UTC. + kXMP_TimeEastOfUTC = +1 +}; + +#define XMPDateTime_IsDateOnly(dt) ((dt).hasDate & (! (dt).hasTime)) +#define XMPDateTime_IsTimeOnly(dt) ((dt).hasTime & (! (dt).hasDate)) + +#define XMPDateTime_ClearTimeZone(dt) { (dt).hasTimeZone = (dt).tzSign = (dt).tzHour = (dt).tzMinute = 0; } + +// ================================================================================================= +// Standard namespace URI constants +// ================================ + +/// \name XML namespace constants for standard XMP schema. +/// @{ +/// +/// \def kXMP_NS_XMP +/// \brief The XML namespace for the XMP "basic" schema. +/// +/// \def kXMP_NS_XMP_Rights +/// \brief The XML namespace for the XMP copyright schema. +/// +/// \def kXMP_NS_XMP_MM +/// \brief The XML namespace for the XMP digital asset management schema. +/// +/// \def kXMP_NS_XMP_BJ +/// \brief The XML namespace for the job management schema. +/// +/// \def kXMP_NS_XMP_T +/// \brief The XML namespace for the XMP text document schema. +/// +/// \def kXMP_NS_XMP_T_PG +/// \brief The XML namespace for the XMP paged document schema. +/// +/// \def kXMP_NS_PDF +/// \brief The XML namespace for the PDF schema. +/// +/// \def kXMP_NS_Photoshop +/// \brief The XML namespace for the Photoshop custom schema. +/// +/// \def kXMP_NS_EXIF +/// \brief The XML namespace for Adobe's EXIF schema. +/// +/// \def kXMP_NS_TIFF +/// \brief The XML namespace for Adobe's TIFF schema. +/// +/// @} + +#define kXMP_NS_XMP "http://ns.adobe.com/xap/1.0/" + +#define kXMP_NS_XMP_Rights "http://ns.adobe.com/xap/1.0/rights/" +#define kXMP_NS_XMP_MM "http://ns.adobe.com/xap/1.0/mm/" +#define kXMP_NS_XMP_BJ "http://ns.adobe.com/xap/1.0/bj/" + +#define kXMP_NS_PDF "http://ns.adobe.com/pdf/1.3/" +#define kXMP_NS_Photoshop "http://ns.adobe.com/photoshop/1.0/" +#define kXMP_NS_PSAlbum "http://ns.adobe.com/album/1.0/" +#define kXMP_NS_EXIF "http://ns.adobe.com/exif/1.0/" +#define kXMP_NS_EXIF_Aux "http://ns.adobe.com/exif/1.0/aux/" +#define kXMP_NS_TIFF "http://ns.adobe.com/tiff/1.0/" +#define kXMP_NS_PNG "http://ns.adobe.com/png/1.0/" +#define kXMP_NS_SWF "http://ns.adobe.com/swf/1.0/" +#define kXMP_NS_JPEG "http://ns.adobe.com/jpeg/1.0/" +#define kXMP_NS_JP2K "http://ns.adobe.com/jp2k/1.0/" +#define kXMP_NS_CameraRaw "http://ns.adobe.com/camera-raw-settings/1.0/" +#define kXMP_NS_DM "http://ns.adobe.com/xmp/1.0/DynamicMedia/" +#define kXMP_NS_Script "http://ns.adobe.com/xmp/1.0/Script/" +#define kXMP_NS_ASF "http://ns.adobe.com/asf/1.0/" +#define kXMP_NS_WAV "http://ns.adobe.com/xmp/wav/1.0/" +#define kXMP_NS_BWF "http://ns.adobe.com/bwf/bext/1.0/" +#define kXMP_NS_AEScart "http://ns.adobe.com/aes/cart/" +#define kXMP_NS_RIFFINFO "http://ns.adobe.com/riff/info/" +#define kXMP_NS_iXML "http://ns.adobe.com/ixml/1.0/" +#define kXMP_NS_XMP_Note "http://ns.adobe.com/xmp/note/" + +#define kXMP_NS_AdobeStockPhoto "http://ns.adobe.com/StockPhoto/1.0/" +#define kXMP_NS_CreatorAtom "http://ns.adobe.com/creatorAtom/1.0/" + +#define kXMP_NS_ExifEX "http://cipa.jp/exif/1.0/" + +/// \name XML namespace constants for qualifiers and structured property fields. +/// @{ +/// +/// \def kXMP_NS_XMP_IdentifierQual +/// \brief The XML namespace for qualifiers of the xmp:Identifier property. +/// +/// \def kXMP_NS_XMP_Dimensions +/// \brief The XML namespace for fields of the Dimensions type. +/// +/// \def kXMP_NS_XMP_Image +/// \brief The XML namespace for fields of a graphical image. Used for the Thumbnail type. +/// +/// \def kXMP_NS_XMP_ResourceEvent +/// \brief The XML namespace for fields of the ResourceEvent type. +/// +/// \def kXMP_NS_XMP_ResourceRef +/// \brief The XML namespace for fields of the ResourceRef type. +/// +/// \def kXMP_NS_XMP_ST_Version +/// \brief The XML namespace for fields of the Version type. +/// +/// \def kXMP_NS_XMP_ST_Job +/// \brief The XML namespace for fields of the JobRef type. +/// +/// @} + +#define kXMP_NS_XMP_IdentifierQual "http://ns.adobe.com/xmp/Identifier/qual/1.0/" +#define kXMP_NS_XMP_Dimensions "http://ns.adobe.com/xap/1.0/sType/Dimensions#" +#define kXMP_NS_XMP_Text "http://ns.adobe.com/xap/1.0/t/" +#define kXMP_NS_XMP_PagedFile "http://ns.adobe.com/xap/1.0/t/pg/" +#define kXMP_NS_XMP_Graphics "http://ns.adobe.com/xap/1.0/g/" +#define kXMP_NS_XMP_Image "http://ns.adobe.com/xap/1.0/g/img/" +#define kXMP_NS_XMP_Font "http://ns.adobe.com/xap/1.0/sType/Font#" +#define kXMP_NS_XMP_ResourceEvent "http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" +#define kXMP_NS_XMP_ResourceRef "http://ns.adobe.com/xap/1.0/sType/ResourceRef#" +#define kXMP_NS_XMP_ST_Version "http://ns.adobe.com/xap/1.0/sType/Version#" +#define kXMP_NS_XMP_ST_Job "http://ns.adobe.com/xap/1.0/sType/Job#" +#define kXMP_NS_XMP_ManifestItem "http://ns.adobe.com/xap/1.0/sType/ManifestItem#" + +// Deprecated XML namespace constants +#define kXMP_NS_XMP_T "http://ns.adobe.com/xap/1.0/t/" +#define kXMP_NS_XMP_T_PG "http://ns.adobe.com/xap/1.0/t/pg/" +#define kXMP_NS_XMP_G_IMG "http://ns.adobe.com/xap/1.0/g/img/" + +/// \name XML namespace constants from outside Adobe. +/// @{ +/// +/// \def kXMP_NS_DC +/// \brief The XML namespace for the Dublin Core schema. +/// +/// \def kXMP_NS_IPTCCore +/// \brief The XML namespace for the IPTC Core schema. +/// +/// \def kXMP_NS_IPTCExt +/// \brief The XML namespace for the IPTC Extension schema. +/// +/// \def kXMP_NS_RDF +/// \brief The XML namespace for RDF. +/// +/// \def kXMP_NS_XML +/// \brief The XML namespace for XML. +/// +/// @} + +#define kXMP_NS_DC "http://purl.org/dc/elements/1.1/" + +#define kXMP_NS_IPTCCore "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/" +#define kXMP_NS_IPTCExt "http://iptc.org/std/Iptc4xmpExt/2008-02-29/" + +#define kXMP_NS_DICOM "http://ns.adobe.com/DICOM/" + +#define kXMP_NS_PLUS "http://ns.useplus.org/ldf/xmp/1.0/" + +#define kXMP_NS_PDFA_Schema "http://www.aiim.org/pdfa/ns/schema#" +#define kXMP_NS_PDFA_Property "http://www.aiim.org/pdfa/ns/property#" +#define kXMP_NS_PDFA_Type "http://www.aiim.org/pdfa/ns/type#" +#define kXMP_NS_PDFA_Field "http://www.aiim.org/pdfa/ns/field#" +#define kXMP_NS_PDFA_ID "http://www.aiim.org/pdfa/ns/id/" +#define kXMP_NS_PDFA_Extension "http://www.aiim.org/pdfa/ns/extension/" + +#define kXMP_NS_PDFX "http://ns.adobe.com/pdfx/1.3/" +#define kXMP_NS_PDFX_ID "http://www.npes.org/pdfx/ns/id/" + +#define kXMP_NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#" +#define kXMP_NS_XML "http://www.w3.org/XML/1998/namespace" + +// ================================================================================================= +// Enums and macros used for option bits +// ===================================== + +/// \name Macros for standard option selections. +/// @{ +/// +/// \def kXMP_ArrayLastItem +/// \brief Options macro accesses last array item. +/// +/// \def kXMP_UseNullTermination +/// \brief Options macro sets string style. +/// +/// \def kXMP_NoOptions +/// \brief Options macro clears all property-type bits. +/// +/// @} + +#define kXMP_ArrayLastItem ((XMP_Index)(-1L)) +#define kXMP_UseNullTermination ((XMP_StringLen)(~0UL)) +#define kXMP_NoOptions ((XMP_OptionBits)0UL) + +/// \name Macros for setting and testing general option bits. +/// @{ +/// +/// \def XMP_SetOption +/// \brief Macro sets an option flag bit. +/// \param var A variable storing an options flag. +/// \param opt The bit-flag constant to set. +/// +/// \def XMP_ClearOption +/// \brief Macro clears an option flag bit. +/// \param var A variable storing an options flag. +/// \param opt The bit-flag constant to clear. +/// +/// \def XMP_TestOption +/// \brief Macro reports whether an option flag bit is set. +/// \param var A variable storing an options flag. +/// \param opt The bit-flag constant to test. +/// \return True if the bit is set. +/// +/// \def XMP_OptionIsSet +/// \brief Macro reports whether an option flag bit is set. +/// \param var A variable storing an options flag. +/// \param opt The bit-flag constant to test. +/// \return True if the bit is set. +/// +/// \def XMP_OptionIsClear +/// \brief Macro reports whether an option flag bit is clear. +/// \param var A variable storing an options flag. +/// \param opt The bit-flag constant to test. +/// \return True if the bit is clear. +/// +/// @} + +#define XMP_SetOption(var,opt) var |= (opt) +#define XMP_ClearOption(var,opt) var &= ~(opt) +#define XMP_TestOption(var,opt) (((var) & (opt)) != 0) +#define XMP_OptionIsSet(var,opt) (((var) & (opt)) != 0) +#define XMP_OptionIsClear(var,opt) (((var) & (opt)) == 0) + +/// \name Macros for setting and testing specific option bits. +/// @{ +/// +/// \def XMP_PropIsSimple +/// \brief Macro reports the property type specified by an options flag. +/// \param opt The options flag to check. +/// +/// \def XMP_PropIsStruct +/// \brief Macro reports the property type specified by an options flag. +/// \param opt The options flag to check. +/// +/// \def XMP_PropIsArray +/// \brief Macro reports the property type specified by an options flag. +/// \param opt The options flag to check. +/// +/// \def XMP_ArrayIsUnordered +/// \brief Macro reports the property type specified by an options flag. +/// \param opt The options flag to check. +/// +/// \def XMP_ArrayIsOrdered +/// \brief Macro reports the property type specified by an options flag. +/// \param opt The options flag to check. +/// +/// \def XMP_ArrayIsAlternate +/// \brief Macro reports the property type specified by an options flag. +/// \param opt The options flag to check. +/// +/// \def XMP_ArrayIsAltText +/// \brief Macro reports the property type specified by an options flag. +/// \param opt The options flag to check. +/// +/// \def XMP_PropHasQualifiers +/// \brief Macro reports the property type specified by an options flag. +/// \param opt The options flag to check. +/// +/// \def XMP_PropIsQualifier +/// \brief Macro reports the property type specified by an options flag. +/// \param opt The options flag to check. +/// +/// \def XMP_PropHasLang +/// \brief Macro reports the property type specified by an options flag. +/// \param opt The options flag to check. +/// +/// \def XMP_NodeIsSchema +/// \brief Macro reports the property type specified by an options flag. +/// \param opt The options flag to check. +/// +/// \def XMP_PropIsAlias +/// \brief Macro reports the property type specified by an options flag. +/// \param opt The options flag to check. +/// +/// @} + +#define XMP_PropIsSimple(opt) (((opt) & kXMP_PropCompositeMask) == 0) +#define XMP_PropIsStruct(opt) (((opt) & kXMP_PropValueIsStruct) != 0) +#define XMP_PropIsArray(opt) (((opt) & kXMP_PropValueIsArray) != 0) + +#define XMP_ArrayIsUnordered(opt) (((opt) & kXMP_PropArrayIsOrdered) == 0) +#define XMP_ArrayIsOrdered(opt) (((opt) & kXMP_PropArrayIsOrdered) != 0) +#define XMP_ArrayIsAlternate(opt) (((opt) & kXMP_PropArrayIsAlternate) != 0) +#define XMP_ArrayIsAltText(opt) (((opt) & kXMP_PropArrayIsAltText) != 0) + +#define XMP_PropHasQualifiers(opt) (((opt) & kXMP_PropHasQualifiers) != 0) +#define XMP_PropIsQualifier(opt) (((opt) & kXMP_PropIsQualifier) != 0) +#define XMP_PropHasLang(opt) (((opt) & kXMP_PropHasLang) != 0) + +#define XMP_NodeIsSchema(opt) (((opt) & kXMP_SchemaNode) != 0) +#define XMP_PropIsAlias(opt) (((opt) & kXMP_PropIsAlias) != 0) + +// ------------------------------------------------------------------------------------------------- + +/// @brief Option bit flags for the \c TXMPMeta property accessor functions. +enum { + + /// The XML string form of the property value is a URI, use rdf:resource attribute. DISCOURAGED + kXMP_PropValueIsURI = 0x00000002UL, + + // ------------------------------------------------------ + // Options relating to qualifiers attached to a property. + + /// The property has qualifiers, includes \c rdf:type and \c xml:lang. + kXMP_PropHasQualifiers = 0x00000010UL, + + /// This is a qualifier for some other property, includes \c rdf:type and \c xml:lang. + /// Qualifiers can have arbitrary structure, and can themselves have qualifiers. If the + /// qualifier itself has a structured value, this flag is only set for the top node of the + /// qualifier's subtree. + kXMP_PropIsQualifier = 0x00000020UL, + + /// Implies \c #kXMP_PropHasQualifiers, property has \c xml:lang. + kXMP_PropHasLang = 0x00000040UL, + + /// Implies \c #kXMP_PropHasQualifiers, property has \c rdf:type. + kXMP_PropHasType = 0x00000080UL, + + // -------------------------------------------- + // Options relating to the data structure form. + + /// The value is a structure with nested fields. + kXMP_PropValueIsStruct = 0x00000100UL, + + /// The value is an array (RDF alt/bag/seq). The "ArrayIs..." flags identify specific types + /// of array; default is a general unordered array, serialized using an \c rdf:Bag container. + kXMP_PropValueIsArray = 0x00000200UL, + + /// The item order does not matter. + kXMP_PropArrayIsUnordered = kXMP_PropValueIsArray, + + /// Implies \c #kXMP_PropValueIsArray, item order matters. It is serialized using an \c rdf:Seq container. + kXMP_PropArrayIsOrdered = 0x00000400UL, + + /// Implies \c #kXMP_PropArrayIsOrdered, items are alternates. It is serialized using an \c rdf:Alt container. + kXMP_PropArrayIsAlternate = 0x00000800UL, + + // ------------------------------------ + // Additional struct and array options. + + /// Implies \c #kXMP_PropArrayIsAlternate, items are localized text. Each array element is a + /// simple property with an \c xml:lang attribute. + kXMP_PropArrayIsAltText = 0x00001000UL, + + // kXMP_InsertBeforeItem = 0x00004000UL, ! Used by SetXyz functions. + // kXMP_InsertAfterItem = 0x00008000UL, ! Used by SetXyz functions. + + // ---------------------------- + // Other miscellaneous options. + + /// This property is an alias name for another property. This is only returned by + /// \c TXMPMeta::GetProperty() and then only if the property name is simple, not an path expression. + kXMP_PropIsAlias = 0x00010000UL, + + /// This property is the base value (actual) for a set of aliases.This is only returned by + /// \c TXMPMeta::GetProperty() and then only if the property name is simple, not an path expression. + kXMP_PropHasAliases = 0x00020000UL, + + /// The value of this property is "owned" by the application, and should not generally be editable in a UI. + kXMP_PropIsInternal = 0x00040000UL, + + /// The value of this property is not derived from the document content. + kXMP_PropIsStable = 0x00100000UL, + + /// The value of this property is derived from the document content. + kXMP_PropIsDerived = 0x00200000UL, + + // kXMPUtil_AllowCommas = 0x10000000UL, ! Used by TXMPUtils::CatenateArrayItems and ::SeparateArrayItems. + // kXMP_DeleteExisting = 0x20000000UL, ! Used by TXMPMeta::SetXyz functions to delete any pre-existing property. + // kXMP_SchemaNode = 0x80000000UL, ! Returned by iterators - #define to avoid warnings + + // ------------------------------ + // Masks that are multiple flags. + + /// Property type bit-flag mask for all array types + kXMP_PropArrayFormMask = kXMP_PropValueIsArray | kXMP_PropArrayIsOrdered | kXMP_PropArrayIsAlternate | kXMP_PropArrayIsAltText, + + /// Property type bit-flag mask for composite types (array and struct) + kXMP_PropCompositeMask = kXMP_PropValueIsStruct | kXMP_PropArrayFormMask, + + /// Mask for bits that are reserved for transient use by the implementation. + kXMP_ImplReservedMask = 0x70000000L + +}; + +#define kXMP_SchemaNode ((XMP_OptionBits)0x80000000UL) + +/// @brief Option bit flags for the \c TXMPMeta property setting functions. +/// @details These option bits are shared with the accessor functions: +/// \li \c #kXMP_PropValueIsURI +/// \li \c #kXMP_PropValueIsStruct +/// \li \c #kXMP_PropValueIsArray +/// \li \c #kXMP_PropArrayIsOrdered +/// \li \c #kXMP_PropArrayIsAlternate +/// \li \c #kXMP_PropArrayIsAltText +enum { + + /// Option for array item location: Insert a new item before the given index. + kXMP_InsertBeforeItem = 0x00004000UL, + + /// Option for array item location: Insert a new item after the given index. + kXMP_InsertAfterItem = 0x00008000UL, + + /// Delete any pre-existing property. + kXMP_DeleteExisting = 0x20000000UL, + + /// Bit-flag mask for property-value option bits + kXMP_PropValueOptionsMask = kXMP_PropValueIsURI, + + /// Bit-flag mask for array-item location bits + kXMP_PropArrayLocationMask = kXMP_InsertBeforeItem | kXMP_InsertAfterItem + +}; + +// ------------------------------------------------------------------------------------------------- + +/// @brief Option bit flags for \c TXMPMeta::ParseFromBuffer(). +enum { + + /// Require a surrounding \c x:xmpmeta element. + kXMP_RequireXMPMeta = 0x0001UL, + + /// This is the not last input buffer for this parse stream. + kXMP_ParseMoreBuffers = 0x0002UL, + + /// Do not reconcile alias differences, throw an exception. + kXMP_StrictAliasing = 0x0004UL + +}; + +/// @brief Option bit flags for \c TXMPMeta::SerializeToBuffer(). +enum { + + // *** Option to remove empty struct/array, or leaf with empty value? + + /// Omit the XML packet wrapper. + kXMP_OmitPacketWrapper = 0x0010UL, + + /// Default is a writeable packet. + kXMP_ReadOnlyPacket = 0x0020UL, + + /// Use a compact form of RDF. + kXMP_UseCompactFormat = 0x0040UL, + + /// Use a canonical form of RDF. + kXMP_UseCanonicalFormat = 0x0080UL, + + /// Include a padding allowance for a thumbnail image. + kXMP_IncludeThumbnailPad = 0x0100UL, + + /// The padding parameter is the overall packet length. + kXMP_ExactPacketLength = 0x0200UL, + + /// Omit all formatting whitespace. + kXMP_OmitAllFormatting = 0x0800UL, + + /// Omit the x:xmpmeta element surrounding the rdf:RDF element. + kXMP_OmitXMPMetaElement = 0x1000UL, + + /// Include a rdf Hash and Merged flag in x:xmpmeta element. + kXMP_IncludeRDFHash = 0x2000UL, + + _XMP_LittleEndian_Bit = 0x0001UL, // ! Don't use directly, see the combined values below! + _XMP_UTF16_Bit = 0x0002UL, + _XMP_UTF32_Bit = 0x0004UL, + + /// Bit-flag mask for encoding-type bits + kXMP_EncodingMask = 0x0007UL, + + /// Use UTF8 encoding + kXMP_EncodeUTF8 = 0UL, + + /// Use UTF16 big-endian encoding + kXMP_EncodeUTF16Big = _XMP_UTF16_Bit, + + /// Use UTF16 little-endian encoding + kXMP_EncodeUTF16Little = _XMP_UTF16_Bit | _XMP_LittleEndian_Bit, + + /// Use UTF32 big-endian encoding + kXMP_EncodeUTF32Big = _XMP_UTF32_Bit, + + /// Use UTF13 little-endian encoding + kXMP_EncodeUTF32Little = _XMP_UTF32_Bit | _XMP_LittleEndian_Bit + +}; + +// ------------------------------------------------------------------------------------------------- + +/// @brief Option bit flags for \c TXMPIterator construction. +enum { + + /// The low 8 bits are an enum of what data structure to iterate. + kXMP_IterClassMask = 0x00FFUL, + + /// Iterate the property tree of a TXMPMeta object. + kXMP_IterProperties = 0x0000UL, + + /// Iterate the global alias table. + kXMP_IterAliases = 0x0001UL, + + /// Iterate the global namespace table. + kXMP_IterNamespaces = 0x0002UL, + + /// Just do the immediate children of the root, default is subtree. + kXMP_IterJustChildren = 0x0100UL, + + /// Just do the leaf nodes, default is all nodes in the subtree. + kXMP_IterJustLeafNodes = 0x0200UL, + + /// Return just the leaf part of the path, default is the full path. + kXMP_IterJustLeafName = 0x0400UL, + + /// Omit all qualifiers. + kXMP_IterOmitQualifiers = 0x1000UL + +}; + +/// @brief Option bit flags for \c TXMPIterator::Skip(). +enum { + + /// Skip the subtree below the current node. + kXMP_IterSkipSubtree = 0x0001UL, + + /// Skip the subtree below and remaining siblings of the current node. + kXMP_IterSkipSiblings = 0x0002UL + +}; + +// ------------------------------------------------------------------------------------------------- + +/// @brief Option bit flags for \c TXMPUtils::CatenateArrayItems() and \c TXMPUtils::SeparateArrayItems(). +/// @details These option bits are shared with the accessor functions: +/// \li \c #kXMP_PropValueIsArray, +/// \li \c #kXMP_PropArrayIsOrdered, +/// \li \c #kXMP_PropArrayIsAlternate, +/// \li \c #kXMP_PropArrayIsAltText +enum { + + /// Allow commas in item values, default is separator. + kXMPUtil_AllowCommas = 0x10000000UL + +}; + +/// @brief Option bit flags for \c TXMPUtils::ApplyTemplate(). +enum { + + /// Do all properties, default is just external properties. + kXMPTemplate_IncludeInternalProperties = 0x0001UL, + + /// Perform a Replace operation, add new properties and modify existing ones. + kXMPTemplate_ReplaceExistingProperties = 0x0002UL, + + /// Similar to Replace, also delete if the template has an empty value. + kXMPTemplate_ReplaceWithDeleteEmpty = 0x0004UL, + + /// Perform an Add operation, add properties if they don't already exist. + kXMPTemplate_AddNewProperties = 0x0008UL, + + /// Perform a Clear operation, keep named properties and delete everything else. + kXMPTemplate_ClearUnnamedProperties = 0x0010UL + +}; + +/// @brief Option bit flags for \c TXMPUtils::RemoveProperties() and \c TXMPUtils::AppendProperties(). +enum { + + /// Do all properties, default is just external properties. + kXMPUtil_DoAllProperties = 0x0001UL, + + /// Replace existing values, default is to leave them. + kXMPUtil_ReplaceOldValues = 0x0002UL, + + /// Delete properties if the new value is empty. + kXMPUtil_DeleteEmptyValues = 0x0004UL, + + /// Include aliases, default is just actual properties. + kXMPUtil_IncludeAliases = 0x0800UL + +}; + +// ================================================================================================= +// Types and Constants for XMPFiles +// ================================ + +/// @brief Seek mode constants for use with XMP_IO and inside XMPFiles library code. +enum SeekMode { kXMP_SeekFromStart, kXMP_SeekFromCurrent, kXMP_SeekFromEnd }; + +/// @brief File format constants for use with XMPFiles. +enum { + + // ! Hex used to avoid gcc warnings. Leave the constants so the text reads big endian. There + // ! seems to be no decent way on UNIX to determine the target endianness at compile time. + // ! Forcing it on the client isn't acceptable. + + // -------------------- + // Public file formats. + + /// Public file format constant: 'PDF ' + kXMP_PDFFile = 0x50444620UL, + /// Public file format constant: 'PS ', general PostScript following DSC conventions + kXMP_PostScriptFile = 0x50532020UL, + /// Public file format constant: 'EPS ', encapsulated PostScript + kXMP_EPSFile = 0x45505320UL, + + /// Public file format constant: 'JPEG' + kXMP_JPEGFile = 0x4A504547UL, + /// Public file format constant: 'JPX ', JPEG 2000, ISO 15444-1 + kXMP_JPEG2KFile = 0x4A505820UL, + /// Public file format constant: 'TIFF' + kXMP_TIFFFile = 0x54494646UL, + /// Public file format constant: 'GIF ' + kXMP_GIFFile = 0x47494620UL, + /// Public file format constant: 'PNG ' + kXMP_PNGFile = 0x504E4720UL, + + /// Public file format constant: 'SWF ' + kXMP_SWFFile = 0x53574620UL, + /// Public file format constant: 'FLA ' + kXMP_FLAFile = 0x464C4120UL, + /// Public file format constant: 'FLV ' + kXMP_FLVFile = 0x464C5620UL, + + /// Public file format constant: 'MOV ', Quicktime + kXMP_MOVFile = 0x4D4F5620UL, + /// Public file format constant: 'AVI ' + kXMP_AVIFile = 0x41564920UL, + /// Public file format constant: 'CIN ', Cineon + kXMP_CINFile = 0x43494E20UL, + /// Public file format constant: 'WAV ' + kXMP_WAVFile = 0x57415620UL, + /// Public file format constant: 'MP3 ' + kXMP_MP3File = 0x4D503320UL, + /// Public file format constant: 'SES ', Audition session + kXMP_SESFile = 0x53455320UL, + /// Public file format constant: 'CEL ', Audition loop + kXMP_CELFile = 0x43454C20UL, + /// Public file format constant: 'MPEG' + kXMP_MPEGFile = 0x4D504547UL, + /// Public file format constant: 'MP2 ' + kXMP_MPEG2File = 0x4D503220UL, + /// Public file format constant: 'MP4 ', ISO 14494-12 and -14 + kXMP_MPEG4File = 0x4D503420UL, + /// Public file format constant: 'MXF ' + kXMP_MXFFile = 0x4D584620UL, + /// Public file format constant: 'WMAV', Windows Media Audio and Video + kXMP_WMAVFile = 0x574D4156UL, + /// Public file format constant: 'AIFF' + kXMP_AIFFFile = 0x41494646UL, + /// Public file format constant: 'RED ', RED file format + kXMP_REDFile = 0x52454420UL, + /// Public file format constant: 'P2 ', a collection not really a single file + kXMP_P2File = 0x50322020UL, + /// Public file format constant: 'XDCF', a collection not really a single file + kXMP_XDCAM_FAMFile = 0x58444346UL, + /// Public file format constant: 'XDCS', a collection not really a single file + kXMP_XDCAM_SAMFile = 0x58444353UL, + /// Public file format constant: 'XDCX', a collection not really a single file + kXMP_XDCAM_EXFile = 0x58444358UL, + /// Public file format constant: 'AVHD', a collection not really a single file + kXMP_AVCHDFile = 0x41564844UL, + /// Public file format constant: 'SHDV', a collection not really a single file + kXMP_SonyHDVFile = 0x53484456UL, + /// Public file format constant: 'CNXF', a collection not really a single file + kXMP_CanonXFFile = 0x434E5846UL, + /// Public file format constant: 'AVCU', a collection not really a single file + kXMP_AVCUltraFile = 0x41564355UL, + + /// Public file format constant: 'HTML' + kXMP_HTMLFile = 0x48544D4CUL, + /// Public file format constant: 'XML ' + kXMP_XMLFile = 0x584D4C20UL, + /// Public file format constant: 'text' + kXMP_TextFile = 0x74657874UL, + /// Public file format constant: 'SVG ' + kXMP_SVGFile = 0x53564720UL, + + // ------------------------------- + // Adobe application file formats. + + /// Adobe application file format constant: 'PSD ' + kXMP_PhotoshopFile = 0x50534420UL, + /// Adobe application file format constant: 'AI ' + kXMP_IllustratorFile = 0x41492020UL, + /// Adobe application file format constant: 'INDD' + kXMP_InDesignFile = 0x494E4444UL, + /// Adobe application file format constant: 'AEP ' + kXMP_AEProjectFile = 0x41455020UL, + /// Adobe application file format constant: 'AET ', After Effects Project Template + kXMP_AEProjTemplateFile = 0x41455420UL, + /// Adobe application file format constant: 'FFX ' + kXMP_AEFilterPresetFile = 0x46465820UL, + /// Adobe application file format constant: 'NCOR' + kXMP_EncoreProjectFile = 0x4E434F52UL, + /// Adobe application file format constant: 'PRPJ' + kXMP_PremiereProjectFile = 0x5052504AUL, + /// Adobe application file format constant: 'PRTL' + kXMP_PremiereTitleFile = 0x5052544CUL, + /// Adobe application file format constant: 'UCF ', Universal Container Format + kXMP_UCFFile = 0x55434620UL, + + // ------- + // Others. + + /// Unknown file format constant: ' ' + kXMP_UnknownFile = 0x20202020UL + +}; + +/// Type for file format identification constants. See \c #kXMP_PDFFile and following. +typedef XMP_Uns32 XMP_FileFormat; + +// ------------------------------------------------------------------------------------------------- + +/// @brief Byte-order masks, do not use directly +enum { + kXMP_CharLittleEndianMask = 1, + kXMP_Char16BitMask = 2, + kXMP_Char32BitMask = 4 +}; + +/// @brief Constants to allow easy testing for 16/32 bit and big/little endian. +enum { + /// 8-bit + kXMP_Char8Bit = 0, + /// 16-bit big-endian + kXMP_Char16BitBig = kXMP_Char16BitMask, + /// 16-bit little-endian + kXMP_Char16BitLittle = kXMP_Char16BitMask | kXMP_CharLittleEndianMask, + /// 32-bit big-endian + kXMP_Char32BitBig = kXMP_Char32BitMask, + /// 32-bit little-endian + kXMP_Char32BitLittle = kXMP_Char32BitMask | kXMP_CharLittleEndianMask, + /// Variable or not-yet-known cases + kXMP_CharUnknown = 1 +}; + +/// \name Macros to test components of the character form mask +/// @{ +/// +/// \def XMP_CharFormIs16Bit +/// \brief Macro reports the encoding of a character. +/// \param f The character to check. +/// +/// \def XMP_CharFormIs32Bit +/// \brief Macro reports the encoding of a character. +/// \param f The character to check. +/// +/// \def XMP_CharFormIsBigEndian +/// \brief Macro reports the byte-order of a character. +/// \param f The character to check. +/// +/// \def XMP_CharFormIsLittleEndian +/// \brief Macro reports the byte-order of a character. +/// \param f The character to check. +/// +/// \def XMP_GetCharSize +/// \brief Macro reports the byte-size of a character. +/// \param f The character to check. +/// +/// \def XMP_CharToSerializeForm +/// \brief Macro converts \c XMP_Uns8 to \c XMP_OptionBits. +/// \param cf The character to convert. +/// +/// \def XMP_CharFromSerializeForm +/// \brief Macro converts \c XMP_OptionBits to \c XMP_Uns8. +/// \param sf The character to convert. +/// +/// @} + +#define XMP_CharFormIs16Bit(f) ( ((int)(f) & kXMP_Char16BitMask) != 0 ) +#define XMP_CharFormIs32Bit(f) ( ((int)(f) & kXMP_Char32BitMask) != 0 ) +#define XMP_CharFormIsBigEndian(f) ( ((int)(f) & kXMP_CharLittleEndianMask) == 0 ) +#define XMP_CharFormIsLittleEndian(f) ( ((int)(f) & kXMP_CharLittleEndianMask) != 0 ) +#define XMP_GetCharSize(f) ( ((int)(f)&6) == 0 ? 1 : (int)(f)&6 ) +#define XMP_CharToSerializeForm(cf) ( (XMP_OptionBits)(cf) ) +#define XMP_CharFromSerializeForm(sf) ( (XMP_Uns8)(sf) ) + +/// \def kXMPFiles_UnknownOffset +/// \brief Constant for an unknown packet offset within a file. +#define kXMPFiles_UnknownOffset ((XMP_Int64)-1) + +/// \def kXMPFiles_UnknownLength +/// \brief Constant for an unknown packet length within a file. +#define kXMPFiles_UnknownLength ((XMP_Int32)-1) + +/// @brief XMP packet description +struct XMP_PacketInfo { + + /// Packet offset in the file in bytes, -1 if unknown. + XMP_Int64 offset; + /// Packet length in the file in bytes, -1 if unknown. + XMP_Int32 length; + /// Packet padding size in bytes, zero if unknown. + XMP_Int32 padSize; // Zero if unknown. + + /// Character format using the values \c kXMP_Char8Bit, \c kXMP_Char16BitBig, etc. + XMP_Uns8 charForm; + /// True if there is a packet wrapper and the trailer says writeable by dumb packet scanners. + XMP_Bool writeable; + /// True if there is a packet wrapper, the "" XML processing instructions. + XMP_Bool hasWrapper; + + /// Padding to make the struct's size be a multiple 4. + XMP_Uns8 pad; + + /// Default constructor. + XMP_PacketInfo() : offset(kXMPFiles_UnknownOffset), length(kXMPFiles_UnknownLength), + padSize(0), charForm(0), writeable(0), hasWrapper(0), pad(0) {}; + +}; + +/// @brief Version of the XMP_PacketInfo type +enum { + /// Version of the XMP_PacketInfo type + kXMP_PacketInfoVersion = 3 +}; + +// ------------------------------------------------------------------------------------------------- + +/// @brief Option bit flags for \c TXMPFiles::Initialize(). +enum { + /// Ignore non-XMP text that uses an undefined "local" encoding. + kXMPFiles_IgnoreLocalText = 0x0002, + /// Combination of flags necessary for server products using XMPFiles. + kXMPFiles_ServerMode = kXMPFiles_IgnoreLocalText +}; + +/// @brief Option bit flags for \c TXMPFiles::GetFormatInfo(). +enum { + + /// Can inject first-time XMP into an existing file. + kXMPFiles_CanInjectXMP = 0x00000001, + + /// Can expand XMP or other metadata in an existing file. + kXMPFiles_CanExpand = 0x00000002, + + /// Can copy one file to another, writing new metadata. + kXMPFiles_CanRewrite = 0x00000004, + + /// Can expand, but prefers in-place update. + kXMPFiles_PrefersInPlace = 0x00000008, + + /// Supports reconciliation between XMP and other forms. + kXMPFiles_CanReconcile = 0x00000010, + + /// Allows access to just the XMP, ignoring other forms. + kXMPFiles_AllowsOnlyXMP = 0x00000020, + + /// File handler returns raw XMP packet information. + kXMPFiles_ReturnsRawPacket = 0x00000040, + + /// The file handler does the file open and close. + kXMPFiles_HandlerOwnsFile = 0x00000100, + + /// The file handler allows crash-safe file updates. + kXMPFiles_AllowsSafeUpdate = 0x00000200, + + /// The file format needs the XMP packet to be read-only. + kXMPFiles_NeedsReadOnlyPacket = 0x00000400, + + /// The file handler uses a "sidecar" file for the XMP. + kXMPFiles_UsesSidecarXMP = 0x00000800, + + /// The format is folder oriented, for example the P2 video format. + kXMPFiles_FolderBasedFormat = 0x00001000, + + /// The file Handler is capable of notifying progress notifications + kXMPFiles_CanNotifyProgress = 0x00002000, + + /// The plugin handler is not capable for delay loading + kXMPFiles_NeedsPreloading = 0x00004000, + +}; + +/// @brief Option bit flags for \c TXMPFiles::OpenFile(). +enum { + + /// Open for read-only access. + kXMPFiles_OpenForRead = 0x00000001, + + /// Open for reading and writing. + kXMPFiles_OpenForUpdate = 0x00000002, + + /// Only the XMP is wanted, allows space/time optimizations. + kXMPFiles_OpenOnlyXMP = 0x00000004, + + /// Force use of the given handler (format), do not even verify the format. + kXMPFiles_ForceGivenHandler = 0x00000008, + + /// Be strict about only attempting to use the designated file handler, no fallback to other handlers. + kXMPFiles_OpenStrictly = 0x00000010, + + /// Require the use of a smart handler. + kXMPFiles_OpenUseSmartHandler = 0x00000020, + + /// Force packet scanning, do not use a smart handler. + kXMPFiles_OpenUsePacketScanning = 0x00000040, + + /// Only packet scan files "known" to need scanning. + kXMPFiles_OpenLimitedScanning = 0x00000080, + + /// Attempt to repair a file opened for update, default is to not open (throw an exception). + kXMPFiles_OpenRepairFile = 0x00000100, + + /// When updating a file, spend the effort necessary to optimize file layout. + kXMPFiles_OptimizeFileLayout = 0x00000200 + +}; + +/// @brief Option bit flags for \c TXMPFiles::CloseFile(). +enum { + /// Write into a temporary file and swap for crash safety. + kXMPFiles_UpdateSafely = 0x0001 +}; + + +// ================================================================================================= +// Error notification and Exceptions +// ================================= + +/// \name Error notification and Exceptions +/// @{ +/// +/// @details From the beginning through version 5.5, XMP Tookit errors result in throwing an \c XMP_Error +/// exception. For the most part exceptions were thrown early and thus API calls aborted as soon as +/// an error was detected. Starting in version 5.5, support has been added for notifications of +/// errors arising in calls to \c TXMPMeta and \c TXMPFiles functions. +/// +/// A client can register an error notification callback function for a \c TXMPMeta or \c TXMPFiles +/// object. This can be done as a global default or individually to each object. The global default +/// applies to all objects created after it is registered. Within the object there is no difference +/// between the global default or explicitly registered callback. The callback function returns a +/// \c bool value indicating if recovery should be attempted (true) or an exception thrown (false). +/// If no callback is registered, a best effort at recovery and continuation will be made with an +/// exception thrown if recovery is not possible. More details can be found in the \c TXMPMeta and +/// \c TXMPFiles documentation. +/// +/// The \c XMP_Error class contains a numeric code and an English explanation. New numeric codes may +/// be added at any time. There are typically many possible explanations for each numeric code. The +/// explanations try to be precise about the specific circumstances causing the error. +/// +/// \note The explanation string is for debugging use only. It must not be shown to users in a +/// final product. It is written for developers not users, and never localized. + +typedef XMP_Uns8 XMP_ErrorSeverity; + +/// @brief Severity codes for error notifications +enum { + /// Partial recovery and continuation is possible. + kXMPErrSev_Recoverable = 0, + /// Recovery is not possible, an exception will be thrown aborting the API call. + kXMPErrSev_OperationFatal = 1, + /// Recovery is not possible, an exception will be thrown, the file is corrupt and possibly unusable. + kXMPErrSev_FileFatal = 2, + /// Recovery is not possible, an exception will be thrown, the entire process should be aborted. + kXMPErrSev_ProcessFatal = 3 +}; + +// ------------------------------------------------------------------------------------------------- +/// @brief The signature of a client-defined callback for TXMPMeta error notifications. +/// +/// @param context A pointer used to carry client-private context. +/// +/// @param severity The severity of the error, see the \c XMP_ErrorSeverity values. +/// +/// @param cause A numeric code for the cause of the error, from the XMP_Error exception codes. +/// Codes used with TXMPMeta error notifications: +/// \li \c kXMPErr_BadXML - An XML syntax error found during parsing. +/// \li \c kXMPErr_BadRDF - A syntax or semantic parsing error in the XMP subset of RDF. +/// \li \c kXMPErr_BadXMP - A semantic XMP data model error. +/// \li \c kXMPErr_BadValue - An XMP value error, wrong type, out of range, etc. +/// \li \c kXMPErr_NoMemory - A heap allocation failure. +/// +/// @param message An explanation of the error, for debugging use only. This should not be displayed +/// to users in a final product. +/// +/// @return True if the operation should continue with a best effort attempt at recovery, false if +/// it should be aborted with an exception thrown from the library back to the original caller. +/// Recovery is possible only if the severity is kXMPErrSev_Recoverable, an exception will be +/// thrown on return from the callback in all other cases. +/// +/// @see \c TXMPMeta::SetDefaultErrorCallback() and \c TXMPMeta::SetErrorCallback() + +typedef bool (* XMPMeta_ErrorCallbackProc) ( void* context, XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr message ); + +// ------------------------------------------------------------------------------------------------- +/// @brief The signature of a client-defined callback for TXMPFiles error notifications. +/// +/// @param context A pointer used to carry client-private context. +/// +/// @param filePath The path for the file involved in the error. +/// +/// @param severity The severity of the error, see the \c XMP_ErrorSeverity values. +/// +/// @param cause A numeric code for the cause of the error, from the XMP_Error exception codes. +/// Codes used with TXMPFiles error notifications: +/// \li \c kXMPErr_NoFile - A file does not exist +/// \li \c kXMPErr_FilePermission - A file exists but cannot be opened +/// \li \c kXMPErr_FilePathNotAFile - A path exists which is not a file +/// \li \c dXMPErr_RejectedFileExtension - Any Operation called on rejected file extension +/// \li \c KXMPErr_NoFileHandler - No suitable handler is found for the file +/// \li \c kXMPErr_DiskSpace - A file write fails due to lack of disk space +/// \li \c kXMPErr_ReadError - A file read fails +/// \li \c kXMPErr_WriteError - A file write fails for some other reason than space +/// \li \c kXMPErr_BadFileFormat - A file is corrupt or ill-formed +/// \li \c kXMPErr_BadBlockFormat - A portion of a file is corrupt or ill-formed +/// \li \c kXMPErr_BadValue - An XMP or non-XMP metadata item has an invalid value +/// \li \c kXMPErr_NoMemory - A heap allocation failure +/// +/// @param message An explanation of the error, for debugging use only. This should not be displayed +/// to users in a final product. +/// +/// @return True if the operation should continue with a best effort attempt at recovery, false if +/// it should be aborted with an exception thrown from the library back to the original caller. +/// Recovery is possible only if the severity is kXMPErrSev_Recoverable, an exception will be +/// thrown on return from the callback in all other cases. +/// +/// @see \c TXMPFiles::SetDefaultErrorCallback() and \c TXMPFiles::SetErrorCallback() + +typedef bool (* XMPFiles_ErrorCallbackProc) ( void* context, XMP_StringPtr filePath, XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr message ); + +// ------------------------------------------------------------------------------------------------- +/// Internal: The signatures of client-side wrappers for the error notification callbacks. + +typedef XMP_Bool (* XMPMeta_ErrorCallbackWrapper) ( XMPMeta_ErrorCallbackProc clientProc, void* context, + XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr message ); + +typedef XMP_Bool (* XMPFiles_ErrorCallbackWrapper) ( XMPFiles_ErrorCallbackProc clientProc, void* context, + XMP_StringPtr filePath, XMP_ErrorSeverity severity, + XMP_Int32 cause, XMP_StringPtr message ); + +/// XMP Toolkit error, associates an error code with a descriptive error string. +class XMP_Error { +public: + + /// @brief Constructor for an XMP_Error. + /// + /// @param _id The numeric code. + /// + /// @param _errMsg The descriptive string, for debugging use only. It must not be shown to users + /// in a final product. It is written for developers, not users, and never localized. + XMP_Error ( XMP_Int32 _id, XMP_StringPtr _errMsg ) : id(_id), errMsg(_errMsg), notified(false) {}; + + /// Retrieves the numeric code from an XMP_Error. + inline XMP_Int32 GetID() const { return id; }; + + /// Retrieves the descriptive string from an XMP_Error. + inline XMP_StringPtr GetErrMsg() const { return errMsg; }; + + /// Retrieves the information whether particular error is notified or not + inline XMP_Bool IsNotified() const { return notified; } + + /// Sets the notification status for an error + inline void SetNotified() { notified = true; }; + +private: + /// Exception code. See constants \c #kXMPErr_Unknown and following. + XMP_Int32 id; + /// Descriptive string, for debugging use only. It must not be shown to users in a final + /// product. It is written for developers, not users, and never localized. + XMP_StringPtr errMsg; + /// Variable to store whether this particular error is notified to user or not + XMP_Bool notified; +}; + +/// @brief XMP_Error exception code constants +enum { + + // -------------------- + /// Generic error codes. + + /// No error + kXMPErr_NoError = -1, + + /// Generic unknown error + kXMPErr_Unknown = 0, + /// Generic undefined error + kXMPErr_TBD = 1, + /// Generic unavailable error + kXMPErr_Unavailable = 2, + /// Generic bad object error + kXMPErr_BadObject = 3, + /// Generic bad parameter error + kXMPErr_BadParam = 4, + /// Generic bad value error + kXMPErr_BadValue = 5, + /// Generic assertion failure + kXMPErr_AssertFailure = 6, + /// Generic enforcement failure + kXMPErr_EnforceFailure = 7, + /// Generic unimplemented error + kXMPErr_Unimplemented = 8, + /// Generic internal failure + kXMPErr_InternalFailure = 9, + /// Generic deprecated error + kXMPErr_Deprecated = 10, + /// Generic external failure + kXMPErr_ExternalFailure = 11, + /// Generic user abort error + kXMPErr_UserAbort = 12, + /// Generic standard exception + kXMPErr_StdException = 13, + /// Generic unknown exception + kXMPErr_UnknownException = 14, + /// Generic out-of-memory error + kXMPErr_NoMemory = 15, + /// Progress reporting callback requested abort + kXMPErr_ProgressAbort = 16, + + // ------------------------------------ + // More specific parameter error codes. + + /// Bad schema parameter + kXMPErr_BadSchema = 101, + /// Bad XPath parameter + kXMPErr_BadXPath = 102, + /// Bad options parameter + kXMPErr_BadOptions = 103, + /// Bad index parameter + kXMPErr_BadIndex = 104, + /// Bad iteration position + kXMPErr_BadIterPosition = 105, + /// XML parsing error (deprecated) + kXMPErr_BadParse = 106, + /// Serialization error + kXMPErr_BadSerialize = 107, + /// File format error + kXMPErr_BadFileFormat = 108, + /// No file handler found for format + kXMPErr_NoFileHandler = 109, + /// Data too large for JPEG file format + kXMPErr_TooLargeForJPEG = 110, + /// A file does not exist + kXMPErr_NoFile = 111, + /// A file exists but cannot be opened + kXMPErr_FilePermission = 112, + /// A file write failed due to lack of disk space + kXMPErr_DiskSpace = 113, + /// A file read failed + kXMPErr_ReadError = 114, + /// A file write failed for a reason other than lack of disk space + kXMPErr_WriteError = 115, + /// A block of a file is ill-formed, e.g. invalid IPTC-IIM in a photo + kXMPErr_BadBlockFormat = 116, + /// File Path is not a file + kXMPErr_FilePathNotAFile = 117, + /// Rejected File extension + kXMPErr_RejectedFileExtension = 118, + + // ----------------------------------------------- + // File format and internal structure error codes. + + /// XML format error + kXMPErr_BadXML = 201, + /// RDF format error + kXMPErr_BadRDF = 202, + /// XMP format error + kXMPErr_BadXMP = 203, + /// Empty iterator + kXMPErr_EmptyIterator = 204, + /// Unicode error + kXMPErr_BadUnicode = 205, + /// TIFF format error + kXMPErr_BadTIFF = 206, + /// JPEG format error + kXMPErr_BadJPEG = 207, + /// PSD format error + kXMPErr_BadPSD = 208, + /// PSIR format error + kXMPErr_BadPSIR = 209, + /// IPTC format error + kXMPErr_BadIPTC = 210, + /// MPEG format error + kXMPErr_BadMPEG = 211 + +}; + +/// @} + +// ================================================================================================= +// Client callbacks +// ================ + +// ------------------------------------------------------------------------------------------------- +/// \name Special purpose callback functions +/// @{ + +/// @brief A signed 32-bit integer used as a status result for the output callback routine, +/// \c XMP_TextOutputProc. Zero means no error, all other values except -1 are private to the callback. +/// The callback is wrapped to prevent exceptions being thrown across DLL boundaries. Any exceptions +/// thrown out of the callback cause a return status of -1. + +typedef XMP_Int32 XMP_Status; + +// ------------------------------------------------------------------------------------------------- +/// @brief The signature of a client-defined callback for text output from XMP Toolkit debugging +/// operations. +/// @details The callback is invoked one or more times for each line of output. The end of a line +/// is signaled by a '\\n' character at the end of the buffer. Formatting newlines are never present +/// in the middle of a buffer, but values of properties might contain any UTF-8 characters. +/// +/// @param refCon A pointer to client-defined data passed to the TextOutputProc. +/// +/// @param buffer A string containing one line of output. +/// +/// @param bufferSize The number of characters in the output buffer. +/// +/// @return A success/fail status value. Any failure result aborts the output. +/// +/// @see \c TXMPMeta::DumpObject() + +typedef XMP_Status (* XMP_TextOutputProc) ( void * refCon, + XMP_StringPtr buffer, + XMP_StringLen bufferSize ); + +// ------------------------------------------------------------------------------------------------- +/// @brief The signature of a client-defined callback to check for a user request to abort a time-consuming +/// operation within XMPFiles. +/// +/// @param arg A pointer to caller-defined data passed from the registration call. +/// +/// @return True to abort the current operation, which results in an exception being thrown. +/// +/// @see \c TXMPFiles::SetAbortProc() + +typedef bool (* XMP_AbortProc) ( void * arg ); + +// ------------------------------------------------------------------------------------------------- +/// @brief The signature of a client-defined callback for progress report notifications. +/// +/// @param context A pointer used to carry client-private context. +/// +/// @param elapsedTime The time in seconds since the progress reporting started. +/// +/// @param fractionDone A float value estimating the amount of work already done, in the range of +/// 0.0 to 1.0. A value of 0.0 is given if the amount is not known, this happens if there is no +/// estimate total for the total work. The units of work are not defined, but should usually be +/// related to the number of bytes of I/O. This will go backwards if total work estimate changes. +/// +/// @param secondsToGo A float value estimating the number of seconds left to complete the file +/// operation. A value of 0.0 is given if the amount is not known, this happens if the amount of +/// total work is unknown. This can go backwards according to throughput or if work estimate changes. +/// +/// @return True if the file operation should continue, false if it should be aborted with an +/// exception being thrown from the XMPFiles library back to the original caller. +/// +/// @see \c TXMPFiles::SetDefaultProgressCallback() and \c TXMPFiles::SetProgressCallback() + +typedef bool (* XMP_ProgressReportProc) ( void * context, float elapsedTime, float fractionDone, float secondsToGo ); + +// ------------------------------------------------------------------------------------------------- +/// Internal: The signature of a client-side wrapper for the progress report callback. + +typedef XMP_Bool (* XMP_ProgressReportWrapper) ( XMP_ProgressReportProc proc, void * context, + float elapsedTime, float fractionDone, float secondsToGo ); + +/// @} + +// ================================================================================================= +// Stuff with no better place to be +// ================================ + +/// XMP Toolkit version information +typedef struct XMP_VersionInfo { + /// The primary release number, the "1" in version "1.2.3". + XMP_Uns8 major; + /// The secondary release number, the "2" in version "1.2.3". + XMP_Uns8 minor; + /// The tertiary release number, the "3" in version "1.2.3". + XMP_Uns8 micro; + /// A 0/1 boolean value, true if this is a debug build. + XMP_Bool isDebug; + /// A rolling build number, monotonically increasing in a release. + XMP_Uns32 build; + /// Individual feature implementation flags. + XMP_Uns32 flags; + /// A comprehensive version information string. + XMP_StringPtr message; +} XMP_VersionInfo; + +// ================================================================================================= + +#if __cplusplus +} // extern "C" +#endif + +#include +#endif // __XMP_Const_h__ diff --git a/xmp/XMP_Environment.h b/xmp/XMP_Environment.h new file mode 100644 index 0000000..4a88b0b --- /dev/null +++ b/xmp/XMP_Environment.h @@ -0,0 +1,192 @@ +#ifndef __XMP_Environment_h__ +#define __XMP_Environment_h__ 1 + +// ================================================================================================= +// XMP_Environment.h - Build environment flags for the XMP toolkit. +// ================================================================ +// +// This header is just C preprocessor macro definitions to set up the XMP toolkit build environment. +// It must be the first #include in any chain since it might affect things in other #includes. +// +// ================================================================================================= + +// ================================================================================================= +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +// ================================================================================================= +// Determine the Platform +// ====================== + +// One of MAC_ENV, WIN_ENV, UNIX_ENV or IOS_ENV must be defined by the client. Since some other code +// requires these to be defined without values, they are only used here to define XMP-specific +// macros with 0 or 1 values. + +// ! Tempting though it might be to have a standard macro for big or little endian, there seems to +// ! be no decent way to do that on our own in UNIX. Forcing it on the client isn't acceptable. + +#if defined ( MAC_ENV ) + + #if 0 // ! maybe someday - ! MAC_ENV + #error "MAC_ENV must be defined so that \"#if MAC_ENV\" is true" + #endif + + #if defined ( WIN_ENV ) || defined ( UNIX_ENV ) || defined ( IOS_ENV ) + #error "XMP environment error - must define only one of MAC_ENV, WIN_ENV, UNIX_ENV or IOS_ENV" + #endif + + #define XMP_MacBuild 1 + #define XMP_WinBuild 0 + #define XMP_UNIXBuild 0 + #define XMP_iOSBuild 0 + +#elif defined ( WIN_ENV ) + + #if 0 // ! maybe someday - ! WIN_ENV + #error "WIN_ENV must be defined so that \"#if WIN_ENV\" is true" + #endif + + #if defined ( MAC_ENV ) || defined ( UNIX_ENV ) || defined ( IOS_ENV ) + #error "XMP environment error - must define only one of MAC_ENV, WIN_ENV, UNIX_ENV or IOS_ENV" + #endif + + #define XMP_MacBuild 0 + #define XMP_WinBuild 1 + #define XMP_UNIXBuild 0 + #define XMP_iOSBuild 0 + +#elif defined ( UNIX_ENV ) + + #if 0 // ! maybe someday - ! UNIX_ENV + #error "UNIX_ENV must be defined so that \"#if UNIX_ENV\" is true" + #endif + + #if defined ( MAC_ENV ) || defined ( WIN_ENV ) || defined ( IOS_ENV ) + #error "XMP environment error - must define only one of MAC_ENV, WIN_ENV, UNIX_ENV or IOS_ENV" + #endif + + #define XMP_MacBuild 0 + #define XMP_WinBuild 0 + #define XMP_UNIXBuild 1 + #define XMP_iOSBuild 0 + +#elif defined ( IOS_ENV ) + + #if 0 // ! maybe someday - ! IOS_ENV + #error "IOS_ENV must be defined so that \"#if IOS_ENV\" is true" + #endif + + #if defined ( MAC_ENV ) || defined ( WIN_ENV ) || defined ( UNIX_ENV ) + #error "XMP environment error - must define only one of MAC_ENV, WIN_ENV, UNIX_ENV or IOS_ENV" + #endif + + #define XMP_MacBuild 0 + #define XMP_WinBuild 0 + #define XMP_UNIXBuild 0 + #define XMP_iOSBuild 1 + +#else + + #error "XMP environment error - must define one of MAC_ENV, WIN_ENV, UNIX_ENV or IOS_ENV" + +#endif + +// ================================================================================================= +// Common Macros +// ============= + +#if defined ( DEBUG ) + #if defined ( NDEBUG ) + #error "XMP environment error - both DEBUG and NDEBUG are defined" + #endif + #define XMP_DebugBuild 1 +#endif + +#if defined ( NDEBUG ) + #define XMP_DebugBuild 0 +#endif + +#ifndef XMP_DebugBuild + #define XMP_DebugBuild 0 +#endif + +#if XMP_DebugBuild + #include // The assert macro needs printf. +#endif + +#ifndef DISABLE_SERIALIZED_IMPORT_EXPORT + #define DISABLE_SERIALIZED_IMPORT_EXPORT 0 +#endif + +#ifndef XMP_64 + #if _WIN64 || defined(_LP64) + #define XMP_64 1 + #else + #define XMP_64 0 + #endif +#endif + +// ================================================================================================= +// Macintosh Specific Settings +// =========================== +#if (XMP_MacBuild) + #define XMP_HELPER_DLL_IMPORT __attribute__((visibility("default"))) + #define XMP_HELPER_DLL_EXPORT __attribute__((visibility("default"))) + #define XMP_HELPER_DLL_PRIVATE __attribute__((visibility("hidden"))) + #define APICALL +#endif + +// ================================================================================================= +// Windows Specific Settings +// ========================= +#if (XMP_WinBuild) + #define XMP_HELPER_DLL_IMPORT + #define XMP_HELPER_DLL_EXPORT + #define XMP_HELPER_DLL_PRIVATE + #define APICALL __stdcall +#endif + +// ================================================================================================= +// UNIX Specific Settings +// ====================== +#if (XMP_UNIXBuild) + #define XMP_HELPER_DLL_IMPORT + #define XMP_HELPER_DLL_EXPORT __attribute__ ((visibility ("default"))) + #define XMP_HELPER_DLL_PRIVATE __attribute__ ((visibility ("hidden"))) + #define APICALL +#endif + +// ================================================================================================= +// IOS Specific Settings +// =========================== +#if (XMP_iOSBuild) + #include + #if (TARGET_CPU_ARM) + #define XMP_IOS_ARM 1 + #else + #define XMP_IOS_ARM 0 + #endif + #define XMP_HELPER_DLL_IMPORT __attribute__((visibility("default"))) + #define XMP_HELPER_DLL_EXPORT __attribute__((visibility("default"))) + #define XMP_HELPER_DLL_PRIVATE __attribute__((visibility("hidden"))) + #define APICALL +#endif + +// ================================================================================================= + +#if (XMP_DynamicBuild) + #define XMP_PUBLIC XMP_HELPER_DLL_EXPORT + #define XMP_PRIVATE XMP_HELPER_DLL_PRIVATE +#elif (XMP_StaticBuild) + #define XMP_PUBLIC + #define XMP_PRIVATE +#else + #define XMP_PUBLIC XMP_HELPER_DLL_IMPORT + #define XMP_PRIVATE XMP_HELPER_DLL_PRIVATE +#endif + +#endif // __XMP_Environment_h__ diff --git a/xmp/XMP_IO.hpp b/xmp/XMP_IO.hpp new file mode 100644 index 0000000..d485392 --- /dev/null +++ b/xmp/XMP_IO.hpp @@ -0,0 +1,171 @@ +#ifndef __XMP_IO_hpp__ +#define __XMP_IO_hpp__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2010 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMP_Environment.h" // ! XMP_Environment.h must be the first included header. + +#include "XMP_Const.h" + +// ================================================================================================= +/// \class XMP_IO XMP_IO.hpp +/// \brief Abstract base class for client-managed I/O with \c TXMPFiles. +/// +/// \c XMP_IO is an abstract base class for client-managed I/O with \c TXMPFiles. This allows a +/// client to use the embedded metadata processing logic of \c TXMPFiles in cases where a string +/// file path cannot be provided, or where it is impractical to allow \c TXMPFiles to separately +/// open the file and do its own I/O. Although described in terms of files, any form of storage may +/// be used as long as the functions operate as defined. +/// +/// This is not a general purpose I/O class. It contains only the necessary functions needed by the +/// internals of \c TXMPFiles. It is intended to be used as an adaptor for an existing I/O mechanism +/// that the client wants \c TXMPFiles to use. +/// +/// To use \c XMP_IO, a client creates a derived class then uses the form of \c TCMPFiles::OpenFile +/// that takes an \c XMP_IO parameter instead of a string file path. The derived \c XMP_IO object +/// must be ready for use when \c TCMPFiles::OpenFile is called. +/// +/// There are no Open or Close functions in \c XMP_IO, they are specific to each implementation. The +/// derived \c XMP_IO object must be open and ready for use before being passed to \c +/// TXMP_Files::OpenFile, and remain open and ready for use until \c TXMP_Files::CloseFile returns, +/// or some other fatal error occurs. The client has final responsibility for closing and +/// terminating the derived \c XMP_IO object. +// ================================================================================================= + +class XMP_IO { +public: + + // --------------------------------------------------------------------------------------------- + /// @brief Read into a buffer, returning the number of bytes read. + /// + /// Read into a buffer, returning the number of bytes read. Returns the actual number of bytes + /// read. Throws an exception if requireSuccess is true and not enough data is available. + /// Throwing \c XMPError is recommended. The buffer content and I/O position after a throw are + /// undefined. + /// + /// @param buffer A pointer to the buffer. + /// @param count The length of the buffer in bytes. + /// @param readAll True if reading less than the requested amount is a failure. + /// + /// @return Returns the number of bytes read. + + enum { kReadAll = true }; + + virtual XMP_Uns32 Read ( void* buffer, XMP_Uns32 count, bool readAll = false ) = 0; + + inline XMP_Uns32 ReadAll ( void* buffer, XMP_Uns32 bytes ) + { return this->Read ( buffer, bytes, kReadAll ); }; + + // --------------------------------------------------------------------------------------------- + /// @brief Write from a buffer. + /// + /// Write from a buffer, overwriting existing data and extending the file as necesary. All data + /// must be written or an exception thrown. Throwing \c XMPError is recommended. + /// + /// @param buffer A pointer to the buffer. + /// @param count The length of the buffer in bytes. + + virtual void Write ( const void* buffer, XMP_Uns32 count ) = 0; + + // --------------------------------------------------------------------------------------------- + /// @brief Set the I/O position, returning the new absolute offset in bytes. + /// + /// Set the I/O position, returning the new absolute offset in bytes. The offset parameter may + /// be positive or negative. A seek beyond EOF is allowed when writing and extends the file, it + /// is equivalent to seeking to EOF then writing the needed amount of undefined data. A + /// read-only seek beyond EOF throws an exception. Throwing \c XMPError is recommended. + /// + /// @param offset The offset relative to the mode. + /// @param mode The mode, or origin, of the seek. + /// + /// @return The new absolute offset in bytes. + + virtual XMP_Int64 Seek ( XMP_Int64 offset, SeekMode mode ) = 0; + + inline XMP_Int64 Offset() { return this->Seek ( 0, kXMP_SeekFromCurrent ); }; + inline XMP_Int64 Rewind() { return this->Seek ( 0, kXMP_SeekFromStart ); }; // Always returns 0. + inline XMP_Int64 ToEOF() { return this->Seek ( 0, kXMP_SeekFromEnd ); }; + + // --------------------------------------------------------------------------------------------- + /// @brief Return the length of the file in bytes. + /// + /// Return the length of the file in bytes. The I/O position is unchanged. + /// + /// @return The length of the file in bytes. + + virtual XMP_Int64 Length() = 0; + + // --------------------------------------------------------------------------------------------- + /// @brief Truncate the file to the given length. + /// + /// Truncate the file to the given length. The I/O position after truncation is unchanged if + /// still valid, otherwise it is set to the new EOF. Throws an exception if the new length is + /// longer than the file's current length. Throwing \c XMPError is recommended. + /// + /// @param length The new length for the file, must be less than or equal to the current length. + + virtual void Truncate ( XMP_Int64 length ) = 0; + + // --------------------------------------------------------------------------------------------- + /// @brief Create an associated temp file for use in a safe-save style operation. + /// + /// Create an associated temp file, for example in the same directory and with a related name. + /// Returns an already existing temp with no other action. The temp must be opened for + /// read-write access. It will be used in a safe-save style operation, using some of the + /// original file plus new portions to write the temp, then replacing the original from the temp + /// when done. Throws an exception if the owning object is opened for read-only access, or if + /// the temp file cannot be created. Throwing \c XMPError is recommended. + /// + /// The temp file is normally closed and deleted, and the temporary \c XMP_IO object deleted, by + /// a call to \c AbsorbTemp or \c DeleteTemp. It must be closed and deleted by the derived \c + /// XMP_IO object's destructor if necessary. + /// + /// \c DeriveTemp may be called on a temporary \c XMP_IO object. + /// + /// @return A pointer to the associated temporary \c XMP_IO object. + + virtual XMP_IO* DeriveTemp() = 0; + + // --------------------------------------------------------------------------------------------- + /// @brief Replace the owning file's content with that of the temp. + /// + /// Used at the end of a safe-save style operation to replace the original content with that + /// from the associated temp file. The temp file must be closed and deleted after the content + /// swap. The temporary \c XMP_IO object is deleted. Throws an exception if the temp file cannot + /// be absorbed. Throwing \c XMPError is recommended. + + virtual void AbsorbTemp() = 0; + + // --------------------------------------------------------------------------------------------- + /// @brief Delete a temp file, leaving the original alone. + /// + /// Used for a failed safe-save style operation. The temp file is closed and deleted without + /// being absorbed, and the temporary \c XMP_IO object is deleted. Does nothing if no temp + /// exists. + + virtual void DeleteTemp() = 0; + + // --------------------------------------------------------------------------------------------- + + XMP_IO() {}; + virtual ~XMP_IO() {}; + +private: + + // --------------------------------------------------------------------------------------------- + /// Copy construction and assignment are not public. That would require the implementation to + /// share state across multiple XMP_IO objects. + + XMP_IO ( const XMP_IO & original ); + void operator= ( const XMP_IO& in ) { *this = in; /* Avoid Win compile warnings. */ }; + +}; + +#endif // __XMP_IO_hpp__ diff --git a/xmp/XMP_Version.h b/xmp/XMP_Version.h new file mode 100644 index 0000000..23d16b7 --- /dev/null +++ b/xmp/XMP_Version.h @@ -0,0 +1,52 @@ +#ifndef __XMP_Version_h__ +#define __XMP_Version_h__ 1 + +/* --------------------------------------------------------------------------------------------- */ +/* ** IMPORTANT ** This file must be usable by strict ANSI C compilers. No "//" comments, etc. */ +/* --------------------------------------------------------------------------------------------- */ + +/* +// ================================================================================================= +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= +*/ + +/* ============================================================================================= */ +/** +XMP Toolkit Version Information + +Version information for the XMP toolkit is stored in the executable and available through a runtime +call, SXMPMeta::GetVersionInfo. In addition a static version number is defined in this +header. The information in the executable or returned by SXMPMeta::GetVersionInfo is about +the implementation internals, it is runtime version information. The values defined in this header +describe the version of the API used at client compile time. They do not necessarily relate to the +runtime version. + +Important: Do not display the static values defined here to users as the version of XMP in use. Do +not base runtime decisions on just this static version. It is OK to compare the static and runtime +versions. + +*/ +/* ============================================================================================= */ + +#define XMPCORE_API_VERSION_MAJOR 5 +#define XMPCORE_API_VERSION_MINOR 6 +#define XMPCORE_API_VERSION_MICRO 0 + +#define XMPCORE_API_VERSION 5.6.0 +#define XMPCORE_API_VERSION_STRING "5.6.0" + +#define XMPFILES_API_VERSION_MAJOR 5 +#define XMPFILES_API_VERSION_MINOR 7 +#define XMPFILES_API_VERSION_MICRO 0 + +#define XMPFILES_API_VERSION 5.7.0 +#define XMPFILES_API_VERSION_STRING "5.7.0" + +/* ============================================================================================= */ + +#endif /* __XMP_Version_h__ */ diff --git a/xmp/client-glue/TXMPFiles.incl_cpp b/xmp/client-glue/TXMPFiles.incl_cpp new file mode 100644 index 0000000..eb19887 --- /dev/null +++ b/xmp/client-glue/TXMPFiles.incl_cpp @@ -0,0 +1,484 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +// ================================================================================================ +/// \file TXMPFiles.incl_cpp +/// \brief The implementation of the TXMPFiles template class. + +#if XMP_WinBuild + #pragma warning ( disable : 4003 ) // not enough actual parameters for macro + #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning) +#endif + +#include "client-glue/WXMP_Common.hpp" + +#include "client-glue/WXMPFiles.hpp" + +// ================================================================================================= +// Implementation Guidelines +// ========================= +// +// The implementations of the template functions are very stylized. The jobs done in this code are: +// +// 1. ... +// +// ================================================================================================= + +#ifndef XMPFiles_TraceCTorDTor + #define XMPFiles_TraceCTorDTor 0 +#endif + +#if XMPFiles_TraceCTorDTor + class XFPeek { // Hack to peek at the client ref count in the internal object. + public: + XFPeek(); + virtual ~XFPeek(); + XMP_Int32 clientRefs; + }; +#endif + +// ================================================================================================= + +XMP_MethodIntro(TXMPFiles,void):: +SetClientString ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen ) +{ + tStringObj * clientStr = (tStringObj*) clientPtr; + clientStr->assign ( valuePtr, valueLen ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +SetClientStringVector ( void * clientPtr, XMP_StringPtr * arrayPtr, XMP_Uns32 stringCount ) +{ + std::vector* clientVec = (std::vector*) clientPtr; + clientVec->clear(); + for ( XMP_Uns32 i = 0; i < stringCount; ++i ) { + tStringObj nextValue ( arrayPtr[i] ); + clientVec->push_back ( nextValue ); + } +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +GetVersionInfo ( XMP_VersionInfo * versionInfo ) +{ + WrapNoCheckVoid ( zXMPFiles_GetVersionInfo_1 ( versionInfo ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +Initialize() +{ + WrapCheckBool ( ok, zXMPFiles_Initialize_1 ( 0 ) ); + return ok; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +Initialize( const char* pluginFolder, const char* plugins ) +{ + WrapCheckBool ( ok, zXMPFiles_Initialize_2 ( 0, pluginFolder, plugins ) ); + return ok; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +Initialize ( XMP_OptionBits options ) +{ + WrapCheckBool ( ok, zXMPFiles_Initialize_1 ( options ) ); + return ok; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +Initialize ( XMP_OptionBits options, const char* pluginFolder, const char* plugins ) +{ + WrapCheckBool ( ok, zXMPFiles_Initialize_2 ( options, pluginFolder, plugins ) ); + return ok; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +Terminate() +{ + WrapNoCheckVoid ( zXMPFiles_Terminate_1() ); +} + +// ================================================================================================= + +static XMPFilesRef Default_CTor() +{ + WrapCheckXMPFilesRef ( newRef, zXMPFiles_CTor_1() ); + return newRef; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPFiles):: +TXMPFiles() : xmpFilesRef(Default_CTor()) +{ + #if XMPFiles_TraceCTorDTor + XFPeek* xfPtr = (XFPeek*)this->xmpFilesRef; + printf ( "Default construct TXMPFiles @ %.8X, ref = %.8X, count = %d\n", this, xfPtr, xfPtr->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPFiles):: +TXMPFiles ( const TXMPFiles & original ) : xmpFilesRef(original.xmpFilesRef) +{ + WXMPFiles_IncrementRefCount_1 ( this->xmpFilesRef ); + #if XMPFiles_TraceCTorDTor + XFPeek* xfPtr = (XFPeek*)this->xmpFilesRef; + printf ( "Copy construct TXMPFiles @ %.8X, ref = %.8X, count = %d\n", this, xfPtr, xfPtr->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +operator= ( const TXMPFiles & rhs ) +{ + #if XMPFiles_TraceCTorDTor + XFPeek* xfLHS = (XFPeek*)this->xmpFilesRef; + XFPeek* xfRHS = (XFPeek*)rhs.xmpFilesRef; + printf ( "Assign TXMPFiles, lhs @ %.8X, rhs @ %.8X\n", this, &rhs ); + printf ( " original lhs ref = %.8X, count = %d\n", xfLHS, xfLHS->clientRefs ); + printf ( " original rhs ref = %.8X, count = %d\n", xfRHS, xfRHS->clientRefs ); + #endif + XMPFilesRef oldRef = this->xmpFilesRef; // ! Decrement last so errors leave client object OK. + this->xmpFilesRef = rhs.xmpFilesRef; + WXMPFiles_IncrementRefCount_1 ( this->xmpFilesRef ); // Increment the count on the new ref. + WXMPFiles_DecrementRefCount_1 ( oldRef ); // Decrement the count on the old ref. + #if XMPFiles_TraceCTorDTor + printf ( " result lhs ref = %.8X, count = %d\n", xfLHS, xfLHS->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPFiles):: +TXMPFiles ( XMPFilesRef _xmpFilesRef ) : xmpFilesRef(_xmpFilesRef) +{ + WXMPFiles_IncrementRefCount_1 ( this->xmpFilesRef ); + #if XMPFiles_TraceCTorDTor + XFPeek* xfPtr = (XFPeek*)this->xmpFilesRef; + printf ( "Ref construct TXMPFiles @ %.8X, ref = %.8X, count = %d\n", this, xfPtr, xfPtr->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPFiles):: +TXMPFiles ( XMP_StringPtr filePath, + XMP_FileFormat format /* = kXMP_UnknownFile */, + XMP_OptionBits openFlags /* = 0 */ ) : xmpFilesRef(Default_CTor()) +{ + #if XMPFiles_TraceCTorDTor + XFPeek* xfPtr = (XFPeek*)this->xmpFilesRef; + printf ( "File construct TXMPFiles @ %.8X, ref = %.8X, count = %d\n", this, xfPtr, xfPtr->clientRefs ); + #endif + bool ok = this->OpenFile ( filePath, format, openFlags ); + if ( ! ok ) throw XMP_Error ( kXMPErr_NoFileHandler, "OpenFile returned false" ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPFiles):: +TXMPFiles ( const tStringObj & filePath, + XMP_FileFormat format /* = kXMP_UnknownFile */, + XMP_OptionBits openFlags /* = 0 */ ) : xmpFilesRef(Default_CTor()) +{ + #if XMPFiles_TraceCTorDTor + XFPeek* xfPtr = (XFPeek*)this->xmpFilesRef; + printf ( "File construct TXMPFiles @ %.8X, ref = %.8X, count = %d\n", this, xfPtr, xfPtr->clientRefs ); + #endif + bool ok = this->OpenFile ( filePath.c_str(), format, openFlags ); + if ( ! ok ) throw XMP_Error ( kXMPErr_NoFileHandler, "OpenFile returned false" ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPFiles):: +~TXMPFiles () throw() +{ + #if XMPFiles_TraceCTorDTor + XFPeek* xfPtr = (XFPeek*)this->xmpFilesRef; + printf ( "Destruct TXMPFiles @ %.8X, ref= %.8X, count = %d\n", this, xfPtr, xfPtr->clientRefs ); + #endif + WXMPFiles_DecrementRefCount_1 ( this->xmpFilesRef ); + this->xmpFilesRef = 0; +} + +// ================================================================================================= + +XMP_MethodIntro(TXMPFiles,bool):: +GetFormatInfo ( XMP_FileFormat format, + XMP_OptionBits * flags ) +{ + WrapCheckBool ( found, zXMPFiles_GetFormatInfo_1 ( format, flags ) ); + return found; +} + +// ================================================================================================= + +XMP_MethodIntro(TXMPFiles,XMPFilesRef):: +GetInternalRef() +{ + return this->xmpFilesRef; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,XMP_FileFormat):: +CheckFileFormat ( XMP_StringPtr filePath ) +{ + WrapCheckFormat ( format, zXMPFiles_CheckFileFormat_1 ( filePath ) ); + return format; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,XMP_FileFormat):: +CheckPackageFormat ( XMP_StringPtr folderPath ) +{ + WrapCheckFormat ( format, zXMPFiles_CheckPackageFormat_1 ( folderPath ) ); + return format; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +GetFileModDate ( XMP_StringPtr filePath, XMP_DateTime * modDate, XMP_FileFormat * format, XMP_OptionBits options ) +{ + WrapCheckBool ( ok, zXMPFiles_GetFileModDate_1 ( filePath, modDate, format, options ) ); + return ok; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +GetAssociatedResources ( XMP_StringPtr filePath, + std::vector* resourceList, + XMP_FileFormat format /* = kXMP_UnknownFile */, + XMP_OptionBits options /* = 0 */) +{ + WrapCheckBool ( ok, zXMPFiles_GetAssociatedResources_1 ( filePath, resourceList, format, options, SetClientStringVector ) ); + return ok; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +IsMetadataWritable ( XMP_StringPtr filePath, + bool * writable, + XMP_FileFormat format /* = kXMP_UnknownFile */, + XMP_OptionBits options /* = 0 */) +{ + if ( writable) + { + XMP_Bool internalWritable = ConvertBoolToXMP_Bool( *writable ); + WrapCheckBool ( ok, zXMPFiles_IsMetadataWritable_1 ( filePath, &internalWritable, format, options ) ); + *writable = ConvertXMP_BoolToBool( internalWritable ); + return ok; + } + else + { + WrapCheckBool ( ok, zXMPFiles_IsMetadataWritable_1 ( filePath, NULL, format, options ) ); + return ok; + } +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +OpenFile ( XMP_StringPtr filePath, + XMP_FileFormat format /* = kXMP_UnknownFile */, + XMP_OptionBits openFlags /* = 0 */ ) +{ + WrapCheckBool ( ok, zXMPFiles_OpenFile_1 ( filePath, format, openFlags ) ); + return ok; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +OpenFile ( const tStringObj & filePath, + XMP_FileFormat format /* = kXMP_UnknownFile */, + XMP_OptionBits openFlags /* = 0 */ ) +{ + return this->OpenFile ( filePath.c_str(), format, openFlags ); +} + +// ------------------------------------------------------------------------------------------------- + +#if XMP_StaticBuild // ! Client XMP_IO objects can only be used in static builds. +XMP_MethodIntro(TXMPFiles,bool):: +OpenFile ( XMP_IO * clientIO, + XMP_FileFormat format /* = kXMP_UnknownFile */, + XMP_OptionBits openFlags /* = 0 */ ) +{ + WrapCheckBool ( ok, zXMPFiles_OpenFile_2 ( clientIO, format, openFlags ) ); + return ok; +} +#endif + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +CloseFile ( XMP_OptionBits closeFlags /* = 0 */ ) +{ + WrapCheckVoid ( zXMPFiles_CloseFile_1 ( closeFlags ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +GetFileInfo ( tStringObj * filePath /* = 0 */, + XMP_OptionBits * openFlags /* = 0 */, + XMP_FileFormat * format /* = 0 */, + XMP_OptionBits * handlerFlags /* = 0 */ ) +{ + WrapCheckBool ( isOpen, zXMPFiles_GetFileInfo_1 ( filePath, openFlags, format, handlerFlags, SetClientString ) ); + return isOpen; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +SetAbortProc ( XMP_AbortProc abortProc, + void * abortArg ) +{ + WrapCheckVoid ( zXMPFiles_SetAbortProc_1 ( abortProc, abortArg ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +GetXMP ( SXMPMeta * xmpObj /* = 0 */, + tStringObj * xmpPacket /* = 0 */, + XMP_PacketInfo * packetInfo /* = 0 */ ) +{ + XMPMetaRef xmpRef = 0; + if ( xmpObj != 0 ) { + SXMPUtils::RemoveProperties ( xmpObj, 0, 0, kXMPUtil_DoAllProperties ); // *** Need an SXMPMeta::Clear method: + xmpRef = xmpObj->GetInternalRef(); + } + + WrapCheckBool ( hasXMP, zXMPFiles_GetXMP_1 ( xmpRef, xmpPacket, packetInfo, SetClientString ) ); + return hasXMP; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +PutXMP ( const SXMPMeta & xmpObj ) +{ + WrapCheckVoid ( zXMPFiles_PutXMP_1 ( xmpObj.GetInternalRef(), 0, 0 ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +PutXMP ( XMP_StringPtr xmpPacket, + XMP_StringLen xmpLength /* = kXMP_UseNullTermination */ ) +{ + WrapCheckVoid ( zXMPFiles_PutXMP_1 ( 0, xmpPacket, xmpLength ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +PutXMP ( const tStringObj & xmpPacket ) +{ + this->PutXMP ( xmpPacket.c_str(), (XMP_StringLen)xmpPacket.size() ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +CanPutXMP ( const SXMPMeta & xmpObj ) +{ + WrapCheckBool ( canPut, zXMPFiles_CanPutXMP_1 ( xmpObj.GetInternalRef(), 0, 0 ) ); + return canPut; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +CanPutXMP ( XMP_StringPtr xmpPacket, + XMP_StringLen xmpLength /* = kXMP_UseNullTermination */ ) +{ + WrapCheckBool ( canPut, zXMPFiles_CanPutXMP_1 ( 0, xmpPacket, xmpLength ) ); + return canPut; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +CanPutXMP ( const tStringObj & xmpPacket ) +{ + return this->CanPutXMP ( xmpPacket.c_str(), (XMP_StringLen)xmpPacket.size() ); +} + +// ================================================================================================= + +XMP_MethodIntro(TXMPFiles,void):: +SetDefaultProgressCallback ( XMP_ProgressReportProc proc, void * context /* = 0 */, + float interval /* = 1.0 */, bool sendStartStop /* = false */ ) +{ + XMP_Bool internalsendStartStop = ConvertBoolToXMP_Bool( sendStartStop ); + WrapCheckVoid ( zXMPFiles_SetDefaultProgressCallback_1 ( proc, context, interval, internalsendStartStop ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +SetProgressCallback ( XMP_ProgressReportProc proc, void * context /* = 0 */, + float interval /* = 1.0 */, bool sendStartStop /* = false */ ) +{ + XMP_Bool internalsendStartStop = ConvertBoolToXMP_Bool( sendStartStop ); + WrapCheckVoid ( zXMPFiles_SetProgressCallback_1 ( proc, context, interval, internalsendStartStop ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +SetDefaultErrorCallback ( XMPFiles_ErrorCallbackProc proc, + void * context /* = 0 */, XMP_Uns32 limit /*= 1 */ ) +{ + WrapCheckVoid ( zXMPFiles_SetDefaultErrorCallback_1 ( proc, context, limit ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +SetErrorCallback ( XMPFiles_ErrorCallbackProc proc, + void * context /* = 0 */, XMP_Uns32 limit /*= 1 */ ) +{ + WrapCheckVoid ( zXMPFiles_SetErrorCallback_1 ( proc, context, limit ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +ResetErrorCallbackLimit ( XMP_Uns32 limit /* = 1 */ ) +{ + WrapCheckVoid ( zXMPFiles_ResetErrorCallbackLimit_1 ( limit ) ); +} + +// ================================================================================================= diff --git a/xmp/client-glue/TXMPIterator.incl_cpp b/xmp/client-glue/TXMPIterator.incl_cpp new file mode 100644 index 0000000..0b39d01 --- /dev/null +++ b/xmp/client-glue/TXMPIterator.incl_cpp @@ -0,0 +1,223 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +// ================================================================================================ +/// \file TXMPIterator.incl_cpp +/// \brief The implementation of the TXMPIterator template class. + +#include "XMP.hpp" +#include "client-glue/WXMP_Common.hpp" +#include "client-glue/WXMPIterator.hpp" + +// ================================================================================================= +// Implementation Guidelines +// ========================= +// +// The implementations of the template functions are very stylized. The jobs done in this code are: +// +// 1. Set up the xmpIter template data member in the constructors. +// 2. Call through to the appropriate WXMPIterator function. +// 3. Copy returned strings and release the threading lock. +// +// The various kinds of functions follow similar patterns, first assuming no returned string: +// +// Constructors - Use an initializer for the xmpIter data member to call the WXMPIterator constructor. +// Destructor - Let the WXMPIterator destructor be implicitly called for the xmpIter data member. +// Static function - Simply call the corresponding WXMPIterator static function. +// Non-static function - Simply call the corresponding WXMPIterator function using xmpIter. +// +// If a member function has returned strings the code looks roughly like this: +// +// <<>> +// <<>> +// if ( <<>> ) { +// if ( outStr != 0 ) outStr->assign ( outPtr, outLen ); +// <<>> +// } +// return result; +// +// The <<>> is the call to the wrapper, and <<>> is the check and throw +// if the wrapper reports failure. The <<>> check is used to determine if the string +// should actually be assigned. For example, GetProperty can't assign the value if the property +// does not exist. There is no <<>> check if it isn't, well, appropriate. Outputs are +// always passed as explicit pointers, and null can be passed if the string is not wanted. The +// inner implementation holds the threading lock if an output string is returned, regardless of +// whether the client wants it or not (which the implementation does not know). +// +// ================================================================================================= + +#ifndef XMP_TraceCTorDTor + #define XMP_TraceCTorDTor 0 +#endif + +#if XMP_TraceCTorDTor + class XIPeek { // Hack to peek at the client ref count in the internal object. + public: + XIPeek(); + virtual ~XIPeek(); + XMP_Int32 clientRefs; + }; +#endif + +// ------------------------------------------------------------------------------------------------- + +#define PropIterCTor(xmpRef,schemaNS,propName,options) \ + WrapCheckIterRef ( newRef, zXMPIterator_PropCTor_1 ( xmpRef, schemaNS, propName, options ) ); \ + this->iterRef = newRef + +// ------------------------------------------------------------------------------------------------- + +#define TableIterCTor(schemaNS,propName,options) \ + WrapCheckIterRef ( newRef, zXMPIterator_TableCTor_1 ( schemaNS, propName, options ) ); \ + this->iterRef = newRef + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPIterator,void):: +SetClientString ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen ) +{ + tStringObj * clientStr = (tStringObj*) clientPtr; + clientStr->assign ( valuePtr, valueLen ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPIterator):: +TXMPIterator ( const TXMPIterator & original ) : iterRef(original.iterRef) +{ + WXMPIterator_IncrementRefCount_1 ( this->iterRef ); + #if XMP_TraceCTorDTor + XIPeek* xiPtr = (XIPeek*)this->iterRef; + printf ( "Copy construct TXMPIterator @ %.8X, ref = %.8X, count = %d\n", this, xiPtr, xiPtr->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPIterator,void):: +operator= ( const TXMPIterator & rhs ) +{ + #if XMP_TraceCTorDTor + XIPeek* xiLHS = (XIPeek*)this->iterRef; + XIPeek* xiRHS = (XIPeek*)rhs.iterRef; + printf ( "Assign TXMPIterator, lhs @ %.8X, rhs @ %.8X\n", this, &rhs ); + printf ( " original lhs ref = %.8X, count = %d\n", xiLHS, xiLHS->clientRefs ); + printf ( " original rhs ref = %.8X, count = %d\n", xiRHS, xiRHS->clientRefs ); + #endif + XMPIteratorRef oldRef = this->iterRef; // ! Decrement last so errors leave client object OK. + this->iterRef = rhs.iterRef; + WXMPIterator_IncrementRefCount_1 ( this->iterRef ); + WXMPIterator_DecrementRefCount_1 ( oldRef ); + #if XMP_TraceCTorDTor + printf ( " result lhs ref = %.8X, count = %d\n", xiLHS, xiLHS->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPIterator):: +TXMPIterator() : iterRef(0) +{ + throw XMP_Error ( kXMPErr_Unavailable, "No default construction for XMP iterators" ); + #if XMP_TraceCTorDTor + XIPeek* xiPtr = (XIPeek*)this->iterRef; + printf ( "Default construct TXMPIterator @ %.8X, ref = %.8X, count = %d\n", this, xiPtr, xiPtr->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPIterator):: +TXMPIterator ( const TXMPMeta & xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_OptionBits options /* = 0 */ ) : iterRef(0) +{ + PropIterCTor ( xmpObj.GetInternalRef(), schemaNS, propName, options ); + #if XMP_TraceCTorDTor + XIPeek* xiPtr = (XIPeek*)this->iterRef; + printf ( "Construct property TXMPIterator @ %.8X, ref = %.8X, count = %d\n", this, xiPtr, xiPtr->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPIterator):: +TXMPIterator ( const TXMPMeta & xmpObj, + XMP_StringPtr schemaNS, + XMP_OptionBits options /* = 0 */ ) : iterRef(0) +{ + PropIterCTor ( xmpObj.GetInternalRef(), schemaNS, "", options ); + #if XMP_TraceCTorDTor + XIPeek* xiPtr = (XIPeek*)this->iterRef; + printf ( "Construct schema TXMPIterator @ %.8X, ref = %.8X, count = %d\n", this, xiPtr, xiPtr->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPIterator):: +TXMPIterator ( const TXMPMeta & xmpObj, + XMP_OptionBits options /* = 0 */ ) : iterRef(0) +{ + PropIterCTor ( xmpObj.GetInternalRef(), "", "", options ); + #if XMP_TraceCTorDTor + XIPeek* xiPtr = (XIPeek*)this->iterRef; + printf ( "Construct tree TXMPIterator @ %.8X, ref = %.8X, count = %d\n", this, xiPtr, xiPtr->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPIterator):: +TXMPIterator ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_OptionBits options ) : iterRef(0) +{ + TableIterCTor ( schemaNS, propName, options ); + #if XMP_TraceCTorDTor + XIPeek* xiPtr = (XIPeek*)this->iterRef; + printf ( "Construct table TXMPIterator @ %.8X, ref = %.8X, count = %d\n", this, xiPtr, xiPtr->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPIterator):: +~TXMPIterator () throw() +{ + #if XMP_TraceCTorDTor + XIPeek* xiPtr = (XIPeek*)this->iterRef; + printf ( "Destruct TXMPIterator @ %.8X, ref = %.8X, count = %d\n", this, xiPtr, xiPtr->clientRefs ); + #endif + WXMPIterator_DecrementRefCount_1 ( this->iterRef ); + this->iterRef = 0; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPIterator,bool):: +Next ( tStringObj * schemaNS /* = 0 */, + tStringObj * propPath /* = 0 */, + tStringObj * propValue /* = 0 */, + XMP_OptionBits * options /* = 0 */ ) +{ + WrapCheckBool ( found, zXMPIterator_Next_1 ( schemaNS, propPath, propValue, options, SetClientString ) ); + return found; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPIterator,void):: +Skip ( XMP_OptionBits options ) +{ + WrapCheckVoid ( zXMPIterator_Skip_1 ( options ) ); +} + +// ================================================================================================= diff --git a/xmp/client-glue/TXMPMeta.incl_cpp b/xmp/client-glue/TXMPMeta.incl_cpp new file mode 100644 index 0000000..7f67a3a --- /dev/null +++ b/xmp/client-glue/TXMPMeta.incl_cpp @@ -0,0 +1,917 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +// ================================================================================================ +/// \file TXMPMeta.incl_cpp +/// \brief The implementation of the TXMPMeta template class. + +#include "XMP.hpp" + +#include "client-glue/WXMP_Common.hpp" + +#include "client-glue/WXMPMeta.hpp" + + +#include "XMPCore/XMPCoreDefines.h" +#if ENABLE_CPP_DOM_MODEL + #include "XMPCore/Interfaces/IMetadata.h" + #include "XMPCore/Interfaces/ICoreObjectFactory.h" +#endif + + +// ================================================================================================= +// Implementation Guidelines +// ========================= +// +// The implementations of the template functions are very stylized. ... +// +// ================================================================================================= + +#ifndef XMP_TraceCTorDTor + #define XMP_TraceCTorDTor 0 +#endif + +#if XMP_TraceCTorDTor + class XMPeek { // Hack to peek at the client ref count in the internal object. + public: + XMPeek(); + virtual ~XMPeek(); + XMP_Int32 clientRefs; + }; +#endif + +// ================================================================================================= +// Local utilities +// =============== + +class TOPW_Info { +public: + XMP_TextOutputProc clientProc; + void * clientRefCon; + TOPW_Info ( XMP_TextOutputProc proc, void * refCon ) : clientProc(proc), clientRefCon(refCon) {}; +private: + TOPW_Info() {}; // ! Hide default constructor. +}; + +static XMP_Status TextOutputProcWrapper ( void * refCon, + XMP_StringPtr buffer, + XMP_StringLen bufferSize ) +{ + try { // Don't let client callback exceptions propagate across DLL boundaries. + TOPW_Info * info = (TOPW_Info*)refCon; + return info->clientProc ( info->clientRefCon, buffer, bufferSize ); + } catch ( ... ) { + return -1; + } +} + +// ================================================================================================= +// Initialization and termination +// ============================== + +XMP_MethodIntro(TXMPMeta,void):: +SetClientString ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen ) +{ + tStringObj * clientStr = (tStringObj*) clientPtr; + clientStr->assign ( valuePtr, valueLen ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +GetVersionInfo ( XMP_VersionInfo * info ) +{ + WrapNoCheckVoid ( zXMPMeta_GetVersionInfo_1 ( info ) ); +} + +// ------------------------------------------------------------------------------------------------- + +#if XMP_TraceClientCalls + FILE * xmpClientLog = stderr; +#endif + +#ifndef XMP_TypeCheck + #if ! XMP_DebugBuild + #define XMP_TypeCheck(e,msg) /* nothing */ + #else + #define XMP_TypeCheck(e,msg) if ( ! (e) ) throw XMP_Error ( kXMPErr_AssertFailure, msg ); + #endif +#endif + +XMP_MethodIntro(TXMPMeta,bool):: +Initialize() +{ + // Verify critical type sizes. + XMP_TypeCheck ( (sizeof(XMP_Int8) == 1), "Size wrong for critical type XMP_Int8" ); + XMP_TypeCheck ( (sizeof(XMP_Int16) == 2), "Size wrong for critical type XMP_Int16" ); + XMP_TypeCheck ( (sizeof(XMP_Int32) == 4), "Size wrong for critical type XMP_Int32" ); + XMP_TypeCheck ( (sizeof(XMP_Int64) == 8), "Size wrong for critical type XMP_Int64" ); + XMP_TypeCheck ( (sizeof(XMP_Uns8) == 1), "Size wrong for critical type XMP_Uns8" ); + XMP_TypeCheck ( (sizeof(XMP_Uns16) == 2), "Size wrong for critical type XMP_Uns16" ); + XMP_TypeCheck ( (sizeof(XMP_Uns32) == 4), "Size wrong for critical type XMP_Uns32" ); + XMP_TypeCheck ( (sizeof(XMP_Uns64) == 8), "Size wrong for critical type XMP_Uns64" ); + XMP_TypeCheck ( (sizeof(XMP_Bool) == 1), "Size wrong for critical type XMP_Bool" ); + + #if XMP_TraceClientCallsToFile + xmpClientLog = fopen ( "XMPClientLog.txt", "w" ); + if ( xmpClientLog == 0 ) xmpClientLog = stderr; + #endif + + WrapCheckBool ( ok, zXMPMeta_Initialize_1() ); + + #if ENABLE_CPP_DOM_MODEL + AdobeXMPCore::ICoreObjectFactory::SetupCoreObjectFactory(); + #endif + + return ok; + +} +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +Terminate() +{ + + #if ENABLE_CPP_DOM_MODEL + AdobeXMPCore::ICoreObjectFactory::DestroyCoreObjectFactory(); + #endif + + WrapNoCheckVoid ( zXMPMeta_Terminate_1() ); + + #if XMP_TraceClientCallsToFile + if ( xmpClientLog != stderr ) fclose ( xmpClientLog ); + xmpClientLog = stderr; + #endif +} + +// ================================================================================================= +// Constuctors, destructor, operators +// ================================== + +static XMPMetaRef DefaultCTor() +{ + WrapCheckMetaRef ( newRef, zXMPMeta_CTor_1() ); + return newRef; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPMeta):: +TXMPMeta() : xmpRef(DefaultCTor()) +{ + #if XMP_TraceCTorDTor + XMPeek* xmPtr = (XMPeek*)this->xmpRef; + printf ( "Default construct TXMPMeta @ %.8X, ref = %.8X, count = %d\n", this, xmPtr, xmPtr->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPMeta):: +TXMPMeta ( const TXMPMeta & original ) : xmpRef(original.xmpRef) +{ + WXMPMeta_IncrementRefCount_1 ( this->xmpRef ); + #if XMP_TraceCTorDTor + XMPeek* xmPtr = (XMPeek*)this->xmpRef; + printf ( "Copy construct TXMPMeta @ %.8X, ref = %.8X, count = %d\n", this, xmPtr, xmPtr->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- +XMP_MethodIntro(TXMPMeta,void):: +operator= ( const TXMPMeta & rhs ) +{ + #if XMP_TraceCTorDTor + XMPeek* xmLHS = (XMPeek*)this->xmpRef; + XMPeek* xmRHS = (XMPeek*)rhs.xmpRef; + printf ( "Assign TXMPMeta, lhs @ %.8X, rhs @ %.8X\n", this, &rhs ); + printf ( " original lhs ref = %.8X, count = %d\n", xmLHS, xmLHS->clientRefs ); + printf ( " original rhs ref = %.8X, count = %d\n", xmRHS, xmRHS->clientRefs ); + #endif + XMPMetaRef oldRef = this->xmpRef; // ! Decrement last so errors leave client object OK. + this->xmpRef = rhs.xmpRef; + WXMPMeta_IncrementRefCount_1 ( this->xmpRef ); // Increment the count on the new ref. + WXMPMeta_DecrementRefCount_1 ( oldRef ); // Decrement the count on the old ref. + #if XMP_TraceCTorDTor + printf ( " result lhs ref = %.8X, count = %d\n", xmLHS, xmLHS->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPMeta):: +TXMPMeta ( XMPMetaRef _xmpRef ) : xmpRef(_xmpRef) +{ + WXMPMeta_IncrementRefCount_1 ( this->xmpRef ); + #if XMP_TraceCTorDTor + XMPeek* xmPtr = (XMPeek*)this->xmpRef; + printf ( "Ref construct TXMPMeta @ %.8X, ref = %.8X, count = %d\n", this, xmPtr, xmPtr->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPMeta):: +TXMPMeta ( XMP_StringPtr buffer, + XMP_StringLen xmpSize ) : xmpRef(DefaultCTor()) +{ + #if XMP_TraceCTorDTor + XMPeek* xmPtr = (XMPeek*)this->xmpRef; + printf ( "Parse construct TXMPMeta @ %.8X, ref = %.8X, count = %d\n", this, xmPtr, xmPtr->clientRefs ); + #endif + try { + this->ParseFromBuffer ( buffer, xmpSize ); + } catch ( ... ) { + WXMPMeta_DecrementRefCount_1 ( this->xmpRef ); + this->xmpRef = 0; + throw; + } +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPMeta):: +~TXMPMeta() throw() +{ + #if XMP_TraceCTorDTor + XMPeek* xmPtr = (XMPeek*)this->xmpRef; + printf ( "Destruct TXMPMeta @ %.8X, ref = %.8X, count = %d\n", this, xmPtr, xmPtr->clientRefs ); + #endif + WXMPMeta_DecrementRefCount_1 ( this->xmpRef ); + this->xmpRef = 0; + +} // ~TXMPMeta () + +// ================================================================================================= +// Global state functions +// ====================== + +XMP_MethodIntro(TXMPMeta,XMP_OptionBits):: +GetGlobalOptions() +{ + WrapCheckOptions ( options, zXMPMeta_GetGlobalOptions_1() ); + return options; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetGlobalOptions ( XMP_OptionBits options ) +{ + WrapCheckVoid ( zXMPMeta_SetGlobalOptions_1 ( options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,XMP_Status):: +DumpNamespaces ( XMP_TextOutputProc outProc, + void * refCon ) +{ + TOPW_Info info ( outProc, refCon ); + WrapCheckStatus ( status, zXMPMeta_DumpNamespaces_1 ( TextOutputProcWrapper, &info ) ); + return status; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +RegisterNamespace ( XMP_StringPtr namespaceURI, + XMP_StringPtr suggestedPrefix, + tStringObj * registeredPrefix ) +{ + WrapCheckBool ( prefixMatch, zXMPMeta_RegisterNamespace_1 ( namespaceURI, suggestedPrefix, registeredPrefix, SetClientString ) ); + return prefixMatch; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +GetNamespacePrefix ( XMP_StringPtr namespaceURI, + tStringObj * namespacePrefix ) +{ + WrapCheckBool ( found, zXMPMeta_GetNamespacePrefix_1 ( namespaceURI, namespacePrefix, SetClientString ) ); + return found; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +GetNamespaceURI ( XMP_StringPtr namespacePrefix, + tStringObj * namespaceURI ) +{ + WrapCheckBool ( found, zXMPMeta_GetNamespaceURI_1 ( namespacePrefix, namespaceURI, SetClientString ) ); + return found; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +DeleteNamespace ( XMP_StringPtr namespaceURI ) +{ + WrapCheckVoid ( zXMPMeta_DeleteNamespace_1 ( namespaceURI ) ); +} + +// ================================================================================================= +// Basic property manipulation functions +// ===================================== + +XMP_MethodIntro(TXMPMeta,bool):: +GetProperty ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + tStringObj * propValue, + XMP_OptionBits * options ) const +{ + WrapCheckBool ( found, zXMPMeta_GetProperty_1 ( schemaNS, propName, propValue, options, SetClientString ) ); + return found; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +GetArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + tStringObj * itemValue, + XMP_OptionBits * options ) const +{ + WrapCheckBool ( found, zXMPMeta_GetArrayItem_1 ( schemaNS, arrayName, itemIndex, itemValue, options, SetClientString ) ); + return found; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +GetStructField ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + tStringObj * fieldValue, + XMP_OptionBits * options ) const +{ + WrapCheckBool ( found, zXMPMeta_GetStructField_1 ( schemaNS, structName, fieldNS, fieldName, fieldValue, options, SetClientString ) ); + return found; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +GetQualifier ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + tStringObj * qualValue, + XMP_OptionBits * options ) const +{ + WrapCheckBool ( found, zXMPMeta_GetQualifier_1 ( schemaNS, propName, qualNS, qualName, qualValue, options, SetClientString ) ); + return found; +} //GetQualifier () + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetProperty ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr propValue, + XMP_OptionBits options /* = 0 */ ) +{ + WrapCheckVoid ( zXMPMeta_SetProperty_1 ( schemaNS, propName, propValue, options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetProperty ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + const tStringObj & propValue, + XMP_OptionBits options /* = 0 */ ) +{ + this->SetProperty ( schemaNS, propName, propValue.c_str(), options ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + XMP_StringPtr itemValue, + XMP_OptionBits options /* = 0 */ ) +{ + WrapCheckVoid ( zXMPMeta_SetArrayItem_1 ( schemaNS, arrayName, itemIndex, itemValue, options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + const tStringObj & itemValue, + XMP_OptionBits options /* = 0 */ ) +{ + this->SetArrayItem ( schemaNS, arrayName, itemIndex, itemValue.c_str(), options ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +AppendArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits arrayOptions, + XMP_StringPtr itemValue, + XMP_OptionBits options /* = 0 */ ) +{ + WrapCheckVoid ( zXMPMeta_AppendArrayItem_1 ( schemaNS, arrayName, arrayOptions, itemValue, options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +AppendArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits arrayOptions, + const tStringObj & itemValue, + XMP_OptionBits options /* = 0 */ ) +{ + this->AppendArrayItem ( schemaNS, arrayName, arrayOptions, itemValue.c_str(), options ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetStructField ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + XMP_StringPtr fieldValue, + XMP_OptionBits options /* = 0 */ ) +{ + WrapCheckVoid ( zXMPMeta_SetStructField_1 ( schemaNS, structName, fieldNS, fieldName, fieldValue, options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetStructField ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + const tStringObj & fieldValue, + XMP_OptionBits options /* = 0 */ ) +{ + this->SetStructField ( schemaNS, structName, fieldNS, fieldName, fieldValue.c_str(), options ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetQualifier ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + XMP_StringPtr qualValue, + XMP_OptionBits options /* = 0 */ ) +{ + WrapCheckVoid ( zXMPMeta_SetQualifier_1 ( schemaNS, propName, qualNS, qualName, qualValue, options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetQualifier ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + const tStringObj & qualValue, + XMP_OptionBits options /* = 0 */ ) +{ + this->SetQualifier ( schemaNS, propName, qualNS, qualName, qualValue.c_str(), options ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +DeleteProperty ( XMP_StringPtr schemaNS, + XMP_StringPtr propName ) +{ + WrapCheckVoid ( zXMPMeta_DeleteProperty_1 ( schemaNS, propName ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +DeleteArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex ) +{ + WrapCheckVoid ( zXMPMeta_DeleteArrayItem_1 ( schemaNS, arrayName, itemIndex ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +DeleteStructField ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName ) +{ + WrapCheckVoid ( zXMPMeta_DeleteStructField_1 ( schemaNS, structName, fieldNS, fieldName ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +DeleteQualifier ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName ) +{ + WrapCheckVoid ( zXMPMeta_DeleteQualifier_1 ( schemaNS, propName, qualNS, qualName ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +DoesPropertyExist ( XMP_StringPtr schemaNS, + XMP_StringPtr propName ) const +{ + WrapCheckBool ( exists, zXMPMeta_DoesPropertyExist_1 ( schemaNS, propName ) ); + return exists; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +DoesArrayItemExist ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex ) const +{ + WrapCheckBool ( exists, zXMPMeta_DoesArrayItemExist_1 ( schemaNS, arrayName, itemIndex ) ); + return exists; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +DoesStructFieldExist ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName ) const +{ + WrapCheckBool ( exists, zXMPMeta_DoesStructFieldExist_1 ( schemaNS, structName, fieldNS, fieldName ) ); + return exists; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +DoesQualifierExist ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName ) const +{ + WrapCheckBool ( exists, zXMPMeta_DoesQualifierExist_1 ( schemaNS, propName, qualNS, qualName ) ); + return exists; +} + +// ================================================================================================= +// Specialized Get and Set functions +// ================================= + +XMP_MethodIntro(TXMPMeta,bool):: +GetLocalizedText ( XMP_StringPtr schemaNS, + XMP_StringPtr altTextName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang, + tStringObj * actualLang, + tStringObj * itemValue, + XMP_OptionBits * options ) const +{ + WrapCheckBool ( found, zXMPMeta_GetLocalizedText_1 ( schemaNS, altTextName, genericLang, specificLang, + actualLang, itemValue, options, SetClientString ) ); + return found; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetLocalizedText ( XMP_StringPtr schemaNS, + XMP_StringPtr altTextName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang, + XMP_StringPtr itemValue, + XMP_OptionBits options /* = 0 */ ) +{ + WrapCheckVoid ( zXMPMeta_SetLocalizedText_1 ( schemaNS, altTextName, genericLang, specificLang, itemValue, options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetLocalizedText ( XMP_StringPtr schemaNS, + XMP_StringPtr altTextName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang, + const tStringObj & itemValue, + XMP_OptionBits options /* = 0 */ ) +{ + this->SetLocalizedText ( schemaNS, altTextName, genericLang, specificLang, itemValue.c_str(), options ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +DeleteLocalizedText ( XMP_StringPtr schemaNS, + XMP_StringPtr altTextName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang ) +{ + WrapCheckVoid ( zXMPMeta_DeleteLocalizedText_1 ( schemaNS, altTextName, genericLang, specificLang ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +GetProperty_Bool ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + bool * propValue, + XMP_OptionBits * options ) const +{ + XMP_Bool binValue; + WrapCheckBool ( found, zXMPMeta_GetProperty_Bool_1 ( schemaNS, propName, &binValue, options ) ); + if ( found && (propValue != 0) ) *propValue = binValue; + return found; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +GetProperty_Int ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int32 * propValue, + XMP_OptionBits * options ) const +{ + WrapCheckBool ( found, zXMPMeta_GetProperty_Int_1 ( schemaNS, propName, propValue, options ) ); + return found; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +GetProperty_Int64 ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int64 * propValue, + XMP_OptionBits * options ) const +{ + WrapCheckBool ( found, zXMPMeta_GetProperty_Int64_1 ( schemaNS, propName, propValue, options ) ); + return found; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +GetProperty_Float ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + double * propValue, + XMP_OptionBits * options ) const +{ + WrapCheckBool ( found, zXMPMeta_GetProperty_Float_1 ( schemaNS, propName, propValue, options ) ); + return found; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +GetProperty_Date ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_DateTime * propValue, + XMP_OptionBits * options ) const +{ + WrapCheckBool ( found, zXMPMeta_GetProperty_Date_1 ( schemaNS, propName, propValue, options ) ); + return found; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetProperty_Bool ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + bool propValue, + XMP_OptionBits options /* = 0 */ ) +{ + WrapCheckVoid ( zXMPMeta_SetProperty_Bool_1 ( schemaNS, propName, propValue, options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetProperty_Int ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int32 propValue, + XMP_OptionBits options /* = 0 */ ) +{ + WrapCheckVoid ( zXMPMeta_SetProperty_Int_1 ( schemaNS, propName, propValue, options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetProperty_Int64 ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int64 propValue, + XMP_OptionBits options /* = 0 */ ) +{ + WrapCheckVoid ( zXMPMeta_SetProperty_Int64_1 ( schemaNS, propName, propValue, options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetProperty_Float ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + double propValue, + XMP_OptionBits options /* = 0 */ ) +{ + WrapCheckVoid ( zXMPMeta_SetProperty_Float_1 ( schemaNS, propName, propValue, options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetProperty_Date ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + const XMP_DateTime & propValue, + XMP_OptionBits options /* = 0 */ ) +{ + WrapCheckVoid ( zXMPMeta_SetProperty_Date_1 ( schemaNS, propName, propValue, options ) ); +} + +// ================================================================================================= +// Miscellaneous Member Functions +// ============================== + +XMP_MethodIntro(TXMPMeta,XMPMetaRef):: +GetInternalRef() const +{ + return this->xmpRef; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +GetObjectName ( tStringObj * nameStr ) const +{ + WrapCheckVoid ( zXMPMeta_GetObjectName_1 ( nameStr, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetObjectName ( XMP_StringPtr name ) +{ + WrapCheckVoid ( zXMPMeta_SetObjectName_1 ( name ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetObjectName ( tStringObj name ) +{ + this->SetObjectName ( name.c_str() ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,XMP_OptionBits):: +GetObjectOptions() const +{ + WrapCheckOptions ( options, zXMPMeta_GetObjectOptions_1() ); + return options; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetObjectOptions ( XMP_OptionBits options ) +{ + WrapCheckVoid ( zXMPMeta_SetObjectOptions_1 ( options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +Sort() +{ + WrapCheckVoid ( zXMPMeta_Sort_1() ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +Erase() +{ + WrapCheckVoid ( zXMPMeta_Erase_1() ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,TXMPMeta):: +Clone ( XMP_OptionBits options ) const +{ + WrapCheckMetaRef ( cloneRef, zXMPMeta_Clone_1 ( options ) ); + return TXMPMeta ( cloneRef ); // Ref construct will increment the clientRefs. +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,XMP_Index):: +CountArrayItems ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName ) const +{ + WrapCheckIndex ( count, zXMPMeta_CountArrayItems_1 ( schemaNS, arrayName ) ); + return count; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,XMP_Status):: +DumpObject ( XMP_TextOutputProc outProc, + void * refCon ) const +{ + TOPW_Info info ( outProc, refCon ); + WrapCheckStatus ( status, zXMPMeta_DumpObject_1 ( TextOutputProcWrapper, &info ) ); + return status; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +ParseFromBuffer ( XMP_StringPtr buffer, + XMP_StringLen bufferSize, + XMP_OptionBits options /* = 0 */ ) +{ + WrapCheckVoid ( zXMPMeta_ParseFromBuffer_1 ( buffer, bufferSize, options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SerializeToBuffer ( tStringObj * pktString, + XMP_OptionBits options, + XMP_StringLen padding, + XMP_StringPtr newline, + XMP_StringPtr indent, + XMP_Index baseIndent /* = 0 */ ) const +{ + WrapCheckVoid ( zXMPMeta_SerializeToBuffer_1 ( pktString, options, padding, newline, indent, baseIndent, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SerializeToBuffer ( tStringObj * pktString, + XMP_OptionBits options /* = 0 */, + XMP_StringLen padding /* = 0 */ ) const +{ + this->SerializeToBuffer ( pktString, options, padding, "", "", 0 ); +} + +// ------------------------------------------------------------------------------------------------- + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetDefaultErrorCallback ( XMPMeta_ErrorCallbackProc proc, + void * context /* = 0 */, + XMP_Uns32 limit /* = 1 */ ) +{ + WrapCheckVoid ( zXMPMeta_SetDefaultErrorCallback_1 ( proc, context, limit ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetErrorCallback ( XMPMeta_ErrorCallbackProc proc, + void * context /* = 0 */, + XMP_Uns32 limit /* = 1 */ ) +{ + WrapCheckVoid ( zXMPMeta_SetErrorCallback_1 ( proc, context, limit ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +ResetErrorCallbackLimit ( XMP_Uns32 limit /* = 1 */ ) +{ + WrapCheckVoid ( zXMPMeta_ResetErrorCallbackLimit_1 ( limit ) ); +} + +// ================================================================================================= diff --git a/xmp/client-glue/TXMPUtils.incl_cpp b/xmp/client-glue/TXMPUtils.incl_cpp new file mode 100644 index 0000000..2cd5bae --- /dev/null +++ b/xmp/client-glue/TXMPUtils.incl_cpp @@ -0,0 +1,445 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +// ================================================================================================ +/// \file TXMPUtils.incl_cpp +/// \brief The implementation of the TXMPUtils template class. + +#include "XMP.hpp" +#include "client-glue/WXMP_Common.hpp" +#include "client-glue/WXMPUtils.hpp" + +// ================================================================================================= +// Implementation Guidelines +// ========================= +// +// The implementations of the template functions are very stylized. ... +// +// ================================================================================================= + +XMP_MethodIntro(TXMPUtils,void):: +SetClientString ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen ) +{ + tStringObj * clientStr = (tStringObj*) clientPtr; + clientStr->assign ( valuePtr, valueLen ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ComposeArrayItemPath ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + tStringObj * fullPath ) +{ + WrapCheckVoid ( zXMPUtils_ComposeArrayItemPath_1 ( schemaNS, arrayName, itemIndex, fullPath, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ComposeStructFieldPath ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + tStringObj * fullPath ) +{ + WrapCheckVoid ( zXMPUtils_ComposeStructFieldPath_1 ( schemaNS, structName, fieldNS, fieldName, fullPath, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ComposeQualifierPath ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + tStringObj * fullPath ) +{ + WrapCheckVoid ( zXMPUtils_ComposeQualifierPath_1 ( schemaNS, propName, qualNS, qualName, fullPath, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ComposeLangSelector ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr langName, + tStringObj * fullPath ) +{ + WrapCheckVoid ( zXMPUtils_ComposeLangSelector_1 ( schemaNS, arrayName, langName, fullPath, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ComposeLangSelector ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + const tStringObj & langName, + tStringObj * fullPath ) +{ + TXMPUtils::ComposeLangSelector ( schemaNS, arrayName, langName.c_str(), fullPath ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ComposeFieldSelector ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + XMP_StringPtr fieldValue, + tStringObj * fullPath ) +{ + WrapCheckVoid ( zXMPUtils_ComposeFieldSelector_1 ( schemaNS, arrayName, fieldNS, fieldName, fieldValue, fullPath, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ComposeFieldSelector ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + const tStringObj & fieldValue, + tStringObj * fullPath ) +{ + TXMPUtils::ComposeFieldSelector ( schemaNS, arrayName, fieldNS, fieldName, fieldValue.c_str(), fullPath ); +} + +// ------------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ConvertFromBool ( bool binValue, + tStringObj * strValue ) +{ + WrapCheckVoid ( zXMPUtils_ConvertFromBool_1 ( binValue, strValue, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ConvertFromInt ( long binValue, + XMP_StringPtr format, + tStringObj * strValue ) +{ + #if XMP_MacBuild & XMP_64 // This is checked because on Mac 64 bit environment, long is of 64 bit and hence gives a warning during implicit + // typecasting to XMP_Int32. Now doing it explicitly in that case. + WrapCheckVoid ( zXMPUtils_ConvertFromInt_1 ( (XMP_Int32)binValue, format, strValue, SetClientString ) ); + #else + WrapCheckVoid ( zXMPUtils_ConvertFromInt_1 ( binValue, format, strValue, SetClientString ) ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ConvertFromInt64 ( long long binValue, + XMP_StringPtr format, + tStringObj * strValue ) +{ + WrapCheckVoid ( zXMPUtils_ConvertFromInt64_1 ( binValue, format, strValue, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ConvertFromFloat ( double binValue, + XMP_StringPtr format, + tStringObj * strValue ) +{ + WrapCheckVoid ( zXMPUtils_ConvertFromFloat_1 ( binValue, format, strValue, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ConvertFromDate ( const XMP_DateTime & binValue, + tStringObj * strValue ) +{ + WrapCheckVoid ( zXMPUtils_ConvertFromDate_1 ( binValue, strValue, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,bool):: +ConvertToBool ( XMP_StringPtr strValue ) +{ + WrapCheckBool ( value, zXMPUtils_ConvertToBool_1 ( strValue ) ); + return value; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,bool):: +ConvertToBool ( const tStringObj & strValue ) +{ + return TXMPUtils::ConvertToBool ( strValue.c_str() ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,long):: +ConvertToInt ( XMP_StringPtr strValue ) +{ + WrapCheckInt32 ( value, zXMPUtils_ConvertToInt_1 ( strValue ) ); + return value; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,long):: +ConvertToInt ( const tStringObj & strValue ) +{ + return TXMPUtils::ConvertToInt ( strValue.c_str() ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,long long):: +ConvertToInt64 ( XMP_StringPtr strValue ) +{ + WrapCheckInt64 ( value, zXMPUtils_ConvertToInt64_1 ( strValue ) ); + return value; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,long long):: +ConvertToInt64 ( const tStringObj & strValue ) +{ + return TXMPUtils::ConvertToInt64 ( strValue.c_str() ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,double):: +ConvertToFloat ( XMP_StringPtr strValue ) +{ + WrapCheckFloat ( value, zXMPUtils_ConvertToFloat_1 ( strValue ) ); + return value; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,double):: +ConvertToFloat ( const tStringObj & strValue ) +{ + return TXMPUtils::ConvertToFloat ( strValue.c_str() ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ConvertToDate ( XMP_StringPtr strValue, + XMP_DateTime * binValue ) +{ + WrapCheckVoid ( zXMPUtils_ConvertToDate_1 ( strValue, binValue ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ConvertToDate ( const tStringObj & strValue, + XMP_DateTime * binValue ) +{ + TXMPUtils::ConvertToDate ( strValue.c_str(), binValue ); +} + +// ------------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +CurrentDateTime ( XMP_DateTime * time ) +{ + WrapCheckVoid ( zXMPUtils_CurrentDateTime_1 ( time ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +SetTimeZone ( XMP_DateTime * time ) +{ + WrapCheckVoid ( zXMPUtils_SetTimeZone_1 ( time ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ConvertToUTCTime ( XMP_DateTime * time ) +{ + WrapCheckVoid ( zXMPUtils_ConvertToUTCTime_1 ( time ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ConvertToLocalTime ( XMP_DateTime * time ) +{ + WrapCheckVoid ( zXMPUtils_ConvertToLocalTime_1 ( time ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,int):: +CompareDateTime ( const XMP_DateTime & left, + const XMP_DateTime & right ) +{ + WrapCheckInt32 ( result, zXMPUtils_CompareDateTime_1 ( left, right ) ); + return result; +} + +// ------------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +EncodeToBase64 ( XMP_StringPtr rawStr, + XMP_StringLen rawLen, + tStringObj * encodedStr ) +{ + WrapCheckVoid ( zXMPUtils_EncodeToBase64_1 ( rawStr, rawLen, encodedStr, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +EncodeToBase64 ( const tStringObj & rawStr, + tStringObj * encodedStr ) +{ + TXMPUtils::EncodeToBase64 ( rawStr.c_str(), (XMP_StringLen)rawStr.size(), encodedStr ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +DecodeFromBase64 ( XMP_StringPtr encodedStr, + XMP_StringLen encodedLen, + tStringObj * rawStr ) +{ + WrapCheckVoid ( zXMPUtils_DecodeFromBase64_1 ( encodedStr, encodedLen, rawStr, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +DecodeFromBase64 ( const tStringObj & encodedStr, + tStringObj * rawStr ) +{ + TXMPUtils::DecodeFromBase64 ( encodedStr.c_str(), (XMP_StringLen)encodedStr.size(), rawStr ); +} + +// ------------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------- + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +PackageForJPEG ( const TXMPMeta & xmpObj, + tStringObj * standardXMP, + tStringObj * extendedXMP, + tStringObj * extendedDigest ) +{ + WrapCheckVoid ( zXMPUtils_PackageForJPEG_1 ( xmpObj.GetInternalRef(), standardXMP, extendedXMP, extendedDigest, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +MergeFromJPEG ( TXMPMeta * fullXMP, + const TXMPMeta & extendedXMP ) +{ + WrapCheckVoid ( zXMPUtils_MergeFromJPEG_1 ( fullXMP->GetInternalRef(), extendedXMP.GetInternalRef() ) ); +} + +// ------------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +CatenateArrayItems ( const TXMPMeta & xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr separator, + XMP_StringPtr quotes, + XMP_OptionBits options, + tStringObj * catedStr ) +{ + WrapCheckVoid ( zXMPUtils_CatenateArrayItems_1 ( xmpObj.GetInternalRef(), schemaNS, arrayName, + separator, quotes, options, catedStr, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +SeparateArrayItems ( TXMPMeta * xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits options, + XMP_StringPtr catedStr ) +{ + if ( xmpObj == 0 ) throw XMP_Error ( kXMPErr_BadParam, "Null output SXMPMeta pointer" ); + WrapCheckVoid ( zXMPUtils_SeparateArrayItems_1 ( xmpObj->GetInternalRef(), schemaNS, arrayName, options, catedStr ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +SeparateArrayItems ( TXMPMeta * xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits options, + const tStringObj & catedStr ) +{ + TXMPUtils::SeparateArrayItems ( xmpObj, schemaNS, arrayName, options, catedStr.c_str() ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ApplyTemplate ( TXMPMeta * workingXMP, + const TXMPMeta & templateXMP, + XMP_OptionBits actions ) +{ + if ( workingXMP == 0 ) throw XMP_Error ( kXMPErr_BadParam, "Null working SXMPMeta pointer" ); + WrapCheckVoid ( zXMPUtils_ApplyTemplate_1 ( workingXMP->GetInternalRef(), templateXMP.GetInternalRef(), actions ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +RemoveProperties ( TXMPMeta * xmpObj, + XMP_StringPtr schemaNS /* = 0 */, + XMP_StringPtr propName /* = 0 */, + XMP_OptionBits options /* = 0 */ ) +{ + if ( xmpObj == 0 ) throw XMP_Error ( kXMPErr_BadParam, "Null output SXMPMeta pointer" ); + WrapCheckVoid ( zXMPUtils_RemoveProperties_1 ( xmpObj->GetInternalRef(), schemaNS, propName, options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +DuplicateSubtree ( const TXMPMeta & source, + TXMPMeta * dest, + XMP_StringPtr sourceNS, + XMP_StringPtr sourceRoot, + XMP_StringPtr destNS /*= 0 */, + XMP_StringPtr destRoot /* = 0 */, + XMP_OptionBits options /* = 0 */ ) +{ + if ( dest == 0 ) throw XMP_Error ( kXMPErr_BadParam, "Null output SXMPMeta pointer" ); + WrapCheckVoid ( zXMPUtils_DuplicateSubtree_1 ( source.GetInternalRef(), dest->GetInternalRef(), + sourceNS, sourceRoot, destNS, destRoot, options ) ); +} + +// ================================================================================================= + +// ================================================================================================= diff --git a/xmp/client-glue/WXMPFiles.hpp b/xmp/client-glue/WXMPFiles.hpp new file mode 100644 index 0000000..2f8bb9a --- /dev/null +++ b/xmp/client-glue/WXMPFiles.hpp @@ -0,0 +1,281 @@ +#ifndef __WXMPFiles_hpp__ +#define __WXMPFiles_hpp__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "client-glue/WXMP_Common.hpp" + +#if XMP_StaticBuild // ! Client XMP_IO objects can only be used in static builds. + #include "XMP_IO.hpp" +#endif + +#if __cplusplus +extern "C" { +#endif + +// ================================================================================================= +/// \file WXMPFiles.hpp +/// \brief High level support to access metadata in files of interest to Adobe applications. +/// +/// This header ... +/// +// ================================================================================================= + +// ================================================================================================= + +#define WrapCheckXMPFilesRef(result,WCallProto) \ + WXMP_Result wResult; \ + WCallProto; \ + PropagateException ( wResult ); \ + XMPFilesRef result = XMPFilesRef(wResult.ptrResult) + +static XMP_Bool WrapProgressReport ( XMP_ProgressReportProc proc, void * context, + float elapsedTime, float fractionDone, float secondsToGo ) +{ + bool ok; + try { + ok = (*proc) ( context, elapsedTime, fractionDone, secondsToGo ); + } catch ( ... ) { + ok = false; + } + return ConvertBoolToXMP_Bool( ok ); +} + +// ================================================================================================= + +static XMP_Bool WrapFilesErrorNotify ( XMPFiles_ErrorCallbackProc proc, void * context, + XMP_StringPtr filePath, XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr message ) +{ + bool ok; + try { + ok = (*proc) ( context, filePath, severity, cause, message ); + } catch ( ... ) { + ok = false; + } + return ConvertBoolToXMP_Bool( ok ); +} + +// ================================================================================================= + +#define zXMPFiles_GetVersionInfo_1(versionInfo) \ + WXMPFiles_GetVersionInfo_1 ( versionInfo /* no wResult */ ) + +#define zXMPFiles_Initialize_1(options) \ + WXMPFiles_Initialize_1 ( options, &wResult ) + +#define zXMPFiles_Initialize_2(options,pluginFolder,plugins) \ + WXMPFiles_Initialize_2 ( options, pluginFolder, plugins, &wResult ) + +#define zXMPFiles_Terminate_1() \ + WXMPFiles_Terminate_1 ( /* no wResult */ ) + +#define zXMPFiles_CTor_1() \ + WXMPFiles_CTor_1 ( &wResult ) + +#define zXMPFiles_GetFormatInfo_1(format,flags) \ + WXMPFiles_GetFormatInfo_1 ( format, flags, &wResult ) + +#define zXMPFiles_CheckFileFormat_1(filePath) \ + WXMPFiles_CheckFileFormat_1 ( filePath, &wResult ) + +#define zXMPFiles_CheckPackageFormat_1(folderPath) \ + WXMPFiles_CheckPackageFormat_1 ( folderPath, &wResult ) + +#define zXMPFiles_GetFileModDate_1(filePath,modDate,format,options) \ + WXMPFiles_GetFileModDate_1 ( filePath, modDate, format, options, &wResult ) + +#define zXMPFiles_GetAssociatedResources_1( filePath, resourceList, format, options, SetClientStringVector ) \ + WXMPFiles_GetAssociatedResources_1 ( filePath, resourceList, format, options, SetClientStringVector, &wResult ) + +#define zXMPFiles_IsMetadataWritable_1( filePath, writable, format, options ) \ + WXMPFiles_IsMetadataWritable_1 ( filePath, writable, format, options, &wResult ) + +#define zXMPFiles_OpenFile_1(filePath,format,openFlags) \ + WXMPFiles_OpenFile_1 ( this->xmpFilesRef, filePath, format, openFlags, &wResult ) + +#if XMP_StaticBuild // ! Client XMP_IO objects can only be used in static builds. +#define zXMPFiles_OpenFile_2(clientIO,format,openFlags) \ + WXMPFiles_OpenFile_2 ( this->xmpFilesRef, clientIO, format, openFlags, &wResult ) +#endif + +#define zXMPFiles_CloseFile_1(closeFlags) \ + WXMPFiles_CloseFile_1 ( this->xmpFilesRef, closeFlags, &wResult ) + +#define zXMPFiles_GetFileInfo_1(clientPath,openFlags,format,handlerFlags,SetClientString) \ + WXMPFiles_GetFileInfo_1 ( this->xmpFilesRef, clientPath, openFlags, format, handlerFlags, SetClientString, &wResult ) + +#define zXMPFiles_SetAbortProc_1(abortProc,abortArg) \ + WXMPFiles_SetAbortProc_1 ( this->xmpFilesRef, abortProc, abortArg, &wResult ) + +#define zXMPFiles_GetXMP_1(xmpRef,clientPacket,packetInfo,SetClientString) \ + WXMPFiles_GetXMP_1 ( this->xmpFilesRef, xmpRef, clientPacket, packetInfo, SetClientString, &wResult ) + +#define zXMPFiles_PutXMP_1(xmpRef,xmpPacket,xmpPacketLen) \ + WXMPFiles_PutXMP_1 ( this->xmpFilesRef, xmpRef, xmpPacket, xmpPacketLen, &wResult ) + +#define zXMPFiles_CanPutXMP_1(xmpRef,xmpPacket,xmpPacketLen) \ + WXMPFiles_CanPutXMP_1 ( this->xmpFilesRef, xmpRef, xmpPacket, xmpPacketLen, &wResult ) + +#define zXMPFiles_SetDefaultProgressCallback_1(proc,context,interval,sendStartStop) \ + WXMPFiles_SetDefaultProgressCallback_1 ( WrapProgressReport, proc, context, interval, sendStartStop, &wResult ) + +#define zXMPFiles_SetProgressCallback_1(proc,context,interval,sendStartStop) \ + WXMPFiles_SetProgressCallback_1 ( this->xmpFilesRef, WrapProgressReport, proc, context, interval, sendStartStop, &wResult ) + +#define zXMPFiles_SetDefaultErrorCallback_1(proc,context,limit) \ + WXMPFiles_SetDefaultErrorCallback_1 ( WrapFilesErrorNotify, proc, context, limit, &wResult ) + +#define zXMPFiles_SetErrorCallback_1(proc,context,limit) \ + WXMPFiles_SetErrorCallback_1 ( this->xmpFilesRef, WrapFilesErrorNotify, proc, context, limit, &wResult ) + +#define zXMPFiles_ResetErrorCallbackLimit_1(limit) \ + WXMPFiles_ResetErrorCallbackLimit_1 ( this->xmpFilesRef, limit, &wResult ) + +// ================================================================================================= + +extern void WXMPFiles_GetVersionInfo_1 ( XMP_VersionInfo * versionInfo ); + +extern void WXMPFiles_Initialize_1 ( XMP_OptionBits options, + WXMP_Result * result ); + +extern void WXMPFiles_Initialize_2 ( XMP_OptionBits options, + const char* pluginFolder, + const char* plugins, + WXMP_Result * result ); + +extern void WXMPFiles_Terminate_1(); + +extern void WXMPFiles_CTor_1 ( WXMP_Result * result ); + +extern void WXMPFiles_IncrementRefCount_1 ( XMPFilesRef xmpFilesRef ); + +extern void WXMPFiles_DecrementRefCount_1 ( XMPFilesRef xmpFilesRef ); + +extern void WXMPFiles_GetFormatInfo_1 ( XMP_FileFormat format, + XMP_OptionBits * flags, // ! Can be null. + WXMP_Result * result ); + +extern void WXMPFiles_CheckFileFormat_1 ( XMP_StringPtr filePath, + WXMP_Result * result ); + +extern void WXMPFiles_CheckPackageFormat_1 ( XMP_StringPtr folderPath, + WXMP_Result * result ); + +extern void WXMPFiles_GetFileModDate_1 ( XMP_StringPtr filePath, + XMP_DateTime * modDate, + XMP_FileFormat * format, // ! Can be null. + XMP_OptionBits options, + WXMP_Result * result ); + + +extern void WXMPFiles_GetAssociatedResources_1 ( XMP_StringPtr filePath, + void * resourceList, + XMP_FileFormat format, + XMP_OptionBits options, + SetClientStringVectorProc SetClientStringVector, + WXMP_Result * result ); + +extern void WXMPFiles_IsMetadataWritable_1 ( XMP_StringPtr filePath, + XMP_Bool * writable, + XMP_FileFormat format, + XMP_OptionBits options, + WXMP_Result * result ); + +extern void WXMPFiles_OpenFile_1 ( XMPFilesRef xmpFilesRef, + XMP_StringPtr filePath, + XMP_FileFormat format, + XMP_OptionBits openFlags, + WXMP_Result * result ); + +#if XMP_StaticBuild // ! Client XMP_IO objects can only be used in static builds. +extern void WXMPFiles_OpenFile_2 ( XMPFilesRef xmpFilesRef, + XMP_IO * clientIO, + XMP_FileFormat format, + XMP_OptionBits openFlags, + WXMP_Result * result ); +#endif + +extern void WXMPFiles_CloseFile_1 ( XMPFilesRef xmpFilesRef, + XMP_OptionBits closeFlags, + WXMP_Result * result ); + +extern void WXMPFiles_GetFileInfo_1 ( XMPFilesRef xmpFilesRef, + void * clientPath, + XMP_OptionBits * openFlags, // ! Can be null. + XMP_FileFormat * format, // ! Can be null. + XMP_OptionBits * handlerFlags, // ! Can be null. + SetClientStringProc SetClientString, + WXMP_Result * result ); + +extern void WXMPFiles_SetAbortProc_1 ( XMPFilesRef xmpFilesRef, + XMP_AbortProc abortProc, + void * abortArg, + WXMP_Result * result ); + +extern void WXMPFiles_GetXMP_1 ( XMPFilesRef xmpFilesRef, + XMPMetaRef xmpRef, // ! Can be null. + void * clientPacket, + XMP_PacketInfo * packetInfo, // ! Can be null. + SetClientStringProc SetClientString, + WXMP_Result * result ); + +extern void WXMPFiles_PutXMP_1 ( XMPFilesRef xmpFilesRef, + XMPMetaRef xmpRef, // ! Only one of the XMP object or packet are passed. + XMP_StringPtr xmpPacket, + XMP_StringLen xmpPacketLen, + WXMP_Result * result ); + +extern void WXMPFiles_CanPutXMP_1 ( XMPFilesRef xmpFilesRef, + XMPMetaRef xmpRef, // ! Only one of the XMP object or packet are passed. + XMP_StringPtr xmpPacket, + XMP_StringLen xmpPacketLen, + WXMP_Result * result ); + +extern void WXMPFiles_SetDefaultProgressCallback_1 ( XMP_ProgressReportWrapper wrapperproc, + XMP_ProgressReportProc clientProc, + void * context, + float interval, + XMP_Bool sendStartStop, + WXMP_Result * result ); + +extern void WXMPFiles_SetProgressCallback_1 ( XMPFilesRef xmpFilesRef, + XMP_ProgressReportWrapper wrapperproc, + XMP_ProgressReportProc clientProc, + void * context, + float interval, + XMP_Bool sendStartStop, + WXMP_Result * result ); + +// ------------------------------------------------------------------------------------------------- + +extern void WXMPFiles_SetDefaultErrorCallback_1 ( XMPFiles_ErrorCallbackWrapper wrapperProc, + XMPFiles_ErrorCallbackProc clientProc, + void * context, + XMP_Uns32 limit, + WXMP_Result * wResult ); + +extern void WXMPFiles_SetErrorCallback_1 ( XMPFilesRef xmpRef, + XMPFiles_ErrorCallbackWrapper wrapperProc, + XMPFiles_ErrorCallbackProc clientProc, + void * context, + XMP_Uns32 limit, + WXMP_Result * wResult ); + +extern void WXMPFiles_ResetErrorCallbackLimit_1 ( XMPFilesRef xmpRef, + XMP_Uns32 limit, + WXMP_Result * wResult ); + +// ================================================================================================= + +#if __cplusplus +} +#endif + +#endif // __WXMPFiles_hpp__ diff --git a/xmp/client-glue/WXMPIterator.hpp b/xmp/client-glue/WXMPIterator.hpp new file mode 100644 index 0000000..e40a1d4 --- /dev/null +++ b/xmp/client-glue/WXMPIterator.hpp @@ -0,0 +1,74 @@ +#if ! __WXMPIterator_hpp__ +#define __WXMPIterator_hpp__ 1 + +// ================================================================================================= +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "client-glue/WXMP_Common.hpp" + +#if __cplusplus +extern "C" { +#endif + +// ================================================================================================= + +#define zXMPIterator_PropCTor_1(xmpRef,schemaNS,propName,options) \ + WXMPIterator_PropCTor_1 ( xmpRef, schemaNS, propName, options, &wResult ); + +#define zXMPIterator_TableCTor_1(schemaNS,propName,options) \ + WXMPIterator_TableCTor_1 ( schemaNS, propName, options, &wResult ); + + +#define zXMPIterator_Next_1(schemaNS,propPath,propValue,options,SetClientString) \ + WXMPIterator_Next_1 ( this->iterRef, schemaNS, propPath, propValue, options, SetClientString, &wResult ); + +#define zXMPIterator_Skip_1(options) \ + WXMPIterator_Skip_1 ( this->iterRef, options, &wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPIterator_PropCTor_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPIterator_TableCTor_1 ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPIterator_IncrementRefCount_1 ( XMPIteratorRef iterRef ); + +extern void +XMP_PUBLIC WXMPIterator_DecrementRefCount_1 ( XMPIteratorRef iterRef ); + +extern void +XMP_PUBLIC WXMPIterator_Next_1 ( XMPIteratorRef iterRef, + void * schemaNS, + void * propPath, + void * propValue, + XMP_OptionBits * options, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPIterator_Skip_1 ( XMPIteratorRef iterRef, + XMP_OptionBits options, + WXMP_Result * wResult ); + +// ================================================================================================= + +#if __cplusplus +} /* extern "C" */ +#endif + +#endif // __WXMPIterator_hpp__ diff --git a/xmp/client-glue/WXMPMeta.hpp b/xmp/client-glue/WXMPMeta.hpp new file mode 100644 index 0000000..af5a11e --- /dev/null +++ b/xmp/client-glue/WXMPMeta.hpp @@ -0,0 +1,635 @@ +#if ! __WXMPMeta_hpp__ +#define __WXMPMeta_hpp__ 1 + +// ================================================================================================= +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "client-glue/WXMP_Common.hpp" + +#if __cplusplus +extern "C" { +#endif + +// ================================================================================================= + +static XMP_Bool WrapErrorNotify ( XMPMeta_ErrorCallbackProc proc, void * context, + XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr message ) +{ + bool ok; + try { + ok = (*proc) ( context, severity, cause, message ); + } catch ( ... ) { + ok = false; + } + return ConvertBoolToXMP_Bool( ok ); +} + +// ================================================================================================= + +#define zXMPMeta_GetVersionInfo_1(info) \ + WXMPMeta_GetVersionInfo_1 ( info /* no wResult */ ) + +#define zXMPMeta_Initialize_1() \ + WXMPMeta_Initialize_1 ( &wResult ) +#define zXMPMeta_Terminate_1() \ + WXMPMeta_Terminate_1 ( /* no wResult */ ) + +#define zXMPMeta_CTor_1() \ + WXMPMeta_CTor_1 ( &wResult ) + +#define zXMPMeta_GetGlobalOptions_1() \ + WXMPMeta_GetGlobalOptions_1 ( &wResult ) + +#define zXMPMeta_SetGlobalOptions_1(options) \ + WXMPMeta_SetGlobalOptions_1 ( options, &wResult ) + +#define zXMPMeta_DumpNamespaces_1(outProc,refCon) \ + WXMPMeta_DumpNamespaces_1 ( outProc, refCon, &wResult ) + +#define zXMPMeta_Use_CPP_DOM_APIs_1(useNewCoreAPIs) \ + WXMPMeta_Use_CPP_DOM_APIs_1( useNewCoreAPIs, &wResult ) +#define zXMPMeta_RegisterNamespace_1(namespaceURI,suggestedPrefix,actualPrefix,SetClientString) \ + WXMPMeta_RegisterNamespace_1 ( namespaceURI, suggestedPrefix, actualPrefix, SetClientString, &wResult ) + +#define zXMPMeta_GetNamespacePrefix_1(namespaceURI,namespacePrefix,SetClientString) \ + WXMPMeta_GetNamespacePrefix_1 ( namespaceURI, namespacePrefix, SetClientString, &wResult ) + +#define zXMPMeta_GetNamespaceURI_1(namespacePrefix,namespaceURI,SetClientString) \ + WXMPMeta_GetNamespaceURI_1 ( namespacePrefix, namespaceURI, SetClientString, &wResult ) + +#define zXMPMeta_DeleteNamespace_1(namespaceURI) \ + WXMPMeta_DeleteNamespace_1 ( namespaceURI, &wResult ) + +#define zXMPMeta_GetIXMPMetadata_1() \ + WXMPMeta_GetIXMPMetadata_1( this->xmpRef, &wResult ) + +#define zXMPMeta_GetProperty_1(schemaNS,propName,propValue,options,SetClientString) \ + WXMPMeta_GetProperty_1 ( this->xmpRef, schemaNS, propName, propValue, options, SetClientString, &wResult ) + +#define zXMPMeta_GetArrayItem_1(schemaNS,arrayName,itemIndex,itemValue,options,SetClientString) \ + WXMPMeta_GetArrayItem_1 ( this->xmpRef, schemaNS, arrayName, itemIndex, itemValue, options, SetClientString, &wResult ) + +#define zXMPMeta_GetStructField_1(schemaNS,structName,fieldNS,fieldName,fieldValue,options,SetClientString) \ + WXMPMeta_GetStructField_1 ( this->xmpRef, schemaNS, structName, fieldNS, fieldName, fieldValue, options, SetClientString, &wResult ) + +#define zXMPMeta_GetQualifier_1(schemaNS,propName,qualNS,qualName,qualValue,options,SetClientString) \ + WXMPMeta_GetQualifier_1 ( this->xmpRef, schemaNS, propName, qualNS, qualName, qualValue, options, SetClientString, &wResult ) + +#define zXMPMeta_SetProperty_1(schemaNS,propName,propValue,options) \ + WXMPMeta_SetProperty_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult ) + +#define zXMPMeta_SetArrayItem_1(schemaNS,arrayName,itemIndex,itemValue,options) \ + WXMPMeta_SetArrayItem_1 ( this->xmpRef, schemaNS, arrayName, itemIndex, itemValue, options, &wResult ) + +#define zXMPMeta_AppendArrayItem_1(schemaNS,arrayName,arrayOptions,itemValue,options) \ + WXMPMeta_AppendArrayItem_1 ( this->xmpRef, schemaNS, arrayName, arrayOptions, itemValue, options, &wResult ) + +#define zXMPMeta_SetStructField_1(schemaNS,structName,fieldNS,fieldName,fieldValue,options) \ + WXMPMeta_SetStructField_1 ( this->xmpRef, schemaNS, structName, fieldNS, fieldName, fieldValue, options, &wResult ) + +#define zXMPMeta_SetQualifier_1(schemaNS,propName,qualNS,qualName,qualValue,options) \ + WXMPMeta_SetQualifier_1 ( this->xmpRef, schemaNS, propName, qualNS, qualName, qualValue, options, &wResult ) + +#define zXMPMeta_DeleteProperty_1(schemaNS,propName) \ + WXMPMeta_DeleteProperty_1 ( this->xmpRef, schemaNS, propName, &wResult ) + +#define zXMPMeta_DeleteArrayItem_1(schemaNS,arrayName,itemIndex) \ + WXMPMeta_DeleteArrayItem_1 ( this->xmpRef, schemaNS, arrayName, itemIndex, &wResult ) + +#define zXMPMeta_DeleteStructField_1(schemaNS,structName,fieldNS,fieldName) \ + WXMPMeta_DeleteStructField_1 ( this->xmpRef, schemaNS, structName, fieldNS, fieldName, &wResult ) + +#define zXMPMeta_DeleteQualifier_1(schemaNS,propName,qualNS,qualName) \ + WXMPMeta_DeleteQualifier_1 ( this->xmpRef, schemaNS, propName, qualNS, qualName, &wResult ) + +#define zXMPMeta_DoesPropertyExist_1(schemaNS,propName) \ + WXMPMeta_DoesPropertyExist_1 ( this->xmpRef, schemaNS, propName, &wResult ) + +#define zXMPMeta_DoesArrayItemExist_1(schemaNS,arrayName,itemIndex) \ + WXMPMeta_DoesArrayItemExist_1 ( this->xmpRef, schemaNS, arrayName, itemIndex, &wResult ) + +#define zXMPMeta_DoesStructFieldExist_1(schemaNS,structName,fieldNS,fieldName) \ + WXMPMeta_DoesStructFieldExist_1 ( this->xmpRef, schemaNS, structName, fieldNS, fieldName, &wResult ) + +#define zXMPMeta_DoesQualifierExist_1(schemaNS,propName,qualNS,qualName) \ + WXMPMeta_DoesQualifierExist_1 ( this->xmpRef, schemaNS, propName, qualNS, qualName, &wResult ) + +#define zXMPMeta_GetLocalizedText_1(schemaNS,altTextName,genericLang,specificLang,clientLang,clientValue,options,SetClientString) \ + WXMPMeta_GetLocalizedText_1 ( this->xmpRef, schemaNS, altTextName, genericLang, specificLang, clientLang, clientValue, options, SetClientString, &wResult ) + +#define zXMPMeta_SetLocalizedText_1(schemaNS,altTextName,genericLang,specificLang,itemValue,options) \ + WXMPMeta_SetLocalizedText_1 ( this->xmpRef, schemaNS, altTextName, genericLang, specificLang, itemValue, options, &wResult ) + +#define zXMPMeta_DeleteLocalizedText_1(schemaNS,altTextName,genericLang,specificLang) \ + WXMPMeta_DeleteLocalizedText_1 ( this->xmpRef, schemaNS, altTextName, genericLang, specificLang, &wResult ) +#define zXMPMeta_GetProperty_Bool_1(schemaNS,propName,propValue,options) \ + WXMPMeta_GetProperty_Bool_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult ) + +#define zXMPMeta_GetProperty_Int_1(schemaNS,propName,propValue,options) \ + WXMPMeta_GetProperty_Int_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult ) + +#define zXMPMeta_GetProperty_Int64_1(schemaNS,propName,propValue,options) \ + WXMPMeta_GetProperty_Int64_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult ) + +#define zXMPMeta_GetProperty_Float_1(schemaNS,propName,propValue,options) \ + WXMPMeta_GetProperty_Float_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult ) + +#define zXMPMeta_GetProperty_Date_1(schemaNS,propName,propValue,options) \ + WXMPMeta_GetProperty_Date_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult ) + +#define zXMPMeta_SetProperty_Bool_1(schemaNS,propName,propValue,options) \ + WXMPMeta_SetProperty_Bool_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult ) + +#define zXMPMeta_SetProperty_Int_1(schemaNS,propName,propValue,options) \ + WXMPMeta_SetProperty_Int_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult ) + +#define zXMPMeta_SetProperty_Int64_1(schemaNS,propName,propValue,options) \ + WXMPMeta_SetProperty_Int64_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult ) + +#define zXMPMeta_SetProperty_Float_1(schemaNS,propName,propValue,options) \ + WXMPMeta_SetProperty_Float_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult ) + +#define zXMPMeta_SetProperty_Date_1(schemaNS,propName,propValue,options) \ + WXMPMeta_SetProperty_Date_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult ) + +#define zXMPMeta_GetObjectName_1(objName,SetClientString) \ + WXMPMeta_GetObjectName_1 ( this->xmpRef, objName, SetClientString, &wResult ) + +#define zXMPMeta_SetObjectName_1(name) \ + WXMPMeta_SetObjectName_1 ( this->xmpRef, name, &wResult ) + +#define zXMPMeta_GetObjectOptions_1() \ + WXMPMeta_GetObjectOptions_1 ( this->xmpRef, &wResult ) + +#define zXMPMeta_SetObjectOptions_1(options) \ + WXMPMeta_SetObjectOptions_1 ( this->xmpRef, options, &wResult ) + +#define zXMPMeta_Sort_1() \ + WXMPMeta_Sort_1 ( this->xmpRef, &wResult ) + +#define zXMPMeta_Erase_1() \ + WXMPMeta_Erase_1 ( this->xmpRef, &wResult ) + +#define zXMPMeta_Clone_1(options) \ + WXMPMeta_Clone_1 ( this->xmpRef, options, &wResult ) + +#define zXMPMeta_CountArrayItems_1(schemaNS,arrayName) \ + WXMPMeta_CountArrayItems_1 ( this->xmpRef, schemaNS, arrayName, &wResult ) + +#define zXMPMeta_DumpObject_1(outProc,refCon) \ + WXMPMeta_DumpObject_1 ( this->xmpRef, outProc, refCon, &wResult ) + +#define zXMPMeta_ParseFromBuffer_1(buffer,bufferSize,options) \ + WXMPMeta_ParseFromBuffer_1 ( this->xmpRef, buffer, bufferSize, options, &wResult ) + +#define zXMPMeta_SerializeToBuffer_1(pktString,options,padding,newline,indent,baseIndent,SetClientString) \ + WXMPMeta_SerializeToBuffer_1 ( this->xmpRef, pktString, options, padding, newline, indent, baseIndent, SetClientString, &wResult ) + +#define zXMPMeta_SetDefaultErrorCallback_1(proc,context,limit) \ + WXMPMeta_SetDefaultErrorCallback_1 ( WrapErrorNotify, proc, context, limit, &wResult ) + +#define zXMPMeta_SetErrorCallback_1(proc,context,limit) \ + WXMPMeta_SetErrorCallback_1 ( this->xmpRef, WrapErrorNotify, proc, context, limit, &wResult ) + +#define zXMPMeta_ResetErrorCallbackLimit_1(limit) \ + WXMPMeta_ResetErrorCallbackLimit_1 ( this->xmpRef, limit, &wResult ) + +// ================================================================================================= + +extern void +XMP_PUBLIC WXMPMeta_GetVersionInfo_1 ( XMP_VersionInfo * info ); + +extern void +XMP_PUBLIC WXMPMeta_Initialize_1 ( WXMP_Result * wResult ); +extern void +XMP_PUBLIC WXMPMeta_Terminate_1(); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_CTor_1 ( WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_IncrementRefCount_1 ( XMPMetaRef xmpRef ); + +extern void +XMP_PUBLIC WXMPMeta_DecrementRefCount_1 ( XMPMetaRef xmpRef ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_GetGlobalOptions_1 ( WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_SetGlobalOptions_1 ( XMP_OptionBits options, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_DumpNamespaces_1 ( XMP_TextOutputProc outProc, + void * refCon, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_Use_CPP_DOM_APIs_1( XMP_Bool useNewCoreAPIs, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_RegisterNamespace_1 ( XMP_StringPtr namespaceURI, + XMP_StringPtr suggestedPrefix, + void * actualPrefix, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_GetNamespacePrefix_1 ( XMP_StringPtr namespaceURI, + void * namespacePrefix, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_GetNamespaceURI_1 ( XMP_StringPtr namespacePrefix, + void * namespaceURI, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_DeleteNamespace_1 ( XMP_StringPtr namespaceURI, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_GetIXMPMetadata_1(XMPMetaRef xmpObjRef, +WXMP_Result * wResult ); + + +extern void +XMP_PUBLIC WXMPMeta_GetProperty_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + void * propValue, + XMP_OptionBits * options, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_GetArrayItem_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + void * itemValue, + XMP_OptionBits * options, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_GetStructField_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + void * fieldValue, + XMP_OptionBits * options, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_GetQualifier_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + void * qualValue, + XMP_OptionBits * options, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) /* const */ ; + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_SetProperty_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr propValue, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_SetArrayItem_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + XMP_StringPtr itemValue, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_AppendArrayItem_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits arrayOptions, + XMP_StringPtr itemValue, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_SetStructField_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + XMP_StringPtr fieldValue, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_SetQualifier_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + XMP_StringPtr qualValue, + XMP_OptionBits options, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_DeleteProperty_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_DeleteArrayItem_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_DeleteStructField_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_DeleteQualifier_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_DoesPropertyExist_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_DoesArrayItemExist_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_DoesStructFieldExist_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_DoesQualifierExist_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + WXMP_Result * wResult ) /* const */ ; + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_GetLocalizedText_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr altTextName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang, + void * clientLang, + void * clientValue, + XMP_OptionBits * options, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_SetLocalizedText_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr altTextName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang, + XMP_StringPtr itemValue, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_DeleteLocalizedText_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr altTextName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_GetProperty_Bool_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Bool * propValue, + XMP_OptionBits * options, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_GetProperty_Int_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int32 * propValue, + XMP_OptionBits * options, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_GetProperty_Int64_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int64 * propValue, + XMP_OptionBits * options, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_GetProperty_Float_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + double * propValue, + XMP_OptionBits * options, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_GetProperty_Date_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_DateTime * propValue, + XMP_OptionBits * options, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_SetProperty_Bool_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Bool propValue, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_SetProperty_Int_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int32 propValue, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_SetProperty_Int64_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int64 propValue, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_SetProperty_Float_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + double propValue, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_SetProperty_Date_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + const XMP_DateTime & propValue, + XMP_OptionBits options, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_GetObjectName_1 ( XMPMetaRef xmpRef, + void * objName, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_SetObjectName_1 ( XMPMetaRef xmpRef, + XMP_StringPtr name, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_GetObjectOptions_1 ( XMPMetaRef xmpRef, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_SetObjectOptions_1 ( XMPMetaRef xmpRef, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_Sort_1 ( XMPMetaRef xmpRef, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_Erase_1 ( XMPMetaRef xmpRef, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_Clone_1 ( XMPMetaRef xmpRef, + XMP_OptionBits options, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_CountArrayItems_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_DumpObject_1 ( XMPMetaRef xmpRef, + XMP_TextOutputProc outProc, + void * refCon, + WXMP_Result * wResult ) /* const */ ; + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_ParseFromBuffer_1 ( XMPMetaRef xmpRef, + XMP_StringPtr buffer, + XMP_StringLen bufferSize, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_SerializeToBuffer_1 ( XMPMetaRef xmpRef, + void * pktString, + XMP_OptionBits options, + XMP_StringLen padding, + XMP_StringPtr newline, + XMP_StringPtr indent, + XMP_Index baseIndent, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) /* const */ ; + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_SetDefaultErrorCallback_1 ( XMPMeta_ErrorCallbackWrapper wrapperProc, + XMPMeta_ErrorCallbackProc clientProc, + void * context, + XMP_Uns32 limit, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_SetErrorCallback_1 ( XMPMetaRef xmpRef, + XMPMeta_ErrorCallbackWrapper wrapperProc, + XMPMeta_ErrorCallbackProc clientProc, + void * context, + XMP_Uns32 limit, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_ResetErrorCallbackLimit_1 ( XMPMetaRef xmpRef, + XMP_Uns32 limit, + WXMP_Result * wResult ); + +// ================================================================================================= + +#if __cplusplus +} /* extern "C" */ +#endif + +#endif // __WXMPMeta_hpp__ diff --git a/xmp/client-glue/WXMPUtils.hpp b/xmp/client-glue/WXMPUtils.hpp new file mode 100644 index 0000000..b785357 --- /dev/null +++ b/xmp/client-glue/WXMPUtils.hpp @@ -0,0 +1,314 @@ +#if ! __WXMPUtils_hpp__ +#define __WXMPUtils_hpp__ 1 + +// ================================================================================================= +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "client-glue/WXMP_Common.hpp" +#if __cplusplus +extern "C" { +#endif + +// ================================================================================================= + +#define zXMPUtils_ComposeArrayItemPath_1(schemaNS,arrayName,itemIndex,itemPath,SetClientString) \ + WXMPUtils_ComposeArrayItemPath_1 ( schemaNS, arrayName, itemIndex, itemPath, SetClientString, &wResult ); + +#define zXMPUtils_ComposeStructFieldPath_1(schemaNS,structName,fieldNS,fieldName,fieldPath,SetClientString) \ + WXMPUtils_ComposeStructFieldPath_1 ( schemaNS, structName, fieldNS, fieldName, fieldPath, SetClientString, &wResult ); + +#define zXMPUtils_ComposeQualifierPath_1(schemaNS,propName,qualNS,qualName,qualPath,SetClientString) \ + WXMPUtils_ComposeQualifierPath_1 ( schemaNS, propName, qualNS, qualName, qualPath, SetClientString, &wResult ); + +#define zXMPUtils_ComposeLangSelector_1(schemaNS,arrayName,langName,selPath,SetClientString) \ + WXMPUtils_ComposeLangSelector_1 ( schemaNS, arrayName, langName, selPath, SetClientString, &wResult ); + +#define zXMPUtils_ComposeFieldSelector_1(schemaNS,arrayName,fieldNS,fieldName,fieldValue,selPath,SetClientString) \ + WXMPUtils_ComposeFieldSelector_1 ( schemaNS, arrayName, fieldNS, fieldName, fieldValue, selPath, SetClientString, &wResult ); + +#define zXMPUtils_ConvertFromBool_1(binValue,strValue,SetClientString) \ + WXMPUtils_ConvertFromBool_1 ( binValue, strValue, SetClientString, &wResult ); + +#define zXMPUtils_ConvertFromInt_1(binValue,format,strValue,SetClientString) \ + WXMPUtils_ConvertFromInt_1 ( binValue, format, strValue, SetClientString, &wResult ); + +#define zXMPUtils_ConvertFromInt64_1(binValue,format,strValue,SetClientString) \ + WXMPUtils_ConvertFromInt64_1 ( binValue, format, strValue, SetClientString, &wResult ); + +#define zXMPUtils_ConvertFromFloat_1(binValue,format,strValue,SetClientString) \ + WXMPUtils_ConvertFromFloat_1 ( binValue, format, strValue, SetClientString, &wResult ); + +#define zXMPUtils_ConvertFromDate_1(binValue,strValue,SetClientString) \ + WXMPUtils_ConvertFromDate_1 ( binValue, strValue, SetClientString, &wResult ); + +#define zXMPUtils_ConvertToBool_1(strValue) \ + WXMPUtils_ConvertToBool_1 ( strValue, &wResult ); + +#define zXMPUtils_ConvertToInt_1(strValue) \ + WXMPUtils_ConvertToInt_1 ( strValue, &wResult ); + +#define zXMPUtils_ConvertToInt64_1(strValue) \ + WXMPUtils_ConvertToInt64_1 ( strValue, &wResult ); + +#define zXMPUtils_ConvertToFloat_1(strValue) \ + WXMPUtils_ConvertToFloat_1 ( strValue, &wResult ); + +#define zXMPUtils_ConvertToDate_1(strValue,binValue) \ + WXMPUtils_ConvertToDate_1 ( strValue, binValue, &wResult ); + +#define zXMPUtils_CurrentDateTime_1(time) \ + WXMPUtils_CurrentDateTime_1 ( time, &wResult ); + +#define zXMPUtils_SetTimeZone_1(time) \ + WXMPUtils_SetTimeZone_1 ( time, &wResult ); + +#define zXMPUtils_ConvertToUTCTime_1(time) \ + WXMPUtils_ConvertToUTCTime_1 ( time, &wResult ); + +#define zXMPUtils_ConvertToLocalTime_1(time) \ + WXMPUtils_ConvertToLocalTime_1 ( time, &wResult ); + +#define zXMPUtils_CompareDateTime_1(left,right) \ + WXMPUtils_CompareDateTime_1 ( left, right, &wResult ); + +#define zXMPUtils_EncodeToBase64_1(rawStr,rawLen,encodedStr,SetClientString) \ + WXMPUtils_EncodeToBase64_1 ( rawStr, rawLen, encodedStr, SetClientString, &wResult ); + +#define zXMPUtils_DecodeFromBase64_1(encodedStr,encodedLen,rawStr,SetClientString) \ + WXMPUtils_DecodeFromBase64_1 ( encodedStr, encodedLen, rawStr, SetClientString, &wResult ); + +#define zXMPUtils_PackageForJPEG_1(xmpObj,stdStr,extStr,digestStr,SetClientString) \ + WXMPUtils_PackageForJPEG_1 ( xmpObj, stdStr, extStr, digestStr, SetClientString, &wResult ); + +#define zXMPUtils_MergeFromJPEG_1(fullXMP,extendedXMP) \ + WXMPUtils_MergeFromJPEG_1 ( fullXMP, extendedXMP, &wResult ); + +#define zXMPUtils_CatenateArrayItems_1(xmpObj,schemaNS,arrayName,separator,quotes,options,catedStr,SetClientString) \ + WXMPUtils_CatenateArrayItems_1 ( xmpObj, schemaNS, arrayName, separator, quotes, options, catedStr, SetClientString, &wResult ); + +#define zXMPUtils_SeparateArrayItems_1(xmpObj,schemaNS,arrayName,options,catedStr) \ + WXMPUtils_SeparateArrayItems_1 ( xmpObj, schemaNS, arrayName, options, catedStr, &wResult ); + +#define zXMPUtils_ApplyTemplate_1(workingXMP,templateXMP,actions) \ + WXMPUtils_ApplyTemplate_1 ( workingXMP, templateXMP, actions, &wResult ); + +#define zXMPUtils_RemoveProperties_1(xmpObj,schemaNS,propName,options) \ + WXMPUtils_RemoveProperties_1 ( xmpObj, schemaNS, propName, options, &wResult ); + +#define zXMPUtils_DuplicateSubtree_1(source,dest,sourceNS,sourceRoot,destNS,destRoot,options) \ + WXMPUtils_DuplicateSubtree_1 ( source, dest, sourceNS, sourceRoot, destNS, destRoot, options, &wResult ); + +// ================================================================================================= + +extern void +XMP_PUBLIC WXMPUtils_ComposeArrayItemPath_1 ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + void * itemPath, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ComposeStructFieldPath_1 ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + void * fieldPath, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ComposeQualifierPath_1 ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + void * qualPath, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ComposeLangSelector_1 ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr langName, + void * selPath, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ComposeFieldSelector_1 ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + XMP_StringPtr fieldValue, + void * selPath, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPUtils_ConvertFromBool_1 ( XMP_Bool binValue, + void * strValue, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ConvertFromInt_1 ( XMP_Int32 binValue, + XMP_StringPtr format, + void * strValue, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ConvertFromInt64_1 ( XMP_Int64 binValue, + XMP_StringPtr format, + void * strValue, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ConvertFromFloat_1 ( double binValue, + XMP_StringPtr format, + void * strValue, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ConvertFromDate_1 ( const XMP_DateTime & binValue, + void * strValue, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPUtils_ConvertToBool_1 ( XMP_StringPtr strValue, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ConvertToInt_1 ( XMP_StringPtr strValue, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ConvertToInt64_1 ( XMP_StringPtr strValue, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ConvertToFloat_1 ( XMP_StringPtr strValue, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ConvertToDate_1 ( XMP_StringPtr strValue, + XMP_DateTime * binValue, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPUtils_CurrentDateTime_1 ( XMP_DateTime * time, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_SetTimeZone_1 ( XMP_DateTime * time, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ConvertToUTCTime_1 ( XMP_DateTime * time, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ConvertToLocalTime_1 ( XMP_DateTime * time, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_CompareDateTime_1 ( const XMP_DateTime & left, + const XMP_DateTime & right, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPUtils_EncodeToBase64_1 ( XMP_StringPtr rawStr, + XMP_StringLen rawLen, + void * encodedStr, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_DecodeFromBase64_1 ( XMP_StringPtr encodedStr, + XMP_StringLen encodedLen, + void * rawStr, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPUtils_PackageForJPEG_1 ( XMPMetaRef xmpObj, + void * stdStr, + void * extStr, + void * digestStr, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_MergeFromJPEG_1 ( XMPMetaRef fullXMP, + XMPMetaRef extendedXMP, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPUtils_CatenateArrayItems_1 ( XMPMetaRef xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr separator, + XMP_StringPtr quotes, + XMP_OptionBits options, + void * catedStr, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_SeparateArrayItems_1 ( XMPMetaRef xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits options, + XMP_StringPtr catedStr, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ApplyTemplate_1 ( XMPMetaRef workingXMP, + XMPMetaRef templateXMP, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_RemoveProperties_1 ( XMPMetaRef xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_DuplicateSubtree_1 ( XMPMetaRef source, + XMPMetaRef dest, + XMP_StringPtr sourceNS, + XMP_StringPtr sourceRoot, + XMP_StringPtr destNS, + XMP_StringPtr destRoot, + XMP_OptionBits options, + WXMP_Result * wResult ); + +// ================================================================================================= + +#if __cplusplus +} /* extern "C" */ +#endif + +#endif // __WXMPUtils_hpp__ diff --git a/xmp/client-glue/WXMP_Common.hpp b/xmp/client-glue/WXMP_Common.hpp new file mode 100644 index 0000000..2fea149 --- /dev/null +++ b/xmp/client-glue/WXMP_Common.hpp @@ -0,0 +1,132 @@ +#if ! __WXMP_Common_hpp__ +#define __WXMP_Common_hpp__ 1 + +// ================================================================================================= +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#ifndef XMP_Inline + #if TXMP_EXPAND_INLINE + #define XMP_Inline inline + #else + #define XMP_Inline /* not inline */ + #endif +#endif + +#define XMP_CTorDTorIntro(Class) template XMP_Inline Class +#define XMP_MethodIntro(Class,ResultType) template XMP_Inline ResultType Class + +typedef void (* SetClientStringProc) ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen ); +typedef void (* SetClientStringVectorProc) ( void * clientPtr, XMP_StringPtr * arrayPtr, XMP_Uns32 stringCount ); + +struct WXMP_Result { + XMP_StringPtr errMessage; + void * ptrResult; + double floatResult; + XMP_Uns64 int64Result; + XMP_Uns32 int32Result; + WXMP_Result() : errMessage(0),ptrResult(NULL),floatResult(0),int64Result(0),int32Result(0){}; +}; + +#if __cplusplus +extern "C" { +#endif + +#define PropagateException(res) \ + if ( res.errMessage != 0 ) throw XMP_Error ( res.int32Result, res.errMessage ); + +#ifndef XMP_TraceClientCalls + #define XMP_TraceClientCalls 0 + #define XMP_TraceClientCallsToFile 0 +#endif + +#if ! XMP_TraceClientCalls + #define InvokeCheck(WCallProto) \ + WXMP_Result wResult; \ + WCallProto; \ + PropagateException ( wResult ) +#else + extern FILE * xmpClientLog; + #define InvokeCheck(WCallProto) \ + WXMP_Result wResult; \ + fprintf ( xmpClientLog, "WXMP calling: %s\n", #WCallProto ); fflush ( xmpClientLog ); \ + WCallProto; \ + if ( wResult.errMessage == 0 ) { \ + fprintf ( xmpClientLog, "WXMP back, no error\n" ); fflush ( xmpClientLog ); \ + } else { \ + fprintf ( xmpClientLog, "WXMP back, error: %s\n", wResult.errMessage ); fflush ( xmpClientLog ); \ + } \ + PropagateException ( wResult ) +#endif + +// ================================================================================================= + +#define WrapNoCheckVoid(WCallProto) \ + WCallProto; + +#define WrapCheckVoid(WCallProto) \ + InvokeCheck(WCallProto); + +#define WrapCheckMetaRef(result,WCallProto) \ + InvokeCheck(WCallProto); \ + XMPMetaRef result = XMPMetaRef(wResult.ptrResult) + +#define WrapCheckIterRef(result,WCallProto) \ + InvokeCheck(WCallProto); \ + XMPIteratorRef result = XMPIteratorRef(wResult.ptrResult) + +#define WrapCheckDocOpsRef(result,WCallProto) \ + InvokeCheck(WCallProto); \ + XMPDocOpsRef result = XMPDocOpsRef(wResult.ptrResult) + +#define WrapCheckNewMetadata(result,WCallProto) \ + InvokeCheck(WCallProto); \ + void * result = wResult.ptrResult + +#define WrapCheckBool(result,WCallProto) \ + InvokeCheck(WCallProto); \ + bool result = bool(wResult.int32Result) + +#define WrapCheckTriState(result,WCallProto) \ + InvokeCheck(WCallProto); \ + XMP_TriState result = XMP_TriState(wResult.int32Result) + +#define WrapCheckOptions(result,WCallProto) \ + InvokeCheck(WCallProto); \ + XMP_OptionBits result = XMP_OptionBits(wResult.int32Result) + +#define WrapCheckStatus(result,WCallProto) \ + InvokeCheck(WCallProto); \ + XMP_Status result = XMP_Status(wResult.int32Result) + +#define WrapCheckIndex(result,WCallProto) \ + InvokeCheck(WCallProto); \ + XMP_Index result = XMP_Index(wResult.int32Result) + +#define WrapCheckInt32(result,WCallProto) \ + InvokeCheck(WCallProto); \ + XMP_Int32 result = wResult.int32Result + +#define WrapCheckInt64(result,WCallProto) \ + InvokeCheck(WCallProto); \ + XMP_Int64 result = wResult.int64Result + +#define WrapCheckFloat(result,WCallProto) \ + InvokeCheck(WCallProto); \ + double result = wResult.floatResult + +#define WrapCheckFormat(result,WCallProto) \ + InvokeCheck(WCallProto); \ + XMP_FileFormat result = wResult.int32Result + +// ================================================================================================= + +#if __cplusplus +} // extern "C" +#endif + +#endif // __WXMP_Common_hpp__