diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /third_party/jpeg-xl/lib/extras/packed_image_convert.cc | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/jpeg-xl/lib/extras/packed_image_convert.cc')
-rw-r--r-- | third_party/jpeg-xl/lib/extras/packed_image_convert.cc | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/third_party/jpeg-xl/lib/extras/packed_image_convert.cc b/third_party/jpeg-xl/lib/extras/packed_image_convert.cc new file mode 100644 index 0000000000..1dd2b45a7f --- /dev/null +++ b/third_party/jpeg-xl/lib/extras/packed_image_convert.cc @@ -0,0 +1,300 @@ +// Copyright (c) the JPEG XL Project Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "lib/extras/packed_image_convert.h" + +#include <jxl/color_encoding.h> +#include <jxl/types.h> + +#include <cstdint> + +#include "lib/jxl/base/status.h" +#include "lib/jxl/color_encoding_internal.h" +#include "lib/jxl/color_management.h" +#include "lib/jxl/dec_external_image.h" +#include "lib/jxl/enc_color_management.h" +#include "lib/jxl/enc_external_image.h" +#include "lib/jxl/enc_image_bundle.h" +#include "lib/jxl/luminance.h" + +namespace jxl { +namespace extras { + +Status ConvertPackedFrameToImageBundle(const JxlBasicInfo& info, + const PackedFrame& frame, + const CodecInOut& io, ThreadPool* pool, + ImageBundle* bundle) { + JXL_ASSERT(frame.color.pixels() != nullptr); + const bool float_in = frame.color.format.data_type == JXL_TYPE_FLOAT16 || + frame.color.format.data_type == JXL_TYPE_FLOAT; + size_t frame_bits_per_sample = + float_in ? PackedImage::BitsPerChannel(frame.color.format.data_type) + : info.bits_per_sample; + JXL_ASSERT(frame_bits_per_sample != 0); + // It is ok for the frame.color.format.num_channels to not match the + // number of channels on the image. + JXL_ASSERT(1 <= frame.color.format.num_channels && + frame.color.format.num_channels <= 4); + + const Span<const uint8_t> span( + static_cast<const uint8_t*>(frame.color.pixels()), + frame.color.pixels_size); + JXL_ASSERT(Rect(frame.frame_info.layer_info.crop_x0, + frame.frame_info.layer_info.crop_y0, + frame.frame_info.layer_info.xsize, + frame.frame_info.layer_info.ysize) + .IsInside(Rect(0, 0, info.xsize, info.ysize))); + if (info.have_animation) { + bundle->duration = frame.frame_info.duration; + bundle->blend = frame.frame_info.layer_info.blend_info.blendmode > 0; + bundle->use_for_next_frame = + frame.frame_info.layer_info.save_as_reference > 0; + bundle->origin.x0 = frame.frame_info.layer_info.crop_x0; + bundle->origin.y0 = frame.frame_info.layer_info.crop_y0; + } + bundle->name = frame.name; // frame.frame_info.name_length is ignored here. + JXL_ASSERT(io.metadata.m.color_encoding.IsGray() == + (frame.color.format.num_channels <= 2)); + + JXL_RETURN_IF_ERROR(ConvertFromExternal( + span, frame.color.xsize, frame.color.ysize, io.metadata.m.color_encoding, + frame_bits_per_sample, frame.color.format, pool, bundle)); + + bundle->extra_channels().resize(io.metadata.m.extra_channel_info.size()); + for (size_t i = 0; i < frame.extra_channels.size(); i++) { + const auto& ppf_ec = frame.extra_channels[i]; + bundle->extra_channels()[i] = ImageF(ppf_ec.xsize, ppf_ec.ysize); + JXL_CHECK(BufferToImageF(ppf_ec.format, ppf_ec.xsize, ppf_ec.ysize, + ppf_ec.pixels(), ppf_ec.pixels_size, pool, + &bundle->extra_channels()[i])); + } + return true; +} + +Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf, + ThreadPool* pool, CodecInOut* io) { + const bool has_alpha = ppf.info.alpha_bits != 0; + JXL_ASSERT(!ppf.frames.empty()); + if (has_alpha) { + JXL_ASSERT(ppf.info.alpha_bits == ppf.info.bits_per_sample); + JXL_ASSERT(ppf.info.alpha_exponent_bits == + ppf.info.exponent_bits_per_sample); + } + + const bool is_gray = ppf.info.num_color_channels == 1; + JXL_ASSERT(ppf.info.num_color_channels == 1 || + ppf.info.num_color_channels == 3); + + // Convert the image metadata + io->SetSize(ppf.info.xsize, ppf.info.ysize); + io->metadata.m.bit_depth.bits_per_sample = ppf.info.bits_per_sample; + io->metadata.m.bit_depth.exponent_bits_per_sample = + ppf.info.exponent_bits_per_sample; + io->metadata.m.bit_depth.floating_point_sample = + ppf.info.exponent_bits_per_sample != 0; + io->metadata.m.modular_16_bit_buffer_sufficient = + ppf.info.exponent_bits_per_sample == 0 && ppf.info.bits_per_sample <= 12; + + io->metadata.m.SetAlphaBits(ppf.info.alpha_bits, + ppf.info.alpha_premultiplied); + + io->metadata.m.xyb_encoded = !ppf.info.uses_original_profile; + JXL_ASSERT(ppf.info.orientation > 0 && ppf.info.orientation <= 8); + io->metadata.m.orientation = ppf.info.orientation; + + // Convert animation metadata + JXL_ASSERT(ppf.frames.size() == 1 || ppf.info.have_animation); + io->metadata.m.have_animation = ppf.info.have_animation; + io->metadata.m.animation.tps_numerator = ppf.info.animation.tps_numerator; + io->metadata.m.animation.tps_denominator = ppf.info.animation.tps_denominator; + io->metadata.m.animation.num_loops = ppf.info.animation.num_loops; + + // Convert the color encoding. + if (!ppf.icc.empty()) { + PaddedBytes icc; + icc.append(ppf.icc); + if (!io->metadata.m.color_encoding.SetICC(std::move(icc))) { + fprintf(stderr, "Warning: error setting ICC profile, assuming SRGB\n"); + io->metadata.m.color_encoding = ColorEncoding::SRGB(is_gray); + } else { + if (io->metadata.m.color_encoding.IsGray() != is_gray) { + // E.g. JPG image has 3 channels, but gray ICC. + return JXL_FAILURE("Embedded ICC does not match image color type"); + } + } + } else { + JXL_RETURN_IF_ERROR(ConvertExternalToInternalColorEncoding( + ppf.color_encoding, &io->metadata.m.color_encoding)); + if (io->metadata.m.color_encoding.ICC().empty()) { + return JXL_FAILURE("Failed to serialize ICC"); + } + } + + // Convert the extra blobs + io->blobs.exif = ppf.metadata.exif; + io->blobs.iptc = ppf.metadata.iptc; + io->blobs.jumbf = ppf.metadata.jumbf; + io->blobs.xmp = ppf.metadata.xmp; + + // Append all other extra channels. + for (const auto& info : ppf.extra_channels_info) { + ExtraChannelInfo out; + out.type = static_cast<jxl::ExtraChannel>(info.ec_info.type); + out.bit_depth.bits_per_sample = info.ec_info.bits_per_sample; + out.bit_depth.exponent_bits_per_sample = + info.ec_info.exponent_bits_per_sample; + out.bit_depth.floating_point_sample = + info.ec_info.exponent_bits_per_sample != 0; + out.dim_shift = info.ec_info.dim_shift; + out.name = info.name; + out.alpha_associated = (info.ec_info.alpha_premultiplied != 0); + out.spot_color[0] = info.ec_info.spot_color[0]; + out.spot_color[1] = info.ec_info.spot_color[1]; + out.spot_color[2] = info.ec_info.spot_color[2]; + out.spot_color[3] = info.ec_info.spot_color[3]; + io->metadata.m.extra_channel_info.push_back(std::move(out)); + } + + // Convert the preview + if (ppf.preview_frame) { + size_t preview_xsize = ppf.preview_frame->color.xsize; + size_t preview_ysize = ppf.preview_frame->color.ysize; + io->metadata.m.have_preview = true; + JXL_RETURN_IF_ERROR( + io->metadata.m.preview_size.Set(preview_xsize, preview_ysize)); + JXL_RETURN_IF_ERROR(ConvertPackedFrameToImageBundle( + ppf.info, *ppf.preview_frame, *io, pool, &io->preview_frame)); + } + + // Convert the pixels + io->frames.clear(); + for (const auto& frame : ppf.frames) { + ImageBundle bundle(&io->metadata.m); + JXL_RETURN_IF_ERROR( + ConvertPackedFrameToImageBundle(ppf.info, frame, *io, pool, &bundle)); + io->frames.push_back(std::move(bundle)); + } + + if (ppf.info.exponent_bits_per_sample == 0) { + // uint case. + io->metadata.m.bit_depth.bits_per_sample = io->Main().DetectRealBitdepth(); + } + if (ppf.info.intensity_target != 0) { + io->metadata.m.SetIntensityTarget(ppf.info.intensity_target); + } else { + SetIntensityTarget(&io->metadata.m); + } + io->CheckMetadata(); + return true; +} + +// Allows converting from internal CodecInOut to external PackedPixelFile +Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io, + const JxlPixelFormat& pixel_format, + const ColorEncoding& c_desired, + ThreadPool* pool, + PackedPixelFile* ppf) { + const bool has_alpha = io.metadata.m.HasAlpha(); + bool alpha_premultiplied = false; + JXL_ASSERT(!io.frames.empty()); + + if (has_alpha) { + JXL_ASSERT(io.metadata.m.GetAlphaBits() == + io.metadata.m.bit_depth.bits_per_sample); + const auto* alpha_channel = io.metadata.m.Find(ExtraChannel::kAlpha); + JXL_ASSERT(alpha_channel->bit_depth.exponent_bits_per_sample == + io.metadata.m.bit_depth.exponent_bits_per_sample); + alpha_premultiplied = alpha_channel->alpha_associated; + } + + // Convert the image metadata + ppf->info.xsize = io.metadata.size.xsize(); + ppf->info.ysize = io.metadata.size.ysize(); + ppf->info.num_color_channels = io.metadata.m.color_encoding.Channels(); + ppf->info.bits_per_sample = io.metadata.m.bit_depth.bits_per_sample; + ppf->info.exponent_bits_per_sample = + io.metadata.m.bit_depth.exponent_bits_per_sample; + + ppf->info.intensity_target = io.metadata.m.tone_mapping.intensity_target; + ppf->info.linear_below = io.metadata.m.tone_mapping.linear_below; + ppf->info.min_nits = io.metadata.m.tone_mapping.min_nits; + ppf->info.relative_to_max_display = + io.metadata.m.tone_mapping.relative_to_max_display; + + ppf->info.alpha_bits = io.metadata.m.GetAlphaBits(); + ppf->info.alpha_premultiplied = alpha_premultiplied; + + ppf->info.uses_original_profile = !io.metadata.m.xyb_encoded; + JXL_ASSERT(0 < io.metadata.m.orientation && io.metadata.m.orientation <= 8); + ppf->info.orientation = + static_cast<JxlOrientation>(io.metadata.m.orientation); + ppf->info.num_color_channels = io.metadata.m.color_encoding.Channels(); + + // Convert animation metadata + JXL_ASSERT(io.frames.size() == 1 || io.metadata.m.have_animation); + ppf->info.have_animation = io.metadata.m.have_animation; + ppf->info.animation.tps_numerator = io.metadata.m.animation.tps_numerator; + ppf->info.animation.tps_denominator = io.metadata.m.animation.tps_denominator; + ppf->info.animation.num_loops = io.metadata.m.animation.num_loops; + + // Convert the color encoding + ppf->icc.assign(c_desired.ICC().begin(), c_desired.ICC().end()); + ConvertInternalToExternalColorEncoding(c_desired, &ppf->color_encoding); + + // Convert the extra blobs + ppf->metadata.exif = io.blobs.exif; + ppf->metadata.iptc = io.blobs.iptc; + ppf->metadata.jumbf = io.blobs.jumbf; + ppf->metadata.xmp = io.blobs.xmp; + const bool float_out = pixel_format.data_type == JXL_TYPE_FLOAT || + pixel_format.data_type == JXL_TYPE_FLOAT16; + // Convert the pixels + ppf->frames.clear(); + for (const auto& frame : io.frames) { + JXL_ASSERT(frame.metadata()->bit_depth.bits_per_sample != 0); + // It is ok for the frame.color().kNumPlanes to not match the + // number of channels on the image. + const uint32_t num_channels = + frame.metadata()->color_encoding.Channels() + has_alpha; + JxlPixelFormat format{/*num_channels=*/num_channels, + /*data_type=*/pixel_format.data_type, + /*endianness=*/pixel_format.endianness, + /*align=*/pixel_format.align}; + + PackedFrame packed_frame(frame.oriented_xsize(), frame.oriented_ysize(), + format); + const size_t bits_per_sample = + float_out ? packed_frame.color.BitsPerChannel(pixel_format.data_type) + : ppf->info.bits_per_sample; + packed_frame.name = frame.name; + packed_frame.frame_info.name_length = frame.name.size(); + // Color transform + ImageBundle ib = frame.Copy(); + const ImageBundle* to_color_transform = &ib; + ImageMetadata metadata = io.metadata.m; + ImageBundle store(&metadata); + const ImageBundle* transformed; + // TODO(firsching): handle the transform here. + JXL_RETURN_IF_ERROR(TransformIfNeeded(*to_color_transform, c_desired, + GetJxlCms(), pool, &store, + &transformed)); + + JXL_RETURN_IF_ERROR(ConvertToExternal( + *transformed, bits_per_sample, float_out, format.num_channels, + format.endianness, + /* stride_out=*/packed_frame.color.stride, pool, + packed_frame.color.pixels(), packed_frame.color.pixels_size, + /*out_callback=*/{}, frame.metadata()->GetOrientation())); + + // TODO(firsching): Convert the extra channels, beside one potential alpha + // channel. FIXME! + JXL_CHECK(frame.extra_channels().size() <= has_alpha); + ppf->frames.push_back(std::move(packed_frame)); + } + + return true; +} +} // namespace extras +} // namespace jxl |