From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- third_party/jpeg-xl/lib/extras/enc/pnm.cc | 331 ++++++++++++++++++++++++++++++ 1 file changed, 331 insertions(+) create mode 100644 third_party/jpeg-xl/lib/extras/enc/pnm.cc (limited to 'third_party/jpeg-xl/lib/extras/enc/pnm.cc') diff --git a/third_party/jpeg-xl/lib/extras/enc/pnm.cc b/third_party/jpeg-xl/lib/extras/enc/pnm.cc new file mode 100644 index 0000000000..4183900198 --- /dev/null +++ b/third_party/jpeg-xl/lib/extras/enc/pnm.cc @@ -0,0 +1,331 @@ +// 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/enc/pnm.h" + +#include + +#include +#include + +#include "lib/extras/packed_image.h" +#include "lib/jxl/base/byte_order.h" +#include "lib/jxl/base/compiler_specific.h" +#include "lib/jxl/base/printf_macros.h" +#include "lib/jxl/base/status.h" +#include "lib/jxl/dec_external_image.h" +#include "lib/jxl/enc_external_image.h" +#include "lib/jxl/enc_image_bundle.h" +#include "lib/jxl/fields.h" // AllDefault +#include "lib/jxl/image.h" +#include "lib/jxl/image_bundle.h" + +namespace jxl { +namespace extras { +namespace { + +constexpr size_t kMaxHeaderSize = 200; + +class BasePNMEncoder : public Encoder { + public: + Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image, + ThreadPool* pool = nullptr) const override { + JXL_RETURN_IF_ERROR(VerifyBasicInfo(ppf.info)); + if (!ppf.metadata.exif.empty() || !ppf.metadata.iptc.empty() || + !ppf.metadata.jumbf.empty() || !ppf.metadata.xmp.empty()) { + JXL_WARNING("PNM encoder ignoring metadata - use a different codec"); + } + encoded_image->icc = ppf.icc; + encoded_image->bitstreams.clear(); + encoded_image->bitstreams.reserve(ppf.frames.size()); + for (const auto& frame : ppf.frames) { + JXL_RETURN_IF_ERROR(VerifyPackedImage(frame.color, ppf.info)); + encoded_image->bitstreams.emplace_back(); + JXL_RETURN_IF_ERROR( + EncodeFrame(ppf, frame, &encoded_image->bitstreams.back())); + } + for (size_t i = 0; i < ppf.extra_channels_info.size(); ++i) { + const auto& ec_info = ppf.extra_channels_info[i].ec_info; + encoded_image->extra_channel_bitstreams.emplace_back(); + auto& ec_bitstreams = encoded_image->extra_channel_bitstreams.back(); + for (const auto& frame : ppf.frames) { + ec_bitstreams.emplace_back(); + JXL_RETURN_IF_ERROR(EncodeExtraChannel(frame.extra_channels[i], + ec_info.bits_per_sample, + &ec_bitstreams.back())); + } + } + return true; + } + + protected: + virtual Status EncodeFrame(const PackedPixelFile& ppf, + const PackedFrame& frame, + std::vector* bytes) const = 0; + virtual Status EncodeExtraChannel(const PackedImage& image, + size_t bits_per_sample, + std::vector* bytes) const = 0; +}; + +class PNMEncoder : public BasePNMEncoder { + public: + static const std::vector kAcceptedFormats; + + std::vector AcceptedFormats() const override { + return kAcceptedFormats; + } + + Status EncodeFrame(const PackedPixelFile& ppf, const PackedFrame& frame, + std::vector* bytes) const override { + return EncodeImage(frame.color, ppf.info.bits_per_sample, bytes); + } + Status EncodeExtraChannel(const PackedImage& image, size_t bits_per_sample, + std::vector* bytes) const override { + return EncodeImage(image, bits_per_sample, bytes); + } + + private: + Status EncodeImage(const PackedImage& image, size_t bits_per_sample, + std::vector* bytes) const { + uint32_t maxval = (1u << bits_per_sample) - 1; + char type = image.format.num_channels == 1 ? '5' : '6'; + char header[kMaxHeaderSize]; + size_t header_size = + snprintf(header, kMaxHeaderSize, "P%c\n%" PRIuS " %" PRIuS "\n%u\n", + type, image.xsize, image.ysize, maxval); + JXL_RETURN_IF_ERROR(header_size < kMaxHeaderSize); + bytes->resize(header_size + image.pixels_size); + memcpy(bytes->data(), header, header_size); + memcpy(bytes->data() + header_size, + reinterpret_cast(image.pixels()), image.pixels_size); + return true; + } +}; + +class PGMEncoder : public PNMEncoder { + public: + static const std::vector kAcceptedFormats; + + std::vector AcceptedFormats() const override { + return kAcceptedFormats; + } +}; + +const std::vector PGMEncoder::kAcceptedFormats = { + JxlPixelFormat{1, JXL_TYPE_UINT8, JXL_BIG_ENDIAN, 0}, + JxlPixelFormat{1, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}}; + +class PPMEncoder : public PNMEncoder { + public: + static const std::vector kAcceptedFormats; + + std::vector AcceptedFormats() const override { + return kAcceptedFormats; + } +}; + +const std::vector PPMEncoder::kAcceptedFormats = { + JxlPixelFormat{3, JXL_TYPE_UINT8, JXL_BIG_ENDIAN, 0}, + JxlPixelFormat{3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}}; + +const std::vector PNMEncoder::kAcceptedFormats = [] { + std::vector combined = PPMEncoder::kAcceptedFormats; + combined.insert(combined.end(), PGMEncoder::kAcceptedFormats.begin(), + PGMEncoder::kAcceptedFormats.end()); + return combined; +}(); + +class PFMEncoder : public BasePNMEncoder { + public: + std::vector AcceptedFormats() const override { + std::vector formats; + for (const uint32_t num_channels : {1, 3}) { + for (JxlEndianness endianness : {JXL_BIG_ENDIAN, JXL_LITTLE_ENDIAN}) { + formats.push_back(JxlPixelFormat{/*num_channels=*/num_channels, + /*data_type=*/JXL_TYPE_FLOAT, + /*endianness=*/endianness, + /*align=*/0}); + } + } + return formats; + } + Status EncodeFrame(const PackedPixelFile& ppf, const PackedFrame& frame, + std::vector* bytes) const override { + return EncodeImage(frame.color, bytes); + } + Status EncodeExtraChannel(const PackedImage& image, size_t bits_per_sample, + std::vector* bytes) const override { + return EncodeImage(image, bytes); + } + + private: + Status EncodeImage(const PackedImage& image, + std::vector* bytes) const { + char type = image.format.num_channels == 1 ? 'f' : 'F'; + double scale = image.format.endianness == JXL_LITTLE_ENDIAN ? -1.0 : 1.0; + char header[kMaxHeaderSize]; + size_t header_size = + snprintf(header, kMaxHeaderSize, "P%c\n%" PRIuS " %" PRIuS "\n%.1f\n", + type, image.xsize, image.ysize, scale); + JXL_RETURN_IF_ERROR(header_size < kMaxHeaderSize); + bytes->resize(header_size + image.pixels_size); + memcpy(bytes->data(), header, header_size); + const uint8_t* in = reinterpret_cast(image.pixels()); + uint8_t* out = bytes->data() + header_size; + for (size_t y = 0; y < image.ysize; ++y) { + size_t y_out = image.ysize - 1 - y; + const uint8_t* row_in = &in[y * image.stride]; + uint8_t* row_out = &out[y_out * image.stride]; + memcpy(row_out, row_in, image.stride); + } + return true; + } +}; + +class PAMEncoder : public BasePNMEncoder { + public: + std::vector AcceptedFormats() const override { + std::vector formats; + for (const uint32_t num_channels : {1, 2, 3, 4}) { + for (const JxlDataType data_type : {JXL_TYPE_UINT8, JXL_TYPE_UINT16}) { + formats.push_back(JxlPixelFormat{/*num_channels=*/num_channels, + /*data_type=*/data_type, + /*endianness=*/JXL_BIG_ENDIAN, + /*align=*/0}); + } + } + return formats; + } + Status EncodeFrame(const PackedPixelFile& ppf, const PackedFrame& frame, + std::vector* bytes) const override { + const PackedImage& color = frame.color; + const auto& ec_info = ppf.extra_channels_info; + JXL_RETURN_IF_ERROR(frame.extra_channels.size() == ec_info.size()); + for (const auto& ec : frame.extra_channels) { + if (ec.xsize != color.xsize || ec.ysize != color.ysize) { + return JXL_FAILURE("Extra channel and color size mismatch."); + } + if (ec.format.data_type != color.format.data_type || + ec.format.endianness != color.format.endianness) { + return JXL_FAILURE("Extra channel and color format mismatch."); + } + } + if (ppf.info.alpha_bits && + (ppf.info.bits_per_sample != ppf.info.alpha_bits)) { + return JXL_FAILURE("Alpha bit depth does not match image bit depth"); + } + for (const auto& it : ec_info) { + if (it.ec_info.bits_per_sample != ppf.info.bits_per_sample) { + return JXL_FAILURE( + "Extra channel bit depth does not match image bit depth"); + } + } + const char* kColorTypes[4] = {"GRAYSCALE", "GRAYSCALE_ALPHA", "RGB", + "RGB_ALPHA"}; + uint32_t maxval = (1u << ppf.info.bits_per_sample) - 1; + uint32_t depth = color.format.num_channels + ec_info.size(); + char header[kMaxHeaderSize]; + size_t pos = 0; + pos += snprintf(header + pos, kMaxHeaderSize - pos, + "P7\nWIDTH %" PRIuS "\nHEIGHT %" PRIuS + "\nDEPTH %u\n" + "MAXVAL %u\nTUPLTYPE %s\n", + color.xsize, color.ysize, depth, maxval, + kColorTypes[color.format.num_channels - 1]); + JXL_RETURN_IF_ERROR(pos < kMaxHeaderSize); + for (const auto& info : ec_info) { + pos += snprintf(header + pos, kMaxHeaderSize - pos, "TUPLTYPE %s\n", + ExtraChannelTypeName(info.ec_info.type).c_str()); + JXL_RETURN_IF_ERROR(pos < kMaxHeaderSize); + } + pos += snprintf(header + pos, kMaxHeaderSize - pos, "ENDHDR\n"); + JXL_RETURN_IF_ERROR(pos < kMaxHeaderSize); + size_t total_size = color.pixels_size; + for (const auto& ec : frame.extra_channels) { + total_size += ec.pixels_size; + } + bytes->resize(pos + total_size); + memcpy(bytes->data(), header, pos); + // If we have no extra channels, just copy color pixel data over. + if (frame.extra_channels.empty()) { + memcpy(bytes->data() + pos, reinterpret_cast(color.pixels()), + color.pixels_size); + return true; + } + // Interleave color and extra channels. + const uint8_t* in = reinterpret_cast(color.pixels()); + std::vector ec_in(frame.extra_channels.size()); + for (size_t i = 0; i < frame.extra_channels.size(); ++i) { + ec_in[i] = + reinterpret_cast(frame.extra_channels[i].pixels()); + } + uint8_t* out = bytes->data() + pos; + size_t pwidth = PackedImage::BitsPerChannel(color.format.data_type) / 8; + for (size_t y = 0; y < color.ysize; ++y) { + for (size_t x = 0; x < color.xsize; ++x) { + memcpy(out, in, color.pixel_stride()); + out += color.pixel_stride(); + in += color.pixel_stride(); + for (auto& p : ec_in) { + memcpy(out, p, pwidth); + out += pwidth; + p += pwidth; + } + } + } + return true; + } + Status EncodeExtraChannel(const PackedImage& image, size_t bits_per_sample, + std::vector* bytes) const override { + return true; + } + + private: + static std::string ExtraChannelTypeName(JxlExtraChannelType type) { + switch (type) { + case JXL_CHANNEL_ALPHA: + return std::string("Alpha"); + case JXL_CHANNEL_DEPTH: + return std::string("Depth"); + case JXL_CHANNEL_SPOT_COLOR: + return std::string("SpotColor"); + case JXL_CHANNEL_SELECTION_MASK: + return std::string("SelectionMask"); + case JXL_CHANNEL_BLACK: + return std::string("Black"); + case JXL_CHANNEL_CFA: + return std::string("CFA"); + case JXL_CHANNEL_THERMAL: + return std::string("Thermal"); + default: + return std::string("UNKNOWN"); + } + } +}; + +} // namespace + +std::unique_ptr GetPPMEncoder() { + return jxl::make_unique(); +} + +std::unique_ptr GetPNMEncoder() { + return jxl::make_unique(); +} + +std::unique_ptr GetPFMEncoder() { + return jxl::make_unique(); +} + +std::unique_ptr GetPGMEncoder() { + return jxl::make_unique(); +} + +std::unique_ptr GetPAMEncoder() { + return jxl::make_unique(); +} + +} // namespace extras +} // namespace jxl -- cgit v1.2.3