in libheif/plugins/encoder_rav1e.cc [478:655]
struct heif_error rav1e_encode_image(void* encoder_raw, const struct heif_image* image,
heif_image_input_class input_class)
{
auto* encoder = (struct encoder_struct_rav1e*) encoder_raw;
const heif_chroma chroma = heif_image_get_chroma_format(image);
uint8_t yShift = 0;
RaChromaSampling chromaSampling;
RaChromaSamplePosition chromaPosition;
RaPixelRange rav1eRange;
if (input_class == heif_image_input_class_alpha) {
chromaSampling = RA_CHROMA_SAMPLING_CS420; // I can't seem to get RA_CHROMA_SAMPLING_CS400 to work right now, unfortunately
chromaPosition = RA_CHROMA_SAMPLE_POSITION_UNKNOWN; // TODO: set to CENTER when AV1 and rav1e supports this
yShift = 1;
}
else {
switch (chroma) {
case heif_chroma_444:
chromaSampling = RA_CHROMA_SAMPLING_CS444;
chromaPosition = RA_CHROMA_SAMPLE_POSITION_COLOCATED;
break;
case heif_chroma_422:
chromaSampling = RA_CHROMA_SAMPLING_CS422;
chromaPosition = RA_CHROMA_SAMPLE_POSITION_COLOCATED;
break;
case heif_chroma_420:
chromaSampling = RA_CHROMA_SAMPLING_CS420;
chromaPosition = RA_CHROMA_SAMPLE_POSITION_UNKNOWN; // TODO: set to CENTER when AV1 and rav1e supports this
yShift = 1;
break;
default:
return heif_error_codec_library_error;
}
}
struct heif_color_profile_nclx* nclx = nullptr;
heif_error err = heif_image_get_nclx_color_profile(image, &nclx);
if (err.code != heif_error_Ok) {
nclx = nullptr;
}
// make sure NCLX profile is deleted at end of function
auto nclx_deleter = std::unique_ptr<heif_color_profile_nclx, void (*)(heif_color_profile_nclx*)>(nclx, heif_nclx_color_profile_free);
rav1eRange = RA_PIXEL_RANGE_FULL;
if (nclx) {
rav1eRange = nclx->full_range_flag ? RA_PIXEL_RANGE_FULL : RA_PIXEL_RANGE_LIMITED;
}
int bitDepth = heif_image_get_bits_per_pixel(image, heif_channel_Y);
auto rav1eConfigRaw = rav1e_config_default();
auto rav1eConfig = std::shared_ptr<RaConfig>(rav1eConfigRaw, [](RaConfig* c) { rav1e_config_unref(c); });
if (rav1e_config_set_pixel_format(rav1eConfig.get(), (uint8_t) bitDepth, chromaSampling, chromaPosition, rav1eRange) < 0) {
return heif_error_codec_library_error;
}
if (rav1e_config_parse(rav1eConfig.get(), "still_picture", "true") == -1) {
return heif_error_codec_library_error;
}
if (rav1e_config_parse_int(rav1eConfig.get(), "width", heif_image_get_width(image, heif_channel_Y)) == -1) {
return heif_error_codec_library_error;
}
if (rav1e_config_parse_int(rav1eConfig.get(), "height", heif_image_get_height(image, heif_channel_Y)) == -1) {
return heif_error_codec_library_error;
}
if (rav1e_config_parse_int(rav1eConfig.get(), "threads", encoder->threads) == -1) {
return heif_error_codec_library_error;
}
if (nclx &&
(input_class == heif_image_input_class_normal ||
input_class == heif_image_input_class_thumbnail)) {
if (rav1e_config_set_color_description(rav1eConfig.get(),
(RaMatrixCoefficients) nclx->matrix_coefficients,
(RaColorPrimaries) nclx->color_primaries,
(RaTransferCharacteristics) nclx->transfer_characteristics) == -1) {
return heif_error_codec_library_error;
}
}
if (rav1e_config_parse_int(rav1eConfig.get(), "min_quantizer", encoder->min_q) == -1) {
return heif_error_codec_library_error;
}
int base_quantizer = ((100 - encoder->quality) * 255 + 50) / 100;
if (rav1e_config_parse_int(rav1eConfig.get(), "quantizer", base_quantizer) == -1) {
return heif_error_codec_library_error;
}
if (encoder->tile_rows != 1) {
if (rav1e_config_parse_int(rav1eConfig.get(), "tile_rows", encoder->tile_rows) == -1) {
return heif_error_codec_library_error;
}
}
if (encoder->tile_cols != 1) {
if (rav1e_config_parse_int(rav1eConfig.get(), "tile_cols", encoder->tile_cols) == -1) {
return heif_error_codec_library_error;
}
}
/*if (encoder->speed != -1)*/ {
if (rav1e_config_parse_int(rav1eConfig.get(), "speed", encoder->speed) == -1) {
return heif_error_codec_library_error;
}
}
if (nclx) {
rav1e_config_set_color_description(rav1eConfig.get(),
(RaMatrixCoefficients) nclx->matrix_coefficients,
(RaColorPrimaries) nclx->color_primaries,
(RaTransferCharacteristics) nclx->transfer_characteristics);
}
RaContext* rav1eContextRaw = rav1e_context_new(rav1eConfig.get());
if (!rav1eContextRaw) {
return heif_error_codec_library_error;
}
auto rav1eContext = std::shared_ptr<RaContext>(rav1eContextRaw, [](RaContext* ctx) { rav1e_context_unref(ctx); });
// --- copy libheif image to rav1e image
auto rav1eFrameRaw = rav1e_frame_new(rav1eContext.get());
auto rav1eFrame = std::shared_ptr<RaFrame>(rav1eFrameRaw, [](RaFrame* frm) { rav1e_frame_unref(frm); });
int byteWidth = (bitDepth > 8) ? 2 : 1;
// if (input_class == heif_image_input_class_alpha) {
//} else
{
int strideY;
const uint8_t* Y = heif_image_get_plane_readonly(image, heif_channel_Y, &strideY);
int strideCb;
const uint8_t* Cb = heif_image_get_plane_readonly(image, heif_channel_Cb, &strideCb);
int strideCr;
const uint8_t* Cr = heif_image_get_plane_readonly(image, heif_channel_Cr, &strideCr);
uint32_t height = heif_image_get_height(image, heif_channel_Y);
uint32_t uvHeight = (height + yShift) >> yShift;
rav1e_frame_fill_plane(rav1eFrame.get(), 0, Y, strideY * height, strideY, byteWidth);
rav1e_frame_fill_plane(rav1eFrame.get(), 1, Cb, strideCb * uvHeight, strideCb, byteWidth);
rav1e_frame_fill_plane(rav1eFrame.get(), 2, Cr, strideCr * uvHeight, strideCr, byteWidth);
}
RaEncoderStatus encoderStatus = rav1e_send_frame(rav1eContext.get(), rav1eFrame.get());
if (encoderStatus != 0) {
return heif_error_codec_library_error;
}
// flush encoder
encoderStatus = rav1e_send_frame(rav1eContext.get(), nullptr);
if (encoderStatus != 0) {
return heif_error_codec_library_error;
}
RaPacket* pkt = nullptr;
encoderStatus = rav1e_receive_packet(rav1eContext.get(), &pkt);
if (encoderStatus != 0) {
return heif_error_codec_library_error;
}
if (pkt && pkt->data && (pkt->len > 0)) {
encoder->compressed_data.resize(pkt->len);
memcpy(encoder->compressed_data.data(), pkt->data, pkt->len);
encoder->data_read = false;
}
if (pkt) {
rav1e_packet_unref(pkt);
}
return heif_error_ok;
}