#ifndef LERC2_H
#define LERC2_H
#include <cfloat>
#include <cmath>
#include <climits>
#include <algorithm>
#include <string>
#include "BitMask.h"
#include "BitStuffer2.h"
#include "fpl_Lerc2Ext.h"
NAMESPACE_LERC_START
class Lerc2
{
public:
Lerc2();
Lerc2(int nDepth, int nCols, int nRows, const Byte* pMaskBits = nullptr); virtual ~Lerc2() {}
static int CurrentVersion() { return 6; }
bool SetEncoderToOldVersion(int version);
bool Set(int nDepth, int nCols, int nRows, const Byte* pMaskBits = nullptr);
bool SetNoDataValues(bool bNeedsNoDataVal, double noDataVal, double noDataValOrig);
bool SetNumBlobsMoreToCome(int nBlobsMore); bool SetIsAllInt(bool bIsAllInt);
template<class T>
unsigned int ComputeNumBytesNeededToWrite(const T* arr, double maxZError, bool encodeMask);
template<class T>
bool Encode(const T* arr, Byte** ppByte);
enum DataType {DT_Char = 0, DT_Byte, DT_Short, DT_UShort, DT_Int, DT_UInt, DT_Float, DT_Double, DT_Undefined};
struct HeaderInfo
{
int version;
unsigned int checksum;
int nRows,
nCols,
nDepth,
numValidPixel,
microBlockSize,
blobSize,
nBlobsMore;
Byte bPassNoDataValues, bIsInt, bReserved3,
bReserved4;
DataType dt;
double maxZError,
zMin, zMax,
noDataVal, noDataValOrig;
void RawInit() { memset(this, 0, sizeof(struct HeaderInfo)); }
bool TryHuffmanInt() const { return (version >= 2) && (dt == DT_Byte || dt == DT_Char) && (maxZError == 0.5); }
bool TryHuffmanFlt() const { return (version >= 6) && (dt == DT_Float || dt == DT_Double) && (maxZError == 0); }
};
static bool GetHeaderInfo(const Byte* pByte, size_t nBytesRemaining, struct HeaderInfo& headerInfo, bool& bHasMask);
bool GetRanges(const Byte* pByte, size_t nBytesRemaining, double* pMins, double* pMaxs);
template<class T>
bool Decode(const Byte** ppByte, size_t& nBytesRemaining, T* arr, Byte* pMaskBits = nullptr);
private:
enum ImageEncodeMode { IEM_Tiling = 0, IEM_DeltaHuffman, IEM_Huffman, IEM_DeltaDeltaHuffman };
enum BlockEncodeMode { BEM_RawBinary = 0, BEM_BitStuffSimple, BEM_BitStuffLUT };
int m_microBlockSize,
m_maxValToQuantize;
BitMask m_bitMask;
HeaderInfo m_headerInfo;
BitStuffer2 m_bitStuffer2;
bool m_encodeMask,
m_writeDataOneSweep;
ImageEncodeMode m_imageEncodeMode;
std::vector<double> m_zMinVec, m_zMaxVec;
std::vector<std::pair<unsigned short, unsigned int> > m_huffmanCodes;
LosslessFPCompression m_lfpc;
private:
static std::string FileKey() { return "Lerc2 "; }
static bool IsLittleEndianSystem() { int n = 1; return (1 == *((Byte*)&n)) && (4 == sizeof(int)); }
void Init();
static unsigned int ComputeNumBytesHeaderToWrite(const struct HeaderInfo& hd);
static bool WriteHeader(Byte** ppByte, const struct HeaderInfo& hd);
static bool ReadHeader(const Byte** ppByte, size_t& nBytesRemaining, struct HeaderInfo& hd);
bool WriteMask(Byte** ppByte) const;
bool ReadMask(const Byte** ppByte, size_t& nBytesRemaining);
bool DoChecksOnEncode(Byte* pBlobBegin, Byte* pBlobEnd) const;
static unsigned int ComputeChecksumFletcher32(const Byte* pByte, int len);
static void AddUIntToCounts(int* pCounts, unsigned int val, int nBits);
static void AddIntToCounts(int* pCounts, int val, int nBits);
template<class T>
bool TryBitPlaneCompression(const T* data, double eps, double& newMaxZError) const;
template<class T>
bool TryRaiseMaxZError(const T* data, double& maxZError) const;
static bool PruneCandidates(std::vector<double>& roundErr, std::vector<double>& zErr,
std::vector<int>& zFac, double maxZError);
template<class T>
bool WriteDataOneSweep(const T* data, Byte** ppByte) const;
template<class T>
bool ReadDataOneSweep(const Byte** ppByte, size_t& nBytesRemaining, T* data) const;
template<class T>
bool ComputeMinMaxRanges(const T* data, std::vector<double>& zMinVec, std::vector<double>& zMaxVec) const;
template<class T>
bool WriteTiles(const T* data, Byte** ppByte, int& numBytes) const;
template<class T>
bool ReadTiles(const Byte** ppByte, size_t& nBytesRemaining, T* data) const;
template<class T>
bool GetValidDataAndStats(const T* data, int i0, int i1, int j0, int j1, int iDepth,
T* dataBuf, T& zMin, T& zMax, int& numValidPixel, bool& tryLut) const;
template<class T>
static bool ComputeDiffSliceInt(const T* data, const T* prevData, int numValidPixel, bool bCheckForIntOverflow,
double maxZError, std::vector<int>& diffDataVec, int& zMin, int& zMax, bool& tryLut);
template<class T>
static bool ComputeDiffSliceFlt(const T* data, const T* prevData, int numValidPixel, bool bCheckForFltRndErr,
double maxZError, std::vector<T>& diffDataVec, T& zMin, T& zMax, bool& tryLut);
static bool NeedToCheckForIntOverflow(const HeaderInfo& hd);
static bool NeedToCheckForFltRndErr(const HeaderInfo& hd);
static double ComputeMaxVal(double zMin, double zMax, double maxZError);
template<class T>
bool NeedToQuantize(int numValidPixel, T zMin, T zMax) const;
template<class T>
void Quantize(const T* dataBuf, int num, T zMin, std::vector<unsigned int>& quantVec) const;
template<class T>
static void ScaleBack(T* dataBuf, const std::vector<unsigned int>& quantVec,
double zMin, bool bDiff, bool bClamp, double zMaxClamp, double maxZError);
template<class T>
static void ScaleBackConstBlock(T* dataBuf, int num, double zMin, bool bClamp, double zMaxClamp);
template<class T>
int NumBytesTile(int numValidPixel, T zMin, T zMax, DataType dtZ, bool tryLut, BlockEncodeMode& blockEncodeMode,
const std::vector<std::pair<unsigned int, unsigned int> >& sortedQuantVec) const;
template<class T>
bool WriteTile(const T* dataBuf, int num, Byte** ppByte, int& numBytesWritten, int j0, T zMin, T zMax,
DataType dtZ, bool bDiffEnc, const std::vector<unsigned int>& quantVec, BlockEncodeMode blockEncodeMode,
const std::vector<std::pair<unsigned int, unsigned int> >& sortedQuantVec) const;
template<class T>
bool ReadTile(const Byte** ppByte, size_t& nBytesRemaining, T* data, int i0, int i1, int j0, int j1, int iDepth,
std::vector<unsigned int>& bufferVec) const;
template<class T>
static int ReduceDataType(T z, DataType dt, DataType& dtReduced);
static DataType GetDataTypeUsed(DataType dt, int reducedTypeCode);
static DataType ValidateDataType(int dt);
static bool WriteVariableDataType(Byte** ppByte, double z, DataType dtUsed);
static double ReadVariableDataType(const Byte** ppByte, DataType dtUsed);
template<class T>
static DataType GetDataType(T z);
static unsigned int GetMaxValToQuantize(DataType dt);
static unsigned int GetDataTypeSize(DataType dt);
static void SortQuantArray(const std::vector<unsigned int>& quantVec,
std::vector<std::pair<unsigned int, unsigned int> >& sortedQuantVec);
template<class T>
void ComputeHuffmanCodes(const T* data, int& numBytes, ImageEncodeMode& imageEncodeMode,
std::vector<std::pair<unsigned short, unsigned int> >& codes) const;
template<class T>
void ComputeHistoForHuffman(const T* data, std::vector<int>& histo, std::vector<int>& deltaHisto) const;
template<class T>
bool EncodeHuffman(const T* data, Byte** ppByte) const;
template<class T>
bool DecodeHuffman(const Byte** ppByte, size_t& nBytesRemaining, T* data) const;
template<class T>
bool WriteMinMaxRanges(const T* data, Byte** ppByte) const;
template<class T>
bool ReadMinMaxRanges(const Byte** ppByte, size_t& nBytesRemaining, const T* data);
bool CheckMinMaxRanges(bool& minMaxEqual) const;
template<class T>
bool FillConstImage(T* data) const;
};
inline void Lerc2::AddUIntToCounts(int* pCounts, unsigned int val, int nBits)
{
pCounts[0] += val & 1;
for (int i = 1; i < nBits; i++)
pCounts[i] += (val >>= 1) & 1;
}
inline void Lerc2::AddIntToCounts(int* pCounts, int val, int nBits)
{
pCounts[0] += val & 1;
for (int i = 1; i < nBits; i++)
pCounts[i] += (val >>= 1) & 1;
}
inline bool Lerc2::NeedToCheckForIntOverflow(const HeaderInfo& hd)
{
return (hd.dt == DT_Int || hd.dt == DT_UInt) && (hd.zMax - hd.zMin >= 0x7FFFFFFF);
}
inline bool Lerc2::NeedToCheckForFltRndErr(const HeaderInfo& hd)
{
if (hd.dt != DT_Float)
return false;
if (hd.zMax - hd.zMin > 100000) return true;
float diff = (float)(hd.zMax - hd.zMin);
double testMax = (double)diff + hd.zMin;
double fltRndErr = fabs(testMax - hd.zMax);
return (fltRndErr > hd.maxZError / 8);
}
inline double Lerc2::ComputeMaxVal(double zMin, double zMax, double maxZError)
{
double fac = 1 / (2 * maxZError); return (zMax - zMin) * fac;
}
template<class T>
inline bool Lerc2::NeedToQuantize(int numValidPixel, T zMin, T zMax) const
{
if (numValidPixel == 0 || m_headerInfo.maxZError == 0)
return false;
double maxVal = ComputeMaxVal(zMin, zMax, m_headerInfo.maxZError);
return !(maxVal > m_maxValToQuantize || (unsigned int)(maxVal + 0.5) == 0);
}
template<class T>
inline void Lerc2::Quantize(const T* dataBuf, int num, T zMin, std::vector<unsigned int>& quantVec) const
{
quantVec.resize(num);
if (m_headerInfo.dt < DT_Float && m_headerInfo.maxZError == 0.5) {
for (int i = 0; i < num; i++)
quantVec[i] = (unsigned int)(dataBuf[i] - zMin); }
else {
double scale = 1 / (2 * m_headerInfo.maxZError);
double zMinDbl = (double)zMin;
for (int i = 0; i < num; i++)
quantVec[i] = (unsigned int)(((double)dataBuf[i] - zMinDbl) * scale + 0.5); }
}
template<class T>
inline void Lerc2::ScaleBack(T* dataBuf, const std::vector<unsigned int>& quantVec,
double zMin, bool bDiff, bool bClamp, double zMaxClamp, double maxZError)
{
double invScale = 2 * maxZError; int num = (int)quantVec.size();
if (!bClamp)
for (int i = 0; i < num; i++)
{
double z = zMin + quantVec[i] * invScale + (bDiff ? dataBuf[i] : 0);
dataBuf[i] = (T)z;
}
else
for (int i = 0; i < num; i++)
{
double z = zMin + quantVec[i] * invScale + (bDiff ? dataBuf[i] : 0);
dataBuf[i] = (T)std::min(z, zMaxClamp);
}
}
template<class T>
inline void Lerc2::ScaleBackConstBlock(T* dataBuf, int num, double zMin, bool bClamp, double zMaxClamp)
{
if (!bClamp)
for (int i = 0; i < num; i++)
dataBuf[i] = (T)(zMin + dataBuf[i]);
else
for (int i = 0; i < num; i++)
dataBuf[i] = (T)std::min(zMin + dataBuf[i], zMaxClamp);
}
template<class T>
inline int Lerc2::NumBytesTile(int numValidPixel, T zMin, T zMax, DataType dtZ, bool tryLut,
BlockEncodeMode& blockEncodeMode, const std::vector<std::pair<unsigned int, unsigned int> >& sortedQuantVec) const
{
blockEncodeMode = BEM_RawBinary;
if (numValidPixel == 0 || (zMin == 0 && zMax == 0))
return 1;
double maxVal = 0, maxZError = m_headerInfo.maxZError;
int nBytesRaw = (int)(1 + numValidPixel * sizeof(T));
if ((maxZError == 0 && zMax > zMin)
|| (maxZError > 0 && (maxVal = ComputeMaxVal(zMin, zMax, maxZError)) > m_maxValToQuantize))
{
return nBytesRaw;
}
else
{
DataType dtReduced;
ReduceDataType(zMin, dtZ, dtReduced);
int nBytes = 1 + GetDataTypeSize(dtReduced);
unsigned int maxElem = (unsigned int)(maxVal + 0.5);
if (maxElem > 0)
{
nBytes += (!tryLut) ? BitStuffer2::ComputeNumBytesNeededSimple(numValidPixel, maxElem)
: BitStuffer2::ComputeNumBytesNeededLut(sortedQuantVec, tryLut);
}
if (nBytes < nBytesRaw)
blockEncodeMode = (!tryLut || maxElem == 0) ? BEM_BitStuffSimple : BEM_BitStuffLUT;
else
nBytes = nBytesRaw;
return nBytes;
}
}
template<class T>
inline int Lerc2::ReduceDataType(T z, DataType dt, DataType& dtReduced)
{
Byte b = (z >= 0 && z <= 255) ? (Byte)z : 0;
switch (dt)
{
case DT_Short:
{
signed char c = (z >= (double)-128 && z <= 127) ? (signed char)z : 0; int tc = (T)c == z ? 2 : (T)b == z ? 1 : 0;
dtReduced = (DataType)(dt - tc);
return tc;
}
case DT_UShort:
{
int tc = (T)b == z ? 1 : 0;
dtReduced = (DataType)(dt - 2 * tc);
return tc;
}
case DT_Int:
{
short s = (z >= (double)SHRT_MIN && z <= SHRT_MAX) ? (short)z : 0;
unsigned short us = (z >= 0 && z <= USHRT_MAX) ? (unsigned short)z : 0;
int tc = (T)b == z ? 3 : (T)s == z ? 2 : (T)us == z ? 1 : 0;
dtReduced = (DataType)(dt - tc);
return tc;
}
case DT_UInt:
{
unsigned short us = (z >= 0 && z <= USHRT_MAX) ? (unsigned short)z : 0;
int tc = (T)b == z ? 2 : (T)us == z ? 1 : 0;
dtReduced = (DataType)(dt - 2 * tc);
return tc;
}
case DT_Float:
{
short s = (z >= (float)SHRT_MIN && z <= SHRT_MAX) ? (short)z : 0;
int tc = (T)b == z ? 2 : (T)s == z ? 1 : 0;
dtReduced = tc == 0 ? dt : (tc == 1 ? DT_Short : DT_Byte);
return tc;
}
case DT_Double:
{
short s = (z >= (double)SHRT_MIN && z <= SHRT_MAX) ? (short)z : 0;
int l = (z >= (double)INT_MIN && z <= (double)INT_MAX) ? (int)z : 0;
float f = (z >= -FLT_MAX && z <= FLT_MAX) ? (float)z : 0;
int tc = (T)s == z ? 3 : (T)l == z ? 2 : (T)f == z ? 1 : 0;
dtReduced = tc == 0 ? dt : (DataType)(dt - 2 * tc + 1);
return tc;
}
default:
{
dtReduced = dt;
return 0;
}
}
}
inline Lerc2::DataType Lerc2::ValidateDataType(int dt)
{
if (dt >= DT_Char && dt <= DT_Double)
return static_cast<DataType>(dt);
return DT_Undefined;
}
inline
Lerc2::DataType Lerc2::GetDataTypeUsed(DataType dt, int tc)
{
switch (dt)
{
case DT_Short:
case DT_Int: return ValidateDataType(dt - tc);
case DT_UShort:
case DT_UInt: return ValidateDataType(dt - 2 * tc);
case DT_Float: return tc == 0 ? dt : (tc == 1 ? DT_Short : DT_Byte);
case DT_Double: return tc == 0 ? dt : ValidateDataType(dt - 2 * tc + 1);
default:
return dt;
}
}
inline
bool Lerc2::WriteVariableDataType(Byte** ppByte, double z, DataType dtUsed)
{
Byte* ptr = *ppByte;
switch (dtUsed)
{
case DT_Char:
{
*((signed char*)ptr) = (signed char)z;
ptr++;
break;
}
case DT_Byte:
{
*((Byte*)ptr) = (Byte)z;
ptr++;
break;
}
case DT_Short:
{
short s = (short)z;
memcpy(ptr, &s, sizeof(short));
ptr += 2;
break;
}
case DT_UShort:
{
unsigned short us = (unsigned short)z;
memcpy(ptr, &us, sizeof(unsigned short));
ptr += 2;
break;
}
case DT_Int:
{
int i = (int)z;
memcpy(ptr, &i, sizeof(int));
ptr += 4;
break;
}
case DT_UInt:
{
unsigned int n = (unsigned int)z;
memcpy(ptr, &n, sizeof(unsigned int));
ptr += 4;
break;
}
case DT_Float:
{
float f = (float)z;
memcpy(ptr, &f, sizeof(float));
ptr += 4;
break;
}
case DT_Double:
{
memcpy(ptr, &z, sizeof(double));
ptr += 8;
break;
}
default:
return false;
}
*ppByte = ptr;
return true;
}
inline
double Lerc2::ReadVariableDataType(const Byte** ppByte, DataType dtUsed)
{
const Byte* ptr = *ppByte;
switch (dtUsed)
{
case DT_Char:
{
signed char c = *((signed char*)ptr);
*ppByte = ptr + 1;
return c;
}
case DT_Byte:
{
Byte b = *((Byte*)ptr);
*ppByte = ptr + 1;
return b;
}
case DT_Short:
{
short s;
memcpy(&s, ptr, sizeof(short));
*ppByte = ptr + 2;
return s;
}
case DT_UShort:
{
unsigned short us;
memcpy(&us, ptr, sizeof(unsigned short));
*ppByte = ptr + 2;
return us;
}
case DT_Int:
{
int i;
memcpy(&i, ptr, sizeof(int));
*ppByte = ptr + 4;
return i;
}
case DT_UInt:
{
unsigned int n;
memcpy(&n, ptr, sizeof(unsigned int));
*ppByte = ptr + 4;
return n;
}
case DT_Float:
{
float f;
memcpy(&f, ptr, sizeof(float));
*ppByte = ptr + 4;
return f;
}
case DT_Double:
{
double d;
memcpy(&d, ptr, sizeof(double));
*ppByte = ptr + 8;
return d;
}
default:
return 0;
}
}
inline
unsigned int Lerc2::GetMaxValToQuantize(DataType dt)
{
switch (dt)
{
case DT_Char:
case DT_Byte: case DT_Short:
case DT_UShort: return (1 << 15) - 1;
case DT_Int:
case DT_UInt:
case DT_Float:
case DT_Double: return (1 << 30) - 1;
default:
return 0;
}
}
inline
unsigned int Lerc2::GetDataTypeSize(DataType dt)
{
switch (dt)
{
case DT_Char:
case DT_Byte: return 1;
case DT_Short:
case DT_UShort: return 2;
case DT_Int:
case DT_UInt:
case DT_Float: return 4;
case DT_Double: return 8;
default:
return 0;
}
}
inline
bool Lerc2::CheckMinMaxRanges(bool& minMaxEqual) const
{
int nDepth = m_headerInfo.nDepth;
if ((int)m_zMinVec.size() != nDepth || (int)m_zMaxVec.size() != nDepth)
return false;
minMaxEqual = (0 == memcmp(&m_zMinVec[0], &m_zMaxVec[0], nDepth * sizeof(m_zMinVec[0])));
return true;
}
NAMESPACE_LERC_END
#endif