static CPLErr DecompressLERC(buf_mgr &dst, buf_mgr &src, const ILImage &img) { CntZImage zImg; Byte *ptr = (Byte *)src.buffer; if (!zImg.read(&ptr, 1e12)) { CPLError(CE_Failure,CPLE_AppDefined,"MRF: Error during LERC decompression"); return CE_Failure; } // Unpack from zImg to dst buffer, calling the right type #define UFILL(T) CntZImgUFill(zImg, (T *)(dst.buffer), img) switch (img.dt) { case GDT_Byte: UFILL(GByte); break; case GDT_UInt16: UFILL(GUInt16); break; case GDT_Int16: UFILL(GInt16); break; case GDT_Int32: UFILL(GInt32); break; case GDT_UInt32: UFILL(GUInt32); break; case GDT_Float32: UFILL(float); break; case GDT_Float64: UFILL(double); break; default: break; } #undef UFILL return CE_None; }
bool Lerc::Convert(const CntZImage& zImg, T* arr, BitMask* pBitMask) const { 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(); if (pBitMask) { pBitMask->SetSize(w, h); pBitMask->SetAllValid(); } const CntZ* srcPtr = zImg.getData(); T* dstPtr = arr; int num = w * h; for (int k = 0; k < num; k++) { if (srcPtr->cnt > 0) *dstPtr = fltPnt ? (T)srcPtr->z : (T)floor(srcPtr->z + 0.5); else if (pBitMask) pBitMask->SetInvalid(k); srcPtr++; dstPtr++; } return true; }
// LERC 1 compression static CPLErr CompressLERC(buf_mgr &dst, buf_mgr &src, const ILImage &img, double precision) { CntZImage zImg; // Fill data into zImg #define FILL(T) CntZImgFill(zImg, (T *)(src.buffer), img) switch (img.dt) { case GDT_Byte: FILL(GByte); break; case GDT_UInt16: FILL(GUInt16); break; case GDT_Int16: FILL(GInt16); break; case GDT_Int32: FILL(GInt32); break; case GDT_UInt32: FILL(GUInt32); break; case GDT_Float32: FILL(float); break; case GDT_Float64: FILL(double); break; default: break; } #undef FILL Byte *ptr = (Byte *)dst.buffer; // if it can't compress in output buffer it will crash if (!zImg.write(&ptr, precision)) { CPLError(CE_Failure,CPLE_AppDefined,"MRF: Error during LERC compression"); return CE_Failure; } // write changes the value of the pointer, we can find the size by testing how far it moved dst.size = ptr - (Byte *)dst.buffer; CPLDebug("MRF_LERC","LERC Compressed to %d\n", (int)dst.size); return CE_None; }
bool Lerc::DecodeTempl(T* pData, // outgoing data bands const Byte* pLercBlob, size_t numBytesBlob, int nCols, int nRows, int nBands, BitMask* pBitMask) const // gets filled if not 0, even if all valid { if (!pLercBlob || !pData) return false; const Byte* pByte = pLercBlob; Byte* pByte1 = const_cast<Byte*>(pLercBlob); Lerc2 lerc2; CntZImage zImg; for (int iBand = 0; iBand < nBands; iBand++) { // first try Lerc2 unsigned int numBytesHeader = lerc2.ComputeNumBytesHeader(); Lerc2::HeaderInfo hdInfo; if (((size_t)(pByte - pLercBlob) + numBytesHeader <= numBytesBlob) && lerc2.GetHeaderInfo(pByte, hdInfo)) { if (hdInfo.nCols != nCols || hdInfo.nRows != nRows) return false; if ((pByte - pLercBlob) + (size_t)hdInfo.blobSize > numBytesBlob) return false; // init bitMask if (pBitMask && iBand == 0) pBitMask->SetSize(nCols, nRows); T* arr = pData + nCols * nRows * iBand; if (!lerc2.Decode(&pByte, arr, (pBitMask && iBand == 0) ? pBitMask->Bits() : 0)) return false; } else // then try Lerc1 { unsigned int numBytesHeader = CntZImage::computeNumBytesNeededToReadHeader(); if ((size_t)(pByte - pLercBlob) + numBytesHeader > numBytesBlob) return false; bool onlyZPart = iBand > 0; if (!zImg.read(&pByte1, 1e12, false, onlyZPart)) return false; if (zImg.getWidth() != nCols || zImg.getHeight() != nRows) return false; T* arr = pData + nCols * nRows * iBand; if (!Convert(zImg, arr, pBitMask)) return false; } } // iBand return true; }
// Unload a zImg into a buffer template <typename T> void CntZImgUFill(CntZImage &zImg, T *dst, const ILImage &img) { int h = static_cast<int>(zImg.getHeight()); int w = static_cast<int>(zImg.getWidth()); T *ptr = dst; T ndv = (T)(img.NoDataValue); // Use 0 if nodata is not defined if (!img.hasNoData) ndv = 0; for (int i = 0; i < h; i++) for (int j = 0; j < w; j++) *ptr++ = (zImg(i, j).cnt == 0) ? ndv : (T)(zImg(i, j).z); }
unsigned int CntZImage::computeNumBytesNeededToWriteVoidImage() { unsigned int cnt = 0; CntZImage zImg; cnt += (unsigned int)zImg.getTypeString().length(); // "CntZImage ", 10 bytes cnt += 2 * sizeof(int); cnt += 2 * sizeof(int); cnt += 1 * sizeof(double); // cnt part cnt += 3 * sizeof(int); cnt += sizeof(float); // z part cnt += 3 * sizeof(int); cnt += sizeof(float); cnt += 1; return cnt; }
// Load a buffer into a zImg template <typename T> void CntZImgFill(CntZImage &zImg, T *src, const ILImage &img) { int w = img.pagesize.x; int h = img.pagesize.y; zImg.resize(w, h); T *ptr = src; // No data value float ndv = float(img.NoDataValue); if (!img.hasNoData) ndv = 0; for (int i = 0; i < h; i++) for (int j = 0; j < w; j++) { zImg(i, j).z = float(*ptr++); zImg(i, j).cnt = !CPLIsEqual(zImg(i, j).z, ndv); } return; }
bool Lerc::GetLercInfo(const Byte* pLercBlob, size_t numBytesBlob, struct LercInfo& lercInfo) const { lercInfo.RawInit(); // first try Lerc2 Lerc2 lerc2; unsigned int numBytesHeader = lerc2.ComputeNumBytesHeader(); struct Lerc2::HeaderInfo lerc2Info; if (numBytesHeader <= numBytesBlob && lerc2.GetHeaderInfo(pLercBlob, lerc2Info)) { lercInfo.version = lerc2Info.version; lercInfo.nCols = lerc2Info.nCols; lercInfo.nRows = lerc2Info.nRows; lercInfo.numValidPixel = lerc2Info.numValidPixel; lercInfo.nBands = 1; lercInfo.blobSize = lerc2Info.blobSize; lercInfo.dt = (DataType)lerc2Info.dt; lercInfo.zMin = lerc2Info.zMin; lercInfo.zMax = lerc2Info.zMax; lercInfo.maxZError = lerc2Info.maxZError; if (lercInfo.blobSize > (int)numBytesBlob) // truncated blob, we won't be able to read this band return false; while (lercInfo.blobSize + numBytesHeader < numBytesBlob) // means there could be another band { struct Lerc2::HeaderInfo hdInfo; if (!lerc2.GetHeaderInfo(pLercBlob + lercInfo.blobSize, hdInfo)) return true; // no other band, we are done if (hdInfo.nCols != lercInfo.nCols || hdInfo.nRows != lercInfo.nRows || hdInfo.numValidPixel != lercInfo.numValidPixel || (int)hdInfo.dt != (int)lercInfo.dt || hdInfo.maxZError != lercInfo.maxZError) { return false; } lercInfo.blobSize += hdInfo.blobSize; if (lercInfo.blobSize > (int)numBytesBlob) // truncated blob, we won't be able to read this band return false; lercInfo.nBands++; lercInfo.zMin = min(lercInfo.zMin, hdInfo.zMin); lercInfo.zMax = max(lercInfo.zMax, hdInfo.zMax); } return true; } // then try Lerc1 numBytesHeader = CntZImage::computeNumBytesNeededToReadHeader(); Byte* pByte = const_cast<Byte*>(pLercBlob); CntZImage cntZImg; if (numBytesHeader <= numBytesBlob && cntZImg.read(&pByte, 1e12, true)) // read just the header { size_t nBytesRead = pByte - pLercBlob; size_t nBytesNeeded = 10 + 4 * sizeof(int) + 1 * sizeof(double); if (nBytesRead < nBytesNeeded) return false; Byte* ptr = const_cast<Byte*>(pLercBlob); ptr += 10 + 2 * sizeof(int); int height = *((const int*)ptr); ptr += sizeof(int); int width = *((const int*)ptr); ptr += sizeof(int); double maxZErrorInFile = *((const double*)ptr); if (height > 20000 || width > 20000) // guard against bogus numbers return false; lercInfo.nCols = width; lercInfo.nRows = height; lercInfo.dt = Lerc::DT_Float; lercInfo.maxZError = maxZErrorInFile; Byte* pByte = const_cast<Byte*>(pLercBlob); bool onlyZPart = false; while (lercInfo.blobSize + numBytesHeader < numBytesBlob) // means there could be another band { if (!cntZImg.read(&pByte, 1e12, false, onlyZPart)) return (lercInfo.nBands > 0); // no other band, we are done onlyZPart = true; lercInfo.nBands++; lercInfo.blobSize = (int)(pByte - pLercBlob); // now that we have decoded it, we can go the extra mile and collect some extra info 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 = min(lercInfo.zMin, zMin); lercInfo.zMax = max(lercInfo.zMax, zMax); } return true; } return false; }