#include "Defines.h"
#include "Lerc.h"
#include "Lerc2.h"
#include <typeinfo>
#include <limits>
#include <functional>
#ifdef HAVE_LERC1_DECODE
#include "Lerc1Decode/CntZImage.h"
#endif
using namespace std;
USING_NAMESPACE_LERC
ErrCode Lerc::ComputeCompressedSize(const void* pData, int version, DataType dt, int nDepth, int nCols, int nRows, int nBands,
int nMasks, const Byte* pValidBytes, double maxZErr, unsigned int& numBytesNeeded, const unsigned char* pUsesNoData, const double* noDataValues)
{
#define LERC_ARG_1 version, nDepth, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, numBytesNeeded, pUsesNoData, noDataValues
switch (dt)
{
case DT_Char: return ComputeCompressedSizeTempl((const signed char*)pData, LERC_ARG_1);
case DT_Byte: return ComputeCompressedSizeTempl((const Byte*)pData, LERC_ARG_1);
case DT_Short: return ComputeCompressedSizeTempl((const short*)pData, LERC_ARG_1);
case DT_UShort: return ComputeCompressedSizeTempl((const unsigned short*)pData, LERC_ARG_1);
case DT_Int: return ComputeCompressedSizeTempl((const int*)pData, LERC_ARG_1);
case DT_UInt: return ComputeCompressedSizeTempl((const unsigned int*)pData, LERC_ARG_1);
case DT_Float: return ComputeCompressedSizeTempl((const float*)pData, LERC_ARG_1);
case DT_Double: return ComputeCompressedSizeTempl((const double*)pData, LERC_ARG_1);
default:
return ErrCode::WrongParam;
}
#undef LERC_ARG_1
}
ErrCode Lerc::Encode(const void* pData, int version, DataType dt, int nDepth, int nCols, int nRows, int nBands,
int nMasks, const Byte* pValidBytes, double maxZErr, Byte* pBuffer, unsigned int numBytesBuffer,
unsigned int& numBytesWritten, const unsigned char* pUsesNoData, const double* noDataValues)
{
#define LERC_ARG_2 version, nDepth, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, pBuffer, numBytesBuffer, numBytesWritten, pUsesNoData, noDataValues
switch (dt)
{
case DT_Char: return EncodeTempl((const signed char*)pData, LERC_ARG_2);
case DT_Byte: return EncodeTempl((const Byte*)pData, LERC_ARG_2);
case DT_Short: return EncodeTempl((const short*)pData, LERC_ARG_2);
case DT_UShort: return EncodeTempl((const unsigned short*)pData, LERC_ARG_2);
case DT_Int: return EncodeTempl((const int*)pData, LERC_ARG_2);
case DT_UInt: return EncodeTempl((const unsigned int*)pData, LERC_ARG_2);
case DT_Float: return EncodeTempl((const float*)pData, LERC_ARG_2);
case DT_Double: return EncodeTempl((const double*)pData, LERC_ARG_2);
default:
return ErrCode::WrongParam;
}
#undef LERC_ARG_2
}
ErrCode Lerc::GetLercInfo(const Byte* pLercBlob, unsigned int numBytesBlob, struct LercInfo& lercInfo, double* pMins, double* pMaxs, size_t nElem)
{
lercInfo.RawInit();
struct Lerc2::HeaderInfo lerc2Info;
bool bHasMask = false;
int nMasks = 0;
if (Lerc2::GetHeaderInfo(pLercBlob, numBytesBlob, lerc2Info, bHasMask))
{
lercInfo.version = lerc2Info.version;
lercInfo.nDepth = lerc2Info.nDepth;
lercInfo.nCols = lerc2Info.nCols;
lercInfo.nRows = lerc2Info.nRows;
lercInfo.numValidPixel = lerc2Info.numValidPixel; lercInfo.blobSize = lerc2Info.blobSize;
lercInfo.dt = (DataType)lerc2Info.dt;
lercInfo.zMin = lerc2Info.zMin;
lercInfo.zMax = lerc2Info.zMax;
lercInfo.maxZError = lerc2Info.maxZError;
lercInfo.nUsesNoDataValue = lerc2Info.bPassNoDataValues ? 1 : 0;
bool bTryNextBlob = (lerc2Info.version <= 5) || (lerc2Info.nBlobsMore > 0);
if (bHasMask || lercInfo.numValidPixel == 0)
nMasks = 1;
if (pMins && pMaxs)
{
ErrCode errCode = GetRanges(pLercBlob, numBytesBlob, 0, lerc2Info, pMins, pMaxs, nElem); if (errCode != ErrCode::Ok)
return errCode;
}
lercInfo.nBands = 1;
if (lercInfo.blobSize > (int)numBytesBlob) return ErrCode::BufferTooSmall;
struct Lerc2::HeaderInfo hdInfo;
while (bTryNextBlob && Lerc2::GetHeaderInfo(pLercBlob + lercInfo.blobSize, numBytesBlob - lercInfo.blobSize, hdInfo, bHasMask))
{
if (hdInfo.nDepth != lercInfo.nDepth
|| hdInfo.nCols != lercInfo.nCols
|| hdInfo.nRows != lercInfo.nRows
|| (int)hdInfo.dt != (int)lercInfo.dt)
{
return ErrCode::Failed;
}
bTryNextBlob = (hdInfo.version <= 5) || (hdInfo.nBlobsMore > 0);
if (hdInfo.bPassNoDataValues)
lercInfo.nUsesNoDataValue++;
if (bHasMask || hdInfo.numValidPixel != lercInfo.numValidPixel) nMasks = 2;
if (lercInfo.blobSize > std::numeric_limits<int>::max() - hdInfo.blobSize) return ErrCode::Failed;
if (lercInfo.blobSize + hdInfo.blobSize > (int)numBytesBlob) return ErrCode::BufferTooSmall;
lercInfo.zMin = min(lercInfo.zMin, hdInfo.zMin);
lercInfo.zMax = max(lercInfo.zMax, hdInfo.zMax);
lercInfo.maxZError = max(lercInfo.maxZError, hdInfo.maxZError);
if (pMins && pMaxs)
{
ErrCode errCode = GetRanges(pLercBlob + lercInfo.blobSize, numBytesBlob - lercInfo.blobSize, lercInfo.nBands, hdInfo, pMins, pMaxs, nElem);
if (errCode != ErrCode::Ok)
return errCode;
}
lercInfo.blobSize += hdInfo.blobSize;
lercInfo.nBands++;
}
lercInfo.nMasks = nMasks > 1 ? lercInfo.nBands : nMasks;
if (lercInfo.nUsesNoDataValue > 0)
lercInfo.nUsesNoDataValue = lercInfo.nBands;
return ErrCode::Ok;
}
#ifdef HAVE_LERC1_DECODE
unsigned int numBytesHeaderBand0 = CntZImage::computeNumBytesNeededToReadHeader(false);
unsigned int numBytesHeaderBand1 = CntZImage::computeNumBytesNeededToReadHeader(true);
const Byte* pByte = pLercBlob;
lercInfo.zMin = FLT_MAX;
lercInfo.zMax = -FLT_MAX;
CntZImage cntZImg;
if (numBytesHeaderBand0 <= numBytesBlob && cntZImg.read(&pByte, 1e12, true)) {
size_t nBytesRead = pByte - pLercBlob;
size_t nBytesNeeded = 10 + 4 * sizeof(int) + 1 * sizeof(double);
if (nBytesRead < nBytesNeeded)
return ErrCode::Failed;
const Byte* ptr = pLercBlob;
ptr += 10 + 2 * sizeof(int);
int height(0), width(0);
memcpy(&height, ptr, sizeof(int)); ptr += sizeof(int);
memcpy(&width, ptr, sizeof(int)); ptr += sizeof(int);
double maxZErrorInFile(0);
memcpy(&maxZErrorInFile, ptr, sizeof(double));
if (height > 20000 || width > 20000) return ErrCode::Failed;
lercInfo.nDepth = 1;
lercInfo.nCols = width;
lercInfo.nRows = height;
lercInfo.dt = Lerc::DT_Float;
lercInfo.maxZError = maxZErrorInFile;
pByte = pLercBlob;
bool onlyZPart = false;
while (lercInfo.blobSize + numBytesHeaderBand1 < numBytesBlob) {
if (!cntZImg.read(&pByte, 1e12, false, onlyZPart))
return (lercInfo.nBands > 0) ? ErrCode::Ok : ErrCode::Failed;
onlyZPart = true;
lercInfo.blobSize = (int)(pByte - pLercBlob);
int numValidPixels = 0;
float zMin = FLT_MAX;
float zMax = -FLT_MAX;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
if (cntZImg(i, j).cnt > 0)
{
numValidPixels++;
float z = cntZImg(i, j).z;
zMax = max(zMax, z);
zMin = min(zMin, z);
}
}
lercInfo.numValidPixel = numValidPixels;
lercInfo.zMin = std::min(lercInfo.zMin, (double)zMin);
lercInfo.zMax = std::max(lercInfo.zMax, (double)zMax);
lercInfo.nMasks = numValidPixels < width * height ? 1 : 0;
if (pMins && pMaxs)
{
pMins[lercInfo.nBands] = zMin;
pMaxs[lercInfo.nBands] = zMax;
}
lercInfo.nBands++;
}
return ErrCode::Ok;
}
#endif
return ErrCode::Failed;
}
ErrCode Lerc::Decode(const Byte* pLercBlob, unsigned int numBytesBlob, int nMasks, Byte* pValidBytes,
int nDepth, int nCols, int nRows, int nBands, DataType dt, void* pData, unsigned char* pUsesNoData, double* noDataValues)
{
#define LERC_ARG_3 pLercBlob, numBytesBlob, nDepth, nCols, nRows, nBands, nMasks, pValidBytes, pUsesNoData, noDataValues
switch (dt)
{
case DT_Char: return DecodeTempl((signed char*)pData, LERC_ARG_3);
case DT_Byte: return DecodeTempl((Byte*)pData, LERC_ARG_3);
case DT_Short: return DecodeTempl((short*)pData, LERC_ARG_3);
case DT_UShort: return DecodeTempl((unsigned short*)pData, LERC_ARG_3);
case DT_Int: return DecodeTempl((int*)pData, LERC_ARG_3);
case DT_UInt: return DecodeTempl((unsigned int*)pData, LERC_ARG_3);
case DT_Float: return DecodeTempl((float*)pData, LERC_ARG_3);
case DT_Double: return DecodeTempl((double*)pData, LERC_ARG_3);
default:
return ErrCode::WrongParam;
}
#undef LERC_ARG_3
}
ErrCode Lerc::ConvertToDouble(const void* pDataIn, DataType dt, size_t nDataValues, double* pDataOut)
{
switch (dt)
{
case DT_Char: return ConvertToDoubleTempl((const signed char*)pDataIn, nDataValues, pDataOut);
case DT_Byte: return ConvertToDoubleTempl((const Byte*)pDataIn, nDataValues, pDataOut);
case DT_Short: return ConvertToDoubleTempl((const short*)pDataIn, nDataValues, pDataOut);
case DT_UShort: return ConvertToDoubleTempl((const unsigned short*)pDataIn, nDataValues, pDataOut);
case DT_Int: return ConvertToDoubleTempl((const int*)pDataIn, nDataValues, pDataOut);
case DT_UInt: return ConvertToDoubleTempl((const unsigned int*)pDataIn, nDataValues, pDataOut);
case DT_Float: return ConvertToDoubleTempl((const float*)pDataIn, nDataValues, pDataOut);
default:
return ErrCode::WrongParam;
}
}
template<class T>
ErrCode Lerc::ComputeCompressedSizeTempl(const T* pData, int version, int nDepth, int nCols, int nRows,
int nBands, int nMasks, const Byte* pValidBytes, double maxZErr, unsigned int& numBytesNeeded,
const unsigned char* pUsesNoData, const double* noDataValues)
{
numBytesNeeded = 0;
if (!pData || nDepth <= 0 || nCols <= 0 || nRows <= 0 || nBands <= 0 || maxZErr < 0)
return ErrCode::WrongParam;
if (!(nMasks == 0 || nMasks == 1 || nMasks == nBands) || (nMasks > 0 && !pValidBytes))
return ErrCode::WrongParam;
unsigned int numBytesWritten = 0;
if (version >= 0 && version <= 5)
{
if (pUsesNoData)
for (int i = 0; i < nBands; i++)
if (pUsesNoData[i])
return ErrCode::WrongParam;
return EncodeInternal_v5(pData, version, nDepth, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr,
numBytesNeeded, nullptr, 0, numBytesWritten);
}
else
{
return EncodeInternal(pData, version, nDepth, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr,
numBytesNeeded, nullptr, 0, numBytesWritten, pUsesNoData, noDataValues);
}
}
template<class T>
ErrCode Lerc::EncodeTempl(const T* pData, int version, int nDepth, int nCols, int nRows, int nBands,
int nMasks, const Byte* pValidBytes, double maxZErr, Byte* pBuffer, unsigned int numBytesBuffer,
unsigned int& numBytesWritten, const unsigned char* pUsesNoData, const double* noDataValues)
{
numBytesWritten = 0;
if (!pData || nDepth <= 0 || nCols <= 0 || nRows <= 0 || nBands <= 0 || maxZErr < 0 || !pBuffer || !numBytesBuffer)
return ErrCode::WrongParam;
if (!(nMasks == 0 || nMasks == 1 || nMasks == nBands) || (nMasks > 0 && !pValidBytes))
return ErrCode::WrongParam;
unsigned int numBytesNeeded = 0;
if (version >= 0 && version <= 5)
{
if (pUsesNoData)
for (int i = 0; i < nBands; i++)
if (pUsesNoData[i])
return ErrCode::WrongParam;
return EncodeInternal_v5(pData, version, nDepth, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr,
numBytesNeeded, pBuffer, numBytesBuffer, numBytesWritten);
}
else
{
return EncodeInternal(pData, version, nDepth, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr,
numBytesNeeded, pBuffer, numBytesBuffer, numBytesWritten, pUsesNoData, noDataValues);
}
}
template<class T>
ErrCode Lerc::DecodeTempl(T* pData, const Byte* pLercBlob, unsigned int numBytesBlob,
int nDepth, int nCols, int nRows, int nBands, int nMasks, Byte* pValidBytes,
unsigned char* pUsesNoData, double* noDataValues)
{
if (!pData || nDepth <= 0 || nCols <= 0 || nRows <= 0 || nBands <= 0 || !pLercBlob || !numBytesBlob)
return ErrCode::WrongParam;
if (!(nMasks == 0 || nMasks == 1 || nMasks == nBands) || (nMasks > 0 && !pValidBytes))
return ErrCode::WrongParam;
const Byte* pByte = pLercBlob;
Lerc2::HeaderInfo hdInfo;
bool bHasMask = false;
if (Lerc2::GetHeaderInfo(pByte, numBytesBlob, hdInfo, bHasMask) && hdInfo.version >= 1) {
LercInfo lercInfo;
ErrCode errCode = GetLercInfo(pLercBlob, numBytesBlob, lercInfo); if (errCode != ErrCode::Ok)
return errCode;
if (nMasks < lercInfo.nMasks) return ErrCode::WrongParam;
if (nBands > lercInfo.nBands)
return ErrCode::WrongParam;
if (lercInfo.nUsesNoDataValue && nDepth > 1)
{
if (!pUsesNoData || !noDataValues)
return ErrCode::HasNoData;
try
{
memset(pUsesNoData, 0, nBands);
memset(noDataValues, 0, nBands * sizeof(double));
}
catch (...)
{
return ErrCode::HasNoData;
}
}
size_t nBytesRemaining = numBytesBlob;
Lerc2 lerc2;
BitMask bitMask;
for (int iBand = 0; iBand < nBands; iBand++)
{
if (((size_t)(pByte - pLercBlob) < numBytesBlob) && Lerc2::GetHeaderInfo(pByte, nBytesRemaining, hdInfo, bHasMask))
{
if (hdInfo.nDepth != nDepth || hdInfo.nCols != nCols || hdInfo.nRows != nRows)
return ErrCode::Failed;
if ((pByte - pLercBlob) + (size_t)hdInfo.blobSize > numBytesBlob)
return ErrCode::BufferTooSmall;
size_t nPix = (size_t)iBand * nRows * nCols;
T* arr = pData + nPix * nDepth;
bool bGetMask = iBand < nMasks;
if (bGetMask && !bitMask.SetSize(nCols, nRows))
return ErrCode::Failed;
if (!lerc2.Decode(&pByte, nBytesRemaining, arr, bGetMask ? bitMask.Bits() : nullptr))
return ErrCode::Failed;
if (lercInfo.nUsesNoDataValue && nDepth > 1)
{
pUsesNoData[iBand] = hdInfo.bPassNoDataValues ? 1 : 0;
noDataValues[iBand] = hdInfo.noDataValOrig;
if (hdInfo.bPassNoDataValues && !RemapNoData(arr, bitMask, hdInfo))
return ErrCode::Failed;
}
if (bGetMask && !Convert(bitMask, pValidBytes + nPix))
return ErrCode::Failed;
}
} }
else {
#ifdef HAVE_LERC1_DECODE
unsigned int numBytesHeaderBand0 = CntZImage::computeNumBytesNeededToReadHeader(false);
unsigned int numBytesHeaderBand1 = CntZImage::computeNumBytesNeededToReadHeader(true);
const Byte* pByte1 = pLercBlob;
CntZImage zImg;
for (int iBand = 0; iBand < nBands; iBand++)
{
unsigned int numBytesHeader = iBand == 0 ? numBytesHeaderBand0 : numBytesHeaderBand1;
if ((size_t)(pByte - pLercBlob) + numBytesHeader > numBytesBlob)
return ErrCode::BufferTooSmall;
bool onlyZPart = iBand > 0;
if (!zImg.read(&pByte1, 1e12, false, onlyZPart))
return ErrCode::Failed;
if (zImg.getWidth() != nCols || zImg.getHeight() != nRows)
return ErrCode::Failed;
size_t nPix = (size_t)iBand * nRows * nCols;
T* arr = pData + nPix;
Byte* pDst = iBand < nMasks ? pValidBytes + nPix : nullptr;
if (!Convert(zImg, arr, pDst, iBand == 0))
return ErrCode::Failed;
}
#else
return ErrCode::Failed;
#endif
}
return ErrCode::Ok;
}
template<class T>
ErrCode Lerc::EncodeInternal_v5(const T* pData, int version, int nDepth, int nCols, int nRows, int nBands,
int nMasks, const Byte* pValidBytes, double maxZErr, unsigned int& numBytesNeeded,
Byte* pBuffer, unsigned int numBytesBuffer, unsigned int& numBytesWritten)
{
numBytesNeeded = 0;
numBytesWritten = 0;
Lerc2 lerc2;
if (version >= 0 && !lerc2.SetEncoderToOldVersion(version))
return ErrCode::WrongParam;
Byte* pDst = pBuffer;
const size_t nPix = (size_t)nCols * nRows;
const size_t nElem = nPix * nDepth;
const Byte* pPrevByteMask = nullptr;
vector<T> dataBuffer;
vector<Byte> maskBuffer, prevMaskBuffer;
BitMask bitMask;
for (int iBand = 0; iBand < nBands; iBand++)
{
bool bEncMsk = (iBand == 0);
const T* arr = pData + nElem * iBand;
const Byte* pByteMask = (nMasks > 0) ? (pValidBytes + ((nMasks > 1) ? nPix * iBand : 0)) : nullptr;
ErrCode errCode = CheckForNaN(arr, nDepth, nCols, nRows, pByteMask);
if (errCode != ErrCode::Ok && errCode != ErrCode::NaN)
return errCode;
if (errCode == ErrCode::NaN) {
if (!Resize(dataBuffer, nElem) || !Resize(maskBuffer, nPix))
return ErrCode::Failed;
memcpy(&dataBuffer[0], arr, nElem * sizeof(T));
pByteMask ? memcpy(&maskBuffer[0], pByteMask, nPix) : memset(&maskBuffer[0], 1, nPix);
if (!ReplaceNaNValues(dataBuffer, maskBuffer, nDepth, nCols, nRows))
return ErrCode::Failed;
if (iBand > 0 && MasksDiffer(&maskBuffer[0], pPrevByteMask, nPix))
bEncMsk = true;
if (iBand < nBands - 1)
{
prevMaskBuffer = maskBuffer;
pPrevByteMask = &prevMaskBuffer[0];
}
arr = &dataBuffer[0];
pByteMask = &maskBuffer[0];
}
else {
if (iBand > 0 && MasksDiffer(pByteMask, pPrevByteMask, nPix))
bEncMsk = true;
pPrevByteMask = pByteMask;
}
if (bEncMsk)
{
if (pByteMask && !Convert(pByteMask, nCols, nRows, bitMask))
return ErrCode::Failed;
if (!lerc2.Set(nDepth, nCols, nRows, pByteMask ? bitMask.Bits() : nullptr))
return ErrCode::Failed;
}
unsigned int nBytes = lerc2.ComputeNumBytesNeededToWrite(arr, maxZErr, bEncMsk);
if (nBytes <= 0)
return ErrCode::Failed;
numBytesNeeded += nBytes;
if (pBuffer)
{
if ((size_t)(pDst - pBuffer) + nBytes > numBytesBuffer) return ErrCode::BufferTooSmall;
if (!lerc2.Encode(arr, &pDst))
return ErrCode::Failed;
}
}
numBytesWritten = (unsigned int)(pDst - pBuffer);
return ErrCode::Ok;
}
template<class T>
ErrCode Lerc::EncodeInternal(const T* pData, int version, int nDepth, int nCols, int nRows, int nBands,
int nMasks, const Byte* pValidBytes, double maxZErr, unsigned int& numBytesNeeded,
Byte* pBuffer, unsigned int numBytesBuffer, unsigned int& numBytesWritten,
const unsigned char* pUsesNoData, const double* noDataValues)
{
numBytesNeeded = 0;
numBytesWritten = 0;
if (version >= 0 && version <= 5)
return ErrCode::WrongParam;
Lerc2 lerc2;
#ifdef ENCODE_VERIFY
Lerc2 lerc2Verify;
#endif
if (version >= 0 && !lerc2.SetEncoderToOldVersion(version))
return ErrCode::WrongParam;
if (pUsesNoData && !noDataValues)
for (int i = 0; i < nBands; i++)
if (pUsesNoData[i])
return ErrCode::WrongParam;
Byte* pDst = pBuffer;
const size_t nPix = (size_t)nCols * nRows;
const size_t nElem = nPix * nDepth;
const Byte* pPrevByteMask = nullptr;
vector<T> dataBuffer;
vector<Byte> maskBuffer, prevMaskBuffer;
BitMask bitMask;
if (!Resize(dataBuffer, nElem) || !Resize(maskBuffer, nPix))
return ErrCode::Failed;
bool bIsFltOrDbl = (typeid(T) == typeid(float) || typeid(T) == typeid(double));
bool bAnyMaskModified = false;
ErrCode errCode = ErrCode::Ok;
for (int iBand = 0; iBand < nBands; iBand++)
{
bool bEncMsk = (iBand == 0);
const T* arrOrig = pData + nElem * iBand;
const Byte* pByteMaskOrig = (nMasks > 0) ? (pValidBytes + ((nMasks > 1) ? nPix * iBand : 0)) : nullptr;
memcpy(&dataBuffer[0], arrOrig, nElem * sizeof(T));
pByteMaskOrig ? memcpy(&maskBuffer[0], pByteMaskOrig, nPix) : memset(&maskBuffer[0], 1, nPix);
double maxZErrL = maxZErr;
bool bPassNoDataValue = (pUsesNoData && (pUsesNoData[iBand] > 0));
const double noDataOrig = bPassNoDataValue ? noDataValues[iBand] : 0;
double noDataL = noDataOrig;
bool bIsFltDblAllInt = false; bool bModifiedMask = false; bool bNeedNoData = false; errCode = ErrCode::Ok;
if (bIsFltOrDbl) {
errCode = FilterNoDataAndNaN(dataBuffer, maskBuffer, nDepth, nCols, nRows, maxZErrL, bPassNoDataValue, noDataL,
bModifiedMask, bNeedNoData, bIsFltDblAllInt);
}
else if (bPassNoDataValue) {
errCode = FilterNoData(dataBuffer, maskBuffer, nDepth, nCols, nRows, maxZErrL, bPassNoDataValue, noDataL, bModifiedMask, bNeedNoData);
}
if (errCode != ErrCode::Ok)
return errCode;
if (bModifiedMask)
bAnyMaskModified = true;
bool bCompareMasks = (nMasks > 1) || bAnyMaskModified;
if (bCompareMasks && (iBand > 0) && MasksDiffer(&maskBuffer[0], pPrevByteMask, nPix))
bEncMsk = true;
if (nBands > 1 && iBand < nBands - 1)
{
prevMaskBuffer = maskBuffer;
pPrevByteMask = &prevMaskBuffer[0];
}
const T* arrL = &dataBuffer[0];
const Byte* pByteMaskL = &maskBuffer[0];
if (bEncMsk)
{
bool bAllValid = !memchr(pByteMaskL, 0, nPix);
if (!bAllValid && !Convert(pByteMaskL, nCols, nRows, bitMask))
return ErrCode::Failed;
if (!lerc2.Set(nDepth, nCols, nRows, !bAllValid ? bitMask.Bits() : nullptr))
return ErrCode::Failed;
}
if (!lerc2.SetNoDataValues(bNeedNoData, noDataL, noDataOrig))
return ErrCode::Failed;
if (!lerc2.SetNumBlobsMoreToCome(nBands - 1 - iBand))
return ErrCode::Failed;
if (!lerc2.SetIsAllInt(bIsFltDblAllInt))
return ErrCode::Failed;
unsigned int nBytes = lerc2.ComputeNumBytesNeededToWrite(arrL, maxZErrL, bEncMsk);
if (nBytes <= 0)
return ErrCode::Failed;
numBytesNeeded += nBytes;
if (pBuffer)
{
if ((size_t)(pDst - pBuffer) + nBytes > numBytesBuffer) return ErrCode::BufferTooSmall;
#ifdef ENCODE_VERIFY
const Byte* pDst0 = pDst;
#endif
if (!lerc2.Encode(arrL, &pDst))
return ErrCode::Failed;
#ifdef ENCODE_VERIFY
size_t blobSize = pDst - pDst0;
if (!DecodeAndCompareToInput(pDst0, blobSize, maxZErrL, lerc2Verify, arrL, pByteMaskL,
arrOrig, pByteMaskOrig, bPassNoDataValue, noDataOrig, bModifiedMask))
{
return ErrCode::Failed;
}
#endif
}
}
numBytesWritten = (unsigned int)(pDst - pBuffer);
return ErrCode::Ok;
}
#ifdef HAVE_LERC1_DECODE
template<class T>
bool Lerc::Convert(const CntZImage& zImg, T* arr, Byte* pByteMask, bool bMustFillMask)
{
if (!arr || !zImg.getSize())
return false;
const bool fltPnt = (typeid(*arr) == typeid(double)) || (typeid(*arr) == typeid(float));
int h = zImg.getHeight();
int w = zImg.getWidth();
const CntZ* srcPtr = zImg.getData();
T* dstPtr = arr;
int num = w * h;
if (pByteMask)
{
memset(pByteMask, 0, num);
for (int k = 0; k < num; k++)
{
if (srcPtr->cnt > 0)
{
*dstPtr = fltPnt ? (T)srcPtr->z : (T)floor(srcPtr->z + 0.5);
pByteMask[k] = 1;
}
srcPtr++;
dstPtr++;
}
}
else
{
for (int k = 0; k < num; k++)
{
if (srcPtr->cnt > 0)
{
*dstPtr = fltPnt ? (T)srcPtr->z : (T)floor(srcPtr->z + 0.5);
}
else if (bMustFillMask)
return false;
srcPtr++;
dstPtr++;
}
}
return true;
}
#endif
template<class T>
ErrCode Lerc::ConvertToDoubleTempl(const T* pDataIn, size_t nDataValues, double* pDataOut)
{
if (!pDataIn || !nDataValues || !pDataOut)
return ErrCode::WrongParam;
for (size_t k = 0; k < nDataValues; k++)
pDataOut[k] = pDataIn[k];
return ErrCode::Ok;
}
template<class T> ErrCode Lerc::CheckForNaN(const T* arr, int nDepth, int nCols, int nRows, const Byte* pByteMask)
{
if (!arr || nDepth <= 0 || nCols <= 0 || nRows <= 0)
return ErrCode::WrongParam;
if (typeid(T) != typeid(double) && typeid(T) != typeid(float))
return ErrCode::Ok;
for (size_t k = 0, i = 0; i < (size_t)nRows; i++)
{
bool bFoundNaN = false;
const T* rowArr = &(arr[i * nCols * nDepth]);
if (!pByteMask) {
size_t num = (size_t)nCols * nDepth;
for (size_t m = 0; m < num; m++)
if (std::isnan((double)rowArr[m]))
bFoundNaN = true;
}
else {
for (size_t n = 0, j = 0; j < (size_t)nCols; j++, k++, n += nDepth)
if (pByteMask[k])
{
for (int m = 0; m < nDepth; m++)
if (std::isnan((double)rowArr[n + m]))
bFoundNaN = true;
}
}
if (bFoundNaN)
return ErrCode::NaN;
}
return ErrCode::Ok;
}
template<class T> bool Lerc::ReplaceNaNValues(std::vector<T>& dataBuffer, std::vector<Byte>& maskBuffer, int nDepth, int nCols, int nRows)
{
if (nDepth <= 0 || nCols <= 0 || nRows <= 0 || dataBuffer.size() != (size_t)nDepth * nCols * nRows || maskBuffer.size() != (size_t)nCols * nRows)
return false;
T noDataValue = 0;
{
#if defined _WIN32
#pragma warning(disable: 4756)
#endif
noDataValue = (T)((typeid(T) == typeid(float)) ? -FLT_MAX : -DBL_MAX);
}
for (size_t k = 0, i = 0; i < (size_t)nRows; i++)
{
T* rowArr = &(dataBuffer[i * nCols * nDepth]);
for (size_t n = 0, j = 0; j < (size_t)nCols; j++, k++, n += nDepth)
{
if (maskBuffer[k])
{
int cntNaN = 0;
for (int m = 0; m < nDepth; m++)
if (std::isnan((double)rowArr[n + m]))
{
cntNaN++;
rowArr[n + m] = noDataValue;
}
if (cntNaN == nDepth)
maskBuffer[k] = 0;
}
}
}
return true;
}
template<class T> bool Lerc::Resize(std::vector<T>& buffer, size_t nElem)
{
try
{
buffer.resize(nElem);
}
catch (...)
{
return false;
}
return true;
}
bool Lerc::Convert(const Byte* pByteMask, int nCols, int nRows, BitMask& bitMask)
{
if (!pByteMask || nCols <= 0 || nRows <= 0)
return false;
if (!bitMask.SetSize(nCols, nRows))
return false;
bitMask.SetAllValid();
for (int k = 0, i = 0; i < nRows; i++)
for (int j = 0; j < nCols; j++, k++)
if (!pByteMask[k])
bitMask.SetInvalid(k);
return true;
}
bool Lerc::Convert(const BitMask& bitMask, Byte* pByteMask)
{
int nCols = bitMask.GetWidth();
int nRows = bitMask.GetHeight();
if (nCols <= 0 || nRows <= 0 || !pByteMask)
return false;
memset(pByteMask, 0, (size_t)nCols * nRows);
for (int k = 0, i = 0; i < nRows; i++)
for (int j = 0; j < nCols; j++, k++)
if (bitMask.IsValid(k))
pByteMask[k] = 1;
return true;
}
bool Lerc::MasksDiffer(const Byte* p0, const Byte* p1, size_t n)
{
if (p0 == p1)
return false;
if (!p0) return memchr(p1, 0, n); else if (!p1)
return memchr(p0, 0, n);
else
return memcmp(p0, p1, n);
}
ErrCode Lerc::GetRanges(const Byte* pLercBlob, unsigned int numBytesBlob, int iBand,
const struct Lerc2::HeaderInfo& lerc2Info, double* pMins, double* pMaxs, size_t nElem)
{
const int nDepth = lerc2Info.nDepth;
if (nDepth <= 0 || iBand < 0 || !pMins || !pMaxs)
return ErrCode::WrongParam;
if (nElem < ((size_t)iBand + 1) * (size_t)nDepth)
return ErrCode::BufferTooSmall;
if (nDepth == 1)
{
pMins[iBand] = lerc2Info.zMin;
pMaxs[iBand] = lerc2Info.zMax;
}
else
{
if (lerc2Info.bPassNoDataValues) return ErrCode::HasNoData;
Lerc2 lerc2;
if (!lerc2.GetRanges(pLercBlob, numBytesBlob, &pMins[iBand * nDepth], &pMaxs[iBand * nDepth]))
return ErrCode::Failed;
}
return ErrCode::Ok;
}
template<class T>
bool Lerc::RemapNoData(T* data, const BitMask& bitMask, const struct Lerc2::HeaderInfo& lerc2Info)
{
int nCols = lerc2Info.nCols;
int nRows = lerc2Info.nRows;
int nDepth = lerc2Info.nDepth;
if (!data || nCols <= 0 || nRows <= 0 || nDepth <= 0)
return false;
const T noDataOld = (T)lerc2Info.noDataVal;
const T noDataNew = (T)lerc2Info.noDataValOrig;
if (noDataNew != noDataOld)
{
bool bUseMask = (bitMask.GetWidth() == nCols) && (bitMask.GetHeight() == nRows);
for (long k = 0, i = 0; i < nRows; i++)
{
T* rowArr = &(data[i * nCols * nDepth]);
for (long n = 0, j = 0; j < nCols; j++, k++, n += nDepth)
if (!bUseMask || bitMask.IsValid(k))
for (long m = 0; m < nDepth; m++)
if (rowArr[n + m] == noDataOld)
rowArr[n + m] = noDataNew;
}
}
return true;
}
template<class T>
bool Lerc::DecodeAndCompareToInput(const Byte* pLercBlob, size_t blobSize, double maxZErr, Lerc2& lerc2Verify,
const T* pData, const Byte* pByteMask, const T* pDataOrig, const Byte* pByteMaskOrig,
bool bInputHasNoData, double origNoDataA, bool bModifiedMask)
{
if (!pLercBlob || !pData || !pDataOrig)
return false;
const Byte* bytePtr = pLercBlob;
size_t nBytesRemaining = blobSize;
bool bHasMask(false);
Lerc2::HeaderInfo hd;
if (!Lerc2::GetHeaderInfo(bytePtr, nBytesRemaining, hd, bHasMask))
return false;
std::vector<T> arrDec;
try
{
arrDec.assign(hd.nRows * hd.nCols * hd.nDepth, 0);
}
catch (...)
{
return false;
}
BitMask bitMaskDec;
if (!bitMaskDec.SetSize(hd.nCols, hd.nRows))
return false;
bitMaskDec.SetAllInvalid();
if (!lerc2Verify.Decode(&bytePtr, nBytesRemaining, &arrDec[0], bitMaskDec.Bits()))
return false;
{
bool bHasMaskBug(false);
double maxDelta = 0;
for (int k = 0, i = 0; i < hd.nRows; i++)
for (int j = 0; j < hd.nCols; j++, k++)
if (bitMaskDec.IsValid(k))
{
if (pByteMask && !pByteMask[k])
bHasMaskBug = true;
for (int n = k * hd.nDepth, m = 0; m < hd.nDepth; m++, n++)
{
double d = fabs((double)arrDec[n] - (double)pData[n]);
if (d > maxDelta)
maxDelta = d;
}
}
else if (!pByteMask || pByteMask[k])
bHasMaskBug = true;
if (bHasMaskBug || maxDelta > maxZErr * 1.1) return false;
}
if (!bInputHasNoData && !bModifiedMask) return true;
const bool bIsFltOrDbl = (typeid(T) == typeid(double) || typeid(T) == typeid(float));
const bool bHaveNoDataVal = (hd.version >= 6 && hd.bPassNoDataValues && hd.nDepth > 1);
if (bHaveNoDataVal && hd.noDataValOrig != origNoDataA)
return false;
if (bHaveNoDataVal && hd.noDataVal != hd.noDataValOrig)
{
if (!RemapNoData(&arrDec[0], bitMaskDec, hd))
return false;
}
{
T noDataOrig = (T)origNoDataA;
double maxDelta = 0;
bool bHasBug(false);
for (int k = 0, i = 0; i < hd.nRows; i++)
for (int j = 0; j < hd.nCols; j++, k++)
if (!pByteMaskOrig || pByteMaskOrig[k])
{
if (!bitMaskDec.IsValid(k)) {
for (int n = k * hd.nDepth, m = 0; m < hd.nDepth; m++, n++)
{
T zOrig = pDataOrig[n];
bool bIsNoData = (bInputHasNoData && zOrig == noDataOrig) || (bIsFltOrDbl && std::isnan((double)zOrig));
if (!bIsNoData)
bHasBug = true;
}
}
else {
for (int n = k * hd.nDepth, m = 0; m < hd.nDepth; m++, n++)
{
T zOrig = pDataOrig[n];
T z = arrDec[n];
if (z == zOrig) continue;
if (bIsFltOrDbl && std::isnan((double)zOrig))
zOrig = noDataOrig;
if (bInputHasNoData && (z == noDataOrig || zOrig == noDataOrig) && (z != zOrig))
bHasBug = true;
if (!bHaveNoDataVal || z != noDataOrig) {
double d = fabs((double)z - (double)zOrig);
if (d > maxDelta)
maxDelta = d;
}
}
}
}
else if (bitMaskDec.IsValid(k))
bHasBug = true;
if (bHasBug || maxDelta > maxZErr * 1.1) return false;
}
return true;
}
template<class T>
bool Lerc::GetTypeRange(const T, std::pair<double, double>& range)
{
range.first = 0;
if (typeid(T) == typeid(Byte))
range.second = UCHAR_MAX;
else if (typeid(T) == typeid(unsigned short))
range.second = USHRT_MAX;
else if (typeid(T) == typeid(unsigned int) || typeid(T) == typeid(unsigned long))
range.second = UINT_MAX;
else if (typeid(T) == typeid(signed char))
range = std::pair<double, double>(CHAR_MIN, CHAR_MAX);
else if (typeid(T) == typeid(short))
range = std::pair<double, double>(SHRT_MIN, SHRT_MAX);
else if (typeid(T) == typeid(int) || typeid(T) == typeid(long))
range = std::pair<double, double>(INT_MIN, INT_MAX);
else
return false;
return true;
}
template<class T>
ErrCode Lerc::FilterNoData(std::vector<T>& dataBuffer, std::vector<Byte>& maskBuffer, int nDepth, int nCols, int nRows,
double& maxZError, bool bPassNoDataValue, double& noDataValue, bool& bModifiedMask, bool& bNeedNoData)
{
if (nDepth <= 0 || nCols <= 0 || nRows <= 0 || maxZError < 0)
return ErrCode::WrongParam;
if ((dataBuffer.size() != (size_t)nDepth * nCols * nRows) || (maskBuffer.size() != (size_t)nCols * nRows))
return ErrCode::Failed;
bModifiedMask = false;
bNeedNoData = false;
if (!bPassNoDataValue) return ErrCode::Ok;
std::pair<double, double> typeRange;
if (!GetTypeRange(dataBuffer[0], typeRange))
return ErrCode::Failed;
if (noDataValue < typeRange.first || noDataValue > typeRange.second)
return ErrCode::WrongParam;
T origNoData = (T)noDataValue;
double minVal = DBL_MAX;
double maxVal = -DBL_MAX;
for (int k = 0, i = 0; i < nRows; i++)
{
T* rowArr = &(dataBuffer[i * nCols * nDepth]);
for (int n = 0, j = 0; j < nCols; j++, k++, n += nDepth)
if (maskBuffer[k])
{
int cntInvalid = 0;
for (int m = 0; m < nDepth; m++)
{
T z = rowArr[n + m];
if (z == origNoData)
cntInvalid++;
else if (z < minVal)
minVal = z;
else if (z > maxVal)
maxVal = z;
}
if (cntInvalid == nDepth)
{
maskBuffer[k] = 0;
bModifiedMask = true;
}
else if (cntInvalid > 0) bNeedNoData = true;
}
}
double maxZErrL = (std::max)(0.5, floor(maxZError)); double dist = floor(maxZErrL);
if ((origNoData >= minVal - dist) && (origNoData <= maxVal + dist))
{
maxZError = 0.5; return ErrCode::Ok;
}
if (bNeedNoData)
{
double minDist = floor(maxZErrL) + 1;
double remapVal = minVal - minDist;
T newNoData = origNoData;
if (remapVal >= typeRange.first)
{
newNoData = (T)remapVal;
}
else
{
maxZErrL = 0.5; remapVal = minVal - 1;
if (remapVal >= typeRange.first)
{
newNoData = (T)remapVal;
}
else
{
remapVal = maxVal + 1;
if ((remapVal <= typeRange.second) && (remapVal < origNoData))
newNoData = (T)remapVal;
}
}
if (newNoData != origNoData)
{
for (int k = 0, i = 0; i < nRows; i++)
{
T* rowArr = &(dataBuffer[i * nCols * nDepth]);
for (int n = 0, j = 0; j < nCols; j++, k++, n += nDepth)
if (maskBuffer[k])
for (int m = 0; m < nDepth; m++)
if (rowArr[n + m] == origNoData)
rowArr[n + m] = newNoData;
}
noDataValue = newNoData;
}
}
if (maxZError != maxZErrL)
maxZError = maxZErrL;
return ErrCode::Ok;
}
template<class T>
ErrCode Lerc::FilterNoDataAndNaN(std::vector<T>& dataBuffer, std::vector<Byte>& maskBuffer, int nDepth, int nCols, int nRows,
double& maxZError, bool bPassNoDataValue, double& noDataValue, bool& bModifiedMask, bool& bNeedNoData, bool& bIsFltDblAllInt)
{
if (nDepth <= 0 || nCols <= 0 || nRows <= 0 || maxZError < 0)
return ErrCode::WrongParam;
if ((dataBuffer.size() != (size_t)nDepth * nCols * nRows) || (maskBuffer.size() != (size_t)nCols * nRows))
return ErrCode::Failed;
if (typeid(T) != typeid(double) && typeid(T) != typeid(float)) return ErrCode::Failed;
bModifiedMask = false;
bNeedNoData = false;
bIsFltDblAllInt = false;
bool bHasNoDataValuesLeft = false;
bool bIsFloat4 = (typeid(T) == typeid(float));
bool bAllInt = true;
bool bHasNaN = false;
T origNoData(0);
if (bPassNoDataValue)
{
if (bIsFloat4 && (noDataValue < -FLT_MAX || noDataValue > FLT_MAX))
return ErrCode::WrongParam;
origNoData = (T)noDataValue;
}
else
origNoData = (T)(bIsFloat4 ? -FLT_MAX : -DBL_MAX);
const double lowIntLimit = (double)(bIsFloat4 ? -((long)1 << 23) : -((int64_t)1 << 53));
const double highIntLimit = (double)(bIsFloat4 ? ((long)1 << 23) : ((int64_t)1 << 53));
double minVal = DBL_MAX;
double maxVal = -DBL_MAX;
long cntValidPixels = 0;
for (int k = 0, i = 0; i < nRows; i++)
{
T* rowArr = &(dataBuffer[i * nCols * nDepth]);
for (int n = 0, j = 0; j < nCols; j++, k++, n += nDepth)
if (maskBuffer[k])
{
cntValidPixels++;
int cntInvalidValues = 0;
for (int m = 0; m < nDepth; m++)
{
T& zVal = rowArr[n + m];
if (std::isnan((double)zVal))
{
bHasNaN = true;
cntInvalidValues++;
if (bPassNoDataValue && nDepth > 1)
zVal = origNoData; }
else if (bPassNoDataValue && zVal == origNoData)
{
cntInvalidValues++;
}
else
{
if (zVal < minVal)
minVal = zVal;
else if (zVal > maxVal)
maxVal = zVal;
if (bAllInt && !IsInt(zVal))
bAllInt = false;
}
}
if (cntInvalidValues == nDepth)
{
maskBuffer[k] = 0;
bModifiedMask = true;
}
else if (cntInvalidValues > 0) bHasNoDataValuesLeft = true;
}
}
bNeedNoData = bHasNoDataValuesLeft;
if (cntValidPixels == 0)
bAllInt = false;
if (bHasNaN && nDepth > 1 && bHasNoDataValuesLeft && !bPassNoDataValue)
{
return ErrCode::NaN; }
double maxZErrL = maxZError;
if (bAllInt)
{
bAllInt &= (minVal >= lowIntLimit) && (minVal <= highIntLimit)
&& (maxVal >= lowIntLimit) && (maxVal <= highIntLimit);
if (bHasNoDataValuesLeft)
bAllInt &= IsInt(origNoData) && (origNoData >= lowIntLimit) && (origNoData <= highIntLimit);
if (bAllInt)
maxZErrL = (std::max)(0.5, floor(maxZError)); }
bIsFltDblAllInt = bAllInt;
if (maxZErrL == 0) return ErrCode::Ok;
if (bPassNoDataValue)
{
double dist = bAllInt ? floor(maxZErrL) : 2 * maxZErrL;
if ((origNoData >= minVal - dist) && (origNoData <= maxVal + dist))
{
maxZError = bAllInt ? 0.5 : 0; return ErrCode::Ok;
}
}
if (bHasNoDataValuesLeft) {
T remapVal = origNoData;
bool bRemapNoData = FindNewNoDataBelowValidMin(minVal, maxZErrL, bAllInt, lowIntLimit, remapVal);
if (bRemapNoData)
{
if (remapVal != origNoData)
{
for (int k = 0, i = 0; i < nRows; i++)
{
T* rowArr = &(dataBuffer[i * nCols * nDepth]);
for (int n = 0, j = 0; j < nCols; j++, k++, n += nDepth)
if (maskBuffer[k])
for (int m = 0; m < nDepth; m++)
if (rowArr[n + m] == origNoData)
rowArr[n + m] = remapVal;
}
noDataValue = remapVal;
}
}
else if ((double)origNoData >= minVal) {
maxZErrL = bAllInt ? 0.5 : 0; }
}
if (maxZError != maxZErrL)
maxZError = maxZErrL;
return ErrCode::Ok;
}
template<class T>
bool Lerc::FindNewNoDataBelowValidMin(double minVal, double maxZErr, bool bAllInt, double lowIntLimit, T& newNoDataVal)
{
if (bAllInt)
{
std::vector<T> noDataCandVec;
{ std::vector<double> distCandVec = { 4 * maxZErr, 1, 10, 100, 1000, 10000 };
for (double dist : distCandVec)
noDataCandVec.push_back((T)(minVal - dist));
double candForLargeMinVal = (minVal > 0 ? floor(minVal / 2) : minVal * 2); noDataCandVec.push_back((T)candForLargeMinVal);
}
std::sort(noDataCandVec.begin(), noDataCandVec.end(), std::greater<double>());
for (T noDataVal : noDataCandVec)
{
if ((noDataVal > (T)lowIntLimit) && (noDataVal < (T)(minVal - 2 * maxZErr)) && IsInt(noDataVal))
{
newNoDataVal = noDataVal;
return true;
}
}
}
else
{
std::vector<T> noDataCandVec;
{ std::vector<double> distCandVec = { 4 * maxZErr, 0.0001, 0.001, 0.01, 0.1, 1, 10, 100, 1000, 10000 };
for (double dist : distCandVec)
noDataCandVec.push_back((T)(minVal - dist));
double candForLargeMinVal = (minVal > 0 ? minVal / 2 : minVal * 2);
noDataCandVec.push_back((T)candForLargeMinVal);
}
std::sort(noDataCandVec.begin(), noDataCandVec.end(), std::greater<double>());
bool bIsFloat4 = (typeid(T) == typeid(float));
T lowestVal = (T)(bIsFloat4 ? -FLT_MAX : -DBL_MAX);
for (T noDataVal : noDataCandVec)
{
if ((noDataVal > lowestVal) && (noDataVal < (T)(minVal - 2 * maxZErr)))
{
newNoDataVal = noDataVal;
return true;
}
}
}
return false;
}