You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1951 lines
44 KiB
C++
1951 lines
44 KiB
C++
/*****************************************************************************/
|
|
// 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 <map>
|
|
|
|
/*****************************************************************************/
|
|
|
|
class dng_big_table_cache
|
|
{
|
|
|
|
private:
|
|
|
|
dng_std_mutex fMutex;
|
|
|
|
typedef std::pair <dng_fingerprint,
|
|
int32> RefCountsPair;
|
|
|
|
typedef std::map <dng_fingerprint,
|
|
int32,
|
|
dng_fingerprint_less_than> RefCountsMap;
|
|
|
|
RefCountsMap fRefCounts;
|
|
|
|
std::vector<dng_fingerprint> 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 <dng_fingerprint,
|
|
dng_look_table::table_data> TableDataPair;
|
|
|
|
typedef std::map <dng_fingerprint,
|
|
dng_look_table::table_data,
|
|
dng_fingerprint_less_than> 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
|
|
<const dng_look_table *>
|
|
(&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
|
|
<dng_look_table *>
|
|
(&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 <dng_fingerprint,
|
|
dng_rgb_table::table_data> TableDataPair;
|
|
|
|
typedef std::map <dng_fingerprint,
|
|
dng_rgb_table::table_data,
|
|
dng_fingerprint_less_than> 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
|
|
<const dng_rgb_table *>
|
|
(&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
|
|
<dng_rgb_table *>
|
|
(&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<dng_memory_block> 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<dng_memory_block> 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<dng_memory_block> block1;
|
|
|
|
{
|
|
|
|
dng_memory_stream stream (allocator);
|
|
|
|
stream.SetLittleEndian ();
|
|
|
|
PutStream (stream, false);
|
|
|
|
block1.Reset (stream.AsMemoryBlock (allocator));
|
|
|
|
}
|
|
|
|
// Compress the block.
|
|
|
|
AutoPtr<dng_memory_block> 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<dng_memory_block> block2 (EncodeAsBinary (allocator, compressedSize));
|
|
|
|
// Encode binary to text.
|
|
|
|
AutoPtr<dng_memory_block> 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<dng_memory_block> 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);
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|