-
Notifications
You must be signed in to change notification settings - Fork 75
How to use the API
The original version of CharLS came with an API that required to setup the decoding and encoding process by using a C struct called JlsParameters and calling one or more of the C functions:
- JpegLsEncode
- JpegLsReadHeader
- JpegLsDecode
There were a couple of problems with this design:
- It was impossible to extend the API or deprecate functionality without breaking the Application binary interface (ABI)
- Several use cases were not possible as no hooks could be injected in the encoding or decoding process.
- Only a C API was offered and not an easy to use C++ interface.
- JpegLsReadHeader and JpegLsDecode would parse parts of the JPEG encoded stream twice.
To solve this, CharLS 2.1 comes with a new C API \ ABI and a C++ class to encode and a C++ to decode.
These C++ classes are header only and will call into the C API functions.
Note: The legacy structs and C functions are still present and will be maintained until the next major (breaking) release.
New projects are recommended to use the new API.
The CharLS source code package comes with two samples (a C and a C++ sample) that demonstrate how to encode a Windows Bitmap file (.bmp) to a JPEG-LS encoded file.
The header file that need to be included is always charls.h. All API functions are documented in this header file with the Microsoft XML Documentation format. Depending on the used C++ IDE, this documentation is automatically used as a tooltip.
CharLS can process and can generate native pixel data that uses padding to align the start of a line on a specific address boundary (for example a 32 bit boundary). This information can be provided with the stride parameter. A stride of 0 can be used to indicate that no padding for the alignment is needed.
Besides the alignment option, CharLS expects a different input format depending on the selected interleave mode and will generate a different output format depending how the JPEG-LS encoded data is stored in the bit-stream. A different format is needed as the image is encoded / decoded per line in a forward only process. For monochrome images this format requirement is not important as only 1 component is encoded, but for multi-component images like color images it is crucial to handle the difference.
The expected format for a RGB image is for example:
| interleave mode | native format | encoded format |
|---|---|---|
| none | by plane: RRRRGGGGBBBB | scan 1:rrr, scan 2:ggg, scan 3: bbb |
| line | by pixel: RGBRGBRGBRGB | scan 1: rrrgggbbb |
| sample | by pixel: RGBRGBRGBRGB | scan 1: rgbrgbrgb |
Note that line and sample use the same native format. Many viewing pipelines can only handle the "by pixel" format and a pre/post processing step may be needed to transform the native input/output format to the "by plane" format to encode in the interleave mode "none". The includes samples demonstrate these steps. The interleave mode is in general faster to encode/decode and provides a better compression ratio.
Pixel samples are always interpreted in the native endian format of the system. In most cases this means Little Endian. This is important for bit sizes larger than 8 bits which require 2 bytes. The least significant bits are used and for bit sizes other then 8 and 16 the not used bits should be set to zero.
The paragraphs below show how to use the C++ CharLS API. The C API is not demonstrated explicitly, but it usages follows the same patterns.
#include <charls/charls.h>
#include <vector>
#include <tuple>
std::vector<uint8_t> decode_simple_8bit_monochrome(const std::vector<uint8_t>& source)
{
std::vector<uint8_t> destination;
charls::frame_info frame_info;
std::tie(frame_info, std::ignore) = charls::jpegls_decoder::decode(source, destination);
if (frame_info.component_count != 1 || frame_info.bits_per_sample != 8)
throw std::exception("Not a 8 bit monochrome image");
return destination;
}Decoding JPEG-LS images contained in a container is relatively easy. It is more complex to decode standalone JPEG-LS images. In such cases a jpegls_decoder instance can be constructed.
#include <charls/charls.h>
#include <vector>
std::vector<uint8_t> decode_advanced(const std::vector<uint8_t>& source)
{
charls::jpegls_decoder decoder;
decoder.source(source);
// Standalone JPEG-LS files may have a SPIFF header (color space info, etc.)
bool spiff_header_found;
const auto spiff_header{decoder.read_spiff_header(spiff_header_found)};
if (spiff_header_found &&
spiff_header.color_space != charls::spiff_color_space::grayscale)
throw std::exception("Not a grayscale image");
decoder.read_header();
// After read_header() other properties can be retrieved.
if (decoder.near_lossless() != 0)
{
// Handle lossy images.
}
return decoder.decode<std::vector<uint8_t>>();
}For references purposes and guidelines how to convert from the legacy API to the new API the sample can be used:
- Create a memory buffer to hold the encoded JPEG-LS data
- Call JpegLsReadHeader to fill the JlsParameters struct
- Compute the size to hold the decoded image
- Call JpegLsDecode to decode the image
#include <charls/charls.h>
#include <vector>
std::vector<uint8_t> decode_simple_8bit_monochrome_legacy(const std::vector<uint8_t>& source)
{
char error_message[ErrorMessageSize];
JlsParameters parameters;
auto error = JpegLsReadHeader(source.data(), source.size(), ¶meters, error_message);
if (error != CharlsApiResultType::OK)
throw std::exception(error_message);
if (parameters.components != 1 || parameters.bitsPerSample != 8)
throw std::exception("Not a 8 bit monochrome image");
const size_t destination_size = static_cast<size_t>(parameters.width) * parameters.height;
std::vector<uint8_t> destination(destination_size);
error = JpegLsDecode(destination.data(), destination.size(),
source.data(), source.size(),
¶meters, error_message);
if (error != CharlsApiResultType::OK)
throw std::exception(error_message);
return destination;
}Encoding lossless monochrome images or color images with the interleave mode none to a STL vector container can be done with the static template function encode. This function creates a STL container containing the encoded JPEG-LS data that can be wrapped in a file container.
#include <charls/charls.h>
#include <vector>
std::vector<uint8_t> encode_simple_8bit_monochrome(const std::vector<uint8_t>& source, uint32_t width, uint32_t height)
{
constexpr auto bits_per_sample = 8;
constexpr auto component_count = 1;
return charls::jpegls_encoder::encode(source,
{width, height,
bits_per_sample, component_count});
}For more advances options and to create standalone .jls files, the pattern is to create an jpegls_encoder instance, configure it and call encode on the encoder instance.
#include <charls/charls.h>
#include <vector>
std::vector<uint8_t> encode_advanced_8bit_monochrome(const std::vector<uint8_t>& source, uint32_t width, uint32_t height)
{
charls::jpegls_encoder encoder;
encoder.frame_info({width, height, 8, 1});
std::vector<uint8_t> destination(encoder.estimated_destination_size());
encoder.destination(destination);
encoder.write_standard_spiff_header(charls::spiff_color_space::grayscale);
const size_t bytes_written{encoder.encode(source)};
destination.resize(bytes_written);
return destination;
}For references purposes and guidelines how to convert from the legacy API to the new API the following sample can be used:
#include <charls/charls.h>
std::vector<uint8_t> encode_simple_8bit_monochrome_legacy(const std::vector<uint8_t>& source, uint32_t width, uint32_t height)
{
char error_message[ErrorMessageSize];
JlsParameters parameters{};
parameters.width = width;
parameters.height = height;
parameters.bitsPerSample = 8;
parameters.components = 1;
const size_t estimated_destination_size = static_cast<size_t>(width) * height * 1 * 1 + 1024;
std::vector<uint8_t> destination(estimated_destination_size);
size_t bytes_written;
auto error = JpegLsEncode(destination.data(), destination.size(),
&bytes_written,
source.data(), source.size(), ¶meters, error_message);
if (error != CharlsApiResultType::OK)
throw std::exception(error_message);
destination.resize(bytes_written);
return destination;
}