jpayne@69: /* jpayne@69: Copyright 2016 - 2022 Esri jpayne@69: jpayne@69: Licensed under the Apache License, Version 2.0 (the "License"); jpayne@69: you may not use this file except in compliance with the License. jpayne@69: You may obtain a copy of the License at jpayne@69: jpayne@69: http://www.apache.org/licenses/LICENSE-2.0 jpayne@69: jpayne@69: Unless required by applicable law or agreed to in writing, software jpayne@69: distributed under the License is distributed on an "AS IS" BASIS, jpayne@69: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. jpayne@69: See the License for the specific language governing permissions and jpayne@69: limitations under the License. jpayne@69: jpayne@69: A local copy of the license and additional notices are located with the jpayne@69: source distribution at: jpayne@69: jpayne@69: http://github.com/Esri/lerc/ jpayne@69: jpayne@69: Contributors: Thomas Maurer jpayne@69: */ jpayne@69: jpayne@69: #ifndef LERC_API_INCLUDE_GUARD jpayne@69: #define LERC_API_INCLUDE_GUARD jpayne@69: jpayne@69: //#define USE_EMSCRIPTEN // to build a wasm Lerc decoder, install emscripten first jpayne@69: jpayne@69: #ifdef USE_EMSCRIPTEN jpayne@69: #include jpayne@69: #endif jpayne@69: jpayne@69: #ifdef __cplusplus jpayne@69: extern "C" { jpayne@69: #endif jpayne@69: jpayne@69: /* LERC version numbers and related macros added in 3.0.0 */ jpayne@69: jpayne@69: #define LERC_VERSION_MAJOR 4 jpayne@69: #define LERC_VERSION_MINOR 0 jpayne@69: #define LERC_VERSION_PATCH 0 jpayne@69: jpayne@69: /* Macro to compute a LERC version number from its components */ jpayne@69: #define LERC_COMPUTE_VERSION(maj,min,patch) ((maj)*10000+(min)*100+(patch)) jpayne@69: jpayne@69: /* Current LERC version from the above version numbers */ jpayne@69: #define LERC_VERSION_NUMBER \ jpayne@69: LERC_COMPUTE_VERSION(LERC_VERSION_MAJOR, LERC_VERSION_MINOR, LERC_VERSION_PATCH) jpayne@69: jpayne@69: /* Macro that returns true if the current LERC version is at least the version specified by (maj,min,patch) */ jpayne@69: #define LERC_AT_LEAST_VERSION(maj,min,patch) \ jpayne@69: (LERC_VERSION_NUMBER >= LERC_COMPUTE_VERSION(maj,min,patch)) jpayne@69: jpayne@69: #if defined _WIN32 || defined __CYGWIN__ jpayne@69: # if defined(LERC_STATIC) jpayne@69: # define LERCDLL_API jpayne@69: # elif defined(LERC_EXPORTS) jpayne@69: # define LERCDLL_API __declspec(dllexport) jpayne@69: # else jpayne@69: # define LERCDLL_API __declspec(dllimport) jpayne@69: # endif jpayne@69: #elif __GNUC__ >= 4 jpayne@69: #define LERCDLL_API __attribute__((visibility("default"))) jpayne@69: #else jpayne@69: #define LERCDLL_API jpayne@69: #endif jpayne@69: jpayne@69: //! C-API for LERC library jpayne@69: jpayne@69: jpayne@69: //! Added in version 4.0: jpayne@69: //! jpayne@69: //! - 1) better support 3D and 4D data, allow for lossy encoding even if a noData value is used jpayne@69: //! - 2) better lossless compression for float and double (pass maxZError = 0) jpayne@69: //! - 3) allow to pass integer > 32 bit as double (Lerc detects it is all integer and uses that) jpayne@69: //! - 4) renamed nDim to nDepth (without changing the function signatures) jpayne@69: //! jpayne@69: //! More on 1). In version 3.0, for 2D images, the 2D valid / invalid byte masks represent invalid pixels. jpayne@69: //! For more than 1 band, different masks per band can be used. No change to that. jpayne@69: //! For nDepth > 1, or an array of values per pixel, there is the special case of a mix of valid and invalid values jpayne@69: //! at the same pixel. The 2D mask cannot cover this case. jpayne@69: //! We have added 4 new functions to version 4.0 to cover this case, see below. If you don't encounter this jpayne@69: //! "mixed case", you can continue using the same API functions as in version 3.0. jpayne@69: //! If you should encounter a Lerc blob that has this mix, both the regular lerc_decode() and jpayne@69: //! lerc_getDataRanges() functions will fail with "ErrCode::HasNoData". jpayne@69: //! In that case, you need to call the new lerc_decode_4D() function. jpayne@69: //! jpayne@69: //! More on 2). Better lossless compression for float and double is enabled for all API functions. jpayne@69: //! For 1) and 3) you have to call the new "..._4D()" functions, see further below. jpayne@69: jpayne@69: jpayne@69: typedef unsigned int lerc_status; jpayne@69: jpayne@69: //! All output buffers must have been allocated by the caller. jpayne@69: jpayne@69: //! Compute the buffer size in bytes required to hold the compressed input tile. Optional. jpayne@69: //! You can call lerc_encode(...) directly as long as the output buffer is big enough. jpayne@69: jpayne@69: //! Order of raw input data is top left corner to lower right corner, row by row. This for each band. jpayne@69: //! Data type is { char = 0, uchar = 1, short = 2, ushort = 3, int = 4, uint = 5, float = 6, double = 7 }, see Lerc_types.h . jpayne@69: //! maxZErr is the max compression error per pixel allowed. jpayne@69: jpayne@69: //! The image or mask of valid pixels is optional. Nullptr means all pixels are valid. jpayne@69: //! If not all pixels are valid, set invalid pixel bytes to 0, valid pixel bytes to 1. jpayne@69: //! Size of the valid / invalid pixel image is (nCols * nRows * nMasks). jpayne@69: jpayne@69: LERCDLL_API jpayne@69: lerc_status lerc_computeCompressedSize( jpayne@69: const void* pData, // raw image data, row by row, band by band jpayne@69: unsigned int dataType, // char = 0, uchar = 1, short = 2, ushort = 3, int = 4, uint = 5, float = 6, double = 7 jpayne@69: int nDepth, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) jpayne@69: int nCols, // number of columns jpayne@69: int nRows, // number of rows jpayne@69: int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) jpayne@69: int nMasks, // 0 - all valid, 1 - same mask for all bands, nBands - masks can differ between bands jpayne@69: const unsigned char* pValidBytes, // nullptr if all pixels are valid; otherwise 1 byte per pixel (1 = valid, 0 = invalid) jpayne@69: double maxZErr, // max coding error per pixel, defines the precision jpayne@69: unsigned int* numBytes); // size of outgoing Lerc blob jpayne@69: jpayne@69: //! Encode the input data into a compressed Lerc blob. jpayne@69: jpayne@69: LERCDLL_API jpayne@69: lerc_status lerc_encode( jpayne@69: const void* pData, // raw image data, row by row, band by band jpayne@69: unsigned int dataType, // char = 0, uchar = 1, short = 2, ushort = 3, int = 4, uint = 5, float = 6, double = 7 jpayne@69: int nDepth, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) jpayne@69: int nCols, // number of columns jpayne@69: int nRows, // number of rows jpayne@69: int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) jpayne@69: int nMasks, // 0 - all valid, 1 - same mask for all bands, nBands - masks can differ between bands jpayne@69: const unsigned char* pValidBytes, // nullptr if all pixels are valid; otherwise 1 byte per pixel (1 = valid, 0 = invalid) jpayne@69: double maxZErr, // max coding error per pixel, defines the precision jpayne@69: unsigned char* pOutBuffer, // buffer to write to, function fails if buffer too small jpayne@69: unsigned int outBufferSize, // size of output buffer jpayne@69: unsigned int* nBytesWritten); // number of bytes written to output buffer jpayne@69: jpayne@69: jpayne@69: //! Use the 2 functions below to encode to an older codec version jpayne@69: jpayne@69: LERCDLL_API jpayne@69: lerc_status lerc_computeCompressedSizeForVersion( jpayne@69: const void* pData, // raw image data, row by row, band by band jpayne@69: int codecVersion, // [2 .. 6] for [v2.2 .. v2.6], or -1 for latest codec v2.6 jpayne@69: unsigned int dataType, // char = 0, uchar = 1, short = 2, ushort = 3, int = 4, uint = 5, float = 6, double = 7 jpayne@69: int nDepth, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) jpayne@69: int nCols, // number of columns jpayne@69: int nRows, // number of rows jpayne@69: int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) jpayne@69: int nMasks, // 0 - all valid, 1 - same mask for all bands, nBands - masks can differ between bands jpayne@69: const unsigned char* pValidBytes, // nullptr if all pixels are valid; otherwise 1 byte per pixel (1 = valid, 0 = invalid) jpayne@69: double maxZErr, // max coding error per pixel, defines the precision jpayne@69: unsigned int* numBytes); // size of outgoing Lerc blob jpayne@69: jpayne@69: LERCDLL_API jpayne@69: lerc_status lerc_encodeForVersion( jpayne@69: const void* pData, // raw image data, row by row, band by band jpayne@69: int codecVersion, // [2 .. 6] for [v2.2 .. v2.6], or -1 for latest codec v2.6 jpayne@69: unsigned int dataType, // char = 0, uchar = 1, short = 2, ushort = 3, int = 4, uint = 5, float = 6, double = 7 jpayne@69: int nDepth, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) jpayne@69: int nCols, // number of columns jpayne@69: int nRows, // number of rows jpayne@69: int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) jpayne@69: int nMasks, // 0 - all valid, 1 - same mask for all bands, nBands - masks can differ between bands jpayne@69: const unsigned char* pValidBytes, // nullptr if all pixels are valid; otherwise 1 byte per pixel (1 = valid, 0 = invalid) jpayne@69: double maxZErr, // max coding error per pixel, defines the precision jpayne@69: unsigned char* pOutBuffer, // buffer to write to, function fails if buffer too small jpayne@69: unsigned int outBufferSize, // size of output buffer jpayne@69: unsigned int* nBytesWritten); // number of bytes written to output buffer jpayne@69: jpayne@69: jpayne@69: //! Call this to get info about the compressed Lerc blob. Optional. jpayne@69: //! Info returned in infoArray is jpayne@69: //! { version, dataType, nDepth, nCols, nRows, nBands, nValidPixels, blobSize, nMasks, nDepth, nUsesNoDataValue }, see Lerc_types.h . jpayne@69: //! Info returned in dataRangeArray is { zMin, zMax, maxZErrorUsed }, see Lerc_types.h . jpayne@69: //! If nDepth > 1 or nBands > 1 the data range [zMin, zMax] is over all values. jpayne@69: jpayne@69: // Remark on function signature. The arrays to be filled may grow in future versions. In order not to break jpayne@69: // existing code, the function fills these arrays only up to their allocated size. jpayne@69: jpayne@69: // Remark on param blobSize. Usually it is known, either the file size of the blob written to disk, jpayne@69: // or the size of the blob transmitted. It should be passed accurately for 2 reasons: jpayne@69: // _ function finds out how many single band Lerc blobs are concatenated, if any jpayne@69: // _ function checks for truncated file or blob jpayne@69: // It is OK to pass blobSize too large as long as there is no other (valid) Lerc blob following next. jpayne@69: // If in doubt, check the code in Lerc::GetLercInfo(...) for the exact logic. jpayne@69: jpayne@69: LERCDLL_API jpayne@69: #ifdef USE_EMSCRIPTEN jpayne@69: EMSCRIPTEN_KEEPALIVE jpayne@69: #endif jpayne@69: lerc_status lerc_getBlobInfo( jpayne@69: const unsigned char* pLercBlob, // Lerc blob to decode jpayne@69: unsigned int blobSize, // blob size in bytes jpayne@69: unsigned int* infoArray, // info array with all info needed to allocate the outgoing arrays for calling decode jpayne@69: double* dataRangeArray, // quick access to overall data range [zMin, zMax] without having to decode the data jpayne@69: int infoArraySize, // number of elements of infoArray jpayne@69: int dataRangeArraySize); // number of elements of dataRangeArray jpayne@69: jpayne@69: //! Call this to quickly get the data ranges [min, max] per dimension and band without having to decode the pixels. Optional. jpayne@69: //! The 2 output data arrays must have been allocated to the same size (nDepth * nBands). jpayne@69: //! The output data array's layout is an image with nDepth columns and nBands rows. jpayne@69: jpayne@69: LERCDLL_API jpayne@69: #ifdef USE_EMSCRIPTEN jpayne@69: EMSCRIPTEN_KEEPALIVE jpayne@69: #endif jpayne@69: lerc_status lerc_getDataRanges( jpayne@69: const unsigned char* pLercBlob, // Lerc blob to decode jpayne@69: unsigned int blobSize, // blob size in bytes jpayne@69: int nDepth, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) jpayne@69: int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) jpayne@69: double* pMins, // outgoing minima per dimension and band jpayne@69: double* pMaxs); // outgoing maxima per dimension and band jpayne@69: jpayne@69: //! Decode the compressed Lerc blob into a raw data array. jpayne@69: //! The data array must have been allocated to size (nDepth * nCols * nRows * nBands * sizeof(dataType)). jpayne@69: //! The valid pixels array, if not all pixels valid, must have been allocated to size (nCols * nRows * nMasks). jpayne@69: jpayne@69: LERCDLL_API jpayne@69: #ifdef USE_EMSCRIPTEN jpayne@69: EMSCRIPTEN_KEEPALIVE jpayne@69: #endif jpayne@69: lerc_status lerc_decode( jpayne@69: const unsigned char* pLercBlob, // Lerc blob to decode jpayne@69: unsigned int blobSize, // blob size in bytes jpayne@69: int nMasks, // 0, 1, or nBands; return as many masks in the next array jpayne@69: unsigned char* pValidBytes, // gets filled if not nullptr, even if all valid jpayne@69: int nDepth, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) jpayne@69: int nCols, // number of columns jpayne@69: int nRows, // number of rows jpayne@69: int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) jpayne@69: unsigned int dataType, // char = 0, uchar = 1, short = 2, ushort = 3, int = 4, uint = 5, float = 6, double = 7 jpayne@69: void* pData); // outgoing data array jpayne@69: jpayne@69: //! Same as above, but decode into double array independent of compressed data type. jpayne@69: //! Wasteful in memory, but convenient if a caller from Python or C# does not want to deal with jpayne@69: //! data type conversion, templating, or casting. jpayne@69: //! Should this api be extended to new data types that don't fit into a double such as int64, jpayne@69: //! then this function will fail for such compressed data types. jpayne@69: jpayne@69: LERCDLL_API jpayne@69: lerc_status lerc_decodeToDouble( jpayne@69: const unsigned char* pLercBlob, // Lerc blob to decode jpayne@69: unsigned int blobSize, // blob size in bytes jpayne@69: int nMasks, // 0, 1, or nBands; return as many masks in the next array jpayne@69: unsigned char* pValidBytes, // gets filled if not nullptr, even if all valid jpayne@69: int nDepth, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) jpayne@69: int nCols, // number of columns jpayne@69: int nRows, // number of rows jpayne@69: int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) jpayne@69: double* pData); // outgoing data array jpayne@69: jpayne@69: jpayne@69: //! Added in version 4.0: jpayne@69: //! jpayne@69: //! The 4 functions below are new. The main purpose (and difference to the functions above) is to support, for 3D and 4D data, jpayne@69: //! the special case of a mix of valid and invalid values at the same pixel. jpayne@69: //! jpayne@69: //! Main idea: Lerc has the property that for each 8x8 pixel block the minimum value is always encoded lossless in the block header. jpayne@69: //! To enable lossy encoding in the presence of noData values, the original noData value is mapped below the range of the valid values, jpayne@69: //! if possible. If not possible, it switches to lossless. On decode, that temporary noData value gets mapped back to the original jpayne@69: //! noData value. jpayne@69: //! jpayne@69: //! To minimize the occurence of noData values (and for better compression), Lerc tries to move noData values to the byte mask jpayne@69: //! wherever possible (e.g., all values at some pixel are invalid). So for a given band the noData values may disappear and get jpayne@69: //! all moved to the byte mask. Decode only returns a noData value if it is really used. In that case the caller needs to filter jpayne@69: //! the decoded arrays using both the byte mask returned and the noData value returned. jpayne@69: //! jpayne@69: //! In addition to the noData support, the new functions can also take integer values > 32 bit (but < 53 bit) as a double array, jpayne@69: //! and if all integer, use that for compression. jpayne@69: //! jpayne@69: //! If floating point data contains NaN, Lerc tries to move it to the byte mask or replace it by a passed noData value. jpayne@69: //! Note, if not all NaN values can be moved to the mask (mixed case), and no noData value was passed, Lerc will fail. jpayne@69: //! It would be wrong to invent a noData value on the tile level. jpayne@69: jpayne@69: jpayne@69: //! Encode functions: jpayne@69: //! jpayne@69: //! If you don't use a noData value, are fine with the byte masks, just pass nullptr for the last 2 arguments. jpayne@69: //! jpayne@69: //! If you do have noData values at pixels that are marked as valid pixels by the byte mask, jpayne@69: //! pass 2 arrays of size nBands each, one value per band. jpayne@69: //! In pUsesNoData array, for each band, pass 1 for noData value is used, 0 if not. jpayne@69: //! In noDataValues array, for each band, pass the noData value if there is one. jpayne@69: jpayne@69: LERCDLL_API jpayne@69: lerc_status lerc_computeCompressedSize_4D( jpayne@69: const void* pData, // raw image data, row by row, band by band jpayne@69: unsigned int dataType, // char = 0, uchar = 1, short = 2, ushort = 3, int = 4, uint = 5, float = 6, double = 7 jpayne@69: int nDepth, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) jpayne@69: int nCols, // number of columns jpayne@69: int nRows, // number of rows jpayne@69: int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) jpayne@69: int nMasks, // 0 - all valid, 1 - same mask for all bands, nBands - masks can differ between bands jpayne@69: const unsigned char* pValidBytes, // nullptr if all pixels are valid; otherwise 1 byte per pixel (1 = valid, 0 = invalid) jpayne@69: double maxZErr, // max coding error per pixel, defines the precision jpayne@69: unsigned int* numBytes, // size of outgoing Lerc blob jpayne@69: const unsigned char* pUsesNoData, // if there are invalid values not marked by the mask, pass an array of size nBands, 1 - uses noData, 0 - not jpayne@69: const double* noDataValues); // same, pass an array of size nBands with noData value per band, or pass nullptr jpayne@69: jpayne@69: LERCDLL_API jpayne@69: lerc_status lerc_encode_4D( jpayne@69: const void* pData, // raw image data, row by row, band by band jpayne@69: unsigned int dataType, // char = 0, uchar = 1, short = 2, ushort = 3, int = 4, uint = 5, float = 6, double = 7 jpayne@69: int nDepth, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) jpayne@69: int nCols, // number of columns jpayne@69: int nRows, // number of rows jpayne@69: int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) jpayne@69: int nMasks, // 0 - all valid, 1 - same mask for all bands, nBands - masks can differ between bands jpayne@69: const unsigned char* pValidBytes, // nullptr if all pixels are valid; otherwise 1 byte per pixel (1 = valid, 0 = invalid) jpayne@69: double maxZErr, // max coding error per pixel, defines the precision jpayne@69: unsigned char* pOutBuffer, // buffer to write to, function fails if buffer too small jpayne@69: unsigned int outBufferSize, // size of output buffer jpayne@69: unsigned int* nBytesWritten, // number of bytes written to output buffer jpayne@69: const unsigned char* pUsesNoData, // if there are invalid values not marked by the mask, pass an array of size nBands, 1 - uses noData, 0 - not jpayne@69: const double* noDataValues); // same, pass an array of size nBands with noData value per band, or pass nullptr jpayne@69: jpayne@69: jpayne@69: //! Decode functions: jpayne@69: //! jpayne@69: //! Same as for regular decode, first call lerc_getBlobInfo() to get all info needed from the blob header. jpayne@69: //! Check the property (InfoArray::nUsesNoDataValue) to check if there is any noData value used. jpayne@69: //! jpayne@69: //! If not, just pass nullptr for the last 2 arguments. jpayne@69: //! jpayne@69: //! If yes, pass 2 arrays of size nBands each, one value per band. jpayne@69: //! In pUsesNoData array, for each band, 1 means a noData value is used, 0 means not. jpayne@69: //! In noDataValues array, for each band, it has the noData value if there is one. jpayne@69: //! This is the same noData value as passed for encode. jpayne@69: jpayne@69: LERCDLL_API jpayne@69: #ifdef USE_EMSCRIPTEN jpayne@69: EMSCRIPTEN_KEEPALIVE jpayne@69: #endif jpayne@69: lerc_status lerc_decode_4D( jpayne@69: const unsigned char* pLercBlob, // Lerc blob to decode jpayne@69: unsigned int blobSize, // blob size in bytes jpayne@69: int nMasks, // 0, 1, or nBands; return as many masks in the next array jpayne@69: unsigned char* pValidBytes, // gets filled if not nullptr, even if all valid jpayne@69: int nDepth, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) jpayne@69: int nCols, // number of columns jpayne@69: int nRows, // number of rows jpayne@69: int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) jpayne@69: unsigned int dataType, // char = 0, uchar = 1, short = 2, ushort = 3, int = 4, uint = 5, float = 6, double = 7 jpayne@69: void* pData, // outgoing data array jpayne@69: unsigned char* pUsesNoData, // pass an array of size nBands, 1 - band uses noData, 0 - not jpayne@69: double* noDataValues); // same, pass an array of size nBands to get the noData value per band, if any jpayne@69: jpayne@69: LERCDLL_API jpayne@69: lerc_status lerc_decodeToDouble_4D( jpayne@69: const unsigned char* pLercBlob, // Lerc blob to decode jpayne@69: unsigned int blobSize, // blob size in bytes jpayne@69: int nMasks, // 0, 1, or nBands; return as many masks in the next array jpayne@69: unsigned char* pValidBytes, // gets filled if not nullptr, even if all valid jpayne@69: int nDepth, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) jpayne@69: int nCols, // number of columns jpayne@69: int nRows, // number of rows jpayne@69: int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) jpayne@69: double* pData, // outgoing data array jpayne@69: unsigned char* pUsesNoData, // pass an array of size nBands, 1 - band uses noData, 0 - not jpayne@69: double* noDataValues); // same, pass an array of size nBands to get the noData value per band, if any jpayne@69: jpayne@69: jpayne@69: #ifdef __cplusplus jpayne@69: } jpayne@69: #endif jpayne@69: jpayne@69: #endif // LERC_API_INCLUDE_GUARD