diff options
Diffstat (limited to 'third_party/jpeg-xl/lib/extras')
34 files changed, 564 insertions, 375 deletions
diff --git a/third_party/jpeg-xl/lib/extras/alpha_blend.cc b/third_party/jpeg-xl/lib/extras/alpha_blend.cc index 50c141c6fb..afb37b0a7d 100644 --- a/third_party/jpeg-xl/lib/extras/alpha_blend.cc +++ b/third_party/jpeg-xl/lib/extras/alpha_blend.cc @@ -12,7 +12,7 @@ namespace extras { namespace { -void AlphaBlend(PackedFrame* frame, float background[3]) { +void AlphaBlend(PackedFrame* frame, const float background[3]) { if (!frame) return; const PackedImage& im = frame->color; JxlPixelFormat format = im.format; @@ -20,7 +20,8 @@ void AlphaBlend(PackedFrame* frame, float background[3]) { return; } --format.num_channels; - PackedImage blended(im.xsize, im.ysize, format); + JXL_ASSIGN_OR_DIE(PackedImage blended, + PackedImage::Create(im.xsize, im.ysize, format)); // TODO(szabadka) SIMDify this and make it work for float16. for (size_t y = 0; y < im.ysize; ++y) { for (size_t x = 0; x < im.xsize; ++x) { @@ -48,7 +49,7 @@ void AlphaBlend(PackedFrame* frame, float background[3]) { } // namespace -void AlphaBlend(PackedPixelFile* ppf, float background[3]) { +void AlphaBlend(PackedPixelFile* ppf, const float background[3]) { if (!ppf || ppf->info.alpha_bits == 0) { return; } diff --git a/third_party/jpeg-xl/lib/extras/alpha_blend.h b/third_party/jpeg-xl/lib/extras/alpha_blend.h index 4d78e8681b..e49349393c 100644 --- a/third_party/jpeg-xl/lib/extras/alpha_blend.h +++ b/third_party/jpeg-xl/lib/extras/alpha_blend.h @@ -11,7 +11,7 @@ namespace jxl { namespace extras { -void AlphaBlend(PackedPixelFile* ppf, float background[3]); +void AlphaBlend(PackedPixelFile* ppf, const float background[3]); } // namespace extras } // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/codec.h b/third_party/jpeg-xl/lib/extras/codec.h index b7bc2a397e..9fd61503cb 100644 --- a/third_party/jpeg-xl/lib/extras/codec.h +++ b/third_party/jpeg-xl/lib/extras/codec.h @@ -43,7 +43,7 @@ JXL_INLINE Status SetFromBytes(const Span<const uint8_t> bytes, CodecInOut* io, orig_codec); } -Status Encode(const extras::PackedPixelFile& ppf, const extras::Codec codec, +Status Encode(const extras::PackedPixelFile& ppf, extras::Codec codec, std::vector<uint8_t>* bytes, ThreadPool* pool); Status Encode(const extras::PackedPixelFile& ppf, const std::string& pathname, diff --git a/third_party/jpeg-xl/lib/extras/codec_test.cc b/third_party/jpeg-xl/lib/extras/codec_test.cc index 4d8a60cb33..50d5e37bc9 100644 --- a/third_party/jpeg-xl/lib/extras/codec_test.cc +++ b/third_party/jpeg-xl/lib/extras/codec_test.cc @@ -40,12 +40,6 @@ using test::ThreadPoolForTests; namespace extras { namespace { -using ::testing::AllOf; -using ::testing::Contains; -using ::testing::Field; -using ::testing::IsEmpty; -using ::testing::SizeIs; - std::string ExtensionFromCodec(Codec codec, const bool is_gray, const bool has_alpha, const size_t bits_per_sample) { @@ -78,8 +72,8 @@ void VerifySameImage(const PackedImage& im0, size_t bits_per_sample0, }; double factor0 = get_factor(im0.format, bits_per_sample0); double factor1 = get_factor(im1.format, bits_per_sample1); - auto pixels0 = static_cast<const uint8_t*>(im0.pixels()); - auto pixels1 = static_cast<const uint8_t*>(im1.pixels()); + const auto* pixels0 = static_cast<const uint8_t*>(im0.pixels()); + const auto* pixels1 = static_cast<const uint8_t*>(im1.pixels()); auto rgba0 = test::ConvertToRGBA32(pixels0, im0.xsize, im0.ysize, im0.format, factor0); auto rgba1 = @@ -227,19 +221,23 @@ void CreateTestImage(const TestImageParams& params, PackedPixelFile* ppf) { ppf->info.exponent_bits_per_sample = params.bits_per_sample == 32 ? 8 : 0; ppf->info.num_color_channels = params.is_gray ? 1 : 3; ppf->info.alpha_bits = params.add_alpha ? params.bits_per_sample : 0; - ppf->info.alpha_premultiplied = (params.codec == Codec::kEXR); + ppf->info.alpha_premultiplied = TO_JXL_BOOL(params.codec == Codec::kEXR); JxlColorEncoding color_encoding = CreateTestColorEncoding(params.is_gray); ppf->icc = GenerateICC(color_encoding); ppf->color_encoding = color_encoding; - PackedFrame frame(params.xsize, params.ysize, params.PixelFormat()); + JXL_ASSIGN_OR_DIE( + PackedFrame frame, + PackedFrame::Create(params.xsize, params.ysize, params.PixelFormat())); FillPackedImage(params.bits_per_sample, &frame.color); if (params.add_extra_channels) { for (size_t i = 0; i < 7; ++i) { JxlPixelFormat ec_format = params.PixelFormat(); ec_format.num_channels = 1; - PackedImage ec(params.xsize, params.ysize, ec_format); + JXL_ASSIGN_OR_DIE( + PackedImage ec, + PackedImage::Create(params.xsize, params.ysize, ec_format)); FillPackedImage(params.bits_per_sample, &ec); frame.extra_channels.emplace_back(std::move(ec)); PackedExtraChannel pec; @@ -432,15 +430,17 @@ TEST(CodecTest, EncodeToPNG) { ASSERT_TRUE(extras::DecodeBytes(Bytes(original_png), ColorHints(), &ppf)); const JxlPixelFormat& format = ppf.frames.front().color.format; - ASSERT_THAT( - png_encoder->AcceptedFormats(), - Contains(AllOf(Field(&JxlPixelFormat::num_channels, format.num_channels), - Field(&JxlPixelFormat::data_type, format.data_type), - Field(&JxlPixelFormat::endianness, format.endianness)))); + const auto& format_matcher = [&format](const JxlPixelFormat& candidate) { + return (candidate.num_channels == format.num_channels) && + (candidate.data_type == format.data_type) && + (candidate.endianness == format.endianness); + }; + const auto formats = png_encoder->AcceptedFormats(); + ASSERT_TRUE(std::any_of(formats.begin(), formats.end(), format_matcher)); EncodedImage encoded_png; ASSERT_TRUE(png_encoder->Encode(ppf, &encoded_png, pool)); - EXPECT_THAT(encoded_png.icc, IsEmpty()); - ASSERT_THAT(encoded_png.bitstreams, SizeIs(1)); + EXPECT_TRUE(encoded_png.icc.empty()); + ASSERT_EQ(encoded_png.bitstreams.size(), 1); PackedPixelFile decoded_ppf; ASSERT_TRUE(extras::DecodeBytes(Bytes(encoded_png.bitstreams.front()), diff --git a/third_party/jpeg-xl/lib/extras/dec/apng.cc b/third_party/jpeg-xl/lib/extras/dec/apng.cc index f77dab77d1..8b0da06eb1 100644 --- a/third_party/jpeg-xl/lib/extras/dec/apng.cc +++ b/third_party/jpeg-xl/lib/extras/dec/apng.cc @@ -71,9 +71,7 @@ const png_byte kIgnoredPngChunks[] = { }; // Returns floating-point value from the PNG encoding (times 10^5). -static double F64FromU32(const uint32_t x) { - return static_cast<int32_t>(x) * 1E-5; -} +double F64FromU32(const uint32_t x) { return static_cast<int32_t>(x) * 1E-5; } Status DecodeSRGB(const unsigned char* payload, const size_t payload_size, JxlColorEncoding* color_encoding) { @@ -402,7 +400,8 @@ class BlobsReaderPNG { } if (pos + 2 >= encoded_end) return false; // Truncated base16 2; - uint32_t nibble0, nibble1; + uint32_t nibble0; + uint32_t nibble1; JXL_RETURN_IF_ERROR(DecodeNibble(pos[0], &nibble0)); JXL_RETURN_IF_ERROR(DecodeNibble(pos[1], &nibble1)); bytes->push_back(static_cast<uint8_t>((nibble0 << 4) + nibble1)); @@ -432,9 +431,22 @@ constexpr uint32_t kId_cHRM = 0x4D524863; constexpr uint32_t kId_eXIf = 0x66495865; struct APNGFrame { - std::vector<uint8_t> pixels; + APNGFrame() : pixels(nullptr, free) {} + std::unique_ptr<void, decltype(free)*> pixels; + size_t pixels_size = 0; std::vector<uint8_t*> rows; unsigned int w, h, delay_num, delay_den; + Status Resize(size_t new_size) { + if (new_size > pixels_size) { + pixels.reset(malloc(new_size)); + if (!pixels.get()) { + // TODO(szabadka): use specialized OOM error code + return JXL_FAILURE("Failed to allocate memory for image buffer"); + } + pixels_size = new_size; + } + return true; + } }; struct Reader { @@ -447,7 +459,7 @@ struct Reader { next += to_copy; return (len == to_copy); } - bool Eof() { return next == last; } + bool Eof() const { return next == last; } }; const unsigned long cMaxPNGSize = 1000000UL; @@ -463,10 +475,11 @@ void info_fn(png_structp png_ptr, png_infop info_ptr) { void row_fn(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass) { - APNGFrame* frame = (APNGFrame*)png_get_progressive_ptr(png_ptr); + APNGFrame* frame = + reinterpret_cast<APNGFrame*>(png_get_progressive_ptr(png_ptr)); JXL_CHECK(frame); JXL_CHECK(row_num < frame->rows.size()); - JXL_CHECK(frame->rows[row_num] < frame->pixels.data() + frame->pixels.size()); + JXL_CHECK(frame->rows[row_num] < frame->rows[0] + frame->pixels_size); png_progressive_combine_row(png_ptr, frame->rows[row_num], new_row); } @@ -494,12 +507,13 @@ int processing_start(png_structp& png_ptr, png_infop& info_ptr, void* frame_ptr, unsigned char header[8] = {137, 80, 78, 71, 13, 10, 26, 10}; // Cleanup prior decoder, if any. - png_destroy_read_struct(&png_ptr, &info_ptr, 0); + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); // Just in case. Not all versions on libpng wipe-out the pointers. png_ptr = nullptr; info_ptr = nullptr; - png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png_ptr = + png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); info_ptr = png_create_info_struct(png_ptr); if (!png_ptr || !info_ptr) return 1; @@ -508,18 +522,17 @@ int processing_start(png_structp& png_ptr, png_infop& info_ptr, void* frame_ptr, } png_set_keep_unknown_chunks(png_ptr, 1, kIgnoredPngChunks, - (int)sizeof(kIgnoredPngChunks) / 5); + static_cast<int>(sizeof(kIgnoredPngChunks) / 5)); png_set_crc_action(png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE); - png_set_progressive_read_fn(png_ptr, frame_ptr, info_fn, row_fn, NULL); + png_set_progressive_read_fn(png_ptr, frame_ptr, info_fn, row_fn, nullptr); png_process_data(png_ptr, info_ptr, header, 8); png_process_data(png_ptr, info_ptr, chunkIHDR.data(), chunkIHDR.size()); if (hasInfo) { - for (unsigned int i = 0; i < chunksInfo.size(); i++) { - png_process_data(png_ptr, info_ptr, chunksInfo[i].data(), - chunksInfo[i].size()); + for (auto& chunk : chunksInfo) { + png_process_data(png_ptr, info_ptr, chunk.data(), chunk.size()); } } return 0; @@ -575,8 +588,6 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, const SizeConstraints* constraints) { #if JPEGXL_ENABLE_APNG Reader r; - unsigned int id, j, w, h, w0, h0, x0, y0; - unsigned int delay_num, delay_den, dop, bop, rowbytes, imagesize; unsigned char sig[8]; png_structp png_ptr = nullptr; png_infop info_ptr = nullptr; @@ -588,7 +599,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, bool seenFctl = false; APNGFrame frameRaw = {}; uint32_t num_channels; - JxlPixelFormat format; + JxlPixelFormat format = {}; unsigned int bytes_per_pixel = 0; struct FrameInfo { @@ -604,7 +615,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, // Make sure png memory is released in any case. auto scope_guard = MakeScopeGuard([&]() { - png_destroy_read_struct(&png_ptr, &info_ptr, 0); + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); // Just in case. Not all versions on libpng wipe-out the pointers. png_ptr = nullptr; info_ptr = nullptr; @@ -616,7 +627,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, if (!r.Read(sig, 8) || memcmp(sig, png_signature, 8) != 0) { return false; } - id = read_chunk(&r, &chunkIHDR); + unsigned int id = read_chunk(&r, &chunkIHDR); ppf->info.exponent_bits_per_sample = 0; ppf->info.alpha_exponent_bits = 0; @@ -625,18 +636,22 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, ppf->frames.clear(); bool have_color = false; - bool have_cicp = false, have_iccp = false, have_srgb = false; + bool have_cicp = false; + bool have_iccp = false; + bool have_srgb = false; bool errorstate = true; if (id == kId_IHDR && chunkIHDR.size() == 25) { - x0 = 0; - y0 = 0; - delay_num = 1; - delay_den = 10; - dop = 0; - bop = 0; - - w0 = w = png_get_uint_32(chunkIHDR.data() + 8); - h0 = h = png_get_uint_32(chunkIHDR.data() + 12); + unsigned int x0 = 0; + unsigned int y0 = 0; + unsigned int delay_num = 1; + unsigned int delay_den = 10; + unsigned int dop = 0; + unsigned int bop = 0; + + unsigned int w = png_get_uint_32(chunkIHDR.data() + 8); + unsigned int h = png_get_uint_32(chunkIHDR.data() + 12); + unsigned int w0 = w; + unsigned int h0 = h; if (w > cMaxPNGSize || h > cMaxPNGSize) { return false; } @@ -648,8 +663,8 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, ppf->color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_SRGB; ppf->color_encoding.rendering_intent = JXL_RENDERING_INTENT_RELATIVE; - if (!processing_start(png_ptr, info_ptr, (void*)&frameRaw, hasInfo, - chunkIHDR, chunksInfo)) { + if (!processing_start(png_ptr, info_ptr, static_cast<void*>(&frameRaw), + hasInfo, chunkIHDR, chunksInfo)) { while (!r.Eof()) { id = read_chunk(&r, &chunk); if (!id) break; @@ -657,7 +672,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, if (id == kId_acTL && !hasInfo && !isAnimated) { isAnimated = true; - ppf->info.have_animation = true; + ppf->info.have_animation = JXL_TRUE; ppf->info.animation.tps_numerator = 1000; ppf->info.animation.tps_denominator = 1; } else if (id == kId_IEND || @@ -666,8 +681,10 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, if (!processing_finish(png_ptr, info_ptr, &ppf->metadata)) { // Allocates the frame buffer. uint32_t duration = delay_num * 1000 / delay_den; - frames.push_back(FrameInfo{PackedImage(w0, h0, format), duration, - x0, w0, y0, h0, dop, bop}); + JXL_ASSIGN_OR_RETURN(PackedImage image, + PackedImage::Create(w0, h0, format)); + frames.push_back(FrameInfo{std::move(image), duration, x0, w0, y0, + h0, dop, bop}); auto& frame = frames.back().data; for (size_t y = 0; y < h0; ++y) { memcpy(static_cast<uint8_t*>(frame.pixels()) + frame.stride * y, @@ -707,7 +724,8 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, if (hasInfo) { memcpy(chunkIHDR.data() + 8, chunk.data() + 12, 8); - if (processing_start(png_ptr, info_ptr, (void*)&frameRaw, hasInfo, + if (processing_start(png_ptr, info_ptr, + static_cast<void*>(&frameRaw), hasInfo, chunkIHDR, chunksInfo)) { break; } @@ -724,7 +742,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, int colortype = png_get_color_type(png_ptr, info_ptr); int png_bit_depth = png_get_bit_depth(png_ptr, info_ptr); ppf->info.bits_per_sample = png_bit_depth; - png_color_8p sigbits = NULL; + png_color_8p sigbits = nullptr; png_get_sBIT(png_ptr, info_ptr, &sigbits); if (colortype & 1) { // palette will actually be 8-bit regardless of the index bitdepth @@ -784,12 +802,18 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, } bytes_per_pixel = num_channels * (format.data_type == JXL_TYPE_UINT16 ? 2 : 1); - rowbytes = w * bytes_per_pixel; - imagesize = h * rowbytes; - frameRaw.pixels.resize(imagesize); + size_t rowbytes = w * bytes_per_pixel; + if (h > std::numeric_limits<size_t>::max() / rowbytes) { + return JXL_FAILURE("Image too big."); + } + size_t imagesize = h * rowbytes; + JXL_RETURN_IF_ERROR(frameRaw.Resize(imagesize)); frameRaw.rows.resize(h); - for (j = 0; j < h; j++) - frameRaw.rows[j] = frameRaw.pixels.data() + j * rowbytes; + for (size_t j = 0; j < h; j++) { + frameRaw.rows[j] = + reinterpret_cast<uint8_t*>(frameRaw.pixels.get()) + + j * rowbytes; + } if (processing_data(png_ptr, info_ptr, chunk.data(), chunk.size())) { break; @@ -813,6 +837,8 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, have_cicp = true; have_color = true; ppf->icc.clear(); + ppf->primary_color_representation = + PackedPixelFile::kColorEncodingIsPrimary; } } else if (!have_cicp && id == kId_iCCP) { if (processing_data(png_ptr, info_ptr, chunk.data(), chunk.size())) { @@ -830,6 +856,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, &profile, &proflen); if (ok && proflen) { ppf->icc.assign(profile, profile + proflen); + ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary; have_color = true; have_iccp = true; } else { @@ -922,7 +949,8 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, py0 + pys >= y0 + ysize && use_for_next_frame) { // If the new frame is contained within the old frame, we can pad the // new frame with zeros and not blend. - PackedImage new_data(pxs, pys, frame.data.format); + JXL_ASSIGN_OR_RETURN(PackedImage new_data, + PackedImage::Create(pxs, pys, frame.data.format)); memset(new_data.pixels(), 0, new_data.pixels_size); for (size_t y = 0; y < ysize; y++) { size_t bytes_per_pixel = @@ -944,7 +972,8 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, ppf->frames.emplace_back(std::move(new_data)); } else { // If all else fails, insert a placeholder blank frame with kReplace. - PackedImage blank(pxs, pys, frame.data.format); + JXL_ASSIGN_OR_RETURN(PackedImage blank, + PackedImage::Create(pxs, pys, frame.data.format)); memset(blank.pixels(), 0, blank.pixels_size); ppf->frames.emplace_back(std::move(blank)); auto& pframe = ppf->frames.back(); @@ -984,7 +1013,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, has_nontrivial_background && frame.dispose_op == DISPOSE_OP_BACKGROUND; } if (ppf->frames.empty()) return JXL_FAILURE("No frames decoded"); - ppf->frames.back().frame_info.is_last = true; + ppf->frames.back().frame_info.is_last = JXL_TRUE; return true; #else diff --git a/third_party/jpeg-xl/lib/extras/dec/color_description.cc b/third_party/jpeg-xl/lib/extras/dec/color_description.cc index 54f6aa4206..bf229632d0 100644 --- a/third_party/jpeg-xl/lib/extras/dec/color_description.cc +++ b/third_party/jpeg-xl/lib/extras/dec/color_description.cc @@ -9,6 +9,8 @@ #include <cmath> +#include "lib/jxl/base/common.h" + namespace jxl { namespace { @@ -19,49 +21,46 @@ struct EnumName { T value; }; -const EnumName<JxlColorSpace> kJxlColorSpaceNames[] = { - {"RGB", JXL_COLOR_SPACE_RGB}, - {"Gra", JXL_COLOR_SPACE_GRAY}, - {"XYB", JXL_COLOR_SPACE_XYB}, - {"CS?", JXL_COLOR_SPACE_UNKNOWN}, -}; - -const EnumName<JxlWhitePoint> kJxlWhitePointNames[] = { - {"D65", JXL_WHITE_POINT_D65}, - {"Cst", JXL_WHITE_POINT_CUSTOM}, - {"EER", JXL_WHITE_POINT_E}, - {"DCI", JXL_WHITE_POINT_DCI}, -}; - -const EnumName<JxlPrimaries> kJxlPrimariesNames[] = { - {"SRG", JXL_PRIMARIES_SRGB}, - {"Cst", JXL_PRIMARIES_CUSTOM}, - {"202", JXL_PRIMARIES_2100}, - {"DCI", JXL_PRIMARIES_P3}, -}; - -const EnumName<JxlTransferFunction> kJxlTransferFunctionNames[] = { - {"709", JXL_TRANSFER_FUNCTION_709}, - {"TF?", JXL_TRANSFER_FUNCTION_UNKNOWN}, - {"Lin", JXL_TRANSFER_FUNCTION_LINEAR}, - {"SRG", JXL_TRANSFER_FUNCTION_SRGB}, - {"PeQ", JXL_TRANSFER_FUNCTION_PQ}, - {"DCI", JXL_TRANSFER_FUNCTION_DCI}, - {"HLG", JXL_TRANSFER_FUNCTION_HLG}, - {"", JXL_TRANSFER_FUNCTION_GAMMA}, -}; - -const EnumName<JxlRenderingIntent> kJxlRenderingIntentNames[] = { - {"Per", JXL_RENDERING_INTENT_PERCEPTUAL}, - {"Rel", JXL_RENDERING_INTENT_RELATIVE}, - {"Sat", JXL_RENDERING_INTENT_SATURATION}, - {"Abs", JXL_RENDERING_INTENT_ABSOLUTE}, -}; - -template <typename T> -Status ParseEnum(const std::string& token, const EnumName<T>* enum_values, - size_t enum_len, T* value) { - for (size_t i = 0; i < enum_len; i++) { +constexpr auto kJxlColorSpaceNames = + to_array<EnumName<JxlColorSpace>>({{"RGB", JXL_COLOR_SPACE_RGB}, + {"Gra", JXL_COLOR_SPACE_GRAY}, + {"XYB", JXL_COLOR_SPACE_XYB}, + {"CS?", JXL_COLOR_SPACE_UNKNOWN}}); + +constexpr auto kJxlWhitePointNames = + to_array<EnumName<JxlWhitePoint>>({{"D65", JXL_WHITE_POINT_D65}, + {"Cst", JXL_WHITE_POINT_CUSTOM}, + {"EER", JXL_WHITE_POINT_E}, + {"DCI", JXL_WHITE_POINT_DCI}}); + +constexpr auto kJxlPrimariesNames = + to_array<EnumName<JxlPrimaries>>({{"SRG", JXL_PRIMARIES_SRGB}, + {"Cst", JXL_PRIMARIES_CUSTOM}, + {"202", JXL_PRIMARIES_2100}, + {"DCI", JXL_PRIMARIES_P3}}); + +constexpr auto kJxlTransferFunctionNames = + to_array<EnumName<JxlTransferFunction>>( + {{"709", JXL_TRANSFER_FUNCTION_709}, + {"TF?", JXL_TRANSFER_FUNCTION_UNKNOWN}, + {"Lin", JXL_TRANSFER_FUNCTION_LINEAR}, + {"SRG", JXL_TRANSFER_FUNCTION_SRGB}, + {"PeQ", JXL_TRANSFER_FUNCTION_PQ}, + {"DCI", JXL_TRANSFER_FUNCTION_DCI}, + {"HLG", JXL_TRANSFER_FUNCTION_HLG}, + {"", JXL_TRANSFER_FUNCTION_GAMMA}}); + +constexpr auto kJxlRenderingIntentNames = + to_array<EnumName<JxlRenderingIntent>>( + {{"Per", JXL_RENDERING_INTENT_PERCEPTUAL}, + {"Rel", JXL_RENDERING_INTENT_RELATIVE}, + {"Sat", JXL_RENDERING_INTENT_SATURATION}, + {"Abs", JXL_RENDERING_INTENT_ABSOLUTE}}); + +template <typename T, size_t N> +Status ParseEnum(const std::string& token, + const std::array<EnumName<T>, N>& enum_values, T* value) { + for (size_t i = 0; i < enum_values.size(); i++) { if (enum_values[i].name == token) { *value = enum_values[i].value; return true; @@ -69,9 +68,6 @@ Status ParseEnum(const std::string& token, const EnumName<T>* enum_values, } return false; } -#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0])) -#define PARSE_ENUM(type, token, value) \ - ParseEnum<type>(token, k##type##Names, ARRAY_SIZE(k##type##Names), value) class Tokenizer { public: @@ -122,7 +118,7 @@ Status ParseColorSpace(Tokenizer* tokenizer, JxlColorEncoding* c) { std::string str; JXL_RETURN_IF_ERROR(tokenizer->Next(&str)); JxlColorSpace cs; - if (PARSE_ENUM(JxlColorSpace, str, &cs)) { + if (ParseEnum(str, kJxlColorSpaceNames, &cs)) { c->color_space = cs; return true; } @@ -139,7 +135,7 @@ Status ParseWhitePoint(Tokenizer* tokenizer, JxlColorEncoding* c) { std::string str; JXL_RETURN_IF_ERROR(tokenizer->Next(&str)); - if (PARSE_ENUM(JxlWhitePoint, str, &c->white_point)) return true; + if (ParseEnum(str, kJxlWhitePointNames, &c->white_point)) return true; Tokenizer xy_tokenizer(&str, ';'); c->white_point = JXL_WHITE_POINT_CUSTOM; @@ -157,7 +153,7 @@ Status ParsePrimaries(Tokenizer* tokenizer, JxlColorEncoding* c) { std::string str; JXL_RETURN_IF_ERROR(tokenizer->Next(&str)); - if (PARSE_ENUM(JxlPrimaries, str, &c->primaries)) return true; + if (ParseEnum(str, kJxlPrimariesNames, &c->primaries)) return true; Tokenizer xy_tokenizer(&str, ';'); JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_red_xy + 0)); @@ -174,7 +170,8 @@ Status ParsePrimaries(Tokenizer* tokenizer, JxlColorEncoding* c) { Status ParseRenderingIntent(Tokenizer* tokenizer, JxlColorEncoding* c) { std::string str; JXL_RETURN_IF_ERROR(tokenizer->Next(&str)); - if (PARSE_ENUM(JxlRenderingIntent, str, &c->rendering_intent)) return true; + if (ParseEnum(str, kJxlRenderingIntentNames, &c->rendering_intent)) + return true; return JXL_FAILURE("Invalid RenderingIntent %s\n", str.c_str()); } @@ -189,7 +186,7 @@ Status ParseTransferFunction(Tokenizer* tokenizer, JxlColorEncoding* c) { std::string str; JXL_RETURN_IF_ERROR(tokenizer->Next(&str)); - if (PARSE_ENUM(JxlTransferFunction, str, &c->transfer_function)) { + if (ParseEnum(str, kJxlTransferFunctionNames, &c->transfer_function)) { return true; } diff --git a/third_party/jpeg-xl/lib/extras/dec/color_hints.cc b/third_party/jpeg-xl/lib/extras/dec/color_hints.cc index 5c6d7b84a0..b1edd9f6ea 100644 --- a/third_party/jpeg-xl/lib/extras/dec/color_hints.cc +++ b/third_party/jpeg-xl/lib/extras/dec/color_hints.cc @@ -43,20 +43,21 @@ Status ApplyColorHints(const ColorHints& color_hints, } else if (key == "icc") { const uint8_t* data = reinterpret_cast<const uint8_t*>(value.data()); std::vector<uint8_t> icc(data, data + value.size()); - ppf->icc.swap(icc); + ppf->icc = std::move(icc); + ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary; got_color_space = true; } else if (key == "exif") { const uint8_t* data = reinterpret_cast<const uint8_t*>(value.data()); std::vector<uint8_t> blob(data, data + value.size()); - ppf->metadata.exif.swap(blob); + ppf->metadata.exif = std::move(blob); } else if (key == "xmp") { const uint8_t* data = reinterpret_cast<const uint8_t*>(value.data()); std::vector<uint8_t> blob(data, data + value.size()); - ppf->metadata.xmp.swap(blob); + ppf->metadata.xmp = std::move(blob); } else if (key == "jumbf") { const uint8_t* data = reinterpret_cast<const uint8_t*>(value.data()); std::vector<uint8_t> blob(data, data + value.size()); - ppf->metadata.jumbf.swap(blob); + ppf->metadata.jumbf = std::move(blob); } else { JXL_WARNING("Ignoring %s hint", key.c_str()); } diff --git a/third_party/jpeg-xl/lib/extras/dec/decode.cc b/third_party/jpeg-xl/lib/extras/dec/decode.cc index b3ca711bb2..3546cb65c0 100644 --- a/third_party/jpeg-xl/lib/extras/dec/decode.cc +++ b/third_party/jpeg-xl/lib/extras/dec/decode.cc @@ -35,7 +35,8 @@ std::string GetExtension(const std::string& path) { } // namespace -Codec CodecFromPath(std::string path, size_t* JXL_RESTRICT bits_per_sample, +Codec CodecFromPath(const std::string& path, + size_t* JXL_RESTRICT bits_per_sample, std::string* extension) { std::string ext = GetExtension(path); if (extension) { @@ -98,7 +99,7 @@ Status DecodeBytes(const Span<const uint8_t> bytes, *ppf = extras::PackedPixelFile(); // Default values when not set by decoders. - ppf->info.uses_original_profile = true; + ppf->info.uses_original_profile = JXL_TRUE; ppf->info.orientation = JXL_ORIENT_IDENTITY; const auto choose_codec = [&]() -> Codec { @@ -116,6 +117,7 @@ Status DecodeBytes(const Span<const uint8_t> bytes, dparams.accepted_formats.push_back( {num_channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, /*align=*/0}); } + dparams.output_bitdepth.type = JXL_BIT_DEPTH_FROM_CODESTREAM; size_t decoded_bytes; if (DecodeImageJXL(bytes.data(), bytes.size(), dparams, &decoded_bytes, ppf) && diff --git a/third_party/jpeg-xl/lib/extras/dec/decode.h b/third_party/jpeg-xl/lib/extras/dec/decode.h index 0d7dfcbef2..1a90f4c6a3 100644 --- a/third_party/jpeg-xl/lib/extras/dec/decode.h +++ b/third_party/jpeg-xl/lib/extras/dec/decode.h @@ -40,7 +40,7 @@ bool CanDecode(Codec codec); // If and only if extension is ".pfm", *bits_per_sample is updated to 32 so // that Encode() would encode to PFM instead of PPM. -Codec CodecFromPath(std::string path, +Codec CodecFromPath(const std::string& path, size_t* JXL_RESTRICT bits_per_sample = nullptr, std::string* extension = nullptr); diff --git a/third_party/jpeg-xl/lib/extras/dec/exr.cc b/third_party/jpeg-xl/lib/extras/dec/exr.cc index 821e0f4b21..d5903155e9 100644 --- a/third_party/jpeg-xl/lib/extras/dec/exr.cc +++ b/third_party/jpeg-xl/lib/extras/dec/exr.cc @@ -121,7 +121,12 @@ Status DecodeImageEXR(Span<const uint8_t> bytes, const ColorHints& color_hints, }; ppf->frames.clear(); // Allocates the frame buffer. - ppf->frames.emplace_back(image_size.x, image_size.y, format); + { + JXL_ASSIGN_OR_RETURN( + PackedFrame frame, + PackedFrame::Create(image_size.x, image_size.y, format)); + ppf->frames.emplace_back(std::move(frame)); + } const auto& frame = ppf->frames.back(); const int row_size = input.dataWindow().size().x + 1; @@ -188,7 +193,7 @@ Status DecodeImageEXR(Span<const uint8_t> bytes, const ColorHints& color_hints, if (has_alpha) { ppf->info.alpha_bits = kExrAlphaBits; ppf->info.alpha_exponent_bits = ppf->info.exponent_bits_per_sample; - ppf->info.alpha_premultiplied = true; + ppf->info.alpha_premultiplied = JXL_TRUE; } ppf->info.intensity_target = intensity_target; return true; diff --git a/third_party/jpeg-xl/lib/extras/dec/gif.cc b/third_party/jpeg-xl/lib/extras/dec/gif.cc index 3d963941c0..3f89d460b8 100644 --- a/third_party/jpeg-xl/lib/extras/dec/gif.cc +++ b/third_party/jpeg-xl/lib/extras/dec/gif.cc @@ -50,8 +50,10 @@ void ensure_have_alpha(PackedFrame* frame) { /*endianness=*/JXL_NATIVE_ENDIAN, /*align=*/0, }; - frame->extra_channels.emplace_back(frame->color.xsize, frame->color.ysize, - alpha_format); + JXL_ASSIGN_OR_DIE(PackedImage image, + PackedImage::Create(frame->color.xsize, frame->color.ysize, + alpha_format)); + frame->extra_channels.emplace_back(std::move(image)); // We need to set opaque-by-default. std::fill_n(static_cast<uint8_t*>(frame->extra_channels[0].pixels()), frame->color.xsize * frame->color.ysize, 255u); @@ -136,7 +138,7 @@ Status DecodeImageGIF(Span<const uint8_t> bytes, const ColorHints& color_hints, } if (gif->ImageCount > 1) { - ppf->info.have_animation = true; + ppf->info.have_animation = JXL_TRUE; // Delays in GIF are specified in 100ths of a second. ppf->info.animation.tps_numerator = 100; ppf->info.animation.tps_denominator = 1; @@ -186,7 +188,9 @@ Status DecodeImageGIF(Span<const uint8_t> bytes, const ColorHints& color_hints, } const PackedRgba background_rgba{background_color.Red, background_color.Green, background_color.Blue, 0}; - PackedFrame canvas(gif->SWidth, gif->SHeight, canvas_format); + JXL_ASSIGN_OR_RETURN( + PackedFrame canvas, + PackedFrame::Create(gif->SWidth, gif->SHeight, canvas_format)); std::fill_n(static_cast<PackedRgba*>(canvas.color.pixels()), canvas.color.xsize * canvas.color.ysize, background_rgba); Rect canvas_rect{0, 0, canvas.color.xsize, canvas.color.ysize}; @@ -230,8 +234,14 @@ Status DecodeImageGIF(Span<const uint8_t> bytes, const ColorHints& color_hints, } // Allocates the frame buffer. - ppf->frames.emplace_back(total_rect.xsize(), total_rect.ysize(), - packed_frame_format); + { + JXL_ASSIGN_OR_RETURN( + PackedFrame frame, + PackedFrame::Create(total_rect.xsize(), total_rect.ysize(), + packed_frame_format)); + ppf->frames.emplace_back(std::move(frame)); + } + PackedFrame* frame = &ppf->frames.back(); // We cannot tell right from the start whether there will be a @@ -301,8 +311,10 @@ Status DecodeImageGIF(Span<const uint8_t> bytes, const ColorHints& color_hints, } // Update the canvas by creating a copy first. - PackedImage new_canvas_image(canvas.color.xsize, canvas.color.ysize, - canvas.color.format); + JXL_ASSIGN_OR_RETURN( + PackedImage new_canvas_image, + PackedImage::Create(canvas.color.xsize, canvas.color.ysize, + canvas.color.format)); memcpy(new_canvas_image.pixels(), canvas.color.pixels(), new_canvas_image.pixels_size); for (size_t y = 0, byte_index = 0; y < image_rect.ysize(); ++y) { diff --git a/third_party/jpeg-xl/lib/extras/dec/jpegli.cc b/third_party/jpeg-xl/lib/extras/dec/jpegli.cc index ffa1b79c25..443dfe86ba 100644 --- a/third_party/jpeg-xl/lib/extras/dec/jpegli.cc +++ b/third_party/jpeg-xl/lib/extras/dec/jpegli.cc @@ -27,7 +27,7 @@ constexpr unsigned char kExifSignature[6] = {0x45, 0x78, 0x69, constexpr int kExifMarker = JPEG_APP0 + 1; constexpr int kICCMarker = JPEG_APP0 + 2; -static inline bool IsJPG(const std::vector<uint8_t>& bytes) { +inline bool IsJPG(const std::vector<uint8_t>& bytes) { if (bytes.size() < 2) return false; if (bytes[0] != 0xFF || bytes[1] != 0xD8) return false; return true; @@ -188,7 +188,11 @@ Status DecodeJpeg(const std::vector<uint8_t>& compressed, } else if (dparams.force_grayscale) { cinfo.out_color_space = JCS_GRAYSCALE; } - if (!ReadICCProfile(&cinfo, &ppf->icc)) { + if (ReadICCProfile(&cinfo, &ppf->icc)) { + ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary; + } else { + ppf->primary_color_representation = + PackedPixelFile::kColorEncodingIsPrimary; ppf->icc.clear(); // Default to SRGB ppf->color_encoding.color_space = @@ -214,7 +218,7 @@ Status DecodeJpeg(const std::vector<uint8_t>& compressed, } else { return failure("unsupported data type"); } - ppf->info.uses_original_profile = true; + ppf->info.uses_original_profile = JXL_TRUE; // No alpha in JPG ppf->info.alpha_bits = 0; @@ -227,8 +231,8 @@ Status DecodeJpeg(const std::vector<uint8_t>& compressed, if (dparams.num_colors > 0) { cinfo.quantize_colors = TRUE; cinfo.desired_number_of_colors = dparams.num_colors; - cinfo.two_pass_quantize = dparams.two_pass_quant; - cinfo.dither_mode = (J_DITHER_MODE)dparams.dither_mode; + cinfo.two_pass_quantize = static_cast<boolean>(dparams.two_pass_quant); + cinfo.dither_mode = static_cast<J_DITHER_MODE>(dparams.dither_mode); } jpegli_start_decompress(&cinfo); @@ -242,7 +246,12 @@ Status DecodeJpeg(const std::vector<uint8_t>& compressed, }; ppf->frames.clear(); // Allocates the frame buffer. - ppf->frames.emplace_back(cinfo.image_width, cinfo.image_height, format); + { + JXL_ASSIGN_OR_RETURN( + PackedFrame frame, + PackedFrame::Create(cinfo.image_width, cinfo.image_height, format)); + ppf->frames.emplace_back(std::move(frame)); + } const auto& frame = ppf->frames.back(); JXL_ASSERT(sizeof(JSAMPLE) * cinfo.out_color_components * cinfo.image_width <= diff --git a/third_party/jpeg-xl/lib/extras/dec/jpg.cc b/third_party/jpeg-xl/lib/extras/dec/jpg.cc index 3c8a4bccfe..4a3e0d3b21 100644 --- a/third_party/jpeg-xl/lib/extras/dec/jpg.cc +++ b/third_party/jpeg-xl/lib/extras/dec/jpg.cc @@ -34,7 +34,7 @@ constexpr unsigned char kExifSignature[6] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00}; constexpr int kExifMarker = JPEG_APP0 + 1; -static inline bool IsJPG(const Span<const uint8_t> bytes) { +inline bool IsJPG(const Span<const uint8_t> bytes) { if (bytes.size() < 2) return false; if (bytes[0] != 0xFF || bytes[1] != 0xD8) return false; return true; @@ -242,7 +242,11 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes, if (nbcomp != 1 && nbcomp != 3) { return failure("unsupported number of components in JPEG"); } - if (!ReadICCProfile(&cinfo, &ppf->icc)) { + if (ReadICCProfile(&cinfo, &ppf->icc)) { + ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary; + } else { + ppf->primary_color_representation = + PackedPixelFile::kColorEncodingIsPrimary; ppf->icc.clear(); // Default to SRGB // Actually, (cinfo.output_components == nbcomp) will be checked after @@ -266,7 +270,7 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes, ppf->info.bits_per_sample = BITS_IN_JSAMPLE; JXL_ASSERT(BITS_IN_JSAMPLE == 8 || BITS_IN_JSAMPLE == 16); ppf->info.exponent_bits_per_sample = 0; - ppf->info.uses_original_profile = true; + ppf->info.uses_original_profile = JXL_TRUE; // No alpha in JPG ppf->info.alpha_bits = 0; @@ -278,8 +282,8 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes, if (dparams && dparams->num_colors > 0) { cinfo.quantize_colors = TRUE; cinfo.desired_number_of_colors = dparams->num_colors; - cinfo.two_pass_quantize = dparams->two_pass_quant; - cinfo.dither_mode = (J_DITHER_MODE)dparams->dither_mode; + cinfo.two_pass_quantize = static_cast<boolean>(dparams->two_pass_quant); + cinfo.dither_mode = static_cast<J_DITHER_MODE>(dparams->dither_mode); } jpeg_start_decompress(&cinfo); @@ -295,19 +299,25 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes, }; ppf->frames.clear(); // Allocates the frame buffer. - ppf->frames.emplace_back(cinfo.image_width, cinfo.image_height, format); + { + JXL_ASSIGN_OR_RETURN( + PackedFrame frame, + PackedFrame::Create(cinfo.image_width, cinfo.image_height, format)); + ppf->frames.emplace_back(std::move(frame)); + } const auto& frame = ppf->frames.back(); JXL_ASSERT(sizeof(JSAMPLE) * cinfo.out_color_components * cinfo.image_width <= frame.color.stride); if (cinfo.quantize_colors) { - jxl::msan::UnpoisonMemory(cinfo.colormap, cinfo.out_color_components * - sizeof(cinfo.colormap[0])); + JSAMPLE** colormap = cinfo.colormap; + jxl::msan::UnpoisonMemory(reinterpret_cast<void*>(colormap), + cinfo.out_color_components * sizeof(JSAMPLE*)); for (int c = 0; c < cinfo.out_color_components; ++c) { jxl::msan::UnpoisonMemory( - cinfo.colormap[c], - cinfo.actual_number_of_colors * sizeof(cinfo.colormap[c][0])); + reinterpret_cast<void*>(colormap[c]), + cinfo.actual_number_of_colors * sizeof(JSAMPLE)); } } for (size_t y = 0; y < cinfo.image_height; ++y) { diff --git a/third_party/jpeg-xl/lib/extras/dec/jxl.cc b/third_party/jpeg-xl/lib/extras/dec/jxl.cc index f3e62c970a..5b7fa03f02 100644 --- a/third_party/jpeg-xl/lib/extras/dec/jxl.cc +++ b/third_party/jpeg-xl/lib/extras/dec/jxl.cc @@ -16,20 +16,23 @@ #include "lib/extras/dec/color_description.h" #include "lib/jxl/base/exif.h" #include "lib/jxl/base/printf_macros.h" +#include "lib/jxl/base/status.h" namespace jxl { namespace extras { namespace { struct BoxProcessor { - BoxProcessor(JxlDecoder* dec) : dec_(dec) { Reset(); } + explicit BoxProcessor(JxlDecoder* dec) : dec_(dec) { Reset(); } void InitializeOutput(std::vector<uint8_t>* out) { + JXL_ASSERT(out != nullptr); box_data_ = out; AddMoreOutput(); } bool AddMoreOutput() { + JXL_ASSERT(box_data_ != nullptr); Flush(); static const size_t kBoxOutputChunkSize = 1 << 16; box_data_->resize(box_data_->size() + kBoxOutputChunkSize); @@ -126,7 +129,7 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size, return false; } - JxlPixelFormat format; + JxlPixelFormat format = {}; // Initialize to calm down clang-tidy. std::vector<JxlPixelFormat> accepted_formats = dparams.accepted_formats; JxlColorEncoding color_encoding; @@ -177,18 +180,18 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size, return false; } if (jpeg_bytes == nullptr) { - if (JXL_DEC_SUCCESS != - JxlDecoderSetRenderSpotcolors(dec, dparams.render_spotcolors)) { + if (JXL_DEC_SUCCESS != JxlDecoderSetRenderSpotcolors( + dec, TO_JXL_BOOL(dparams.render_spotcolors))) { fprintf(stderr, "JxlDecoderSetRenderSpotColors failed\n"); return false; } - if (JXL_DEC_SUCCESS != - JxlDecoderSetKeepOrientation(dec, dparams.keep_orientation)) { + if (JXL_DEC_SUCCESS != JxlDecoderSetKeepOrientation( + dec, TO_JXL_BOOL(dparams.keep_orientation))) { fprintf(stderr, "JxlDecoderSetKeepOrientation failed\n"); return false; } - if (JXL_DEC_SUCCESS != - JxlDecoderSetUnpremultiplyAlpha(dec, dparams.unpremultiply_alpha)) { + if (JXL_DEC_SUCCESS != JxlDecoderSetUnpremultiplyAlpha( + dec, TO_JXL_BOOL(dparams.unpremultiply_alpha))) { fprintf(stderr, "JxlDecoderSetUnpremultiplyAlpha failed\n"); return false; } @@ -267,6 +270,7 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size, return false; } } else if (status == JXL_DEC_JPEG_NEED_MORE_OUTPUT) { + JXL_ASSERT(jpeg_bytes != nullptr); // Help clang-tidy. // Decoded a chunk to JPEG. size_t used_jpeg_output = jpeg_data_chunk.size() - JxlDecoderReleaseJPEGBuffer(dec); @@ -309,7 +313,7 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size, } else { if (dparams.unpremultiply_alpha) { // Mark in the basic info that alpha was unpremultiplied. - ppf->info.alpha_premultiplied = false; + ppf->info.alpha_premultiplied = JXL_FALSE; } } bool alpha_found = false; @@ -327,7 +331,8 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size, } std::string name(eci.name_length + 1, 0); if (JXL_DEC_SUCCESS != - JxlDecoderGetExtraChannelName(dec, i, &name[0], name.size())) { + JxlDecoderGetExtraChannelName( + dec, i, const_cast<char*>(name.data()), name.size())) { fprintf(stderr, "JxlDecoderGetExtraChannelName failed\n"); return false; } @@ -351,24 +356,26 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size, } size_t icc_size = 0; JxlColorProfileTarget target = JXL_COLOR_PROFILE_TARGET_DATA; - ppf->color_encoding.color_space = JXL_COLOR_SPACE_UNKNOWN; - if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsEncodedProfile( - dec, target, &ppf->color_encoding) || - dparams.need_icc) { - // only get ICC if it is not an Enum color encoding - if (JXL_DEC_SUCCESS != - JxlDecoderGetICCProfileSize(dec, target, &icc_size)) { - fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n"); - } - if (icc_size != 0) { - ppf->icc.resize(icc_size); - if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsICCProfile( - dec, target, ppf->icc.data(), icc_size)) { - fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n"); - return false; - } + if (JXL_DEC_SUCCESS != + JxlDecoderGetICCProfileSize(dec, target, &icc_size)) { + fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n"); + } + if (icc_size != 0) { + ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary; + ppf->icc.resize(icc_size); + if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsICCProfile( + dec, target, ppf->icc.data(), icc_size)) { + fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n"); + return false; } } + if (JXL_DEC_SUCCESS == JxlDecoderGetColorAsEncodedProfile( + dec, target, &ppf->color_encoding)) { + ppf->primary_color_representation = + PackedPixelFile::kColorEncodingIsPrimary; + } else { + ppf->color_encoding.color_space = JXL_COLOR_SPACE_UNKNOWN; + } icc_size = 0; target = JXL_COLOR_PROFILE_TARGET_ORIGINAL; if (JXL_DEC_SUCCESS != @@ -385,14 +392,21 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size, } } } else if (status == JXL_DEC_FRAME) { - jxl::extras::PackedFrame frame(ppf->info.xsize, ppf->info.ysize, format); + auto frame_or = jxl::extras::PackedFrame::Create(ppf->info.xsize, + ppf->info.ysize, format); + if (!frame_or.status()) { + fprintf(stderr, "Failed to create image frame."); + return false; + } + jxl::extras::PackedFrame frame = std::move(frame_or).value(); if (JXL_DEC_SUCCESS != JxlDecoderGetFrameHeader(dec, &frame.frame_info)) { fprintf(stderr, "JxlDecoderGetFrameHeader failed\n"); return false; } frame.name.resize(frame.frame_info.name_length + 1, 0); if (JXL_DEC_SUCCESS != - JxlDecoderGetFrameName(dec, &frame.name[0], frame.name.size())) { + JxlDecoderGetFrameName(dec, const_cast<char*>(frame.name.data()), + frame.name.size())) { fprintf(stderr, "JxlDecoderGetFrameName failed\n"); return false; } @@ -423,9 +437,16 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size, fprintf(stderr, "JxlDecoderPreviewOutBufferSize failed\n"); return false; } + auto preview_image_or = jxl::extras::PackedImage::Create( + ppf->info.preview.xsize, ppf->info.preview.ysize, format); + if (!preview_image_or.status()) { + fprintf(stderr, "Failed to create preview image\n"); + return false; + } + jxl::extras::PackedImage preview_image = + std::move(preview_image_or).value(); ppf->preview_frame = std::unique_ptr<jxl::extras::PackedFrame>( - new jxl::extras::PackedFrame(ppf->info.preview.xsize, - ppf->info.preview.ysize, format)); + new jxl::extras::PackedFrame(std::move(preview_image))); if (buffer_size != ppf->preview_frame->color.pixels_size) { fprintf(stderr, "Invalid out buffer size %" PRIuS " %" PRIuS "\n", buffer_size, ppf->preview_frame->color.pixels_size); @@ -492,8 +513,13 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size, JxlPixelFormat ec_format = format; ec_format.num_channels = 1; for (auto& eci : ppf->extra_channels_info) { - frame.extra_channels.emplace_back(jxl::extras::PackedImage( - ppf->info.xsize, ppf->info.ysize, ec_format)); + auto image = jxl::extras::PackedImage::Create( + ppf->info.xsize, ppf->info.ysize, ec_format); + if (!image.status()) { + fprintf(stderr, "Failed to create extra channel image\n"); + return false; + } + frame.extra_channels.emplace_back(std::move(image).value()); auto& ec = frame.extra_channels.back(); size_t buffer_size; if (JXL_DEC_SUCCESS != JxlDecoderExtraChannelBufferSize( diff --git a/third_party/jpeg-xl/lib/extras/dec/jxl.h b/third_party/jpeg-xl/lib/extras/dec/jxl.h index cbada1f6dd..5f4ed7f683 100644 --- a/third_party/jpeg-xl/lib/extras/dec/jxl.h +++ b/third_party/jpeg-xl/lib/extras/dec/jxl.h @@ -41,10 +41,6 @@ struct JXLDecompressParams { // Whether truncated input should be treated as an error. bool allow_partial_input = false; - // Set to true if an ICC profile has to be synthesized for Enum color - // encodings - bool need_icc = false; - // How many passes to decode at most. By default, decode everything. uint32_t max_passes = std::numeric_limits<uint32_t>::max(); diff --git a/third_party/jpeg-xl/lib/extras/dec/pgx.cc b/third_party/jpeg-xl/lib/extras/dec/pgx.cc index a99eb0f4ee..4499069d77 100644 --- a/third_party/jpeg-xl/lib/extras/dec/pgx.cc +++ b/third_party/jpeg-xl/lib/extras/dec/pgx.cc @@ -150,7 +150,7 @@ Status DecodeImagePGX(const Span<const uint8_t> bytes, const SizeConstraints* constraints) { Parser parser(bytes); HeaderPGX header = {}; - const uint8_t* pos; + const uint8_t* pos = nullptr; if (!parser.ParseHeader(&header, &pos)) return false; JXL_RETURN_IF_ERROR( VerifyDimensions(constraints, header.xsize, header.ysize)); @@ -165,7 +165,7 @@ Status DecodeImagePGX(const Span<const uint8_t> bytes, // Original data is uint, so exponent_bits_per_sample = 0. ppf->info.bits_per_sample = header.bits_per_sample; ppf->info.exponent_bits_per_sample = 0; - ppf->info.uses_original_profile = true; + ppf->info.uses_original_profile = JXL_TRUE; // No alpha in PGX ppf->info.alpha_bits = 0; @@ -188,7 +188,12 @@ Status DecodeImagePGX(const Span<const uint8_t> bytes, }; ppf->frames.clear(); // Allocates the frame buffer. - ppf->frames.emplace_back(header.xsize, header.ysize, format); + { + JXL_ASSIGN_OR_RETURN( + PackedFrame frame, + PackedFrame::Create(header.xsize, header.ysize, format)); + ppf->frames.emplace_back(std::move(frame)); + } const auto& frame = ppf->frames.back(); size_t pgx_remaining_size = bytes.data() + bytes.size() - pos; if (pgx_remaining_size < frame.color.pixels_size) { diff --git a/third_party/jpeg-xl/lib/extras/dec/pnm.cc b/third_party/jpeg-xl/lib/extras/dec/pnm.cc index 4c4618d41d..040c0bff81 100644 --- a/third_party/jpeg-xl/lib/extras/dec/pnm.cc +++ b/third_party/jpeg-xl/lib/extras/dec/pnm.cc @@ -9,6 +9,7 @@ #include <string.h> #include <cmath> +#include <cstdint> #include <mutex> #include "jxl/encode.h" @@ -55,8 +56,10 @@ class Parser { case 'f': header->is_gray = true; return ParseHeaderPFM(header, pos); + + default: + return false; } - return false; } // Exposed for testing @@ -160,11 +163,12 @@ class Parser { Status MatchString(const char* keyword, bool skipws = true) { const uint8_t* ppos = pos_; - while (*keyword) { + const uint8_t* kw = reinterpret_cast<const uint8_t*>(keyword); + while (*kw) { if (ppos >= end_) return JXL_FAILURE("PAM: unexpected end of input"); - if (*keyword != *ppos) return false; + if (*kw != *ppos) return false; ppos++; - keyword++; + kw++; } pos_ = ppos; if (skipws) { @@ -387,8 +391,8 @@ StatusOr<ChunkedPNMDecoder> ChunkedPNMDecoder::Init(const char* path) { const size_t num_channels = dec.header_.is_gray ? 1 : 3; const size_t bytes_per_pixel = num_channels * bytes_per_channel; size_t row_size = dec.header_.xsize * bytes_per_pixel; - if (header.ysize * row_size + dec.data_start_ < size) { - return JXL_FAILURE("Invalid ppm"); + if (size < header.ysize * row_size + dec.data_start_) { + return JXL_FAILURE("PNM file too small"); } return dec; } @@ -495,10 +499,18 @@ Status DecodeImagePNM(const Span<const uint8_t> bytes, }; const JxlPixelFormat ec_format{1, format.data_type, format.endianness, 0}; ppf->frames.clear(); - ppf->frames.emplace_back(header.xsize, header.ysize, format); + { + JXL_ASSIGN_OR_RETURN( + PackedFrame frame, + PackedFrame::Create(header.xsize, header.ysize, format)); + ppf->frames.emplace_back(std::move(frame)); + } auto* frame = &ppf->frames.back(); for (size_t i = 0; i < header.ec_types.size(); ++i) { - frame->extra_channels.emplace_back(header.xsize, header.ysize, ec_format); + JXL_ASSIGN_OR_RETURN( + PackedImage ec, + PackedImage::Create(header.xsize, header.ysize, ec_format)); + frame->extra_channels.emplace_back(std::move(ec)); } size_t pnm_remaining_size = bytes.data() + bytes.size() - pos; if (pnm_remaining_size < frame->color.pixels_size) { @@ -533,6 +545,9 @@ Status DecodeImagePNM(const Span<const uint8_t> bytes, } } } + if (ppf->info.exponent_bits_per_sample == 0) { + ppf->input_bitdepth.type = JXL_BIT_DEPTH_FROM_CODESTREAM; + } return true; } diff --git a/third_party/jpeg-xl/lib/extras/enc/apng.cc b/third_party/jpeg-xl/lib/extras/enc/apng.cc index 413a9c8081..40aa876e84 100644 --- a/third_party/jpeg-xl/lib/extras/enc/apng.cc +++ b/third_party/jpeg-xl/lib/extras/enc/apng.cc @@ -86,7 +86,7 @@ class APNGEncoder : public Encoder { std::vector<uint8_t>* bytes) const; }; -static void PngWrite(png_structp png_ptr, png_bytep data, png_size_t length) { +void PngWrite(png_structp png_ptr, png_bytep data, png_size_t length) { std::vector<uint8_t>* bytes = static_cast<std::vector<uint8_t>*>(png_get_io_ptr(png_ptr)); bytes->insert(bytes->end(), data, data + length); @@ -137,7 +137,7 @@ class BlobsWriterPNG { std::vector<std::string>* strings) { // Encoding: base16 with newline after 72 chars. const size_t base16_size = - 2 * bytes.size() + DivCeil(bytes.size(), size_t(36)) + 1; + 2 * bytes.size() + DivCeil(bytes.size(), static_cast<size_t>(36)) + 1; std::string base16; base16.reserve(base16_size); for (size_t i = 0; i < bytes.size(); ++i) { @@ -155,7 +155,7 @@ class BlobsWriterPNG { snprintf(header, sizeof(header), "\n%s\n%8" PRIuS, type.c_str(), bytes.size()); - strings->push_back(std::string(key)); + strings->emplace_back(key); strings->push_back(std::string(header) + base16); return true; } @@ -303,7 +303,7 @@ Status APNGEncoder::EncodePackedPixelFileToAPNG( out[i] = static_cast<uint8_t>(in[i] * mul + 0.5); } } else { - memcpy(&out[0], in, out_size); + memcpy(out.data(), in, out_size); } } else if (format.data_type == JXL_TYPE_UINT16) { if (ppf.info.bits_per_sample < 16 || @@ -317,20 +317,21 @@ Status APNGEncoder::EncodePackedPixelFileToAPNG( StoreBE16(static_cast<uint32_t>(val * mul + 0.5), p_out); } } else { - memcpy(&out[0], in, out_size); + memcpy(out.data(), in, out_size); } } png_structp png_ptr; png_infop info_ptr; - png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, + nullptr); if (!png_ptr) return JXL_FAILURE("Could not init png encoder"); info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) return JXL_FAILURE("Could not init png info struct"); - png_set_write_fn(png_ptr, bytes, PngWrite, NULL); + png_set_write_fn(png_ptr, bytes, PngWrite, nullptr); png_set_flush(png_ptr, 0); int width = xsize; @@ -344,11 +345,13 @@ Status APNGEncoder::EncodePackedPixelFileToAPNG( PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); if (count == 0) { - if (!ppf.icc.empty()) { - png_set_benign_errors(png_ptr, 1); - png_set_iCCP(png_ptr, info_ptr, "1", 0, ppf.icc.data(), ppf.icc.size()); - } else if (!MaybeAddSRGB(ppf.color_encoding, png_ptr, info_ptr)) { + if (!MaybeAddSRGB(ppf.color_encoding, png_ptr, info_ptr)) { MaybeAddCICP(ppf.color_encoding, png_ptr, info_ptr); + if (!ppf.icc.empty()) { + png_set_benign_errors(png_ptr, 1); + png_set_iCCP(png_ptr, info_ptr, "1", 0, ppf.icc.data(), + ppf.icc.size()); + } MaybeAddCHRM(ppf.color_encoding, png_ptr, info_ptr); MaybeAddGAMA(ppf.color_encoding, png_ptr, info_ptr); } @@ -404,7 +407,7 @@ Status APNGEncoder::EncodePackedPixelFileToAPNG( png_write_flush(png_ptr); const size_t pos = bytes->size(); - png_write_image(png_ptr, &rows[0]); + png_write_image(png_ptr, rows.data()); png_write_flush(png_ptr); if (count > 0) { std::vector<uint8_t> fdata(4); @@ -428,7 +431,7 @@ Status APNGEncoder::EncodePackedPixelFileToAPNG( count++; if (count == ppf.frames.size() || !ppf.info.have_animation) { - png_write_end(png_ptr, NULL); + png_write_end(png_ptr, nullptr); } png_destroy_write_struct(&png_ptr, &info_ptr); diff --git a/third_party/jpeg-xl/lib/extras/enc/encode.cc b/third_party/jpeg-xl/lib/extras/enc/encode.cc index 8c9a148b27..c5e22d8c7e 100644 --- a/third_party/jpeg-xl/lib/extras/enc/encode.cc +++ b/third_party/jpeg-xl/lib/extras/enc/encode.cc @@ -54,7 +54,7 @@ Status Encoder::VerifyBitDepth(JxlDataType data_type, uint32_t bits_per_sample, (bits_per_sample > 16 || exponent_bits > 5))) { return JXL_FAILURE( "Incompatible data_type %d and bit depth %u with exponent bits %u", - (int)data_type, bits_per_sample, exponent_bits); + static_cast<int>(data_type), bits_per_sample, exponent_bits); } return true; } diff --git a/third_party/jpeg-xl/lib/extras/enc/encode.h b/third_party/jpeg-xl/lib/extras/enc/encode.h index da5f509838..2502d9976b 100644 --- a/third_party/jpeg-xl/lib/extras/enc/encode.h +++ b/third_party/jpeg-xl/lib/extras/enc/encode.h @@ -56,7 +56,7 @@ class Encoder { // Any existing data in encoded_image is discarded. virtual Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image, - ThreadPool* pool = nullptr) const = 0; + ThreadPool* pool) const = 0; void SetOption(std::string name, std::string value) { options_[std::move(name)] = std::move(value); diff --git a/third_party/jpeg-xl/lib/extras/enc/exr.cc b/third_party/jpeg-xl/lib/extras/enc/exr.cc index d4005c3097..5a4f7d768c 100644 --- a/third_party/jpeg-xl/lib/extras/enc/exr.cc +++ b/third_party/jpeg-xl/lib/extras/enc/exr.cc @@ -84,7 +84,7 @@ Status EncodeImageEXR(const PackedImage& image, const JxlBasicInfo& info, const size_t xsize = info.xsize; const size_t ysize = info.ysize; const bool has_alpha = info.alpha_bits > 0; - const bool alpha_is_premultiplied = info.alpha_premultiplied; + const bool alpha_is_premultiplied = FROM_JXL_BOOL(info.alpha_premultiplied); if (info.num_color_channels != 3 || c_enc.color_space != JXL_COLOR_SPACE_RGB || @@ -177,7 +177,7 @@ class EXREncoder : public Encoder { return formats; } Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image, - ThreadPool* pool = nullptr) const override { + ThreadPool* pool) const override { JXL_RETURN_IF_ERROR(VerifyBasicInfo(ppf.info)); encoded_image->icc.clear(); encoded_image->bitstreams.clear(); diff --git a/third_party/jpeg-xl/lib/extras/enc/jpegli.cc b/third_party/jpeg-xl/lib/extras/enc/jpegli.cc index 7e1aa426df..aa10b584d0 100644 --- a/third_party/jpeg-xl/lib/extras/enc/jpegli.cc +++ b/third_party/jpeg-xl/lib/extras/enc/jpegli.cc @@ -34,6 +34,7 @@ #include "lib/jxl/color_encoding_internal.h" #include "lib/jxl/enc_xyb.h" #include "lib/jxl/image.h" +#include "lib/jxl/simd_util.h" namespace jxl { namespace extras { @@ -53,6 +54,10 @@ Status VerifyInput(const PackedPixelFile& ppf) { if (ppf.frames.size() != 1) { return JXL_FAILURE("JPEG input must have exactly one frame."); } + if (info.num_color_channels != 1 && info.num_color_channels != 3) { + return JXL_FAILURE("Invalid number of color channels %d", + info.num_color_channels); + } const PackedImage& image = ppf.frames[0].color; JXL_RETURN_IF_ERROR(Encoder::VerifyImageSize(image, info)); if (image.format.data_type == JXL_TYPE_FLOAT16) { @@ -71,7 +76,7 @@ Status VerifyInput(const PackedPixelFile& ppf) { Status GetColorEncoding(const PackedPixelFile& ppf, ColorEncoding* color_encoding) { - if (!ppf.icc.empty()) { + if (ppf.primary_color_representation == PackedPixelFile::kIccIsPrimary) { IccBytes icc = ppf.icc; JXL_RETURN_IF_ERROR( color_encoding->SetICC(std::move(icc), JxlGetDefaultCms())); @@ -122,12 +127,12 @@ Status WriteAppData(j_compress_ptr cinfo, return true; } -static constexpr int kICCMarker = 0xe2; +constexpr int kICCMarker = 0xe2; constexpr unsigned char kICCSignature[12] = { 0x49, 0x43, 0x43, 0x5F, 0x50, 0x52, 0x4F, 0x46, 0x49, 0x4C, 0x45, 0x00}; -static constexpr uint8_t kUnknownTf = 2; -static constexpr unsigned char kCICPTagSignature[4] = {0x63, 0x69, 0x63, 0x70}; -static constexpr size_t kCICPTagSize = 12; +constexpr uint8_t kUnknownTf = 2; +constexpr unsigned char kCICPTagSignature[4] = {0x63, 0x69, 0x63, 0x70}; +constexpr size_t kCICPTagSize = 12; bool FindCICPTag(const uint8_t* icc_data, size_t len, bool is_first_chunk, size_t* cicp_offset, size_t* cicp_length, uint8_t* cicp_tag, @@ -248,32 +253,48 @@ JpegliEndianness ConvertEndianness(JxlEndianness endianness) { } } -void ToFloatRow(const uint8_t* row_in, JxlPixelFormat format, size_t len, - float* row_out) { +void ToFloatRow(const uint8_t* row_in, JxlPixelFormat format, size_t xsize, + size_t c_out, float* row_out) { bool is_little_endian = (format.endianness == JXL_LITTLE_ENDIAN || (format.endianness == JXL_NATIVE_ENDIAN && IsLittleEndian())); static constexpr double kMul8 = 1.0 / 255.0; static constexpr double kMul16 = 1.0 / 65535.0; + const size_t c_in = format.num_channels; if (format.data_type == JXL_TYPE_UINT8) { - for (size_t x = 0; x < len; ++x) { - row_out[x] = row_in[x] * kMul8; + for (size_t x = 0; x < xsize; ++x) { + for (size_t c = 0; c < c_out; ++c) { + const size_t ix = c_in * x + c; + row_out[c_out * x + c] = row_in[ix] * kMul8; + } } } else if (format.data_type == JXL_TYPE_UINT16 && is_little_endian) { - for (size_t x = 0; x < len; ++x) { - row_out[x] = LoadLE16(&row_in[2 * x]) * kMul16; + for (size_t x = 0; x < xsize; ++x) { + for (size_t c = 0; c < c_out; ++c) { + const size_t ix = c_in * x + c; + row_out[c_out * x + c] = LoadLE16(&row_in[2 * ix]) * kMul16; + } } } else if (format.data_type == JXL_TYPE_UINT16 && !is_little_endian) { - for (size_t x = 0; x < len; ++x) { - row_out[x] = LoadBE16(&row_in[2 * x]) * kMul16; + for (size_t x = 0; x < xsize; ++x) { + for (size_t c = 0; c < c_out; ++c) { + const size_t ix = c_in * x + c; + row_out[c_out * x + c] = LoadBE16(&row_in[2 * ix]) * kMul16; + } } } else if (format.data_type == JXL_TYPE_FLOAT && is_little_endian) { - for (size_t x = 0; x < len; ++x) { - row_out[x] = LoadLEFloat(&row_in[4 * x]); + for (size_t x = 0; x < xsize; ++x) { + for (size_t c = 0; c < c_out; ++c) { + const size_t ix = c_in * x + c; + row_out[c_out * x + c] = LoadLEFloat(&row_in[4 * ix]); + } } } else if (format.data_type == JXL_TYPE_FLOAT && !is_little_endian) { - for (size_t x = 0; x < len; ++x) { - row_out[x] = LoadBEFloat(&row_in[4 * x]); + for (size_t x = 0; x < xsize; ++x) { + for (size_t c = 0; c < c_out; ++c) { + const size_t ix = c_in * x + c; + row_out[c_out * x + c] = LoadBEFloat(&row_in[4 * ix]); + } } } } @@ -352,9 +373,6 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings, ColorSpaceTransform c_transform(*JxlGetDefaultCms()); ColorEncoding xyb_encoding; if (jpeg_settings.xyb) { - if (ppf.info.num_color_channels != 3) { - return JXL_FAILURE("Only RGB input is supported in XYB mode."); - } if (HasICCProfile(jpeg_settings.app_data)) { return JXL_FAILURE("APP data ICC profile is not supported in XYB mode."); } @@ -374,11 +392,11 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings, unsigned char* output_buffer = nullptr; unsigned long output_size = 0; std::vector<uint8_t> row_bytes; - size_t rowlen = RoundUpTo(ppf.info.xsize, VectorSize()); + size_t rowlen = RoundUpTo(ppf.info.xsize, MaxVectorSize()); hwy::AlignedFreeUniquePtr<float[]> xyb_tmp = hwy::AllocateAligned<float>(6 * rowlen); hwy::AlignedFreeUniquePtr<float[]> premul_absorb = - hwy::AllocateAligned<float>(VectorSize() * 12); + hwy::AllocateAligned<float>(MaxVectorSize() * 12); ComputePremulAbsorb(255.0f, premul_absorb.get()); jpeg_compress_struct cinfo; @@ -401,6 +419,8 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings, cinfo.input_components == 1 ? JCS_GRAYSCALE : JCS_RGB; if (jpeg_settings.xyb) { jpegli_set_xyb_mode(&cinfo); + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; } else if (jpeg_settings.use_std_quant_tables) { jpegli_use_standard_quant_tables(&cinfo); } @@ -436,7 +456,7 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings, } } jpegli_enable_adaptive_quantization( - &cinfo, jpeg_settings.use_adaptive_quantization); + &cinfo, TO_JXL_BOOL(jpeg_settings.use_adaptive_quantization)); if (jpeg_settings.psnr_target > 0.0) { jpegli_set_psnr(&cinfo, jpeg_settings.psnr_target, jpeg_settings.search_tolerance, @@ -448,11 +468,11 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings, jpegli_set_distance(&cinfo, jpeg_settings.distance, TRUE); } jpegli_set_progressive_level(&cinfo, jpeg_settings.progressive_level); - cinfo.optimize_coding = jpeg_settings.optimize_coding; + cinfo.optimize_coding = TO_JXL_BOOL(jpeg_settings.optimize_coding); if (!jpeg_settings.app_data.empty()) { // Make sure jpegli_start_compress() does not write any APP markers. - cinfo.write_JFIF_header = false; - cinfo.write_Adobe_marker = false; + cinfo.write_JFIF_header = JXL_FALSE; + cinfo.write_Adobe_marker = JXL_FALSE; } const PackedImage& image = ppf.frames[0].color; if (jpeg_settings.xyb) { @@ -476,10 +496,10 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings, float* dst_buf = c_transform.BufDst(0); for (size_t y = 0; y < image.ysize; ++y) { // convert to float - ToFloatRow(&pixels[y * image.stride], image.format, 3 * image.xsize, - src_buf); + ToFloatRow(&pixels[y * image.stride], image.format, image.xsize, + info.num_color_channels, src_buf); // convert to linear srgb - if (!c_transform.Run(0, src_buf, dst_buf)) { + if (!c_transform.Run(0, src_buf, dst_buf, image.xsize)) { return false; } // deinterleave channels @@ -508,9 +528,9 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings, } } else { row_bytes.resize(image.stride); - if (cinfo.num_components == (int)image.format.num_channels) { + if (cinfo.num_components == static_cast<int>(image.format.num_channels)) { for (size_t y = 0; y < info.ysize; ++y) { - memcpy(&row_bytes[0], pixels + y * image.stride, image.stride); + memcpy(row_bytes.data(), pixels + y * image.stride, image.stride); JSAMPROW row[] = {row_bytes.data()}; jpegli_write_scanlines(&cinfo, row, 1); } diff --git a/third_party/jpeg-xl/lib/extras/enc/jpg.cc b/third_party/jpeg-xl/lib/extras/enc/jpg.cc index c34dc6c13f..de0228fc0d 100644 --- a/third_party/jpeg-xl/lib/extras/enc/jpg.cc +++ b/third_party/jpeg-xl/lib/extras/enc/jpg.cc @@ -23,6 +23,7 @@ #include <vector> #include "lib/extras/exif.h" +#include "lib/jxl/base/common.h" #include "lib/jxl/base/status.h" #include "lib/jxl/sanitizers.h" #if JPEGXL_ENABLE_SJPEG @@ -50,23 +51,21 @@ enum class JpegEncoder { kSJpeg, }; -#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0])) - // Popular jpeg scan scripts // The fields of the individual scans are: // comps_in_scan, component_index[], Ss, Se, Ah, Al -static constexpr jpeg_scan_info kScanScript1[] = { +constexpr auto kScanScript1 = to_array<jpeg_scan_info>({ {1, {0}, 0, 0, 0, 0}, // {1, {1}, 0, 0, 0, 0}, // {1, {2}, 0, 0, 0, 0}, // {1, {0}, 1, 8, 0, 0}, // {1, {0}, 9, 63, 0, 0}, // {1, {1}, 1, 63, 0, 0}, // - {1, {2}, 1, 63, 0, 0}, // -}; -static constexpr size_t kNumScans1 = ARRAY_SIZE(kScanScript1); + {1, {2}, 1, 63, 0, 0} // +}); +constexpr size_t kNumScans1 = kScanScript1.size(); -static constexpr jpeg_scan_info kScanScript2[] = { +constexpr auto kScanScript2 = to_array<jpeg_scan_info>({ {1, {0}, 0, 0, 0, 0}, // {1, {1}, 0, 0, 0, 0}, // {1, {2}, 0, 0, 0, 0}, // @@ -74,11 +73,11 @@ static constexpr jpeg_scan_info kScanScript2[] = { {1, {0}, 3, 63, 0, 1}, // {1, {0}, 1, 63, 1, 0}, // {1, {1}, 1, 63, 0, 0}, // - {1, {2}, 1, 63, 0, 0}, // -}; -static constexpr size_t kNumScans2 = ARRAY_SIZE(kScanScript2); + {1, {2}, 1, 63, 0, 0} // +}); +constexpr size_t kNumScans2 = kScanScript2.size(); -static constexpr jpeg_scan_info kScanScript3[] = { +constexpr auto kScanScript3 = to_array<jpeg_scan_info>({ {1, {0}, 0, 0, 0, 0}, // {1, {1}, 0, 0, 0, 0}, // {1, {2}, 0, 0, 0, 0}, // @@ -86,11 +85,11 @@ static constexpr jpeg_scan_info kScanScript3[] = { {1, {0}, 1, 63, 2, 1}, // {1, {0}, 1, 63, 1, 0}, // {1, {1}, 1, 63, 0, 0}, // - {1, {2}, 1, 63, 0, 0}, // -}; -static constexpr size_t kNumScans3 = ARRAY_SIZE(kScanScript3); + {1, {2}, 1, 63, 0, 0} // +}); +constexpr size_t kNumScans3 = kScanScript3.size(); -static constexpr jpeg_scan_info kScanScript4[] = { +constexpr auto kScanScript4 = to_array<jpeg_scan_info>({ {3, {0, 1, 2}, 0, 0, 0, 1}, // {1, {0}, 1, 5, 0, 2}, // {1, {2}, 1, 63, 0, 1}, // @@ -100,11 +99,11 @@ static constexpr jpeg_scan_info kScanScript4[] = { {3, {0, 1, 2}, 0, 0, 1, 0}, // {1, {2}, 1, 63, 1, 0}, // {1, {1}, 1, 63, 1, 0}, // - {1, {0}, 1, 63, 1, 0}, // -}; -static constexpr size_t kNumScans4 = ARRAY_SIZE(kScanScript4); + {1, {0}, 1, 63, 1, 0} // +}); +constexpr size_t kNumScans4 = kScanScript4.size(); -static constexpr jpeg_scan_info kScanScript5[] = { +constexpr auto kScanScript5 = to_array<jpeg_scan_info>({ {3, {0, 1, 2}, 0, 0, 0, 1}, // {1, {0}, 1, 5, 0, 2}, // {1, {1}, 1, 5, 0, 2}, // @@ -118,12 +117,12 @@ static constexpr jpeg_scan_info kScanScript5[] = { {3, {0, 1, 2}, 0, 0, 1, 0}, // {1, {0}, 1, 63, 1, 0}, // {1, {1}, 1, 63, 1, 0}, // - {1, {2}, 1, 63, 1, 0}, // -}; -static constexpr size_t kNumScans5 = ARRAY_SIZE(kScanScript5); + {1, {2}, 1, 63, 1, 0} // +}); +constexpr size_t kNumScans5 = kScanScript5.size(); // default progressive mode of jpegli -static constexpr jpeg_scan_info kScanScript6[] = { +constexpr auto kScanScript6 = to_array<jpeg_scan_info>({ {3, {0, 1, 2}, 0, 0, 0, 0}, // {1, {0}, 1, 2, 0, 0}, // {1, {1}, 1, 2, 0, 0}, // @@ -137,8 +136,8 @@ static constexpr jpeg_scan_info kScanScript6[] = { {1, {0}, 3, 63, 1, 0}, // {1, {1}, 3, 63, 1, 0}, // {1, {2}, 3, 63, 1, 0}, // -}; -static constexpr size_t kNumScans6 = ARRAY_SIZE(kScanScript6); +}); +constexpr size_t kNumScans6 = kScanScript6.size(); // Adapt RGB scan info to grayscale jpegs. void FilterScanComponents(const jpeg_compress_struct* cinfo, @@ -163,12 +162,12 @@ Status SetJpegProgression(int progressive_id, jpeg_simple_progression(cinfo); return true; } - constexpr const jpeg_scan_info* kScanScripts[] = {kScanScript1, kScanScript2, - kScanScript3, kScanScript4, - kScanScript5, kScanScript6}; - constexpr size_t kNumScans[] = {kNumScans1, kNumScans2, kNumScans3, - kNumScans4, kNumScans5, kNumScans6}; - if (progressive_id > static_cast<int>(ARRAY_SIZE(kNumScans))) { + const jpeg_scan_info* kScanScripts[] = { + kScanScript1.data(), kScanScript2.data(), kScanScript3.data(), + kScanScript4.data(), kScanScript5.data(), kScanScript6.data()}; + constexpr auto kNumScans = to_array<size_t>( + {kNumScans1, kNumScans2, kNumScans3, kNumScans4, kNumScans5, kNumScans6}); + if (progressive_id > static_cast<int>(kNumScans.size())) { return JXL_FAILURE("Unknown jpeg scan script id %d", progressive_id); } const jpeg_scan_info* scan_script = kScanScripts[progressive_id - 1]; @@ -178,7 +177,7 @@ Status SetJpegProgression(int progressive_id, jpeg_scan_info scan_info = scan_script[i]; FilterScanComponents(cinfo, &scan_info); if (scan_info.comps_in_scan > 0) { - scan_infos->emplace_back(std::move(scan_info)); + scan_infos->emplace_back(scan_info); } } cinfo->scan_info = scan_infos->data(); @@ -217,8 +216,8 @@ void WriteExif(jpeg_compress_struct* const cinfo, for (const unsigned char c : kExifSignature) { jpeg_write_m_byte(cinfo, c); } - for (size_t i = 0; i < exif.size(); ++i) { - jpeg_write_m_byte(cinfo, exif[i]); + for (uint8_t c : exif) { + jpeg_write_m_byte(cinfo, c); } } @@ -284,7 +283,7 @@ Status EncodeWithLibJpeg(const PackedImage& image, const JxlBasicInfo& info, cinfo.input_components = info.num_color_channels; cinfo.in_color_space = info.num_color_channels == 1 ? JCS_GRAYSCALE : JCS_RGB; jpeg_set_defaults(&cinfo); - cinfo.optimize_coding = params.optimize_coding; + cinfo.optimize_coding = static_cast<boolean>(params.optimize_coding); if (cinfo.input_components == 3) { JXL_RETURN_IF_ERROR( SetChromaSubsampling(params.chroma_subsampling, &cinfo)); @@ -310,10 +309,10 @@ Status EncodeWithLibJpeg(const PackedImage& image, const JxlBasicInfo& info, std::vector<uint8_t> row_bytes(image.stride); const uint8_t* pixels = reinterpret_cast<const uint8_t*>(image.pixels()); - if (cinfo.num_components == (int)image.format.num_channels && + if (cinfo.num_components == static_cast<int>(image.format.num_channels) && image.format.data_type == JXL_TYPE_UINT8) { for (size_t y = 0; y < info.ysize; ++y) { - memcpy(&row_bytes[0], pixels + y * image.stride, image.stride); + memcpy(row_bytes.data(), pixels + y * image.stride, image.stride); JSAMPROW row[] = {row_bytes.data()}; jpeg_write_scanlines(&cinfo, row, 1); } @@ -401,7 +400,7 @@ struct MySearchHook : public sjpeg::SearchHook { } bool Update(float result) override { value = result; - if (fabs(value - target) < tolerance * target) { + if (std::fabs(value - target) < tolerance * target) { return true; } if (value > target) { @@ -420,9 +419,9 @@ struct MySearchHook : public sjpeg::SearchHook { } else { q = (qmin + qmax) / 2.; } - return (pass > 0 && fabs(q - last_q) < q_precision); + return (pass > 0 && std::fabs(q - last_q) < q_precision); } - ~MySearchHook() override {} + ~MySearchHook() override = default; }; #endif @@ -539,7 +538,7 @@ class JPEGEncoder : public Encoder { return formats; } Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image, - ThreadPool* pool = nullptr) const override { + ThreadPool* pool) const override { JXL_RETURN_IF_ERROR(VerifyBasicInfo(ppf.info)); JpegEncoder jpeg_encoder = JpegEncoder::kLibJpeg; JpegParams params; diff --git a/third_party/jpeg-xl/lib/extras/enc/jxl.cc b/third_party/jpeg-xl/lib/extras/enc/jxl.cc index 00adbb7dda..d563f298e6 100644 --- a/third_party/jpeg-xl/lib/extras/enc/jxl.cc +++ b/third_party/jpeg-xl/lib/extras/enc/jxl.cc @@ -7,6 +7,7 @@ #include <jxl/encode.h> #include <jxl/encode_cxx.h> +#include <jxl/types.h> #include "lib/jxl/base/exif.h" @@ -132,7 +133,7 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf, return false; } - auto settings = JxlEncoderFrameSettingsCreate(enc, nullptr); + auto* settings = JxlEncoderFrameSettingsCreate(enc, nullptr); size_t option_idx = 0; if (!SetFrameOptions(params.options, 0, &option_idx, settings)) { return false; @@ -150,10 +151,11 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf, JxlEncoderCollectStats(settings, params.stats); } + bool has_jpeg_bytes = (jpeg_bytes != nullptr); bool use_boxes = !ppf.metadata.exif.empty() || !ppf.metadata.xmp.empty() || !ppf.metadata.jumbf.empty() || !ppf.metadata.iptc.empty(); bool use_container = params.use_container || use_boxes || - (jpeg_bytes && params.jpeg_store_metadata); + (has_jpeg_bytes && params.jpeg_store_metadata); if (JXL_ENC_SUCCESS != JxlEncoderUseContainer(enc, static_cast<int>(use_container))) { @@ -161,12 +163,22 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf, return false; } - if (jpeg_bytes) { + if (has_jpeg_bytes) { if (params.jpeg_store_metadata && JXL_ENC_SUCCESS != JxlEncoderStoreJPEGMetadata(enc, JXL_TRUE)) { fprintf(stderr, "Storing JPEG metadata failed.\n"); return false; } + if (params.jpeg_store_metadata && params.jpeg_strip_exif) { + fprintf(stderr, + "Cannot store metadata and strip exif at the same time.\n"); + return false; + } + if (params.jpeg_store_metadata && params.jpeg_strip_xmp) { + fprintf(stderr, + "Cannot store metadata and strip xmp at the same time.\n"); + return false; + } if (!params.jpeg_store_metadata && params.jpeg_strip_exif) { JxlEncoderFrameSettingsSetOption(settings, JXL_ENC_FRAME_SETTING_JPEG_KEEP_EXIF, 0); @@ -210,8 +222,8 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf, basic_info.num_extra_channels = std::max<uint32_t>(num_alpha_channels, ppf.info.num_extra_channels); basic_info.num_color_channels = ppf.info.num_color_channels; - const bool lossless = params.distance == 0; - basic_info.uses_original_profile = lossless; + const bool lossless = (params.distance == 0); + basic_info.uses_original_profile = TO_JXL_BOOL(lossless); if (params.override_bitdepth != 0) { basic_info.bits_per_sample = params.override_bitdepth; basic_info.exponent_bits_per_sample = @@ -233,7 +245,7 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf, return false; } if (JXL_ENC_SUCCESS != - JxlEncoderSetFrameBitDepth(settings, ¶ms.input_bitdepth)) { + JxlEncoderSetFrameBitDepth(settings, &ppf.input_bitdepth)) { fprintf(stderr, "JxlEncoderSetFrameBitDepth() failed.\n"); return false; } @@ -248,7 +260,7 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf, fprintf(stderr, "JxlEncoderSetFrameLossless() failed.\n"); return false; } - if (!ppf.icc.empty()) { + if (ppf.primary_color_representation == PackedPixelFile::kIccIsPrimary) { if (JXL_ENC_SUCCESS != JxlEncoderSetICCProfile(enc, ppf.icc.data(), ppf.icc.size())) { fprintf(stderr, "JxlEncoderSetICCProfile() failed.\n"); @@ -284,14 +296,15 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf, {"jumb", ppf.metadata.jumbf}, {"xml ", ppf.metadata.iptc}, }; - for (size_t i = 0; i < sizeof boxes / sizeof *boxes; ++i) { - const BoxInfo& box = boxes[i]; - if (!box.bytes.empty() && - JXL_ENC_SUCCESS != JxlEncoderAddBox(enc, box.type, box.bytes.data(), - box.bytes.size(), - params.compress_boxes)) { - fprintf(stderr, "JxlEncoderAddBox() failed (%s).\n", box.type); - return false; + for (auto box : boxes) { + if (!box.bytes.empty()) { + if (JXL_ENC_SUCCESS != + JxlEncoderAddBox(enc, box.type, box.bytes.data(), + box.bytes.size(), + TO_JXL_BOOL(params.compress_boxes))) { + fprintf(stderr, "JxlEncoderAddBox() failed (%s).\n", box.type); + return false; + } } } JxlEncoderCloseBoxes(enc); @@ -336,7 +349,7 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf, } const bool last_frame = fi + 1 == ppf.chunked_frames.size(); if (JXL_ENC_SUCCESS != - JxlEncoderAddChunkedFrame(settings, last_frame, + JxlEncoderAddChunkedFrame(settings, TO_JXL_BOOL(last_frame), chunked_frame.GetInputSource())) { fprintf(stderr, "JxlEncoderAddChunkedFrame() failed.\n"); return false; diff --git a/third_party/jpeg-xl/lib/extras/enc/jxl.h b/third_party/jpeg-xl/lib/extras/enc/jxl.h index b8ca5bda2f..2b3793c0c4 100644 --- a/third_party/jpeg-xl/lib/extras/enc/jxl.h +++ b/third_party/jpeg-xl/lib/extras/enc/jxl.h @@ -38,7 +38,7 @@ struct JXLCompressParams { std::vector<JXLOption> options; // Target butteraugli distance, 0.0 means lossless. float distance = 1.0f; - float alpha_distance = 1.0f; + float alpha_distance = 0.0f; // If set to true, forces container mode. bool use_container = false; // Whether to enable/disable byte-exact jpeg reconstruction for jpeg inputs. @@ -57,8 +57,6 @@ struct JXLCompressParams { size_t override_bitdepth = 0; int32_t codestream_level = -1; int32_t premultiply = -1; - // Override input buffer interpretation. - JxlBitDepth input_bitdepth = {JXL_BIT_DEPTH_FROM_PIXEL_FORMAT, 0, 0}; // If runner_opaque is set, the decoder uses this parallel runner. JxlParallelRunner runner = JxlThreadParallelRunner; void* runner_opaque = nullptr; @@ -69,10 +67,10 @@ struct JXLCompressParams { bool allow_expert_options = false; void AddOption(JxlEncoderFrameSettingId id, int64_t val) { - options.emplace_back(JXLOption(id, val, 0)); + options.emplace_back(id, val, 0); } void AddFloatOption(JxlEncoderFrameSettingId id, float val) { - options.emplace_back(JXLOption(id, val, 0)); + options.emplace_back(id, val, 0); } bool HasOutputProcessor() const { return (output_processor.get_buffer != nullptr && diff --git a/third_party/jpeg-xl/lib/extras/enc/npy.cc b/third_party/jpeg-xl/lib/extras/enc/npy.cc index ae8cf13cc4..8d9954ef31 100644 --- a/third_party/jpeg-xl/lib/extras/enc/npy.cc +++ b/third_party/jpeg-xl/lib/extras/enc/npy.cc @@ -7,11 +7,13 @@ #include <jxl/types.h> +#include <memory> #include <sstream> #include <string> #include <vector> #include "lib/extras/packed_image.h" +#include "lib/jxl/base/common.h" namespace jxl { namespace extras { @@ -52,14 +54,17 @@ class JSONDict : public JSONField { static_assert(std::is_convertible<T*, JSONField*>::value, "T must be a JSONField"); T* ret = new T(); - values_.emplace_back( - key, std::unique_ptr<JSONField>(static_cast<JSONField*>(ret))); + JSONField* field = static_cast<JSONField*>(ret); + auto handle = std::unique_ptr<JSONField>(field); + values_.emplace_back(key, std::move(handle)); return ret; } template <typename T> void Add(const std::string& key, const T& value) { - values_.emplace_back(key, std::unique_ptr<JSONField>(new JSONValue(value))); + JSONField* field = static_cast<JSONField*>(new JSONValue(value)); + auto handle = std::unique_ptr<JSONField>(field); + values_.emplace_back(key, std::move(handle)); } void Write(std::ostream& o, uint32_t indent) const override { @@ -71,11 +76,11 @@ class JSONDict : public JSONField { o << ","; } is_first = false; - o << std::endl << indent_str << " \"" << key_value.first << "\": "; + o << "\n" << indent_str << " \"" << key_value.first << "\": "; key_value.second->Write(o, indent + 2); } if (!values_.empty()) { - o << std::endl << indent_str; + o << "\n" << indent_str; } o << "}"; } @@ -112,11 +117,11 @@ class JSONArray : public JSONField { o << ","; } is_first = false; - o << std::endl << indent_str << " "; + o << "\n" << indent_str << " "; value->Write(o, indent + 2); } if (!values_.empty()) { - o << std::endl << indent_str; + o << "\n" << indent_str; } o << "]"; } @@ -160,13 +165,13 @@ void GenerateMetadata(const PackedPixelFile& ppf, std::vector<uint8_t>* out) { } { - auto ectype = meta.AddEmpty<JSONArray>("extra_channel_type"); - auto bps = meta.AddEmpty<JSONArray>("bits_per_sample"); - auto ebps = meta.AddEmpty<JSONArray>("exp_bits_per_sample"); + auto* ectype = meta.AddEmpty<JSONArray>("extra_channel_type"); + auto* bps = meta.AddEmpty<JSONArray>("bits_per_sample"); + auto* ebps = meta.AddEmpty<JSONArray>("exp_bits_per_sample"); bps->Add(ppf.info.bits_per_sample); ebps->Add(ppf.info.exponent_bits_per_sample); - for (size_t i = 0; i < ppf.extra_channels_info.size(); i++) { - switch (ppf.extra_channels_info[i].ec_info.type) { + for (const auto& eci : ppf.extra_channels_info) { + switch (eci.ec_info.type) { case JXL_CHANNEL_ALPHA: { ectype->Add(std::string("Alpha")); break; @@ -200,8 +205,8 @@ void GenerateMetadata(const PackedPixelFile& ppf, std::vector<uint8_t>* out) { break; } } - bps->Add(ppf.extra_channels_info[i].ec_info.bits_per_sample); - ebps->Add(ppf.extra_channels_info[i].ec_info.exponent_bits_per_sample); + bps->Add(eci.ec_info.bits_per_sample); + ebps->Add(eci.ec_info.exponent_bits_per_sample); } } @@ -282,7 +287,7 @@ bool WriteNPYArray(const PackedPixelFile& ppf, std::vector<uint8_t>* out) { class NumPyEncoder : public Encoder { public: Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image, - ThreadPool* pool = nullptr) const override { + ThreadPool* pool) const override { JXL_RETURN_IF_ERROR(VerifyBasicInfo(ppf.info)); GenerateMetadata(ppf, &encoded_image->metadata); encoded_image->bitstreams.emplace_back(); diff --git a/third_party/jpeg-xl/lib/extras/enc/pgx.cc b/third_party/jpeg-xl/lib/extras/enc/pgx.cc index d4809e38b6..eb8eab4271 100644 --- a/third_party/jpeg-xl/lib/extras/enc/pgx.cc +++ b/third_party/jpeg-xl/lib/extras/enc/pgx.cc @@ -60,7 +60,7 @@ Status EncodeImagePGX(const PackedFrame& frame, const JxlBasicInfo& info, std::vector<uint8_t> pixels(num_samples * bytes_per_sample); if (format.data_type == JXL_TYPE_UINT8) { - memcpy(&pixels[0], in, num_samples * bytes_per_sample); + memcpy(pixels.data(), in, num_samples * bytes_per_sample); } else if (format.data_type == JXL_TYPE_UINT16) { if (format.endianness != JXL_BIG_ENDIAN) { const uint8_t* p_in = in; @@ -69,7 +69,7 @@ Status EncodeImagePGX(const PackedFrame& frame, const JxlBasicInfo& info, StoreBE16(LoadLE16(p_in), p_out); } } else { - memcpy(&pixels[0], in, num_samples * bytes_per_sample); + memcpy(pixels.data(), in, num_samples * bytes_per_sample); } } else { return JXL_FAILURE("Unsupported pixel data type"); diff --git a/third_party/jpeg-xl/lib/extras/enc/pnm.cc b/third_party/jpeg-xl/lib/extras/enc/pnm.cc index 4183900198..966611cfca 100644 --- a/third_party/jpeg-xl/lib/extras/enc/pnm.cc +++ b/third_party/jpeg-xl/lib/extras/enc/pnm.cc @@ -31,7 +31,7 @@ constexpr size_t kMaxHeaderSize = 200; class BasePNMEncoder : public Encoder { public: Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image, - ThreadPool* pool = nullptr) const override { + ThreadPool* pool) 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()) { diff --git a/third_party/jpeg-xl/lib/extras/jpegli_test.cc b/third_party/jpeg-xl/lib/extras/jpegli_test.cc index 66c18617a6..3049049a64 100644 --- a/third_party/jpeg-xl/lib/extras/jpegli_test.cc +++ b/third_party/jpeg-xl/lib/extras/jpegli_test.cc @@ -85,7 +85,7 @@ Status EncodeWithLibjpeg(const PackedPixelFile& ppf, int quality, std::unique_ptr<Encoder> encoder = GetJPEGEncoder(); encoder->SetOption("q", std::to_string(quality)); EncodedImage encoded; - JXL_RETURN_IF_ERROR(encoder->Encode(ppf, &encoded)); + JXL_RETURN_IF_ERROR(encoder->Encode(ppf, &encoded, nullptr)); JXL_RETURN_IF_ERROR(!encoded.bitstreams.empty()); *compressed = std::move(encoded.bitstreams[0]); return true; @@ -156,8 +156,8 @@ TEST(JpegliTest, JpegliXYBEncodeTest) { PackedPixelFile ppf_out; ASSERT_TRUE(DecodeWithLibjpeg(compressed, &ppf_out)); - EXPECT_THAT(BitsPerPixel(ppf_in, compressed), IsSlightlyBelow(1.45f)); - EXPECT_THAT(ButteraugliDistance(ppf_in, ppf_out), IsSlightlyBelow(1.32f)); + EXPECT_SLIGHTLY_BELOW(BitsPerPixel(ppf_in, compressed), 1.45f); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(ppf_in, ppf_out), 1.32f); } TEST(JpegliTest, JpegliDecodeTestLargeSmoothArea) { @@ -205,8 +205,8 @@ TEST(JpegliTest, JpegliYUVEncodeTest) { PackedPixelFile ppf_out; ASSERT_TRUE(DecodeWithLibjpeg(compressed, &ppf_out)); - EXPECT_THAT(BitsPerPixel(ppf_in, compressed), IsSlightlyBelow(1.7f)); - EXPECT_THAT(ButteraugliDistance(ppf_in, ppf_out), IsSlightlyBelow(1.32f)); + EXPECT_SLIGHTLY_BELOW(BitsPerPixel(ppf_in, compressed), 1.7f); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(ppf_in, ppf_out), 1.32f); } TEST(JpegliTest, JpegliYUVChromaSubsamplingEncodeTest) { @@ -247,8 +247,8 @@ TEST(JpegliTest, JpegliYUVEncodeTestNoAq) { PackedPixelFile ppf_out; ASSERT_TRUE(DecodeWithLibjpeg(compressed, &ppf_out)); - EXPECT_THAT(BitsPerPixel(ppf_in, compressed), IsSlightlyBelow(1.85f)); - EXPECT_THAT(ButteraugliDistance(ppf_in, ppf_out), IsSlightlyBelow(1.25f)); + EXPECT_SLIGHTLY_BELOW(BitsPerPixel(ppf_in, compressed), 1.85f); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(ppf_in, ppf_out), 1.25f); } TEST(JpegliTest, JpegliHDRRoundtripTest) { @@ -267,8 +267,8 @@ TEST(JpegliTest, JpegliHDRRoundtripTest) { JpegDecompressParams dparams; dparams.output_data_type = JXL_TYPE_UINT16; ASSERT_TRUE(DecodeJpeg(compressed, dparams, nullptr, &ppf_out)); - EXPECT_THAT(BitsPerPixel(ppf_in, compressed), IsSlightlyBelow(2.95f)); - EXPECT_THAT(ButteraugliDistance(ppf_in, ppf_out), IsSlightlyBelow(1.05f)); + EXPECT_SLIGHTLY_BELOW(BitsPerPixel(ppf_in, compressed), 2.95f); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(ppf_in, ppf_out), 1.05f); } TEST(JpegliTest, JpegliSetAppData) { diff --git a/third_party/jpeg-xl/lib/extras/mmap.cc b/third_party/jpeg-xl/lib/extras/mmap.cc index 7852831ebd..9f5bba97ed 100644 --- a/third_party/jpeg-xl/lib/extras/mmap.cc +++ b/third_party/jpeg-xl/lib/extras/mmap.cc @@ -10,7 +10,8 @@ #include "lib/jxl/base/common.h" -#if __unix__ +#if defined(__unix__) || defined(__unix) || \ + defined(__APPLE__) && defined(__MACH__) #include <fcntl.h> #include <sys/mman.h> #include <unistd.h> @@ -54,7 +55,7 @@ struct MemoryMappedFileImpl { } // namespace jxl -#elif __WIN32__ +#elif defined(_WIN32) #include <string.h> #include <windows.h> @@ -97,6 +98,8 @@ struct MemoryMappedFileImpl { return f; } + ~MemoryMappedFileImpl() { UnmapViewOfFile(ptr); } + const uint8_t* data() const { return reinterpret_cast<const uint8_t*>(ptr); } size_t size() const { return fsize.QuadPart; } diff --git a/third_party/jpeg-xl/lib/extras/packed_image.h b/third_party/jpeg-xl/lib/extras/packed_image.h index edd5f1be75..a66ddfbd70 100644 --- a/third_party/jpeg-xl/lib/extras/packed_image.h +++ b/third_party/jpeg-xl/lib/extras/packed_image.h @@ -37,11 +37,18 @@ namespace extras { // Class representing an interleaved image with a bunch of channels. class PackedImage { public: - PackedImage(size_t xsize, size_t ysize, const JxlPixelFormat& format) - : PackedImage(xsize, ysize, format, CalcStride(format, xsize)) {} + static StatusOr<PackedImage> Create(size_t xsize, size_t ysize, + const JxlPixelFormat& format) { + PackedImage image(xsize, ysize, format, CalcStride(format, xsize)); + if (!image.pixels()) { + // TODO(szabadka): use specialized OOM error code + return JXL_FAILURE("Failed to allocate memory for image"); + } + return image; + } PackedImage Copy() const { - PackedImage copy(xsize, ysize, format); + PackedImage copy(xsize, ysize, format, CalcStride(format, xsize)); memcpy(reinterpret_cast<uint8_t*>(copy.pixels()), reinterpret_cast<const uint8_t*>(pixels()), pixels_size); return copy; @@ -108,7 +115,7 @@ class PackedImage { } } - void SetPixelValue(size_t y, size_t x, size_t c, float val) { + void SetPixelValue(size_t y, size_t x, size_t c, float val) const { uint8_t* data = pixels(y, x, c); switch (format.data_type) { case JXL_TYPE_UINT8: @@ -169,17 +176,25 @@ class PackedImage { // as all other frames in the same image. class PackedFrame { public: - template <typename... Args> - explicit PackedFrame(Args&&... args) : color(std::forward<Args>(args)...) {} + explicit PackedFrame(PackedImage&& image) : color(std::move(image)) {} + + static StatusOr<PackedFrame> Create(size_t xsize, size_t ysize, + const JxlPixelFormat& format) { + JXL_ASSIGN_OR_RETURN(PackedImage image, + PackedImage::Create(xsize, ysize, format)); + PackedFrame frame(std::move(image)); + return frame; + } - PackedFrame Copy() const { - PackedFrame copy(color.xsize, color.ysize, color.format); + StatusOr<PackedFrame> Copy() const { + JXL_ASSIGN_OR_RETURN( + PackedFrame copy, + PackedFrame::Create(color.xsize, color.ysize, color.format)); copy.frame_info = frame_info; copy.name = name; copy.color = color.Copy(); - for (size_t i = 0; i < extra_channels.size(); ++i) { - PackedImage ec = extra_channels[i].Copy(); - copy.extra_channels.emplace_back(std::move(ec)); + for (const auto& ec : extra_channels) { + copy.extra_channels.emplace_back(ec.Copy()); } return copy; } @@ -244,12 +259,26 @@ class PackedPixelFile { std::vector<PackedExtraChannel> extra_channels_info; // Color information of the decoded pixels. - // If the icc is empty, the JxlColorEncoding should be used instead. - std::vector<uint8_t> icc; + // `primary_color_representation` indicates whether `color_encoding` or `icc` + // is the “authoritative” encoding of the colorspace, as opposed to a fallback + // encoding. For example, if `color_encoding` is the primary one, as would + // occur when decoding a jxl file with such a representation, then `enc/jxl` + // will use it and ignore the ICC profile, whereas `enc/png` will include the + // ICC profile for compatibility. + // If `icc` is the primary representation, `enc/jxl` will preserve it when + // compressing losslessly, but *may* encode it as a color_encoding when + // compressing lossily. + enum { + kColorEncodingIsPrimary, + kIccIsPrimary + } primary_color_representation = kColorEncodingIsPrimary; JxlColorEncoding color_encoding = {}; + std::vector<uint8_t> icc; // The icc profile of the original image. std::vector<uint8_t> orig_icc; + JxlBitDepth input_bitdepth = {JXL_BIT_DEPTH_FROM_PIXEL_FORMAT, 0, 0}; + std::unique_ptr<PackedFrame> preview_frame; std::vector<PackedFrame> frames; mutable std::vector<ChunkedPackedFrame> chunked_frames; diff --git a/third_party/jpeg-xl/lib/extras/packed_image_convert.cc b/third_party/jpeg-xl/lib/extras/packed_image_convert.cc index 56f3b044a4..2ad001bf09 100644 --- a/third_party/jpeg-xl/lib/extras/packed_image_convert.cc +++ b/third_party/jpeg-xl/lib/extras/packed_image_convert.cc @@ -22,15 +22,15 @@ namespace jxl { namespace extras { Status ConvertPackedFrameToImageBundle(const JxlBasicInfo& info, + const JxlBitDepth& input_bitdepth, 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; + input_bitdepth.type == JXL_BIT_DEPTH_FROM_PIXEL_FORMAT + ? 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. @@ -64,7 +64,8 @@ Status ConvertPackedFrameToImageBundle(const JxlBasicInfo& info, 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_ASSIGN_OR_RETURN(bundle->extra_channels()[i], + ImageF::Create(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])); @@ -97,23 +98,23 @@ Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf, 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); + FROM_JXL_BOOL(ppf.info.alpha_premultiplied)); ExtraChannelInfo* alpha = io->metadata.m.Find(ExtraChannel::kAlpha); if (alpha) alpha->bit_depth = io->metadata.m.bit_depth; - io->metadata.m.xyb_encoded = !ppf.info.uses_original_profile; + io->metadata.m.xyb_encoded = !FROM_JXL_BOOL(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.have_animation = FROM_JXL_BOOL(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()) { + if (ppf.primary_color_representation == PackedPixelFile::kIccIsPrimary) { IccBytes icc = ppf.icc; if (!io->metadata.m.color_encoding.SetICC(std::move(icc), JxlGetDefaultCms())) { @@ -170,15 +171,16 @@ Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf, 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)); + ppf.info, ppf.input_bitdepth, *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)); + JXL_RETURN_IF_ERROR(ConvertPackedFrameToImageBundle( + ppf.info, ppf.input_bitdepth, frame, *io, pool, &bundle)); io->frames.push_back(std::move(bundle)); } @@ -210,7 +212,8 @@ PackedPixelFile ConvertImage3FToPackedPixelFile(const Image3F& image, : 0; ppf.color_encoding = c_enc.ToExternal(); ppf.frames.clear(); - PackedFrame frame(image.xsize(), image.ysize(), format); + JXL_ASSIGN_OR_DIE(PackedFrame frame, + PackedFrame::Create(image.xsize(), image.ysize(), format)); const ImageF* channels[3]; for (int c = 0; c < 3; ++c) { channels[c] = &image.Plane(c); @@ -242,7 +245,8 @@ Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io, ppf->info.alpha_bits = alpha_channel->bit_depth.bits_per_sample; ppf->info.alpha_exponent_bits = alpha_channel->bit_depth.exponent_bits_per_sample; - ppf->info.alpha_premultiplied = alpha_channel->alpha_associated; + ppf->info.alpha_premultiplied = + TO_JXL_BOOL(alpha_channel->alpha_associated); } // Convert the image metadata @@ -257,9 +261,9 @@ Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io, 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; + TO_JXL_BOOL(io.metadata.m.tone_mapping.relative_to_max_display); - ppf->info.uses_original_profile = !io.metadata.m.xyb_encoded; + ppf->info.uses_original_profile = TO_JXL_BOOL(!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); @@ -267,13 +271,16 @@ Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io, // 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.have_animation = TO_JXL_BOOL(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()); + ppf->primary_color_representation = + c_desired.WantICC() ? PackedPixelFile::kIccIsPrimary + : PackedPixelFile::kColorEncodingIsPrimary; ppf->color_encoding = c_desired.ToExternal(); // Convert the extra blobs @@ -289,22 +296,24 @@ Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io, 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 alpha_channels = has_alpha ? 1 : 0; const uint32_t num_channels = - frame.metadata()->color_encoding.Channels() + has_alpha; + frame.metadata()->color_encoding.Channels() + alpha_channels; 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); + JXL_ASSIGN_OR_RETURN(PackedFrame packed_frame, + PackedFrame::Create(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(); + JXL_ASSIGN_OR_RETURN(ImageBundle ib, frame.Copy()); const ImageBundle* to_color_transform = &ib; ImageMetadata metadata = io.metadata.m; ImageBundle store(&metadata); diff --git a/third_party/jpeg-xl/lib/extras/tone_mapping.cc b/third_party/jpeg-xl/lib/extras/tone_mapping.cc index 3d0269524b..39df304501 100644 --- a/third_party/jpeg-xl/lib/extras/tone_mapping.cc +++ b/third_party/jpeg-xl/lib/extras/tone_mapping.cc @@ -19,7 +19,7 @@ HWY_BEFORE_NAMESPACE(); namespace jxl { namespace HWY_NAMESPACE { -static constexpr float rec2020_luminances[3] = {0.2627f, 0.6780f, 0.0593f}; +static constexpr Vector3 rec2020_luminances{0.2627f, 0.6780f, 0.0593f}; Status ToneMapFrame(const std::pair<float, float> display_nits, ImageBundle* const ib, ThreadPool* const pool) { diff --git a/third_party/jpeg-xl/lib/extras/tone_mapping_gbench.cc b/third_party/jpeg-xl/lib/extras/tone_mapping_gbench.cc index 34cbdde781..8fc928a4fd 100644 --- a/third_party/jpeg-xl/lib/extras/tone_mapping_gbench.cc +++ b/third_party/jpeg-xl/lib/extras/tone_mapping_gbench.cc @@ -6,11 +6,12 @@ #include "benchmark/benchmark.h" #include "lib/extras/codec.h" #include "lib/extras/tone_mapping.h" +#include "lib/jxl/image.h" namespace jxl { static void BM_ToneMapping(benchmark::State& state) { - Image3F color(2268, 1512); + JXL_ASSIGN_OR_DIE(Image3F color, Image3F::Create(2268, 1512)); FillImage(0.5f, &color); // Use linear Rec. 2020 so that `ToneMapTo` doesn't have to convert to it and @@ -25,7 +26,8 @@ static void BM_ToneMapping(benchmark::State& state) { for (auto _ : state) { state.PauseTiming(); CodecInOut tone_mapping_input; - Image3F color2(color.xsize(), color.ysize()); + JXL_ASSIGN_OR_DIE(Image3F color2, + Image3F::Create(color.xsize(), color.ysize())); CopyImageTo(color, &color2); tone_mapping_input.SetFromImage(std::move(color2), linear_rec2020); tone_mapping_input.metadata.m.SetIntensityTarget(255); |