in libheif/context.cc [3888:4078]
Error HeifContext::encode_image_as_jpeg(const std::shared_ptr<HeifPixelImage>& image,
struct heif_encoder* encoder,
const struct heif_encoding_options& options,
enum heif_image_input_class input_class,
std::shared_ptr<Image>& out_image)
{
heif_item_id image_id = m_heif_file->add_new_image("jpeg");
out_image = std::make_shared<Image>(this, image_id);
m_top_level_images.push_back(out_image);
m_all_images[image_id] = out_image;
// --- check whether we have to convert the image color space
heif_colorspace colorspace = image->get_colorspace();
heif_chroma chroma = image->get_chroma_format();
// JPEG always uses CCIR-601
heif_color_profile_nclx target_heif_nclx;
target_heif_nclx.matrix_coefficients = heif_matrix_coefficients_ITU_R_BT_601_6;
target_heif_nclx.color_primaries = heif_color_primaries_ITU_R_BT_601_6;
target_heif_nclx.transfer_characteristics = heif_transfer_characteristic_ITU_R_BT_601_6;
target_heif_nclx.full_range_flag = true;
auto target_nclx_profile = std::make_shared<color_profile_nclx>();
target_nclx_profile->set_from_heif_color_profile_nclx(&target_heif_nclx);
if (encoder->plugin->plugin_api_version >= 2) {
encoder->plugin->query_input_colorspace2(encoder->encoder, &colorspace, &chroma);
}
else {
encoder->plugin->query_input_colorspace(&colorspace, &chroma);
}
std::shared_ptr<HeifPixelImage> src_image;
if (colorspace != image->get_colorspace() ||
chroma != image->get_chroma_format() ||
!nclx_profile_matches_spec(colorspace, image->get_color_profile_nclx(), &target_heif_nclx)) {
// @TODO: use color profile when converting
int output_bpp = 0; // same as input
src_image = convert_colorspace(image, colorspace, chroma, target_nclx_profile,
output_bpp, options.color_conversion_options);
if (!src_image) {
return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_color_conversion);
}
}
else {
src_image = image;
}
// --- choose which color profile to put into 'colr' box
if (input_class == heif_image_input_class_normal || input_class == heif_image_input_class_thumbnail) {
auto icc_profile = src_image->get_color_profile_icc();
if (icc_profile) {
m_heif_file->set_color_profile(image_id, icc_profile);
}
if (// target_nclx_profile &&
(!icc_profile || (options.version >= 3 &&
options.save_two_colr_boxes_when_ICC_and_nclx_available))) {
m_heif_file->set_color_profile(image_id, target_nclx_profile);
}
}
// --- if there is an alpha channel, add it as an additional image
if (options.save_alpha_channel && src_image->has_channel(heif_channel_Alpha)) {
// --- generate alpha image
// TODO: can we directly code a monochrome image instead of the dummy color channels?
std::shared_ptr<HeifPixelImage> alpha_image;
alpha_image = create_alpha_image_from_image_alpha_channel(src_image);
// --- encode the alpha image
std::shared_ptr<HeifContext::Image> heif_alpha_image;
Error error = encode_image_as_jpeg(alpha_image, encoder, options,
heif_image_input_class_alpha,
heif_alpha_image);
if (error) {
return error;
}
m_heif_file->add_iref_reference(heif_alpha_image->get_id(), fourcc("auxl"), {image_id});
m_heif_file->set_auxC_property(heif_alpha_image->get_id(), "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha");
if (src_image->is_premultiplied_alpha()) {
m_heif_file->add_iref_reference(image_id, fourcc("prem"), {heif_alpha_image->get_id()});
}
}
heif_image c_api_image;
c_api_image.image = src_image;
struct heif_error err = encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class);
if (err.code) {
return Error(err.code,
err.subcode,
err.message);
}
std::vector<uint8_t> vec;
for (;;) {
uint8_t* data;
int size;
encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, nullptr);
if (data == nullptr) {
break;
}
size_t oldsize = vec.size();
vec.resize(oldsize + size);
memcpy(vec.data() + oldsize, data, size);
}
// Optional: split the JPEG data into a jpgC box and the actual image data.
// Currently disabled because not supported yet in other decoders.
if (false) {
size_t pos = find_jpeg_marker_start(vec, JPEG_SOS);
if (pos > 0) {
std::vector<uint8_t> jpgC_data(vec.begin(), vec.begin() + pos);
auto jpgC = std::make_shared<Box_jpgC>();
jpgC->set_data(jpgC_data);
auto ipma_box = m_heif_file->get_ipma_box();
int index = m_heif_file->get_ipco_box()->find_or_append_child_box(jpgC);
ipma_box->add_property_for_item_ID(image_id, Box_ipma::PropertyAssociation{true, uint16_t(index + 1)});
std::vector<uint8_t> image_data(vec.begin() + pos, vec.end());
vec = std::move(image_data);
}
}
m_heif_file->append_iloc_data(image_id, vec);
#if 0
// TODO: extract 'jpgC' header data
#endif
uint32_t input_width, input_height;
input_width = src_image->get_width();
input_height = src_image->get_height();
// Note: 'ispe' must be before the transformation properties
m_heif_file->add_ispe_property(image_id, input_width, input_height);
uint32_t encoded_width = input_width, encoded_height = input_height;
if (encoder->plugin->plugin_api_version >= 3 &&
encoder->plugin->query_encoded_size != nullptr) {
encoder->plugin->query_encoded_size(encoder->encoder,
input_width, input_height,
&encoded_width,
&encoded_height);
}
if (input_width != encoded_width ||
input_height != encoded_height) {
m_heif_file->add_clap_property(image_id, input_width, input_height,
encoded_width, encoded_height);
// MIAF 7.3.6.7
// This is according to MIAF without Amd2. With Amd2, the restriction has been liften and the image is MIAF compatible.
// We might remove this code at a later point in time when MIAF Amd2 is in wide use.
if (!is_integer_multiple_of_chroma_size(input_width,
input_height,
src_image->get_chroma_format())) {
out_image->mark_not_miaf_compatible();
}
}
m_heif_file->add_orientation_properties(image_id, options.image_orientation);
write_image_metadata(src_image, image_id);
return Error::Ok;
}