fwt_software/src/camera/JpegXlEncoder.cpp

99 lines
3.6 KiB
C++

#include "fgc/JpegXlEncoder.h"
#include "fgc/Logger.h"
#include <cstdio>
#include <vector>
#include <jxl/encode.h>
#include <jxl/encode_cxx.h>
#include <jxl/thread_parallel_runner.h>
#include <jxl/thread_parallel_runner_cxx.h>
#include <jxl/types.h>
namespace fgc {
bool JpegXlEncoder::encodeToFile(const std::string& path, const uint8_t* img, int width,
int height, int channels, float distance, int effort,
int threads) {
auto enc = JxlEncoderMake(nullptr);
auto runner = JxlThreadParallelRunnerMake(nullptr, threads);
if (JXL_ENC_SUCCESS !=
JxlEncoderSetParallelRunner(enc.get(), JxlThreadParallelRunner, runner.get())) {
LOG_ERROR << "JxlEncoderSetParallelRunner failed";
return false;
}
JxlPixelFormat pixel_format = {static_cast<uint32_t>(channels), JXL_TYPE_UINT8,
JXL_NATIVE_ENDIAN, 0};
JxlBasicInfo basic_info;
JxlEncoderInitBasicInfo(&basic_info);
basic_info.xsize = width;
basic_info.ysize = height;
basic_info.alpha_bits = 0;
basic_info.bits_per_sample = 8;
basic_info.num_color_channels = channels;
basic_info.uses_original_profile = JXL_FALSE;
if (JXL_ENC_SUCCESS != JxlEncoderSetBasicInfo(enc.get(), &basic_info)) {
LOG_ERROR << "JxlEncoderSetBasicInfo failed";
return false;
}
JxlColorEncoding color_encoding = {};
JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/channels < 3);
if (JXL_ENC_SUCCESS != JxlEncoderSetColorEncoding(enc.get(), &color_encoding)) {
LOG_ERROR << "JxlEncoderSetColorEncoding failed";
return false;
}
JxlEncoderFrameSettings* fs = JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
JxlEncoderFrameSettingsSetOption(fs, JXL_ENC_FRAME_SETTING_EFFORT, effort);
JxlEncoderFrameSettingsSetOption(fs, JXL_ENC_FRAME_SETTING_DECODING_SPEED, 0);
if (distance == 0.0f) {
JxlEncoderSetFrameDistance(fs, 0);
JxlEncoderSetFrameLossless(fs, JXL_TRUE);
} else {
JxlEncoderSetFrameLossless(fs, JXL_FALSE);
JxlEncoderSetFrameDistance(fs, distance);
}
const size_t byte_size = static_cast<size_t>(width) * height * channels;
if (JXL_ENC_SUCCESS !=
JxlEncoderAddImageFrame(fs, &pixel_format, img, byte_size)) {
LOG_ERROR << "JxlEncoderAddImageFrame failed";
return false;
}
JxlEncoderCloseInput(enc.get());
std::vector<uint8_t> compressed(64);
uint8_t* next_out = compressed.data();
size_t avail_out = compressed.size();
JxlEncoderStatus result = JXL_ENC_NEED_MORE_OUTPUT;
while (result == JXL_ENC_NEED_MORE_OUTPUT) {
result = JxlEncoderProcessOutput(enc.get(), &next_out, &avail_out);
if (result == JXL_ENC_NEED_MORE_OUTPUT) {
size_t offset = next_out - compressed.data();
compressed.resize(compressed.size() * 2);
next_out = compressed.data() + offset;
avail_out = compressed.size() - offset;
}
}
if (JXL_ENC_SUCCESS != result) {
LOG_ERROR << "JxlEncoderProcessOutput failed";
return false;
}
compressed.resize(next_out - compressed.data());
FILE* file = std::fopen(path.c_str(), "wb");
if (!file) {
LOG_ERROR << "Could not open " << path << " for writing";
return false;
}
bool ok = std::fwrite(compressed.data(), 1, compressed.size(), file) == compressed.size();
if (std::fclose(file) != 0) ok = false;
if (!ok) LOG_ERROR << "Could not write " << path;
return ok;
}
} // namespace fgc