diff --git a/media/libjxl/moz.yaml b/media/libjxl/moz.yaml index 1ce8c8f6b3f2..15a4994e4529 100644 --- a/media/libjxl/moz.yaml +++ b/media/libjxl/moz.yaml @@ -10,9 +10,9 @@ origin: url: https://github.com/libjxl/libjxl - release: a741085a1dee343f143f5fdca3212ca13d66e401 (2024-04-23T11:57:55Z). + release: 715b44238e67e521bba944e2864eb2933296e51c (2024-05-14T15:25:56Z). - revision: a741085a1dee343f143f5fdca3212ca13d66e401 + revision: 715b44238e67e521bba944e2864eb2933296e51c license: Apache-2.0 diff --git a/third_party/jpeg-xl/CMakeLists.txt b/third_party/jpeg-xl/CMakeLists.txt index ea8ccc43bad1..e3a672231577 100644 --- a/third_party/jpeg-xl/CMakeLists.txt +++ b/third_party/jpeg-xl/CMakeLists.txt @@ -433,7 +433,7 @@ if(JPEGXL_ENABLE_MANPAGES) find_program(ASCIIDOC a2x) if(ASCIIDOC) file(STRINGS "${ASCIIDOC}" ASCIIDOC_SHEBANG LIMIT_COUNT 1) - if(ASCIIDOC_SHEBANG MATCHES "/sh|/bash" OR MINGW) + if(ASCIIDOC_SHEBANG MATCHES "sh$" OR MINGW) set(ASCIIDOC_PY_FOUND ON) # Run the program directly and set ASCIIDOC as empty. set(ASCIIDOC_PY "${ASCIIDOC}") diff --git a/third_party/jpeg-xl/lib/extras/dec/apng.cc b/third_party/jpeg-xl/lib/extras/dec/apng.cc index 824a6f47ee23..2da114d76315 100644 --- a/third_party/jpeg-xl/lib/extras/dec/apng.cc +++ b/third_party/jpeg-xl/lib/extras/dec/apng.cc @@ -42,16 +42,21 @@ #include #include #include +#include +#include #include #include #include +#include "lib/extras/packed_image.h" #include "lib/extras/size_constraints.h" #include "lib/jxl/base/byte_order.h" #include "lib/jxl/base/common.h" #include "lib/jxl/base/compiler_specific.h" #include "lib/jxl/base/printf_macros.h" +#include "lib/jxl/base/rect.h" #include "lib/jxl/base/span.h" +#include "lib/jxl/base/status.h" #if JPEGXL_ENABLE_APNG #include "png.h" /* original (unpatched) libpng is ok */ #endif @@ -72,11 +77,8 @@ Status DecodeImageAPNG(const Span bytes, namespace { -constexpr uint8_t kExifSignature[6] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00}; - -/* hIST chunk tail is not proccesed properly; skip this chunk completely; - see https://github.com/glennrp/libpng/pull/413 */ -const uint8_t kIgnoredPngChunks[] = {'h', 'I', 'S', 'T', '\0'}; +constexpr std::array kPngSignature = {137, 'P', 'N', 'G', + '\r', '\n', 26, '\n'}; // Returns floating-point value from the PNG encoding (times 10^5). double F64FromU32(const uint32_t x) { return static_cast(x) * 1E-5; } @@ -393,15 +395,17 @@ Status DecodeBlob(const png_text_struct& info, PackedMetadata* metadata) { return false; } if (type == "exif") { - // Remove "Exif\0\0" prefix if present - if (bytes.size() >= sizeof kExifSignature && - memcmp(bytes.data(), kExifSignature, sizeof kExifSignature) == 0) { - bytes.erase(bytes.begin(), bytes.begin() + sizeof kExifSignature); + // Remove prefix if present. + constexpr std::array kExifPrefix = {'E', 'x', 'i', 'f', 0, 0}; + if (bytes.size() >= kExifPrefix.size() && + memcmp(bytes.data(), kExifPrefix.data(), kExifPrefix.size()) == 0) { + bytes.erase(bytes.begin(), bytes.begin() + kExifPrefix.size()); } if (!metadata->exif.empty()) { - JXL_WARNING("overwriting EXIF (%" PRIuS " bytes) with base16 (%" PRIuS - " bytes)", - metadata->exif.size(), bytes.size()); + JXL_DEBUG(2, + "overwriting EXIF (%" PRIuS " bytes) with base16 (%" PRIuS + " bytes)", + metadata->exif.size(), bytes.size()); } metadata->exif = std::move(bytes); } else if (type == "iptc") { @@ -410,15 +414,16 @@ Status DecodeBlob(const png_text_struct& info, PackedMetadata* metadata) { // TODO(jon): Deal with 8bim in some way } else if (type == "xmp") { if (!metadata->xmp.empty()) { - JXL_WARNING("overwriting XMP (%" PRIuS " bytes) with base16 (%" PRIuS - " bytes)", - metadata->xmp.size(), bytes.size()); + JXL_DEBUG(2, + "overwriting XMP (%" PRIuS " bytes) with base16 (%" PRIuS + " bytes)", + metadata->xmp.size(), bytes.size()); } metadata->xmp = std::move(bytes); } else { - JXL_WARNING("Unknown type in 'Raw format type' text chunk: %s: %" PRIuS - " bytes", - type.c_str(), bytes.size()); + JXL_DEBUG( + 2, "Unknown type in 'Raw format type' text chunk: %s: %" PRIuS " bytes", + type.c_str(), bytes.size()); } return true; } @@ -513,7 +518,6 @@ void ProgressiveRead_OnRow(png_structp png_ptr, png_bytep new_row, Pixels* frame = reinterpret_cast(png_get_progressive_ptr(png_ptr)); JXL_CHECK(frame); JXL_CHECK(row_num < frame->rows.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); } @@ -536,7 +540,8 @@ struct Context { * * TODO(eustas): add details */ - bool InitPngDecoder(bool hasInfo, std::vector& chunksInfo) { + bool InitPngDecoder(const std::vector& chunksInfo, + const RectT& viewport) { ResetPngDecoder(); png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, @@ -550,25 +555,31 @@ struct Context { return false; } - png_set_keep_unknown_chunks( - png_ptr, 1, kIgnoredPngChunks, - static_cast(sizeof(kIgnoredPngChunks) / 5)); + /* hIST chunk tail is not proccesed properly; skip this chunk completely; + see https://github.com/glennrp/libpng/pull/413 */ + constexpr std::array kIgnoredChunks = {'h', 'I', 'S', 'T', 0}; + png_set_keep_unknown_chunks(png_ptr, 1, kIgnoredChunks.data(), + static_cast(kIgnoredChunks.size() / 5)); png_set_crc_action(png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE); png_set_progressive_read_fn(png_ptr, static_cast(&frameRaw), ProgressiveRead_OnInfo, ProgressiveRead_OnRow, nullptr); - std::array header = {137, 80, 78, 71, 13, 10, 26, 10}; - png_process_data(png_ptr, info_ptr, header.data(), header.size()); + png_process_data(png_ptr, info_ptr, + const_cast(kPngSignature.data()), + kPngSignature.size()); + + // Patch dimensions. + png_save_uint_32(ihdr.data() + 8, static_cast(viewport.xsize())); + png_save_uint_32(ihdr.data() + 12, static_cast(viewport.ysize())); png_process_data(png_ptr, info_ptr, ihdr.data(), ihdr.size()); - if (hasInfo) { - for (auto& chunk : chunksInfo) { - png_process_data(png_ptr, info_ptr, const_cast(chunk.data()), - chunk.size()); - } + for (const auto& chunk : chunksInfo) { + png_process_data(png_ptr, info_ptr, const_cast(chunk.data()), + chunk.size()); } + return true; } @@ -624,27 +635,143 @@ struct Context { info_ptr = nullptr; } - std::vector ihdr; // (modified) copy of file IHDR chunk + std::array ihdr; // (modified) copy of file IHDR chunk png_structp png_ptr = nullptr; png_infop info_ptr = nullptr; Pixels frameRaw = {}; }; -constexpr uint32_t kMaxPNGSize = 1000000UL; +enum class DisposeOp : uint8_t { NONE = 0, BACKGROUND = 1, PREVIOUS = 2 }; -struct FrameInfo { - PackedImage data; - uint32_t duration; - size_t x0, xsize; - size_t y0, ysize; - uint32_t dispose_op; - uint32_t blend_op; +constexpr uint8_t kLastDisposeOp = static_cast(DisposeOp::PREVIOUS); + +enum class BlendOp : uint8_t { SOURCE = 0, OVER = 1 }; + +constexpr uint8_t kLastBlendOp = static_cast(BlendOp::OVER); + +// fcTL +struct FrameControl { + uint32_t delay_num; + uint32_t delay_den; + RectT viewport; + DisposeOp dispose_op; + BlendOp blend_op; +}; + +struct Frame { + PackedImage pixels; + FrameControl metadata; +}; + +bool ValidateViewport(const RectT& r) { + constexpr uint32_t kMaxPngDim = 1000000UL; + return (r.xsize() <= kMaxPngDim) && (r.ysize() <= kMaxPngDim); +} + +/** + * Setup #channels, bpp, colorspace, etc. from PNG values. + */ +void SetColorData(PackedPixelFile* ppf, uint8_t color_type, uint8_t bit_depth, + png_color_8p sig_bits, uint32_t has_transparency) { + bool palette_used = ((color_type & 1) != 0); + bool color_used = ((color_type & 2) != 0); + bool alpha_channel_used = ((color_type & 4) != 0); + if (palette_used) { + if (!color_used || alpha_channel_used) { + JXL_DEBUG(2, "Unexpected PNG color type"); + } + } + + ppf->info.bits_per_sample = bit_depth; + + if (palette_used) { + // palette will actually be 8-bit regardless of the index bitdepth + ppf->info.bits_per_sample = 8; + } + if (color_used) { + ppf->info.num_color_channels = 3; + ppf->color_encoding.color_space = JXL_COLOR_SPACE_RGB; + if (sig_bits) { + if (sig_bits->red == sig_bits->green && + sig_bits->green == sig_bits->blue) { + ppf->info.bits_per_sample = sig_bits->red; + } else { + int maxbps = + std::max(sig_bits->red, std::max(sig_bits->green, sig_bits->blue)); + JXL_DEBUG(2, + "sBIT chunk: bit depths for R, G, and B are not the same (%i " + "%i %i), while in JPEG XL they have to be the same. Setting " + "RGB bit depth to %i.", + sig_bits->red, sig_bits->green, sig_bits->blue, maxbps); + ppf->info.bits_per_sample = maxbps; + } + } + } else { + ppf->info.num_color_channels = 1; + ppf->color_encoding.color_space = JXL_COLOR_SPACE_GRAY; + if (sig_bits) ppf->info.bits_per_sample = sig_bits->gray; + } + if (alpha_channel_used || has_transparency) { + ppf->info.alpha_bits = ppf->info.bits_per_sample; + if (sig_bits && sig_bits->alpha != ppf->info.bits_per_sample) { + JXL_DEBUG(2, + "sBIT chunk: bit depths for RGBA are inconsistent " + "(%i %i %i %i). Setting A bitdepth to %i.", + sig_bits->red, sig_bits->green, sig_bits->blue, sig_bits->alpha, + ppf->info.bits_per_sample); + } + } else { + ppf->info.alpha_bits = 0; + } + ppf->color_encoding.color_space = (ppf->info.num_color_channels == 1) + ? JXL_COLOR_SPACE_GRAY + : JXL_COLOR_SPACE_RGB; +} + +// Color profile chunks: cICP has the highest priority, followed by +// iCCP and sRGB (which shouldn't co-exist, but if they do, we use +// iCCP), followed finally by gAMA and cHRM. +enum class ColorInfoType { + NONE = 0, + GAMA_OR_CHRM = 1, + ICCP_OR_SRGB = 2, + CICP = 3 }; } // namespace bool CanDecodeAPNG() { return true; } +/** + * Parse and decode PNG file. + * + * Useful PNG chunks: + * acTL : animation control (#frames, loop count) + * fcTL : frame control (seq#, viewport, delay, disposal blending) + * bKGD : preferable background + * IDAT : "default image" + * if single fcTL goes before IDAT, then it is also first frame + * fdAT : seq# + IDAT-like content + * PLTE : palette + * cICP : coding-independent code points for video signal type identification + * iCCP : embedded ICC profile + * sRGB : standard RGB colour space + * eXIf : exchangeable image file profile + * gAMA : image gamma + * cHRM : primary chromaticities and white point + * tRNS : transparency + * + * PNG chunk ordering: + * - IHDR first + * - IEND last + * - acTL, cHRM, cICP, gAMA, iCCP, sRGB, bKGD, eXIf, PLTE before IDAT + * - fdAT after IDAT + * + * More rules: + * - iCCP and sRGB are exclusive + * - fcTL and fdAT seq# must be in order fro 0, with no gaps or duplicates + * - fcTL before corresponding IDAT / fdAT + */ Status DecodeImageAPNG(const Span bytes, const ColorHints& color_hints, PackedPixelFile* ppf, const SizeConstraints* constraints) { @@ -661,347 +788,411 @@ Status DecodeImageAPNG(const Span bytes, Reader input(bytes); - // Not a PNG => not an error - unsigned char png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; - Bytes sig = input.Read(8); - if (sig.size() != 8 || memcmp(sig.data(), png_signature, 8) != 0) { - return false; - } - - Bytes chunk_ihdr = input.ReadChunk(); - if (chunk_ihdr.empty()) { - return false; - } - uint32_t id = LoadLE32(chunk_ihdr.data() + 4); - if (id != MakeTag('I', 'H', 'D', 'R') || chunk_ihdr.size() != 25) { - return false; - } - const uint32_t w = png_get_uint_32(chunk_ihdr.data() + 8); - const uint32_t h = png_get_uint_32(chunk_ihdr.data() + 12); - if (w > kMaxPNGSize || h > kMaxPNGSize) { - return false; + // Check signature. + Bytes sig = input.Read(kPngSignature.size()); + if (sig.size() != 8 || + memcmp(sig.data(), kPngSignature.data(), kPngSignature.size()) != 0) { + return JXL_FAILURE("PNG signature mismatch"); } + // Check IHDR chunk. Context ctx; - ctx.ihdr = chunk_ihdr.Copy(); - - std::vector chunksInfo; - if (!ctx.InitPngDecoder(false, chunksInfo)) { - return false; + Bytes ihdr = input.ReadChunk(); + if (ihdr.size() != ctx.ihdr.size()) { + return JXL_FAILURE("Unexpected first chunk payload size"); + } + memcpy(ctx.ihdr.data(), ihdr.data(), ihdr.size()); + uint32_t id = LoadLE32(ihdr.data() + 4); + if (id != MakeTag('I', 'H', 'D', 'R')) { + return JXL_FAILURE("First chunk is not IHDR"); + } + const RectT image_rect(0, 0, png_get_uint_32(ihdr.data() + 8), + png_get_uint_32(ihdr.data() + 12)); + if (!ValidateViewport(image_rect)) { + return JXL_FAILURE("PNG image dimensions are too large"); } - bool isAnimated = false; - bool hasInfo = false; + // Chunks we supply to PNG decoder for every animation frame. + std::vector passthrough_chunks; + if (!ctx.InitPngDecoder(passthrough_chunks, image_rect)) { + return JXL_FAILURE("Failed to initialize PNG decoder"); + } + + // Marker that this PNG is animated. + bool seen_actl = false; + // First IDAT is a very important milestone; at this moment we freeze + // gathered metadata. + bool seen_idat = false; + // fCTL can occur multiple times, but only once before IDAT. + bool seen_fctl = false; + // Logical EOF. + bool seen_iend = false; + + ColorInfoType color_info_type = ColorInfoType::NONE; + + // Flag that we processed some IDAT / fDAT after image / frame start. + bool seen_pixel_data = false; + uint32_t num_channels; JxlPixelFormat format = {}; - unsigned int bytes_per_pixel = 0; - std::vector frames; - bool have_color = false; - bool have_cicp = false; - bool have_iccp = false; - bool have_srgb = false; - uint32_t x0 = 0; - uint32_t y0 = 0; - uint32_t delay_num = 1; - uint32_t delay_den = 10; - uint32_t dop = 0; - uint32_t bop = 0; - uint32_t w0 = w; - uint32_t h0 = h; + size_t bytes_per_pixel = 0; + std::vector frames; + FrameControl current_frame = {/*delay_num=*/1, /*delay_den=*/10, image_rect, + DisposeOp::NONE, BlendOp::SOURCE}; + + // Copies frame pixels / metadata from temporary storage. + // TODO(eustas): avoid copying. + const auto finalize_frame = [&]() -> Status { + if (!seen_pixel_data) { + return JXL_FAILURE("Frame / image without fdAT / IDAT chunks"); + } + if (!ctx.FinalizeStream(&ppf->metadata)) { + return JXL_FAILURE("Failed to finalize PNG substream"); + } + // Allocates the frame buffer. + const RectT& vp = current_frame.viewport; + size_t xsize = static_cast(vp.xsize()); + size_t ysize = static_cast(vp.ysize()); + JXL_ASSIGN_OR_RETURN(PackedImage image, + PackedImage::Create(xsize, ysize, format)); + for (size_t y = 0; y < ysize; ++y) { + // TODO(eustas): ensure multiplication is safe + memcpy(static_cast(image.pixels()) + image.stride * y, + ctx.frameRaw.rows[y], bytes_per_pixel * xsize); + } + frames.push_back(Frame{std::move(image), current_frame}); + seen_pixel_data = false; + return true; + }; while (!input.Eof()) { + if (seen_iend) { + return JXL_FAILURE("Exuberant input after IEND chunk"); + } Bytes chunk = input.ReadChunk(); if (chunk.empty()) { - return false; + return JXL_FAILURE("Malformed chunk"); } - id = LoadLE32(chunk.data() + 4); + Bytes type(chunk.data() + 4, 4); + id = LoadLE32(type.data()); + // Cut 'size' and 'type' at front and 'CRC' at the end. + Bytes payload(chunk.data() + 8, chunk.size() - 12); - if (id == MakeTag('a', 'c', 'T', 'L') && !hasInfo && !isAnimated) { - isAnimated = true; - ppf->info.have_animation = JXL_TRUE; - ppf->info.animation.tps_numerator = 1000; - ppf->info.animation.tps_denominator = 1; - } else if (id == MakeTag('I', 'E', 'N', 'D') || - (id == MakeTag('f', 'c', 'T', 'L') && - (!hasInfo || isAnimated))) { - if (hasInfo) { - if (ctx.FinalizeStream(&ppf->metadata)) { - // Allocates the frame buffer. - uint32_t duration = delay_num * 1000 / delay_den; - 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) { - // TODO(eustas): ensure multiplication is safe - memcpy(static_cast(frame.pixels()) + frame.stride * y, - ctx.frameRaw.rows[y], bytes_per_pixel * w0); + if (!isAbc(type[0]) || !isAbc(type[1]) || !isAbc(type[2]) || + !isAbc(type[3])) { + return JXL_FAILURE("Exotic PNG chunk"); + } + + switch (id) { + case MakeTag('a', 'c', 'T', 'L'): + if (seen_idat) { + JXL_DEBUG(2, "aCTL after IDAT ignored"); + continue; + } + if (seen_actl) { + JXL_DEBUG(2, "Duplicate aCTL chunk ignored"); + continue; + } + seen_actl = true; + ppf->info.have_animation = JXL_TRUE; + // TODO(eustas): decode from chunk? + ppf->info.animation.tps_numerator = 1000; + ppf->info.animation.tps_denominator = 1; + continue; + + case MakeTag('I', 'E', 'N', 'D'): + seen_iend = true; + JXL_RETURN_IF_ERROR(finalize_frame()); + continue; + + case MakeTag('f', 'c', 'T', 'L'): { + if (payload.size() != 26) { + return JXL_FAILURE("Unexpected fcTL payload size: %u", + static_cast(payload.size())); + } + if (seen_fctl && !seen_idat) { + return JXL_FAILURE("More than one fcTL before IDAT"); + } + if (seen_idat && !seen_actl) { + return JXL_FAILURE("fcTL after IDAT, but without acTL"); + } + seen_fctl = true; + + // TODO(eustas): check order? + // sequence_number = png_get_uint_32(payload.data()); + RectT raw_viewport(png_get_uint_32(payload.data() + 12), + png_get_uint_32(payload.data() + 16), + png_get_uint_32(payload.data() + 4), + png_get_uint_32(payload.data() + 8)); + uint8_t dispose_op = payload[24]; + if (dispose_op > kLastDisposeOp) { + return JXL_FAILURE("Invalid DisposeOp"); + } + uint8_t blend_op = payload[25]; + if (blend_op > kLastBlendOp) { + return JXL_FAILURE("Invalid BlendOp"); + } + FrameControl next_frame = { + /*delay_num=*/png_get_uint_16(payload.data() + 20), + /*delay_den=*/png_get_uint_16(payload.data() + 22), raw_viewport, + static_cast(dispose_op), static_cast(blend_op)}; + + if (!raw_viewport.Intersection(image_rect).IsSame(raw_viewport)) { + // Cropping happened. + return JXL_FAILURE("PNG frame is outside of image rect"); + } + + if (!seen_idat) { + // "Default" image is the first animation frame. Its viewport must + // cover the whole image area. + if (!raw_viewport.IsSame(image_rect)) { + return JXL_FAILURE( + "If the first animation frame is default image, its viewport " + "must cover full image"); } } else { - return false; + JXL_RETURN_IF_ERROR(finalize_frame()); + if (!ctx.InitPngDecoder(passthrough_chunks, next_frame.viewport)) { + return JXL_FAILURE("Failed to initialize PNG decoder"); + } } + current_frame = next_frame; + continue; } - if (id == MakeTag('I', 'E', 'N', 'D')) { - break; - } - if (chunk.size() < 34) { - return JXL_FAILURE("Received a chunk that is too small (%" PRIuS "B)", - chunk.size()); - } - // At this point the old frame is done. Let's start a new one. - w0 = png_get_uint_32(chunk.data() + 12); - h0 = png_get_uint_32(chunk.data() + 16); - x0 = png_get_uint_32(chunk.data() + 20); - y0 = png_get_uint_32(chunk.data() + 24); - delay_num = png_get_uint_16(chunk.data() + 28); - delay_den = png_get_uint_16(chunk.data() + 30); - dop = chunk[32]; - bop = chunk[33]; - - if (!delay_den) delay_den = 100; - - if (w0 > kMaxPNGSize || h0 > kMaxPNGSize || x0 > kMaxPNGSize || - y0 > kMaxPNGSize || x0 + w0 > w || y0 + h0 > h || dop > 2 || - bop > 1) { - return false; - } - - if (hasInfo) { - // Copy dimensions. - memcpy(ctx.ihdr.data() + 8, chunk.data() + 12, 8); - if (!ctx.InitPngDecoder(hasInfo, chunksInfo)) { - return false; + case MakeTag('I', 'D', 'A', 'T'): { + if (!frames.empty()) { + return JXL_FAILURE("IDAT after default image is over"); } - } - } else if (id == MakeTag('I', 'D', 'A', 'T')) { - // First IDAT chunk means we now have all header info - hasInfo = true; - JXL_CHECK(w == png_get_image_width(ctx.png_ptr, ctx.info_ptr)); - JXL_CHECK(h == png_get_image_height(ctx.png_ptr, ctx.info_ptr)); - int colortype = png_get_color_type(ctx.png_ptr, ctx.info_ptr); - int png_bit_depth = png_get_bit_depth(ctx.png_ptr, ctx.info_ptr); - ppf->info.bits_per_sample = png_bit_depth; - png_color_8p sigbits = nullptr; - png_get_sBIT(ctx.png_ptr, ctx.info_ptr, &sigbits); - if (colortype & 1) { - // palette will actually be 8-bit regardless of the index bitdepth - ppf->info.bits_per_sample = 8; - } - if (colortype & 2) { - ppf->info.num_color_channels = 3; - ppf->color_encoding.color_space = JXL_COLOR_SPACE_RGB; - if (sigbits && sigbits->red == sigbits->green && - sigbits->green == sigbits->blue) { - ppf->info.bits_per_sample = sigbits->red; - } else if (sigbits) { - int maxbps = - std::max(sigbits->red, std::max(sigbits->green, sigbits->blue)); - JXL_WARNING( - "sBIT chunk: bit depths for R, G, and B are not the same (%i " - "%i %i), while in JPEG XL they have to be the same. Setting " - "RGB bit depth to %i.", - sigbits->red, sigbits->green, sigbits->blue, maxbps); - ppf->info.bits_per_sample = maxbps; - } - } else { - ppf->info.num_color_channels = 1; - ppf->color_encoding.color_space = JXL_COLOR_SPACE_GRAY; - if (sigbits) ppf->info.bits_per_sample = sigbits->gray; - } - if (colortype & 4 || - png_get_valid(ctx.png_ptr, ctx.info_ptr, PNG_INFO_tRNS)) { - ppf->info.alpha_bits = ppf->info.bits_per_sample; - if (sigbits && sigbits->alpha != ppf->info.bits_per_sample) { - JXL_WARNING( - "sBIT chunk: bit depths for RGBA are inconsistent " - "(%i %i %i %i). Setting A bitdepth to %i.", - sigbits->red, sigbits->green, sigbits->blue, sigbits->alpha, - ppf->info.bits_per_sample); - } - } else { - ppf->info.alpha_bits = 0; - } - ppf->color_encoding.color_space = - (ppf->info.num_color_channels == 1 ? JXL_COLOR_SPACE_GRAY - : JXL_COLOR_SPACE_RGB); - ppf->info.xsize = w; - ppf->info.ysize = h; - JXL_RETURN_IF_ERROR(VerifyDimensions(constraints, w, h)); - num_channels = - ppf->info.num_color_channels + (ppf->info.alpha_bits ? 1 : 0); - format = { - /*num_channels=*/num_channels, - /*data_type=*/ppf->info.bits_per_sample > 8 ? JXL_TYPE_UINT16 - : JXL_TYPE_UINT8, - /*endianness=*/JXL_BIG_ENDIAN, - /*align=*/0, - }; - if (png_bit_depth > 8 && format.data_type == JXL_TYPE_UINT8) { - png_set_strip_16(ctx.png_ptr); - } - bytes_per_pixel = - num_channels * (format.data_type == JXL_TYPE_UINT16 ? 2 : 1); - // TODO(eustas): ensure multiplication is safe - size_t rowbytes = w * bytes_per_pixel; - if (h > std::numeric_limits::max() / rowbytes) { - return JXL_FAILURE("Image too big."); - } - JXL_RETURN_IF_ERROR(ctx.frameRaw.Resize(rowbytes, h)); + if (!seen_idat) { + // First IDAT means that all metadata is ready. + seen_idat = true; + JXL_CHECK(image_rect.xsize() == + png_get_image_width(ctx.png_ptr, ctx.info_ptr)); + JXL_CHECK(image_rect.ysize() == + png_get_image_height(ctx.png_ptr, ctx.info_ptr)); + JXL_RETURN_IF_ERROR(VerifyDimensions(constraints, image_rect.xsize(), + image_rect.ysize())); + ppf->info.xsize = image_rect.xsize(); + ppf->info.ysize = image_rect.ysize(); - if (!ctx.FeedChunks(chunk)) { - return false; + png_color_8p sig_bits = nullptr; + // Error is OK -> sig_bits remains nullptr. + png_get_sBIT(ctx.png_ptr, ctx.info_ptr, &sig_bits); + SetColorData(ppf, png_get_color_type(ctx.png_ptr, ctx.info_ptr), + png_get_bit_depth(ctx.png_ptr, ctx.info_ptr), sig_bits, + png_get_valid(ctx.png_ptr, ctx.info_ptr, PNG_INFO_tRNS)); + num_channels = + ppf->info.num_color_channels + (ppf->info.alpha_bits ? 1 : 0); + format = { + /*num_channels=*/num_channels, + /*data_type=*/ppf->info.bits_per_sample > 8 ? JXL_TYPE_UINT16 + : JXL_TYPE_UINT8, + /*endianness=*/JXL_BIG_ENDIAN, + /*align=*/0, + }; + bytes_per_pixel = + num_channels * (format.data_type == JXL_TYPE_UINT16 ? 2 : 1); + // TODO(eustas): ensure multiplication is safe + uint64_t row_bytes = + static_cast(image_rect.xsize()) * bytes_per_pixel; + uint64_t max_rows = std::numeric_limits::max() / row_bytes; + if (image_rect.ysize() > max_rows) { + return JXL_FAILURE("Image too big."); + } + // TODO(eustas): drop frameRaw + JXL_RETURN_IF_ERROR( + ctx.frameRaw.Resize(row_bytes, image_rect.ysize())); + } + + if (!ctx.FeedChunks(chunk)) { + return JXL_FAILURE("Decoding IDAT failed"); + } + seen_pixel_data = true; + continue; } - } else if (id == MakeTag('f', 'd', 'A', 'T') && isAnimated) { - if (!hasInfo) { - return JXL_FAILURE("fdAT chunk before IDAT"); + + case MakeTag('f', 'd', 'A', 'T'): { + if (!seen_idat) { + return JXL_FAILURE("fdAT chunk before IDAT"); + } + if (!seen_actl) { + return JXL_FAILURE("fdAT chunk before acTL"); + } + /* The 'fdAT' chunk has... the same structure as an 'IDAT' chunk, + * except preceded by a sequence number. */ + if (payload.size() < 4) { + return JXL_FAILURE("Corrupted fdAT chunk"); + } + // Turn 'fdAT' to 'IDAT' by cutting sequence number and replacing tag. + std::array preamble; + png_save_uint_32(preamble.data(), payload.size() - 4); + memcpy(preamble.data() + 4, "IDAT", 4); + // Cut-off 'size', 'type' and 'sequence_number' + Bytes chunk_tail(chunk.data() + 12, chunk.size() - 12); + if (!ctx.FeedChunks(Bytes(preamble), chunk_tail)) { + return JXL_FAILURE("Decoding fdAT failed"); + } + seen_pixel_data = true; + continue; } - /* The 'fdAT' chunk has... the same structure as an 'IDAT' chunk, - * except preceded by a sequence number. */ - size_t payload_size = chunk.size() - 12; - if (payload_size < 4) { - return JXL_FAILURE("Corrupted fdAT chunk"); - } - // Turn 'fdAT' to 'IDAT' by cutting sequence number and replacing tag. - std::array preamble; - png_save_uint_32(preamble.data(), payload_size - 4); - memcpy(preamble.data() + 4, "IDAT", 4); - if (!ctx.FeedChunks(Bytes(preamble), - Bytes(chunk.data() + 12, chunk.size() - 12))) { - return false; - } - } else if (id == MakeTag('c', 'I', 'C', 'P')) { - // Color profile chunks: cICP has the highest priority, followed by - // iCCP and sRGB (which shouldn't co-exist, but if they do, we use - // iCCP), followed finally by gAMA and cHRM. - if (DecodeCicpChunk(Bytes(chunk.data() + 8, chunk.size() - 12), - &ppf->color_encoding)) { - have_cicp = true; - have_color = true; + + case MakeTag('c', 'I', 'C', 'P'): + if (color_info_type == ColorInfoType::CICP) { + JXL_DEBUG(2, "Excessive colorspace definition; cICP chunk ignored"); + continue; + } + JXL_RETURN_IF_ERROR(DecodeCicpChunk(payload, &ppf->color_encoding)); ppf->icc.clear(); ppf->primary_color_representation = PackedPixelFile::kColorEncodingIsPrimary; - } - } else if (!have_cicp && id == MakeTag('i', 'C', 'C', 'P')) { - if (!ctx.FeedChunks(chunk)) { - JXL_WARNING("Corrupt iCCP chunk"); - return false; - } + color_info_type = ColorInfoType::CICP; + continue; - // TODO(jon): catch special case of PQ and synthesize color encoding - // in that case - int compression_type; - png_bytep profile; - png_charp name; - png_uint_32 proflen = 0; - auto ok = png_get_iCCP(ctx.png_ptr, ctx.info_ptr, &name, - &compression_type, &profile, &proflen); - if (ok && proflen) { - ppf->icc.assign(profile, profile + proflen); + case MakeTag('i', 'C', 'C', 'P'): { + if (color_info_type == ColorInfoType::ICCP_OR_SRGB) { + return JXL_FAILURE("Repeated iCCP / sRGB chunk"); + } + if (color_info_type > ColorInfoType::ICCP_OR_SRGB) { + JXL_DEBUG(2, "Excessive colorspace definition; iCCP chunk ignored"); + continue; + } + // Let PNG decoder deal with chunk processing. + if (!ctx.FeedChunks(chunk)) { + return JXL_FAILURE("Corrupt iCCP chunk"); + } + color_info_type = ColorInfoType::ICCP_OR_SRGB; + + // TODO(jon): catch special case of PQ and synthesize color encoding + // in that case + int compression_type = 0; + png_bytep profile = nullptr; + png_charp name = nullptr; + png_uint_32 profile_len = 0; + png_uint_32 ok = + png_get_iCCP(ctx.png_ptr, ctx.info_ptr, &name, &compression_type, + &profile, &profile_len); + if (!ok || !profile_len) { + return JXL_FAILURE("Malformed / incomplete iCCP chunk"); + } + ppf->icc.assign(profile, profile + profile_len); ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary; - have_color = true; - have_iccp = true; - } else { - // TODO(eustas): JXL_WARNING? - } - } else if (!have_cicp && !have_iccp && id == MakeTag('s', 'R', 'G', 'B')) { - JXL_RETURN_IF_ERROR(DecodeSrgbChunk( - Bytes(chunk.data() + 8, chunk.size() - 12), &ppf->color_encoding)); - have_srgb = true; - have_color = true; - } else if (!have_cicp && !have_srgb && !have_iccp && - id == MakeTag('g', 'A', 'M', 'A')) { - JXL_RETURN_IF_ERROR(DecodeGamaChunk( - Bytes(chunk.data() + 8, chunk.size() - 12), &ppf->color_encoding)); - have_color = true; - } else if (!have_cicp && !have_srgb && !have_iccp && - id == MakeTag('c', 'H', 'R', 'M')) { - JXL_RETURN_IF_ERROR(DecodeChrmChunk( - Bytes(chunk.data() + 8, chunk.size() - 12), &ppf->color_encoding)); - have_color = true; - } else if (id == MakeTag('e', 'X', 'I', 'f')) { - ppf->metadata.exif.resize(chunk.size() - 12); - memcpy(ppf->metadata.exif.data(), chunk.data() + 8, chunk.size() - 12); - } else if (!isAbc(chunk[4]) || !isAbc(chunk[5]) || !isAbc(chunk[6]) || - !isAbc(chunk[7])) { - return false; - } else { - if (!ctx.FeedChunks(chunk)) { - return false; - } - if (!hasInfo) { - chunksInfo.push_back(chunk); + color_info_type = ColorInfoType::ICCP_OR_SRGB; continue; } + + case MakeTag('s', 'R', 'G', 'B'): + if (color_info_type == ColorInfoType::ICCP_OR_SRGB) { + return JXL_FAILURE("Repeated iCCP / sRGB chunk"); + } + if (color_info_type > ColorInfoType::ICCP_OR_SRGB) { + JXL_DEBUG(2, "Excessive colorspace definition; sRGB chunk ignored"); + continue; + } + JXL_RETURN_IF_ERROR(DecodeSrgbChunk(payload, &ppf->color_encoding)); + color_info_type = ColorInfoType::ICCP_OR_SRGB; + continue; + + case MakeTag('g', 'A', 'M', 'A'): + if (color_info_type >= ColorInfoType::GAMA_OR_CHRM) { + JXL_DEBUG(2, "Excessive colorspace definition; gAMA chunk ignored"); + continue; + } + JXL_RETURN_IF_ERROR(DecodeGamaChunk(payload, &ppf->color_encoding)); + color_info_type = ColorInfoType::GAMA_OR_CHRM; + continue; + + case MakeTag('c', 'H', 'R', 'M'): + if (color_info_type >= ColorInfoType::GAMA_OR_CHRM) { + JXL_DEBUG(2, "Excessive colorspace definition; cHRM chunk ignored"); + continue; + } + JXL_RETURN_IF_ERROR(DecodeChrmChunk(payload, &ppf->color_encoding)); + color_info_type = ColorInfoType::GAMA_OR_CHRM; + continue; + + case MakeTag('e', 'X', 'I', 'f'): + // TODO(eustas): next eXIF chunk overwrites current; is it ok? + ppf->metadata.exif.resize(payload.size()); + memcpy(ppf->metadata.exif.data(), payload.data(), payload.size()); + continue; + + default: + // We don't know what is that, just pass through. + if (!ctx.FeedChunks(chunk)) { + return JXL_FAILURE("PNG decoder failed to process chunk"); + } + // If it happens before IDAT, we consider it metadata and pass to all + // sub-decoders. + if (!seen_idat) { + passthrough_chunks.push_back(chunk); + } + continue; } } - JXL_RETURN_IF_ERROR(ApplyColorHints(color_hints, have_color, - ppf->info.num_color_channels == 1, ppf)); + bool color_is_already_set = (color_info_type != ColorInfoType::NONE); + bool is_gray = (ppf->info.num_color_channels == 1); + JXL_RETURN_IF_ERROR( + ApplyColorHints(color_hints, color_is_already_set, is_gray, ppf)); bool has_nontrivial_background = false; bool previous_frame_should_be_cleared = false; - enum { - DISPOSE_OP_NONE = 0, - DISPOSE_OP_BACKGROUND = 1, - DISPOSE_OP_PREVIOUS = 2, - }; - enum { - BLEND_OP_SOURCE = 0, - BLEND_OP_OVER = 1, - }; for (size_t i = 0; i < frames.size(); i++) { - auto& frame = frames[i]; - JXL_ASSERT(frame.data.xsize == frame.xsize); - JXL_ASSERT(frame.data.ysize == frame.ysize); + Frame& frame = frames[i]; + const FrameControl& fc = frame.metadata; + const RectT vp = fc.viewport; + const auto& pixels = frame.pixels; + size_t xsize = pixels.xsize; + size_t ysize = pixels.ysize; + JXL_ASSERT(xsize == vp.xsize()); + JXL_ASSERT(ysize == vp.ysize()); - // Before encountering a DISPOSE_OP_NONE frame, the canvas is filled with 0, - // so DISPOSE_OP_BACKGROUND and DISPOSE_OP_PREVIOUS are equivalent. - if (frame.dispose_op == DISPOSE_OP_NONE) { + // Before encountering a DISPOSE_OP_NONE frame, the canvas is filled with + // 0, so DISPOSE_OP_BACKGROUND and DISPOSE_OP_PREVIOUS are equivalent. + if (fc.dispose_op == DisposeOp::NONE) { has_nontrivial_background = true; } - bool should_blend = frame.blend_op == BLEND_OP_OVER; + bool should_blend = fc.blend_op == BlendOp::OVER; bool use_for_next_frame = - has_nontrivial_background && frame.dispose_op != DISPOSE_OP_PREVIOUS; - size_t x0 = frame.x0; - size_t y0 = frame.y0; - size_t xsize = frame.data.xsize; - size_t ysize = frame.data.ysize; + has_nontrivial_background && fc.dispose_op != DisposeOp::PREVIOUS; + size_t x0 = vp.x0(); + size_t y0 = vp.y0(); if (previous_frame_should_be_cleared) { - size_t px0 = frames[i - 1].x0; - size_t py0 = frames[i - 1].y0; - size_t pxs = frames[i - 1].xsize; - size_t pys = frames[i - 1].ysize; + const auto& pvp = frames[i - 1].metadata.viewport; + size_t px0 = pvp.x0(); + size_t py0 = pvp.y0(); + size_t pxs = pvp.xsize(); + size_t pys = pvp.ysize(); if (px0 >= x0 && py0 >= y0 && px0 + pxs <= x0 + xsize && - py0 + pys <= y0 + ysize && frame.blend_op == BLEND_OP_SOURCE && + py0 + pys <= y0 + ysize && fc.blend_op == BlendOp::SOURCE && use_for_next_frame) { - // If the previous frame is entirely contained in the current frame and - // we are using BLEND_OP_SOURCE, nothing special needs to be done. - ppf->frames.emplace_back(std::move(frame.data)); + // If the previous frame is entirely contained in the current frame + // and we are using BLEND_OP_SOURCE, nothing special needs to be done. + ppf->frames.emplace_back(std::move(frame.pixels)); } else if (px0 == x0 && py0 == y0 && px0 + pxs == x0 + xsize && py0 + pys == y0 + ysize && use_for_next_frame) { // If the new frame has the same size as the old one, but we are // blending, we can instead just not blend. should_blend = false; - ppf->frames.emplace_back(std::move(frame.data)); + ppf->frames.emplace_back(std::move(frame.pixels)); } else if (px0 <= x0 && py0 <= y0 && px0 + pxs >= x0 + xsize && 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. JXL_ASSIGN_OR_RETURN(PackedImage new_data, - PackedImage::Create(pxs, pys, frame.data.format)); + PackedImage::Create(pxs, pys, pixels.format)); memset(new_data.pixels(), 0, new_data.pixels_size); for (size_t y = 0; y < ysize; y++) { size_t bytes_per_pixel = PackedImage::BitsPerChannel(new_data.format.data_type) * new_data.format.num_channels / 8; - memcpy(static_cast(new_data.pixels()) + - new_data.stride * (y + y0 - py0) + - bytes_per_pixel * (x0 - px0), - static_cast(frame.data.pixels()) + - frame.data.stride * y, - xsize * bytes_per_pixel); + memcpy( + static_cast(new_data.pixels()) + + new_data.stride * (y + y0 - py0) + + bytes_per_pixel * (x0 - px0), + static_cast(pixels.pixels()) + pixels.stride * y, + xsize * bytes_per_pixel); } x0 = px0; @@ -1013,7 +1204,7 @@ Status DecodeImageAPNG(const Span bytes, } else { // If all else fails, insert a placeholder blank frame with kReplace. JXL_ASSIGN_OR_RETURN(PackedImage blank, - PackedImage::Create(pxs, pys, frame.data.format)); + PackedImage::Create(pxs, pys, pixels.format)); memset(blank.pixels(), 0, blank.pixels_size); ppf->frames.emplace_back(std::move(blank)); auto& pframe = ppf->frames.back(); @@ -1028,10 +1219,10 @@ Status DecodeImageAPNG(const Span bytes, pframe.frame_info.layer_info.blend_info.blendmode = JXL_BLEND_REPLACE; pframe.frame_info.layer_info.blend_info.source = 1; pframe.frame_info.layer_info.save_as_reference = 1; - ppf->frames.emplace_back(std::move(frame.data)); + ppf->frames.emplace_back(std::move(frame.pixels)); } } else { - ppf->frames.emplace_back(std::move(frame.data)); + ppf->frames.emplace_back(std::move(frame.pixels)); } auto& pframe = ppf->frames.back(); @@ -1039,7 +1230,8 @@ Status DecodeImageAPNG(const Span bytes, pframe.frame_info.layer_info.crop_y0 = y0; pframe.frame_info.layer_info.xsize = xsize; pframe.frame_info.layer_info.ysize = ysize; - pframe.frame_info.duration = frame.duration; + pframe.frame_info.duration = + fc.delay_num * 1000 / (fc.delay_den ? fc.delay_den : 100); pframe.frame_info.layer_info.blend_info.blendmode = should_blend ? JXL_BLEND_BLEND : JXL_BLEND_REPLACE; bool is_full_size = x0 == 0 && y0 == 0 && xsize == ppf->info.xsize && @@ -1050,7 +1242,7 @@ Status DecodeImageAPNG(const Span bytes, pframe.frame_info.layer_info.save_as_reference = use_for_next_frame ? 1 : 0; previous_frame_should_be_cleared = - has_nontrivial_background && frame.dispose_op == DISPOSE_OP_BACKGROUND; + has_nontrivial_background && (fc.dispose_op == DisposeOp::BACKGROUND); } if (ppf->frames.empty()) return JXL_FAILURE("No frames decoded"); diff --git a/third_party/jpeg-xl/lib/extras/dec/decode.h b/third_party/jpeg-xl/lib/extras/dec/decode.h index 26dc1409df89..bbffc7e0f0a3 100644 --- a/third_party/jpeg-xl/lib/extras/dec/decode.h +++ b/third_party/jpeg-xl/lib/extras/dec/decode.h @@ -8,11 +8,9 @@ // Facade for image decoders (PNG, PNM, ...). -#include -#include - +#include +#include #include -#include #include "lib/extras/dec/color_hints.h" #include "lib/jxl/base/span.h" diff --git a/third_party/jpeg-xl/lib/extras/dec/gif.cc b/third_party/jpeg-xl/lib/extras/dec/gif.cc index 243d8b5103a0..f5dfd728828f 100644 --- a/third_party/jpeg-xl/lib/extras/dec/gif.cc +++ b/third_party/jpeg-xl/lib/extras/dec/gif.cc @@ -9,8 +9,8 @@ #include #endif #include -#include +#include #include #include #include @@ -18,7 +18,7 @@ #include "lib/extras/size_constraints.h" #include "lib/jxl/base/compiler_specific.h" #include "lib/jxl/base/rect.h" -#include "lib/jxl/sanitizers.h" +#include "lib/jxl/base/sanitizers.h" namespace jxl { namespace extras { diff --git a/third_party/jpeg-xl/lib/extras/dec/jpegli.cc b/third_party/jpeg-xl/lib/extras/dec/jpegli.cc index 443dfe86ba09..4aa224321d39 100644 --- a/third_party/jpeg-xl/lib/extras/dec/jpegli.cc +++ b/third_party/jpeg-xl/lib/extras/dec/jpegli.cc @@ -6,16 +6,16 @@ #include "lib/extras/dec/jpegli.h" #include -#include #include -#include +#include +#include #include #include #include "lib/jpegli/decode.h" +#include "lib/jxl/base/sanitizers.h" #include "lib/jxl/base/status.h" -#include "lib/jxl/sanitizers.h" namespace jxl { namespace extras { @@ -181,7 +181,9 @@ Status DecodeJpeg(const std::vector& compressed, } int nbcomp = cinfo.num_components; if (nbcomp != 1 && nbcomp != 3) { - return failure("unsupported number of components in JPEG"); + std::string msg = + "unsupported number of components in JPEG: " + std::to_string(nbcomp); + return failure(msg.c_str()); } if (dparams.force_rgb) { cinfo.out_color_space = JCS_RGB; diff --git a/third_party/jpeg-xl/lib/extras/dec/jpg.cc b/third_party/jpeg-xl/lib/extras/dec/jpg.cc index a65b46b4c86c..9cd6960a40b5 100644 --- a/third_party/jpeg-xl/lib/extras/dec/jpg.cc +++ b/third_party/jpeg-xl/lib/extras/dec/jpg.cc @@ -8,16 +8,16 @@ #if JPEGXL_ENABLE_JPEG #include "lib/jxl/base/include_jpeglib.h" // NOLINT #endif -#include #include +#include #include #include #include #include "lib/extras/size_constraints.h" +#include "lib/jxl/base/sanitizers.h" #include "lib/jxl/base/status.h" -#include "lib/jxl/sanitizers.h" namespace jxl { namespace extras { diff --git a/third_party/jpeg-xl/lib/extras/dec/pgx_test.cc b/third_party/jpeg-xl/lib/extras/dec/pgx_test.cc index 5dbc3149a2cd..1c90e1f1de1c 100644 --- a/third_party/jpeg-xl/lib/extras/dec/pgx_test.cc +++ b/third_party/jpeg-xl/lib/extras/dec/pgx_test.cc @@ -9,6 +9,7 @@ #include "lib/extras/packed_image_convert.h" #include "lib/jxl/image_bundle.h" +#include "lib/jxl/test_utils.h" #include "lib/jxl/testing.h" namespace jxl { @@ -26,7 +27,7 @@ TEST(CodecPGXTest, Test8bits) { ThreadPool* pool = nullptr; EXPECT_TRUE(DecodeImagePGX(MakeSpan(pgx.c_str()), ColorHints(), &ppf)); - CodecInOut io; + CodecInOut io{jxl::test::MemoryManager()}; EXPECT_TRUE(ConvertPackedPixelFileToCodecInOut(ppf, pool, &io)); ScaleImage(255.f, io.Main().color()); @@ -53,7 +54,7 @@ TEST(CodecPGXTest, Test16bits) { ThreadPool* pool = nullptr; EXPECT_TRUE(DecodeImagePGX(MakeSpan(pgx.c_str()), ColorHints(), &ppf)); - CodecInOut io; + CodecInOut io{jxl::test::MemoryManager()}; EXPECT_TRUE(ConvertPackedPixelFileToCodecInOut(ppf, pool, &io)); ScaleImage(255.f, io.Main().color()); diff --git a/third_party/jpeg-xl/lib/extras/dec/pnm.cc b/third_party/jpeg-xl/lib/extras/dec/pnm.cc index b3f9cd1206ce..3daa16187fca 100644 --- a/third_party/jpeg-xl/lib/extras/dec/pnm.cc +++ b/third_party/jpeg-xl/lib/extras/dec/pnm.cc @@ -15,6 +15,7 @@ #include "lib/extras/size_constraints.h" #include "lib/jxl/base/bits.h" +#include "lib/jxl/base/c_callback_support.h" #include "lib/jxl/base/span.h" #include "lib/jxl/base/status.h" diff --git a/third_party/jpeg-xl/lib/extras/enc/jpg.cc b/third_party/jpeg-xl/lib/extras/enc/jpg.cc index a2ef4a9fc444..9bb12516c568 100644 --- a/third_party/jpeg-xl/lib/extras/enc/jpg.cc +++ b/third_party/jpeg-xl/lib/extras/enc/jpg.cc @@ -21,8 +21,8 @@ #include "lib/extras/exif.h" #include "lib/jxl/base/common.h" +#include "lib/jxl/base/sanitizers.h" #include "lib/jxl/base/status.h" -#include "lib/jxl/sanitizers.h" #if JPEGXL_ENABLE_SJPEG #include "sjpeg.h" #include "sjpegi.h" diff --git a/third_party/jpeg-xl/lib/extras/enc/jxl.cc b/third_party/jpeg-xl/lib/extras/enc/jxl.cc index d563f298e6e4..a3774243f724 100644 --- a/third_party/jpeg-xl/lib/extras/enc/jxl.cc +++ b/third_party/jpeg-xl/lib/extras/enc/jxl.cc @@ -202,7 +202,7 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf, fprintf(stderr, "JPEG bitstream reconstruction data could not be created. " "Possibly there is too much tail data.\n" - "Try using --jpeg_store_metadata 0, to losslessly " + "Try using --allow_jpeg_reconstruction 0, to losslessly " "recompress the JPEG image data without bitstream " "reconstruction data.\n"); } else { @@ -223,7 +223,14 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf, std::max(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 = TO_JXL_BOOL(lossless); + auto non_perceptual_option = std::find_if( + params.options.begin(), params.options.end(), [](JXLOption option) { + return option.id == + JXL_ENC_FRAME_SETTING_DISABLE_PERCEPTUAL_HEURISTICS; + }); + const bool non_perceptual = non_perceptual_option != params.options.end() && + non_perceptual_option->ival == 1; + basic_info.uses_original_profile = TO_JXL_BOOL(lossless || non_perceptual); if (params.override_bitdepth != 0) { basic_info.bits_per_sample = params.override_bitdepth; basic_info.exponent_bits_per_sample = diff --git a/third_party/jpeg-xl/lib/extras/packed_image.h b/third_party/jpeg-xl/lib/extras/packed_image.h index a66ddfbd707a..be7eb37848f2 100644 --- a/third_party/jpeg-xl/lib/extras/packed_image.h +++ b/third_party/jpeg-xl/lib/extras/packed_image.h @@ -12,22 +12,19 @@ #include #include #include -#include -#include -#include -#include #include #include +#include +#include +#include +#include #include #include -#include -#include #include #include #include "lib/jxl/base/byte_order.h" -#include "lib/jxl/base/c_callback_support.h" #include "lib/jxl/base/common.h" #include "lib/jxl/base/status.h" 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 7e4b592fc4a1..19bdbe0445a6 100644 --- a/third_party/jpeg-xl/lib/extras/packed_image_convert.cc +++ b/third_party/jpeg-xl/lib/extras/packed_image_convert.cc @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -27,6 +28,7 @@ Status ConvertPackedFrameToImageBundle(const JxlBasicInfo& info, const PackedFrame& frame, const CodecInOut& io, ThreadPool* pool, ImageBundle* bundle) { + JxlMemoryManager* memory_manager = io.memory_manager; JXL_ASSERT(frame.color.pixels() != nullptr); size_t frame_bits_per_sample = input_bitdepth.type == JXL_BIT_DEPTH_FROM_PIXEL_FORMAT @@ -65,8 +67,9 @@ 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]; - JXL_ASSIGN_OR_RETURN(bundle->extra_channels()[i], - ImageF::Create(ppf_ec.xsize, ppf_ec.ysize)); + JXL_ASSIGN_OR_RETURN( + bundle->extra_channels()[i], + ImageF::Create(memory_manager, 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])); @@ -76,6 +79,7 @@ Status ConvertPackedFrameToImageBundle(const JxlBasicInfo& info, Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf, ThreadPool* pool, CodecInOut* io) { + JxlMemoryManager* memory_manager = io->memory_manager; const bool has_alpha = ppf.info.alpha_bits != 0; JXL_ASSERT(!ppf.frames.empty()); if (has_alpha) { @@ -179,7 +183,7 @@ Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf, // Convert the pixels io->frames.clear(); for (const auto& frame : ppf.frames) { - ImageBundle bundle(&io->metadata.m); + ImageBundle bundle(memory_manager, &io->metadata.m); JXL_RETURN_IF_ERROR(ConvertPackedFrameToImageBundle( ppf.info, ppf.input_bitdepth, frame, *io, pool, &bundle)); io->frames.push_back(std::move(bundle)); @@ -234,6 +238,7 @@ Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io, const ColorEncoding& c_desired, ThreadPool* pool, PackedPixelFile* ppf) { + JxlMemoryManager* memory_manager = io.memory_manager; const bool has_alpha = io.metadata.m.HasAlpha(); JXL_ASSERT(!io.frames.empty()); @@ -317,7 +322,7 @@ Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io, JXL_ASSIGN_OR_RETURN(ImageBundle ib, frame.Copy()); const ImageBundle* to_color_transform = &ib; ImageMetadata metadata = io.metadata.m; - ImageBundle store(&metadata); + ImageBundle store(memory_manager, &metadata); const ImageBundle* transformed; // TODO(firsching): handle the transform here. JXL_RETURN_IF_ERROR(TransformIfNeeded(*to_color_transform, c_desired, 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 8fc928a4fd0d..490895573e41 100644 --- a/third_party/jpeg-xl/lib/extras/tone_mapping_gbench.cc +++ b/third_party/jpeg-xl/lib/extras/tone_mapping_gbench.cc @@ -3,15 +3,18 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +#include + #include "benchmark/benchmark.h" -#include "lib/extras/codec.h" #include "lib/extras/tone_mapping.h" #include "lib/jxl/image.h" +#include "tools/no_memory_manager.h" namespace jxl { static void BM_ToneMapping(benchmark::State& state) { - JXL_ASSIGN_OR_DIE(Image3F color, Image3F::Create(2268, 1512)); + JxlMemoryManager* memory_manager = jpegxl::tools::NoMemoryManager(); + JXL_ASSIGN_OR_DIE(Image3F color, Image3F::Create(memory_manager, 2268, 1512)); FillImage(0.5f, &color); // Use linear Rec. 2020 so that `ToneMapTo` doesn't have to convert to it and @@ -25,9 +28,10 @@ static void BM_ToneMapping(benchmark::State& state) { for (auto _ : state) { state.PauseTiming(); - CodecInOut tone_mapping_input; - JXL_ASSIGN_OR_DIE(Image3F color2, - Image3F::Create(color.xsize(), color.ysize())); + CodecInOut tone_mapping_input{memory_manager}; + JXL_ASSIGN_OR_DIE( + Image3F color2, + Image3F::Create(memory_manager, color.xsize(), color.ysize())); CopyImageTo(color, &color2); tone_mapping_input.SetFromImage(std::move(color2), linear_rec2020); tone_mapping_input.metadata.m.SetIntensityTarget(255); diff --git a/third_party/jpeg-xl/lib/include/jxl/color_encoding.h b/third_party/jpeg-xl/lib/include/jxl/color_encoding.h index f5de188223d3..64d6131516b7 100644 --- a/third_party/jpeg-xl/lib/include/jxl/color_encoding.h +++ b/third_party/jpeg-xl/lib/include/jxl/color_encoding.h @@ -32,9 +32,9 @@ typedef enum { JXL_COLOR_SPACE_UNKNOWN, } JxlColorSpace; -/** Built-in whitepoints for color encoding. When decoding, the numerical xy - * whitepoint value can be read from the @ref JxlColorEncoding white_point field - * regardless of the enum value. When encoding, enum values except +/** Built-in white points for color encoding. When decoding, the numerical xy + * white point value can be read from the @ref JxlColorEncoding white_point + * field regardless of the enum value. When encoding, enum values except * ::JXL_WHITE_POINT_CUSTOM override the numerical fields. Some enum values * match a subset of CICP (Rec. ITU-T H.273 | ISO/IEC 23091-2:2019(E)), however * the white point and RGB primaries are separate enums here. @@ -78,7 +78,7 @@ typedef enum { * of CICP (Rec. ITU-T H.273 | ISO/IEC 23091-2:2019(E)) unless specified * otherwise. */ typedef enum { - /** As specified in SMPTE RP 431-2 */ + /** As specified in ITU-R BT.709-6 */ JXL_TRANSFER_FUNCTION_709 = 1, /** None of the other table entries describe the transfer function. */ JXL_TRANSFER_FUNCTION_UNKNOWN = 2, @@ -97,7 +97,7 @@ typedef enum { JXL_TRANSFER_FUNCTION_GAMMA = 65535, } JxlTransferFunction; -/** Renderig intent for color encoding, as specified in ISO 15076-1:2010 */ +/** Rendering intent for color encoding, as specified in ISO 15076-1:2010 */ typedef enum { /** vendor-specific */ JXL_RENDERING_INTENT_PERCEPTUAL = 0, @@ -117,7 +117,7 @@ typedef struct { JxlColorSpace color_space; /** Built-in white point. If this value is ::JXL_WHITE_POINT_CUSTOM, must - * use the numerical whitepoint values from white_point_xy. + * use the numerical white point values from white_point_xy. */ JxlWhitePoint white_point; diff --git a/third_party/jpeg-xl/lib/include/jxl/decode.h b/third_party/jpeg-xl/lib/include/jxl/decode.h index 599b8336f3fc..904bd72fa9fa 100644 --- a/third_party/jpeg-xl/lib/include/jxl/decode.h +++ b/third_party/jpeg-xl/lib/include/jxl/decode.h @@ -721,7 +721,7 @@ typedef enum { * It is often possible to use @ref JxlDecoderGetColorAsICCProfile as an * alternative anyway. The following scenarios are possible: * - The JPEG XL image has an attached ICC Profile, in that case, the encoded - * structured data is not available, this function will return an error + * structured data is not available and this function will return an error * status. @ref JxlDecoderGetColorAsICCProfile should be called instead. * - The JPEG XL image has an encoded structured color profile, and it * represents an RGB or grayscale color space. This function will return it. @@ -800,8 +800,8 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetICCProfileSize( * or the color profile of the decoded pixels. * @param icc_profile buffer to copy the ICC profile into * @param size size of the icc_profile buffer in bytes - * @return ::JXL_DEC_SUCCESS if the profile was successfully returned is - * available, ::JXL_DEC_NEED_MORE_INPUT if not yet available, @ref + * @return ::JXL_DEC_SUCCESS if the profile was successfully returned, + * ::JXL_DEC_NEED_MORE_INPUT if not yet available, @ref * JXL_DEC_ERROR if the profile doesn't exist or the output size is not * large enough. */ @@ -869,7 +869,7 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetDesiredIntensityTarget( * * This function must not be called before @ref JxlDecoderSetCms. * - * @param dec decoder orbject + * @param dec decoder object * @param color_encoding the output color encoding * @param icc_data bytes of the icc profile * @param icc_size size of the icc profile in bytes @@ -913,7 +913,7 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderPreviewOutBufferSize( const JxlDecoder* dec, const JxlPixelFormat* format, size_t* size); /** - * Sets the buffer to write the small resolution preview image + * Sets the buffer to write the low-resolution preview image * to. The size of the buffer must be at least as large as given by @ref * JxlDecoderPreviewOutBufferSize. The buffer follows the format described * by @ref JxlPixelFormat. The preview image dimensions are given by the @@ -962,10 +962,10 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetFrameName(const JxlDecoder* dec, /** * Outputs the blend information for the current frame for a specific extra - * channel. This function can be called when ::JXL_DEC_FRAME occurred for the - * current frame, even when have_animation in the @ref JxlBasicInfo is @ref - * JXL_FALSE. This information is only useful if coalescing is disabled; - * otherwise the decoder will have performed blending already. + * channel. This function can be called once the ::JXL_DEC_FRAME event occurred + * for the current frame, even if the `have_animation` field in the @ref + * JxlBasicInfo is @ref JXL_FALSE. This information is only useful if coalescing + * is disabled; otherwise the decoder will have performed blending already. * * @param dec decoder object * @param index the index of the extra channel @@ -1344,7 +1344,7 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetDecompressBoxes(JxlDecoder* dec, * animation allowing the decoder to jump to individual frames more * efficiently. * - "jbrd": JPEG reconstruction box, contains the information required to - * byte-for-byte losslessly recontruct a JPEG-1 image. The JPEG DCT + * byte-for-byte losslessly reconstruct a JPEG-1 image. The JPEG DCT * coefficients (pixel content) themselves as well as the ICC profile are * encoded in the JXL codestream (jxlc or jxlp) itself. EXIF, XMP and JUMBF * metadata is encoded in the corresponding boxes. The jbrd box itself @@ -1366,7 +1366,7 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetDecompressBoxes(JxlDecoder* dec, * @param decompressed which box type to get: JXL_FALSE to get the raw box type, * which can be "brob", JXL_TRUE, get the underlying box type. * @return ::JXL_DEC_SUCCESS if the value is available, ::JXL_DEC_ERROR if - * not, for example the JXL file does not use the container format. + * not, for example the JPEG XL file does not use the container format. */ JXL_EXPORT JxlDecoderStatus JxlDecoderGetBoxType(JxlDecoder* dec, JxlBoxType type, diff --git a/third_party/jpeg-xl/lib/include/jxl/encode.h b/third_party/jpeg-xl/lib/include/jxl/encode.h index bb11dd757287..880e47167974 100644 --- a/third_party/jpeg-xl/lib/include/jxl/encode.h +++ b/third_party/jpeg-xl/lib/include/jxl/encode.h @@ -388,6 +388,11 @@ typedef enum { */ JXL_ENC_FRAME_SETTING_USE_FULL_IMAGE_HEURISTICS = 38, + /** Disable perceptual optimizations. 0 = optimizations enabled (default), 1 = + * optimizations disabled. + */ + JXL_ENC_FRAME_SETTING_DISABLE_PERCEPTUAL_HEURISTICS = 39, + /** Enum value not to be used as an option. This value is added to force the * C compiler to have the enum to take a known size. */ diff --git a/third_party/jpeg-xl/lib/jpegli/libjpeg_test_util.cc b/third_party/jpeg-xl/lib/jpegli/libjpeg_test_util.cc index d34ec7e9997d..619942fa592c 100644 --- a/third_party/jpeg-xl/lib/jpegli/libjpeg_test_util.cc +++ b/third_party/jpeg-xl/lib/jpegli/libjpeg_test_util.cc @@ -5,8 +5,11 @@ #include "lib/jpegli/libjpeg_test_util.h" +#include + #include "lib/jxl/base/include_jpeglib.h" // NOLINT -#include "lib/jxl/sanitizers.h" +#include "lib/jxl/base/sanitizers.h" +#include "lib/jxl/base/status.h" namespace jpegli { diff --git a/third_party/jpeg-xl/lib/jpegli/test_utils.cc b/third_party/jpeg-xl/lib/jpegli/test_utils.cc index 5315c692a1f2..fc0d51648dde 100644 --- a/third_party/jpeg-xl/lib/jpegli/test_utils.cc +++ b/third_party/jpeg-xl/lib/jpegli/test_utils.cc @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -14,8 +15,8 @@ #include "lib/jpegli/encode.h" #include "lib/jxl/base/byte_order.h" #include "lib/jxl/base/printf_macros.h" +#include "lib/jxl/base/sanitizers.h" #include "lib/jxl/base/status.h" -#include "lib/jxl/sanitizers.h" #if !defined(TEST_DATA_PATH) #include "tools/cpp/runfiles/runfiles.h" diff --git a/third_party/jpeg-xl/lib/jxl/ac_strategy.cc b/third_party/jpeg-xl/lib/jxl/ac_strategy.cc index e7a72f5a3324..adcd34d8537c 100644 --- a/third_party/jpeg-xl/lib/jxl/ac_strategy.cc +++ b/third_party/jpeg-xl/lib/jxl/ac_strategy.cc @@ -5,9 +5,10 @@ #include "lib/jxl/ac_strategy.h" -#include +#include #include +#include #include #include "lib/jxl/base/bits.h" @@ -83,9 +84,11 @@ constexpr size_t AcStrategy::kMaxCoeffBlocks; constexpr size_t AcStrategy::kMaxBlockDim; constexpr size_t AcStrategy::kMaxCoeffArea; -StatusOr AcStrategyImage::Create(size_t xsize, size_t ysize) { +StatusOr AcStrategyImage::Create( + JxlMemoryManager* memory_manager, size_t xsize, size_t ysize) { AcStrategyImage img; - JXL_ASSIGN_OR_RETURN(img.layers_, ImageB::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(img.layers_, + ImageB::Create(memory_manager, xsize, ysize)); img.row_ = img.layers_.Row(0); img.stride_ = img.layers_.PixelsPerRow(); return img; diff --git a/third_party/jpeg-xl/lib/jxl/ac_strategy.h b/third_party/jpeg-xl/lib/jxl/ac_strategy.h index fd40b0ced8c3..5de72485d9d0 100644 --- a/third_party/jpeg-xl/lib/jxl/ac_strategy.h +++ b/third_party/jpeg-xl/lib/jxl/ac_strategy.h @@ -6,9 +6,10 @@ #ifndef LIB_JXL_AC_STRATEGY_H_ #define LIB_JXL_AC_STRATEGY_H_ -#include -#include +#include +#include +#include #include // kMaxVectorSize #include "lib/jxl/base/compiler_specific.h" @@ -196,7 +197,8 @@ class AcStrategyRow { class AcStrategyImage { public: AcStrategyImage() = default; - static StatusOr Create(size_t xsize, size_t ysize); + static StatusOr Create(JxlMemoryManager* memory_manager, + size_t xsize, size_t ysize); AcStrategyImage(AcStrategyImage&&) = default; AcStrategyImage& operator=(AcStrategyImage&&) = default; @@ -252,6 +254,8 @@ class AcStrategyImage { // Count the number of blocks of a given type. size_t CountBlocks(AcStrategy::Type type) const; + JxlMemoryManager* memory_manager() const { return layers_.memory_manager(); } + private: ImageB layers_; uint8_t* JXL_RESTRICT row_; diff --git a/third_party/jpeg-xl/lib/jxl/ans_test.cc b/third_party/jpeg-xl/lib/jxl/ans_test.cc index 83a2e732f8d4..94a99ab05f98 100644 --- a/third_party/jpeg-xl/lib/jxl/ans_test.cc +++ b/third_party/jpeg-xl/lib/jxl/ans_test.cc @@ -3,9 +3,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#include -#include +#include +#include +#include #include #include "lib/jxl/ans_params.h" @@ -15,6 +16,7 @@ #include "lib/jxl/dec_bit_reader.h" #include "lib/jxl/enc_ans.h" #include "lib/jxl/enc_bit_writer.h" +#include "lib/jxl/test_utils.h" #include "lib/jxl/testing.h" namespace jxl { @@ -22,10 +24,11 @@ namespace { void RoundtripTestcase(int n_histograms, int alphabet_size, const std::vector& input_values) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); constexpr uint16_t kMagic1 = 0x9e33; constexpr uint16_t kMagic2 = 0x8b04; - BitWriter writer; + BitWriter writer{memory_manager}; // Space for magic bytes. BitWriter::Allotment allotment_magic1(&writer, 16); writer.Write(16, kMagic1); @@ -36,8 +39,9 @@ void RoundtripTestcase(int n_histograms, int alphabet_size, std::vector> input_values_vec; input_values_vec.push_back(input_values); - BuildAndEncodeHistograms(HistogramParams(), n_histograms, input_values_vec, - &codes, &context_map, &writer, 0, nullptr); + BuildAndEncodeHistograms(memory_manager, HistogramParams(), n_histograms, + input_values_vec, &codes, &context_map, &writer, 0, + nullptr); WriteTokens(input_values_vec[0], codes, context_map, 0, &writer, 0, nullptr); // Magic bytes + padding @@ -54,10 +58,11 @@ void RoundtripTestcase(int n_histograms, int alphabet_size, std::vector dec_context_map; ANSCode decoded_codes; - ASSERT_TRUE( - DecodeHistograms(&br, n_histograms, &decoded_codes, &dec_context_map)); + ASSERT_TRUE(DecodeHistograms(memory_manager, &br, n_histograms, + &decoded_codes, &dec_context_map)); ASSERT_EQ(dec_context_map, context_map); - ANSSymbolReader reader(&decoded_codes, &br); + JXL_ASSIGN_OR_DIE(ANSSymbolReader reader, + ANSSymbolReader::Create(&decoded_codes, &br)); for (const Token& symbol : input_values) { uint32_t read_symbol = @@ -156,6 +161,7 @@ TEST(ANSTest, RandomUnbalancedStreamRoundtripBig) { } TEST(ANSTest, UintConfigRoundtrip) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); for (size_t log_alpha_size = 5; log_alpha_size <= 8; log_alpha_size++) { std::vector uint_config; std::vector uint_config_dec; @@ -168,7 +174,7 @@ TEST(ANSTest, UintConfigRoundtrip) { } uint_config.emplace_back(log_alpha_size, 0, 0); uint_config_dec.resize(uint_config.size()); - BitWriter writer; + BitWriter writer{memory_manager}; BitWriter::Allotment allotment(&writer, 10 * uint_config.size()); EncodeUintConfigs(uint_config, &writer, log_alpha_size); allotment.ReclaimAndCharge(&writer, 0, nullptr); @@ -185,6 +191,7 @@ TEST(ANSTest, UintConfigRoundtrip) { } void TestCheckpointing(bool ans, bool lz77) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); std::vector> input_values(1); for (size_t i = 0; i < 1024; i++) { input_values[0].emplace_back(0, i % 4); @@ -206,11 +213,11 @@ void TestCheckpointing(bool ans, bool lz77) { : HistogramParams::LZ77Method::kNone; params.force_huffman = !ans; - BitWriter writer; + BitWriter writer{memory_manager}; { auto input_values_copy = input_values; - BuildAndEncodeHistograms(params, 1, input_values_copy, &codes, &context_map, - &writer, 0, nullptr); + BuildAndEncodeHistograms(memory_manager, params, 1, input_values_copy, + &codes, &context_map, &writer, 0, nullptr); WriteTokens(input_values_copy[0], codes, context_map, 0, &writer, 0, nullptr); writer.ZeroPadToByte(); @@ -225,9 +232,11 @@ void TestCheckpointing(bool ans, bool lz77) { std::vector dec_context_map; ANSCode decoded_codes; - ASSERT_TRUE(DecodeHistograms(&br, 1, &decoded_codes, &dec_context_map)); + ASSERT_TRUE(DecodeHistograms(memory_manager, &br, 1, &decoded_codes, + &dec_context_map)); ASSERT_EQ(dec_context_map, context_map); - ANSSymbolReader reader(&decoded_codes, &br); + JXL_ASSIGN_OR_DIE(ANSSymbolReader reader, + ANSSymbolReader::Create(&decoded_codes, &br)); ANSSymbolReader::Checkpoint checkpoint; size_t br_pos = 0; diff --git a/third_party/jpeg-xl/lib/jxl/base/rect.h b/third_party/jpeg-xl/lib/jxl/base/rect.h index 666c3d73ec3d..06070d834923 100644 --- a/third_party/jpeg-xl/lib/jxl/base/rect.h +++ b/third_party/jpeg-xl/lib/jxl/base/rect.h @@ -115,6 +115,11 @@ class RectT { y1() <= other.y1(); } + bool IsSame(const RectT& other) const { + return x0_ == other.x0_ && xsize_ == other.xsize_ && y0_ == other.y0_ && + ysize_ <= other.ysize_; + } + // Returns true if this Rect fully resides in the given image. ImageT could be // Plane or Image3; however if ImageT is Rect, results are nonsensical. template diff --git a/third_party/jpeg-xl/lib/jxl/sanitizers.h b/third_party/jpeg-xl/lib/jxl/base/sanitizers.h similarity index 94% rename from third_party/jpeg-xl/lib/jxl/sanitizers.h rename to third_party/jpeg-xl/lib/jxl/base/sanitizers.h index e05f79737b54..29ee158d7cbe 100644 --- a/third_party/jpeg-xl/lib/jxl/sanitizers.h +++ b/third_party/jpeg-xl/lib/jxl/base/sanitizers.h @@ -10,7 +10,6 @@ #include "lib/jxl/base/compiler_specific.h" #include "lib/jxl/base/sanitizer_definitions.h" -#include "lib/jxl/image.h" #if JXL_MEMORY_SANITIZER #include @@ -49,17 +48,17 @@ static JXL_INLINE JXL_MAYBE_UNUSED void MemoryIsInitialized( } // Mark all the bytes of an image (including padding) as poisoned bytes. -template -static JXL_INLINE JXL_MAYBE_UNUSED void PoisonImage(const Plane& im) { +template +static JXL_INLINE JXL_MAYBE_UNUSED void PoisonImage(const Pixels& im) { PoisonMemory(im.bytes(), im.bytes_per_row() * im.ysize()); } namespace { // Print the uninitialized regions of an image. -template +template static JXL_INLINE JXL_MAYBE_UNUSED void PrintImageUninitialized( - const Plane& im) { + const Pixels& im) { fprintf(stderr, "Uninitialized regions for image of size %" PRIu64 "x%" PRIu64 ":\n", static_cast(im.xsize()), static_cast(im.ysize())); @@ -157,9 +156,9 @@ static JXL_INLINE JXL_MAYBE_UNUSED void PrintImageUninitialized( // Check that all the pixels in the provided rect of the image are initialized // (not poisoned). If any of the values is poisoned it will abort. -template +template static JXL_INLINE JXL_MAYBE_UNUSED void CheckImageInitialized( - const Plane& im, const Rect& r, size_t c, const char* message) { + const Pixels& im, const Rect& r, size_t c, const char* message) { JXL_ASSERT(r.x0() <= im.xsize()); JXL_ASSERT(r.x0() + r.xsize() <= im.xsize()); JXL_ASSERT(r.y0() <= im.ysize()); @@ -190,9 +189,9 @@ static JXL_INLINE JXL_MAYBE_UNUSED void CheckImageInitialized( } } -template +template static JXL_INLINE JXL_MAYBE_UNUSED void CheckImageInitialized( - const Image3& im, const Rect& r, const char* message) { + const Image& im, const Rect& r, const char* message) { for (size_t c = 0; c < 3; c++) { std::string str_message(message); str_message += " c=" + std::to_string(c); @@ -220,8 +219,8 @@ static JXL_INLINE JXL_MAYBE_UNUSED void UnpoisonMemory(const void* m, static JXL_INLINE JXL_MAYBE_UNUSED void MemoryIsInitialized(const void* m, size_t size) {} -template -static JXL_INLINE JXL_MAYBE_UNUSED void PoisonImage(const Plane& im) {} +template +static JXL_INLINE JXL_MAYBE_UNUSED void PoisonImage(const Pixels& im) {} #define JXL_CHECK_IMAGE_INITIALIZED(im, r) #define JXL_CHECK_PLANE_INITIALIZED(im, r, c) diff --git a/third_party/jpeg-xl/lib/jxl/bit_reader_test.cc b/third_party/jpeg-xl/lib/jxl/bit_reader_test.cc index b2d5773d157e..74466941617a 100644 --- a/third_party/jpeg-xl/lib/jxl/bit_reader_test.cc +++ b/third_party/jpeg-xl/lib/jxl/bit_reader_test.cc @@ -3,10 +3,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#include -#include - #include +#include +#include #include #include @@ -52,12 +51,13 @@ struct Symbol { // Reading from output gives the same values. TEST(BitReaderTest, TestRoundTrip) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); test::ThreadPoolForTests pool(8); EXPECT_TRUE(RunOnPool( pool.get(), 0, 1000, ThreadPool::NoInit, - [](const uint32_t task, size_t /* thread */) { + [&memory_manager](const uint32_t task, size_t /* thread */) { constexpr size_t kMaxBits = 8000; - BitWriter writer; + BitWriter writer{memory_manager}; BitWriter::Allotment allotment(&writer, kMaxBits); std::vector symbols; @@ -86,14 +86,15 @@ TEST(BitReaderTest, TestRoundTrip) { // SkipBits is the same as reading that many bits. TEST(BitReaderTest, TestSkip) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); test::ThreadPoolForTests pool(8); EXPECT_TRUE(RunOnPool( pool.get(), 0, 96, ThreadPool::NoInit, - [](const uint32_t task, size_t /* thread */) { + [&memory_manager](const uint32_t task, size_t /* thread */) { constexpr size_t kSize = 100; for (size_t skip = 0; skip < 128; ++skip) { - BitWriter writer; + BitWriter writer{memory_manager}; BitWriter::Allotment allotment(&writer, kSize * kBitsPerByte); // Start with "task" 1-bits. for (size_t i = 0; i < task; ++i) { @@ -142,11 +143,12 @@ TEST(BitReaderTest, TestSkip) { // Verifies byte order and different groupings of bits. TEST(BitReaderTest, TestOrder) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); constexpr size_t kMaxBits = 16; // u(1) - bits written into LSBs of first byte { - BitWriter writer; + BitWriter writer{memory_manager}; BitWriter::Allotment allotment(&writer, kMaxBits); for (size_t i = 0; i < 5; ++i) { writer.Write(1, 1); @@ -168,7 +170,7 @@ TEST(BitReaderTest, TestOrder) { // u(8) - get bytes in the same order { - BitWriter writer; + BitWriter writer{memory_manager}; BitWriter::Allotment allotment(&writer, kMaxBits); writer.Write(8, 0xF8); writer.Write(8, 0x3F); @@ -183,7 +185,7 @@ TEST(BitReaderTest, TestOrder) { // u(16) - little-endian bytes { - BitWriter writer; + BitWriter writer{memory_manager}; BitWriter::Allotment allotment(&writer, kMaxBits); writer.Write(16, 0xF83F); @@ -197,7 +199,7 @@ TEST(BitReaderTest, TestOrder) { // Non-byte-aligned, mixed sizes { - BitWriter writer; + BitWriter writer{memory_manager}; BitWriter::Allotment allotment(&writer, kMaxBits); writer.Write(1, 1); writer.Write(3, 6); diff --git a/third_party/jpeg-xl/lib/jxl/blending.cc b/third_party/jpeg-xl/lib/jxl/blending.cc index 0e269808162d..7b6543ec7e36 100644 --- a/third_party/jpeg-xl/lib/jxl/blending.cc +++ b/third_party/jpeg-xl/lib/jxl/blending.cc @@ -5,6 +5,8 @@ #include "lib/jxl/blending.h" +#include + #include #include #include @@ -38,9 +40,9 @@ bool NeedsBlending(const FrameHeader& frame_header) { } Status PerformBlending( - const float* const* bg, const float* const* fg, float* const* out, - size_t x0, size_t xsize, const PatchBlending& color_blending, - const PatchBlending* ec_blending, + JxlMemoryManager* memory_manager, const float* const* bg, + const float* const* fg, float* const* out, size_t x0, size_t xsize, + const PatchBlending& color_blending, const PatchBlending* ec_blending, const std::vector& extra_channel_info) { bool has_alpha = false; size_t num_ec = extra_channel_info.size(); @@ -50,7 +52,8 @@ Status PerformBlending( break; } } - JXL_ASSIGN_OR_RETURN(ImageF tmp, ImageF::Create(xsize, 3 + num_ec)); + JXL_ASSIGN_OR_RETURN(ImageF tmp, + ImageF::Create(memory_manager, xsize, 3 + num_ec)); // Blend extra channels first so that we use the pre-blending alpha. for (size_t i = 0; i < num_ec; i++) { if (ec_blending[i].mode == PatchBlendMode::kAdd) { diff --git a/third_party/jpeg-xl/lib/jxl/blending.h b/third_party/jpeg-xl/lib/jxl/blending.h index 6ed862ac1c85..f7933bee2dda 100644 --- a/third_party/jpeg-xl/lib/jxl/blending.h +++ b/third_party/jpeg-xl/lib/jxl/blending.h @@ -6,6 +6,8 @@ #ifndef LIB_JXL_BLENDING_H_ #define LIB_JXL_BLENDING_H_ +#include + #include #include @@ -18,9 +20,9 @@ namespace jxl { bool NeedsBlending(const FrameHeader& frame_header); -Status PerformBlending(const float* const* bg, const float* const* fg, - float* const* out, size_t x0, size_t xsize, - const PatchBlending& color_blending, +Status PerformBlending(JxlMemoryManager* memory_manager, const float* const* bg, + const float* const* fg, float* const* out, size_t x0, + size_t xsize, const PatchBlending& color_blending, const PatchBlending* ec_blending, const std::vector& extra_channel_info); diff --git a/third_party/jpeg-xl/lib/jxl/box_content_decoder.cc b/third_party/jpeg-xl/lib/jxl/box_content_decoder.cc index c7d87f4ca087..d54f11b139a8 100644 --- a/third_party/jpeg-xl/lib/jxl/box_content_decoder.cc +++ b/third_party/jpeg-xl/lib/jxl/box_content_decoder.cc @@ -5,7 +5,12 @@ #include "lib/jxl/box_content_decoder.h" -#include "lib/jxl/sanitizers.h" +#include +#include +#include +#include + +#include "lib/jxl/base/sanitizers.h" namespace jxl { diff --git a/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.cc b/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.cc index 4e49f18df8d0..555d81a4ccf8 100644 --- a/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.cc +++ b/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.cc @@ -22,6 +22,8 @@ #include "lib/jxl/butteraugli/butteraugli.h" +#include + #include #include #include @@ -409,8 +411,9 @@ Status SeparateMFAndHF(const ButteraugliParams& params, Image3F* mf, ImageF* hf, static const double kSigmaHf = 3.22489901262; const size_t xsize = mf->xsize(); const size_t ysize = mf->ysize(); - JXL_ASSIGN_OR_RETURN(hf[0], ImageF::Create(xsize, ysize)); - JXL_ASSIGN_OR_RETURN(hf[1], ImageF::Create(xsize, ysize)); + JxlMemoryManager* memory_manager = mf[0].memory_manager(); + JXL_ASSIGN_OR_RETURN(hf[0], ImageF::Create(memory_manager, xsize, ysize)); + JXL_ASSIGN_OR_RETURN(hf[1], ImageF::Create(memory_manager, xsize, ysize)); for (int i = 0; i < 3; ++i) { if (i == 2) { JXL_RETURN_IF_ERROR( @@ -465,9 +468,10 @@ Status SeparateHFAndUHF(const ButteraugliParams& params, ImageF* hf, const HWY_FULL(float) d; const size_t xsize = hf[0].xsize(); const size_t ysize = hf[0].ysize(); + JxlMemoryManager* memory_manager = hf[0].memory_manager(); static const double kSigmaUhf = 1.56416327805; - JXL_ASSIGN_OR_RETURN(uhf[0], ImageF::Create(xsize, ysize)); - JXL_ASSIGN_OR_RETURN(uhf[1], ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(uhf[0], ImageF::Create(memory_manager, xsize, ysize)); + JXL_ASSIGN_OR_RETURN(uhf[1], ImageF::Create(memory_manager, xsize, ysize)); for (int i = 0; i < 2; ++i) { // Divide hf into hf and uhf. for (size_t y = 0; y < ysize; ++y) { @@ -531,8 +535,11 @@ void DeallocateHFAndUHF(ImageF* hf, ImageF* uhf) { Status SeparateFrequencies(size_t xsize, size_t ysize, const ButteraugliParams& params, BlurTemp* blur_temp, const Image3F& xyb, PsychoImage& ps) { - JXL_ASSIGN_OR_RETURN(ps.lf, Image3F::Create(xyb.xsize(), xyb.ysize())); - JXL_ASSIGN_OR_RETURN(ps.mf, Image3F::Create(xyb.xsize(), xyb.ysize())); + JxlMemoryManager* memory_manager = xyb.memory_manager(); + JXL_ASSIGN_OR_RETURN( + ps.lf, Image3F::Create(memory_manager, xyb.xsize(), xyb.ysize())); + JXL_ASSIGN_OR_RETURN( + ps.mf, Image3F::Create(memory_manager, xyb.xsize(), xyb.ysize())); JXL_RETURN_IF_ERROR(SeparateLFAndMF(params, xyb, &ps.lf, &ps.mf, blur_temp)); JXL_RETURN_IF_ERROR(SeparateMFAndHF(params, &ps.mf, &ps.hf[0], blur_temp)); JXL_RETURN_IF_ERROR( @@ -1204,14 +1211,19 @@ Status Mask(const ImageF& mask0, const ImageF& mask1, ImageF* BUTTERAUGLI_RESTRICT diff_ac) { const size_t xsize = mask0.xsize(); const size_t ysize = mask0.ysize(); - JXL_ASSIGN_OR_RETURN(*mask, ImageF::Create(xsize, ysize)); + JxlMemoryManager* memory_manager = mask0.memory_manager(); + JXL_ASSIGN_OR_RETURN(*mask, ImageF::Create(memory_manager, xsize, ysize)); static const float kMul = 6.19424080439; static const float kBias = 12.61050594197; static const float kRadius = 2.7; - JXL_ASSIGN_OR_RETURN(ImageF diff0, ImageF::Create(xsize, ysize)); - JXL_ASSIGN_OR_RETURN(ImageF diff1, ImageF::Create(xsize, ysize)); - JXL_ASSIGN_OR_RETURN(ImageF blurred0, ImageF::Create(xsize, ysize)); - JXL_ASSIGN_OR_RETURN(ImageF blurred1, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF diff0, + ImageF::Create(memory_manager, xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF diff1, + ImageF::Create(memory_manager, xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF blurred0, + ImageF::Create(memory_manager, xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF blurred1, + ImageF::Create(memory_manager, xsize, ysize)); DiffPrecompute(mask0, kMul, kBias, &diff0); DiffPrecompute(mask1, kMul, kBias, &diff1); JXL_RETURN_IF_ERROR(Blur(diff0, kRadius, params, blur_temp, &blurred0)); @@ -1236,8 +1248,11 @@ Status MaskPsychoImage(const PsychoImage& pi0, const PsychoImage& pi1, const ButteraugliParams& params, BlurTemp* blur_temp, ImageF* BUTTERAUGLI_RESTRICT mask, ImageF* BUTTERAUGLI_RESTRICT diff_ac) { - JXL_ASSIGN_OR_RETURN(ImageF mask0, ImageF::Create(xsize, ysize)); - JXL_ASSIGN_OR_RETURN(ImageF mask1, ImageF::Create(xsize, ysize)); + JxlMemoryManager* memory_manager = pi0.hf[0].memory_manager(); + JXL_ASSIGN_OR_RETURN(ImageF mask0, + ImageF::Create(memory_manager, xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF mask1, + ImageF::Create(memory_manager, xsize, ysize)); CombineChannelsForMasking(&pi0.hf[0], &pi0.uhf[0], &mask0); CombineChannelsForMasking(&pi1.hf[0], &pi1.uhf[0], &mask1); JXL_RETURN_IF_ERROR(Mask(mask0, mask1, params, blur_temp, mask, diff_ac)); @@ -1521,23 +1536,28 @@ Status ButteraugliDiffmapInPlace(Image3F& image0, Image3F& image1, // image0 and image1 are in linear sRGB color space const size_t xsize = image0.xsize(); const size_t ysize = image0.ysize(); + JxlMemoryManager* memory_manager = image0.memory_manager(); BlurTemp blur_temp; { // Convert image0 and image1 to XYB in-place - JXL_ASSIGN_OR_RETURN(Image3F temp, Image3F::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(Image3F temp, + Image3F::Create(memory_manager, xsize, ysize)); JXL_RETURN_IF_ERROR( OpsinDynamicsImage(image0, params, &temp, &blur_temp, &image0)); JXL_RETURN_IF_ERROR( OpsinDynamicsImage(image1, params, &temp, &blur_temp, &image1)); } // image0 and image1 are in XYB color space - JXL_ASSIGN_OR_RETURN(ImageF block_diff_dc, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF block_diff_dc, + ImageF::Create(memory_manager, xsize, ysize)); ZeroFillImage(&block_diff_dc); { // separate out LF components from image0 and image1 and compute the dc // diff image from them - JXL_ASSIGN_OR_RETURN(Image3F lf0, Image3F::Create(xsize, ysize)); - JXL_ASSIGN_OR_RETURN(Image3F lf1, Image3F::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(Image3F lf0, + Image3F::Create(memory_manager, xsize, ysize)); + JXL_ASSIGN_OR_RETURN(Image3F lf1, + Image3F::Create(memory_manager, xsize, ysize)); JXL_RETURN_IF_ERROR( SeparateLFAndMF(params, image0, &lf0, &image0, &blur_temp)); JXL_RETURN_IF_ERROR( @@ -1553,11 +1573,13 @@ Status ButteraugliDiffmapInPlace(Image3F& image0, Image3F& image1, JXL_RETURN_IF_ERROR(SeparateMFAndHF(params, &image1, &hf1[0], &blur_temp)); // image0 and image1 are MF-images in XYB color space - JXL_ASSIGN_OR_RETURN(ImageF block_diff_ac, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF block_diff_ac, + ImageF::Create(memory_manager, xsize, ysize)); ZeroFillImage(&block_diff_ac); // start accumulating ac diff image from MF images { - JXL_ASSIGN_OR_RETURN(ImageF diffs, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF diffs, + ImageF::Create(memory_manager, xsize, ysize)); MaltaDiffMapLF(image0.Plane(1), image1.Plane(1), wMfMalta, wMfMalta, norm1Mf, &diffs, &block_diff_ac); MaltaDiffMapLF(image0.Plane(0), image1.Plane(0), wMfMaltaX, wMfMaltaX, @@ -1579,7 +1601,8 @@ Status ButteraugliDiffmapInPlace(Image3F& image0, Image3F& image1, // continue accumulating ac diff image from HF and UHF images const float hf_asymmetry = params.hf_asymmetry; { - JXL_ASSIGN_OR_RETURN(ImageF diffs, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF diffs, + ImageF::Create(memory_manager, xsize, ysize)); MaltaDiffMap(uhf0[1], uhf1[1], wUhfMalta * hf_asymmetry, wUhfMalta / hf_asymmetry, norm1Uhf, &diffs, &block_diff_ac); MaltaDiffMap(uhf0[0], uhf1[0], wUhfMaltaX * hf_asymmetry, @@ -1597,10 +1620,13 @@ Status ButteraugliDiffmapInPlace(Image3F& image0, Image3F& image1, } // compute mask image from HF and UHF X and Y images - JXL_ASSIGN_OR_RETURN(ImageF mask, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF mask, + ImageF::Create(memory_manager, xsize, ysize)); { - JXL_ASSIGN_OR_RETURN(ImageF mask0, ImageF::Create(xsize, ysize)); - JXL_ASSIGN_OR_RETURN(ImageF mask1, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF mask0, + ImageF::Create(memory_manager, xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF mask1, + ImageF::Create(memory_manager, xsize, ysize)); CombineChannelsForMasking(&hf0[0], &uhf0[0], &mask0); CombineChannelsForMasking(&hf1[0], &uhf1[0], &mask1); DeallocateHFAndUHF(&hf1[0], &uhf1[0]); @@ -1610,7 +1636,7 @@ Status ButteraugliDiffmapInPlace(Image3F& image0, Image3F& image1, } // compute final diffmap from mask image and ac and dc diff images - JXL_ASSIGN_OR_RETURN(diffmap, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(diffmap, ImageF::Create(memory_manager, xsize, ysize)); for (size_t y = 0; y < ysize; ++y) { const float* row_dc = block_diff_dc.Row(y); const float* row_ac = block_diff_ac.Row(y); @@ -1694,7 +1720,8 @@ static inline void CheckImage(const ImageF& image, const char* name) { static StatusOr SubSample2x(const Image3F& in) { size_t xs = (in.xsize() + 1) / 2; size_t ys = (in.ysize() + 1) / 2; - JXL_ASSIGN_OR_RETURN(Image3F retval, Image3F::Create(xs, ys)); + JxlMemoryManager* memory_manager = in.memory_manager(); + JXL_ASSIGN_OR_RETURN(Image3F retval, Image3F::Create(memory_manager, xs, ys)); for (size_t c = 0; c < 3; ++c) { for (size_t y = 0; y < ys; ++y) { for (size_t x = 0; x < xs; ++x) { @@ -1754,16 +1781,19 @@ StatusOr> ButteraugliComparator::Make( const Image3F& rgb0, const ButteraugliParams& params) { size_t xsize = rgb0.xsize(); size_t ysize = rgb0.ysize(); + JxlMemoryManager* memory_manager = rgb0.memory_manager(); std::unique_ptr result = std::unique_ptr( new ButteraugliComparator(xsize, ysize, params)); - JXL_ASSIGN_OR_RETURN(result->temp_, Image3F::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(result->temp_, + Image3F::Create(memory_manager, xsize, ysize)); if (xsize < 8 || ysize < 8) { return result; } - JXL_ASSIGN_OR_RETURN(Image3F xyb0, Image3F::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(Image3F xyb0, + Image3F::Create(memory_manager, xsize, ysize)); JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage)( rgb0, params, result->Temp(), &result->blur_temp_, &xyb0)); result->ReleaseTemp(); @@ -1789,11 +1819,13 @@ Status ButteraugliComparator::Mask(ImageF* BUTTERAUGLI_RESTRICT mask) const { Status ButteraugliComparator::Diffmap(const Image3F& rgb1, ImageF& result) const { + JxlMemoryManager* memory_manager = rgb1.memory_manager(); if (xsize_ < 8 || ysize_ < 8) { ZeroFillImage(&result); return true; } - JXL_ASSIGN_OR_RETURN(Image3F xyb1, Image3F::Create(xsize_, ysize_)); + JXL_ASSIGN_OR_RETURN(Image3F xyb1, + Image3F::Create(memory_manager, xsize_, ysize_)); JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage)( rgb1, params_, Temp(), &blur_temp_, &xyb1)); ReleaseTemp(); @@ -1802,8 +1834,9 @@ Status ButteraugliComparator::Diffmap(const Image3F& rgb1, if (sub_->xsize_ < 8 || sub_->ysize_ < 8) { return true; } - JXL_ASSIGN_OR_RETURN(Image3F sub_xyb, - Image3F::Create(sub_->xsize_, sub_->ysize_)); + JXL_ASSIGN_OR_RETURN( + Image3F sub_xyb, + Image3F::Create(memory_manager, sub_->xsize_, sub_->ysize_)); JXL_ASSIGN_OR_RETURN(Image3F subsampledRgb1, SubSample2x(rgb1)); JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage)( subsampledRgb1, params_, sub_->Temp(), &sub_->blur_temp_, &sub_xyb)); @@ -1817,6 +1850,7 @@ Status ButteraugliComparator::Diffmap(const Image3F& rgb1, Status ButteraugliComparator::DiffmapOpsinDynamicsImage(const Image3F& xyb1, ImageF& result) const { + JxlMemoryManager* memory_manager = xyb1.memory_manager(); if (xsize_ < 8 || ysize_ < 8) { ZeroFillImage(&result); return true; @@ -1824,7 +1858,7 @@ Status ButteraugliComparator::DiffmapOpsinDynamicsImage(const Image3F& xyb1, PsychoImage pi1; JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(SeparateFrequencies)( xsize_, ysize_, params_, &blur_temp_, xyb1, pi1)); - JXL_ASSIGN_OR_RETURN(result, ImageF::Create(xsize_, ysize_)); + JXL_ASSIGN_OR_RETURN(result, ImageF::Create(memory_manager, xsize_, ysize_)); return DiffmapPsychoImage(pi1, result); } @@ -1850,6 +1884,7 @@ void MaltaDiffMapLF(const ImageF& lum0, const ImageF& lum1, const double w_0gt1, Status ButteraugliComparator::DiffmapPsychoImage(const PsychoImage& pi1, ImageF& diffmap) const { + JxlMemoryManager* memory_manager = diffmap.memory_manager(); if (xsize_ < 8 || ysize_ < 8) { ZeroFillImage(&diffmap); return true; @@ -1858,8 +1893,10 @@ Status ButteraugliComparator::DiffmapPsychoImage(const PsychoImage& pi1, const float hf_asymmetry_ = params_.hf_asymmetry; const float xmul_ = params_.xmul; - JXL_ASSIGN_OR_RETURN(ImageF diffs, ImageF::Create(xsize_, ysize_)); - JXL_ASSIGN_OR_RETURN(Image3F block_diff_ac, Image3F::Create(xsize_, ysize_)); + JXL_ASSIGN_OR_RETURN(ImageF diffs, + ImageF::Create(memory_manager, xsize_, ysize_)); + JXL_ASSIGN_OR_RETURN(Image3F block_diff_ac, + Image3F::Create(memory_manager, xsize_, ysize_)); ZeroFillImage(&block_diff_ac); MaltaDiffMap(pi0_.uhf[1], pi1.uhf[1], wUhfMalta * hf_asymmetry_, wUhfMalta / hf_asymmetry_, norm1Uhf, &diffs, &block_diff_ac, 1); @@ -1877,7 +1914,8 @@ Status ButteraugliComparator::DiffmapPsychoImage(const PsychoImage& pi1, MaltaDiffMapLF(pi0_.mf.Plane(0), pi1.mf.Plane(0), wMfMaltaX, wMfMaltaX, norm1MfX, &diffs, &block_diff_ac, 0); - JXL_ASSIGN_OR_RETURN(Image3F block_diff_dc, Image3F::Create(xsize_, ysize_)); + JXL_ASSIGN_OR_RETURN(Image3F block_diff_dc, + Image3F::Create(memory_manager, xsize_, ysize_)); for (size_t c = 0; c < 3; ++c) { if (c < 2) { // No blue channel error accumulated at HF. HWY_DYNAMIC_DISPATCH(L2DiffAsymmetric) @@ -1925,6 +1963,7 @@ bool ButteraugliDiffmapSmall(const Image3F& rgb0, const Image3F& rgb1, const ButteraugliParams& params, ImageF& diffmap) { const size_t xsize = rgb0.xsize(); const size_t ysize = rgb0.ysize(); + JxlMemoryManager* memory_manager = rgb0.memory_manager(); // Butteraugli values for small (where xsize or ysize is smaller // than 8 pixels) images are non-sensical, but most likely it is // less disruptive to try to compute something than just give up. @@ -1933,8 +1972,10 @@ bool ButteraugliDiffmapSmall(const Image3F& rgb0, const Image3F& rgb1, size_t yborder = ysize < kMax ? (kMax - ysize) / 2 : 0; size_t xscaled = std::max(kMax, xsize); size_t yscaled = std::max(kMax, ysize); - JXL_ASSIGN_OR_RETURN(Image3F scaled0, Image3F::Create(xscaled, yscaled)); - JXL_ASSIGN_OR_RETURN(Image3F scaled1, Image3F::Create(xscaled, yscaled)); + JXL_ASSIGN_OR_RETURN(Image3F scaled0, + Image3F::Create(memory_manager, xscaled, yscaled)); + JXL_ASSIGN_OR_RETURN(Image3F scaled1, + Image3F::Create(memory_manager, xscaled, yscaled)); for (int i = 0; i < 3; ++i) { for (size_t y = 0; y < yscaled; ++y) { for (size_t x = 0; x < xscaled; ++x) { @@ -1947,7 +1988,7 @@ bool ButteraugliDiffmapSmall(const Image3F& rgb0, const Image3F& rgb1, } ImageF diffmap_scaled; const bool ok = ButteraugliDiffmap(scaled0, scaled1, params, diffmap_scaled); - JXL_ASSIGN_OR_RETURN(diffmap, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(diffmap, ImageF::Create(memory_manager, xsize, ysize)); for (size_t y = 0; y < ysize; ++y) { for (size_t x = 0; x < xsize; ++x) { diffmap.Row(y)[x] = diffmap_scaled.Row(y + yborder)[x + xborder]; @@ -2111,8 +2152,10 @@ void ScoreToRgb(double score, double good_threshold, double bad_threshold, StatusOr CreateHeatMapImage(const ImageF& distmap, double good_threshold, double bad_threshold) { - JXL_ASSIGN_OR_RETURN(Image3F heatmap, - Image3F::Create(distmap.xsize(), distmap.ysize())); + JxlMemoryManager* memory_manager = distmap.memory_manager(); + JXL_ASSIGN_OR_RETURN( + Image3F heatmap, + Image3F::Create(memory_manager, distmap.xsize(), distmap.ysize())); for (size_t y = 0; y < distmap.ysize(); ++y) { const float* BUTTERAUGLI_RESTRICT row_distmap = distmap.ConstRow(y); float* BUTTERAUGLI_RESTRICT row_h0 = heatmap.PlaneRow(0, y); diff --git a/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.h b/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.h index 487db2bdce00..823b3ba1acd9 100644 --- a/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.h +++ b/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.h @@ -8,11 +8,12 @@ #ifndef LIB_JXL_BUTTERAUGLI_BUTTERAUGLI_H_ #define LIB_JXL_BUTTERAUGLI_BUTTERAUGLI_H_ -#include -#include +#include #include #include +#include +#include #include #include "lib/jxl/base/compiler_specific.h" @@ -149,9 +150,11 @@ struct PsychoImage { // Hold it here and only allocate on demand to reduce memory usage. struct BlurTemp { Status GetTransposed(const ImageF &in, ImageF **out) { + JxlMemoryManager *memory_manager = in.memory_manager(); if (transposed_temp.xsize() == 0) { - JXL_ASSIGN_OR_RETURN(transposed_temp, - ImageF::Create(in.ysize(), in.xsize())); + JXL_ASSIGN_OR_RETURN( + transposed_temp, + ImageF::Create(memory_manager, in.ysize(), in.xsize())); } *out = &transposed_temp; return true; diff --git a/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli_test.cc b/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli_test.cc index 39df4bd473e4..c4a1d6ef6633 100644 --- a/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli_test.cc +++ b/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli_test.cc @@ -5,8 +5,8 @@ #include "lib/jxl/butteraugli/butteraugli.h" +#include #include -#include #include #include @@ -20,6 +20,7 @@ #include "lib/jxl/image.h" #include "lib/jxl/image_ops.h" #include "lib/jxl/test_image.h" +#include "lib/jxl/test_utils.h" #include "lib/jxl/testing.h" namespace jxl { @@ -30,7 +31,8 @@ using extras::PackedPixelFile; using test::TestImage; Image3F SinglePixelImage(float red, float green, float blue) { - JXL_ASSIGN_OR_DIE(Image3F img, Image3F::Create(1, 1)); + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + JXL_ASSIGN_OR_DIE(Image3F img, Image3F::Create(memory_manager, 1, 1)); img.PlaneRow(0, 0)[0] = red; img.PlaneRow(1, 0)[0] = green; img.PlaneRow(2, 0)[0] = blue; @@ -38,11 +40,13 @@ Image3F SinglePixelImage(float red, float green, float blue) { } Image3F GetColorImage(const PackedPixelFile& ppf) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); JXL_CHECK(!ppf.frames.empty()); const PackedImage& image = ppf.frames[0].color; const JxlPixelFormat& format = image.format; const uint8_t* pixels = reinterpret_cast(image.pixels()); - JXL_ASSIGN_OR_DIE(Image3F color, Image3F::Create(image.xsize, image.ysize)); + JXL_ASSIGN_OR_DIE(Image3F color, + Image3F::Create(memory_manager, image.xsize, image.ysize)); for (size_t c = 0; c < format.num_channels; ++c) { JXL_CHECK(ConvertFromExternal(pixels, image.pixels_size, image.xsize, image.ysize, ppf.info.bits_per_sample, format, @@ -88,12 +92,14 @@ TEST(ButteraugliInPlaceTest, SinglePixel) { } TEST(ButteraugliInPlaceTest, LargeImage) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); const size_t xsize = 1024; const size_t ysize = 1024; TestImage img; img.SetDimensions(xsize, ysize).AddFrame().RandomFill(777); Image3F rgb0 = GetColorImage(img.ppf()); - JXL_ASSIGN_OR_DIE(Image3F rgb1, Image3F::Create(xsize, ysize)); + JXL_ASSIGN_OR_DIE(Image3F rgb1, + Image3F::Create(memory_manager, xsize, ysize)); CopyImageTo(rgb0, &rgb1); AddUniformNoise(&rgb1, 0.02f, 7777); AddEdge(&rgb1, 0.1f, xsize / 2, xsize / 2); diff --git a/third_party/jpeg-xl/lib/jxl/chroma_from_luma.cc b/third_party/jpeg-xl/lib/jxl/chroma_from_luma.cc index 69585c44cf99..f36e2b7a9f78 100644 --- a/third_party/jpeg-xl/lib/jxl/chroma_from_luma.cc +++ b/third_party/jpeg-xl/lib/jxl/chroma_from_luma.cc @@ -5,18 +5,21 @@ #include "lib/jxl/chroma_from_luma.h" +#include + #include "lib/jxl/image_ops.h" namespace jxl { -StatusOr ColorCorrelationMap::Create(size_t xsize, - size_t ysize, - bool XYB) { +StatusOr ColorCorrelationMap::Create( + JxlMemoryManager* memory_manager, size_t xsize, size_t ysize, bool XYB) { ColorCorrelationMap result; size_t xblocks = DivCeil(xsize, kColorTileDim); size_t yblocks = DivCeil(ysize, kColorTileDim); - JXL_ASSIGN_OR_RETURN(result.ytox_map, ImageSB::Create(xblocks, yblocks)); - JXL_ASSIGN_OR_RETURN(result.ytob_map, ImageSB::Create(xblocks, yblocks)); + JXL_ASSIGN_OR_RETURN(result.ytox_map, + ImageSB::Create(memory_manager, xblocks, yblocks)); + JXL_ASSIGN_OR_RETURN(result.ytob_map, + ImageSB::Create(memory_manager, xblocks, yblocks)); ZeroFillImage(&result.ytox_map); ZeroFillImage(&result.ytob_map); if (!XYB) { diff --git a/third_party/jpeg-xl/lib/jxl/chroma_from_luma.h b/third_party/jpeg-xl/lib/jxl/chroma_from_luma.h index 090d95165c2b..6520114b75c0 100644 --- a/third_party/jpeg-xl/lib/jxl/chroma_from_luma.h +++ b/third_party/jpeg-xl/lib/jxl/chroma_from_luma.h @@ -9,6 +9,8 @@ // Chroma-from-luma, computed using heuristics to determine the best linear // model for the X and B channels from the Y channel. +#include + #include #include #include @@ -52,7 +54,8 @@ struct ColorCorrelationMap { // xsize/ysize are in pixels // set XYB=false to do something close to no-op cmap (needed for now since // cmap is mandatory) - static StatusOr Create(size_t xsize, size_t ysize, + static StatusOr Create(JxlMemoryManager* memory_manager, + size_t xsize, size_t ysize, bool XYB = true); float YtoXRatio(int32_t x_factor) const { diff --git a/third_party/jpeg-xl/lib/jxl/cms/jxl_cms_internal.h b/third_party/jpeg-xl/lib/jxl/cms/jxl_cms_internal.h index 69a5dc434e97..41df84d599c9 100644 --- a/third_party/jpeg-xl/lib/jxl/cms/jxl_cms_internal.h +++ b/third_party/jpeg-xl/lib/jxl/cms/jxl_cms_internal.h @@ -76,7 +76,7 @@ constexpr Matrix3x3 kBradfordInv{{{0.9869929f, -0.1470543f, 0.1599627f}, {0.4323053f, 0.5183603f, 0.0492912f}, {-0.0085287f, 0.0400428f, 0.9684867f}}}; -// Adapts whitepoint x, y to D50 +// Adapts white point x, y to D50 static Status AdaptToXYZD50(float wx, float wy, Matrix3x3& matrix) { bool ok = (wx >= 0) && (wx <= 1) && (wy > 0) && (wy <= 1); if (!ok) { @@ -359,7 +359,7 @@ static Status CreateICCChadMatrix(double wx, double wy, Matrix3x3& result) { return true; } -// Creates RGB to XYZ matrix given RGB primaries and whitepoint in xy. +// Creates RGB to XYZ matrix given RGB primaries and white point in xy. static Status CreateICCRGBMatrix(double rx, double ry, double gx, double gy, double bx, double by, double wx, double wy, Matrix3x3& result) { diff --git a/third_party/jpeg-xl/lib/jxl/cms/transfer_functions_test.cc b/third_party/jpeg-xl/lib/jxl/cms/transfer_functions_test.cc index 26de409a4ebd..3a7bd7f43f64 100644 --- a/third_party/jpeg-xl/lib/jxl/cms/transfer_functions_test.cc +++ b/third_party/jpeg-xl/lib/jxl/cms/transfer_functions_test.cc @@ -34,7 +34,7 @@ HWY_NOINLINE void TestPqEncodedFromDisplay() { const float actual = GetLane(tf_pq.EncodedFromDisplay(d, Set(d, f))); const float expected = TF_PQ_Base::EncodedFromDisplay(intensity, f); const float abs_err = std::abs(expected - actual); - EXPECT_LT(abs_err, 5e-7) << "f = " << f; + EXPECT_LT(abs_err, 6e-7) << "f = " << f; max_abs_err = std::max(max_abs_err, abs_err); } printf("max abs err %e\n", static_cast(max_abs_err)); diff --git a/third_party/jpeg-xl/lib/jxl/codec_in_out.h b/third_party/jpeg-xl/lib/jxl/codec_in_out.h index 028f3ecaace9..244ac35f9dbe 100644 --- a/third_party/jpeg-xl/lib/jxl/codec_in_out.h +++ b/third_party/jpeg-xl/lib/jxl/codec_in_out.h @@ -8,16 +8,14 @@ // Holds inputs/outputs for decoding/encoding images. -#include -#include +#include -#include +#include +#include #include #include -#include "lib/jxl/base/data_parallel.h" #include "lib/jxl/base/status.h" -#include "lib/jxl/frame_header.h" #include "lib/jxl/headers.h" #include "lib/jxl/image.h" #include "lib/jxl/image_bundle.h" @@ -37,9 +35,11 @@ struct Blobs { // to/from decoding/encoding. class CodecInOut { public: - CodecInOut() : preview_frame(&metadata.m) { + explicit CodecInOut(JxlMemoryManager* memory_manager) + : memory_manager(memory_manager), + preview_frame(memory_manager, &metadata.m) { frames.reserve(1); - frames.emplace_back(&metadata.m); + frames.emplace_back(memory_manager, &metadata.m); } // Move-only. @@ -97,6 +97,8 @@ class CodecInOut { // Metadata stored into / retrieved from bitstreams. + JxlMemoryManager* memory_manager; + Blobs blobs; CodecMetadata metadata; // applies to preview and all frames diff --git a/third_party/jpeg-xl/lib/jxl/coeff_order.cc b/third_party/jpeg-xl/lib/jxl/coeff_order.cc index 296a7cb2f0d1..d98caec3192a 100644 --- a/third_party/jpeg-xl/lib/jxl/coeff_order.cc +++ b/third_party/jpeg-xl/lib/jxl/coeff_order.cc @@ -5,20 +5,18 @@ #include "lib/jxl/coeff_order.h" -#include +#include #include +#include #include -#include "lib/jxl/ans_params.h" -#include "lib/jxl/base/span.h" +#include "lib/jxl/base/status.h" #include "lib/jxl/coeff_order_fwd.h" #include "lib/jxl/dec_ans.h" #include "lib/jxl/dec_bit_reader.h" -#include "lib/jxl/entropy_coder.h" #include "lib/jxl/lehmer_code.h" #include "lib/jxl/modular/encoding/encoding.h" -#include "lib/jxl/modular/modular_image.h" namespace jxl { @@ -57,13 +55,14 @@ Status ReadPermutation(size_t skip, size_t size, coeff_order_t* order, } // namespace -Status DecodePermutation(size_t skip, size_t size, coeff_order_t* order, - BitReader* br) { +Status DecodePermutation(JxlMemoryManager* memory_manager, size_t skip, + size_t size, coeff_order_t* order, BitReader* br) { std::vector context_map; ANSCode code; - JXL_RETURN_IF_ERROR( - DecodeHistograms(br, kPermutationContexts, &code, &context_map)); - ANSSymbolReader reader(&code, br); + JXL_RETURN_IF_ERROR(DecodeHistograms(memory_manager, br, kPermutationContexts, + &code, &context_map)); + JXL_ASSIGN_OR_RETURN(ANSSymbolReader reader, + ANSSymbolReader::Create(&code, br)); JXL_RETURN_IF_ERROR( ReadPermutation(skip, size, order, br, &reader, context_map)); if (!reader.CheckANSFinalState()) { @@ -92,18 +91,19 @@ Status DecodeCoeffOrder(AcStrategy acs, coeff_order_t* order, BitReader* br, } // namespace -Status DecodeCoeffOrders(uint16_t used_orders, uint32_t used_acs, - coeff_order_t* order, BitReader* br) { +Status DecodeCoeffOrders(JxlMemoryManager* memory_manager, uint16_t used_orders, + uint32_t used_acs, coeff_order_t* order, + BitReader* br) { uint16_t computed = 0; std::vector context_map; ANSCode code; - std::unique_ptr reader; + ANSSymbolReader reader; std::vector natural_order; // Bitstream does not have histograms if no coefficient order is used. if (used_orders != 0) { - JXL_RETURN_IF_ERROR( - DecodeHistograms(br, kPermutationContexts, &code, &context_map)); - reader = make_unique(&code, br); + JXL_RETURN_IF_ERROR(DecodeHistograms( + memory_manager, br, kPermutationContexts, &code, &context_map)); + JXL_ASSIGN_OR_RETURN(reader, ANSSymbolReader::Create(&code, br)); } uint32_t acs_mask = 0; for (uint8_t o = 0; o < AcStrategy::kNumValidStrategies; ++o) { @@ -136,12 +136,12 @@ Status DecodeCoeffOrders(uint16_t used_orders, uint32_t used_acs, } else { for (size_t c = 0; c < 3; c++) { coeff_order_t* dest = used ? &order[CoeffOrderOffset(ord, c)] : nullptr; - JXL_RETURN_IF_ERROR(DecodeCoeffOrder(acs, dest, br, reader.get(), + JXL_RETURN_IF_ERROR(DecodeCoeffOrder(acs, dest, br, &reader, natural_order, context_map)); } } } - if (used_orders && !reader->CheckANSFinalState()) { + if (used_orders && !reader.CheckANSFinalState()) { return JXL_FAILURE("Invalid ANS stream"); } return true; diff --git a/third_party/jpeg-xl/lib/jxl/coeff_order.h b/third_party/jpeg-xl/lib/jxl/coeff_order.h index 395e29664205..1c1cec3ff579 100644 --- a/third_party/jpeg-xl/lib/jxl/coeff_order.h +++ b/third_party/jpeg-xl/lib/jxl/coeff_order.h @@ -6,6 +6,8 @@ #ifndef LIB_JXL_COEFF_ORDER_H_ #define LIB_JXL_COEFF_ORDER_H_ +#include + #include #include #include @@ -53,12 +55,13 @@ constexpr JXL_MAYBE_UNUSED uint32_t kPermutationContexts = 8; uint32_t CoeffOrderContext(uint32_t val); -Status DecodeCoeffOrders(uint16_t used_orders, uint32_t used_acs, - coeff_order_t* order, BitReader* br); - -Status DecodePermutation(size_t skip, size_t size, coeff_order_t* order, +Status DecodeCoeffOrders(JxlMemoryManager* memory_manager, uint16_t used_orders, + uint32_t used_acs, coeff_order_t* order, BitReader* br); +Status DecodePermutation(JxlMemoryManager* memory_manager, size_t skip, + size_t size, coeff_order_t* order, BitReader* br); + } // namespace jxl #endif // LIB_JXL_COEFF_ORDER_H_ diff --git a/third_party/jpeg-xl/lib/jxl/coeff_order_test.cc b/third_party/jpeg-xl/lib/jxl/coeff_order_test.cc index a88dcfa27465..ed0131f92b8a 100644 --- a/third_party/jpeg-xl/lib/jxl/coeff_order_test.cc +++ b/third_party/jpeg-xl/lib/jxl/coeff_order_test.cc @@ -5,6 +5,8 @@ #include "lib/jxl/coeff_order.h" +#include + #include #include // iota #include @@ -16,6 +18,7 @@ #include "lib/jxl/coeff_order_fwd.h" #include "lib/jxl/dec_bit_reader.h" #include "lib/jxl/enc_coeff_order.h" +#include "lib/jxl/test_utils.h" #include "lib/jxl/testing.h" namespace jxl { @@ -23,14 +26,15 @@ namespace { void RoundtripPermutation(coeff_order_t* perm, coeff_order_t* out, size_t len, size_t* size) { - BitWriter writer; + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + BitWriter writer{memory_manager}; EncodePermutation(perm, 0, len, &writer, 0, nullptr); writer.ZeroPadToByte(); Status status = true; { BitReader reader(writer.GetSpan()); BitReaderScopedCloser closer(&reader, &status); - ASSERT_TRUE(DecodePermutation(0, len, out, &reader)); + ASSERT_TRUE(DecodePermutation(memory_manager, 0, len, out, &reader)); } ASSERT_TRUE(status); *size = writer.GetSpan().size(); diff --git a/third_party/jpeg-xl/lib/jxl/color_encoding_internal.h b/third_party/jpeg-xl/lib/jxl/color_encoding_internal.h index afcd5a4c1c04..14f7554ad8fa 100644 --- a/third_party/jpeg-xl/lib/jxl/color_encoding_internal.h +++ b/third_party/jpeg-xl/lib/jxl/color_encoding_internal.h @@ -155,7 +155,7 @@ struct ColorEncoding : public Fields { } // Sets the raw ICC profile bytes, without parsing the ICC, and without - // updating the direct fields such as whitepoint, primaries and color + // updating the direct fields such as white point, primaries and color // space. Functions to get and set fields, such as SetWhitePoint, cannot be // used anymore after this and functions such as IsSRGB return false no matter // what the contents of the icc profile. diff --git a/third_party/jpeg-xl/lib/jxl/color_management_test.cc b/third_party/jpeg-xl/lib/jxl/color_management_test.cc index b6a45b0a856e..8c2674d2d719 100644 --- a/third_party/jpeg-xl/lib/jxl/color_management_test.cc +++ b/third_party/jpeg-xl/lib/jxl/color_management_test.cc @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include @@ -61,17 +61,19 @@ struct Globals { private: Globals() { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); in_gray = GenerateGray(); in_color = GenerateColor(); - JXL_ASSIGN_OR_DIE(out_gray, ImageF::Create(kWidth, 1)); - JXL_ASSIGN_OR_DIE(out_color, ImageF::Create(kWidth * 3, 1)); + JXL_ASSIGN_OR_DIE(out_gray, ImageF::Create(memory_manager, kWidth, 1)); + JXL_ASSIGN_OR_DIE(out_color, ImageF::Create(memory_manager, kWidth * 3, 1)); c_native = ColorEncoding::LinearSRGB(/*is_gray=*/false); c_gray = ColorEncoding::LinearSRGB(/*is_gray=*/true); } static ImageF GenerateGray() { - JXL_ASSIGN_OR_DIE(ImageF gray, ImageF::Create(kWidth, 1)); + JXL_ASSIGN_OR_DIE(ImageF gray, + ImageF::Create(jxl::test::MemoryManager(), kWidth, 1)); float* JXL_RESTRICT row = gray.Row(0); // Increasing left to right for (uint32_t x = 0; x < kWidth; ++x) { @@ -81,7 +83,8 @@ struct Globals { } static ImageF GenerateColor() { - JXL_ASSIGN_OR_DIE(ImageF image, ImageF::Create(kWidth * 3, 1)); + JXL_ASSIGN_OR_DIE(ImageF image, ImageF::Create(jxl::test::MemoryManager(), + kWidth * 3, 1)); float* JXL_RESTRICT interleaved = image.Row(0); std::fill(interleaved, interleaved + kWidth * 3, 0.0f); @@ -343,6 +346,7 @@ TEST_F(ColorManagementTest, HlgOotf) { } TEST_F(ColorManagementTest, XYBProfile) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); ColorEncoding c_xyb; c_xyb.SetColorSpace(ColorSpace::kXYB); c_xyb.SetRenderingIntent(RenderingIntent::kPerceptual); @@ -358,8 +362,9 @@ TEST_F(ColorManagementTest, XYBProfile) { ImageMetadata metadata; metadata.color_encoding = c_native; - ImageBundle ib(&metadata); - JXL_ASSIGN_OR_DIE(Image3F native, Image3F::Create(kNumColors, 1)); + ImageBundle ib(memory_manager, &metadata); + JXL_ASSIGN_OR_DIE(Image3F native, + Image3F::Create(memory_manager, kNumColors, 1)); float mul = 1.0f / (kGridDim - 1); for (size_t ir = 0, x = 0; ir < kGridDim; ++ir) { for (size_t ig = 0; ig < kGridDim; ++ig) { @@ -372,10 +377,12 @@ TEST_F(ColorManagementTest, XYBProfile) { } ib.SetFromImage(std::move(native), c_native); const Image3F& in = *ib.color(); - JXL_ASSIGN_OR_DIE(Image3F opsin, Image3F::Create(kNumColors, 1)); + JXL_ASSIGN_OR_DIE(Image3F opsin, + Image3F::Create(memory_manager, kNumColors, 1)); JXL_CHECK(ToXYB(ib, nullptr, &opsin, cms, nullptr)); - JXL_ASSIGN_OR_DIE(Image3F opsin2, Image3F::Create(kNumColors, 1)); + JXL_ASSIGN_OR_DIE(Image3F opsin2, + Image3F::Create(memory_manager, kNumColors, 1)); CopyImageTo(opsin, &opsin2); ScaleXYB(&opsin2); @@ -389,7 +396,8 @@ TEST_F(ColorManagementTest, XYBProfile) { float* dst = xform.BufDst(0); ASSERT_TRUE(xform.Run(0, src, dst, kNumColors)); - JXL_ASSIGN_OR_DIE(Image3F out, Image3F::Create(kNumColors, 1)); + JXL_ASSIGN_OR_DIE(Image3F out, + Image3F::Create(memory_manager, kNumColors, 1)); for (size_t i = 0; i < kNumColors; ++i) { for (size_t c = 0; c < 3; ++c) { out.PlaneRow(c, 0)[i] = dst[3 * i + c]; diff --git a/third_party/jpeg-xl/lib/jxl/compressed_dc.cc b/third_party/jpeg-xl/lib/jxl/compressed_dc.cc index ec78afbd41b1..26038abec981 100644 --- a/third_party/jpeg-xl/lib/jxl/compressed_dc.cc +++ b/third_party/jpeg-xl/lib/jxl/compressed_dc.cc @@ -5,11 +5,12 @@ #include "lib/jxl/compressed_dc.h" -#include -#include -#include +#include #include +#include +#include +#include #include #undef HWY_TARGET_INCLUDE @@ -121,7 +122,8 @@ JXL_INLINE void ComputePixel( Store(out, d, out_rows[2] + x); } -Status AdaptiveDCSmoothing(const float* dc_factors, Image3F* dc, +Status AdaptiveDCSmoothing(JxlMemoryManager* memory_manager, + const float* dc_factors, Image3F* dc, ThreadPool* pool) { const size_t xsize = dc->xsize(); const size_t ysize = dc->ysize(); @@ -132,7 +134,8 @@ Status AdaptiveDCSmoothing(const float* dc_factors, Image3F* dc, // the x and b channels through color correlation. JXL_ASSERT(w1 + w2 < 0.25f); - JXL_ASSIGN_OR_RETURN(Image3F smoothed, Image3F::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(Image3F smoothed, + Image3F::Create(memory_manager, xsize, ysize)); // Fill in borders that the loop below will not. First and last are unused. for (size_t c = 0; c < 3; c++) { for (size_t y : {static_cast(0), ysize - 1}) { @@ -289,9 +292,11 @@ namespace jxl { HWY_EXPORT(DequantDC); HWY_EXPORT(AdaptiveDCSmoothing); -Status AdaptiveDCSmoothing(const float* dc_factors, Image3F* dc, +Status AdaptiveDCSmoothing(JxlMemoryManager* memory_manager, + const float* dc_factors, Image3F* dc, ThreadPool* pool) { - return HWY_DYNAMIC_DISPATCH(AdaptiveDCSmoothing)(dc_factors, dc, pool); + return HWY_DYNAMIC_DISPATCH(AdaptiveDCSmoothing)(memory_manager, dc_factors, + dc, pool); } void DequantDC(const Rect& r, Image3F* dc, ImageB* quant_dc, const Image& in, diff --git a/third_party/jpeg-xl/lib/jxl/compressed_dc.h b/third_party/jpeg-xl/lib/jxl/compressed_dc.h index d8846a8f048a..12e70d84cfd3 100644 --- a/third_party/jpeg-xl/lib/jxl/compressed_dc.h +++ b/third_party/jpeg-xl/lib/jxl/compressed_dc.h @@ -6,6 +6,8 @@ #ifndef LIB_JXL_COMPRESSED_DC_H_ #define LIB_JXL_COMPRESSED_DC_H_ +#include + #include "lib/jxl/ac_context.h" #include "lib/jxl/base/data_parallel.h" #include "lib/jxl/base/rect.h" @@ -20,7 +22,8 @@ namespace jxl { // Smooth DC in already-smooth areas, to counteract banding. -Status AdaptiveDCSmoothing(const float* dc_factors, Image3F* dc, +Status AdaptiveDCSmoothing(JxlMemoryManager* memory_manager, + const float* dc_factors, Image3F* dc, ThreadPool* pool); void DequantDC(const Rect& r, Image3F* dc, ImageB* quant_dc, const Image& in, diff --git a/third_party/jpeg-xl/lib/jxl/convolve_test.cc b/third_party/jpeg-xl/lib/jxl/convolve_test.cc index 5ba36b33fe0b..4acbc3d2f967 100644 --- a/third_party/jpeg-xl/lib/jxl/convolve_test.cc +++ b/third_party/jpeg-xl/lib/jxl/convolve_test.cc @@ -5,10 +5,11 @@ #include "lib/jxl/convolve.h" +#include #include -#include #include // PRIx64 +#include #undef HWY_TARGET_INCLUDE #define HWY_TARGET_INCLUDE "lib/jxl/convolve_test.cc" @@ -70,13 +71,16 @@ void TestNeighbors() { void VerifySymmetric3(const size_t xsize, const size_t ysize, ThreadPool* pool, Rng* rng) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); const Rect rect(0, 0, xsize, ysize); - JXL_ASSIGN_OR_DIE(ImageF in, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_DIE(ImageF in, ImageF::Create(memory_manager, xsize, ysize)); GenerateImage(*rng, &in, 0.0f, 1.0f); - JXL_ASSIGN_OR_DIE(ImageF out_expected, ImageF::Create(xsize, ysize)); - JXL_ASSIGN_OR_DIE(ImageF out_actual, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_DIE(ImageF out_expected, + ImageF::Create(memory_manager, xsize, ysize)); + JXL_ASSIGN_OR_DIE(ImageF out_actual, + ImageF::Create(memory_manager, xsize, ysize)); const WeightsSymmetric3& weights = WeightsSymmetric3Lowpass(); Symmetric3(in, rect, weights, pool, &out_expected); @@ -100,7 +104,8 @@ std::vector GenerateTestRectangles(size_t xsize, size_t ysize) { // Ensures Symmetric and Separable give the same result. void VerifySymmetric5(const size_t xsize, const size_t ysize, ThreadPool* pool, Rng* rng) { - JXL_ASSIGN_OR_DIE(ImageF in, ImageF::Create(xsize, ysize)); + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + JXL_ASSIGN_OR_DIE(ImageF in, ImageF::Create(memory_manager, xsize, ysize)); GenerateImage(*rng, &in, 0.0f, 1.0f); for (const Rect& in_rect : GenerateTestRectangles(xsize, ysize)) { @@ -109,8 +114,10 @@ void VerifySymmetric5(const size_t xsize, const size_t ysize, ThreadPool* pool, in_rect.xsize(), in_rect.ysize(), in_rect.x0(), in_rect.y0()); { Rect out_rect = in_rect; - JXL_ASSIGN_OR_DIE(ImageF out_expected, ImageF::Create(xsize, ysize)); - JXL_ASSIGN_OR_DIE(ImageF out_actual, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_DIE(ImageF out_expected, + ImageF::Create(memory_manager, xsize, ysize)); + JXL_ASSIGN_OR_DIE(ImageF out_actual, + ImageF::Create(memory_manager, xsize, ysize)); FillImage(-1.0f, &out_expected); FillImage(-1.0f, &out_actual); @@ -124,10 +131,12 @@ void VerifySymmetric5(const size_t xsize, const size_t ysize, ThreadPool* pool, } { Rect out_rect(0, 0, in_rect.xsize(), in_rect.ysize()); - JXL_ASSIGN_OR_DIE(ImageF out_expected, - ImageF::Create(out_rect.xsize(), out_rect.ysize())); - JXL_ASSIGN_OR_DIE(ImageF out_actual, - ImageF::Create(out_rect.xsize(), out_rect.ysize())); + JXL_ASSIGN_OR_DIE( + ImageF out_expected, + ImageF::Create(memory_manager, out_rect.xsize(), out_rect.ysize())); + JXL_ASSIGN_OR_DIE( + ImageF out_actual, + ImageF::Create(memory_manager, out_rect.xsize(), out_rect.ysize())); SlowSeparable5(in, in_rect, WeightsSeparable5Lowpass(), pool, &out_expected, out_rect); @@ -142,13 +151,16 @@ void VerifySymmetric5(const size_t xsize, const size_t ysize, ThreadPool* pool, void VerifySeparable5(const size_t xsize, const size_t ysize, ThreadPool* pool, Rng* rng) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); const Rect rect(0, 0, xsize, ysize); - JXL_ASSIGN_OR_DIE(ImageF in, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_DIE(ImageF in, ImageF::Create(memory_manager, xsize, ysize)); GenerateImage(*rng, &in, 0.0f, 1.0f); - JXL_ASSIGN_OR_DIE(ImageF out_expected, ImageF::Create(xsize, ysize)); - JXL_ASSIGN_OR_DIE(ImageF out_actual, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_DIE(ImageF out_expected, + ImageF::Create(memory_manager, xsize, ysize)); + JXL_ASSIGN_OR_DIE(ImageF out_actual, + ImageF::Create(memory_manager, xsize, ysize)); const WeightsSeparable5& weights = WeightsSeparable5Lowpass(); SlowSeparable5(in, rect, weights, pool, &out_expected, rect); @@ -198,15 +210,16 @@ void TestConvolve() { template void BenchmarkConv(const char* caption, const Conv& conv, const hwy::FuncInput unpredictable1) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); const size_t kNumInputs = 1; const hwy::FuncInput inputs[kNumInputs] = {unpredictable1}; hwy::Result results[kNumInputs]; const size_t kDim = 160; // in+out fit in L2 - JXL_ASSIGN_OR_DIE(ImageF in, ImageF::Create(kDim, kDim)); + JXL_ASSIGN_OR_DIE(ImageF in, ImageF::Create(memory_manager, kDim, kDim)); ZeroFillImage(&in); in.Row(kDim / 2)[kDim / 2] = unpredictable1; - JXL_ASSIGN_OR_DIE(ImageF out, ImageF::Create(kDim, kDim)); + JXL_ASSIGN_OR_DIE(ImageF out, ImageF::Create(memory_manager, kDim, kDim)); hwy::Params p; p.verbose = false; diff --git a/third_party/jpeg-xl/lib/jxl/dct_util.h b/third_party/jpeg-xl/lib/jxl/dct_util.h index 077c17bd77c7..a0036db8c010 100644 --- a/third_party/jpeg-xl/lib/jxl/dct_util.h +++ b/third_party/jpeg-xl/lib/jxl/dct_util.h @@ -6,6 +6,8 @@ #ifndef LIB_JXL_DCT_UTIL_H_ #define LIB_JXL_DCT_UTIL_H_ +#include + #include #include #include @@ -53,12 +55,14 @@ class ACImageT final : public ACImage { public: ACImageT() = default; - static StatusOr> Make(size_t xsize, size_t ysize) { + static StatusOr> Make( + JxlMemoryManager* memory_manager, size_t xsize, size_t ysize) { static_assert( std::is_same::value || std::is_same::value, "ACImage must be either 32- or 16- bit"); std::unique_ptr result = jxl::make_unique(); - JXL_ASSIGN_OR_RETURN(result->img_, Image3::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(result->img_, + Image3::Create(memory_manager, xsize, ysize)); return result; } diff --git a/third_party/jpeg-xl/lib/jxl/dec_ans.cc b/third_party/jpeg-xl/lib/jxl/dec_ans.cc index 8b7b54ce9107..088d0a26bfd2 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_ans.cc +++ b/third_party/jpeg-xl/lib/jxl/dec_ans.cc @@ -5,8 +5,9 @@ #include "lib/jxl/dec_ans.h" -#include +#include +#include #include #include "lib/jxl/ans_common.h" @@ -182,9 +183,11 @@ Status ReadHistogram(int precision_bits, std::vector* counts, } // namespace -Status DecodeANSCodes(const size_t num_histograms, +Status DecodeANSCodes(JxlMemoryManager* memory_manager, + const size_t num_histograms, const size_t max_alphabet_size, BitReader* in, ANSCode* result) { + result->memory_manager = memory_manager; result->degenerate_symbols.resize(num_histograms, -1); if (result->use_prefix_code) { JXL_ASSERT(max_alphabet_size <= 1 << PREFIX_MAX_BITS); @@ -220,9 +223,9 @@ Status DecodeANSCodes(const size_t num_histograms, } } else { JXL_ASSERT(max_alphabet_size <= ANS_MAX_ALPHABET_SIZE); - result->alias_tables = - AllocateArray(num_histograms * (1 << result->log_alpha_size) * - sizeof(AliasTable::Entry)); + size_t alloc_size = num_histograms * (1 << result->log_alpha_size) * + sizeof(AliasTable::Entry); + result->alias_tables = AllocateArray(alloc_size); AliasTable::Entry* alias_tables = reinterpret_cast(result->alias_tables.get()); for (size_t c = 0; c < num_histograms; ++c) { @@ -325,7 +328,8 @@ void ANSCode::UpdateMaxNumBits(size_t ctx, size_t symbol) { max_num_bits = std::max(max_num_bits, total_bits); } -Status DecodeHistograms(BitReader* br, size_t num_contexts, ANSCode* code, +Status DecodeHistograms(JxlMemoryManager* memory_manager, BitReader* br, + size_t num_contexts, ANSCode* code, std::vector* context_map, bool disallow_lz77) { JXL_RETURN_IF_ERROR(Bundle::Read(br, &code->lz77)); if (code->lz77.enabled) { @@ -339,7 +343,8 @@ Status DecodeHistograms(BitReader* br, size_t num_contexts, ANSCode* code, size_t num_histograms = 1; context_map->resize(num_contexts); if (num_contexts > 1) { - JXL_RETURN_IF_ERROR(DecodeContextMap(context_map, &num_histograms, br)); + JXL_RETURN_IF_ERROR( + DecodeContextMap(memory_manager, context_map, &num_histograms, br)); } JXL_DEBUG_V( 4, "Decoded context map of size %" PRIuS " and %" PRIuS " histograms", @@ -355,9 +360,44 @@ Status DecodeHistograms(BitReader* br, size_t num_contexts, ANSCode* code, JXL_RETURN_IF_ERROR( DecodeUintConfigs(code->log_alpha_size, &code->uint_config, br)); const size_t max_alphabet_size = 1 << code->log_alpha_size; - JXL_RETURN_IF_ERROR( - DecodeANSCodes(num_histograms, max_alphabet_size, br, code)); + JXL_RETURN_IF_ERROR(DecodeANSCodes(memory_manager, num_histograms, + max_alphabet_size, br, code)); return true; } +StatusOr ANSSymbolReader::Create(const ANSCode* code, + BitReader* JXL_RESTRICT br, + size_t distance_multiplier) { + return ANSSymbolReader(code, br, distance_multiplier); +} + +ANSSymbolReader::ANSSymbolReader(const ANSCode* code, + BitReader* JXL_RESTRICT br, + size_t distance_multiplier) + : alias_tables_( + reinterpret_cast(code->alias_tables.get())), + huffman_data_(code->huffman_data.data()), + use_prefix_code_(code->use_prefix_code), + configs(code->uint_config.data()) { + if (!use_prefix_code_) { + state_ = static_cast(br->ReadFixedBits<32>()); + log_alpha_size_ = code->log_alpha_size; + log_entry_size_ = ANS_LOG_TAB_SIZE - code->log_alpha_size; + entry_size_minus_1_ = (1 << log_entry_size_) - 1; + } else { + state_ = (ANS_SIGNATURE << 16u); + } + if (!code->lz77.enabled) return; + lz77_window_storage_ = AllocateArray(kWindowSize * sizeof(uint32_t)); + lz77_window_ = reinterpret_cast(lz77_window_storage_.get()); + lz77_ctx_ = code->lz77.nonserialized_distance_context; + lz77_length_uint_ = code->lz77.length_uint_config; + lz77_threshold_ = code->lz77.min_symbol; + lz77_min_length_ = code->lz77.min_length; + num_special_distances_ = distance_multiplier == 0 ? 0 : kNumSpecialDistances; + for (size_t i = 0; i < num_special_distances_; i++) { + special_distances_[i] = SpecialDistance(i, distance_multiplier); + } +} + } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/dec_ans.h b/third_party/jpeg-xl/lib/jxl/dec_ans.h index dc56df12165f..abdab7364d3e 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_ans.h +++ b/third_party/jpeg-xl/lib/jxl/dec_ans.h @@ -9,6 +9,7 @@ // Library to decode the ANS population counts from the bit-stream and build a // decoding table from them. +#include #include #include @@ -152,6 +153,7 @@ struct ANSCode { // Maximum number of bits necessary to represent the result of a // ReadHybridUint call done with this ANSCode. size_t max_num_bits = 0; + JxlMemoryManager* memory_manager; void UpdateMaxNumBits(size_t ctx, size_t symbol); }; @@ -159,36 +161,9 @@ class ANSSymbolReader { public: // Invalid symbol reader, to be overwritten. ANSSymbolReader() = default; - ANSSymbolReader(const ANSCode* code, BitReader* JXL_RESTRICT br, - size_t distance_multiplier = 0) - : alias_tables_( - reinterpret_cast(code->alias_tables.get())), - huffman_data_(code->huffman_data.data()), - use_prefix_code_(code->use_prefix_code), - configs(code->uint_config.data()) { - if (!use_prefix_code_) { - state_ = static_cast(br->ReadFixedBits<32>()); - log_alpha_size_ = code->log_alpha_size; - log_entry_size_ = ANS_LOG_TAB_SIZE - code->log_alpha_size; - entry_size_minus_1_ = (1 << log_entry_size_) - 1; - } else { - state_ = (ANS_SIGNATURE << 16u); - } - if (!code->lz77.enabled) return; - // a std::vector incurs unacceptable decoding speed loss because of - // initialization. - lz77_window_storage_ = AllocateArray(kWindowSize * sizeof(uint32_t)); - lz77_window_ = reinterpret_cast(lz77_window_storage_.get()); - lz77_ctx_ = code->lz77.nonserialized_distance_context; - lz77_length_uint_ = code->lz77.length_uint_config; - lz77_threshold_ = code->lz77.min_symbol; - lz77_min_length_ = code->lz77.min_length; - num_special_distances_ = - distance_multiplier == 0 ? 0 : kNumSpecialDistances; - for (size_t i = 0; i < num_special_distances_; i++) { - special_distances_[i] = SpecialDistance(i, distance_multiplier); - } - } + static StatusOr Create(const ANSCode* code, + BitReader* JXL_RESTRICT br, + size_t distance_multiplier = 0); JXL_INLINE size_t ReadSymbolANSWithoutRefill(const size_t histo_idx, BitReader* JXL_RESTRICT br) { @@ -471,6 +446,9 @@ class ANSSymbolReader { } private: + ANSSymbolReader(const ANSCode* code, BitReader* JXL_RESTRICT br, + size_t distance_multiplier); + const AliasTable::Entry* JXL_RESTRICT alias_tables_; // not owned const HuffmanDecodingData* huffman_data_; bool use_prefix_code_; @@ -482,6 +460,8 @@ class ANSSymbolReader { // LZ77 structures and constants. static constexpr size_t kWindowMask = kWindowSize - 1; + // a std::vector incurs unacceptable decoding speed loss because of + // initialization. CacheAlignedUniquePtr lz77_window_storage_; uint32_t* lz77_window_ = nullptr; uint32_t num_decoded_ = 0; @@ -495,7 +475,8 @@ class ANSSymbolReader { uint32_t num_special_distances_{}; }; -Status DecodeHistograms(BitReader* br, size_t num_contexts, ANSCode* code, +Status DecodeHistograms(JxlMemoryManager* memory_manager, BitReader* br, + size_t num_contexts, ANSCode* code, std::vector* context_map, bool disallow_lz77 = false); diff --git a/third_party/jpeg-xl/lib/jxl/dec_cache.cc b/third_party/jpeg-xl/lib/jxl/dec_cache.cc index 639857d4f8a2..2e2bef913170 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_cache.cc +++ b/third_party/jpeg-xl/lib/jxl/dec_cache.cc @@ -5,6 +5,8 @@ #include "lib/jxl/dec_cache.h" +#include + #include "lib/jxl/base/status.h" #include "lib/jxl/blending.h" #include "lib/jxl/common.h" // JXL_HIGH_PRECISION @@ -28,8 +30,10 @@ namespace jxl { Status PassesDecoderState::PreparePipeline(const FrameHeader& frame_header, + const ImageMetadata* metadata, ImageBundle* decoded, PipelineOptions options) { + JxlMemoryManager* memory_manager = this->memory_manager(); size_t num_c = 3 + frame_header.nonserialized_metadata->m.num_extra_channels; if (options.render_noise && (frame_header.flags & FrameHeader::kNoise) != 0) { num_c += 3; @@ -37,10 +41,10 @@ Status PassesDecoderState::PreparePipeline(const FrameHeader& frame_header, if (frame_header.CanBeReferenced()) { // Necessary so that SetInputSizes() can allocate output buffers as needed. - frame_storage_for_referencing = ImageBundle(decoded->metadata()); + frame_storage_for_referencing = ImageBundle(memory_manager, metadata); } - RenderPipeline::Builder builder(num_c); + RenderPipeline::Builder builder(memory_manager, num_c); if (options.use_slow_render_pipeline) { builder.UseSimpleImplementation(); @@ -120,7 +124,7 @@ Status PassesDecoderState::PreparePipeline(const FrameHeader& frame_header, } if (frame_header.dc_level != 0) { builder.AddStage(GetWriteToImage3FStage( - &shared_storage.dc_frames[frame_header.dc_level - 1])); + memory_manager, &shared_storage.dc_frames[frame_header.dc_level - 1])); } if (frame_header.CanBeReferenced() && @@ -131,9 +135,8 @@ Status PassesDecoderState::PreparePipeline(const FrameHeader& frame_header, bool has_alpha = false; size_t alpha_c = 0; - for (size_t i = 0; i < decoded->metadata()->extra_channel_info.size(); i++) { - if (decoded->metadata()->extra_channel_info[i].type == - ExtraChannel::kAlpha) { + for (size_t i = 0; i < metadata->extra_channel_info.size(); i++) { + if (metadata->extra_channel_info[i].type == ExtraChannel::kAlpha) { has_alpha = true; alpha_c = 3 + i; break; @@ -146,7 +149,7 @@ Status PassesDecoderState::PreparePipeline(const FrameHeader& frame_header, JXL_ASSERT(!frame_header.CanBeReferenced() || frame_header.save_before_color_transform); JXL_ASSERT(!options.render_spotcolors || - !decoded->metadata()->Find(ExtraChannel::kSpotColor)); + !metadata->Find(ExtraChannel::kSpotColor)); bool is_rgba = (main_output.format.num_channels == 4); uint8_t* rgb_output = reinterpret_cast(main_output.buffer); builder.AddStage(GetFastXYBTosRGB8Stage(rgb_output, main_output.stride, @@ -186,11 +189,9 @@ Status PassesDecoderState::PreparePipeline(const FrameHeader& frame_header, if (options.render_spotcolors && frame_header.nonserialized_metadata->m.Find(ExtraChannel::kSpotColor)) { - for (size_t i = 0; i < decoded->metadata()->extra_channel_info.size(); - i++) { + for (size_t i = 0; i < metadata->extra_channel_info.size(); i++) { // Don't use Find() because there may be multiple spot color channels. - const ExtraChannelInfo& eci = - decoded->metadata()->extra_channel_info[i]; + const ExtraChannelInfo& eci = metadata->extra_channel_info[i]; if (eci.type == ExtraChannel::kSpotColor) { builder.AddStage(GetSpotColorStage(3 + i, eci.spot_color)); } @@ -251,9 +252,9 @@ Status PassesDecoderState::PreparePipeline(const FrameHeader& frame_header, (void)linear; if (main_output.callback.IsPresent() || main_output.buffer) { - builder.AddStage(GetWriteToOutputStage(main_output, width, height, - has_alpha, unpremul_alpha, alpha_c, - undo_orientation, extra_output)); + builder.AddStage(GetWriteToOutputStage( + main_output, width, height, has_alpha, unpremul_alpha, alpha_c, + undo_orientation, extra_output, memory_manager)); } else { builder.AddStage( GetWriteToImageBundleStage(decoded, output_encoding_info)); diff --git a/third_party/jpeg-xl/lib/jxl/dec_cache.h b/third_party/jpeg-xl/lib/jxl/dec_cache.h index d0310745322f..844a547c3c94 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_cache.h +++ b/third_party/jpeg-xl/lib/jxl/dec_cache.h @@ -7,12 +7,13 @@ #define LIB_JXL_DEC_CACHE_H_ #include +#include #include -#include #include #include #include +#include #include // HWY_ALIGN_MAX #include #include @@ -86,6 +87,10 @@ struct ImageOutput { // Per-frame decoder state. All the images here should be accessed through a // group rect (either with block units or pixel units). struct PassesDecoderState { + explicit PassesDecoderState(JxlMemoryManager* memory_manager) + : shared_storage(memory_manager), + frame_storage_for_referencing(memory_manager) {} + PassesSharedState shared_storage; // Allows avoiding copies for encoder loop. const PassesSharedState* JXL_RESTRICT shared = &shared_storage; @@ -144,7 +149,10 @@ struct PassesDecoderState { bool render_noise; }; - Status PreparePipeline(const FrameHeader& frame_header, ImageBundle* decoded, + JxlMemoryManager* memory_manager() const { return shared->memory_manager; } + + Status PreparePipeline(const FrameHeader& frame_header, + const ImageMetadata* metadata, ImageBundle* decoded, PipelineOptions options); // Information for colour conversions. @@ -152,6 +160,7 @@ struct PassesDecoderState { // Initializes decoder-specific structures using information from *shared. Status Init(const FrameHeader& frame_header) { + JxlMemoryManager* memory_manager = this->memory_manager(); x_dm_multiplier = std::pow(1 / (1.25f), frame_header.x_qm_scale - 2.0f); b_dm_multiplier = std::pow(1 / (1.25f), frame_header.b_qm_scale - 2.0f); @@ -169,7 +178,8 @@ struct PassesDecoderState { if (frame_header.loop_filter.epf_iters > 0) { JXL_ASSIGN_OR_RETURN( sigma, - ImageF::Create(shared->frame_dim.xsize_blocks + 2 * kSigmaPadding, + ImageF::Create(memory_manager, + shared->frame_dim.xsize_blocks + 2 * kSigmaPadding, shared->frame_dim.ysize_blocks + 2 * kSigmaPadding)); } return true; @@ -196,16 +206,17 @@ struct PassesDecoderState { // Temp images required for decoding a single group. Reduces memory allocations // for large images because we only initialize min(#threads, #groups) instances. struct GroupDecCache { - Status InitOnce(size_t num_passes, size_t used_acs) { + Status InitOnce(JxlMemoryManager* memory_manager, size_t num_passes, + size_t used_acs) { for (size_t i = 0; i < num_passes; i++) { if (num_nzeroes[i].xsize() == 0) { // Allocate enough for a whole group - partial groups on the // right/bottom border just use a subset. The valid size is passed via // Rect. - JXL_ASSIGN_OR_RETURN( - num_nzeroes[i], - Image3I::Create(kGroupDimInBlocks, kGroupDimInBlocks)); + JXL_ASSIGN_OR_RETURN(num_nzeroes[i], + Image3I::Create(memory_manager, kGroupDimInBlocks, + kGroupDimInBlocks)); } } size_t max_block_area = 0; @@ -235,11 +246,12 @@ struct GroupDecCache { return true; } - Status InitDCBufferOnce() { + Status InitDCBufferOnce(JxlMemoryManager* memory_manager) { if (dc_buffer.xsize() == 0) { JXL_ASSIGN_OR_RETURN( dc_buffer, - ImageF::Create(kGroupDimInBlocks + kRenderPipelineXOffset * 2, + ImageF::Create(memory_manager, + kGroupDimInBlocks + kRenderPipelineXOffset * 2, kGroupDimInBlocks + 4)); } return true; diff --git a/third_party/jpeg-xl/lib/jxl/dec_context_map.cc b/third_party/jpeg-xl/lib/jxl/dec_context_map.cc index baff87fa493c..c97eb8ec65c9 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_context_map.cc +++ b/third_party/jpeg-xl/lib/jxl/dec_context_map.cc @@ -5,14 +5,14 @@ #include "lib/jxl/dec_context_map.h" +#include + #include #include #include -#include "lib/jxl/ans_params.h" #include "lib/jxl/base/status.h" #include "lib/jxl/dec_ans.h" -#include "lib/jxl/entropy_coder.h" #include "lib/jxl/inverse_mtf-inl.h" namespace jxl { @@ -40,7 +40,8 @@ Status VerifyContextMap(const std::vector& context_map, } // namespace -Status DecodeContextMap(std::vector* context_map, size_t* num_htrees, +Status DecodeContextMap(JxlMemoryManager* memory_manager, + std::vector* context_map, size_t* num_htrees, BitReader* input) { bool is_simple = static_cast(input->ReadFixedBits<1>()); if (is_simple) { @@ -61,9 +62,10 @@ Status DecodeContextMap(std::vector* context_map, size_t* num_htrees, // in malicious bitstreams by making every context map require its own // context map. JXL_RETURN_IF_ERROR( - DecodeHistograms(input, 1, &code, &sink_ctx_map, + DecodeHistograms(memory_manager, input, 1, &code, &sink_ctx_map, /*disallow_lz77=*/context_map->size() <= 2)); - ANSSymbolReader reader(&code, input); + JXL_ASSIGN_OR_RETURN(ANSSymbolReader reader, + ANSSymbolReader::Create(&code, input)); size_t i = 0; uint32_t maxsym = 0; while (i < context_map->size()) { diff --git a/third_party/jpeg-xl/lib/jxl/dec_context_map.h b/third_party/jpeg-xl/lib/jxl/dec_context_map.h index 95b8a0ca92f7..8edad38a3a66 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_context_map.h +++ b/third_party/jpeg-xl/lib/jxl/dec_context_map.h @@ -6,9 +6,10 @@ #ifndef LIB_JXL_DEC_CONTEXT_MAP_H_ #define LIB_JXL_DEC_CONTEXT_MAP_H_ -#include -#include +#include +#include +#include #include #include "lib/jxl/dec_bit_reader.h" @@ -22,7 +23,8 @@ constexpr size_t kMaxClusters = 256; // context_map->size() must be the number of possible context ids. // Sets *num_htrees to the number of different histogram ids in // *context_map. -Status DecodeContextMap(std::vector* context_map, size_t* num_htrees, +Status DecodeContextMap(JxlMemoryManager* memory_manager, + std::vector* context_map, size_t* num_htrees, BitReader* input); } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/dec_external_image.cc b/third_party/jpeg-xl/lib/jxl/dec_external_image.cc index 51e12fcc819c..8c87200efe5c 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_external_image.cc +++ b/third_party/jpeg-xl/lib/jxl/dec_external_image.cc @@ -5,10 +5,11 @@ #include "lib/jxl/dec_external_image.h" +#include #include -#include #include +#include #include #include @@ -24,7 +25,7 @@ #include "lib/jxl/base/common.h" #include "lib/jxl/base/compiler_specific.h" #include "lib/jxl/base/printf_macros.h" -#include "lib/jxl/sanitizers.h" +#include "lib/jxl/base/sanitizers.h" HWY_BEFORE_NAMESPACE(); namespace jxl { @@ -111,9 +112,10 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane& image, Plane& out, jxl::ThreadPool* pool) { const size_t xsize = image.xsize(); const size_t ysize = image.ysize(); + JxlMemoryManager* memory_manager = image.memory_manager(); if (undo_orientation == Orientation::kFlipHorizontal) { - JXL_ASSIGN_OR_RETURN(out, Plane::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(out, Plane::Create(memory_manager, xsize, ysize)); JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast(ysize), ThreadPool::NoInit, [&](const uint32_t task, size_t /*thread*/) { @@ -126,7 +128,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane& image, }, "UndoOrientation")); } else if (undo_orientation == Orientation::kRotate180) { - JXL_ASSIGN_OR_RETURN(out, Plane::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(out, Plane::Create(memory_manager, xsize, ysize)); JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast(ysize), ThreadPool::NoInit, [&](const uint32_t task, size_t /*thread*/) { @@ -139,7 +141,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane& image, }, "UndoOrientation")); } else if (undo_orientation == Orientation::kFlipVertical) { - JXL_ASSIGN_OR_RETURN(out, Plane::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(out, Plane::Create(memory_manager, xsize, ysize)); JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast(ysize), ThreadPool::NoInit, [&](const uint32_t task, size_t /*thread*/) { @@ -152,7 +154,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane& image, }, "UndoOrientation")); } else if (undo_orientation == Orientation::kTranspose) { - JXL_ASSIGN_OR_RETURN(out, Plane::Create(ysize, xsize)); + JXL_ASSIGN_OR_RETURN(out, Plane::Create(memory_manager, ysize, xsize)); JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast(ysize), ThreadPool::NoInit, [&](const uint32_t task, size_t /*thread*/) { @@ -164,7 +166,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane& image, }, "UndoOrientation")); } else if (undo_orientation == Orientation::kRotate90) { - JXL_ASSIGN_OR_RETURN(out, Plane::Create(ysize, xsize)); + JXL_ASSIGN_OR_RETURN(out, Plane::Create(memory_manager, ysize, xsize)); JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast(ysize), ThreadPool::NoInit, [&](const uint32_t task, size_t /*thread*/) { @@ -176,7 +178,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane& image, }, "UndoOrientation")); } else if (undo_orientation == Orientation::kAntiTranspose) { - JXL_ASSIGN_OR_RETURN(out, Plane::Create(ysize, xsize)); + JXL_ASSIGN_OR_RETURN(out, Plane::Create(memory_manager, ysize, xsize)); JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast(ysize), ThreadPool::NoInit, [&](const uint32_t task, size_t /*thread*/) { @@ -188,7 +190,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane& image, }, "UndoOrientation")); } else if (undo_orientation == Orientation::kRotate270) { - JXL_ASSIGN_OR_RETURN(out, Plane::Create(ysize, xsize)); + JXL_ASSIGN_OR_RETURN(out, Plane::Create(memory_manager, ysize, xsize)); JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast(ysize), ThreadPool::NoInit, [&](const uint32_t task, size_t /*thread*/) { @@ -245,6 +247,7 @@ Status ConvertChannelsToExternal(const ImageF* in_channels[], jxl::Orientation undo_orientation) { JXL_DASSERT(num_channels != 0 && num_channels <= kConvertMaxChannels); JXL_DASSERT(in_channels[0] != nullptr); + JxlMemoryManager* memory_manager = in_channels[0]->memory_manager(); JXL_CHECK(float_out ? bits_per_sample == 16 || bits_per_sample == 32 : bits_per_sample > 0 && bits_per_sample <= 16); const bool has_out_image = (out_image != nullptr); @@ -310,7 +313,7 @@ Status ConvertChannelsToExternal(const ImageF* in_channels[], ImageF ones; for (size_t c = 0; c < num_channels; ++c) { if (!channels[c]) { - JXL_ASSIGN_OR_RETURN(ones, ImageF::Create(xsize, 1)); + JXL_ASSIGN_OR_RETURN(ones, ImageF::Create(memory_manager, xsize, 1)); FillImage(1.0f, &ones); break; } @@ -324,7 +327,7 @@ Status ConvertChannelsToExternal(const ImageF* in_channels[], pool, 0, static_cast(ysize), [&](size_t num_threads) { StatusOr> f16_cache_or = - Plane::Create(xsize, + Plane::Create(memory_manager, xsize, num_channels * num_threads); if (!f16_cache_or.ok()) return false; f16_cache = std::move(f16_cache_or).value(); @@ -402,8 +405,8 @@ Status ConvertChannelsToExternal(const ImageF* in_channels[], JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast(ysize), [&](size_t num_threads) { - StatusOr> u32_cache_or = - Plane::Create(xsize, num_channels * num_threads); + StatusOr> u32_cache_or = Plane::Create( + memory_manager, xsize, num_channels * num_threads); if (!u32_cache_or.ok()) return false; u32_cache = std::move(u32_cache_or).value(); return !!InitOutCallback(num_threads); @@ -457,11 +460,13 @@ Status ConvertToExternal(const jxl::ImageBundle& ib, size_t bits_per_sample, size_t color_channels = num_channels <= 2 ? 1 : 3; const Image3F* color = &ib.color(); + JxlMemoryManager* memory_manager = color->memory_manager(); // Undo premultiplied alpha. Image3F unpremul; if (ib.AlphaIsPremultiplied() && ib.HasAlpha() && unpremul_alpha) { - JXL_ASSIGN_OR_RETURN(unpremul, - Image3F::Create(color->xsize(), color->ysize())); + JXL_ASSIGN_OR_RETURN( + unpremul, + Image3F::Create(memory_manager, color->xsize(), color->ysize())); CopyImageTo(*color, &unpremul); for (size_t y = 0; y < unpremul.ysize(); y++) { UnpremultiplyAlpha(unpremul.PlaneRow(0, y), unpremul.PlaneRow(1, y), diff --git a/third_party/jpeg-xl/lib/jxl/dec_external_image_gbench.cc b/third_party/jpeg-xl/lib/jxl/dec_external_image_gbench.cc index 720a278fc02c..00fdb0ddddf1 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_external_image_gbench.cc +++ b/third_party/jpeg-xl/lib/jxl/dec_external_image_gbench.cc @@ -3,16 +3,20 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +#include + #include "benchmark/benchmark.h" #include "lib/jxl/dec_external_image.h" #include "lib/jxl/image.h" #include "lib/jxl/image_ops.h" +#include "tools/no_memory_manager.h" namespace jxl { namespace { // Decoder case, interleaves an internal float image. void BM_DecExternalImage_ConvertImageRGBA(benchmark::State& state) { + JxlMemoryManager* memory_manager = jpegxl::tools::NoMemoryManager(); const size_t kNumIter = 5; size_t xsize = state.range(); size_t ysize = state.range(); @@ -20,11 +24,12 @@ void BM_DecExternalImage_ConvertImageRGBA(benchmark::State& state) { ImageMetadata im; im.SetAlphaBits(8); - ImageBundle ib(&im); - JXL_ASSIGN_OR_DIE(Image3F color, Image3F::Create(xsize, ysize)); + ImageBundle ib(memory_manager, &im); + JXL_ASSIGN_OR_DIE(Image3F color, + Image3F::Create(memory_manager, xsize, ysize)); ZeroFillImage(&color); ib.SetFromImage(std::move(color), ColorEncoding::SRGB()); - JXL_ASSIGN_OR_DIE(ImageF alpha, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_DIE(ImageF alpha, ImageF::Create(memory_manager, xsize, ysize)); ZeroFillImage(&alpha); ib.SetAlpha(std::move(alpha)); diff --git a/third_party/jpeg-xl/lib/jxl/dec_frame.cc b/third_party/jpeg-xl/lib/jxl/dec_frame.cc index ab51c1841f71..c3511d3b8cf8 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_frame.cc +++ b/third_party/jpeg-xl/lib/jxl/dec_frame.cc @@ -6,11 +6,12 @@ #include "lib/jxl/dec_frame.h" #include -#include -#include +#include #include #include +#include +#include #include #include #include @@ -63,8 +64,8 @@ Status DecodeGlobalDCInfo(BitReader* reader, bool is_jpeg, PassesDecoderState* state, ThreadPool* pool) { JXL_RETURN_IF_ERROR(state->shared_storage.quantizer.Decode(reader)); - JXL_RETURN_IF_ERROR( - DecodeBlockCtxMap(reader, &state->shared_storage.block_ctx_map)); + JXL_RETURN_IF_ERROR(DecodeBlockCtxMap(state->memory_manager(), reader, + &state->shared_storage.block_ctx_map)); JXL_RETURN_IF_ERROR(state->shared_storage.cmap.DecodeDC(reader)); @@ -135,6 +136,7 @@ Status FrameDecoder::InitFrame(BitReader* JXL_RESTRICT br, ImageBundle* decoded, bool is_preview) { decoded_ = decoded; JXL_ASSERT(is_finalized_); + JxlMemoryManager* memory_manager = decoded_->memory_manager(); // Reset the dequantization matrices to their default values. dec_state_->shared_storage.matrices = DequantMatrices(); @@ -170,7 +172,8 @@ Status FrameDecoder::InitFrame(BitReader* JXL_RESTRICT br, ImageBundle* decoded, NumTocEntries(num_groups, frame_dim_.num_dc_groups, num_passes); std::vector sizes; std::vector permutation; - JXL_RETURN_IF_ERROR(ReadToc(toc_entries, br, &sizes, &permutation)); + JXL_RETURN_IF_ERROR( + ReadToc(memory_manager, toc_entries, br, &sizes, &permutation)); bool have_permutation = !permutation.empty(); toc_.resize(toc_entries); section_sizes_sum_ = 0; @@ -264,10 +267,11 @@ Status FrameDecoder::InitFrameOutput() { Status FrameDecoder::ProcessDCGlobal(BitReader* br) { PassesSharedState& shared = dec_state_->shared_storage; + JxlMemoryManager* memory_manager = shared.memory_manager; if (frame_header_.flags & FrameHeader::kPatches) { bool uses_extra_channels = false; JXL_RETURN_IF_ERROR(shared.image_features.patches.Decode( - br, frame_dim_.xsize_padded, frame_dim_.ysize_padded, + memory_manager, br, frame_dim_.xsize_padded, frame_dim_.ysize_padded, &uses_extra_channels)); if (uses_extra_channels && frame_header_.upsampling != 1) { for (size_t ecups : frame_header_.extra_channel_upsampling) { @@ -284,7 +288,7 @@ Status FrameDecoder::ProcessDCGlobal(BitReader* br) { shared.image_features.splines.Clear(); if (frame_header_.flags & FrameHeader::kSplines) { JXL_RETURN_IF_ERROR(shared.image_features.splines.Decode( - br, frame_dim_.xsize * frame_dim_.ysize)); + memory_manager, br, frame_dim_.xsize * frame_dim_.ysize)); } if (frame_header_.flags & FrameHeader::kNoise) { JXL_RETURN_IF_ERROR(DecodeNoise(br, &shared.image_features.noise_params)); @@ -339,12 +343,13 @@ Status FrameDecoder::ProcessDCGroup(size_t dc_group_id, BitReader* br) { Status FrameDecoder::FinalizeDC() { // Do Adaptive DC smoothing if enabled. This *must* happen between all the // ProcessDCGroup and ProcessACGroup. + JxlMemoryManager* memory_manager = dec_state_->memory_manager(); if (frame_header_.encoding == FrameEncoding::kVarDCT && !(frame_header_.flags & FrameHeader::kSkipAdaptiveDCSmoothing) && !(frame_header_.flags & FrameHeader::kUseDcFrame)) { - JXL_RETURN_IF_ERROR( - AdaptiveDCSmoothing(dec_state_->shared->quantizer.MulDC(), - &dec_state_->shared_storage.dc_storage, pool_)); + JXL_RETURN_IF_ERROR(AdaptiveDCSmoothing( + memory_manager, dec_state_->shared->quantizer.MulDC(), + &dec_state_->shared_storage.dc_storage, pool_)); } finalized_dc_ = true; @@ -363,11 +368,12 @@ Status FrameDecoder::AllocateOutput() { Status FrameDecoder::ProcessACGlobal(BitReader* br) { JXL_CHECK(finalized_dc_); + JxlMemoryManager* memory_manager = dec_state_->memory_manager(); // Decode AC group. if (frame_header_.encoding == FrameEncoding::kVarDCT) { JXL_RETURN_IF_ERROR(dec_state_->shared_storage.matrices.Decode( - br, &modular_frame_decoder_)); + memory_manager, br, &modular_frame_decoder_)); JXL_RETURN_IF_ERROR(dec_state_->shared_storage.matrices.EnsureComputed( dec_state_->used_acs)); @@ -389,15 +395,16 @@ Status FrameDecoder::ProcessACGlobal(BitReader* br) { for (size_t i = 0; i < frame_header_.passes.num_passes; i++) { uint16_t used_orders = U32Coder::Read(kOrderEnc, br); JXL_RETURN_IF_ERROR(DecodeCoeffOrders( - used_orders, dec_state_->used_acs, + memory_manager, used_orders, dec_state_->used_acs, &dec_state_->shared_storage .coeff_orders[i * dec_state_->shared_storage.coeff_order_size], br)); size_t num_contexts = dec_state_->shared->num_histograms * dec_state_->shared_storage.block_ctx_map.NumACContexts(); - JXL_RETURN_IF_ERROR(DecodeHistograms( - br, num_contexts, &dec_state_->code[i], &dec_state_->context_map[i])); + JXL_RETURN_IF_ERROR(DecodeHistograms(memory_manager, br, num_contexts, + &dec_state_->code[i], + &dec_state_->context_map[i])); // Add extra values to enable the cheat in hot loop of DecodeACVarBlock. dec_state_->context_map[i].resize( num_contexts + kZeroDensityContextLimit - kZeroDensityContextCount); @@ -414,10 +421,10 @@ Status FrameDecoder::ProcessACGlobal(BitReader* br) { size_t ys = store ? frame_dim_.num_groups : 0; if (use_16_bit) { JXL_ASSIGN_OR_RETURN(dec_state_->coefficients, - ACImageT::Make(xs, ys)); + ACImageT::Make(memory_manager, xs, ys)); } else { JXL_ASSIGN_OR_RETURN(dec_state_->coefficients, - ACImageT::Make(xs, ys)); + ACImageT::Make(memory_manager, xs, ys)); } if (store) { dec_state_->coefficients->ZeroFill(); @@ -475,6 +482,7 @@ Status FrameDecoder::ProcessACGroup(size_t ac_group_id, const size_t gy = ac_group_id / frame_dim_.xsize_groups; const size_t x = gx * group_dim; const size_t y = gy * group_dim; + JxlMemoryManager* memory_manager = dec_state_->memory_manager(); JXL_DEBUG_V(3, "Processing AC group %" PRIuS "(%" PRIuS ",%" PRIuS ") group_dim: %" PRIuS " decoded passes: %u new passes: %" PRIuS, @@ -488,12 +496,12 @@ Status FrameDecoder::ProcessACGroup(size_t ac_group_id, if (frame_header_.encoding == FrameEncoding::kVarDCT) { JXL_RETURN_IF_ERROR(group_dec_caches_[thread].InitOnce( - frame_header_.passes.num_passes, dec_state_->used_acs)); - JXL_RETURN_IF_ERROR(DecodeGroup(frame_header_, br, num_passes, ac_group_id, - dec_state_, &group_dec_caches_[thread], - thread, render_pipeline_input, decoded_, - decoded_passes_per_ac_group_[ac_group_id], - force_draw, dc_only, &should_run_pipeline)); + memory_manager, frame_header_.passes.num_passes, dec_state_->used_acs)); + JXL_RETURN_IF_ERROR(DecodeGroup( + frame_header_, br, num_passes, ac_group_id, dec_state_, + &group_dec_caches_[thread], thread, render_pipeline_input, + decoded_->jpeg_data.get(), decoded_passes_per_ac_group_[ac_group_id], + force_draw, dc_only, &should_run_pipeline)); } // don't limit to image dimensions here (is done in DecodeGroup) @@ -676,8 +684,9 @@ Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num, pipeline_options.coalescing = coalescing_; pipeline_options.render_spotcolors = render_spotcolors_; pipeline_options.render_noise = true; - JXL_RETURN_IF_ERROR( - dec_state_->PreparePipeline(frame_header_, decoded_, pipeline_options)); + JXL_RETURN_IF_ERROR(dec_state_->PreparePipeline( + frame_header_, &frame_header_.nonserialized_metadata->m, decoded_, + pipeline_options)); JXL_RETURN_IF_ERROR(FinalizeDC()); JXL_RETURN_IF_ERROR(AllocateOutput()); if (progressive_detail_ >= JxlProgressiveDetail::kDC) { @@ -892,7 +901,7 @@ Status FrameDecoder::FinalizeFrame() { if (frame_header_.CanBeReferenced()) { auto& info = dec_state_->shared_storage .reference_frames[frame_header_.save_as_reference]; - info.frame = std::move(dec_state_->frame_storage_for_referencing); + *info.frame = std::move(dec_state_->frame_storage_for_referencing); info.ib_is_in_xyb = frame_header_.save_before_color_transform; } return true; diff --git a/third_party/jpeg-xl/lib/jxl/dec_frame.h b/third_party/jpeg-xl/lib/jxl/dec_frame.h index 663f1a8b3321..26412fd2f186 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_frame.h +++ b/third_party/jpeg-xl/lib/jxl/dec_frame.h @@ -8,10 +8,10 @@ #include #include -#include #include #include +#include #include #include #include @@ -49,6 +49,7 @@ class FrameDecoder { : dec_state_(dec_state), pool_(pool), frame_header_(&metadata), + modular_frame_decoder_(dec_state_->memory_manager()), use_slow_rendering_pipeline_(use_slow_rendering_pipeline) {} void SetRenderSpotcolors(bool rsc) { render_spotcolors_ = rsc; } diff --git a/third_party/jpeg-xl/lib/jxl/dec_group.cc b/third_party/jpeg-xl/lib/jxl/dec_group.cc index d1b03969c8db..e189b566491f 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_group.cc +++ b/third_party/jpeg-xl/lib/jxl/dec_group.cc @@ -170,7 +170,7 @@ Status DecodeGroupImpl(const FrameHeader& frame_header, PassesDecoderState* JXL_RESTRICT dec_state, size_t thread, size_t group_idx, RenderPipelineInput& render_pipeline_input, - ImageBundle* decoded, DrawMode draw) { + jpeg::JPEGData* jpeg_data, DrawMode draw) { // TODO(veluca): investigate cache usage in this function. const Rect block_rect = dec_state->shared->frame_dim.BlockGroupRect(group_idx); @@ -209,11 +209,11 @@ Status DecodeGroupImpl(const FrameHeader& frame_header, std::array dcoff = {}; // TODO(veluca): all of this should be done only once per image. - if (decoded->IsJPEG()) { + if (jpeg_data) { if (!dec_state->shared->cmap.IsJPEGCompatible()) { return JXL_FAILURE("The CfL map is not JPEG-compatible"); } - jpeg_is_gray = (decoded->jpeg_data->components.size() == 1); + jpeg_is_gray = (jpeg_data->components.size() == 1); jpeg_c_map = JpegOrder(frame_header.color_transform, jpeg_is_gray); const std::vector& qe = dec_state->shared->matrices.encodings(); @@ -279,8 +279,8 @@ Status DecodeGroupImpl(const FrameHeader& frame_header, for (size_t c = 0; c < 3; c++) { idct_row[c] = render_pipeline_input.GetBuffer(c).second.Row( render_pipeline_input.GetBuffer(c).first, sby[c] * kBlockDim); - if (decoded->IsJPEG()) { - auto& component = decoded->jpeg_data->components[jpeg_c_map[c]]; + if (jpeg_data) { + auto& component = jpeg_data->components[jpeg_c_map[c]]; jpeg_row[c] = component.coeffs.data() + (component.width_in_blocks * (r[c].y0() + sby[c]) + r[c].x0()) * @@ -344,7 +344,7 @@ Status DecodeGroupImpl(const FrameHeader& frame_header, continue; } - if (JXL_UNLIKELY(decoded->IsJPEG())) { + if (JXL_UNLIKELY(jpeg_data)) { if (acs.Strategy() != AcStrategy::Type::DCT) { return JXL_FAILURE( "Can only decode to JPEG if only DCT-8 is used."); @@ -606,8 +606,10 @@ struct GetBlockFromBitstream : public GetBlock { } ctx_offset[pass] = cur_histogram * block_ctx_map->NumACContexts(); - decoders[pass] = - ANSSymbolReader(&dec_state->code[pass + first_pass], readers[pass]); + JXL_ASSIGN_OR_RETURN( + decoders[pass], + ANSSymbolReader::Create(&dec_state->code[pass + first_pass], + readers[pass])); } nzeros_stride = group_dec_cache->num_nzeroes[0].PixelsPerRow(); for (size_t i = 0; i < num_passes; i++) { @@ -688,8 +690,9 @@ Status DecodeGroup(const FrameHeader& frame_header, PassesDecoderState* JXL_RESTRICT dec_state, GroupDecCache* JXL_RESTRICT group_dec_cache, size_t thread, RenderPipelineInput& render_pipeline_input, - ImageBundle* JXL_RESTRICT decoded, size_t first_pass, + jpeg::JPEGData* JXL_RESTRICT jpeg_data, size_t first_pass, bool force_draw, bool dc_only, bool* should_run_pipeline) { + JxlMemoryManager* memory_manager = dec_state->memory_manager(); DrawMode draw = (num_passes + first_pass == frame_header.passes.num_passes) || force_draw ? kDraw @@ -700,7 +703,7 @@ Status DecodeGroup(const FrameHeader& frame_header, } if (draw == kDraw && num_passes == 0 && first_pass == 0) { - JXL_RETURN_IF_ERROR(group_dec_cache->InitDCBufferOnce()); + JXL_RETURN_IF_ERROR(group_dec_cache->InitDCBufferOnce(memory_manager)); const YCbCrChromaSubsampling& cs = frame_header.chroma_subsampling; for (size_t c : {0, 1, 2}) { size_t hs = cs.HShift(c); @@ -777,7 +780,7 @@ Status DecodeGroup(const FrameHeader& frame_header, JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(DecodeGroupImpl)( frame_header, get_block.get(), group_dec_cache, dec_state, thread, - group_idx, render_pipeline_input, decoded, draw)); + group_idx, render_pipeline_input, jpeg_data, draw)); for (size_t pass = 0; pass < num_passes; pass++) { if (!get_block->decoders[pass].CheckANSFinalState()) { @@ -794,16 +797,18 @@ Status DecodeGroupForRoundtrip(const FrameHeader& frame_header, GroupDecCache* JXL_RESTRICT group_dec_cache, size_t thread, RenderPipelineInput& render_pipeline_input, - ImageBundle* JXL_RESTRICT decoded, + jpeg::JPEGData* JXL_RESTRICT jpeg_data, AuxOut* aux_out) { + JxlMemoryManager* memory_manager = dec_state->memory_manager(); GetBlockFromEncoder get_block(ac, group_idx, frame_header.passes.shift); JXL_RETURN_IF_ERROR(group_dec_cache->InitOnce( + memory_manager, /*num_passes=*/0, /*used_acs=*/(1u << AcStrategy::kNumValidStrategies) - 1)); return HWY_DYNAMIC_DISPATCH(DecodeGroupImpl)( frame_header, &get_block, group_dec_cache, dec_state, thread, group_idx, - render_pipeline_input, decoded, kDraw); + render_pipeline_input, jpeg_data, kDraw); } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/dec_group.h b/third_party/jpeg-xl/lib/jxl/dec_group.h index 7f3ba5e868a3..0d598a94cf44 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_group.h +++ b/third_party/jpeg-xl/lib/jxl/dec_group.h @@ -29,7 +29,7 @@ Status DecodeGroup(const FrameHeader& frame_header, PassesDecoderState* JXL_RESTRICT dec_state, GroupDecCache* JXL_RESTRICT group_dec_cache, size_t thread, RenderPipelineInput& render_pipeline_input, - ImageBundle* JXL_RESTRICT decoded, size_t first_pass, + jpeg::JPEGData* JXL_RESTRICT jpeg_data, size_t first_pass, bool force_draw, bool dc_only, bool* should_run_pipeline); Status DecodeGroupForRoundtrip(const FrameHeader& frame_header, @@ -39,7 +39,7 @@ Status DecodeGroupForRoundtrip(const FrameHeader& frame_header, GroupDecCache* JXL_RESTRICT group_dec_cache, size_t thread, RenderPipelineInput& render_pipeline_input, - ImageBundle* JXL_RESTRICT decoded, + jpeg::JPEGData* JXL_RESTRICT jpeg_data, AuxOut* aux_out); } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/dec_modular.cc b/third_party/jpeg-xl/lib/jxl/dec_modular.cc index 049d6e4f91f6..4c67e5d0ce4b 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_modular.cc +++ b/third_party/jpeg-xl/lib/jxl/dec_modular.cc @@ -5,6 +5,8 @@ #include "lib/jxl/dec_modular.h" +#include + #include #include #include @@ -177,6 +179,7 @@ std::string ModularStreamId::DebugString() const { Status ModularFrameDecoder::DecodeGlobalInfo(BitReader* reader, const FrameHeader& frame_header, bool allow_truncated_group) { + JxlMemoryManager* memory_manager = this->memory_manager(); bool decode_color = frame_header.encoding == FrameEncoding::kModular; const auto& metadata = frame_header.nonserialized_metadata->m; bool is_gray = metadata.color_encoding.IsGray(); @@ -194,9 +197,10 @@ Status ModularFrameDecoder::DecodeGlobalInfo(BitReader* reader, std::min(static_cast(1 << 22), 1024 + frame_dim.xsize * frame_dim.ysize * (nb_chans + nb_extra) / 16); - JXL_RETURN_IF_ERROR(DecodeTree(reader, &tree, tree_size_limit)); JXL_RETURN_IF_ERROR( - DecodeHistograms(reader, (tree.size() + 1) / 2, &code, &context_map)); + DecodeTree(memory_manager, reader, &tree, tree_size_limit)); + JXL_RETURN_IF_ERROR(DecodeHistograms( + memory_manager, reader, (tree.size() + 1) / 2, &code, &context_map)); } } if (!do_color) nb_chans = 0; @@ -215,7 +219,7 @@ Status ModularFrameDecoder::DecodeGlobalInfo(BitReader* reader, JXL_ASSIGN_OR_RETURN( Image gi, - Image::Create(frame_dim.xsize, frame_dim.ysize, + Image::Create(memory_manager, frame_dim.xsize, frame_dim.ysize, metadata.bit_depth.bits_per_sample, nb_chans + nb_extra)); all_same_shift = true; @@ -306,8 +310,8 @@ Status ModularFrameDecoder::DecodeGroup( stream.kind == ModularStreamId::kModularAC); const size_t xsize = rect.xsize(); const size_t ysize = rect.ysize(); - JXL_ASSIGN_OR_RETURN(Image gi, - Image::Create(xsize, ysize, full_image.bitdepth, 0)); + JXL_ASSIGN_OR_RETURN(Image gi, Image::Create(memory_manager_, xsize, ysize, + full_image.bitdepth, 0)); // start at the first bigger-than-groupsize non-metachannel size_t c = full_image.nb_meta_channels; for (; c < full_image.channel.size(); c++) { @@ -329,7 +333,8 @@ Status ModularFrameDecoder::DecodeGroup( memset(row_out, 0, r.xsize() * sizeof(*row_out)); } } else { - JXL_ASSIGN_OR_RETURN(Channel gc, Channel::Create(r.xsize(), r.ysize())); + JXL_ASSIGN_OR_RETURN( + Channel gc, Channel::Create(memory_manager_, r.xsize(), r.ysize())); if (zerofill) ZeroFillImage(&gc.plane); gc.hshift = fc.hshift; gc.vshift = fc.vshift; @@ -391,6 +396,7 @@ Status ModularFrameDecoder::DecodeGroup( Status ModularFrameDecoder::DecodeVarDCTDC(const FrameHeader& frame_header, size_t group_id, BitReader* reader, PassesDecoderState* dec_state) { + JxlMemoryManager* memory_manager = dec_state->memory_manager(); const Rect r = dec_state->shared->frame_dim.DCGroupRect(group_id); JXL_DEBUG_V(6, "Decoding VarDCT DC with rect %s", Description(r).c_str()); // TODO(eustas): investigate if we could reduce the impact of @@ -399,8 +405,9 @@ Status ModularFrameDecoder::DecodeVarDCTDC(const FrameHeader& frame_header, // 3 comes from XybToRgb that cubes the values, and "magic" is // the sum of all other contributions. 2**18 is known to lead // to NaN on input found by fuzzing (see commit message). - JXL_ASSIGN_OR_RETURN( - Image image, Image::Create(r.xsize(), r.ysize(), full_image.bitdepth, 3)); + JXL_ASSIGN_OR_RETURN(Image image, + Image::Create(memory_manager, r.xsize(), r.ysize(), + full_image.bitdepth, 3)); size_t stream_id = ModularStreamId::VarDCTDC(group_id).ID(frame_dim); reader->Refill(); size_t extra_precision = reader->ReadFixedBits<2>(); @@ -429,6 +436,7 @@ Status ModularFrameDecoder::DecodeVarDCTDC(const FrameHeader& frame_header, Status ModularFrameDecoder::DecodeAcMetadata(const FrameHeader& frame_header, size_t group_id, BitReader* reader, PassesDecoderState* dec_state) { + JxlMemoryManager* memory_manager = dec_state->memory_manager(); const Rect r = dec_state->shared->frame_dim.DCGroupRect(group_id); JXL_DEBUG_V(6, "Decoding AcMetadata with rect %s", Description(r).c_str()); size_t upper_bound = r.xsize() * r.ysize(); @@ -436,15 +444,19 @@ Status ModularFrameDecoder::DecodeAcMetadata(const FrameHeader& frame_header, size_t count = reader->ReadBits(CeilLog2Nonzero(upper_bound)) + 1; size_t stream_id = ModularStreamId::ACMetadata(group_id).ID(frame_dim); // YToX, YToB, ACS + QF, EPF - JXL_ASSIGN_OR_RETURN( - Image image, Image::Create(r.xsize(), r.ysize(), full_image.bitdepth, 4)); + JXL_ASSIGN_OR_RETURN(Image image, + Image::Create(memory_manager, r.xsize(), r.ysize(), + full_image.bitdepth, 4)); static_assert(kColorTileDimInBlocks == 8, "Color tile size changed"); Rect cr(r.x0() >> 3, r.y0() >> 3, (r.xsize() + 7) >> 3, (r.ysize() + 7) >> 3); - JXL_ASSIGN_OR_RETURN(image.channel[0], - Channel::Create(cr.xsize(), cr.ysize(), 3, 3)); - JXL_ASSIGN_OR_RETURN(image.channel[1], - Channel::Create(cr.xsize(), cr.ysize(), 3, 3)); - JXL_ASSIGN_OR_RETURN(image.channel[2], Channel::Create(count, 2, 0, 0)); + JXL_ASSIGN_OR_RETURN( + image.channel[0], + Channel::Create(memory_manager, cr.xsize(), cr.ysize(), 3, 3)); + JXL_ASSIGN_OR_RETURN( + image.channel[1], + Channel::Create(memory_manager, cr.xsize(), cr.ysize(), 3, 3)); + JXL_ASSIGN_OR_RETURN(image.channel[2], + Channel::Create(memory_manager, count, 2, 0, 0)); ModularOptions options; if (!ModularGenericDecompress( reader, image, /*header=*/nullptr, stream_id, &options, @@ -692,7 +704,8 @@ Status ModularFrameDecoder::FinalizeDecoding(const FrameHeader& frame_header, jxl::ThreadPool* pool, bool inplace) { if (!use_full_image) return true; - Image gi; + JxlMemoryManager* memory_manager = dec_state->memory_manager(); + Image gi{memory_manager}; if (inplace) { gi = std::move(full_image); } else { @@ -747,8 +760,8 @@ Status ModularFrameDecoder::FinalizeDecoding(const FrameHeader& frame_header, static constexpr const float kAlmostZero = 1e-8f; Status ModularFrameDecoder::DecodeQuantTable( - size_t required_size_x, size_t required_size_y, BitReader* br, - QuantEncoding* encoding, size_t idx, + JxlMemoryManager* memory_manager, size_t required_size_x, + size_t required_size_y, BitReader* br, QuantEncoding* encoding, size_t idx, ModularFrameDecoder* modular_frame_decoder) { JXL_RETURN_IF_ERROR(F16Coder::Read(br, &encoding->qraw.qtable_den)); if (encoding->qraw.qtable_den < kAlmostZero) { @@ -756,8 +769,9 @@ Status ModularFrameDecoder::DecodeQuantTable( // be negative. return JXL_FAILURE("Invalid qtable_den: value too small"); } - JXL_ASSIGN_OR_RETURN(Image image, - Image::Create(required_size_x, required_size_y, 8, 3)); + JXL_ASSIGN_OR_RETURN( + Image image, + Image::Create(memory_manager, required_size_x, required_size_y, 8, 3)); ModularOptions options; if (modular_frame_decoder) { JXL_RETURN_IF_ERROR(ModularGenericDecompress( diff --git a/third_party/jpeg-xl/lib/jxl/dec_modular.h b/third_party/jpeg-xl/lib/jxl/dec_modular.h index 70eab8af89a0..1fab8e860260 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_modular.h +++ b/third_party/jpeg-xl/lib/jxl/dec_modular.h @@ -6,6 +6,8 @@ #ifndef LIB_JXL_DEC_MODULAR_H_ #define LIB_JXL_DEC_MODULAR_H_ +#include + #include #include #include @@ -91,6 +93,8 @@ struct ModularStreamId { class ModularFrameDecoder { public: + explicit ModularFrameDecoder(JxlMemoryManager* memory_manager) + : memory_manager_(memory_manager), full_image(memory_manager) {} void Init(const FrameDimensions& frame_dim) { this->frame_dim = frame_dim; } Status DecodeGlobalInfo(BitReader* reader, const FrameHeader& frame_header, bool allow_truncated_group); @@ -109,7 +113,8 @@ class ModularFrameDecoder { // Decodes a RAW quant table from `br` into the given `encoding`, of size // `required_size_x x required_size_y`. If `modular_frame_decoder` is passed, // its global tree is used, otherwise no global tree is used. - static Status DecodeQuantTable(size_t required_size_x, size_t required_size_y, + static Status DecodeQuantTable(JxlMemoryManager* memory_manager, + size_t required_size_x, size_t required_size_y, BitReader* br, QuantEncoding* encoding, size_t idx, ModularFrameDecoder* modular_frame_decoder); @@ -122,6 +127,7 @@ class ModularFrameDecoder { bool have_dc() const { return have_something; } void MaybeDropFullImage(); bool UsesFullImage() const { return use_full_image; } + JxlMemoryManager* memory_manager() const { return memory_manager_; } private: Status ModularImageToDecodedRect(const FrameHeader& frame_header, Image& gi, @@ -129,7 +135,7 @@ class ModularFrameDecoder { jxl::ThreadPool* pool, RenderPipelineInput& render_pipeline_input, Rect modular_rect) const; - + JxlMemoryManager* memory_manager_; Image full_image; std::vector global_transform; FrameDimensions frame_dim; diff --git a/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.cc b/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.cc index 120a977ca7ee..605818de43e9 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.cc +++ b/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.cc @@ -5,11 +5,12 @@ #include "lib/jxl/dec_patch_dictionary.h" -#include -#include +#include #include #include +#include +#include #include #include @@ -25,14 +26,16 @@ namespace jxl { -Status PatchDictionary::Decode(BitReader* br, size_t xsize, size_t ysize, +Status PatchDictionary::Decode(JxlMemoryManager* memory_manager, BitReader* br, + size_t xsize, size_t ysize, bool* uses_extra_channels) { positions_.clear(); std::vector context_map; ANSCode code; - JXL_RETURN_IF_ERROR( - DecodeHistograms(br, kNumPatchDictionaryContexts, &code, &context_map)); - ANSSymbolReader decoder(&code, br); + JXL_RETURN_IF_ERROR(DecodeHistograms( + memory_manager, br, kNumPatchDictionaryContexts, &code, &context_map)); + JXL_ASSIGN_OR_RETURN(ANSSymbolReader decoder, + ANSSymbolReader::Create(&code, br)); auto read_num = [&](size_t context) { size_t r = decoder.ReadHybridUint(context, br, context_map); @@ -58,14 +61,14 @@ Status PatchDictionary::Decode(BitReader* br, size_t xsize, size_t ysize, PatchReferencePosition ref_pos; ref_pos.ref = read_num(kReferenceFrameContext); if (ref_pos.ref >= kMaxNumReferenceFrames || - shared_->reference_frames[ref_pos.ref].frame.xsize() == 0) { + shared_->reference_frames[ref_pos.ref].frame->xsize() == 0) { return JXL_FAILURE("Invalid reference frame ID"); } if (!shared_->reference_frames[ref_pos.ref].ib_is_in_xyb) { return JXL_FAILURE( "Patches cannot use frames saved post color transforms"); } - const ImageBundle& ib = shared_->reference_frames[ref_pos.ref].frame; + const ImageBundle& ib = *shared_->reference_frames[ref_pos.ref].frame; ref_pos.x0 = read_num(kPatchReferencePositionContext); ref_pos.y0 = read_num(kPatchReferencePositionContext); ref_pos.xsize = read_num(kPatchSizeContext) + 1; @@ -318,6 +321,7 @@ Status PatchDictionary::AddOneRow(float* const* inout, size_t y, size_t x0, size_t xsize) const { size_t num_ec = shared_->metadata->m.num_extra_channels; std::vector fg_ptrs(3 + num_ec); + JxlMemoryManager* memory_manager = shared_->memory_manager; for (size_t pos_idx : GetPatchesForRow(y)) { const size_t blending_idx = pos_idx * (num_ec + 1); const PatchPosition& pos = positions_[pos_idx]; @@ -334,19 +338,20 @@ Status PatchDictionary::AddOneRow(float* const* inout, size_t y, size_t x0, size_t patch_x0 = std::max(bx, x0); size_t patch_x1 = std::min(bx + patch_xsize, x0 + xsize); for (size_t c = 0; c < 3; c++) { - fg_ptrs[c] = shared_->reference_frames[ref].frame.color().ConstPlaneRow( + fg_ptrs[c] = shared_->reference_frames[ref].frame->color()->ConstPlaneRow( c, ref_pos.y0 + iy) + ref_pos.x0 + x0 - bx; } for (size_t i = 0; i < num_ec; i++) { fg_ptrs[3 + i] = - shared_->reference_frames[ref].frame.extra_channels()[i].ConstRow( + shared_->reference_frames[ref].frame->extra_channels()[i].ConstRow( ref_pos.y0 + iy) + ref_pos.x0 + x0 - bx; } JXL_RETURN_IF_ERROR(PerformBlending( - inout, fg_ptrs.data(), inout, patch_x0 - x0, patch_x1 - patch_x0, - blendings_[blending_idx], blendings_.data() + blending_idx + 1, + memory_manager, inout, fg_ptrs.data(), inout, patch_x0 - x0, + patch_x1 - patch_x0, blendings_[blending_idx], + blendings_.data() + blending_idx + 1, shared_->metadata->m.extra_channel_info)); } return true; diff --git a/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.h b/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.h index ad0f5d6a04bb..0efdce56d106 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.h +++ b/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.h @@ -8,6 +8,7 @@ // Chooses reference patches, and avoids encoding them once per occurrence. +#include #include #include @@ -99,8 +100,8 @@ class PatchDictionary { bool HasAny() const { return !positions_.empty(); } - Status Decode(BitReader* br, size_t xsize, size_t ysize, - bool* uses_extra_channels); + Status Decode(JxlMemoryManager* memory_manager, BitReader* br, size_t xsize, + size_t ysize, bool* uses_extra_channels); void Clear() { positions_.clear(); diff --git a/third_party/jpeg-xl/lib/jxl/dec_xyb.cc b/third_party/jpeg-xl/lib/jxl/dec_xyb.cc index 009c078fae01..12c27f9a88ff 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_xyb.cc +++ b/third_party/jpeg-xl/lib/jxl/dec_xyb.cc @@ -15,6 +15,7 @@ #include "lib/jxl/base/compiler_specific.h" #include "lib/jxl/base/matrix_ops.h" #include "lib/jxl/base/rect.h" +#include "lib/jxl/base/sanitizers.h" #include "lib/jxl/base/status.h" #include "lib/jxl/cms/jxl_cms_internal.h" #include "lib/jxl/cms/opsin_params.h" @@ -23,7 +24,6 @@ #include "lib/jxl/image.h" #include "lib/jxl/opsin_params.h" #include "lib/jxl/quantizer.h" -#include "lib/jxl/sanitizers.h" HWY_BEFORE_NAMESPACE(); namespace jxl { namespace HWY_NAMESPACE { diff --git a/third_party/jpeg-xl/lib/jxl/decode.cc b/third_party/jpeg-xl/lib/jxl/decode.cc index 605c2d6fd614..a7e0237fd802 100644 --- a/third_party/jpeg-xl/lib/jxl/decode.cc +++ b/third_party/jpeg-xl/lib/jxl/decode.cc @@ -26,9 +26,7 @@ #if JPEGXL_ENABLE_BOXES || JPEGXL_ENABLE_TRANSCODE_JPEG #include "lib/jxl/box_content_decoder.h" #endif -#include "lib/jxl/dec_external_image.h" #include "lib/jxl/dec_frame.h" -#include "lib/jxl/dec_modular.h" #if JPEGXL_ENABLE_TRANSCODE_JPEG #include "lib/jxl/decode_to_jpeg.h" #endif @@ -38,10 +36,7 @@ #include "lib/jxl/headers.h" #include "lib/jxl/icc_codec.h" #include "lib/jxl/image_bundle.h" -#include "lib/jxl/loop_filter.h" #include "lib/jxl/memory_manager_internal.h" -#include "lib/jxl/sanitizers.h" -#include "lib/jxl/toc.h" namespace { @@ -364,7 +359,7 @@ struct JxlDecoderStruct { bool got_transform_data; // To skip everything before ICC. bool got_all_headers; // Codestream metadata headers. bool post_headers; // Already decoding pixels. - jxl::ICCReader icc_reader; + std::unique_ptr icc_reader; jxl::JxlDecoderFrameIndexBox frame_index_box; // This means either we actually got the preview image, or determined we // cannot get it or there is none. @@ -687,7 +682,7 @@ void JxlDecoderRewindDecodingState(JxlDecoder* dec) { dec->got_transform_data = false; dec->got_all_headers = false; dec->post_headers = false; - dec->icc_reader.Reset(); + if (dec->icc_reader) dec->icc_reader->Reset(); dec->got_preview_image = false; dec->preview_frame = false; dec->file_pos = 0; @@ -1050,7 +1045,7 @@ JxlDecoderStatus JxlDecoderReadAllHeaders(JxlDecoder* dec) { if (dec->metadata.m.color_encoding.WantICC()) { jxl::Status status = - dec->icc_reader.Init(reader.get(), dec->memory_limit_base); + dec->icc_reader->Init(reader.get(), dec->memory_limit_base); // Always check AllReadsWithinBounds, not all the C++ decoder implementation // handles reader out of bounds correctly yet (e.g. context map). Not // checking AllReadsWithinBounds can cause reader->Close() to trigger an @@ -1063,8 +1058,8 @@ JxlDecoderStatus JxlDecoderReadAllHeaders(JxlDecoder* dec) { // Other non-successful status is an error return JXL_DEC_ERROR; } - PaddedBytes decoded_icc; - status = dec->icc_reader.Process(reader.get(), &decoded_icc); + PaddedBytes decoded_icc{&dec->memory_manager}; + status = dec->icc_reader->Process(reader.get(), &decoded_icc); if (status.code() == StatusCode::kNotEnoughBytes) { return dec->RequestMoreInput(); } @@ -1087,7 +1082,7 @@ JxlDecoderStatus JxlDecoderReadAllHeaders(JxlDecoder* dec) { dec->codestream_bits_ahead = 0; if (!dec->passes_state) { - dec->passes_state.reset(new jxl::PassesDecoderState()); + dec->passes_state.reset(new jxl::PassesDecoderState(&dec->memory_manager)); } JXL_API_RETURN_IF_ERROR( @@ -1188,6 +1183,10 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec) { return JXL_DEC_SUCCESS; } + if (!dec->icc_reader) { + dec->icc_reader.reset(new ICCReader(&dec->memory_manager)); + } + if (!dec->got_all_headers) { JxlDecoderStatus status = JxlDecoderReadAllHeaders(dec); if (status != JXL_DEC_SUCCESS) return status; @@ -1233,7 +1232,8 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec) { } #endif if (!dec->ib) { - dec->ib.reset(new jxl::ImageBundle(&dec->image_metadata)); + dec->ib.reset( + new jxl::ImageBundle(&dec->memory_manager, &dec->image_metadata)); } #if JPEGXL_ENABLE_TRANSCODE_JPEG // If JPEG reconstruction is wanted and possible, set the jpeg_data of @@ -2345,7 +2345,7 @@ JxlDecoderStatus JxlDecoderFlushImage(JxlDecoder* dec) { JXL_EXPORT JxlDecoderStatus JxlDecoderSetCms(JxlDecoder* dec, const JxlCmsInterface cms) { if (!dec->passes_state) { - dec->passes_state.reset(new jxl::PassesDecoderState()); + dec->passes_state.reset(new jxl::PassesDecoderState(&dec->memory_manager)); } dec->passes_state->output_encoding_info.color_management_system = cms; dec->passes_state->output_encoding_info.cms_set = true; diff --git a/third_party/jpeg-xl/lib/jxl/decode_test.cc b/third_party/jpeg-xl/lib/jxl/decode_test.cc index bf6723016f01..1758d8376b7a 100644 --- a/third_party/jpeg-xl/lib/jxl/decode_test.cc +++ b/third_party/jpeg-xl/lib/jxl/decode_test.cc @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -203,6 +202,7 @@ enum PreviewMode { }; void GeneratePreview(PreviewMode preview_mode, ImageBundle* ib) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); if (preview_mode == kSmallPreview) { ib->ShrinkTo(ib->xsize() / 7, ib->ysize() / 7); } else if (preview_mode == kBigPreview) { @@ -213,15 +213,17 @@ void GeneratePreview(PreviewMode preview_mode, ImageBundle* ib) { } } }; - JXL_ASSIGN_OR_DIE(Image3F preview, - Image3F::Create(ib->xsize() * 7, ib->ysize() * 7)); + JXL_ASSIGN_OR_DIE( + Image3F preview, + Image3F::Create(memory_manager, ib->xsize() * 7, ib->ysize() * 7)); for (size_t c = 0; c < 3; ++c) { upsample7(ib->color()->Plane(c), &preview.Plane(c)); } std::vector extra_channels; for (size_t i = 0; i < ib->extra_channels().size(); ++i) { - JXL_ASSIGN_OR_DIE(ImageF ec, - ImageF::Create(ib->xsize() * 7, ib->ysize() * 7)); + JXL_ASSIGN_OR_DIE( + ImageF ec, + ImageF::Create(memory_manager, ib->xsize() * 7, ib->ysize() * 7)); upsample7(ib->extra_channels()[i], &ec); extra_channels.emplace_back(std::move(ec)); } @@ -257,12 +259,13 @@ struct TestCodestreamParams { std::vector CreateTestJXLCodestream( Span pixels, size_t xsize, size_t ysize, size_t num_channels, const TestCodestreamParams& params) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); // Compress the pixels with JPEG XL. bool grayscale = (num_channels <= 2); bool have_alpha = ((num_channels & 1) == 0); bool include_alpha = have_alpha && params.jpeg_codestream == nullptr; size_t bitdepth = params.jpeg_codestream == nullptr ? 16 : 8; - CodecInOut io; + CodecInOut io{jxl::test::MemoryManager()}; io.SetSize(xsize, ysize); ColorEncoding color_encoding; if (params.add_icc_profile) { @@ -315,8 +318,8 @@ std::vector CreateTestJXLCodestream( Bytes(jpeg_bytes).AppendTo(*params.jpeg_codestream); EXPECT_TRUE(jxl::jpeg::DecodeImageJPG( jxl::Bytes(jpeg_bytes.data(), jpeg_bytes.size()), &io)); - EXPECT_TRUE( - EncodeJPEGData(*io.Main().jpeg_data, &jpeg_data, params.cparams)); + EXPECT_TRUE(EncodeJPEGData(memory_manager, *io.Main().jpeg_data, + &jpeg_data, params.cparams)); io.metadata.m.xyb_encoded = false; } else { JXL_ABORT( @@ -720,7 +723,8 @@ std::vector GetTestHeader(size_t xsize, size_t ysize, bool have_container, bool metadata_default, bool insert_extra_box, const jxl::IccBytes& icc_profile) { - jxl::BitWriter writer; + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + jxl::BitWriter writer{memory_manager}; jxl::BitWriter::Allotment allotment(&writer, 65536); // Large enough if (have_container) { @@ -1352,7 +1356,7 @@ TEST_P(DecodeTestParam, PixelTest) { jxl::ColorEncoding color_encoding = jxl::ColorEncoding::SRGB(config.grayscale); - jxl::CodecInOut io; + jxl::CodecInOut io{jxl::test::MemoryManager()}; if (config.include_alpha) io.metadata.m.SetAlphaBits(16); io.metadata.m.color_encoding = color_encoding; io.SetSize(config.xsize, config.ysize); @@ -1651,6 +1655,7 @@ TEST(DecodeTest, PixelTestWithICCProfileLossless) { } TEST(DecodeTest, PixelTestWithICCProfileLossy) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); JxlDecoder* dec = JxlDecoderCreate(nullptr); size_t xsize = 123; @@ -1680,7 +1685,7 @@ TEST(DecodeTest, PixelTestWithICCProfileLossy) { jxl::ColorEncoding color_encoding0; EXPECT_TRUE(color_encoding0.SetICC(GetIccTestProfile(), JxlGetDefaultCms())); jxl::Span span0(pixels.data(), pixels.size()); - jxl::CodecInOut io0; + jxl::CodecInOut io0{memory_manager}; io0.SetSize(xsize, ysize); EXPECT_TRUE(ConvertFromExternal(span0, xsize, ysize, color_encoding0, /*bits_per_sample=*/16, format_orig, @@ -1691,7 +1696,7 @@ TEST(DecodeTest, PixelTestWithICCProfileLossy) { jxl::Bytes(icc_data).AppendTo(icc); EXPECT_TRUE(color_encoding1.SetICC(std::move(icc), JxlGetDefaultCms())); jxl::Span span1(pixels2.data(), pixels2.size()); - jxl::CodecInOut io1; + jxl::CodecInOut io1{memory_manager}; io1.SetSize(xsize, ysize); EXPECT_TRUE(ConvertFromExternal(span1, xsize, ysize, color_encoding1, /*bits_per_sample=*/32, format, @@ -1735,7 +1740,8 @@ double ButteraugliDistance(size_t xsize, size_t ysize, const std::vector& pixels_out, const jxl::ColorEncoding& color_out, float intensity_out) { - jxl::CodecInOut in; + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + jxl::CodecInOut in{memory_manager}; in.metadata.m.color_encoding = color_in; in.metadata.m.SetIntensityTarget(intensity_in); JxlPixelFormat format_in = {static_cast(color_in.Channels()), @@ -1744,7 +1750,7 @@ double ButteraugliDistance(size_t xsize, size_t ysize, jxl::Bytes(pixels_in.data(), pixels_in.size()), xsize, ysize, color_in, /*bits_per_sample=*/16, format_in, /*pool=*/nullptr, &in.Main())); - jxl::CodecInOut out; + jxl::CodecInOut out{memory_manager}; out.metadata.m.color_encoding = color_out; out.metadata.m.SetIntensityTarget(intensity_out); JxlPixelFormat format_out = {static_cast(color_out.Channels()), @@ -2058,6 +2064,7 @@ TEST_P(DecodeAllEncodingsWithCMSTest, DecodeWithCMS) { // Tests the case of lossy sRGB image without alpha channel, decoded to RGB8 // and to RGBA8 TEST(DecodeTest, PixelTestOpaqueSrgbLossy) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); for (unsigned channels = 3; channels <= 4; channels++) { JxlDecoder* dec = JxlDecoderCreate(nullptr); @@ -2083,7 +2090,7 @@ TEST(DecodeTest, PixelTestOpaqueSrgbLossy) { jxl::ColorEncoding color_encoding0 = jxl::ColorEncoding::SRGB(false); jxl::Span span0(pixels.data(), pixels.size()); - jxl::CodecInOut io0; + jxl::CodecInOut io0{memory_manager}; io0.SetSize(xsize, ysize); EXPECT_TRUE(ConvertFromExternal(span0, xsize, ysize, color_encoding0, /*bits_per_sample=*/16, format_orig, @@ -2091,7 +2098,7 @@ TEST(DecodeTest, PixelTestOpaqueSrgbLossy) { jxl::ColorEncoding color_encoding1 = jxl::ColorEncoding::SRGB(false); jxl::Span span1(pixels2.data(), pixels2.size()); - jxl::CodecInOut io1; + jxl::CodecInOut io1{memory_manager}; EXPECT_TRUE(ConvertFromExternal(span1, xsize, ysize, color_encoding1, /*bits_per_sample=*/8, format, /*pool=*/nullptr, &io1.Main())); @@ -2134,7 +2141,7 @@ TEST(DecodeTest, PixelTestOpaqueSrgbLossyNoise) { jxl::ColorEncoding color_encoding0 = jxl::ColorEncoding::SRGB(false); jxl::Span span0(pixels.data(), pixels.size()); - jxl::CodecInOut io0; + jxl::CodecInOut io0{jxl::test::MemoryManager()}; io0.SetSize(xsize, ysize); EXPECT_TRUE(ConvertFromExternal(span0, xsize, ysize, color_encoding0, /*bits_per_sample=*/16, format_orig, @@ -2142,7 +2149,7 @@ TEST(DecodeTest, PixelTestOpaqueSrgbLossyNoise) { jxl::ColorEncoding color_encoding1 = jxl::ColorEncoding::SRGB(false); jxl::Span span1(pixels2.data(), pixels2.size()); - jxl::CodecInOut io1; + jxl::CodecInOut io1{jxl::test::MemoryManager()}; EXPECT_TRUE(ConvertFromExternal(span1, xsize, ysize, color_encoding1, /*bits_per_sample=*/8, format, /*pool=*/nullptr, &io1.Main())); @@ -2510,6 +2517,7 @@ TEST(DecodeTest, DCNotGettableTest) { } TEST(DecodeTest, PreviewTest) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); size_t xsize = 77; size_t ysize = 120; std::vector pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); @@ -2540,7 +2548,7 @@ TEST(DecodeTest, PreviewTest) { JxlDecoderPreviewOutBufferSize(dec, &format, &buffer_size)); jxl::ColorEncoding c_srgb = jxl::ColorEncoding::SRGB(false); - jxl::CodecInOut io0; + jxl::CodecInOut io0{memory_manager}; EXPECT_TRUE(jxl::ConvertFromExternal( jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, c_srgb, /*bits_per_sample=*/16, format_orig, /*pool=*/nullptr, &io0.Main())); @@ -2561,7 +2569,7 @@ TEST(DecodeTest, PreviewTest) { EXPECT_EQ(JXL_DEC_PREVIEW_IMAGE, JxlDecoderProcessInput(dec)); - jxl::CodecInOut io1; + jxl::CodecInOut io1{memory_manager}; EXPECT_TRUE( jxl::ConvertFromExternal(jxl::Bytes(preview.data(), preview.size()), xsize_preview, ysize_preview, c_srgb, @@ -2619,6 +2627,7 @@ TEST(DecodeTest, AlignTest) { } TEST(DecodeTest, AnimationTest) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); size_t xsize = 123; size_t ysize = 77; static const size_t num_frames = 2; @@ -2627,7 +2636,7 @@ TEST(DecodeTest, AnimationTest) { frames[1] = jxl::test::GetSomeTestImage(xsize, ysize, 3, 1); JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; - jxl::CodecInOut io; + jxl::CodecInOut io{memory_manager}; io.SetSize(xsize, ysize); io.metadata.m.SetUintSamples(16); io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false); @@ -2642,7 +2651,7 @@ TEST(DecodeTest, AnimationTest) { } for (size_t i = 0; i < num_frames; ++i) { - jxl::ImageBundle bundle(&io.metadata.m); + jxl::ImageBundle bundle(memory_manager, &io.metadata.m); EXPECT_TRUE(ConvertFromExternal( jxl::Bytes(frames[i].data(), frames[i].size()), xsize, ysize, @@ -2720,6 +2729,7 @@ TEST(DecodeTest, AnimationTest) { } TEST(DecodeTest, AnimationTestStreaming) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); size_t xsize = 123; size_t ysize = 77; static const size_t num_frames = 2; @@ -2728,7 +2738,7 @@ TEST(DecodeTest, AnimationTestStreaming) { frames[1] = jxl::test::GetSomeTestImage(xsize, ysize, 3, 1); JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; - jxl::CodecInOut io; + jxl::CodecInOut io{memory_manager}; io.SetSize(xsize, ysize); io.metadata.m.SetUintSamples(16); io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false); @@ -2743,7 +2753,7 @@ TEST(DecodeTest, AnimationTestStreaming) { } for (size_t i = 0; i < num_frames; ++i) { - jxl::ImageBundle bundle(&io.metadata.m); + jxl::ImageBundle bundle(memory_manager, &io.metadata.m); EXPECT_TRUE(ConvertFromExternal( jxl::Bytes(frames[i].data(), frames[i].size()), xsize, ysize, @@ -2931,6 +2941,7 @@ TEST(DecodeTest, ExtraChannelTest) { } TEST(DecodeTest, SkipCurrentFrameTest) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); size_t xsize = 90; size_t ysize = 120; constexpr size_t num_frames = 7; @@ -2940,7 +2951,7 @@ TEST(DecodeTest, SkipCurrentFrameTest) { } JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; - jxl::CodecInOut io; + jxl::CodecInOut io{memory_manager}; io.SetSize(xsize, ysize); io.metadata.m.SetUintSamples(16); io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false); @@ -2955,7 +2966,7 @@ TEST(DecodeTest, SkipCurrentFrameTest) { } for (size_t i = 0; i < num_frames; ++i) { - jxl::ImageBundle bundle(&io.metadata.m); + jxl::ImageBundle bundle(memory_manager, &io.metadata.m); if (i & 1) { // Mark some frames as referenceable, others not. bundle.use_for_next_frame = true; @@ -3043,6 +3054,7 @@ TEST(DecodeTest, SkipCurrentFrameTest) { } TEST(DecodeTest, SkipFrameTest) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); size_t xsize = 90; size_t ysize = 120; constexpr size_t num_frames = 16; @@ -3052,7 +3064,7 @@ TEST(DecodeTest, SkipFrameTest) { } JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; - jxl::CodecInOut io; + jxl::CodecInOut io{memory_manager}; io.SetSize(xsize, ysize); io.metadata.m.SetUintSamples(16); io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false); @@ -3067,7 +3079,7 @@ TEST(DecodeTest, SkipFrameTest) { } for (size_t i = 0; i < num_frames; ++i) { - jxl::ImageBundle bundle(&io.metadata.m); + jxl::ImageBundle bundle(memory_manager, &io.metadata.m); if (i & 1) { // Mark some frames as referenceable, others not. bundle.use_for_next_frame = true; @@ -3180,13 +3192,14 @@ TEST(DecodeTest, SkipFrameTest) { } TEST(DecodeTest, SkipFrameWithBlendingTest) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); size_t xsize = 90; size_t ysize = 120; constexpr size_t num_frames = 16; std::vector frames[num_frames]; JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; - jxl::CodecInOut io; + jxl::CodecInOut io{memory_manager}; io.SetSize(xsize, ysize); io.metadata.m.SetUintSamples(16); io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false); @@ -3204,7 +3217,7 @@ TEST(DecodeTest, SkipFrameWithBlendingTest) { // An internal frame with 0 duration, and use_for_next_frame, this is a // frame that is not rendered and not output by the API, but on which the // rendered frames depend - jxl::ImageBundle bundle_internal(&io.metadata.m); + jxl::ImageBundle bundle_internal(memory_manager, &io.metadata.m); EXPECT_TRUE(ConvertFromExternal( jxl::Bytes(frame_internal.data(), frame_internal.size()), xsize, ysize, jxl::ColorEncoding::SRGB(/*is_gray=*/false), @@ -3219,7 +3232,7 @@ TEST(DecodeTest, SkipFrameWithBlendingTest) { jxl::test::GetSomeTestImage(xsize, ysize, 3, i * 2); // Actual rendered frame frame_durations[i] = 5 + i; - jxl::ImageBundle bundle(&io.metadata.m); + jxl::ImageBundle bundle(memory_manager, &io.metadata.m); EXPECT_TRUE(ConvertFromExternal(jxl::Bytes(frame.data(), frame.size()), xsize, ysize, jxl::ColorEncoding::SRGB(/*is_gray=*/false), @@ -3394,13 +3407,14 @@ TEST(DecodeTest, SkipFrameWithBlendingTest) { } TEST(DecodeTest, SkipFrameWithAlphaBlendingTest) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); size_t xsize = 90; size_t ysize = 120; constexpr size_t num_frames = 16; std::vector frames[num_frames + 5]; JxlPixelFormat format = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; - jxl::CodecInOut io; + jxl::CodecInOut io{memory_manager}; io.SetSize(xsize, ysize); io.metadata.m.SetUintSamples(16); io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false); @@ -3427,7 +3441,7 @@ TEST(DecodeTest, SkipFrameWithAlphaBlendingTest) { // An internal frame with 0 duration, and use_for_next_frame, this is a // frame that is not rendered and not output by default by the API, but on // which the rendered frames depend - jxl::ImageBundle bundle_internal(&io.metadata.m); + jxl::ImageBundle bundle_internal(memory_manager, &io.metadata.m); EXPECT_TRUE(ConvertFromExternal( jxl::Bytes(frame_internal.data(), frame_internal.size()), xsize / 2, ysize / 2, jxl::ColorEncoding::SRGB(/*is_gray=*/false), @@ -3447,7 +3461,7 @@ TEST(DecodeTest, SkipFrameWithAlphaBlendingTest) { std::vector frame = jxl::test::GetSomeTestImage(cropxsize, cropysize, 4, i * 2); // Actual rendered frame - jxl::ImageBundle bundle(&io.metadata.m); + jxl::ImageBundle bundle(memory_manager, &io.metadata.m); EXPECT_TRUE(ConvertFromExternal(jxl::Bytes(frame.data(), frame.size()), cropxsize, cropysize, jxl::ColorEncoding::SRGB(/*is_gray=*/false), @@ -3684,14 +3698,15 @@ TEST(DecodeTest, SkipFrameWithAlphaBlendingTest) { } TEST(DecodeTest, OrientedCroppedFrameTest) { - const auto test = [](bool keep_orientation, uint32_t orientation, - uint32_t resampling) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + const auto test = [&](bool keep_orientation, uint32_t orientation, + uint32_t resampling) { size_t xsize = 90; size_t ysize = 120; JxlPixelFormat format = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; size_t oxsize = (!keep_orientation && orientation > 4 ? ysize : xsize); size_t oysize = (!keep_orientation && orientation > 4 ? xsize : ysize); - jxl::CodecInOut io; + jxl::CodecInOut io{memory_manager}; io.SetSize(xsize, ysize); io.metadata.m.SetUintSamples(16); io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false); @@ -3707,7 +3722,7 @@ TEST(DecodeTest, OrientedCroppedFrameTest) { std::vector frame = jxl::test::GetSomeTestImage(cropxsize, cropysize, 4, i * 2); - jxl::ImageBundle bundle(&io.metadata.m); + jxl::ImageBundle bundle(memory_manager, &io.metadata.m); EXPECT_TRUE(ConvertFromExternal( jxl::Bytes(frame.data(), frame.size()), cropxsize, cropysize, jxl::ColorEncoding::SRGB(/*is_gray=*/false), @@ -3841,6 +3856,7 @@ struct StreamPositions { void AnalyzeCodestream(const std::vector& data, StreamPositions* streampos) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); // Unbox data to codestream and mark where it is broken up by boxes. std::vector codestream; std::vector> breakpoints; @@ -3926,8 +3942,9 @@ void AnalyzeCodestream(const std::vector& data, frame_header.passes.num_passes); std::vector section_offsets; std::vector section_sizes; - ASSERT_TRUE(ReadGroupOffsets(toc_entries, &br, §ion_offsets, - §ion_sizes, &groups_total_size)); + ASSERT_TRUE(ReadGroupOffsets(memory_manager, toc_entries, &br, + §ion_offsets, §ion_sizes, + &groups_total_size)); EXPECT_EQ(br.TotalBitsConsumed() % jxl::kBitsPerByte, 0); size_t sections_start = br.TotalBitsConsumed() / jxl::kBitsPerByte; p.toc_end = add_offset(sections_start); @@ -4778,6 +4795,7 @@ JXL_GTEST_INSTANTIATE_TEST_SUITE_P(DecodeProgressiveTestInstantiation, DecodeProgressiveTest, ::testing::Range(0, 8)); TEST_P(DecodeProgressiveTest, ProgressiveEventTest) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); const int params = GetParam(); bool single_group = ((params & 1) != 0); bool lossless = (((params >> 1) & 1) != 0); @@ -4804,7 +4822,7 @@ TEST_P(DecodeProgressiveTest, ProgressiveEventTest) { jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); JxlPixelFormat format = {num_channels, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; jxl::ColorEncoding color_encoding = jxl::ColorEncoding::SRGB(false); - jxl::CodecInOut io; + jxl::CodecInOut io{memory_manager}; EXPECT_TRUE(jxl::ConvertFromExternal( jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, color_encoding, /*bits_per_sample=*/16, format, @@ -4921,7 +4939,7 @@ TEST_P(DecodeProgressiveTest, ProgressiveEventTest) { jxl::ButteraugliParams ba; std::vector distances(kNumPasses + 1); for (int p = 0;; p = next_pass(p)) { - jxl::CodecInOut io1; + jxl::CodecInOut io1{memory_manager}; EXPECT_TRUE(jxl::ConvertFromExternal( jxl::Bytes(passes[p].data(), passes[p].size()), xsize, ysize, color_encoding, @@ -4995,24 +5013,26 @@ JXL_TRANSCODE_JPEG_TEST(DecodeTest, JPEGReconstructTestCodestream) { } JXL_TRANSCODE_JPEG_TEST(DecodeTest, JPEGReconstructionTest) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); const std::string jpeg_path = "jxl/flower/flower.png.im_q85_420.jpg"; const std::vector orig = jxl::test::ReadTestData(jpeg_path); - jxl::CodecInOut orig_io; + jxl::CodecInOut orig_io{memory_manager}; ASSERT_TRUE(jxl::jpeg::DecodeImageJPG(jxl::Bytes(orig), &orig_io)); orig_io.metadata.m.xyb_encoded = false; - jxl::BitWriter writer; + jxl::BitWriter writer{memory_manager}; ASSERT_TRUE(WriteCodestreamHeaders(&orig_io.metadata, &writer, nullptr)); writer.ZeroPadToByte(); jxl::CompressParams cparams; cparams.color_transform = jxl::ColorTransform::kNone; - ASSERT_TRUE(jxl::EncodeFrame(cparams, jxl::FrameInfo{}, &orig_io.metadata, - orig_io.Main(), *JxlGetDefaultCms(), + ASSERT_TRUE(jxl::EncodeFrame(memory_manager, cparams, jxl::FrameInfo{}, + &orig_io.metadata, orig_io.Main(), + *JxlGetDefaultCms(), /*pool=*/nullptr, &writer, /*aux_out=*/nullptr)); std::vector jpeg_data; - ASSERT_TRUE( - EncodeJPEGData(*orig_io.Main().jpeg_data.get(), &jpeg_data, cparams)); + ASSERT_TRUE(EncodeJPEGData(memory_manager, *orig_io.Main().jpeg_data.get(), + &jpeg_data, cparams)); std::vector container; jxl::Bytes(jxl::kContainerHeader).AppendTo(container); jxl::AppendBoxHeader(jxl::MakeBoxType("jbrd"), jpeg_data.size(), false, @@ -5549,12 +5569,14 @@ JXL_BOXES_TEST(DecodeTest, PartialCodestreamBoxTest) { } TEST(DecodeTest, SpotColorTest) { - jxl::CodecInOut io; + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + jxl::CodecInOut io{memory_manager}; size_t xsize = 55; size_t ysize = 257; io.metadata.m.color_encoding = jxl::ColorEncoding::LinearSRGB(); - JXL_ASSIGN_OR_DIE(Image3F main, Image3F::Create(xsize, ysize)); - JXL_ASSIGN_OR_DIE(ImageF spot, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_DIE(Image3F main, + Image3F::Create(memory_manager, xsize, ysize)); + JXL_ASSIGN_OR_DIE(ImageF spot, ImageF::Create(memory_manager, xsize, ysize)); jxl::ZeroFillImage(&main); jxl::ZeroFillImage(&spot); diff --git a/third_party/jpeg-xl/lib/jxl/enc_ac_strategy.cc b/third_party/jpeg-xl/lib/jxl/enc_ac_strategy.cc index 0be7cdb882ed..702fa23056b5 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_ac_strategy.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_ac_strategy.cc @@ -5,6 +5,8 @@ #include "lib/jxl/enc_ac_strategy.h" +#include + #include #include #include @@ -213,7 +215,9 @@ const uint8_t* TypeMask(const uint8_t& raw_strategy) { Status DumpAcStrategy(const AcStrategyImage& ac_strategy, size_t xsize, size_t ysize, const char* tag, AuxOut* aux_out, const CompressParams& cparams) { - JXL_ASSIGN_OR_RETURN(Image3F color_acs, Image3F::Create(xsize, ysize)); + JxlMemoryManager* memory_manager = ac_strategy.memory_manager(); + JXL_ASSIGN_OR_RETURN(Image3F color_acs, + Image3F::Create(memory_manager, xsize, ysize)); for (size_t y = 0; y < ysize; y++) { float* JXL_RESTRICT rows[3] = { color_acs.PlaneRow(0, y), @@ -451,11 +455,11 @@ float EstimateEntropy(const AcStrategy& acs, float entropy_mul, size_t x, } } static const double kChannelMul[3] = { - 10.2, - 1.0, - 1.03, + pow(10.2, 8.0), + pow(1.0, 8.0), + pow(1.03, 8.0), }; - lossc = Mul(Set(df8, pow(kChannelMul[c], 8.0)), lossc); + lossc = Mul(Set(df8, kChannelMul[c]), lossc); loss = Add(loss, lossc); } entropy += config.cost_delta * GetLane(SumOfLanes(df, entropy_v)); @@ -846,7 +850,7 @@ void ProcessRectACS(const CompressParams& cparams, const ACSConfig& config, float entropy_mul; }; // These numbers need to be figured out manually and looking at - // ringing next to sky etc. Optimization will find larger numbers + // ringing next to sky etc. Optimization will find smaller numbers // and produce more ringing than is ideal. Larger numbers will // help stop ringing. const float entropy_mul16X8 = 1.25; diff --git a/third_party/jpeg-xl/lib/jxl/enc_adaptive_quantization.cc b/third_party/jpeg-xl/lib/jxl/enc_adaptive_quantization.cc index fa0570d9376d..6b169162d580 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_adaptive_quantization.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_adaptive_quantization.cc @@ -5,12 +5,13 @@ #include "lib/jxl/enc_adaptive_quantization.h" -#include -#include +#include #include #include #include +#include +#include #include #include @@ -171,30 +172,28 @@ V GammaModulation(const D d, const size_t x, const size_t y, JXL_DASSERT(kBias > jxl::cms::kOpsinAbsorbanceBias[2]); auto overall_ratio = Zero(d); auto bias = Set(d, kBias); - auto half = Set(d, 0.5f); for (size_t dy = 0; dy < 8; ++dy) { const float* const JXL_RESTRICT row_in_x = rect.ConstRow(xyb_x, y + dy); const float* const JXL_RESTRICT row_in_y = rect.ConstRow(xyb_y, y + dy); for (size_t dx = 0; dx < 8; dx += Lanes(d)) { const auto iny = Add(Load(d, row_in_y + x + dx), bias); const auto inx = Load(d, row_in_x + x + dx); + const auto r = Sub(iny, inx); - const auto g = Add(iny, inx); const auto ratio_r = RatioOfDerivativesOfCubicRootToSimpleGamma(d, r); + overall_ratio = Add(overall_ratio, ratio_r); + + const auto g = Add(iny, inx); const auto ratio_g = RatioOfDerivativesOfCubicRootToSimpleGamma(d, g); - const auto avg_ratio = Mul(half, Add(ratio_r, ratio_g)); - - overall_ratio = Add(overall_ratio, avg_ratio); + overall_ratio = Add(overall_ratio, ratio_g); } } - overall_ratio = Mul(SumOfLanes(d, overall_ratio), Set(d, 1.0f / 64)); + overall_ratio = Mul(SumOfLanes(d, overall_ratio), Set(d, 0.5f / 64)); // ideally -1.0, but likely optimal correction adds some entropy, so slightly // less than that. - // ln(2) constant folded in because we want std::log but have FastLog2f. - static const float v = 0.14507933746197058f; - const auto kGam = Set(d, v * 0.693147180559945f); + const auto kGam = Set(d, 0.1005613337192697f); return MulAdd(kGam, FastLog2f(d, overall_ratio), out_val); } @@ -396,13 +395,15 @@ void FuzzyErosion(const float butteraugli_target, const Rect& from_rect, } struct AdaptiveQuantizationImpl { - Status PrepareBuffers(size_t num_threads) { - JXL_ASSIGN_OR_RETURN(diff_buffer, - ImageF::Create(kEncTileDim + 8, num_threads)); + Status PrepareBuffers(JxlMemoryManager* memory_manager, size_t num_threads) { + JXL_ASSIGN_OR_RETURN( + diff_buffer, + ImageF::Create(memory_manager, kEncTileDim + 8, num_threads)); for (size_t i = pre_erosion.size(); i < num_threads; i++) { - JXL_ASSIGN_OR_RETURN(ImageF tmp, - ImageF::Create(kEncTileDimInBlocks * 2 + 2, - kEncTileDimInBlocks * 2 + 2)); + JXL_ASSIGN_OR_RETURN( + ImageF tmp, + ImageF::Create(memory_manager, kEncTileDimInBlocks * 2 + 2, + kEncTileDimInBlocks * 2 + 2)); pre_erosion.emplace_back(std::move(tmp)); } return true; @@ -572,7 +573,8 @@ struct AdaptiveQuantizationImpl { ImageF diff_buffer; }; -Status Blur1x1Masking(ThreadPool* pool, ImageF* mask1x1, const Rect& rect) { +Status Blur1x1Masking(JxlMemoryManager* memory_manager, ThreadPool* pool, + ImageF* mask1x1, const Rect& rect) { // Blur the mask1x1 to obtain the masking image. // Before blurring it contains an image of absolute value of the // Laplacian of the intensity channel. @@ -598,7 +600,8 @@ Status Blur1x1Masking(ThreadPool* pool, ImageF* mask1x1, const Rect& rect) { {HWY_REP4(normalize_mul * kFilterMask1x1[1])}, {HWY_REP4(normalize_mul * kFilterMask1x1[4])}, {HWY_REP4(normalize_mul * kFilterMask1x1[3])}}; - JXL_ASSIGN_OR_RETURN(ImageF temp, ImageF::Create(rect.xsize(), rect.ysize())); + JXL_ASSIGN_OR_RETURN( + ImageF temp, ImageF::Create(memory_manager, rect.xsize(), rect.ysize())); Symmetric5(*mask1x1, rect, weights, pool, &temp); *mask1x1 = std::move(temp); return true; @@ -613,15 +616,19 @@ StatusOr AdaptiveQuantizationMap(const float butteraugli_target, AdaptiveQuantizationImpl impl; const size_t xsize_blocks = rect.xsize() / kBlockDim; const size_t ysize_blocks = rect.ysize() / kBlockDim; - JXL_ASSIGN_OR_RETURN(impl.aq_map, ImageF::Create(xsize_blocks, ysize_blocks)); - JXL_ASSIGN_OR_RETURN(*mask, ImageF::Create(xsize_blocks, ysize_blocks)); - JXL_ASSIGN_OR_RETURN(*mask1x1, ImageF::Create(xyb.xsize(), xyb.ysize())); + JxlMemoryManager* memory_manager = xyb.memory_manager(); + JXL_ASSIGN_OR_RETURN( + impl.aq_map, ImageF::Create(memory_manager, xsize_blocks, ysize_blocks)); + JXL_ASSIGN_OR_RETURN( + *mask, ImageF::Create(memory_manager, xsize_blocks, ysize_blocks)); + JXL_ASSIGN_OR_RETURN( + *mask1x1, ImageF::Create(memory_manager, xyb.xsize(), xyb.ysize())); JXL_CHECK(RunOnPool( pool, 0, DivCeil(xsize_blocks, kEncTileDimInBlocks) * DivCeil(ysize_blocks, kEncTileDimInBlocks), [&](const size_t num_threads) { - return !!impl.PrepareBuffers(num_threads); + return !!impl.PrepareBuffers(memory_manager, num_threads); }, [&](const uint32_t tid, const size_t thread) { size_t n_enc_tiles = DivCeil(xsize_blocks, kEncTileDimInBlocks); @@ -637,7 +644,7 @@ StatusOr AdaptiveQuantizationMap(const float butteraugli_target, }, "AQ DiffPrecompute")); - JXL_RETURN_IF_ERROR(Blur1x1Masking(pool, mask1x1, rect)); + JXL_RETURN_IF_ERROR(Blur1x1Masking(memory_manager, pool, mask1x1, rect)); return std::move(impl).aq_map; } @@ -675,10 +682,12 @@ Status DumpHeatmap(const CompressParams& cparams, const AuxOut* aux_out, Status DumpHeatmaps(const CompressParams& cparams, const AuxOut* aux_out, float ba_target, const ImageF& quant_field, const ImageF& tile_heatmap, const ImageF& bt_diffmap) { + JxlMemoryManager* memory_manager = quant_field.memory_manager(); if (JXL_DEBUG_ADAPTIVE_QUANTIZATION) { if (!WantDebugOutput(cparams)) return true; - JXL_ASSIGN_OR_RETURN(ImageF inv_qmap, ImageF::Create(quant_field.xsize(), - quant_field.ysize())); + JXL_ASSIGN_OR_RETURN(ImageF inv_qmap, + ImageF::Create(memory_manager, quant_field.xsize(), + quant_field.ysize())); for (size_t y = 0; y < quant_field.ysize(); ++y) { const float* JXL_RESTRICT row_q = quant_field.ConstRow(y); float* JXL_RESTRICT row_inv_q = inv_qmap.Row(y); @@ -702,8 +711,9 @@ StatusOr TileDistMap(const ImageF& distmap, int tile_size, int margin, const AcStrategyImage& ac_strategy) { const int tile_xsize = (distmap.xsize() + tile_size - 1) / tile_size; const int tile_ysize = (distmap.ysize() + tile_size - 1) / tile_size; + JxlMemoryManager* memory_manager = distmap.memory_manager(); JXL_ASSIGN_OR_RETURN(ImageF tile_distmap, - ImageF::Create(tile_xsize, tile_ysize)); + ImageF::Create(memory_manager, tile_xsize, tile_ysize)); size_t distmap_stride = tile_distmap.PixelsPerRow(); for (int tile_y = 0; tile_y < tile_ysize; ++tile_y) { AcStrategyRow ac_strategy_row = ac_strategy.ConstRow(tile_y); @@ -774,8 +784,9 @@ StatusOr RoundtripImage(const FrameHeader& frame_header, PassesEncoderState* enc_state, const JxlCmsInterface& cms, ThreadPool* pool) { + JxlMemoryManager* memory_manager = enc_state->memory_manager(); std::unique_ptr dec_state = - jxl::make_unique(); + jxl::make_unique(memory_manager); JXL_CHECK(dec_state->output_encoding_info.SetFromMetadata( *enc_state->shared.metadata)); dec_state->shared = &enc_state->shared; @@ -787,18 +798,19 @@ StatusOr RoundtripImage(const FrameHeader& frame_header, size_t num_special_frames = enc_state->special_frames.size(); size_t num_passes = enc_state->progressive_splitter.GetNumPasses(); - ModularFrameEncoder modular_frame_encoder(frame_header, enc_state->cparams, - false); + ModularFrameEncoder modular_frame_encoder(memory_manager, frame_header, + enc_state->cparams, false); JXL_CHECK(InitializePassesEncoder(frame_header, opsin, Rect(opsin), cms, pool, enc_state, &modular_frame_encoder, nullptr)); JXL_CHECK(dec_state->Init(frame_header)); JXL_CHECK(dec_state->InitForAC(num_passes, pool)); - ImageBundle decoded(&enc_state->shared.metadata->m); + ImageBundle decoded(memory_manager, &enc_state->shared.metadata->m); decoded.origin = frame_header.frame_origin; - JXL_ASSIGN_OR_RETURN(Image3F tmp, - Image3F::Create(opsin.xsize(), opsin.ysize())); + JXL_ASSIGN_OR_RETURN( + Image3F tmp, + Image3F::Create(memory_manager, opsin.xsize(), opsin.ysize())); decoded.SetFromImage(std::move(tmp), dec_state->output_encoding_info.color_encoding); @@ -811,7 +823,8 @@ StatusOr RoundtripImage(const FrameHeader& frame_header, // Same as frame_header.nonserialized_metadata->m const ImageMetadata& metadata = *decoded.metadata(); - JXL_CHECK(dec_state->PreparePipeline(frame_header, &decoded, options)); + JXL_CHECK(dec_state->PreparePipeline( + frame_header, &enc_state->shared.metadata->m, &decoded, options)); hwy::AlignedUniquePtr group_dec_caches; const auto allocate_storage = [&](const size_t num_threads) -> Status { @@ -834,7 +847,7 @@ StatusOr RoundtripImage(const FrameHeader& frame_header, dec_state->render_pipeline->GetInputBuffers(group_index, thread); JXL_CHECK(DecodeGroupForRoundtrip( frame_header, enc_state->coeffs, group_index, dec_state.get(), - &group_dec_caches[thread], thread, input, &decoded, nullptr)); + &group_dec_caches[thread], thread, input, nullptr, nullptr)); for (size_t c = 0; c < metadata.num_extra_channels; c++) { std::pair ri = input.GetBuffer(3 + c); FillPlane(0.0f, ri.first, ri.second); @@ -869,6 +882,7 @@ Status FindBestQuantization(const FrameHeader& frame_header, // so in this case we enable it only for very high butteraugli targets. return true; } + JxlMemoryManager* memory_manager = enc_state->memory_manager(); Quantizer& quantizer = enc_state->shared.quantizer; ImageI& raw_quant_field = enc_state->shared.raw_quant_field; @@ -886,7 +900,7 @@ Status FindBestQuantization(const FrameHeader& frame_header, ImageF tile_distmap; JXL_ASSIGN_OR_RETURN( ImageF initial_quant_field, - ImageF::Create(quant_field.xsize(), quant_field.ysize())); + ImageF::Create(memory_manager, quant_field.xsize(), quant_field.ysize())); CopyImageTo(quant_field, &initial_quant_field); float initial_qf_min; diff --git a/third_party/jpeg-xl/lib/jxl/enc_ans.cc b/third_party/jpeg-xl/lib/jxl/enc_ans.cc index 5e59790b1ebf..be2668ac3afc 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_ans.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_ans.cc @@ -5,16 +5,14 @@ #include "lib/jxl/enc_ans.h" +#include #include -#include #include #include #include #include #include -#include -#include #include #include #include @@ -439,6 +437,7 @@ uint32_t ComputeBestMethod( // Returns an estimate of the cost of encoding this histogram and the // corresponding data. size_t BuildAndStoreANSEncodingData( + JxlMemoryManager* memory_manager, HistogramParams::ANSHistogramStrategy ans_histogram_strategy, const ANSHistBin* histogram, size_t alphabet_size, size_t log_alpha_size, bool use_prefix_code, ANSEncSymbolInfo* info, BitWriter* writer) { @@ -454,7 +453,7 @@ size_t BuildAndStoreANSEncodingData( std::vector depths(alphabet_size); std::vector bits(alphabet_size); if (writer == nullptr) { - BitWriter tmp_writer; + BitWriter tmp_writer{memory_manager}; BitWriter::Allotment allotment( &tmp_writer, 8 * alphabet_size + 8); // safe upper bound BuildAndStoreHuffmanTree(histo.data(), alphabet_size, depths.data(), @@ -715,7 +714,7 @@ class HistogramBuilder { // NOTE: `layer` is only for clustered_entropy; caller does ReclaimAndCharge. size_t BuildAndStoreEntropyCodes( - const HistogramParams& params, + JxlMemoryManager* memory_manager, const HistogramParams& params, const std::vector>& tokens, EntropyEncodingData* codes, std::vector* context_map, BitWriter* writer, size_t layer, AuxOut* aux_out) const { @@ -810,14 +809,15 @@ class HistogramBuilder { codes->encoding_info.back().resize(alphabet_size); BitWriter* histo_writer = writer; if (params.streaming_mode) { - codes->encoded_histograms.emplace_back(); + codes->encoded_histograms.emplace_back(memory_manager); histo_writer = &codes->encoded_histograms.back(); } BitWriter::Allotment allotment(histo_writer, 256 + alphabet_size * 24); cost += BuildAndStoreANSEncodingData( - params.ans_histogram_strategy, clustered_histograms[c].data_.data(), - alphabet_size, log_alpha_size, codes->use_prefix_code, - codes->encoding_info.back().data(), histo_writer); + memory_manager, params.ans_histogram_strategy, + clustered_histograms[c].data_.data(), alphabet_size, log_alpha_size, + codes->use_prefix_code, codes->encoding_info.back().data(), + histo_writer); allotment.FinishedHistogram(histo_writer); allotment.ReclaimAndCharge(histo_writer, layer, aux_out); if (params.streaming_mode) { @@ -1535,13 +1535,11 @@ void EncodeHistograms(const std::vector& context_map, allotment.ReclaimAndCharge(writer, layer, aux_out); } -size_t BuildAndEncodeHistograms(const HistogramParams& params, - size_t num_contexts, - std::vector>& tokens, - EntropyEncodingData* codes, - std::vector* context_map, - BitWriter* writer, size_t layer, - AuxOut* aux_out) { +size_t BuildAndEncodeHistograms( + JxlMemoryManager* memory_manager, const HistogramParams& params, + size_t num_contexts, std::vector>& tokens, + EntropyEncodingData* codes, std::vector* context_map, + BitWriter* writer, size_t layer, AuxOut* aux_out) { size_t total_bits = 0; codes->lz77.nonserialized_distance_context = num_contexts; std::vector> tokens_lz77; @@ -1655,19 +1653,20 @@ size_t BuildAndEncodeHistograms(const HistogramParams& params, CreateFlatHistogram(alphabet_size, ANS_TAB_SIZE); codes->encoding_info.emplace_back(); codes->encoding_info.back().resize(alphabet_size); - codes->encoded_histograms.emplace_back(); + codes->encoded_histograms.emplace_back(memory_manager); BitWriter* histo_writer = &codes->encoded_histograms.back(); BitWriter::Allotment allotment(histo_writer, 256 + alphabet_size * 24); BuildAndStoreANSEncodingData( - params.ans_histogram_strategy, counts.data(), alphabet_size, - log_alpha_size, codes->use_prefix_code, + memory_manager, params.ans_histogram_strategy, counts.data(), + alphabet_size, log_alpha_size, codes->use_prefix_code, codes->encoding_info.back().data(), histo_writer); allotment.ReclaimAndCharge(histo_writer, 0, nullptr); } // Encode histograms. - total_bits += builder.BuildAndStoreEntropyCodes( - params, tokens, codes, context_map, writer, layer, aux_out); + total_bits += + builder.BuildAndStoreEntropyCodes(memory_manager, params, tokens, codes, + context_map, writer, layer, aux_out); allotment.FinishedHistogram(writer); allotment.ReclaimAndCharge(writer, layer, aux_out); diff --git a/third_party/jpeg-xl/lib/jxl/enc_ans.h b/third_party/jpeg-xl/lib/jxl/enc_ans.h index ae4d955a563b..50df1c4f103d 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_ans.h +++ b/third_party/jpeg-xl/lib/jxl/enc_ans.h @@ -9,6 +9,8 @@ // Library to encode the ANS population counts to the bit-stream and encode // symbols based on the respective distributions. +#include + #include #include #include @@ -106,13 +108,11 @@ void EncodeHistograms(const std::vector& context_map, // estimate of the total bits used for encoding the stream. If `writer` == // nullptr, the bit estimate will not take into account the context map (which // does not get written if `num_contexts` == 1). -size_t BuildAndEncodeHistograms(const HistogramParams& params, - size_t num_contexts, - std::vector>& tokens, - EntropyEncodingData* codes, - std::vector* context_map, - BitWriter* writer, size_t layer, - AuxOut* aux_out); +size_t BuildAndEncodeHistograms( + JxlMemoryManager* memory_manager, const HistogramParams& params, + size_t num_contexts, std::vector>& tokens, + EntropyEncodingData* codes, std::vector* context_map, + BitWriter* writer, size_t layer, AuxOut* aux_out); // Write the tokens to a string. void WriteTokens(const std::vector& tokens, diff --git a/third_party/jpeg-xl/lib/jxl/enc_ar_control_field.cc b/third_party/jpeg-xl/lib/jxl/enc_ar_control_field.cc deleted file mode 100644 index c323dc68bbcd..000000000000 --- a/third_party/jpeg-xl/lib/jxl/enc_ar_control_field.cc +++ /dev/null @@ -1,318 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "lib/jxl/enc_ar_control_field.h" - -#include -#include -#include - -#undef HWY_TARGET_INCLUDE -#define HWY_TARGET_INCLUDE "lib/jxl/enc_ar_control_field.cc" -#include -#include - -#include "lib/jxl/ac_strategy.h" -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/base/rect.h" -#include "lib/jxl/base/status.h" -#include "lib/jxl/enc_params.h" -#include "lib/jxl/image.h" -#include "lib/jxl/image_ops.h" - -HWY_BEFORE_NAMESPACE(); -namespace jxl { -namespace HWY_NAMESPACE { -namespace { - -// These templates are not found via ADL. -using hwy::HWY_NAMESPACE::Add; -using hwy::HWY_NAMESPACE::GetLane; -using hwy::HWY_NAMESPACE::Mul; -using hwy::HWY_NAMESPACE::MulAdd; -using hwy::HWY_NAMESPACE::Sqrt; - -Status ProcessTile(const CompressParams& cparams, - const FrameHeader& frame_header, const Image3F& opsin, - const Rect& opsin_rect, const ImageF& quant_field, - const AcStrategyImage& ac_strategy, ImageB* epf_sharpness, - const Rect& rect, - ArControlFieldHeuristics::TempImages* temp_image) { - JXL_ASSERT(opsin_rect.x0() % 8 == 0); - JXL_ASSERT(opsin_rect.y0() % 8 == 0); - JXL_ASSERT(opsin_rect.xsize() % 8 == 0); - JXL_ASSERT(opsin_rect.ysize() % 8 == 0); - constexpr size_t N = kBlockDim; - if (cparams.butteraugli_distance < kMinButteraugliForDynamicAR || - cparams.speed_tier > SpeedTier::kWombat || - frame_header.loop_filter.epf_iters == 0) { - FillPlane(static_cast(4), epf_sharpness, rect); - return true; - } - - // Likely better to have a higher X weight, like: - // const float kChannelWeights[3] = {47.0f, 4.35f, 0.287f}; - const float kChannelWeights[3] = {4.35f, 4.35f, 0.287f}; - const float kChannelWeightsLapNeg[3] = {-0.125f * kChannelWeights[0], - -0.125f * kChannelWeights[1], - -0.125f * kChannelWeights[2]}; - const size_t sharpness_stride = - static_cast(epf_sharpness->PixelsPerRow()); - - size_t by0 = opsin_rect.y0() / 8 + rect.y0(); - size_t by1 = by0 + rect.ysize(); - size_t bx0 = opsin_rect.x0() / 8 + rect.x0(); - size_t bx1 = bx0 + rect.xsize(); - JXL_RETURN_IF_ERROR(temp_image->InitOnce()); - ImageF& laplacian_sqrsum = temp_image->laplacian_sqrsum; - // Calculate the L2 of the 3x3 Laplacian in an integral transform - // (for example 32x32 dct). This relates to transforms ability - // to propagate artefacts. - size_t y0 = by0 == 0 ? 0 : by0 * N - 2; - size_t y1 = by1 * N == opsin.ysize() ? by1 * N : by1 * N + 2; - size_t x0 = bx0 == 0 ? 0 : bx0 * N - 2; - size_t x1 = bx1 * N == opsin.xsize() ? bx1 * N : bx1 * N + 2; - HWY_FULL(float) df; - for (size_t y = y0; y < y1; y++) { - float* JXL_RESTRICT laplacian_sqrsum_row = - laplacian_sqrsum.Row(y + 2 - by0 * N); - const float* JXL_RESTRICT in_row_t[3]; - const float* JXL_RESTRICT in_row[3]; - const float* JXL_RESTRICT in_row_b[3]; - for (size_t c = 0; c < 3; c++) { - in_row_t[c] = opsin.ConstPlaneRow(c, y > 0 ? y - 1 : y); - in_row[c] = opsin.ConstPlaneRow(c, y); - in_row_b[c] = opsin.ConstPlaneRow(c, y + 1 < opsin.ysize() ? y + 1 : y); - } - auto compute_laplacian_scalar = [&](size_t x) { - const size_t prevX = x >= 1 ? x - 1 : x; - const size_t nextX = x + 1 < opsin.xsize() ? x + 1 : x; - float sumsqr = 0; - for (size_t c = 0; c < 3; c++) { - float laplacian = - kChannelWeights[c] * in_row[c][x] + - kChannelWeightsLapNeg[c] * - (in_row[c][prevX] + in_row[c][nextX] + in_row_b[c][prevX] + - in_row_b[c][x] + in_row_b[c][nextX] + in_row_t[c][prevX] + - in_row_t[c][x] + in_row_t[c][nextX]); - sumsqr += laplacian * laplacian; - } - laplacian_sqrsum_row[x + 2 - bx0 * N] = sumsqr; - }; - size_t x = x0; - for (; x < 1; x++) { - compute_laplacian_scalar(x); - } - // Interior. One extra pixel of border as the last pixel is special. - for (; x + Lanes(df) <= x1 && x + Lanes(df) + 1 <= opsin.xsize(); - x += Lanes(df)) { - auto sumsqr = Zero(df); - for (size_t c = 0; c < 3; c++) { - auto laplacian = - Mul(LoadU(df, in_row[c] + x), Set(df, kChannelWeights[c])); - auto sum_oth0 = LoadU(df, in_row[c] + x - 1); - auto sum_oth1 = LoadU(df, in_row[c] + x + 1); - auto sum_oth2 = LoadU(df, in_row_t[c] + x - 1); - auto sum_oth3 = LoadU(df, in_row_t[c] + x); - sum_oth0 = Add(sum_oth0, LoadU(df, in_row_t[c] + x + 1)); - sum_oth1 = Add(sum_oth1, LoadU(df, in_row_b[c] + x - 1)); - sum_oth2 = Add(sum_oth2, LoadU(df, in_row_b[c] + x)); - sum_oth3 = Add(sum_oth3, LoadU(df, in_row_b[c] + x + 1)); - sum_oth0 = Add(sum_oth0, sum_oth1); - sum_oth2 = Add(sum_oth2, sum_oth3); - sum_oth0 = Add(sum_oth0, sum_oth2); - laplacian = - MulAdd(Set(df, kChannelWeightsLapNeg[c]), sum_oth0, laplacian); - sumsqr = MulAdd(laplacian, laplacian, sumsqr); - } - StoreU(sumsqr, df, laplacian_sqrsum_row + x + 2 - bx0 * N); - } - for (; x < x1; x++) { - compute_laplacian_scalar(x); - } - } - HWY_CAPPED(float, 4) df4; - // Calculate the L2 of the 3x3 Laplacian in 4x4 blocks within the area - // of the integral transform. Sample them within the integral transform - // with two offsets (0,0) and (-2, -2) pixels (sqrsum_00 and sqrsum_22, - // respectively). - ImageF& sqrsum_00 = temp_image->sqrsum_00; - size_t sqrsum_00_stride = sqrsum_00.PixelsPerRow(); - float* JXL_RESTRICT sqrsum_00_row = sqrsum_00.Row(0); - for (size_t y = 0; y < rect.ysize() * 2; y++) { - const float* JXL_RESTRICT rows_in[4]; - for (size_t iy = 0; iy < 4; iy++) { - rows_in[iy] = laplacian_sqrsum.ConstRow(y * 4 + iy + 2); - } - float* JXL_RESTRICT row_out = sqrsum_00_row + y * sqrsum_00_stride; - for (size_t x = 0; x < rect.xsize() * 2; x++) { - auto sum = Zero(df4); - for (auto& row : rows_in) { - for (size_t ix = 0; ix < 4; ix += Lanes(df4)) { - sum = Add(sum, LoadU(df4, row + x * 4 + ix + 2)); - } - } - row_out[x] = GetLane(Sqrt(SumOfLanes(df4, sum))) * (1.0f / 4.0f); - } - } - // Indexing iy and ix is a bit tricky as we include a 2 pixel border - // around the block for evenness calculations. This is similar to what - // we did in guetzli for the observability of artefacts, except there - // the element is a sliding 5x5, not sparsely sampled 4x4 box like here. - ImageF& sqrsum_22 = temp_image->sqrsum_22; - size_t sqrsum_22_stride = sqrsum_22.PixelsPerRow(); - float* JXL_RESTRICT sqrsum_22_row = sqrsum_22.Row(0); - for (size_t y = 0; y < rect.ysize() * 2 + 1; y++) { - const float* JXL_RESTRICT rows_in[4]; - for (size_t iy = 0; iy < 4; iy++) { - rows_in[iy] = laplacian_sqrsum.ConstRow(y * 4 + iy); - } - float* JXL_RESTRICT row_out = sqrsum_22_row + y * sqrsum_22_stride; - // ignore pixels outside the image. - // Y coordinates are relative to by0*8+y*4. - size_t sy = y * 4 + by0 * 8 > 0 ? 0 : 2; - size_t ey = y * 4 + by0 * 8 + 2 <= opsin.ysize() - ? 4 - : opsin.ysize() - y * 4 - by0 * 8 + 2; - for (size_t x = 0; x < rect.xsize() * 2 + 1; x++) { - // ignore pixels outside the image. - // X coordinates are relative to bx0*8. - size_t sx = x * 4 + bx0 * 8 > 0 ? x * 4 : x * 4 + 2; - size_t ex = x * 4 + bx0 * 8 + 2 <= opsin.xsize() - ? x * 4 + 4 - : opsin.xsize() - bx0 * 8 + 2; - if (ex - sx == 4 && ey - sy == 4) { - auto sum = Zero(df4); - for (size_t iy = sy; iy < ey; iy++) { - for (size_t ix = sx; ix < ex; ix += Lanes(df4)) { - sum = Add(sum, Load(df4, rows_in[iy] + ix)); - } - } - row_out[x] = GetLane(Sqrt(SumOfLanes(df4, sum))) * (1.0f / 4.0f); - } else { - float sum = 0; - for (size_t iy = sy; iy < ey; iy++) { - for (size_t ix = sx; ix < ex; ix++) { - sum += rows_in[iy][ix]; - } - } - row_out[x] = std::sqrt(sum / ((ex - sx) * (ey - sy))); - } - } - } - for (size_t by = rect.y0(); by < rect.y1(); by++) { - AcStrategyRow acs_row = ac_strategy.ConstRow(by); - uint8_t* JXL_RESTRICT out_row = epf_sharpness->Row(by); - const float* JXL_RESTRICT quant_row = quant_field.Row(by); - for (size_t bx = rect.x0(); bx < rect.x1(); bx++) { - AcStrategy acs = acs_row[bx]; - if (!acs.IsFirstBlock()) continue; - // The errors are going to be linear to the quantization value in this - // locality. We only have access to the initial quant field here. - float quant_val = 1.0f / quant_row[bx]; - - const auto sq00 = [&](size_t y, size_t x) { - return sqrsum_00_row[((by - rect.y0()) * 2 + y) * sqrsum_00_stride + - (bx - rect.x0()) * 2 + x]; - }; - const auto sq22 = [&](size_t y, size_t x) { - return sqrsum_22_row[((by - rect.y0()) * 2 + y) * sqrsum_22_stride + - (bx - rect.x0()) * 2 + x]; - }; - float sqrsum_integral_transform = 0; - for (size_t iy = 0; iy < acs.covered_blocks_y() * 2; iy++) { - for (size_t ix = 0; ix < acs.covered_blocks_x() * 2; ix++) { - sqrsum_integral_transform += sq00(iy, ix) * sq00(iy, ix); - } - } - sqrsum_integral_transform /= - 4 * acs.covered_blocks_x() * acs.covered_blocks_y(); - sqrsum_integral_transform = std::sqrt(sqrsum_integral_transform); - // If masking is high or amplitude of the artefacts is low, then no - // smoothing is needed. - for (size_t iy = 0; iy < acs.covered_blocks_y(); iy++) { - for (size_t ix = 0; ix < acs.covered_blocks_x(); ix++) { - // Five 4x4 blocks for masking estimation, all within the - // 8x8 area. - float minval_1 = std::min(sq00(2 * iy + 0, 2 * ix + 0), - sq00(2 * iy + 0, 2 * ix + 1)); - float minval_2 = std::min(sq00(2 * iy + 1, 2 * ix + 0), - sq00(2 * iy + 1, 2 * ix + 1)); - float minval = std::min(minval_1, minval_2); - minval = std::min(minval, sq22(2 * iy + 1, 2 * ix + 1)); - // Nine more 4x4 blocks for masking estimation, includes - // the 2 pixel area around the 8x8 block being controlled. - float minval2_1 = std::min(sq22(2 * iy + 0, 2 * ix + 0), - sq22(2 * iy + 0, 2 * ix + 1)); - float minval2_2 = std::min(sq22(2 * iy + 0, 2 * ix + 2), - sq22(2 * iy + 1, 2 * ix + 0)); - float minval2_3 = std::min(sq22(2 * iy + 1, 2 * ix + 1), - sq22(2 * iy + 1, 2 * ix + 2)); - float minval2_4 = std::min(sq22(2 * iy + 2, 2 * ix + 0), - sq22(2 * iy + 2, 2 * ix + 1)); - float minval2_5 = std::min(minval2_1, minval2_2); - float minval2_6 = std::min(minval2_3, minval2_4); - float minval2 = std::min(minval2_5, minval2_6); - minval2 = std::min(minval2, sq22(2 * iy + 2, 2 * ix + 2)); - float minval3 = std::min(minval, minval2); - minval *= 0.125f; - minval += 0.625f * minval3; - minval += - 0.125f * std::min(1.5f * minval3, sq22(2 * iy + 1, 2 * ix + 1)); - minval += 0.125f * minval2; - // Larger kBias, less smoothing for low intensity changes. - float kDeltaLimit = 3.2; - float bias = 0.0625f * quant_val; - float delta = - (sqrsum_integral_transform + (kDeltaLimit + 0.05) * bias) / - (minval + bias); - int out = 4; - if (delta > kDeltaLimit) { - out = 4; // smooth - } else { - out = 0; - } - // 'threshold' is separate from 'bias' for easier tuning of these - // heuristics. - float threshold = 0.0625f * quant_val; - const float kSmoothLimit = 0.085f; - float smooth = 0.20f * (sq00(2 * iy + 0, 2 * ix + 0) + - sq00(2 * iy + 0, 2 * ix + 1) + - sq00(2 * iy + 1, 2 * ix + 0) + - sq00(2 * iy + 1, 2 * ix + 1) + minval); - if (smooth < kSmoothLimit * threshold) { - out = 4; - } - out_row[bx + sharpness_stride * iy + ix] = out; - } - } - } - } - return true; -} - -} // namespace -// NOLINTNEXTLINE(google-readability-namespace-comments) -} // namespace HWY_NAMESPACE -} // namespace jxl -HWY_AFTER_NAMESPACE(); - -#if HWY_ONCE -namespace jxl { -HWY_EXPORT(ProcessTile); - -Status ArControlFieldHeuristics::RunRect( - const CompressParams& cparams, const FrameHeader& frame_header, - const Rect& block_rect, const Image3F& opsin, const Rect& opsin_rect, - const ImageF& quant_field, const AcStrategyImage& ac_strategy, - ImageB* epf_sharpness, size_t thread) { - return HWY_DYNAMIC_DISPATCH(ProcessTile)( - cparams, frame_header, opsin, opsin_rect, quant_field, ac_strategy, - epf_sharpness, block_rect, &temp_images[thread]); -} - -} // namespace jxl - -#endif diff --git a/third_party/jpeg-xl/lib/jxl/enc_ar_control_field.h b/third_party/jpeg-xl/lib/jxl/enc_ar_control_field.h deleted file mode 100644 index fef5a51d05cf..000000000000 --- a/third_party/jpeg-xl/lib/jxl/enc_ar_control_field.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) the JPEG XL Project Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#ifndef LIB_JXL_ENC_AR_CONTROL_FIELD_H_ -#define LIB_JXL_ENC_AR_CONTROL_FIELD_H_ - -#include -#include - -#include "lib/jxl/ac_strategy.h" -#include "lib/jxl/base/rect.h" -#include "lib/jxl/base/status.h" -#include "lib/jxl/enc_params.h" -#include "lib/jxl/frame_header.h" -#include "lib/jxl/image.h" - -namespace jxl { - -struct PassesEncoderState; - -struct ArControlFieldHeuristics { - struct TempImages { - Status InitOnce() { - if (laplacian_sqrsum.xsize() != 0) return true; - JXL_ASSIGN_OR_RETURN(laplacian_sqrsum, - ImageF::Create(kEncTileDim + 4, kEncTileDim + 4)); - JXL_ASSIGN_OR_RETURN(sqrsum_00, - ImageF::Create(kEncTileDim / 4, kEncTileDim / 4)); - JXL_ASSIGN_OR_RETURN( - sqrsum_22, ImageF::Create(kEncTileDim / 4 + 1, kEncTileDim / 4 + 1)); - return true; - } - - ImageF laplacian_sqrsum; - ImageF sqrsum_00; - ImageF sqrsum_22; - }; - - void PrepareForThreads(size_t num_threads) { - temp_images.resize(num_threads); - } - - Status RunRect(const CompressParams& cparams, const FrameHeader& frame_header, - const Rect& block_rect, const Image3F& opsin, - const Rect& opsin_rect, const ImageF& quant_field, - const AcStrategyImage& ac_strategy, ImageB* epf_sharpness, - size_t thread); - - std::vector temp_images; -}; - -} // namespace jxl - -#endif // LIB_JXL_AR_ENC_CONTROL_FIELD_H_ diff --git a/third_party/jpeg-xl/lib/jxl/enc_bit_writer.cc b/third_party/jpeg-xl/lib/jxl/enc_bit_writer.cc index 6e6e6a353c35..8c5379642901 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_bit_writer.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_bit_writer.cc @@ -6,11 +6,11 @@ #include "lib/jxl/enc_bit_writer.h" #include -#include // memcpy + +#include // memcpy #include "lib/jxl/base/byte_order.h" #include "lib/jxl/base/printf_macros.h" -#include "lib/jxl/dec_bit_reader.h" #include "lib/jxl/enc_aux_out.h" namespace jxl { diff --git a/third_party/jpeg-xl/lib/jxl/enc_bit_writer.h b/third_party/jpeg-xl/lib/jxl/enc_bit_writer.h index 6f4865077d2c..019be801622c 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_bit_writer.h +++ b/third_party/jpeg-xl/lib/jxl/enc_bit_writer.h @@ -8,9 +8,10 @@ // BitWriter class: unbuffered writes using unaligned 64-bit stores. -#include -#include +#include +#include +#include #include #include @@ -32,7 +33,8 @@ struct BitWriter { // yet zero-initialized). static constexpr size_t kMaxBitsPerCall = 56; - BitWriter() : bits_written_(0) {} + explicit BitWriter(JxlMemoryManager* memory_manager) + : bits_written_(0), storage_(memory_manager) {} // Disallow copying - may lead to bugs. BitWriter(const BitWriter&) = delete; @@ -42,6 +44,8 @@ struct BitWriter { size_t BitsWritten() const { return bits_written_; } + JxlMemoryManager* memory_manager() const { return storage_.memory_manager(); } + Span GetSpan() const { // Callers must ensure byte alignment to avoid uninitialized bits. JXL_ASSERT(bits_written_ % kBitsPerByte == 0); diff --git a/third_party/jpeg-xl/lib/jxl/enc_butteraugli_comparator.cc b/third_party/jpeg-xl/lib/jxl/enc_butteraugli_comparator.cc index b20fa751c186..55517cadaec6 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_butteraugli_comparator.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_butteraugli_comparator.cc @@ -16,8 +16,9 @@ JxlButteraugliComparator::JxlButteraugliComparator( Status JxlButteraugliComparator::SetReferenceImage(const ImageBundle& ref) { const ImageBundle* ref_linear_srgb; + JxlMemoryManager* memory_manager = ref.memory_manager(); ImageMetadata metadata = *ref.metadata(); - ImageBundle store(&metadata); + ImageBundle store(memory_manager, &metadata); if (!TransformIfNeeded(ref, ColorEncoding::LinearSRGB(ref.IsGray()), cms_, /*pool=*/nullptr, &store, &ref_linear_srgb)) { return false; @@ -46,17 +47,19 @@ Status JxlButteraugliComparator::CompareWith(const ImageBundle& actual, if (xsize_ != actual.xsize() || ysize_ != actual.ysize()) { return JXL_FAILURE("Images must have same size"); } + JxlMemoryManager* memory_manager = actual.memory_manager(); const ImageBundle* actual_linear_srgb; ImageMetadata metadata = *actual.metadata(); - ImageBundle store(&metadata); + ImageBundle store(memory_manager, &metadata); if (!TransformIfNeeded(actual, ColorEncoding::LinearSRGB(actual.IsGray()), cms_, /*pool=*/nullptr, &store, &actual_linear_srgb)) { return false; } - JXL_ASSIGN_OR_RETURN(ImageF temp_diffmap, ImageF::Create(xsize_, ysize_)); + JXL_ASSIGN_OR_RETURN(ImageF temp_diffmap, + ImageF::Create(memory_manager, xsize_, ysize_)); JXL_RETURN_IF_ERROR( comparator_->Diffmap(actual_linear_srgb->color(), temp_diffmap)); diff --git a/third_party/jpeg-xl/lib/jxl/enc_cache.cc b/third_party/jpeg-xl/lib/jxl/enc_cache.cc index 91d0046c299c..b339b6dcb580 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_cache.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_cache.cc @@ -33,6 +33,31 @@ namespace jxl { +Status ComputeACMetadata(ThreadPool* pool, PassesEncoderState* enc_state, + ModularFrameEncoder* modular_frame_encoder) { + PassesSharedState& shared = enc_state->shared; + std::atomic has_error{false}; + auto compute_ac_meta = [&](int group_index, int /* thread */) { + const Rect r = shared.frame_dim.DCGroupRect(group_index); + int modular_group_index = group_index; + if (enc_state->streaming_mode) { + JXL_ASSERT(group_index == 0); + modular_group_index = enc_state->dc_group_index; + } + if (!modular_frame_encoder->AddACMetadata(r, modular_group_index, + /*jpeg_transcode=*/false, + enc_state)) { + has_error = true; + return; + } + }; + JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, shared.frame_dim.num_dc_groups, + ThreadPool::NoInit, compute_ac_meta, + "Compute AC Metadata")); + if (has_error) return JXL_FAILURE("Compute AC Metadata failed"); + return true; +} + Status InitializePassesEncoder(const FrameHeader& frame_header, const Image3F& opsin, const Rect& rect, const JxlCmsInterface& cms, ThreadPool* pool, @@ -40,6 +65,7 @@ Status InitializePassesEncoder(const FrameHeader& frame_header, ModularFrameEncoder* modular_frame_encoder, AuxOut* aux_out) { PassesSharedState& JXL_RESTRICT shared = enc_state->shared; + JxlMemoryManager* memory_manager = enc_state->memory_manager(); enc_state->x_qm_multiplier = std::pow(1.25f, frame_header.x_qm_scale - 2.0f); enc_state->b_qm_multiplier = std::pow(1.25f, frame_header.b_qm_scale - 2.0f); @@ -51,7 +77,7 @@ Status InitializePassesEncoder(const FrameHeader& frame_header, // Allocate enough coefficients for each group on every row. JXL_ASSIGN_OR_RETURN( std::unique_ptr> coeffs, - ACImageT::Make(kGroupDim * kGroupDim, + ACImageT::Make(memory_manager, kGroupDim * kGroupDim, shared.frame_dim.num_groups)); enc_state->coeffs.emplace_back(std::move(coeffs)); } @@ -63,13 +89,13 @@ Status InitializePassesEncoder(const FrameHeader& frame_header, if (enc_state->initialize_global_state) { float scale = shared.quantizer.ScaleGlobalScale(enc_state->cparams.quant_ac_rescale); - DequantMatricesScaleDC(&shared.matrices, scale); + DequantMatricesScaleDC(memory_manager, &shared.matrices, scale); shared.quantizer.RecomputeFromGlobalScale(); } - JXL_ASSIGN_OR_RETURN(Image3F dc, - Image3F::Create(shared.frame_dim.xsize_blocks, - shared.frame_dim.ysize_blocks)); + JXL_ASSIGN_OR_RETURN( + Image3F dc, Image3F::Create(memory_manager, shared.frame_dim.xsize_blocks, + shared.frame_dim.ysize_blocks)); JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, shared.frame_dim.num_groups, ThreadPool::NoInit, [&](size_t group_idx, size_t _) { @@ -110,7 +136,7 @@ Status InitializePassesEncoder(const FrameHeader& frame_header, std::max(kMinButteraugliDistance, enc_state->cparams.butteraugli_distance * 0.1f); } - ImageBundle ib(&shared.metadata->m); + ImageBundle ib(memory_manager, &shared.metadata->m); // This is a lie - dc is in XYB // (but EncodeFrame will skip RGB->XYB conversion anyway) ib.SetFromImage( @@ -124,7 +150,8 @@ Status InitializePassesEncoder(const FrameHeader& frame_header, std::vector extra_channels; extra_channels.reserve(ib.metadata()->extra_channel_info.size()); for (size_t i = 0; i < ib.metadata()->extra_channel_info.size(); i++) { - JXL_ASSIGN_OR_RETURN(ImageF ch, ImageF::Create(ib.xsize(), ib.ysize())); + JXL_ASSIGN_OR_RETURN( + ImageF ch, ImageF::Create(memory_manager, ib.xsize(), ib.ysize())); extra_channels.emplace_back(std::move(ch)); // Must initialize the image with data to not affect blending with // uninitialized memory. @@ -134,15 +161,16 @@ Status InitializePassesEncoder(const FrameHeader& frame_header, } ib.SetExtraChannels(std::move(extra_channels)); } - auto special_frame = std::unique_ptr(new BitWriter()); + auto special_frame = + std::unique_ptr(new BitWriter(memory_manager)); FrameInfo dc_frame_info; dc_frame_info.frame_type = FrameType::kDCFrame; dc_frame_info.dc_level = frame_header.dc_level + 1; dc_frame_info.ib_needs_color_transform = false; dc_frame_info.save_before_color_transform = true; // Implicitly true AuxOut dc_aux_out; - JXL_CHECK(EncodeFrame(cparams, dc_frame_info, shared.metadata, ib, cms, - pool, special_frame.get(), + JXL_CHECK(EncodeFrame(memory_manager, cparams, dc_frame_info, + shared.metadata, ib, cms, pool, special_frame.get(), aux_out ? &dc_aux_out : nullptr)); if (aux_out) { for (const auto& l : dc_aux_out.layers) { @@ -152,9 +180,9 @@ Status InitializePassesEncoder(const FrameHeader& frame_header, const Span encoded = special_frame->GetSpan(); enc_state->special_frames.emplace_back(std::move(special_frame)); - ImageBundle decoded(&shared.metadata->m); + ImageBundle decoded(memory_manager, &shared.metadata->m); std::unique_ptr dec_state = - jxl::make_unique(); + jxl::make_unique(memory_manager); JXL_CHECK( dec_state->output_encoding_info.SetFromMetadata(*shared.metadata)); const uint8_t* frame_start = encoded.data(); @@ -173,8 +201,9 @@ Status InitializePassesEncoder(const FrameHeader& frame_header, // outputs multiple frames, this assumption could be wrong. const Image3F& dc_frame = dec_state->shared->dc_frames[frame_header.dc_level]; - JXL_ASSIGN_OR_RETURN(shared.dc_storage, - Image3F::Create(dc_frame.xsize(), dc_frame.ysize())); + JXL_ASSIGN_OR_RETURN( + shared.dc_storage, + Image3F::Create(memory_manager, dc_frame.xsize(), dc_frame.ysize())); CopyImageTo(dc_frame, &shared.dc_storage); ZeroFillImage(&shared.quant_dc); shared.dc = &shared.dc_storage; @@ -203,29 +232,10 @@ Status InitializePassesEncoder(const FrameHeader& frame_header, if (has_error) return JXL_FAILURE("Compute DC coeffs failed"); // TODO(veluca): this is only useful in tests and if inspection is enabled. if (!(frame_header.flags & FrameHeader::kSkipAdaptiveDCSmoothing)) { - JXL_RETURN_IF_ERROR(AdaptiveDCSmoothing(shared.quantizer.MulDC(), - &shared.dc_storage, pool)); + JXL_RETURN_IF_ERROR(AdaptiveDCSmoothing( + memory_manager, shared.quantizer.MulDC(), &shared.dc_storage, pool)); } } - std::atomic has_error{false}; - auto compute_ac_meta = [&](int group_index, int /* thread */) { - const Rect r = enc_state->shared.frame_dim.DCGroupRect(group_index); - int modular_group_index = group_index; - if (enc_state->streaming_mode) { - JXL_ASSERT(group_index == 0); - modular_group_index = enc_state->dc_group_index; - } - if (!modular_frame_encoder->AddACMetadata(r, modular_group_index, - /*jpeg_transcode=*/false, - enc_state)) { - has_error = true; - return; - } - }; - JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, shared.frame_dim.num_dc_groups, - ThreadPool::NoInit, compute_ac_meta, - "Compute AC Metadata")); - if (has_error) return JXL_FAILURE("Compute AC Metadata failed"); return true; } diff --git a/third_party/jpeg-xl/lib/jxl/enc_cache.h b/third_party/jpeg-xl/lib/jxl/enc_cache.h index 76b7e3ddae63..138a7c8ce897 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_cache.h +++ b/third_party/jpeg-xl/lib/jxl/enc_cache.h @@ -7,6 +7,7 @@ #define LIB_JXL_ENC_CACHE_H_ #include +#include #include #include @@ -32,6 +33,9 @@ struct AuxOut; // Contains encoder state. struct PassesEncoderState { + explicit PassesEncoderState(JxlMemoryManager* memory_manager) + : shared(memory_manager) {} + PassesSharedState shared; bool streaming_mode = false; @@ -66,6 +70,10 @@ struct PassesEncoderState { // Multiplier to be applied to the quant matrices of the x channel. float x_qm_multiplier = 1.0f; float b_qm_multiplier = 1.0f; + + ImageF initial_quant_masking1x1; + + JxlMemoryManager* memory_manager() const { return shared.memory_manager; } }; // Initialize per-frame information. @@ -77,6 +85,9 @@ Status InitializePassesEncoder(const FrameHeader& frame_header, ModularFrameEncoder* modular_frame_encoder, AuxOut* aux_out); +Status ComputeACMetadata(ThreadPool* pool, PassesEncoderState* enc_state, + ModularFrameEncoder* modular_frame_encoder); + } // namespace jxl #endif // LIB_JXL_ENC_CACHE_H_ diff --git a/third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.cc b/third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.cc index d45899936b79..9aaf4eb08e98 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.cc @@ -5,6 +5,8 @@ #include "lib/jxl/enc_chroma_from_luma.h" +#include + #include #include #include @@ -170,13 +172,15 @@ int32_t FindBestMultiplier(const float* values_m, const float* values_s, return std::max(-128.0f, std::min(127.0f, roundf(x))); } -Status InitDCStorage(size_t num_blocks, ImageF* dc_values) { +Status InitDCStorage(JxlMemoryManager* memory_manager, size_t num_blocks, + ImageF* dc_values) { // First row: Y channel // Second row: X channel // Third row: Y channel // Fourth row: B channel - JXL_ASSIGN_OR_RETURN(*dc_values, - ImageF::Create(RoundUpTo(num_blocks, Lanes(df)), 4)); + JXL_ASSIGN_OR_RETURN( + *dc_values, + ImageF::Create(memory_manager, RoundUpTo(num_blocks, Lanes(df)), 4)); JXL_ASSERT(dc_values->xsize() != 0); // Zero-fill the last lanes @@ -349,11 +353,11 @@ namespace jxl { HWY_EXPORT(InitDCStorage); HWY_EXPORT(ComputeTile); -Status CfLHeuristics::Init(const Rect& rect) { +Status CfLHeuristics::Init(JxlMemoryManager* memory_manager, const Rect& rect) { size_t xsize_blocks = rect.xsize() / kBlockDim; size_t ysize_blocks = rect.ysize() / kBlockDim; - return HWY_DYNAMIC_DISPATCH(InitDCStorage)(xsize_blocks * ysize_blocks, - &dc_values); + return HWY_DYNAMIC_DISPATCH(InitDCStorage)( + memory_manager, xsize_blocks * ysize_blocks, &dc_values); } void CfLHeuristics::ComputeTile(const Rect& r, const Image3F& opsin, diff --git a/third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.h b/third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.h index 514202bf8b24..105c2bcff8e9 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.h +++ b/third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.h @@ -9,6 +9,8 @@ // Chroma-from-luma, computed using heuristics to determine the best linear // model for the X and B channels from the Y channel. +#include + #include #include @@ -31,7 +33,7 @@ void ColorCorrelationMapEncodeDC(const ColorCorrelationMap& map, AuxOut* aux_out); struct CfLHeuristics { - Status Init(const Rect& rect); + Status Init(JxlMemoryManager* memory_manager, const Rect& rect); void PrepareForThreads(size_t num_threads) { mem = hwy::AllocateAligned(num_threads * ItemsPerThread()); diff --git a/third_party/jpeg-xl/lib/jxl/enc_coeff_order.cc b/third_party/jpeg-xl/lib/jxl/enc_coeff_order.cc index abe404ce9e73..6e2a5600b754 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_coeff_order.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_coeff_order.cc @@ -3,6 +3,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +#include + #include #include #include @@ -234,12 +236,14 @@ void TokenizePermutation(const coeff_order_t* JXL_RESTRICT order, size_t skip, void EncodePermutation(const coeff_order_t* JXL_RESTRICT order, size_t skip, size_t size, BitWriter* writer, int layer, AuxOut* aux_out) { + JxlMemoryManager* memory_manager = writer->memory_manager(); std::vector> tokens(1); TokenizePermutation(order, skip, size, tokens.data()); std::vector context_map; EntropyEncodingData codes; - BuildAndEncodeHistograms(HistogramParams(), kPermutationContexts, tokens, - &codes, &context_map, writer, layer, aux_out); + BuildAndEncodeHistograms(memory_manager, HistogramParams(), + kPermutationContexts, tokens, &codes, &context_map, + writer, layer, aux_out); WriteTokens(tokens[0], codes, context_map, 0, writer, layer, aux_out); } @@ -260,6 +264,7 @@ void EncodeCoeffOrders(uint16_t used_orders, const coeff_order_t* JXL_RESTRICT order, BitWriter* writer, size_t layer, AuxOut* JXL_RESTRICT aux_out) { + JxlMemoryManager* memory_manager = writer->memory_manager(); auto mem = hwy::AllocateAligned(AcStrategy::kMaxCoeffArea); uint16_t computed = 0; std::vector> tokens(1); @@ -283,8 +288,9 @@ void EncodeCoeffOrders(uint16_t used_orders, if (used_orders != 0) { std::vector context_map; EntropyEncodingData codes; - BuildAndEncodeHistograms(HistogramParams(), kPermutationContexts, tokens, - &codes, &context_map, writer, layer, aux_out); + BuildAndEncodeHistograms(memory_manager, HistogramParams(), + kPermutationContexts, tokens, &codes, &context_map, + writer, layer, aux_out); WriteTokens(tokens[0], codes, context_map, 0, writer, layer, aux_out); } } diff --git a/third_party/jpeg-xl/lib/jxl/enc_comparator.cc b/third_party/jpeg-xl/lib/jxl/enc_comparator.cc index 3ef6a49b8f69..eb420bbb2745 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_comparator.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_comparator.cc @@ -5,10 +5,10 @@ #include "lib/jxl/enc_comparator.h" -#include -#include +#include #include +#include #include "lib/jxl/base/compiler_specific.h" #include "lib/jxl/enc_gamma_correct.h" @@ -70,14 +70,15 @@ Status ComputeScore(const ImageBundle& rgb0, const ImageBundle& rgb1, Comparator* comparator, const JxlCmsInterface& cms, float* score, ImageF* diffmap, ThreadPool* pool, bool ignore_alpha) { + JxlMemoryManager* memory_manager = rgb0.memory_manager(); // Convert to linear sRGB (unless already in that space) ImageMetadata metadata0 = *rgb0.metadata(); - ImageBundle store0(&metadata0); + ImageBundle store0(memory_manager, &metadata0); const ImageBundle* linear_srgb0; JXL_CHECK(TransformIfNeeded(rgb0, ColorEncoding::LinearSRGB(rgb0.IsGray()), cms, pool, &store0, &linear_srgb0)); ImageMetadata metadata1 = *rgb1.metadata(); - ImageBundle store1(&metadata1); + ImageBundle store1(memory_manager, &metadata1); const ImageBundle* linear_srgb1; JXL_CHECK(TransformIfNeeded(rgb1, ColorEncoding::LinearSRGB(rgb1.IsGray()), cms, pool, &store1, &linear_srgb1)); @@ -115,7 +116,8 @@ Status ComputeScore(const ImageBundle& rgb0, const ImageBundle& rgb1, if (diffmap != nullptr) { const size_t xsize = rgb0.xsize(); const size_t ysize = rgb0.ysize(); - JXL_ASSIGN_OR_RETURN(*diffmap, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(*diffmap, + ImageF::Create(memory_manager, xsize, ysize)); for (size_t y = 0; y < ysize; ++y) { const float* JXL_RESTRICT row_black = diffmap_black.ConstRow(y); const float* JXL_RESTRICT row_white = diffmap_white.ConstRow(y); diff --git a/third_party/jpeg-xl/lib/jxl/enc_context_map.cc b/third_party/jpeg-xl/lib/jxl/enc_context_map.cc index 36efc4e64944..739179105a6d 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_context_map.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_context_map.cc @@ -7,6 +7,7 @@ #include "lib/jxl/enc_context_map.h" +#include #include #include @@ -70,6 +71,7 @@ void EncodeContextMap(const std::vector& context_map, return; } + JxlMemoryManager* memory_manager = writer->memory_manager(); std::vector transformed_symbols = MoveToFrontTransform(context_map); std::vector> tokens(1); std::vector> mtf_tokens(1); @@ -86,14 +88,16 @@ void EncodeContextMap(const std::vector& context_map, { EntropyEncodingData codes; std::vector sink_context_map; - ans_cost = BuildAndEncodeHistograms(params, 1, tokens, &codes, - &sink_context_map, nullptr, 0, nullptr); + ans_cost = + BuildAndEncodeHistograms(memory_manager, params, 1, tokens, &codes, + &sink_context_map, nullptr, 0, nullptr); } { EntropyEncodingData codes; std::vector sink_context_map; - mtf_cost = BuildAndEncodeHistograms(params, 1, mtf_tokens, &codes, - &sink_context_map, nullptr, 0, nullptr); + mtf_cost = + BuildAndEncodeHistograms(memory_manager, params, 1, mtf_tokens, &codes, + &sink_context_map, nullptr, 0, nullptr); } bool use_mtf = mtf_cost < ans_cost; // Rebuild token list. @@ -118,8 +122,8 @@ void EncodeContextMap(const std::vector& context_map, writer->Write(1, TO_JXL_BOOL(use_mtf)); // Use/don't use MTF. EntropyEncodingData codes; std::vector sink_context_map; - BuildAndEncodeHistograms(params, 1, tokens, &codes, &sink_context_map, - writer, layer, aux_out); + BuildAndEncodeHistograms(memory_manager, params, 1, tokens, &codes, + &sink_context_map, writer, layer, aux_out); WriteTokens(tokens[0], codes, sink_context_map, 0, writer); allotment.ReclaimAndCharge(writer, layer, aux_out); } diff --git a/third_party/jpeg-xl/lib/jxl/enc_context_map.h b/third_party/jpeg-xl/lib/jxl/enc_context_map.h index 041e71de7af2..a18bc7646c9f 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_context_map.h +++ b/third_party/jpeg-xl/lib/jxl/enc_context_map.h @@ -6,9 +6,8 @@ #ifndef LIB_JXL_ENC_CONTEXT_MAP_H_ #define LIB_JXL_ENC_CONTEXT_MAP_H_ -#include -#include - +#include +#include #include #include "lib/jxl/ac_context.h" diff --git a/third_party/jpeg-xl/lib/jxl/enc_debug_image.cc b/third_party/jpeg-xl/lib/jxl/enc_debug_image.cc index 6b4d115e7ff9..a57a7070a7f2 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_debug_image.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_debug_image.cc @@ -5,6 +5,8 @@ #include "lib/jxl/enc_debug_image.h" +#include + #include #include @@ -24,7 +26,9 @@ StatusOr ConvertToFloat(const Image3& from) { if (std::is_same::value || std::is_same::value) { factor = 1.0f; } - JXL_ASSIGN_OR_RETURN(Image3F to, Image3F::Create(from.xsize(), from.ysize())); + JxlMemoryManager* memory_manager = from.memory_manager(); + JXL_ASSIGN_OR_RETURN( + Image3F to, Image3F::Create(memory_manager, from.xsize(), from.ysize())); for (size_t c = 0; c < 3; ++c) { for (size_t y = 0; y < from.ysize(); ++y) { const From* const JXL_RESTRICT row_from = from.ConstPlaneRow(c, y); @@ -63,8 +67,11 @@ Status DumpPlaneNormalizedT(const CompressParams& cparams, const char* label, T min; T max; ImageMinMax(image, &min, &max); - JXL_ASSIGN_OR_RETURN(Image3B normalized, - Image3B::Create(image.xsize(), image.ysize())); + JxlMemoryManager* memory_manager = image.memory_manager(); + + JXL_ASSIGN_OR_RETURN( + Image3B normalized, + Image3B::Create(memory_manager, image.xsize(), image.ysize())); for (size_t c = 0; c < 3; ++c) { float mul = min == max ? 0 : (255.0f / (max - min)); for (size_t y = 0; y < image.ysize(); ++y) { @@ -93,9 +100,11 @@ Status DumpImage(const CompressParams& cparams, const char* label, Status DumpXybImage(const CompressParams& cparams, const char* label, const Image3F& image) { if (!cparams.debug_image) return true; + JxlMemoryManager* memory_manager = image.memory_manager(); - JXL_ASSIGN_OR_RETURN(Image3F linear, - Image3F::Create(image.xsize(), image.ysize())); + JXL_ASSIGN_OR_RETURN( + Image3F linear, + Image3F::Create(memory_manager, image.xsize(), image.ysize())); OpsinParams opsin_params; opsin_params.Init(kDefaultIntensityTarget); OpsinToLinear(image, Rect(linear), nullptr, &linear, opsin_params); diff --git a/third_party/jpeg-xl/lib/jxl/enc_detect_dots.cc b/third_party/jpeg-xl/lib/jxl/enc_detect_dots.cc index 81dd2f2b670e..3d0428626a24 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_detect_dots.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_detect_dots.cc @@ -5,6 +5,8 @@ #include "lib/jxl/enc_detect_dots.h" +#include + #include #include #include @@ -50,9 +52,11 @@ StatusOr SumOfSquareDifferences(const Image3F& forig, const auto color_coef0 = Set(d, 0.0f); const auto color_coef1 = Set(d, 10.0f); const auto color_coef2 = Set(d, 0.0f); + JxlMemoryManager* memory_manager = forig.memory_manager(); - JXL_ASSIGN_OR_RETURN(ImageF sum_of_squares, - ImageF::Create(forig.xsize(), forig.ysize())); + JXL_ASSIGN_OR_RETURN( + ImageF sum_of_squares, + ImageF::Create(memory_manager, forig.xsize(), forig.ysize())); JXL_CHECK(RunOnPool( pool, 0, forig.ysize(), ThreadPool::NoInit, [&](const uint32_t task, size_t thread) { @@ -145,10 +149,13 @@ const WeightsSeparable5& WeightsSeparable5Gaussian3() { StatusOr ComputeEnergyImage(const Image3F& orig, Image3F* smooth, ThreadPool* pool) { + JxlMemoryManager* memory_manager = orig.memory_manager(); // Prepare guidance images for dot selection. - JXL_ASSIGN_OR_RETURN(Image3F forig, - Image3F::Create(orig.xsize(), orig.ysize())); - JXL_ASSIGN_OR_RETURN(*smooth, Image3F::Create(orig.xsize(), orig.ysize())); + JXL_ASSIGN_OR_RETURN( + Image3F forig, + Image3F::Create(memory_manager, orig.xsize(), orig.ysize())); + JXL_ASSIGN_OR_RETURN( + *smooth, Image3F::Create(memory_manager, orig.xsize(), orig.ysize())); Rect rect(orig); const auto& weights1 = WeightsSeparable5Gaussian0_65(); @@ -289,8 +296,10 @@ StatusOr> FindCC(const ImageF& energy, uint32_t maxWindow, double minScore) { const int kExtraRect = 4; - JXL_ASSIGN_OR_RETURN(ImageF img, - ImageF::Create(energy.xsize(), energy.ysize())); + JxlMemoryManager* memory_manager = energy.memory_manager(); + JXL_ASSIGN_OR_RETURN( + ImageF img, + ImageF::Create(memory_manager, energy.xsize(), energy.ysize())); CopyImageTo(energy, &img); std::vector ans; for (size_t y = 0; y < rect.ysize(); y++) { @@ -537,9 +546,11 @@ GaussianEllipse FitGaussian(const ConnectedComponent& cc, const Rect& rect, StatusOr> DetectGaussianEllipses( const Image3F& opsin, const Rect& rect, const GaussianDetectParams& params, const EllipseQuantParams& qParams, ThreadPool* pool) { + JxlMemoryManager* memory_manager = opsin.memory_manager(); std::vector dots; - JXL_ASSIGN_OR_RETURN(Image3F smooth, - Image3F::Create(opsin.xsize(), opsin.ysize())); + JXL_ASSIGN_OR_RETURN( + Image3F smooth, + Image3F::Create(memory_manager, opsin.xsize(), opsin.ysize())); JXL_ASSIGN_OR_RETURN(ImageF energy, ComputeEnergyImage(opsin, &smooth, pool)); JXL_ASSIGN_OR_RETURN(std::vector components, FindCC(energy, rect, params.t_low, params.t_high, diff --git a/third_party/jpeg-xl/lib/jxl/enc_external_image.cc b/third_party/jpeg-xl/lib/jxl/enc_external_image.cc index 90e4937e7be3..cb53b77d1606 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_external_image.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_external_image.cc @@ -5,10 +5,11 @@ #include "lib/jxl/enc_external_image.h" +#include #include -#include #include +#include #include #include "lib/jxl/base/byte_order.h" @@ -102,6 +103,7 @@ Status ConvertFromExternalNoSizeCheck(const uint8_t* data, size_t xsize, size_t bits_per_sample, JxlPixelFormat format, ThreadPool* pool, ImageBundle* ib) { + JxlMemoryManager* memory_manager = ib->memory_manager(); bool has_alpha = format.num_channels == 2 || format.num_channels == 4; if (format.num_channels < color_channels) { return JXL_FAILURE("Expected %" PRIuS @@ -109,7 +111,8 @@ Status ConvertFromExternalNoSizeCheck(const uint8_t* data, size_t xsize, color_channels, format.num_channels); } - JXL_ASSIGN_OR_RETURN(Image3F color, Image3F::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(Image3F color, + Image3F::Create(memory_manager, xsize, ysize)); for (size_t c = 0; c < color_channels; ++c) { JXL_RETURN_IF_ERROR(ConvertFromExternalNoSizeCheck( data, xsize, ysize, stride, bits_per_sample, format, c, pool, @@ -124,7 +127,8 @@ Status ConvertFromExternalNoSizeCheck(const uint8_t* data, size_t xsize, // Passing an interleaved image with an alpha channel to an image that doesn't // have alpha channel just discards the passed alpha channel. if (has_alpha && ib->HasAlpha()) { - JXL_ASSIGN_OR_RETURN(ImageF alpha, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF alpha, + ImageF::Create(memory_manager, xsize, ysize)); JXL_RETURN_IF_ERROR(ConvertFromExternalNoSizeCheck( data, xsize, ysize, stride, bits_per_sample, format, format.num_channels - 1, pool, &alpha)); @@ -132,7 +136,8 @@ Status ConvertFromExternalNoSizeCheck(const uint8_t* data, size_t xsize, } else if (!has_alpha && ib->HasAlpha()) { // if alpha is not passed, but it is expected, then assume // it is all-opaque - JXL_ASSIGN_OR_RETURN(ImageF alpha, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF alpha, + ImageF::Create(memory_manager, xsize, ysize)); FillImage(1.0f, &alpha); ib->SetAlpha(std::move(alpha)); } @@ -172,6 +177,7 @@ Status ConvertFromExternal(Span bytes, size_t xsize, size_t color_channels, size_t bits_per_sample, JxlPixelFormat format, ThreadPool* pool, ImageBundle* ib) { + JxlMemoryManager* memory_manager = ib->memory_manager(); bool has_alpha = format.num_channels == 2 || format.num_channels == 4; if (format.num_channels < color_channels) { return JXL_FAILURE("Expected %" PRIuS @@ -179,7 +185,8 @@ Status ConvertFromExternal(Span bytes, size_t xsize, color_channels, format.num_channels); } - JXL_ASSIGN_OR_RETURN(Image3F color, Image3F::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(Image3F color, + Image3F::Create(memory_manager, xsize, ysize)); for (size_t c = 0; c < color_channels; ++c) { JXL_RETURN_IF_ERROR(ConvertFromExternal(bytes.data(), bytes.size(), xsize, ysize, bits_per_sample, format, c, @@ -194,7 +201,8 @@ Status ConvertFromExternal(Span bytes, size_t xsize, // Passing an interleaved image with an alpha channel to an image that doesn't // have alpha channel just discards the passed alpha channel. if (has_alpha && ib->HasAlpha()) { - JXL_ASSIGN_OR_RETURN(ImageF alpha, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF alpha, + ImageF::Create(memory_manager, xsize, ysize)); JXL_RETURN_IF_ERROR(ConvertFromExternal( bytes.data(), bytes.size(), xsize, ysize, bits_per_sample, format, format.num_channels - 1, pool, &alpha)); @@ -202,7 +210,8 @@ Status ConvertFromExternal(Span bytes, size_t xsize, } else if (!has_alpha && ib->HasAlpha()) { // if alpha is not passed, but it is expected, then assume // it is all-opaque - JXL_ASSIGN_OR_RETURN(ImageF alpha, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF alpha, + ImageF::Create(memory_manager, xsize, ysize)); FillImage(1.0f, &alpha); ib->SetAlpha(std::move(alpha)); } diff --git a/third_party/jpeg-xl/lib/jxl/enc_external_image_gbench.cc b/third_party/jpeg-xl/lib/jxl/enc_external_image_gbench.cc index 64e9cf6ad569..2eab7ef8f0bc 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_external_image_gbench.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_external_image_gbench.cc @@ -3,9 +3,17 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +#include + +#include +#include +#include + #include "benchmark/benchmark.h" +#include "lib/jxl/base/status.h" #include "lib/jxl/enc_external_image.h" -#include "lib/jxl/image_ops.h" +#include "lib/jxl/image_metadata.h" +#include "tools/no_memory_manager.h" namespace jxl { namespace { @@ -18,7 +26,7 @@ void BM_EncExternalImage_ConvertImageRGBA(benchmark::State& state) { ImageMetadata im; im.SetAlphaBits(8); - ImageBundle ib(&im); + ImageBundle ib(jpegxl::tools::NoMemoryManager(), &im); std::vector interleaved(xsize * ysize * 4); JxlPixelFormat format = {4, JXL_TYPE_UINT8, JXL_NATIVE_ENDIAN, 0}; diff --git a/third_party/jpeg-xl/lib/jxl/enc_external_image_test.cc b/third_party/jpeg-xl/lib/jxl/enc_external_image_test.cc index de2e15ed1604..cb06552123bc 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_external_image_test.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_external_image_test.cc @@ -5,14 +5,16 @@ #include "lib/jxl/enc_external_image.h" -#include -#include +#include -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/base/data_parallel.h" +#include +#include + +#include "lib/jxl/base/span.h" #include "lib/jxl/color_encoding_internal.h" -#include "lib/jxl/image_ops.h" -#include "lib/jxl/image_test_utils.h" +#include "lib/jxl/image_bundle.h" +#include "lib/jxl/image_metadata.h" +#include "lib/jxl/test_utils.h" #include "lib/jxl/testing.h" namespace jxl { @@ -22,7 +24,7 @@ namespace { TEST(ExternalImageTest, InvalidSize) { ImageMetadata im; im.SetAlphaBits(8); - ImageBundle ib(&im); + ImageBundle ib(jxl::test::MemoryManager(), &im); JxlPixelFormat format = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; const uint8_t buf[10 * 100 * 8] = {}; @@ -44,7 +46,7 @@ TEST(ExternalImageTest, InvalidSize) { TEST(ExternalImageTest, AlphaMissing) { ImageMetadata im; im.SetAlphaBits(0); // No alpha - ImageBundle ib(&im); + ImageBundle ib(jxl::test::MemoryManager(), &im); const size_t xsize = 10; const size_t ysize = 20; @@ -63,7 +65,7 @@ TEST(ExternalImageTest, AlphaPremultiplied) { ImageMetadata im; im.SetAlphaBits(8, true); - ImageBundle ib(&im); + ImageBundle ib(jxl::test::MemoryManager(), &im); const size_t xsize = 10; const size_t ysize = 20; const size_t size = xsize * ysize * 8; diff --git a/third_party/jpeg-xl/lib/jxl/enc_frame.cc b/third_party/jpeg-xl/lib/jxl/enc_frame.cc index ec0af1e8e5fc..ab4c2100c9c1 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_frame.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_frame.cc @@ -5,6 +5,8 @@ #include "lib/jxl/enc_frame.h" +#include + #include #include #include @@ -36,7 +38,6 @@ #include "lib/jxl/enc_ac_strategy.h" #include "lib/jxl/enc_adaptive_quantization.h" #include "lib/jxl/enc_ans.h" -#include "lib/jxl/enc_ar_control_field.h" #include "lib/jxl/enc_aux_out.h" #include "lib/jxl/enc_bit_writer.h" #include "lib/jxl/enc_cache.h" @@ -260,10 +261,14 @@ Status LoopFilterFromParams(const CompressParams& cparams, bool streaming_mode, loop_filter->gab = ApplyOverride( cparams.gaborish, cparams.speed_tier <= SpeedTier::kHare && frame_header->encoding == FrameEncoding::kVarDCT && - cparams.decoding_speed_tier < 4); + cparams.decoding_speed_tier < 4 && + !cparams.disable_percepeptual_optimizations); if (cparams.epf != -1) { loop_filter->epf_iters = cparams.epf; + } else if (cparams.disable_percepeptual_optimizations) { + loop_filter->epf_iters = 0; + return true; } else { if (frame_header->encoding == FrameEncoding::kModular) { loop_filter->epf_iters = 0; @@ -732,6 +737,7 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data, ModularFrameEncoder* enc_modular, PassesEncoderState* enc_state) { PassesSharedState& shared = enc_state->shared; + JxlMemoryManager* memory_manager = enc_state->memory_manager(); const FrameDimensions& frame_dim = shared.frame_dim; const size_t xsize = frame_dim.xsize_padded; @@ -740,8 +746,8 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data, const size_t ysize_blocks = frame_dim.ysize_blocks; // no-op chroma from luma - JXL_ASSIGN_OR_RETURN(shared.cmap, - ColorCorrelationMap::Create(xsize, ysize, false)); + JXL_ASSIGN_OR_RETURN(shared.cmap, ColorCorrelationMap::Create( + memory_manager, xsize, ysize, false)); shared.ac_strategy.FillDCT8(); FillImage(static_cast(0), &shared.epf_sharpness); @@ -749,7 +755,8 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data, while (enc_state->coeffs.size() < enc_state->passes.size()) { JXL_ASSIGN_OR_RETURN( std::unique_ptr> coeffs, - ACImageT::Make(kGroupDim * kGroupDim, frame_dim.num_groups)); + ACImageT::Make(memory_manager, kGroupDim * kGroupDim, + frame_dim.num_groups)); enc_state->coeffs.emplace_back(std::move(coeffs)); } @@ -775,7 +782,7 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data, } } } - DequantMatricesSetCustomDC(&shared.matrices, dcquantization); + DequantMatricesSetCustomDC(memory_manager, &shared.matrices, dcquantization); float dcquantization_r[3] = {1.0f / dcquantization[0], 1.0f / dcquantization[1], 1.0f / dcquantization[2]}; @@ -893,7 +900,8 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data, } } - JXL_ASSIGN_OR_RETURN(Image3F dc, Image3F::Create(xsize_blocks, ysize_blocks)); + JXL_ASSIGN_OR_RETURN( + Image3F dc, Image3F::Create(memory_manager, xsize_blocks, ysize_blocks)); if (!frame_header.chroma_subsampling.Is444()) { ZeroFillImage(&dc); for (auto& coeff : enc_state->coeffs) { @@ -1062,12 +1070,27 @@ Status ComputeVarDCTEncodingData(const FrameHeader& frame_header, AuxOut* aux_out) { JXL_ASSERT((rect.xsize() % kBlockDim) == 0 && (rect.ysize() % kBlockDim) == 0); + JxlMemoryManager* memory_manager = enc_state->memory_manager(); + // Save pre-Gaborish opsin for AR control field heuristics computation. + Image3F orig_opsin; + JXL_ASSIGN_OR_RETURN( + orig_opsin, Image3F::Create(memory_manager, rect.xsize(), rect.ysize())); + CopyImageTo(rect, *opsin, Rect(orig_opsin), &orig_opsin); + orig_opsin.ShrinkTo(enc_state->shared.frame_dim.xsize, + enc_state->shared.frame_dim.ysize); + JXL_RETURN_IF_ERROR(LossyFrameHeuristics(frame_header, enc_state, enc_modular, linear, opsin, rect, cms, pool, aux_out)); JXL_RETURN_IF_ERROR(InitializePassesEncoder( frame_header, *opsin, rect, cms, pool, enc_state, enc_modular, aux_out)); + + JXL_RETURN_IF_ERROR( + ComputeARHeuristics(frame_header, enc_state, orig_opsin, rect, pool)); + + JXL_RETURN_IF_ERROR(ComputeACMetadata(pool, enc_state, enc_modular)); + return true; } @@ -1090,10 +1113,11 @@ void ComputeAllCoeffOrders(PassesEncoderState& enc_state, // Working area for TokenizeCoefficients (per-group!) struct EncCache { // Allocates memory when first called. - Status InitOnce() { + Status InitOnce(JxlMemoryManager* memory_manager) { if (num_nzeroes.xsize() == 0) { - JXL_ASSIGN_OR_RETURN( - num_nzeroes, Image3I::Create(kGroupDimInBlocks, kGroupDimInBlocks)); + JXL_ASSIGN_OR_RETURN(num_nzeroes, + Image3I::Create(memory_manager, kGroupDimInBlocks, + kGroupDimInBlocks)); } return true; } @@ -1106,6 +1130,7 @@ Status TokenizeAllCoefficients(const FrameHeader& frame_header, PassesEncoderState* enc_state) { PassesSharedState& shared = enc_state->shared; std::vector group_caches; + JxlMemoryManager* memory_manager = enc_state->memory_manager(); const auto tokenize_group_init = [&](const size_t num_threads) { group_caches.resize(num_threads); return true; @@ -1124,7 +1149,7 @@ Status TokenizeAllCoefficients(const FrameHeader& frame_header, enc_state->coeffs[idx_pass]->PlaneRow(2, group_index, 0).ptr32, }; // Ensure group cache is initialized. - if (!group_caches[thread].InitOnce()) { + if (!group_caches[thread].InitOnce(memory_manager)) { has_error = true; return; } @@ -1160,8 +1185,10 @@ Status EncodeGlobalDCInfo(const PassesSharedState& shared, BitWriter* writer, Status EncodeGlobalACInfo(PassesEncoderState* enc_state, BitWriter* writer, ModularFrameEncoder* enc_modular, AuxOut* aux_out) { PassesSharedState& shared = enc_state->shared; - JXL_RETURN_IF_ERROR(DequantMatricesEncode(shared.matrices, writer, - kLayerQuant, aux_out, enc_modular)); + JxlMemoryManager* memory_manager = enc_state->memory_manager(); + JXL_RETURN_IF_ERROR(DequantMatricesEncode(memory_manager, shared.matrices, + writer, kLayerQuant, aux_out, + enc_modular)); size_t num_histo_bits = CeilLog2Nonzero(shared.frame_dim.num_groups); if (!enc_state->streaming_mode && num_histo_bits != 0) { BitWriter::Allotment allotment(writer, num_histo_bits); @@ -1213,7 +1240,7 @@ Status EncodeGlobalACInfo(PassesEncoderState* enc_state, BitWriter* writer, hist_params.streaming_mode = enc_state->streaming_mode; hist_params.initialize_global_state = enc_state->initialize_global_state; BuildAndEncodeHistograms( - hist_params, + memory_manager, hist_params, num_histogram_groups * shared.block_ctx_map.NumACContexts(), enc_state->passes[i].ac_tokens, &enc_state->passes[i].codes, &enc_state->passes[i].context_map, writer, kLayerAC, aux_out); @@ -1225,8 +1252,10 @@ Status EncodeGlobalACInfo(PassesEncoderState* enc_state, BitWriter* writer, Status EncodeGroups(const FrameHeader& frame_header, PassesEncoderState* enc_state, ModularFrameEncoder* enc_modular, ThreadPool* pool, - std::vector* group_codes, AuxOut* aux_out) { + std::vector>* group_codes, + AuxOut* aux_out) { const PassesSharedState& shared = enc_state->shared; + JxlMemoryManager* memory_manager = shared.memory_manager; const FrameDimensions& frame_dim = shared.frame_dim; const size_t num_groups = frame_dim.num_groups; const size_t num_passes = enc_state->progressive_splitter.GetNumPasses(); @@ -1237,10 +1266,14 @@ Status EncodeGroups(const FrameHeader& frame_header, is_small_image ? 1 : AcGroupIndex(0, 0, num_groups, frame_dim.num_dc_groups) + num_groups * num_passes; - group_codes->resize(num_toc_entries); + JXL_ASSERT(group_codes->empty()); + group_codes->reserve(num_toc_entries); + for (size_t i = 0; i < num_toc_entries; ++i) { + group_codes->emplace_back(jxl::make_unique(memory_manager)); + } - const auto get_output = [&](const size_t index) { - return &(*group_codes)[is_small_image ? 0 : index]; + const auto get_output = [&](const size_t index) -> BitWriter* { + return (*group_codes)[is_small_image ? 0 : index].get(); }; auto ac_group_code = [&](size_t pass, size_t group) { return get_output(AcGroupIndex(pass, group, frame_dim.num_groups, @@ -1377,10 +1410,10 @@ Status EncodeGroups(const FrameHeader& frame_header, // Resizing aux_outs to 0 also Assimilates the array. static_cast(resize_aux_outs(0)); - for (BitWriter& bw : *group_codes) { - BitWriter::Allotment allotment(&bw, 8); - bw.ZeroPadToByte(); // end of group. - allotment.ReclaimAndCharge(&bw, kLayerAC, aux_out); + for (std::unique_ptr& bw : *group_codes) { + BitWriter::Allotment allotment(bw.get(), 8); + bw->ZeroPadToByte(); // end of group. + allotment.ReclaimAndCharge(bw.get(), kLayerAC, aux_out); } return true; } @@ -1391,10 +1424,11 @@ Status ComputeEncodingData( const jpeg::JPEGData* jpeg_data, size_t x0, size_t y0, size_t xsize, size_t ysize, const JxlCmsInterface& cms, ThreadPool* pool, FrameHeader& mutable_frame_header, ModularFrameEncoder& enc_modular, - PassesEncoderState& enc_state, std::vector* group_codes, - AuxOut* aux_out) { + PassesEncoderState& enc_state, + std::vector>* group_codes, AuxOut* aux_out) { JXL_ASSERT(x0 + xsize <= frame_data.xsize); JXL_ASSERT(y0 + ysize <= frame_data.ysize); + JxlMemoryManager* memory_manager = enc_state.memory_manager(); const FrameHeader& frame_header = mutable_frame_header; PassesSharedState& shared = enc_state.shared; shared.metadata = metadata; @@ -1412,26 +1446,29 @@ Status ComputeEncodingData( const FrameDimensions& frame_dim = shared.frame_dim; JXL_ASSIGN_OR_RETURN( shared.ac_strategy, - AcStrategyImage::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); + AcStrategyImage::Create(memory_manager, frame_dim.xsize_blocks, + frame_dim.ysize_blocks)); + JXL_ASSIGN_OR_RETURN(shared.raw_quant_field, + ImageI::Create(memory_manager, frame_dim.xsize_blocks, + frame_dim.ysize_blocks)); + JXL_ASSIGN_OR_RETURN(shared.epf_sharpness, + ImageB::Create(memory_manager, frame_dim.xsize_blocks, + frame_dim.ysize_blocks)); JXL_ASSIGN_OR_RETURN( - shared.raw_quant_field, - ImageI::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); - JXL_ASSIGN_OR_RETURN( - shared.epf_sharpness, - ImageB::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); - JXL_ASSIGN_OR_RETURN(shared.cmap, ColorCorrelationMap::Create( - frame_dim.xsize, frame_dim.ysize)); + shared.cmap, ColorCorrelationMap::Create(memory_manager, frame_dim.xsize, + frame_dim.ysize)); shared.coeff_order_size = kCoeffOrderMaxSize; if (frame_header.encoding == FrameEncoding::kVarDCT) { shared.coeff_orders.resize(frame_header.passes.num_passes * kCoeffOrderMaxSize); } - JXL_ASSIGN_OR_RETURN(shared.quant_dc, ImageB::Create(frame_dim.xsize_blocks, - frame_dim.ysize_blocks)); - JXL_ASSIGN_OR_RETURN( - shared.dc_storage, - Image3F::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); + JXL_ASSIGN_OR_RETURN(shared.quant_dc, + ImageB::Create(memory_manager, frame_dim.xsize_blocks, + frame_dim.ysize_blocks)); + JXL_ASSIGN_OR_RETURN(shared.dc_storage, + Image3F::Create(memory_manager, frame_dim.xsize_blocks, + frame_dim.ysize_blocks)); shared.dc = &shared.dc_storage; const size_t num_extra_channels = metadata->m.num_extra_channels; @@ -1451,14 +1488,16 @@ Status ComputeEncodingData( JXL_ASSERT(patch_rect.IsInside(frame_rect)); // Allocating a large enough image avoids a copy when padding. - JXL_ASSIGN_OR_RETURN(Image3F color, - Image3F::Create(RoundUpToBlockDim(patch_rect.xsize()), - RoundUpToBlockDim(patch_rect.ysize()))); + JXL_ASSIGN_OR_RETURN( + Image3F color, + Image3F::Create(memory_manager, RoundUpToBlockDim(patch_rect.xsize()), + RoundUpToBlockDim(patch_rect.ysize()))); color.ShrinkTo(patch_rect.xsize(), patch_rect.ysize()); std::vector extra_channels(num_extra_channels); for (auto& extra_channel : extra_channels) { JXL_ASSIGN_OR_RETURN( - extra_channel, ImageF::Create(patch_rect.xsize(), patch_rect.ysize())); + extra_channel, + ImageF::Create(memory_manager, patch_rect.xsize(), patch_rect.ysize())); } ImageF* alpha = alpha_eci ? &extra_channels[alpha_idx] : nullptr; ImageF* black = black_eci ? &extra_channels[black_idx] : nullptr; @@ -1484,9 +1523,9 @@ Status ComputeEncodingData( frame_info.ib_needs_color_transform) { if (frame_header.encoding == FrameEncoding::kVarDCT && cparams.speed_tier <= SpeedTier::kKitten) { - JXL_ASSIGN_OR_RETURN( - linear_storage, - Image3F::Create(patch_rect.xsize(), patch_rect.ysize())); + JXL_ASSIGN_OR_RETURN(linear_storage, + Image3F::Create(memory_manager, patch_rect.xsize(), + patch_rect.ysize())); linear = &linear_storage; } ToXYB(c_enc, metadata->m.IntensityTarget(), black, pool, &color, cms, @@ -1500,8 +1539,9 @@ Status ComputeEncodingData( bool lossless = cparams.IsLossless(); if (alpha && !alpha_eci->alpha_associated && frame_header.frame_type == FrameType::kRegularFrame && - !ApplyOverride(cparams.keep_invisible, true) && - cparams.ec_resampling == cparams.resampling) { + !ApplyOverride(cparams.keep_invisible, cparams.IsLossless()) && + cparams.ec_resampling == cparams.resampling && + !cparams.disable_percepeptual_optimizations) { // simplify invisible pixels SimplifyInvisible(&color, *alpha, lossless); if (linear) { @@ -1596,7 +1636,7 @@ Status ComputeEncodingData( Status PermuteGroups(const CompressParams& cparams, const FrameDimensions& frame_dim, size_t num_passes, std::vector* permutation, - std::vector* group_codes) { + std::vector>* group_codes) { const size_t num_groups = frame_dim.num_groups; if (!cparams.centerfirst || (num_passes == 1 && num_groups == 1)) { return true; @@ -1665,11 +1705,11 @@ Status PermuteGroups(const CompressParams& cparams, permutation->push_back(pass_start + v); } } - std::vector new_group_codes(group_codes->size()); + std::vector> new_group_codes(group_codes->size()); for (size_t i = 0; i < permutation->size(); i++) { new_group_codes[(*permutation)[i]] = std::move((*group_codes)[i]); } - *group_codes = std::move(new_group_codes); + group_codes->swap(new_group_codes); return true; } @@ -1790,8 +1830,9 @@ size_t TOCSize(const std::vector& group_sizes) { return (toc_bits + 7) / 8; } -PaddedBytes EncodeTOC(const std::vector& group_sizes, AuxOut* aux_out) { - BitWriter writer; +PaddedBytes EncodeTOC(JxlMemoryManager* memory_manager, + const std::vector& group_sizes, AuxOut* aux_out) { + BitWriter writer{memory_manager}; BitWriter::Allotment allotment(&writer, 32 * group_sizes.size()); for (size_t group_size : group_sizes) { JXL_CHECK(U32Coder::Write(kTocDist, group_size, &writer)); @@ -1831,17 +1872,17 @@ size_t ComputeDcGlobalPadding(const std::vector& group_sizes, return group_data_offset - actual_offset; } -Status OutputGroups(std::vector&& group_codes, +Status OutputGroups(std::vector>&& group_codes, std::vector* group_sizes, JxlEncoderOutputProcessorWrapper* output_processor) { JXL_ASSERT(group_codes.size() >= 4); { - PaddedBytes dc_group = std::move(group_codes[1]).TakeBytes(); + PaddedBytes dc_group = std::move(*group_codes[1]).TakeBytes(); group_sizes->push_back(dc_group.size()); JXL_RETURN_IF_ERROR(AppendData(*output_processor, dc_group)); } for (size_t i = 3; i < group_codes.size(); ++i) { - PaddedBytes ac_group = std::move(group_codes[i]).TakeBytes(); + PaddedBytes ac_group = std::move(*group_codes[i]).TakeBytes(); group_sizes->push_back(ac_group.size()); JXL_RETURN_IF_ERROR(AppendData(*output_processor, ac_group)); } @@ -1879,7 +1920,8 @@ Status OutputAcGlobal(PassesEncoderState& enc_state, JxlEncoderOutputProcessorWrapper* output_processor, AuxOut* aux_out) { JXL_ASSERT(frame_dim.num_groups > 1); - BitWriter writer; + JxlMemoryManager* memory_manager = enc_state.memory_manager(); + BitWriter writer{memory_manager}; { size_t num_histo_bits = CeilLog2Nonzero(frame_dim.num_groups); BitWriter::Allotment allotment(&writer, num_histo_bits + 1); @@ -1917,14 +1959,15 @@ Status OutputAcGlobal(PassesEncoderState& enc_state, return true; } -Status EncodeFrameStreaming(const CompressParams& cparams, +Status EncodeFrameStreaming(JxlMemoryManager* memory_manager, + const CompressParams& cparams, const FrameInfo& frame_info, const CodecMetadata* metadata, JxlEncoderChunkedFrameAdapter& frame_data, const JxlCmsInterface& cms, ThreadPool* pool, JxlEncoderOutputProcessorWrapper* output_processor, AuxOut* aux_out) { - PassesEncoderState enc_state; + PassesEncoderState enc_state{memory_manager}; SetProgressiveMode(cparams, &enc_state.progressive_splitter); FrameHeader frame_header(metadata); std::unique_ptr jpeg_data; @@ -1936,7 +1979,7 @@ Status EncodeFrameStreaming(const CompressParams& cparams, frame_info, jpeg_data.get(), true, &frame_header)); const size_t num_passes = enc_state.progressive_splitter.GetNumPasses(); - ModularFrameEncoder enc_modular(frame_header, cparams, true); + ModularFrameEncoder enc_modular(memory_manager, frame_header, cparams, true); std::vector permutation; std::vector dc_group_order; size_t group_size = frame_header.ToFrameDimensions().group_dim; @@ -1947,8 +1990,8 @@ Status EncodeFrameStreaming(const CompressParams& cparams, size_t dc_group_xsize = DivCeil(frame_data.xsize, dc_group_size); size_t min_dc_global_size = 0; size_t group_data_offset = 0; - PaddedBytes frame_header_bytes; - PaddedBytes dc_global_bytes; + PaddedBytes frame_header_bytes{memory_manager}; + PaddedBytes dc_global_bytes{memory_manager}; std::vector group_sizes; size_t start_pos = output_processor->CurrentPosition(); for (size_t i = 0; i < dc_group_order.size(); ++i) { @@ -1970,14 +2013,14 @@ Status EncodeFrameStreaming(const CompressParams& cparams, enc_state.initialize_global_state = (i == 0); enc_state.dc_group_index = dc_ix; enc_state.histogram_idx = std::vector(group_xsize * group_ysize, i); - std::vector group_codes; + std::vector> group_codes; JXL_RETURN_IF_ERROR(ComputeEncodingData( cparams, frame_info, metadata, frame_data, jpeg_data.get(), x0, y0, xsize, ysize, cms, pool, frame_header, enc_modular, enc_state, &group_codes, aux_out)); JXL_ASSERT(enc_state.special_frames.empty()); if (i == 0) { - BitWriter writer; + BitWriter writer{memory_manager}; JXL_RETURN_IF_ERROR(WriteFrameHeader(frame_header, &writer, aux_out)); BitWriter::Allotment allotment(&writer, 8); writer.Write(1, 1); // write permutation @@ -1986,7 +2029,7 @@ Status EncodeFrameStreaming(const CompressParams& cparams, writer.ZeroPadToByte(); allotment.ReclaimAndCharge(&writer, kLayerHeader, aux_out); frame_header_bytes = std::move(writer).TakeBytes(); - dc_global_bytes = std::move(group_codes[0]).TakeBytes(); + dc_global_bytes = std::move(*group_codes[0]).TakeBytes(); ComputeGroupDataOffset(frame_header_bytes.size(), dc_global_bytes.size(), permutation.size(), min_dc_global_size, group_data_offset); @@ -2015,7 +2058,7 @@ Status EncodeFrameStreaming(const CompressParams& cparams, ComputeDcGlobalPadding(group_sizes, frame_header_bytes.size(), group_data_offset, min_dc_global_size); group_sizes[0] += padding_size; - PaddedBytes toc_bytes = EncodeTOC(group_sizes, aux_out); + PaddedBytes toc_bytes = EncodeTOC(memory_manager, group_sizes, aux_out); std::vector padding_bytes(padding_size); JXL_RETURN_IF_ERROR(AppendData(*output_processor, frame_header_bytes)); JXL_RETURN_IF_ERROR(AppendData(*output_processor, toc_bytes)); @@ -2029,16 +2072,16 @@ Status EncodeFrameStreaming(const CompressParams& cparams, return true; } -Status EncodeFrameOneShot(const CompressParams& cparams, +Status EncodeFrameOneShot(JxlMemoryManager* memory_manager, + const CompressParams& cparams, const FrameInfo& frame_info, const CodecMetadata* metadata, JxlEncoderChunkedFrameAdapter& frame_data, const JxlCmsInterface& cms, ThreadPool* pool, JxlEncoderOutputProcessorWrapper* output_processor, AuxOut* aux_out) { - PassesEncoderState enc_state; + PassesEncoderState enc_state{memory_manager}; SetProgressiveMode(cparams, &enc_state.progressive_splitter); - std::vector group_codes; FrameHeader frame_header(metadata); std::unique_ptr jpeg_data; if (frame_data.IsJPEG()) { @@ -2049,13 +2092,14 @@ Status EncodeFrameOneShot(const CompressParams& cparams, frame_info, jpeg_data.get(), false, &frame_header)); const size_t num_passes = enc_state.progressive_splitter.GetNumPasses(); - ModularFrameEncoder enc_modular(frame_header, cparams, false); + ModularFrameEncoder enc_modular(memory_manager, frame_header, cparams, false); + std::vector> group_codes; JXL_RETURN_IF_ERROR(ComputeEncodingData( cparams, frame_info, metadata, frame_data, jpeg_data.get(), 0, 0, frame_data.xsize, frame_data.ysize, cms, pool, frame_header, enc_modular, enc_state, &group_codes, aux_out)); - BitWriter writer; + BitWriter writer{memory_manager}; writer.AppendByteAligned(enc_state.special_frames); JXL_RETURN_IF_ERROR(WriteFrameHeader(frame_header, &writer, aux_out)); @@ -2075,7 +2119,8 @@ Status EncodeFrameOneShot(const CompressParams& cparams, } // namespace -Status EncodeFrame(const CompressParams& cparams_orig, +Status EncodeFrame(JxlMemoryManager* memory_manager, + const CompressParams& cparams_orig, const FrameInfo& frame_info, const CodecMetadata* metadata, JxlEncoderChunkedFrameAdapter& frame_data, const JxlCmsInterface& cms, ThreadPool* pool, @@ -2146,8 +2191,9 @@ Status EncodeFrame(const CompressParams& cparams_orig, size_t avail_out = output.size(); JxlEncoderOutputProcessorWrapper local_output; local_output.SetAvailOut(&next_out, &avail_out); - if (!EncodeFrame(all_params[task], frame_info, metadata, frame_data, - cms, nullptr, &local_output, aux_out)) { + if (!EncodeFrame(memory_manager, all_params[task], frame_info, + metadata, frame_data, cms, nullptr, &local_output, + aux_out)) { has_error = true; return; } @@ -2215,15 +2261,17 @@ Status EncodeFrame(const CompressParams& cparams_orig, } if (CanDoStreamingEncoding(cparams, frame_info, *metadata, frame_data)) { - return EncodeFrameStreaming(cparams, frame_info, metadata, frame_data, cms, - pool, output_processor, aux_out); + return EncodeFrameStreaming(memory_manager, cparams, frame_info, metadata, + frame_data, cms, pool, output_processor, + aux_out); } else { - return EncodeFrameOneShot(cparams, frame_info, metadata, frame_data, cms, - pool, output_processor, aux_out); + return EncodeFrameOneShot(memory_manager, cparams, frame_info, metadata, + frame_data, cms, pool, output_processor, aux_out); } } -Status EncodeFrame(const CompressParams& cparams_orig, +Status EncodeFrame(JxlMemoryManager* memory_manager, + const CompressParams& cparams_orig, const FrameInfo& frame_info, const CodecMetadata* metadata, const ImageBundle& ib, const JxlCmsInterface& cms, ThreadPool* pool, BitWriter* writer, AuxOut* aux_out) { @@ -2268,8 +2316,9 @@ Status EncodeFrame(const CompressParams& cparams_orig, size_t avail_out = output.size(); JxlEncoderOutputProcessorWrapper output_processor; output_processor.SetAvailOut(&next_out, &avail_out); - JXL_RETURN_IF_ERROR(EncodeFrame(cparams_orig, fi, metadata, frame_data, cms, - pool, &output_processor, aux_out)); + JXL_RETURN_IF_ERROR(EncodeFrame(memory_manager, cparams_orig, fi, metadata, + frame_data, cms, pool, &output_processor, + aux_out)); output_processor.SetFinalizedPosition(); output_processor.CopyOutput(output, next_out, avail_out); writer->AppendByteAligned(Bytes(output)); diff --git a/third_party/jpeg-xl/lib/jxl/enc_frame.h b/third_party/jpeg-xl/lib/jxl/enc_frame.h index a5b74cc648b3..896f50df4c0f 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_frame.h +++ b/third_party/jpeg-xl/lib/jxl/enc_frame.h @@ -7,6 +7,7 @@ #define LIB_JXL_ENC_FRAME_H_ #include +#include #include #include @@ -91,14 +92,16 @@ Status ParamsPostInit(CompressParams* p); // be processed in parallel by `pool`. metadata is the ImageMetadata encoded in // the codestream, and must be used for the FrameHeaders, do not use // ib.metadata. -Status EncodeFrame(const CompressParams& cparams_orig, +Status EncodeFrame(JxlMemoryManager* memory_manager, + const CompressParams& cparams_orig, const FrameInfo& frame_info, const CodecMetadata* metadata, JxlEncoderChunkedFrameAdapter& frame_data, const JxlCmsInterface& cms, ThreadPool* pool, JxlEncoderOutputProcessorWrapper* output_processor, AuxOut* aux_out); -Status EncodeFrame(const CompressParams& cparams_orig, +Status EncodeFrame(JxlMemoryManager* memory_manager, + const CompressParams& cparams_orig, const FrameInfo& frame_info, const CodecMetadata* metadata, const ImageBundle& ib, const JxlCmsInterface& cms, ThreadPool* pool, BitWriter* writer, AuxOut* aux_out); diff --git a/third_party/jpeg-xl/lib/jxl/enc_gaborish.cc b/third_party/jpeg-xl/lib/jxl/enc_gaborish.cc index ba1181e285e8..14c684d27a61 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_gaborish.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_gaborish.cc @@ -5,6 +5,8 @@ #include "lib/jxl/enc_gaborish.h" +#include + #include #include "lib/jxl/base/data_parallel.h" @@ -18,6 +20,7 @@ namespace jxl { Status GaborishInverse(Image3F* in_out, const Rect& rect, const float mul[3], ThreadPool* pool) { + JxlMemoryManager* memory_manager = in_out->memory_manager(); WeightsSymmetric5 weights[3]; // Only an approximation. One or even two 3x3, and rank-1 (separable) 5x5 // are insufficient. The numbers here have been obtained by butteraugli @@ -49,8 +52,9 @@ Status GaborishInverse(Image3F* in_out, const Rect& rect, const float mul[3], // have planes of different stride. Instead, we copy one plane in a temporary // image and reuse the existing planes of the in/out image. ImageF temp; - JXL_ASSIGN_OR_RETURN( - temp, ImageF::Create(in_out->Plane(2).xsize(), in_out->Plane(2).ysize())); + JXL_ASSIGN_OR_RETURN(temp, + ImageF::Create(memory_manager, in_out->Plane(2).xsize(), + in_out->Plane(2).ysize())); CopyImageTo(in_out->Plane(2), &temp); Rect xrect = rect.Extend(3, Rect(*in_out)); Symmetric5(in_out->Plane(0), xrect, weights[0], pool, &in_out->Plane(2), diff --git a/third_party/jpeg-xl/lib/jxl/enc_gaborish_test.cc b/third_party/jpeg-xl/lib/jxl/enc_gaborish_test.cc index 074c547287ab..75ad7475aa9b 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_gaborish_test.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_gaborish_test.cc @@ -17,6 +17,7 @@ #include "lib/jxl/image.h" #include "lib/jxl/image_ops.h" #include "lib/jxl/image_test_utils.h" +#include "lib/jxl/test_utils.h" #include "lib/jxl/testing.h" namespace jxl { @@ -43,7 +44,8 @@ void ConvolveGaborish(const ImageF& in, float weight1, float weight2, } void TestRoundTrip(const Image3F& in, float max_l1) { - JXL_ASSIGN_OR_DIE(Image3F fwd, Image3F::Create(in.xsize(), in.ysize())); + JXL_ASSIGN_OR_DIE(Image3F fwd, Image3F::Create(jxl::test::MemoryManager(), + in.xsize(), in.ysize())); ThreadPool* null_pool = nullptr; ConvolveGaborish(in.Plane(0), 0, 0, null_pool, &fwd.Plane(0)); ConvolveGaborish(in.Plane(1), 0, 0, null_pool, &fwd.Plane(1)); @@ -59,7 +61,8 @@ void TestRoundTrip(const Image3F& in, float max_l1) { } TEST(GaborishTest, TestZero) { - JXL_ASSIGN_OR_DIE(Image3F in, Image3F::Create(20, 20)); + JXL_ASSIGN_OR_DIE(Image3F in, + Image3F::Create(jxl::test::MemoryManager(), 20, 20)); ZeroFillImage(&in); TestRoundTrip(in, 0.0f); } @@ -67,7 +70,8 @@ TEST(GaborishTest, TestZero) { // Disabled: large difference. #if JXL_FALSE TEST(GaborishTest, TestDirac) { - JXL_ASSIGN_OR_DIE(Image3F in, Image3F::Create(20, 20)); + JXL_ASSIGN_OR_DIE(Image3F in, + Image3F::Create(jxl::test::MemoryManager(), 20, 20)); ZeroFillImage(&in); in.PlaneRow(1, 10)[10] = 10.0f; TestRoundTrip(in, 0.26f); @@ -75,7 +79,8 @@ TEST(GaborishTest, TestDirac) { #endif TEST(GaborishTest, TestFlat) { - JXL_ASSIGN_OR_DIE(Image3F in, Image3F::Create(20, 20)); + JXL_ASSIGN_OR_DIE(Image3F in, + Image3F::Create(jxl::test::MemoryManager(), 20, 20)); FillImage(1.0f, &in); TestRoundTrip(in, 1E-5f); } diff --git a/third_party/jpeg-xl/lib/jxl/enc_heuristics.cc b/third_party/jpeg-xl/lib/jxl/enc_heuristics.cc index 152468255f26..1a4ae8b6512f 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_heuristics.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_heuristics.cc @@ -6,6 +6,7 @@ #include "lib/jxl/enc_heuristics.h" #include +#include #include #include @@ -30,10 +31,11 @@ #include "lib/jxl/chroma_from_luma.h" #include "lib/jxl/coeff_order.h" #include "lib/jxl/coeff_order_fwd.h" +#include "lib/jxl/common.h" +#include "lib/jxl/dec_group.h" #include "lib/jxl/dec_xyb.h" #include "lib/jxl/enc_ac_strategy.h" #include "lib/jxl/enc_adaptive_quantization.h" -#include "lib/jxl/enc_ar_control_field.h" #include "lib/jxl/enc_cache.h" #include "lib/jxl/enc_chroma_from_luma.h" #include "lib/jxl/enc_gaborish.h" @@ -43,6 +45,7 @@ #include "lib/jxl/enc_patch_dictionary.h" #include "lib/jxl/enc_quant_weights.h" #include "lib/jxl/enc_splines.h" +#include "lib/jxl/epf.h" #include "lib/jxl/frame_dimensions.h" #include "lib/jxl/frame_header.h" #include "lib/jxl/image.h" @@ -193,26 +196,27 @@ void FindBestBlockEntropyModel(const CompressParams& cparams, const ImageI& rqf, namespace { -Status FindBestDequantMatrices(const CompressParams& cparams, +Status FindBestDequantMatrices(JxlMemoryManager* memory_manager, + const CompressParams& cparams, ModularFrameEncoder* modular_frame_encoder, DequantMatrices* dequant_matrices) { // TODO(veluca): quant matrices for no-gaborish. // TODO(veluca): heuristics for in-bitstream quant tables. *dequant_matrices = DequantMatrices(); - if (cparams.max_error_mode) { + if (cparams.max_error_mode || cparams.disable_percepeptual_optimizations) { + constexpr float kMSEWeights[3] = {0.001, 0.001, 0.001}; + const float* wp = cparams.disable_percepeptual_optimizations + ? kMSEWeights + : cparams.max_error; // Set numerators of all quantization matrices to constant values. - float weights[3][1] = {{1.0f / cparams.max_error[0]}, - {1.0f / cparams.max_error[1]}, - {1.0f / cparams.max_error[2]}}; + float weights[3][1] = {{1.0f / wp[0]}, {1.0f / wp[1]}, {1.0f / wp[2]}}; DctQuantWeightParams dct_params(weights); std::vector encodings(DequantMatrices::kNum, QuantEncoding::DCT(dct_params)); JXL_RETURN_IF_ERROR(DequantMatricesSetCustom(dequant_matrices, encodings, modular_frame_encoder)); - float dc_weights[3] = {1.0f / cparams.max_error[0], - 1.0f / cparams.max_error[1], - 1.0f / cparams.max_error[2]}; - DequantMatricesSetCustomDC(dequant_matrices, dc_weights); + float dc_weights[3] = {1.0f / wp[0], 1.0f / wp[1], 1.0f / wp[2]}; + DequantMatricesSetCustomDC(memory_manager, dequant_matrices, dc_weights); } return true; } @@ -265,6 +269,7 @@ void CreateMask(const ImageF& image, ImageF& mask) { Status DownsampleImage2_Sharper(const ImageF& input, ImageF* output) { const int64_t kernelx = 12; const int64_t kernely = 12; + JxlMemoryManager* memory_manager = input.memory_manager(); static const float kernel[144] = { -0.000314256996835, -0.000314256996835, -0.000897597057705, @@ -319,12 +324,14 @@ Status DownsampleImage2_Sharper(const ImageF& input, ImageF* output) { int64_t xsize = input.xsize(); int64_t ysize = input.ysize(); - JXL_ASSIGN_OR_RETURN(ImageF box_downsample, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF box_downsample, + ImageF::Create(memory_manager, xsize, ysize)); CopyImageTo(input, &box_downsample); JXL_ASSIGN_OR_RETURN(box_downsample, DownsampleImage(box_downsample, 2)); - JXL_ASSIGN_OR_RETURN(ImageF mask, ImageF::Create(box_downsample.xsize(), - box_downsample.ysize())); + JXL_ASSIGN_OR_RETURN(ImageF mask, + ImageF::Create(memory_manager, box_downsample.xsize(), + box_downsample.ysize())); CreateMask(box_downsample, mask); for (size_t y = 0; y < output->ysize(); y++) { @@ -391,9 +398,11 @@ Status DownsampleImage2_Sharper(const ImageF& input, ImageF* output) { Status DownsampleImage2_Sharper(Image3F* opsin) { // Allocate extra space to avoid a reallocation when padding. - JXL_ASSIGN_OR_RETURN(Image3F downsampled, - Image3F::Create(DivCeil(opsin->xsize(), 2) + kBlockDim, - DivCeil(opsin->ysize(), 2) + kBlockDim)); + JxlMemoryManager* memory_manager = opsin->memory_manager(); + JXL_ASSIGN_OR_RETURN( + Image3F downsampled, + Image3F::Create(memory_manager, DivCeil(opsin->xsize(), 2) + kBlockDim, + DivCeil(opsin->ysize(), 2) + kBlockDim)); downsampled.ShrinkTo(downsampled.xsize() - kBlockDim, downsampled.ysize() - kBlockDim); @@ -637,30 +646,37 @@ Status DownsampleImage2_Iterative(const ImageF& orig, ImageF* output) { int64_t ysize = orig.ysize(); int64_t xsize2 = DivCeil(orig.xsize(), 2); int64_t ysize2 = DivCeil(orig.ysize(), 2); + JxlMemoryManager* memory_manager = orig.memory_manager(); - JXL_ASSIGN_OR_RETURN(ImageF box_downsample, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF box_downsample, + ImageF::Create(memory_manager, xsize, ysize)); CopyImageTo(orig, &box_downsample); JXL_ASSIGN_OR_RETURN(box_downsample, DownsampleImage(box_downsample, 2)); - JXL_ASSIGN_OR_RETURN(ImageF mask, ImageF::Create(box_downsample.xsize(), - box_downsample.ysize())); + JXL_ASSIGN_OR_RETURN(ImageF mask, + ImageF::Create(memory_manager, box_downsample.xsize(), + box_downsample.ysize())); CreateMask(box_downsample, mask); output->ShrinkTo(xsize2, ysize2); // Initial result image using the sharper downsampling. // Allocate extra space to avoid a reallocation when padding. - JXL_ASSIGN_OR_RETURN(ImageF initial, - ImageF::Create(DivCeil(orig.xsize(), 2) + kBlockDim, - DivCeil(orig.ysize(), 2) + kBlockDim)); + JXL_ASSIGN_OR_RETURN( + ImageF initial, + ImageF::Create(memory_manager, DivCeil(orig.xsize(), 2) + kBlockDim, + DivCeil(orig.ysize(), 2) + kBlockDim)); initial.ShrinkTo(initial.xsize() - kBlockDim, initial.ysize() - kBlockDim); JXL_RETURN_IF_ERROR(DownsampleImage2_Sharper(orig, &initial)); - JXL_ASSIGN_OR_RETURN(ImageF down, - ImageF::Create(initial.xsize(), initial.ysize())); + JXL_ASSIGN_OR_RETURN( + ImageF down, + ImageF::Create(memory_manager, initial.xsize(), initial.ysize())); CopyImageTo(initial, &down); - JXL_ASSIGN_OR_RETURN(ImageF up, ImageF::Create(xsize, ysize)); - JXL_ASSIGN_OR_RETURN(ImageF corr, ImageF::Create(xsize, ysize)); - JXL_ASSIGN_OR_RETURN(ImageF corr2, ImageF::Create(xsize2, ysize2)); + JXL_ASSIGN_OR_RETURN(ImageF up, ImageF::Create(memory_manager, xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF corr, + ImageF::Create(memory_manager, xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF corr2, + ImageF::Create(memory_manager, xsize2, ysize2)); // In the weights map, relatively higher values will allow less ringing but // also less sharpness. With all constant values, it optimizes equally @@ -669,14 +685,16 @@ Status DownsampleImage2_Iterative(const ImageF& orig, ImageF* output) { // TODO(lode): Make use of the weights field for anti-ringing and clamping, // the values are all set to 1 for now, but it is intended to be used for // reducing ringing based on the mask, and taking clamping into account. - JXL_ASSIGN_OR_RETURN(ImageF weights, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF weights, + ImageF::Create(memory_manager, xsize, ysize)); for (size_t y = 0; y < weights.ysize(); y++) { auto* row = weights.Row(y); for (size_t x = 0; x < weights.xsize(); x++) { row[x] = 1; } } - JXL_ASSIGN_OR_RETURN(ImageF weights2, ImageF::Create(xsize2, ysize2)); + JXL_ASSIGN_OR_RETURN(ImageF weights2, + ImageF::Create(memory_manager, xsize2, ysize2)); AntiUpsample(weights, &weights2); const size_t num_it = 3; @@ -706,27 +724,32 @@ Status DownsampleImage2_Iterative(const ImageF& orig, ImageF* output) { } // namespace Status DownsampleImage2_Iterative(Image3F* opsin) { + JxlMemoryManager* memory_manager = opsin->memory_manager(); // Allocate extra space to avoid a reallocation when padding. - JXL_ASSIGN_OR_RETURN(Image3F downsampled, - Image3F::Create(DivCeil(opsin->xsize(), 2) + kBlockDim, - DivCeil(opsin->ysize(), 2) + kBlockDim)); + JXL_ASSIGN_OR_RETURN( + Image3F downsampled, + Image3F::Create(memory_manager, DivCeil(opsin->xsize(), 2) + kBlockDim, + DivCeil(opsin->ysize(), 2) + kBlockDim)); downsampled.ShrinkTo(downsampled.xsize() - kBlockDim, downsampled.ysize() - kBlockDim); - JXL_ASSIGN_OR_RETURN(Image3F rgb, - Image3F::Create(opsin->xsize(), opsin->ysize())); + JXL_ASSIGN_OR_RETURN( + Image3F rgb, + Image3F::Create(memory_manager, opsin->xsize(), opsin->ysize())); OpsinParams opsin_params; // TODO(user): use the ones that are actually used opsin_params.Init(kDefaultIntensityTarget); OpsinToLinear(*opsin, Rect(rgb), nullptr, &rgb, opsin_params); - JXL_ASSIGN_OR_RETURN(ImageF mask, - ImageF::Create(opsin->xsize(), opsin->ysize())); + JXL_ASSIGN_OR_RETURN( + ImageF mask, + ImageF::Create(memory_manager, opsin->xsize(), opsin->ysize())); ButteraugliParams butter_params; JXL_ASSIGN_OR_RETURN(std::unique_ptr butter, ButteraugliComparator::Make(rgb, butter_params)); JXL_RETURN_IF_ERROR(butter->Mask(&mask)); - JXL_ASSIGN_OR_RETURN(ImageF mask_fuzzy, - ImageF::Create(opsin->xsize(), opsin->ysize())); + JXL_ASSIGN_OR_RETURN( + ImageF mask_fuzzy, + ImageF::Create(memory_manager, opsin->xsize(), opsin->ysize())); for (size_t c = 0; c < 3; c++) { JXL_RETURN_IF_ERROR( @@ -736,10 +759,220 @@ Status DownsampleImage2_Iterative(Image3F* opsin) { return true; } +StatusOr ReconstructImage( + const FrameHeader& orig_frame_header, const PassesSharedState& shared, + const std::vector>& coeffs, ThreadPool* pool) { + const FrameDimensions& frame_dim = shared.frame_dim; + JxlMemoryManager* memory_manager = shared.memory_manager; + + FrameHeader frame_header = orig_frame_header; + frame_header.UpdateFlag(shared.image_features.patches.HasAny(), + FrameHeader::kPatches); + frame_header.UpdateFlag(shared.image_features.splines.HasAny(), + FrameHeader::kSplines); + frame_header.color_transform = ColorTransform::kNone; + + const bool is_gray = shared.metadata->m.color_encoding.IsGray(); + PassesDecoderState dec_state(memory_manager); + JXL_RETURN_IF_ERROR( + dec_state.output_encoding_info.SetFromMetadata(*shared.metadata)); + JXL_RETURN_IF_ERROR(dec_state.output_encoding_info.MaybeSetColorEncoding( + ColorEncoding::LinearSRGB(is_gray))); + dec_state.shared = &shared; + JXL_CHECK(dec_state.Init(frame_header)); + + ImageBundle decoded(memory_manager, &shared.metadata->m); + decoded.origin = frame_header.frame_origin; + JXL_ASSIGN_OR_RETURN( + Image3F tmp, + Image3F::Create(memory_manager, frame_dim.xsize, frame_dim.ysize)); + decoded.SetFromImage(std::move(tmp), + dec_state.output_encoding_info.color_encoding); + + PassesDecoderState::PipelineOptions options; + options.use_slow_render_pipeline = false; + options.coalescing = false; + options.render_spotcolors = false; + options.render_noise = true; + + JXL_CHECK(dec_state.PreparePipeline(frame_header, &shared.metadata->m, + &decoded, options)); + + hwy::AlignedUniquePtr group_dec_caches; + const auto allocate_storage = [&](const size_t num_threads) -> Status { + JXL_RETURN_IF_ERROR( + dec_state.render_pipeline->PrepareForThreads(num_threads, + /*use_group_ids=*/false)); + group_dec_caches = hwy::MakeUniqueAlignedArray(num_threads); + return true; + }; + std::atomic has_error{false}; + const auto process_group = [&](const uint32_t group_index, + const size_t thread) { + if (has_error) return; + if (frame_header.loop_filter.epf_iters > 0) { + ComputeSigma(frame_header.loop_filter, + frame_dim.BlockGroupRect(group_index), &dec_state); + } + RenderPipelineInput input = + dec_state.render_pipeline->GetInputBuffers(group_index, thread); + JXL_CHECK(DecodeGroupForRoundtrip(frame_header, coeffs, group_index, + &dec_state, &group_dec_caches[thread], + thread, input, nullptr, nullptr)); + if (!input.Done()) { + has_error = true; + return; + } + }; + JXL_CHECK(RunOnPool(pool, 0, frame_dim.num_groups, allocate_storage, + process_group, "ReconstructImage")); + if (has_error) return JXL_FAILURE("ReconstructImage failure"); + return std::move(*decoded.color()); +} + +float ComputeBlockL2Distance(const Image3F& a, const Image3F& b, + const ImageF& mask1x1, size_t by, size_t bx) { + Rect rect(bx * kBlockDim, by * kBlockDim, kBlockDim, kBlockDim, a.xsize(), + a.ysize()); + float err2 = 0.0f; + static const float kXYBWeights[] = {36.0f, 1.0f, 0.2f}; + for (size_t y = 0; y < rect.ysize(); ++y) { + const float* row_a_x = rect.ConstPlaneRow(a, 0, y); + const float* row_a_y = rect.ConstPlaneRow(a, 1, y); + const float* row_a_b = rect.ConstPlaneRow(a, 2, y); + const float* row_b_x = rect.ConstPlaneRow(b, 0, y); + const float* row_b_y = rect.ConstPlaneRow(b, 1, y); + const float* row_b_b = rect.ConstPlaneRow(b, 2, y); + const float* row_mask = rect.ConstRow(mask1x1, y); + + for (size_t x = 0; x < rect.xsize(); ++x) { + float mask = row_mask[x]; + for (size_t c = 0; c < 3; ++c) { + float diff_x = row_a_x[x] - row_b_x[x]; + float diff_y = row_a_y[x] - row_b_y[x]; + float diff_b = row_a_b[x] - row_b_b[x]; + err2 += (kXYBWeights[0] * diff_x * diff_x + + kXYBWeights[1] * diff_y * diff_y + + kXYBWeights[2] * diff_b * diff_b) * + mask * mask; + } + } + } + return err2; +} + +Status ComputeARHeuristics(const FrameHeader& frame_header, + PassesEncoderState* enc_state, + const Image3F& orig_opsin, const Rect& rect, + ThreadPool* pool) { + const CompressParams& cparams = enc_state->cparams; + PassesSharedState& shared = enc_state->shared; + const FrameDimensions& frame_dim = shared.frame_dim; + const ImageF& initial_quant_masking1x1 = enc_state->initial_quant_masking1x1; + ImageB& epf_sharpness = shared.epf_sharpness; + JxlMemoryManager* memory_manager = enc_state->memory_manager(); + + if (cparams.butteraugli_distance < kMinButteraugliForDynamicAR || + cparams.speed_tier > SpeedTier::kWombat || + frame_header.loop_filter.epf_iters == 0) { + FillPlane(static_cast(4), &epf_sharpness, Rect(epf_sharpness)); + return true; + } + + std::vector epf_steps; + if (cparams.butteraugli_distance > 4.5f) { + epf_steps.push_back(0); + epf_steps.push_back(4); + } else { + epf_steps.push_back(0); + epf_steps.push_back(3); + epf_steps.push_back(7); + } + static const int kNumEPFVals = 8; + std::array error_images; + for (uint8_t val : epf_steps) { + FillPlane(val, &epf_sharpness, Rect(epf_sharpness)); + JXL_ASSIGN_OR_RETURN( + Image3F decoded, + ReconstructImage(frame_header, shared, enc_state->coeffs, pool)); + JXL_ASSIGN_OR_RETURN(error_images[val], + ImageF::Create(memory_manager, frame_dim.xsize_blocks, + frame_dim.ysize_blocks)); + for (size_t by = 0; by < frame_dim.ysize_blocks; by++) { + float* error_row = error_images[val].Row(by); + for (size_t bx = 0; bx < frame_dim.xsize_blocks; bx++) { + error_row[bx] = ComputeBlockL2Distance( + orig_opsin, decoded, initial_quant_masking1x1, by, bx); + } + } + } + std::vector> histo(4, std::vector(kNumEPFVals)); + std::vector totals(4, 1); + for (size_t by = 0; by < frame_dim.ysize_blocks; by++) { + uint8_t* JXL_RESTRICT out_row = epf_sharpness.Row(by); + uint8_t* JXL_RESTRICT prev_row = epf_sharpness.Row(by > 0 ? by - 1 : 0); + for (size_t bx = 0; bx < frame_dim.xsize_blocks; bx++) { + uint8_t best_val = 0; + float best_error = std::numeric_limits::max(); + uint8_t top_val = by > 0 ? prev_row[bx] : 0; + uint8_t left_val = bx > 0 ? out_row[bx - 1] : 0; + float top_error = error_images[top_val].Row(by)[bx]; + float left_error = error_images[left_val].Row(by)[bx]; + for (uint8_t val : epf_steps) { + float error = error_images[val].Row(by)[bx]; + if (val == 0) { + error *= 0.97f; + } + if (error < best_error) { + best_val = val; + best_error = error; + } + } + if (best_error < 0.995 * std::min(top_error, left_error)) { + out_row[bx] = best_val; + } else if (top_error < left_error) { + out_row[bx] = top_val; + } else { + out_row[bx] = left_val; + } + int context = ((top_val > 3) ? 2 : 0) + ((left_val > 3) ? 1 : 0); + ++histo[context][out_row[bx]]; + ++totals[context]; + } + } + const float context_weight = + 0.14f + 0.007f * std::min(10.0f, cparams.butteraugli_distance); + for (size_t by = 0; by < frame_dim.ysize_blocks; by++) { + uint8_t* JXL_RESTRICT out_row = epf_sharpness.Row(by); + uint8_t* JXL_RESTRICT prev_row = epf_sharpness.Row(by > 0 ? by - 1 : 0); + for (size_t bx = 0; bx < frame_dim.xsize_blocks; bx++) { + uint8_t best_val = 0; + float best_error = std::numeric_limits::max(); + uint8_t top_val = by > 0 ? prev_row[bx] : 0; + uint8_t left_val = bx > 0 ? out_row[bx - 1] : 0; + int context = ((top_val > 3) ? 2 : 0) + ((left_val > 3) ? 1 : 0); + const auto& ctx_histo = histo[context]; + for (uint8_t val : epf_steps) { + float error = + error_images[val].Row(by)[bx] / + (1 + std::log1p(ctx_histo[val] * context_weight / totals[context])); + if (val == 0) error *= 0.93f; + if (error < best_error) { + best_val = val; + best_error = error; + } + } + out_row[bx] = best_val; + } + } + + return true; +} + Status LossyFrameHeuristics(const FrameHeader& frame_header, PassesEncoderState* enc_state, ModularFrameEncoder* modular_frame_encoder, - const Image3F* original_pixels, Image3F* opsin, + const Image3F* linear, Image3F* opsin, const Rect& rect, const JxlCmsInterface& cms, ThreadPool* pool, AuxOut* aux_out) { const CompressParams& cparams = enc_state->cparams; @@ -750,11 +983,12 @@ Status LossyFrameHeuristics(const FrameHeader& frame_header, ImageFeatures& image_features = shared.image_features; DequantMatrices& matrices = shared.matrices; Quantizer& quantizer = shared.quantizer; + ImageF& initial_quant_masking1x1 = enc_state->initial_quant_masking1x1; ImageI& raw_quant_field = shared.raw_quant_field; ColorCorrelationMap& cmap = shared.cmap; AcStrategyImage& ac_strategy = shared.ac_strategy; - ImageB& epf_sharpness = shared.epf_sharpness; BlockCtxMap& block_ctx_map = shared.block_ctx_map; + JxlMemoryManager* memory_manager = enc_state->memory_manager(); // Find and subtract splines. if (cparams.custom_splines.HasAny()) { @@ -799,27 +1033,33 @@ Status LossyFrameHeuristics(const FrameHeader& frame_header, // // output: Gaborished XYB, CfL, ACS, raw quant field, EPF control field. - ArControlFieldHeuristics ar_heuristics; AcStrategyHeuristics acs_heuristics(cparams); CfLHeuristics cfl_heuristics; ImageF initial_quant_field; ImageF initial_quant_masking; - ImageF initial_quant_masking1x1; // Compute an initial estimate of the quantization field. // Call InitialQuantField only in Hare mode or slower. Otherwise, rely // on simple heuristics in FindBestAcStrategy, or set a constant for Falcon // mode. - if (cparams.speed_tier > SpeedTier::kHare) { - JXL_ASSIGN_OR_RETURN( - initial_quant_field, - ImageF::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); - JXL_ASSIGN_OR_RETURN( - initial_quant_masking, - ImageF::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); + if (cparams.speed_tier > SpeedTier::kHare || + cparams.disable_percepeptual_optimizations) { + JXL_ASSIGN_OR_RETURN(initial_quant_field, + ImageF::Create(memory_manager, frame_dim.xsize_blocks, + frame_dim.ysize_blocks)); + JXL_ASSIGN_OR_RETURN(initial_quant_masking, + ImageF::Create(memory_manager, frame_dim.xsize_blocks, + frame_dim.ysize_blocks)); float q = 0.79 / cparams.butteraugli_distance; FillImage(q, &initial_quant_field); - FillImage(1.0f / (q + 0.001f), &initial_quant_masking); + float masking = 1.0f / (q + 0.001f); + FillImage(masking, &initial_quant_masking); + if (cparams.disable_percepeptual_optimizations) { + JXL_ASSIGN_OR_RETURN( + initial_quant_masking1x1, + ImageF::Create(memory_manager, frame_dim.xsize, frame_dim.ysize)); + FillImage(masking, &initial_quant_masking1x1); + } quantizer.ComputeGlobalScaleAndQuant(quant_dc, q, 0); } else { // Call this here, as it relies on pre-gaborish values. @@ -850,17 +1090,15 @@ Status LossyFrameHeuristics(const FrameHeader& frame_header, } if (initialize_global_state) { - JXL_RETURN_IF_ERROR( - FindBestDequantMatrices(cparams, modular_frame_encoder, &matrices)); + JXL_RETURN_IF_ERROR(FindBestDequantMatrices( + memory_manager, cparams, modular_frame_encoder, &matrices)); } - JXL_RETURN_IF_ERROR(cfl_heuristics.Init(rect)); + JXL_RETURN_IF_ERROR(cfl_heuristics.Init(memory_manager, rect)); acs_heuristics.Init(*opsin, rect, initial_quant_field, initial_quant_masking, initial_quant_masking1x1, &matrices); - std::atomic has_error{false}; auto process_tile = [&](const uint32_t tid, const size_t thread) { - if (has_error) return; size_t n_enc_tiles = DivCeil(frame_dim.xsize_blocks, kEncTileDimInBlocks); size_t tx = tid % n_enc_tiles; size_t ty = tid / n_enc_tiles; @@ -885,15 +1123,6 @@ Status LossyFrameHeuristics(const FrameHeader& frame_header, // Choose block sizes. acs_heuristics.ProcessRect(r, cmap, &ac_strategy, thread); - // Choose amount of post-processing smoothing. - // TODO(veluca): should this go *after* AdjustQuantField? - if (!ar_heuristics.RunRect(cparams, frame_header, r, *opsin, rect, - initial_quant_field, ac_strategy, &epf_sharpness, - thread)) { - has_error = true; - return; - } - // Always set the initial quant field, so we can compute the CfL map with // more accuracy. The initial quant field might change in slower modes, but // adjusting the quant field with butteraugli when all the other encoding @@ -915,18 +1144,18 @@ Status LossyFrameHeuristics(const FrameHeader& frame_header, DivCeil(frame_dim.ysize_blocks, kEncTileDimInBlocks), [&](const size_t num_threads) { acs_heuristics.PrepareForThreads(num_threads); - ar_heuristics.PrepareForThreads(num_threads); cfl_heuristics.PrepareForThreads(num_threads); return true; }, process_tile, "Enc Heuristics")); - if (has_error) return JXL_FAILURE("Enc Heuristics failed"); JXL_RETURN_IF_ERROR(acs_heuristics.Finalize(frame_dim, ac_strategy, aux_out)); // Refine quantization levels. - if (!streaming_mode) { - JXL_RETURN_IF_ERROR(FindBestQuantizer(frame_header, original_pixels, *opsin, + if (!streaming_mode && !cparams.disable_percepeptual_optimizations) { + ImageB& epf_sharpness = shared.epf_sharpness; + FillPlane(static_cast(4), &epf_sharpness, Rect(epf_sharpness)); + JXL_RETURN_IF_ERROR(FindBestQuantizer(frame_header, linear, *opsin, initial_quant_field, enc_state, cms, pool, aux_out)); } diff --git a/third_party/jpeg-xl/lib/jxl/enc_heuristics.h b/third_party/jpeg-xl/lib/jxl/enc_heuristics.h index 75a196748c50..a1843410ba1d 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_heuristics.h +++ b/third_party/jpeg-xl/lib/jxl/enc_heuristics.h @@ -32,10 +32,15 @@ class ModularFrameEncoder; Status LossyFrameHeuristics(const FrameHeader& frame_header, PassesEncoderState* enc_state, ModularFrameEncoder* modular_frame_encoder, - const Image3F* original_pixels, Image3F* opsin, + const Image3F* linear, Image3F* opsin, const Rect& rect, const JxlCmsInterface& cms, ThreadPool* pool, AuxOut* aux_out); +Status ComputeARHeuristics(const FrameHeader& frame_header, + PassesEncoderState* enc_state, + const Image3F& orig_opsin, const Rect& rect, + ThreadPool* pool); + void FindBestBlockEntropyModel(PassesEncoderState& enc_state); Status DownsampleImage2_Iterative(Image3F* opsin); diff --git a/third_party/jpeg-xl/lib/jxl/enc_icc_codec.cc b/third_party/jpeg-xl/lib/jxl/enc_icc_codec.cc index c3899600e51c..1e40f32b307d 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_icc_codec.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_icc_codec.cc @@ -5,14 +5,13 @@ #include "lib/jxl/enc_icc_codec.h" -#include +#include +#include #include #include -#include #include -#include "lib/jxl/base/byte_order.h" #include "lib/jxl/color_encoding_internal.h" #include "lib/jxl/enc_ans.h" #include "lib/jxl/enc_aux_out.h" @@ -33,9 +32,10 @@ namespace { // elements at the bottom of the rightmost column. The input is the input matrix // in scanline order, the output is the result matrix in scanline order, with // missing elements skipped over (this may occur at multiple positions). -void Unshuffle(uint8_t* data, size_t size, size_t width) { +void Unshuffle(JxlMemoryManager* memory_manager, uint8_t* data, size_t size, + size_t width) { size_t height = (size + width - 1) / width; // amount of rows of input - PaddedBytes result(size); + PaddedBytes result(memory_manager, size); // i = input index, j output index size_t s = 0; size_t j = 0; @@ -57,6 +57,7 @@ Status PredictAndShuffle(size_t stride, size_t width, int order, size_t num, const uint8_t* data, size_t size, size_t* pos, PaddedBytes* result) { JXL_RETURN_IF_ERROR(CheckOutOfBounds(*pos, num, size)); + JxlMemoryManager* memory_manager = result->memory_manager(); // Required by the specification, see decoder. stride * 4 must be < *pos. if (!*pos || ((*pos - 1u) >> 2u) < stride) { return JXL_FAILURE("Invalid stride"); @@ -69,7 +70,7 @@ Status PredictAndShuffle(size_t stride, size_t width, int order, size_t num, result->push_back(data[*pos + i] - predicted); } *pos += num; - if (width > 1) Unshuffle(result->data() + start, num, width); + if (width > 1) Unshuffle(memory_manager, result->data() + start, num, width); return true; } @@ -105,8 +106,9 @@ constexpr size_t kSizeLimit = std::numeric_limits::max() >> 2; // form that is easier to compress (more zeroes, ...) and will compress better // with brotli. Status PredictICC(const uint8_t* icc, size_t size, PaddedBytes* result) { - PaddedBytes commands; - PaddedBytes data; + JxlMemoryManager* memory_manager = result->memory_manager(); + PaddedBytes commands{memory_manager}; + PaddedBytes data{memory_manager}; static_assert(sizeof(size_t) >= 4, "size_t is too short"); // Fuzzer expects that PredictICC can accept any input, @@ -118,7 +120,7 @@ Status PredictICC(const uint8_t* icc, size_t size, PaddedBytes* result) { EncodeVarInt(size, result); // Header - PaddedBytes header; + PaddedBytes header{memory_manager}; header.append(ICCInitialHeaderPrediction()); EncodeUint32(0, size, &header); for (size_t i = 0; i < kICCHeaderSize && i < size; i++) { @@ -256,8 +258,8 @@ Status PredictICC(const uint8_t* icc, size_t size, PaddedBytes* result) { // but will not predict as well. while (pos <= size) { size_t last1 = pos; - PaddedBytes commands_add; - PaddedBytes data_add; + PaddedBytes commands_add{memory_manager}; + PaddedBytes data_add{memory_manager}; // This means the loop brought the position beyond the tag end. // If tagsize is nonsensical, any pos looks "ok-ish". @@ -285,7 +287,7 @@ Status PredictICC(const uint8_t* icc, size_t size, PaddedBytes* result) { data_add.push_back(icc[pos]); pos++; } - Unshuffle(data_add.data() + start, num, 2); + Unshuffle(memory_manager, data_add.data() + start, num, 2); } if (tag == kCurvTag && tag_sane() && pos + tagsize <= size && @@ -430,7 +432,8 @@ Status PredictICC(const uint8_t* icc, size_t size, PaddedBytes* result) { Status WriteICC(const IccBytes& icc, BitWriter* JXL_RESTRICT writer, size_t layer, AuxOut* JXL_RESTRICT aux_out) { if (icc.empty()) return JXL_FAILURE("ICC must be non-empty"); - PaddedBytes enc; + JxlMemoryManager* memory_manager = writer->memory_manager(); + PaddedBytes enc{memory_manager}; JXL_RETURN_IF_ERROR(PredictICC(icc.data(), icc.size(), &enc)); std::vector> tokens(1); BitWriter::Allotment allotment(writer, 128); @@ -448,8 +451,8 @@ Status WriteICC(const IccBytes& icc, BitWriter* JXL_RESTRICT writer, EntropyEncodingData code; std::vector context_map; params.force_huffman = true; - BuildAndEncodeHistograms(params, kNumICCContexts, tokens, &code, &context_map, - writer, layer, aux_out); + BuildAndEncodeHistograms(memory_manager, params, kNumICCContexts, tokens, + &code, &context_map, writer, layer, aux_out); WriteTokens(tokens[0], code, context_map, 0, writer, layer, aux_out); return true; } diff --git a/third_party/jpeg-xl/lib/jxl/enc_icc_codec.h b/third_party/jpeg-xl/lib/jxl/enc_icc_codec.h index a99e11b19cd7..956a33ff4d61 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_icc_codec.h +++ b/third_party/jpeg-xl/lib/jxl/enc_icc_codec.h @@ -8,9 +8,8 @@ // Compressed representation of ICC profiles. -#include -#include - +#include +#include #include #include "lib/jxl/base/compiler_specific.h" diff --git a/third_party/jpeg-xl/lib/jxl/enc_image_bundle.cc b/third_party/jpeg-xl/lib/jxl/enc_image_bundle.cc index db3c497cde4f..9d678b455c8e 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_image_bundle.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_image_bundle.cc @@ -6,6 +6,7 @@ #include "lib/jxl/enc_image_bundle.h" #include +#include #include #include @@ -27,8 +28,10 @@ Status ApplyColorTransform(const ColorEncoding& c_current, // Changing IsGray is probably a bug. JXL_CHECK(c_current.IsGray() == c_desired.IsGray()); bool is_gray = c_current.IsGray(); + JxlMemoryManager* memory_amanger = color.memory_manager(); if (out->xsize() < rect.xsize() || out->ysize() < rect.ysize()) { - JXL_ASSIGN_OR_RETURN(*out, Image3F::Create(rect.xsize(), rect.ysize())); + JXL_ASSIGN_OR_RETURN( + *out, Image3F::Create(memory_amanger, rect.xsize(), rect.ysize())); } else { out->ShrinkTo(rect.xsize(), rect.ysize()); } @@ -131,10 +134,12 @@ Status TransformIfNeeded(const ImageBundle& in, const ColorEncoding& c_desired, *out = ∈ return true; } + JxlMemoryManager* memory_manager = in.memory_manager(); // TODO(janwas): avoid copying via createExternal+copyBackToIO // instead of copy+createExternal+copyBackToIO - JXL_ASSIGN_OR_RETURN(Image3F color, - Image3F::Create(in.color().xsize(), in.color().ysize())); + JXL_ASSIGN_OR_RETURN( + Image3F color, + Image3F::Create(memory_manager, in.color().xsize(), in.color().ysize())); CopyImageTo(in.color(), &color); store->SetFromImage(std::move(color), in.c_current()); @@ -142,8 +147,9 @@ Status TransformIfNeeded(const ImageBundle& in, const ColorEncoding& c_desired, if (in.HasExtraChannels()) { std::vector extra_channels; for (const ImageF& extra_channel : in.extra_channels()) { - JXL_ASSIGN_OR_RETURN(ImageF ec, ImageF::Create(extra_channel.xsize(), - extra_channel.ysize())); + JXL_ASSIGN_OR_RETURN(ImageF ec, + ImageF::Create(memory_manager, extra_channel.xsize(), + extra_channel.ysize())); CopyImageTo(extra_channel, &ec); extra_channels.emplace_back(std::move(ec)); } diff --git a/third_party/jpeg-xl/lib/jxl/enc_linalg.cc b/third_party/jpeg-xl/lib/jxl/enc_linalg.cc index 452c17f4e82c..9557459febaa 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_linalg.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_linalg.cc @@ -17,7 +17,11 @@ void ConvertToDiagonal(const Matrix2x2& A, Vector2& diag, Matrix2x2& U) { JXL_ASSERT(std::abs(A[0][1] - A[1][0]) < 1e-15); #endif - if (std::abs(A[0][1]) < 1e-15) { + double b = -(A[0][0] + A[1][1]); + double c = A[0][0] * A[1][1] - A[0][1] * A[0][1]; + double d = b * b - 4.0 * c; + + if (std::abs(A[0][1]) < 1e-10 || d < 0) { // Already diagonal. diag[0] = A[0][0]; diag[1] = A[1][1]; @@ -25,9 +29,7 @@ void ConvertToDiagonal(const Matrix2x2& A, Vector2& diag, Matrix2x2& U) { U[0][1] = U[1][0] = 0.0; return; } - double b = -(A[0][0] + A[1][1]); - double c = A[0][0] * A[1][1] - A[0][1] * A[0][1]; - double d = b * b - 4.0 * c; + double sqd = std::sqrt(d); double l1 = (-b - sqd) * 0.5; double l2 = (-b + sqd) * 0.5; diff --git a/third_party/jpeg-xl/lib/jxl/enc_linalg_test.cc b/third_party/jpeg-xl/lib/jxl/enc_linalg_test.cc index 1056c4f06c14..b5678863dbf7 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_linalg_test.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_linalg_test.cc @@ -73,14 +73,24 @@ TEST(LinAlgTest, ConvertToDiagonal) { VerifyOrthogonal(U, 1e-12); VerifyMatrixEqual(A, MatMul(U, MatMul(Diagonal(d), Transpose(U))), 1e-12); } + { + Matrix2x2 A; + A[0] = {0.000208649, 1.13687e-12}; + A[1] = {1.13687e-12, 0.000208649}; + Matrix2x2 U; + Vector2 d; + ConvertToDiagonal(A, d, U); + VerifyOrthogonal(U, 1e-12); + VerifyMatrixEqual(A, MatMul(U, MatMul(Diagonal(d), Transpose(U))), 1e-11); + } Rng rng(0); - for (size_t i = 0; i < 100; ++i) { + for (size_t i = 0; i < 1000000; ++i) { Matrix2x2 A = RandomSymmetricMatrix(rng, -1.0, 1.0); Matrix2x2 U; Vector2 d; ConvertToDiagonal(A, d, U); VerifyOrthogonal(U, 1e-12); - VerifyMatrixEqual(A, MatMul(U, MatMul(Diagonal(d), Transpose(U))), 1e-12); + VerifyMatrixEqual(A, MatMul(U, MatMul(Diagonal(d), Transpose(U))), 5e-10); } } diff --git a/third_party/jpeg-xl/lib/jxl/enc_modular.cc b/third_party/jpeg-xl/lib/jxl/enc_modular.cc index 25464737b8f9..d935bcaa9299 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_modular.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_modular.cc @@ -5,6 +5,8 @@ #include "lib/jxl/enc_modular.h" +#include + #include #include #include @@ -425,10 +427,13 @@ void try_palettes(Image& gi, int& max_bitdepth, int& maxval, } // namespace -ModularFrameEncoder::ModularFrameEncoder(const FrameHeader& frame_header, +ModularFrameEncoder::ModularFrameEncoder(JxlMemoryManager* memory_manager, + const FrameHeader& frame_header, const CompressParams& cparams_orig, bool streaming_mode) - : frame_dim_(frame_header.ToFrameDimensions()), cparams_(cparams_orig) { + : memory_manager_(memory_manager), + frame_dim_(frame_header.ToFrameDimensions()), + cparams_(cparams_orig) { size_t num_streams = ModularStreamId::Num(frame_dim_, frame_header.passes.num_passes); if (cparams_.ModularPartIsLossless()) { @@ -461,7 +466,9 @@ ModularFrameEncoder::ModularFrameEncoder(const FrameHeader& frame_header, ModularOptions::TreeKind::kTrivialTreeNoPredictor; cparams_.options.nb_repeats = 0; } - stream_images_.resize(num_streams); + for (size_t i = 0; i < num_streams; ++i) { + stream_images_.emplace_back(memory_manager); + } // use a sensible default if nothing explicit is specified: // Squeeze for lossy, no squeeze for lossless @@ -624,6 +631,7 @@ Status ModularFrameEncoder::ComputeEncodingData( const Rect& frame_area_rect, PassesEncoderState* JXL_RESTRICT enc_state, const JxlCmsInterface& cms, ThreadPool* pool, AuxOut* aux_out, bool do_color) { + JxlMemoryManager* memory_manager = enc_state->memory_manager(); JXL_DEBUG_V(6, "Computing modular encoding data for frame %s", frame_header.DebugString().c_str()); @@ -682,8 +690,8 @@ Status ModularFrameEncoder::ComputeEncodingData( do_color ? metadata.bit_depth.bits_per_sample + (fp ? 0 : 1) : 0; Image& gi = stream_images_[0]; JXL_ASSIGN_OR_RETURN( - gi, Image::Create(xsize, ysize, metadata.bit_depth.bits_per_sample, - nb_chans)); + gi, Image::Create(memory_manager, xsize, ysize, + metadata.bit_depth.bits_per_sample, nb_chans)); int c = 0; if (cparams_.color_transform == ColorTransform::kXYB && cparams_.modular_mode == true) { @@ -696,11 +704,12 @@ Status ModularFrameEncoder::ComputeEncodingData( cparams_.butteraugli_distance = 0; } if (cparams_.manual_xyb_factors.size() == 3) { - DequantMatricesSetCustomDC(&enc_state->shared.matrices, + DequantMatricesSetCustomDC(memory_manager, &enc_state->shared.matrices, cparams_.manual_xyb_factors.data()); // TODO(jon): update max_bitdepth in this case } else { - DequantMatricesSetCustomDC(&enc_state->shared.matrices, enc_factors); + DequantMatricesSetCustomDC(memory_manager, &enc_state->shared.matrices, + enc_factors); max_bitdepth = 12; } } @@ -1238,6 +1247,7 @@ Status ModularFrameEncoder::ComputeTokens(ThreadPool* pool) { Status ModularFrameEncoder::EncodeGlobalInfo(bool streaming_mode, BitWriter* writer, AuxOut* aux_out) { + JxlMemoryManager* memory_manager = writer->memory_manager(); BitWriter::Allotment allotment(writer, 1); // If we are using brotli, or not using modular mode. if (tree_tokens_.empty() || tree_tokens_[0].empty()) { @@ -1254,9 +1264,9 @@ Status ModularFrameEncoder::EncodeGlobalInfo(bool streaming_mode, { EntropyEncodingData tree_code; std::vector tree_context_map; - BuildAndEncodeHistograms(params, kNumTreeContexts, tree_tokens_, &tree_code, - &tree_context_map, writer, kLayerModularTree, - aux_out); + BuildAndEncodeHistograms(memory_manager, params, kNumTreeContexts, + tree_tokens_, &tree_code, &tree_context_map, + writer, kLayerModularTree, aux_out); WriteTokens(tree_tokens_[0], tree_code, tree_context_map, 0, writer, kLayerModularTree, aux_out); } @@ -1264,8 +1274,9 @@ Status ModularFrameEncoder::EncodeGlobalInfo(bool streaming_mode, params.add_missing_symbols = streaming_mode; params.image_widths = image_widths_; // Write histograms. - BuildAndEncodeHistograms(params, (tree_.size() + 1) / 2, tokens_, &code_, - &context_map_, writer, kLayerModularGlobal, aux_out); + BuildAndEncodeHistograms(memory_manager, params, (tree_.size() + 1) / 2, + tokens_, &code_, &context_map_, writer, + kLayerModularGlobal, aux_out); return true; } @@ -1292,7 +1303,7 @@ Status ModularFrameEncoder::EncodeStream(BitWriter* writer, AuxOut* aux_out, void ModularFrameEncoder::ClearStreamData(const ModularStreamId& stream) { size_t stream_id = stream.ID(frame_dim_); - Image empty_image; + Image empty_image(stream_images_[stream_id].memory_manager()); std::swap(stream_images_[stream_id], empty_image); } @@ -1321,12 +1332,13 @@ Status ModularFrameEncoder::PrepareStreamParams(const Rect& rect, bool do_color, bool groupwise) { size_t stream_id = stream.ID(frame_dim_); Image& full_image = stream_images_[0]; + JxlMemoryManager* memory_manager = full_image.memory_manager(); const size_t xsize = rect.xsize(); const size_t ysize = rect.ysize(); Image& gi = stream_images_[stream_id]; if (stream_id > 0) { - JXL_ASSIGN_OR_RETURN(gi, - Image::Create(xsize, ysize, full_image.bitdepth, 0)); + JXL_ASSIGN_OR_RETURN(gi, Image::Create(memory_manager, xsize, ysize, + full_image.bitdepth, 0)); // start at the first bigger-than-frame_dim.group_dim non-metachannel size_t c = full_image.nb_meta_channels; if (!groupwise) { @@ -1344,7 +1356,8 @@ Status ModularFrameEncoder::PrepareStreamParams(const Rect& rect, rect.xsize() >> fc.hshift, rect.ysize() >> fc.vshift, fc.w, fc.h); if (r.xsize() == 0 || r.ysize() == 0) continue; gi_channel_[stream_id].push_back(c); - JXL_ASSIGN_OR_RETURN(Channel gc, Channel::Create(r.xsize(), r.ysize())); + JXL_ASSIGN_OR_RETURN( + Channel gc, Channel::Create(memory_manager, r.xsize(), r.ysize())); gc.hshift = fc.hshift; gc.vshift = fc.vshift; for (size_t y = 0; y < r.ysize(); ++y) { @@ -1490,6 +1503,7 @@ Status ModularFrameEncoder::AddVarDCTDC(const FrameHeader& frame_header, size_t group_index, bool nl_dc, PassesEncoderState* enc_state, bool jpeg_transcode) { + JxlMemoryManager* memory_manager = dc.memory_manager(); extra_dc_precision[group_index] = nl_dc ? 1 : 0; float mul = 1 << extra_dc_precision[group_index]; @@ -1515,8 +1529,9 @@ Status ModularFrameEncoder::AddVarDCTDC(const FrameHeader& frame_header, stream_options_[stream_id].histogram_params = stream_options_[0].histogram_params; - JXL_ASSIGN_OR_RETURN(stream_images_[stream_id], - Image::Create(r.xsize(), r.ysize(), 8, 3)); + JXL_ASSIGN_OR_RETURN( + stream_images_[stream_id], + Image::Create(memory_manager, r.xsize(), r.ysize(), 8, 3)); if (nl_dc && stream_options_[stream_id].tree_kind == ModularOptions::TreeKind::kGradientFixedDC) { JXL_ASSERT(frame_header.chroma_subsampling.Is444()); @@ -1637,6 +1652,7 @@ Status ModularFrameEncoder::AddVarDCTDC(const FrameHeader& frame_header, Status ModularFrameEncoder::AddACMetadata(const Rect& r, size_t group_index, bool jpeg_transcode, PassesEncoderState* enc_state) { + JxlMemoryManager* memory_manager = enc_state->memory_manager(); size_t stream_id = ModularStreamId::ACMetadata(group_index).ID(frame_dim_); stream_options_[stream_id].max_chan_size = 0xFFFFFF; if (stream_options_[stream_id].predictor != Predictor::Weighted) { @@ -1661,15 +1677,19 @@ Status ModularFrameEncoder::AddACMetadata(const Rect& r, size_t group_index, stream_options_[0].histogram_params; // YToX, YToB, ACS + QF, EPF Image& image = stream_images_[stream_id]; - JXL_ASSIGN_OR_RETURN(image, Image::Create(r.xsize(), r.ysize(), 8, 4)); + JXL_ASSIGN_OR_RETURN( + image, Image::Create(memory_manager, r.xsize(), r.ysize(), 8, 4)); static_assert(kColorTileDimInBlocks == 8, "Color tile size changed"); Rect cr(r.x0() >> 3, r.y0() >> 3, (r.xsize() + 7) >> 3, (r.ysize() + 7) >> 3); - JXL_ASSIGN_OR_RETURN(image.channel[0], - Channel::Create(cr.xsize(), cr.ysize(), 3, 3)); - JXL_ASSIGN_OR_RETURN(image.channel[1], - Channel::Create(cr.xsize(), cr.ysize(), 3, 3)); - JXL_ASSIGN_OR_RETURN(image.channel[2], - Channel::Create(r.xsize() * r.ysize(), 2, 0, 0)); + JXL_ASSIGN_OR_RETURN( + image.channel[0], + Channel::Create(memory_manager, cr.xsize(), cr.ysize(), 3, 3)); + JXL_ASSIGN_OR_RETURN( + image.channel[1], + Channel::Create(memory_manager, cr.xsize(), cr.ysize(), 3, 3)); + JXL_ASSIGN_OR_RETURN( + image.channel[2], + Channel::Create(memory_manager, r.xsize() * r.ysize(), 2, 0, 0)); ConvertPlaneAndClamp(cr, enc_state->shared.cmap.ytox_map, Rect(image.channel[0].plane), &image.channel[0].plane); ConvertPlaneAndClamp(cr, enc_state->shared.cmap.ytob_map, @@ -1696,8 +1716,8 @@ Status ModularFrameEncoder::AddACMetadata(const Rect& r, size_t group_index, } Status ModularFrameEncoder::EncodeQuantTable( - size_t size_x, size_t size_y, BitWriter* writer, - const QuantEncoding& encoding, size_t idx, + JxlMemoryManager* memory_manager, size_t size_x, size_t size_y, + BitWriter* writer, const QuantEncoding& encoding, size_t idx, ModularFrameEncoder* modular_frame_encoder) { JXL_ASSERT(encoding.qraw.qtable != nullptr); JXL_ASSERT(size_x * size_y * 3 == encoding.qraw.qtable->size()); @@ -1707,7 +1727,8 @@ Status ModularFrameEncoder::EncodeQuantTable( writer, nullptr, 0, ModularStreamId::QuantTable(idx))); return true; } - JXL_ASSIGN_OR_RETURN(Image image, Image::Create(size_x, size_y, 8, 3)); + JXL_ASSIGN_OR_RETURN(Image image, + Image::Create(memory_manager, size_x, size_y, 8, 3)); for (size_t c = 0; c < 3; c++) { for (size_t y = 0; y < size_y; y++) { int32_t* JXL_RESTRICT row = image.channel[c].Row(y); @@ -1728,7 +1749,9 @@ Status ModularFrameEncoder::AddQuantTable(size_t size_x, size_t size_y, JXL_ASSERT(encoding.qraw.qtable != nullptr); JXL_ASSERT(size_x * size_y * 3 == encoding.qraw.qtable->size()); Image& image = stream_images_[stream_id]; - JXL_ASSIGN_OR_RETURN(image, Image::Create(size_x, size_y, 8, 3)); + JxlMemoryManager* memory_manager = image.memory_manager(); + JXL_ASSIGN_OR_RETURN(image, + Image::Create(memory_manager, size_x, size_y, 8, 3)); for (size_t c = 0; c < 3; c++) { for (size_t y = 0; y < size_y; y++) { int32_t* JXL_RESTRICT row = image.channel[c].Row(y); diff --git a/third_party/jpeg-xl/lib/jxl/enc_modular.h b/third_party/jpeg-xl/lib/jxl/enc_modular.h index cac6483a00b2..8c0cf88ba28b 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_modular.h +++ b/third_party/jpeg-xl/lib/jxl/enc_modular.h @@ -7,6 +7,7 @@ #define LIB_JXL_ENC_MODULAR_H_ #include +#include #include #include @@ -37,7 +38,8 @@ struct AuxOut; class ModularFrameEncoder { public: - ModularFrameEncoder(const FrameHeader& frame_header, + ModularFrameEncoder(JxlMemoryManager* memory_manager, + const FrameHeader& frame_header, const CompressParams& cparams_orig, bool streaming_mode); Status ComputeEncodingData( const FrameHeader& frame_header, const ImageMetadata& metadata, @@ -77,7 +79,8 @@ class ModularFrameEncoder { // null, the quantization table in `encoding` is used, with dimensions `size_x // x size_y`. Otherwise, the table with ID `idx` is encoded from the given // `modular_frame_encoder`. - static Status EncodeQuantTable(size_t size_x, size_t size_y, + static Status EncodeQuantTable(JxlMemoryManager* memory_manager, + size_t size_x, size_t size_y, BitWriter* writer, const QuantEncoding& encoding, size_t idx, ModularFrameEncoder* modular_frame_encoder); @@ -88,11 +91,14 @@ class ModularFrameEncoder { std::vector ac_metadata_size; std::vector extra_dc_precision; + JxlMemoryManager* memory_manager() const { return memory_manager_; } + private: Status PrepareStreamParams(const Rect& rect, const CompressParams& cparams, int minShift, int maxShift, const ModularStreamId& stream, bool do_color, bool groupwise); + JxlMemoryManager* memory_manager_; std::vector stream_images_; std::vector stream_options_; std::vector quants_; diff --git a/third_party/jpeg-xl/lib/jxl/enc_noise.cc b/third_party/jpeg-xl/lib/jxl/enc_noise.cc index 80b90eed2c73..446a770c6836 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_noise.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_noise.cc @@ -5,19 +5,14 @@ #include "lib/jxl/enc_noise.h" -#include -#include - #include +#include +#include #include #include -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/chroma_from_luma.h" -#include "lib/jxl/convolve.h" #include "lib/jxl/enc_aux_out.h" #include "lib/jxl/enc_optimize.h" -#include "lib/jxl/image_ops.h" namespace jxl { namespace { diff --git a/third_party/jpeg-xl/lib/jxl/enc_params.h b/third_party/jpeg-xl/lib/jxl/enc_params.h index 5e3ff7789cb9..b51a5b1a2cff 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_params.h +++ b/third_party/jpeg-xl/lib/jxl/enc_params.h @@ -37,6 +37,8 @@ struct CompressParams { bool max_error_mode = false; float max_error[3] = {0.0, 0.0, 0.0}; + bool disable_percepeptual_optimizations = false; + SpeedTier speed_tier = SpeedTier::kSquirrel; int brotli_effort = -1; diff --git a/third_party/jpeg-xl/lib/jxl/enc_patch_dictionary.cc b/third_party/jpeg-xl/lib/jxl/enc_patch_dictionary.cc index 2586692c4eae..507e82caad04 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_patch_dictionary.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_patch_dictionary.cc @@ -5,6 +5,7 @@ #include "lib/jxl/enc_patch_dictionary.h" +#include #include #include @@ -46,6 +47,7 @@ void PatchDictionaryEncoder::Encode(const PatchDictionary& pdic, BitWriter* writer, size_t layer, AuxOut* aux_out) { JXL_ASSERT(pdic.HasAny()); + JxlMemoryManager* memory_manager = writer->memory_manager(); std::vector> tokens(1); size_t num_ec = pdic.shared_->metadata->m.num_extra_channels; @@ -106,9 +108,9 @@ void PatchDictionaryEncoder::Encode(const PatchDictionary& pdic, EntropyEncodingData codes; std::vector context_map; - BuildAndEncodeHistograms(HistogramParams(), kNumPatchDictionaryContexts, - tokens, &codes, &context_map, writer, layer, - aux_out); + BuildAndEncodeHistograms(memory_manager, HistogramParams(), + kNumPatchDictionaryContexts, tokens, &codes, + &context_map, writer, layer, aux_out); WriteTokens(tokens[0], codes, context_map, 0, writer, layer, aux_out); } @@ -137,13 +139,13 @@ void PatchDictionaryEncoder::SubtractFrom(const PatchDictionary& pdic, size_t iy = y - by; size_t ref = ref_pos.ref; const float* JXL_RESTRICT ref_rows[3] = { - pdic.shared_->reference_frames[ref].frame.color().ConstPlaneRow( + pdic.shared_->reference_frames[ref].frame->color()->ConstPlaneRow( 0, ref_pos.y0 + iy) + ref_pos.x0, - pdic.shared_->reference_frames[ref].frame.color().ConstPlaneRow( + pdic.shared_->reference_frames[ref].frame->color()->ConstPlaneRow( 1, ref_pos.y0 + iy) + ref_pos.x0, - pdic.shared_->reference_frames[ref].frame.color().ConstPlaneRow( + pdic.shared_->reference_frames[ref].frame->color()->ConstPlaneRow( 2, ref_pos.y0 + iy) + ref_pos.x0, }; @@ -213,6 +215,7 @@ StatusOr> FindTextLikePatches( std::vector info; if (state->cparams.patches == Override::kOff) return info; const auto& frame_dim = state->shared.frame_dim; + JxlMemoryManager* memory_manager = opsin.memory_manager(); PatchColorspaceInfo pci(is_xyb); float kSimilarThreshold = 0.8f; @@ -258,9 +261,10 @@ StatusOr> FindTextLikePatches( // Look for kPatchSide size squares, naturally aligned, that all have the same // pixel values. - JXL_ASSIGN_OR_RETURN(ImageB is_screenshot_like, - ImageB::Create(DivCeil(frame_dim.xsize, kPatchSide), - DivCeil(frame_dim.ysize, kPatchSide))); + JXL_ASSIGN_OR_RETURN( + ImageB is_screenshot_like, + ImageB::Create(memory_manager, DivCeil(frame_dim.xsize, kPatchSide), + DivCeil(frame_dim.ysize, kPatchSide))); ZeroFillImage(&is_screenshot_like); uint8_t* JXL_RESTRICT screenshot_row = is_screenshot_like.Row(0); const size_t screenshot_stride = is_screenshot_like.PixelsPerRow(); @@ -314,11 +318,13 @@ StatusOr> FindTextLikePatches( } // Search for "similar enough" pixels near the screenshot-like areas. - JXL_ASSIGN_OR_RETURN(ImageB is_background, - ImageB::Create(frame_dim.xsize, frame_dim.ysize)); + JXL_ASSIGN_OR_RETURN( + ImageB is_background, + ImageB::Create(memory_manager, frame_dim.xsize, frame_dim.ysize)); ZeroFillImage(&is_background); - JXL_ASSIGN_OR_RETURN(Image3F background, - Image3F::Create(frame_dim.xsize, frame_dim.ysize)); + JXL_ASSIGN_OR_RETURN( + Image3F background, + Image3F::Create(memory_manager, frame_dim.xsize, frame_dim.ysize)); ZeroFillImage(&background); constexpr size_t kDistanceLimit = 50; float* JXL_RESTRICT background_rows[3] = { @@ -394,7 +400,8 @@ StatusOr> FindTextLikePatches( } else { JXL_RETURN_IF_ERROR(DumpImage(cparams, "background", background)); } - JXL_ASSIGN_OR_RETURN(ccs, ImageF::Create(frame_dim.xsize, frame_dim.ysize)); + JXL_ASSIGN_OR_RETURN( + ccs, ImageF::Create(memory_manager, frame_dim.xsize, frame_dim.ysize)); ZeroFillImage(&ccs); paint_ccs = true; } @@ -414,8 +421,9 @@ StatusOr> FindTextLikePatches( // Find small CC outside the "similar enough" areas, compute bounding boxes, // and run heuristics to exclude some patches. - JXL_ASSIGN_OR_RETURN(ImageB visited, - ImageB::Create(frame_dim.xsize, frame_dim.ysize)); + JXL_ASSIGN_OR_RETURN( + ImageB visited, + ImageB::Create(memory_manager, frame_dim.xsize, frame_dim.ysize)); ZeroFillImage(&visited); uint8_t* JXL_RESTRICT visited_row = visited.Row(0); const size_t visited_stride = visited.PixelsPerRow(); @@ -580,6 +588,7 @@ Status FindBestPatchDictionary(const Image3F& opsin, JXL_ASSIGN_OR_RETURN( std::vector info, FindTextLikePatches(state->cparams, opsin, state, pool, aux_out, is_xyb)); + JxlMemoryManager* memory_manager = opsin.memory_manager(); // TODO(veluca): this doesn't work if both dots and patches are enabled. // For now, since dots and patches are not likely to occur in the same kind of @@ -588,7 +597,8 @@ Status FindBestPatchDictionary(const Image3F& opsin, ApplyOverride( state->cparams.dots, state->cparams.speed_tier <= SpeedTier::kSquirrel && - state->cparams.butteraugli_distance >= kMinButteraugliForDots)) { + state->cparams.butteraugli_distance >= kMinButteraugliForDots && + !state->cparams.disable_percepeptual_optimizations)) { Rect rect(0, 0, state->shared.frame_dim.xsize, state->shared.frame_dim.ysize); JXL_ASSIGN_OR_RETURN(info, FindDotDictionary(state->cparams, opsin, rect, @@ -626,7 +636,8 @@ Status FindBestPatchDictionary(const Image3F& opsin, ref_xsize = ref_xsize * kBinPackingSlackness + 1; ref_ysize = ref_ysize * kBinPackingSlackness + 1; - JXL_ASSIGN_OR_RETURN(ImageB occupied, ImageB::Create(ref_xsize, ref_ysize)); + JXL_ASSIGN_OR_RETURN(ImageB occupied, + ImageB::Create(memory_manager, ref_xsize, ref_ysize)); ZeroFillImage(&occupied); uint8_t* JXL_RESTRICT occupied_rows = occupied.Row(0); size_t occupied_stride = occupied.PixelsPerRow(); @@ -691,7 +702,7 @@ Status FindBestPatchDictionary(const Image3F& opsin, ref_ysize = max_y; JXL_ASSIGN_OR_RETURN(Image3F reference_frame, - Image3F::Create(ref_xsize, ref_ysize)); + Image3F::Create(memory_manager, ref_xsize, ref_ysize)); // TODO(veluca): figure out a better way to fill the image. ZeroFillImage(&reference_frame); std::vector positions; @@ -755,6 +766,7 @@ Status RoundtripPatchFrame(Image3F* reference_frame, PassesEncoderState* JXL_RESTRICT state, int idx, CompressParams& cparams, const JxlCmsInterface& cms, ThreadPool* pool, AuxOut* aux_out, bool subtract) { + JxlMemoryManager* memory_manager = state->memory_manager(); FrameInfo patch_frame_info; cparams.resampling = 1; cparams.ec_resampling = 1; @@ -770,7 +782,7 @@ Status RoundtripPatchFrame(Image3F* reference_frame, patch_frame_info.save_as_reference = idx; // always saved. patch_frame_info.frame_type = FrameType::kReferenceOnly; patch_frame_info.save_before_color_transform = true; - ImageBundle ib(&state->shared.metadata->m); + ImageBundle ib(memory_manager, &state->shared.metadata->m); // TODO(veluca): metadata.color_encoding is a lie: ib is in XYB, but there is // no simple way to express that yet. patch_frame_info.ib_needs_color_transform = false; @@ -783,7 +795,8 @@ Status RoundtripPatchFrame(Image3F* reference_frame, std::vector extra_channels; extra_channels.reserve(ib.metadata()->extra_channel_info.size()); for (size_t i = 0; i < ib.metadata()->extra_channel_info.size(); i++) { - JXL_ASSIGN_OR_RETURN(ImageF ch, ImageF::Create(ib.xsize(), ib.ysize())); + JXL_ASSIGN_OR_RETURN( + ImageF ch, ImageF::Create(memory_manager, ib.xsize(), ib.ysize())); extra_channels.emplace_back(std::move(ch)); // Must initialize the image with data to not affect blending with // uninitialized memory. @@ -792,11 +805,12 @@ Status RoundtripPatchFrame(Image3F* reference_frame, } ib.SetExtraChannels(std::move(extra_channels)); } - auto special_frame = std::unique_ptr(new BitWriter()); + auto special_frame = + std::unique_ptr(new BitWriter(memory_manager)); AuxOut patch_aux_out; - JXL_CHECK(EncodeFrame(cparams, patch_frame_info, state->shared.metadata, ib, - cms, pool, special_frame.get(), - aux_out ? &patch_aux_out : nullptr)); + JXL_CHECK(EncodeFrame( + memory_manager, cparams, patch_frame_info, state->shared.metadata, ib, + cms, pool, special_frame.get(), aux_out ? &patch_aux_out : nullptr)); if (aux_out) { for (const auto& l : patch_aux_out.layers) { aux_out->layers[kLayerDictionary].Assimilate(l); @@ -805,8 +819,8 @@ Status RoundtripPatchFrame(Image3F* reference_frame, const Span encoded = special_frame->GetSpan(); state->special_frames.emplace_back(std::move(special_frame)); if (subtract) { - ImageBundle decoded(&state->shared.metadata->m); - PassesDecoderState dec_state; + ImageBundle decoded(memory_manager, &state->shared.metadata->m); + PassesDecoderState dec_state(memory_manager); JXL_CHECK(dec_state.output_encoding_info.SetFromMetadata( *state->shared.metadata)); const uint8_t* frame_start = encoded.data(); @@ -817,7 +831,7 @@ Status RoundtripPatchFrame(Image3F* reference_frame, frame_start += decoded.decoded_bytes(); encoded_size -= decoded.decoded_bytes(); size_t ref_xsize = - dec_state.shared_storage.reference_frames[idx].frame.color()->xsize(); + dec_state.shared_storage.reference_frames[idx].frame->color()->xsize(); // if the frame itself uses patches, we need to decode another frame if (!ref_xsize) { JXL_CHECK(DecodeFrame(&dec_state, pool, frame_start, encoded_size, @@ -828,7 +842,7 @@ Status RoundtripPatchFrame(Image3F* reference_frame, state->shared.reference_frames[idx] = std::move(dec_state.shared_storage.reference_frames[idx]); } else { - state->shared.reference_frames[idx].frame = std::move(ib); + *state->shared.reference_frames[idx].frame = std::move(ib); } return true; } diff --git a/third_party/jpeg-xl/lib/jxl/enc_quant_weights.cc b/third_party/jpeg-xl/lib/jxl/enc_quant_weights.cc index 978dfd592562..eefdecc89889 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_quant_weights.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_quant_weights.cc @@ -5,10 +5,11 @@ #include "lib/jxl/enc_quant_weights.h" +#include #include -#include #include +#include #include "lib/jxl/base/common.h" #include "lib/jxl/base/status.h" @@ -37,7 +38,8 @@ Status EncodeDctParams(const DctQuantWeightParams& params, BitWriter* writer) { return true; } -Status EncodeQuant(const QuantEncoding& encoding, size_t idx, size_t size_x, +Status EncodeQuant(JxlMemoryManager* memory_manager, + const QuantEncoding& encoding, size_t idx, size_t size_x, size_t size_y, BitWriter* writer, ModularFrameEncoder* modular_frame_encoder) { writer->Write(kLog2NumQuantModes, encoding.mode); @@ -90,7 +92,8 @@ Status EncodeQuant(const QuantEncoding& encoding, size_t idx, size_t size_x, } case QuantEncoding::kQuantModeRAW: { JXL_RETURN_IF_ERROR(ModularFrameEncoder::EncodeQuantTable( - size_x, size_y, writer, encoding, idx, modular_frame_encoder)); + memory_manager, size_x, size_y, writer, encoding, idx, + modular_frame_encoder)); break; } case QuantEncoding::kQuantModeAFV: { @@ -110,7 +113,8 @@ Status EncodeQuant(const QuantEncoding& encoding, size_t idx, size_t size_x, } // namespace -Status DequantMatricesEncode(const DequantMatrices& matrices, BitWriter* writer, +Status DequantMatricesEncode(JxlMemoryManager* memory_manager, + const DequantMatrices& matrices, BitWriter* writer, size_t layer, AuxOut* aux_out, ModularFrameEncoder* modular_frame_encoder) { bool all_default = true; @@ -128,7 +132,7 @@ Status DequantMatricesEncode(const DequantMatrices& matrices, BitWriter* writer, if (!all_default) { for (size_t i = 0; i < encodings.size(); i++) { JXL_RETURN_IF_ERROR(EncodeQuant( - encodings[i], i, DequantMatrices::required_size_x[i], + memory_manager, encodings[i], i, DequantMatrices::required_size_x[i], DequantMatrices::required_size_y[i], writer, modular_frame_encoder)); } } @@ -157,10 +161,11 @@ Status DequantMatricesEncodeDC(const DequantMatrices& matrices, return true; } -void DequantMatricesSetCustomDC(DequantMatrices* matrices, const float* dc) { +void DequantMatricesSetCustomDC(JxlMemoryManager* memory_manager, + DequantMatrices* matrices, const float* dc) { matrices->SetDCQuant(dc); // Roundtrip encode/decode DC to ensure same values as decoder. - BitWriter writer; + BitWriter writer{memory_manager}; JXL_CHECK(DequantMatricesEncodeDC(*matrices, &writer, 0, nullptr)); writer.ZeroPadToByte(); BitReader br(writer.GetSpan()); @@ -169,30 +174,35 @@ void DequantMatricesSetCustomDC(DequantMatrices* matrices, const float* dc) { JXL_CHECK(br.Close()); } -void DequantMatricesScaleDC(DequantMatrices* matrices, const float scale) { +void DequantMatricesScaleDC(JxlMemoryManager* memory_manager, + DequantMatrices* matrices, const float scale) { float dc[3]; for (size_t c = 0; c < 3; ++c) { dc[c] = matrices->InvDCQuant(c) * (1.0f / scale); } - DequantMatricesSetCustomDC(matrices, dc); + DequantMatricesSetCustomDC(memory_manager, matrices, dc); } -void DequantMatricesRoundtrip(DequantMatrices* matrices) { +void DequantMatricesRoundtrip(JxlMemoryManager* memory_manager, + DequantMatrices* matrices) { // Do not pass modular en/decoder, as they only change entropy and not // values. - BitWriter writer; - JXL_CHECK(DequantMatricesEncode(*matrices, &writer, 0, nullptr)); + BitWriter writer{memory_manager}; + JXL_CHECK( + DequantMatricesEncode(memory_manager, *matrices, &writer, 0, nullptr)); writer.ZeroPadToByte(); BitReader br(writer.GetSpan()); // Called only in the encoder: should fail only for programmer errors. - JXL_CHECK(matrices->Decode(&br)); + JXL_CHECK(matrices->Decode(memory_manager, &br)); JXL_CHECK(br.Close()); } Status DequantMatricesSetCustom(DequantMatrices* matrices, const std::vector& encodings, ModularFrameEncoder* encoder) { + JXL_CHECK(encoder != nullptr); JXL_ASSERT(encodings.size() == DequantMatrices::kNum); + JxlMemoryManager* memory_manager = encoder->memory_manager(); matrices->SetEncodings(encodings); for (size_t i = 0; i < encodings.size(); i++) { if (encodings[i].mode == QuantEncodingInternal::kQuantModeRAW) { @@ -201,7 +211,7 @@ Status DequantMatricesSetCustom(DequantMatrices* matrices, DequantMatrices::required_size_y[i] * kBlockDim, encodings[i], i)); } } - DequantMatricesRoundtrip(matrices); + DequantMatricesRoundtrip(memory_manager, matrices); return true; } diff --git a/third_party/jpeg-xl/lib/jxl/enc_quant_weights.h b/third_party/jpeg-xl/lib/jxl/enc_quant_weights.h index 897ace6c7ee2..e11bcf52ccab 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_quant_weights.h +++ b/third_party/jpeg-xl/lib/jxl/enc_quant_weights.h @@ -6,6 +6,8 @@ #ifndef LIB_JXL_ENC_QUANT_WEIGHTS_H_ #define LIB_JXL_ENC_QUANT_WEIGHTS_H_ +#include + #include #include @@ -18,23 +20,27 @@ struct AuxOut; struct BitWriter; Status DequantMatricesEncode( - const DequantMatrices& matrices, BitWriter* writer, size_t layer, - AuxOut* aux_out, ModularFrameEncoder* modular_frame_encoder = nullptr); + JxlMemoryManager* memory_manager, const DequantMatrices& matrices, + BitWriter* writer, size_t layer, AuxOut* aux_out, + ModularFrameEncoder* modular_frame_encoder = nullptr); Status DequantMatricesEncodeDC(const DequantMatrices& matrices, BitWriter* writer, size_t layer, AuxOut* aux_out); // For consistency with QuantEncoding, higher values correspond to more // precision. -void DequantMatricesSetCustomDC(DequantMatrices* matrices, const float* dc); +void DequantMatricesSetCustomDC(JxlMemoryManager* memory_manager, + DequantMatrices* matrices, const float* dc); -void DequantMatricesScaleDC(DequantMatrices* matrices, float scale); +void DequantMatricesScaleDC(JxlMemoryManager* memory_manager, + DequantMatrices* matrices, float scale); Status DequantMatricesSetCustom(DequantMatrices* matrices, const std::vector& encodings, ModularFrameEncoder* encoder); // Roundtrip encode/decode the matrices to ensure same values as decoder. -void DequantMatricesRoundtrip(DequantMatrices* matrices); +void DequantMatricesRoundtrip(JxlMemoryManager* memory_manager, + DequantMatrices* matrices); } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_splines.cc b/third_party/jpeg-xl/lib/jxl/enc_splines.cc index 186f19da9344..ec35accd60c2 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_splines.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_splines.cc @@ -3,14 +3,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#include - -#include "lib/jxl/ans_params.h" #include "lib/jxl/base/status.h" -#include "lib/jxl/chroma_from_luma.h" -#include "lib/jxl/dct_scales.h" #include "lib/jxl/enc_ans.h" -#include "lib/jxl/entropy_coder.h" #include "lib/jxl/pack_signed.h" #include "lib/jxl/splines.h" @@ -84,8 +78,9 @@ void EncodeSplines(const Splines& splines, BitWriter* writer, EntropyEncodingData codes; std::vector context_map; - BuildAndEncodeHistograms(histogram_params, kNumSplineContexts, tokens, &codes, - &context_map, writer, layer, aux_out); + BuildAndEncodeHistograms(writer->memory_manager(), histogram_params, + kNumSplineContexts, tokens, &codes, &context_map, + writer, layer, aux_out); WriteTokens(tokens[0], codes, context_map, 0, writer, layer, aux_out); } diff --git a/third_party/jpeg-xl/lib/jxl/enc_toc.cc b/third_party/jpeg-xl/lib/jxl/enc_toc.cc index 4ecba8fdb10b..19b6d7023f26 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_toc.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_toc.cc @@ -5,10 +5,9 @@ #include "lib/jxl/enc_toc.h" -#include +#include #include "lib/jxl/base/common.h" -#include "lib/jxl/coeff_order.h" #include "lib/jxl/enc_aux_out.h" #include "lib/jxl/enc_coeff_order.h" #include "lib/jxl/field_encodings.h" @@ -16,9 +15,10 @@ #include "lib/jxl/toc.h" namespace jxl { -Status WriteGroupOffsets(const std::vector& group_codes, - const std::vector& permutation, - BitWriter* JXL_RESTRICT writer, AuxOut* aux_out) { +Status WriteGroupOffsets( + const std::vector>& group_codes, + const std::vector& permutation, + BitWriter* JXL_RESTRICT writer, AuxOut* aux_out) { BitWriter::Allotment allotment(writer, MaxBits(group_codes.size())); if (!permutation.empty() && !group_codes.empty()) { // Don't write a permutation at all for an empty group_codes. @@ -33,8 +33,8 @@ Status WriteGroupOffsets(const std::vector& group_codes, writer->ZeroPadToByte(); // before TOC entries for (const auto& bw : group_codes) { - JXL_ASSERT(bw.BitsWritten() % kBitsPerByte == 0); - const size_t group_size = bw.BitsWritten() / kBitsPerByte; + JXL_ASSERT(bw->BitsWritten() % kBitsPerByte == 0); + const size_t group_size = bw->BitsWritten() / kBitsPerByte; JXL_RETURN_IF_ERROR(U32Coder::Write(kTocDist, group_size, writer)); } writer->ZeroPadToByte(); // before first group diff --git a/third_party/jpeg-xl/lib/jxl/enc_toc.h b/third_party/jpeg-xl/lib/jxl/enc_toc.h index aa222141bec0..574439c6ced0 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_toc.h +++ b/third_party/jpeg-xl/lib/jxl/enc_toc.h @@ -6,9 +6,7 @@ #ifndef LIB_JXL_ENC_TOC_H_ #define LIB_JXL_ENC_TOC_H_ -#include -#include - +#include #include #include "lib/jxl/base/compiler_specific.h" @@ -22,9 +20,10 @@ struct AuxOut; // Writes the group offsets. If the permutation vector is empty, the identity // permutation will be used. -Status WriteGroupOffsets(const std::vector& group_codes, - const std::vector& permutation, - BitWriter* JXL_RESTRICT writer, AuxOut* aux_out); +Status WriteGroupOffsets( + const std::vector>& group_codes, + const std::vector& permutation, + BitWriter* JXL_RESTRICT writer, AuxOut* aux_out); } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_xyb.cc b/third_party/jpeg-xl/lib/jxl/enc_xyb.cc index 89bb4de37ccc..ea044aa22f10 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_xyb.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_xyb.cc @@ -5,6 +5,8 @@ #include "lib/jxl/enc_xyb.h" +#include + #include #include #include @@ -220,7 +222,9 @@ StatusOr TransformToLinearRGB(const Image3F& in, ColorSpaceTransform c_transform(cms); bool is_gray = color_encoding.IsGray(); const ColorEncoding& c_desired = ColorEncoding::LinearSRGB(is_gray); - JXL_ASSIGN_OR_RETURN(Image3F out, Image3F::Create(in.xsize(), in.ysize())); + JxlMemoryManager* memory_manager = in.memory_manager(); + JXL_ASSIGN_OR_RETURN(Image3F out, + Image3F::Create(memory_manager, in.xsize(), in.ysize())); std::atomic has_error{false}; JXL_CHECK(RunOnPool( pool, 0, in.ysize(), @@ -401,7 +405,9 @@ void ToXYB(const ColorEncoding& c_current, float intensity_target, Status ToXYB(const ImageBundle& in, ThreadPool* pool, Image3F* JXL_RESTRICT xyb, const JxlCmsInterface& cms, Image3F* JXL_RESTRICT linear) { - JXL_ASSIGN_OR_RETURN(*xyb, Image3F::Create(in.xsize(), in.ysize())); + JxlMemoryManager* memory_manager = in.memory_manager(); + JXL_ASSIGN_OR_RETURN(*xyb, + Image3F::Create(memory_manager, in.xsize(), in.ysize())); CopyImageTo(in.color(), xyb); ToXYB(in.c_current(), in.metadata()->IntensityTarget(), in.HasBlack() ? &in.black() : nullptr, pool, xyb, cms, linear); diff --git a/third_party/jpeg-xl/lib/jxl/encode.cc b/third_party/jpeg-xl/lib/jxl/encode.cc index 28a925dfc1b7..1e2cc0e3e531 100644 --- a/third_party/jpeg-xl/lib/jxl/encode.cc +++ b/third_party/jpeg-xl/lib/jxl/encode.cc @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -20,13 +21,13 @@ #include "lib/jxl/base/data_parallel.h" #include "lib/jxl/base/exif.h" #include "lib/jxl/base/printf_macros.h" +#include "lib/jxl/base/sanitizers.h" #include "lib/jxl/base/span.h" #include "lib/jxl/base/status.h" #include "lib/jxl/codec_in_out.h" #include "lib/jxl/enc_aux_out.h" #include "lib/jxl/enc_bit_writer.h" #include "lib/jxl/enc_cache.h" -#include "lib/jxl/enc_external_image.h" #include "lib/jxl/enc_fast_lossless.h" #include "lib/jxl/enc_fields.h" #include "lib/jxl/enc_frame.h" @@ -37,7 +38,6 @@ #include "lib/jxl/luminance.h" #include "lib/jxl/memory_manager_internal.h" #include "lib/jxl/padded_bytes.h" -#include "lib/jxl/sanitizers.h" struct JxlErrorOrStatus { // NOLINTNEXTLINE(google-explicit-constructor) @@ -123,14 +123,14 @@ JxlEncoderOutputProcessorWrapper::GetBuffer(size_t min_size, external_output_processor_->release_buffer( external_output_processor_->opaque, 0); } else { - internal_buffers_.emplace(position_, InternalBuffer()); + internal_buffers_.emplace(position_, InternalBuffer(memory_manager_)); has_buffer_ = true; return JxlOutputProcessorBuffer(user_buffer, size, 0, this); } } } else { if (min_size + additional_size < *avail_out_) { - internal_buffers_.emplace(position_, InternalBuffer()); + internal_buffers_.emplace(position_, InternalBuffer(memory_manager_)); has_buffer_ = true; return JxlOutputProcessorBuffer(*next_out_ + additional_size, *avail_out_ - additional_size, 0, this); @@ -138,7 +138,9 @@ JxlEncoderOutputProcessorWrapper::GetBuffer(size_t min_size, } // Otherwise, we need to allocate our own buffer. - auto it = internal_buffers_.emplace(position_, InternalBuffer()).first; + auto it = + internal_buffers_.emplace(position_, InternalBuffer(memory_manager_)) + .first; InternalBuffer& buffer = it->second; size_t alloc_size = requested_size; it++; @@ -463,6 +465,7 @@ void QueueBox(JxlEncoder* enc, // TODO(lode): share this code and the Brotli compression code in enc_jpeg_data JxlEncoderStatus BrotliCompress(int quality, const uint8_t* in, size_t in_size, jxl::PaddedBytes* out) { + JxlMemoryManager* memory_manager = out->memory_manager(); std::unique_ptr enc(BrotliEncoderCreateInstance(nullptr, nullptr, nullptr), BrotliEncoderDestroyInstance); @@ -472,7 +475,7 @@ JxlEncoderStatus BrotliCompress(int quality, const uint8_t* in, size_t in_size, BrotliEncoderSetParameter(enc.get(), BROTLI_PARAM_SIZE_HINT, in_size); constexpr size_t kBufferSize = 128 * 1024; - jxl::PaddedBytes temp_buffer(kBufferSize); + jxl::PaddedBytes temp_buffer(memory_manager, kBufferSize); size_t avail_in = in_size; const uint8_t* next_in = in; @@ -694,7 +697,7 @@ bool EncodeFrameIndexBox(const jxl::JxlEncoderFrameIndexBox& frame_index_box, } // namespace jxl::Status JxlEncoderStruct::ProcessOneEnqueuedInput() { - jxl::PaddedBytes header_bytes; + jxl::PaddedBytes header_bytes{&memory_manager}; jxl::JxlEncoderQueuedInput& input = input_queue[0]; @@ -730,7 +733,7 @@ jxl::Status JxlEncoderStruct::ProcessOneEnqueuedInput() { } jxl::AuxOut* aux_out = input.frame ? input.frame->option_values.aux_out : nullptr; - jxl::BitWriter writer; + jxl::BitWriter writer{&memory_manager}; if (!WriteCodestreamHeaders(&metadata, &writer, aux_out)) { return JXL_API_ERROR(this, JXL_ENC_ERR_GENERIC, "Failed to write codestream header"); @@ -944,8 +947,8 @@ jxl::Status JxlEncoderStruct::ProcessOneEnqueuedInput() { frame_info.timecode = timecode; frame_info.name = input_frame->option_values.frame_name; - if (!jxl::EncodeFrame(input_frame->option_values.cparams, frame_info, - &metadata, input_frame->frame_data, cms, + if (!jxl::EncodeFrame(&memory_manager, input_frame->option_values.cparams, + frame_info, &metadata, input_frame->frame_data, cms, thread_pool.get(), &output_processor, input_frame->option_values.aux_out)) { return JXL_API_ERROR(this, JXL_ENC_ERR_GENERIC, @@ -1024,7 +1027,7 @@ jxl::Status JxlEncoderStruct::ProcessOneEnqueuedInput() { num_queued_boxes--; if (box->compress_box) { - jxl::PaddedBytes compressed(4); + jxl::PaddedBytes compressed(&memory_manager, 4); // Prepend the original box type in the brob box contents for (size_t i = 0; i < 4; i++) { compressed[i] = static_cast(box->type[i]); @@ -1780,6 +1783,21 @@ JxlEncoderStatus JxlEncoderFrameSettingsSetOption( frame_settings->values.cparams.use_full_image_heuristics = default_to_false(value); break; + case JXL_ENC_FRAME_SETTING_DISABLE_PERCEPTUAL_HEURISTICS: + if (value < 0 || value > 1) { + return JXL_API_ERROR(frame_settings->enc, JXL_ENC_ERR_NOT_SUPPORTED, + "Option value has to be 0 or 1"); + } + frame_settings->values.cparams.disable_percepeptual_optimizations = + default_to_false(value); + if (frame_settings->values.cparams.disable_percepeptual_optimizations && + frame_settings->enc->basic_info_set && + frame_settings->enc->metadata.m.xyb_encoded) { + return JXL_API_ERROR( + frame_settings->enc, JXL_ENC_ERR_API_USAGE, + "Set uses_original_profile=true for non-perceptual encoding"); + } + break; default: return JXL_API_ERROR(frame_settings->enc, JXL_ENC_ERR_NOT_SUPPORTED, @@ -1923,9 +1941,15 @@ void JxlEncoderReset(JxlEncoder* enc) { enc->intensity_target_set = false; enc->use_container = false; enc->use_boxes = false; + enc->store_jpeg_metadata = false; enc->codestream_level = -1; enc->output_processor = JxlEncoderOutputProcessorWrapper(); JxlEncoderInitBasicInfo(&enc->basic_info); + + // jxl::JxlEncoderFrameIndexBox frame_index_box; + // JxlEncoderError error = JxlEncoderError::JXL_ENC_ERR_OK; + // bool allow_expert_options = false; + // int brotli_effort = -1; } void JxlEncoderDestroy(JxlEncoder* enc) { @@ -2028,7 +2052,7 @@ JxlEncoderStatus JxlEncoderAddJPEGFrame( "Frame input is already closed"); } - jxl::CodecInOut io; + jxl::CodecInOut io{&frame_settings->enc->memory_manager}; if (!jxl::jpeg::DecodeImageJPG(jxl::Bytes(buffer, size), &io)) { return JXL_API_ERROR(frame_settings->enc, JXL_ENC_ERR_BAD_INPUT, "Error during decode of input JPEG"); @@ -2113,7 +2137,8 @@ JxlEncoderStatus JxlEncoderAddJPEGFrame( } jxl::jpeg::JPEGData data_in = *io.Main().jpeg_data; std::vector jpeg_data; - if (!jxl::jpeg::EncodeJPEGData(data_in, &jpeg_data, + if (!jxl::jpeg::EncodeJPEGData(&frame_settings->enc->memory_manager, + data_in, &jpeg_data, frame_settings->values.cparams)) { return JXL_API_ERROR( frame_settings->enc, JXL_ENC_ERR_JBRD, @@ -2246,6 +2271,12 @@ JxlEncoderStatus JxlEncoderAddImageFrameInternal( frame_settings->enc, JXL_ENC_ERR_API_USAGE, "Set uses_original_profile=true for lossless encoding"); } + if (frame_settings->values.cparams.disable_percepeptual_optimizations && + frame_settings->enc->metadata.m.xyb_encoded) { + return JXL_API_ERROR( + frame_settings->enc, JXL_ENC_ERR_API_USAGE, + "Set uses_original_profile=true for non-perceptual encoding"); + } if (JXL_ENC_SUCCESS != VerifyInputBitDepth(frame_settings->values.image_bit_depth, pixel_format)) { @@ -2514,7 +2545,8 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetOutputProcessor( return JXL_API_ERROR(enc, JXL_ENC_ERR_API_USAGE, "Missing output processor functions"); } - enc->output_processor = JxlEncoderOutputProcessorWrapper(output_processor); + enc->output_processor = + JxlEncoderOutputProcessorWrapper(&enc->memory_manager, output_processor); return JxlErrorOrStatus::Success(); } diff --git a/third_party/jpeg-xl/lib/jxl/encode_internal.h b/third_party/jpeg-xl/lib/jxl/encode_internal.h index 8030c8314aba..d2154c2d9a87 100644 --- a/third_party/jpeg-xl/lib/jxl/encode_internal.h +++ b/third_party/jpeg-xl/lib/jxl/encode_internal.h @@ -431,9 +431,11 @@ class JxlEncoderOutputProcessorWrapper { friend class JxlOutputProcessorBuffer; public: - JxlEncoderOutputProcessorWrapper() = default; - explicit JxlEncoderOutputProcessorWrapper(JxlEncoderOutputProcessor processor) - : external_output_processor_( + JxlEncoderOutputProcessorWrapper() : memory_manager_(nullptr) {} + JxlEncoderOutputProcessorWrapper(JxlMemoryManager* memory_manager, + JxlEncoderOutputProcessor processor) + : memory_manager_(memory_manager), + external_output_processor_( jxl::make_unique(processor)) {} bool HasAvailOut() const { return avail_out_ != nullptr; } @@ -472,6 +474,8 @@ class JxlEncoderOutputProcessorWrapper { bool AppendBufferToExternalProcessor(void* data, size_t count); struct InternalBuffer { + explicit InternalBuffer(JxlMemoryManager* memory_manager) + : owned_data(memory_manager) {} // Bytes in the range `[output_position_ - start_of_the_buffer, // written_bytes)` need to be flushed out. size_t written_bytes = 0; @@ -496,6 +500,7 @@ class JxlEncoderOutputProcessorWrapper { bool stop_requested_ = false; bool has_buffer_ = false; + JxlMemoryManager* memory_manager_; std::unique_ptr external_output_processor_; }; diff --git a/third_party/jpeg-xl/lib/jxl/encode_test.cc b/third_party/jpeg-xl/lib/jxl/encode_test.cc index 7400b237de00..fcd4034295a9 100644 --- a/third_party/jpeg-xl/lib/jxl/encode_test.cc +++ b/third_party/jpeg-xl/lib/jxl/encode_test.cc @@ -227,7 +227,7 @@ void VerifyFrameEncoding(size_t xsize, size_t ysize, JxlEncoder* enc, compressed.resize(next_out - compressed.data()); EXPECT_LE(compressed.size(), max_compressed_size); EXPECT_EQ(JXL_ENC_SUCCESS, process_result); - jxl::CodecInOut decoded_io; + jxl::CodecInOut decoded_io{jxl::test::MemoryManager()}; EXPECT_TRUE(jxl::test::DecodeFile( {}, jxl::Bytes(compressed.data(), compressed.size()), &decoded_io)); @@ -259,7 +259,7 @@ TEST(EncodeTest, EncoderResetTest) { JxlEncoderPtr enc = JxlEncoderMake(nullptr); EXPECT_NE(nullptr, enc.get()); VerifyFrameEncoding(50, 200, enc.get(), - JxlEncoderFrameSettingsCreate(enc.get(), nullptr), 4550, + JxlEncoderFrameSettingsCreate(enc.get(), nullptr), 4577, false); // Encoder should become reusable for a new image from scratch after using // reset. @@ -1487,6 +1487,7 @@ JXL_GTEST_INSTANTIATE_TEST_SUITE_P( JXL_TRANSCODE_JPEG_TEST(EncodeTest, JPEGFrameTest) { TEST_LIBJPEG_SUPPORT(); + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); for (int skip_basic_info = 0; skip_basic_info < 2; skip_basic_info++) { for (int skip_color_encoding = 0; skip_color_encoding < 2; skip_color_encoding++) { @@ -1494,7 +1495,7 @@ JXL_TRANSCODE_JPEG_TEST(EncodeTest, JPEGFrameTest) { if (skip_basic_info && !skip_color_encoding) continue; const std::string jpeg_path = "jxl/flower/flower_cropped.jpg"; const std::vector orig = jxl::test::ReadTestData(jpeg_path); - jxl::CodecInOut orig_io; + jxl::CodecInOut orig_io{memory_manager}; ASSERT_TRUE(SetFromBytes(jxl::Bytes(orig), &orig_io, /*pool=*/nullptr)); @@ -1539,7 +1540,7 @@ JXL_TRANSCODE_JPEG_TEST(EncodeTest, JPEGFrameTest) { compressed.resize(next_out - compressed.data()); EXPECT_EQ(JXL_ENC_SUCCESS, process_result); - jxl::CodecInOut decoded_io; + jxl::CodecInOut decoded_io{memory_manager}; EXPECT_TRUE(jxl::test::DecodeFile( {}, jxl::Bytes(compressed.data(), compressed.size()), &decoded_io)); diff --git a/third_party/jpeg-xl/lib/jxl/entropy_coder.cc b/third_party/jpeg-xl/lib/jxl/entropy_coder.cc index 5dc101b36fcb..eb78aa6190a8 100644 --- a/third_party/jpeg-xl/lib/jxl/entropy_coder.cc +++ b/third_party/jpeg-xl/lib/jxl/entropy_coder.cc @@ -5,31 +5,24 @@ #include "lib/jxl/entropy_coder.h" -#include -#include +#include -#include -#include +#include #include #include "lib/jxl/ac_context.h" -#include "lib/jxl/ac_strategy.h" -#include "lib/jxl/base/bits.h" -#include "lib/jxl/base/compiler_specific.h" #include "lib/jxl/base/status.h" #include "lib/jxl/coeff_order.h" #include "lib/jxl/coeff_order_fwd.h" -#include "lib/jxl/dec_ans.h" #include "lib/jxl/dec_bit_reader.h" #include "lib/jxl/dec_context_map.h" -#include "lib/jxl/epf.h" -#include "lib/jxl/image.h" -#include "lib/jxl/image_ops.h" +#include "lib/jxl/fields.h" #include "lib/jxl/pack_signed.h" namespace jxl { -Status DecodeBlockCtxMap(BitReader* br, BlockCtxMap* block_ctx_map) { +Status DecodeBlockCtxMap(JxlMemoryManager* memory_manager, BitReader* br, + BlockCtxMap* block_ctx_map) { auto& dct = block_ctx_map->dc_thresholds; auto& qft = block_ctx_map->qf_thresholds; auto& ctx_map = block_ctx_map->ctx_map; @@ -57,7 +50,8 @@ Status DecodeBlockCtxMap(BitReader* br, BlockCtxMap* block_ctx_map) { ctx_map.resize(3 * kNumOrders * block_ctx_map->num_dc_ctxs * (qft.size() + 1)); - JXL_RETURN_IF_ERROR(DecodeContextMap(&ctx_map, &block_ctx_map->num_ctxs, br)); + JXL_RETURN_IF_ERROR( + DecodeContextMap(memory_manager, &ctx_map, &block_ctx_map->num_ctxs, br)); if (block_ctx_map->num_ctxs > 16) { return JXL_FAILURE("Invalid block context map: too many distinct contexts"); } diff --git a/third_party/jpeg-xl/lib/jxl/entropy_coder.h b/third_party/jpeg-xl/lib/jxl/entropy_coder.h index e4afa7a63148..2ce9f53f8780 100644 --- a/third_party/jpeg-xl/lib/jxl/entropy_coder.h +++ b/third_party/jpeg-xl/lib/jxl/entropy_coder.h @@ -6,8 +6,10 @@ #ifndef LIB_JXL_ENTROPY_CODER_H_ #define LIB_JXL_ENTROPY_CODER_H_ -#include -#include +#include + +#include +#include #include "lib/jxl/ac_context.h" #include "lib/jxl/base/compiler_specific.h" @@ -38,7 +40,8 @@ static constexpr U32Enc kDCThresholdDist(Bits(4), BitsOffset(8, 16), static constexpr U32Enc kQFThresholdDist(Bits(2), BitsOffset(3, 4), BitsOffset(5, 12), BitsOffset(8, 44)); -Status DecodeBlockCtxMap(BitReader* br, BlockCtxMap* block_ctx_map); +Status DecodeBlockCtxMap(JxlMemoryManager* memory_manager, BitReader* br, + BlockCtxMap* block_ctx_map); } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/fast_math_test.cc b/third_party/jpeg-xl/lib/jxl/fast_math_test.cc index b242dbe5758d..34e2296539fe 100644 --- a/third_party/jpeg-xl/lib/jxl/fast_math_test.cc +++ b/third_party/jpeg-xl/lib/jxl/fast_math_test.cc @@ -3,6 +3,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +#include "lib/jxl/test_utils.h" #undef HWY_TARGET_INCLUDE #define HWY_TARGET_INCLUDE "lib/jxl/fast_math_test.cc" #include @@ -158,7 +159,7 @@ HWY_NOINLINE void TestFast709EFD() { HWY_NOINLINE void TestFastXYB() { if (!HasFastXYBTosRGB8()) return; ImageMetadata metadata; - ImageBundle ib(&metadata); + ImageBundle ib(jxl::test::MemoryManager(), &metadata); int scaling = 1; int n = 256 * scaling; float inv_scaling = 1.0f / scaling; @@ -168,7 +169,8 @@ HWY_NOINLINE void TestFastXYB() { for (int cg = 0; cg < n; cg += kChunk) { for (int cb = 0; cb < n; cb += kChunk) { JXL_ASSIGN_OR_DIE(Image3F chunk, - Image3F::Create(kChunk * kChunk, kChunk)); + Image3F::Create(jxl::test::MemoryManager(), + kChunk * kChunk, kChunk)); for (int ir = 0; ir < kChunk; ir++) { for (int ig = 0; ig < kChunk; ig++) { for (int ib = 0; ib < kChunk; ib++) { @@ -183,7 +185,8 @@ HWY_NOINLINE void TestFastXYB() { } ib.SetFromImage(std::move(chunk), ColorEncoding::SRGB()); JXL_ASSIGN_OR_DIE(Image3F xyb, - Image3F::Create(kChunk * kChunk, kChunk)); + Image3F::Create(jxl::test::MemoryManager(), + kChunk * kChunk, kChunk)); std::vector roundtrip(kChunk * kChunk * kChunk * 3); JXL_CHECK(ToXYB(ib, nullptr, &xyb, *JxlGetDefaultCms())); for (int y = 0; y < kChunk; y++) { diff --git a/third_party/jpeg-xl/lib/jxl/fields_test.cc b/third_party/jpeg-xl/lib/jxl/fields_test.cc index 5af68d2d5fee..fd0e23465b79 100644 --- a/third_party/jpeg-xl/lib/jxl/fields_test.cc +++ b/third_party/jpeg-xl/lib/jxl/fields_test.cc @@ -5,6 +5,8 @@ #include "lib/jxl/fields.h" +#include + #include #include #include @@ -20,6 +22,7 @@ #include "lib/jxl/frame_header.h" #include "lib/jxl/headers.h" #include "lib/jxl/image_metadata.h" +#include "lib/jxl/test_utils.h" #include "lib/jxl/testing.h" namespace jxl { @@ -27,9 +30,10 @@ namespace { // Ensures `value` round-trips and in exactly `expected_bits_written`. void TestU32Coder(const uint32_t value, const size_t expected_bits_written) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); const U32Enc enc(Val(0), Bits(4), Val(0x7FFFFFFF), Bits(32)); - BitWriter writer; + BitWriter writer{memory_manager}; BitWriter::Allotment allotment( &writer, RoundUpBitsToByteMultiple(U32Coder::MaxEncodedBits(enc))); @@ -60,7 +64,8 @@ TEST(FieldsTest, U32CoderTest) { } void TestU64Coder(const uint64_t value, const size_t expected_bits_written) { - BitWriter writer; + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + BitWriter writer{memory_manager}; BitWriter::Allotment allotment( &writer, RoundUpBitsToByteMultiple(U64Coder::MaxEncodedBits())); @@ -160,12 +165,13 @@ TEST(FieldsTest, U64CoderTest) { } Status TestF16Coder(const float value) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); size_t max_encoded_bits; // It is not a fatal error if it can't be encoded. if (!F16Coder::CanEncode(value, &max_encoded_bits)) return false; EXPECT_EQ(F16Coder::MaxEncodedBits(), max_encoded_bits); - BitWriter writer; + BitWriter writer{memory_manager}; BitWriter::Allotment allotment(&writer, RoundUpBitsToByteMultiple(max_encoded_bits)); @@ -199,6 +205,7 @@ TEST(FieldsTest, F16CoderTest) { // Ensures Read(Write()) returns the same fields. TEST(FieldsTest, TestRoundtripSize) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); for (int i = 0; i < 8; i++) { SizeHeader size; ASSERT_TRUE(size.Set(123 + 77 * i, 7 + i)); @@ -208,7 +215,7 @@ TEST(FieldsTest, TestRoundtripSize) { ASSERT_TRUE(Bundle::CanEncode(size, &extension_bits, &total_bits)); EXPECT_EQ(0u, extension_bits); - BitWriter writer; + BitWriter writer{memory_manager}; ASSERT_TRUE(WriteSizeHeader(size, &writer, 0, nullptr)); EXPECT_EQ(total_bits, writer.BitsWritten()); writer.ZeroPadToByte(); @@ -256,6 +263,7 @@ TEST(FieldsTest, TestPreview) { // Ensures Read(Write()) returns the same fields. TEST(FieldsTest, TestRoundtripFrame) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); CodecMetadata metadata; FrameHeader h(&metadata); h.extensions = 0x800; @@ -264,7 +272,7 @@ TEST(FieldsTest, TestRoundtripFrame) { size_t total_bits = 999; // Initialize as garbage. ASSERT_TRUE(Bundle::CanEncode(h, &extension_bits, &total_bits)); EXPECT_EQ(0u, extension_bits); - BitWriter writer; + BitWriter writer{memory_manager}; ASSERT_TRUE(WriteFrameHeader(h, &writer, nullptr)); EXPECT_EQ(total_bits, writer.BitsWritten()); writer.ZeroPadToByte(); @@ -348,6 +356,7 @@ struct NewBundle : public Fields { }; TEST(FieldsTest, TestNewDecoderOldData) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); OldBundle old_bundle; old_bundle.old_large = 123; old_bundle.old_f = 3.75f; @@ -355,7 +364,7 @@ TEST(FieldsTest, TestNewDecoderOldData) { // Write to bit stream const size_t kMaxOutBytes = 999; - BitWriter writer; + BitWriter writer{memory_manager}; // Make sure values are initialized by code under test. size_t extension_bits = 12345; size_t total_bits = 12345; @@ -392,6 +401,7 @@ TEST(FieldsTest, TestNewDecoderOldData) { } TEST(FieldsTest, TestOldDecoderNewData) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); NewBundle new_bundle; new_bundle.old_large = 123; new_bundle.extensions = 3; @@ -400,7 +410,7 @@ TEST(FieldsTest, TestOldDecoderNewData) { // Write to bit stream constexpr size_t kMaxOutBytes = 999; - BitWriter writer; + BitWriter writer{memory_manager}; // Make sure values are initialized by code under test. size_t extension_bits = 12345; size_t total_bits = 12345; diff --git a/third_party/jpeg-xl/lib/jxl/gradient_test.cc b/third_party/jpeg-xl/lib/jxl/gradient_test.cc index e09b34603fc0..a3bdeda4a894 100644 --- a/third_party/jpeg-xl/lib/jxl/gradient_test.cc +++ b/third_party/jpeg-xl/lib/jxl/gradient_test.cc @@ -4,12 +4,12 @@ // license that can be found in the LICENSE file. #include -#include -#include -#include +#include #include #include +#include +#include #include #include @@ -44,7 +44,8 @@ double PointLineDist(double x0, double y0, double x1, double y1, double x, // angle in which the change direction happens. Image3F GenerateTestGradient(uint32_t color0, uint32_t color1, double angle, size_t xsize, size_t ysize) { - JXL_ASSIGN_OR_DIE(Image3F image, Image3F::Create(xsize, ysize)); + JXL_ASSIGN_OR_DIE(Image3F image, + Image3F::Create(jxl::test::MemoryManager(), xsize, ysize)); double x0 = xsize / 2.0; double y0 = ysize / 2.0; @@ -78,9 +79,11 @@ Image3F GenerateTestGradient(uint32_t color0, uint32_t color1, double angle, // The radius over which the derivative is computed is only 1 pixel and it only // checks two angles (hor and ver), but this approximation works well enough. Image3F Gradient2(const Image3F& image) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); size_t xsize = image.xsize(); size_t ysize = image.ysize(); - JXL_ASSIGN_OR_DIE(Image3F image2, Image3F::Create(xsize, ysize)); + JXL_ASSIGN_OR_DIE(Image3F image2, + Image3F::Create(memory_manager, xsize, ysize)); for (size_t c = 0; c < 3; ++c) { for (size_t y = 1; y + 1 < ysize; y++) { const auto* JXL_RESTRICT row0 = image.ConstPlaneRow(c, y - 1); @@ -140,6 +143,7 @@ Angle in degrees, colors can be given in hex as 0xRRGGBB. void TestGradient(ThreadPool* pool, uint32_t color0, uint32_t color1, size_t xsize, size_t ysize, float angle, bool fast_mode, float butteraugli_distance, bool use_gradient = true) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); CompressParams cparams; cparams.butteraugli_distance = butteraugli_distance; if (fast_mode) { @@ -147,12 +151,12 @@ void TestGradient(ThreadPool* pool, uint32_t color0, uint32_t color1, } Image3F gradient = GenerateTestGradient(color0, color1, angle, xsize, ysize); - CodecInOut io; + CodecInOut io{memory_manager}; io.metadata.m.SetUintSamples(8); io.metadata.m.color_encoding = ColorEncoding::SRGB(); io.SetFromImage(std::move(gradient), io.metadata.m.color_encoding); - CodecInOut io2; + CodecInOut io2{memory_manager}; std::vector compressed; EXPECT_TRUE(test::EncodeFile(cparams, &io, &compressed, pool)); diff --git a/third_party/jpeg-xl/lib/jxl/icc_codec.cc b/third_party/jpeg-xl/lib/jxl/icc_codec.cc index 8501c684ac3f..51fb5a44e800 100644 --- a/third_party/jpeg-xl/lib/jxl/icc_codec.cc +++ b/third_party/jpeg-xl/lib/jxl/icc_codec.cc @@ -5,13 +5,11 @@ #include "lib/jxl/icc_codec.h" -#include +#include -#include -#include -#include +#include -#include "lib/jxl/base/byte_order.h" +#include "lib/jxl/base/status.h" #include "lib/jxl/dec_ans.h" #include "lib/jxl/fields.h" #include "lib/jxl/icc_codec_common.h" @@ -29,9 +27,10 @@ namespace { // scanline order but with missing elements skipped (which may occur in multiple // locations), the output is the result matrix in scanline order (with // no need to skip missing elements as they are past the end of the data). -void Shuffle(uint8_t* data, size_t size, size_t width) { +void Shuffle(JxlMemoryManager* memory_manager, uint8_t* data, size_t size, + size_t width) { size_t height = (size + width - 1) / width; // amount of rows of output - PaddedBytes result(size); + PaddedBytes result(memory_manager, size); // i = output index, j input index size_t s = 0; size_t j = 0; @@ -92,6 +91,7 @@ Status CheckPreamble(const PaddedBytes& data, size_t enc_size, // Decodes the result of PredictICC back to a valid ICC profile. Status UnpredictICC(const uint8_t* enc, size_t size, PaddedBytes* result) { if (!result->empty()) return JXL_FAILURE("result must be empty initially"); + JxlMemoryManager* memory_manager = result->memory_manager(); size_t pos = 0; // TODO(lode): technically speaking we need to check that the entire varint // decoding never goes out of bounds, not just the first byte. This requires @@ -111,7 +111,7 @@ Status UnpredictICC(const uint8_t* enc, size_t size, PaddedBytes* result) { pos = commands_end; // pos in data stream // Header - PaddedBytes header; + PaddedBytes header{memory_manager}; header.append(ICCInitialHeaderPrediction()); EncodeUint32(0, osize, &header); for (size_t i = 0; i <= kICCHeaderSize; i++) { @@ -225,14 +225,14 @@ Status UnpredictICC(const uint8_t* enc, size_t size, PaddedBytes* result) { if (cpos >= commands_end) return JXL_FAILURE("Out of bounds"); uint64_t num = DecodeVarInt(enc, size, &cpos); JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, num, size)); - PaddedBytes shuffled(num); + PaddedBytes shuffled(memory_manager, num); for (size_t i = 0; i < num; i++) { shuffled[i] = enc[pos + i]; } if (command == kCommandShuffle2) { - Shuffle(shuffled.data(), num, 2); + Shuffle(memory_manager, shuffled.data(), num, 2); } else if (command == kCommandShuffle4) { - Shuffle(shuffled.data(), num, 4); + Shuffle(memory_manager, shuffled.data(), num, 4); } for (size_t i = 0; i < num; i++) { result->push_back(shuffled[i]); @@ -269,11 +269,11 @@ Status UnpredictICC(const uint8_t* enc, size_t size, PaddedBytes* result) { uint64_t num = DecodeVarInt(enc, size, &cpos); // in bytes JXL_RETURN_IF_ERROR(CheckOutOfBounds(pos, num, size)); - PaddedBytes shuffled(num); + PaddedBytes shuffled(memory_manager, num); for (size_t i = 0; i < num; i++) { shuffled[i] = enc[pos + i]; } - if (width > 1) Shuffle(shuffled.data(), num, width); + if (width > 1) Shuffle(memory_manager, shuffled.data(), num, width); size_t start = result->size(); for (size_t i = 0; i < num; i++) { @@ -308,6 +308,7 @@ Status UnpredictICC(const uint8_t* enc, size_t size, PaddedBytes* result) { Status ICCReader::Init(BitReader* reader, size_t output_limit) { JXL_RETURN_IF_ERROR(CheckEOI(reader)); + JxlMemoryManager* memory_manager = decompressed_.memory_manager(); used_bits_base_ = reader->TotalBitsConsumed(); if (bits_to_skip_ == 0) { enc_size_ = U64Coder::Read(reader); @@ -315,9 +316,9 @@ Status ICCReader::Init(BitReader* reader, size_t output_limit) { // Avoid too large memory allocation for invalid file. return JXL_FAILURE("Too large encoded profile"); } - JXL_RETURN_IF_ERROR( - DecodeHistograms(reader, kNumICCContexts, &code_, &context_map_)); - ans_reader_ = ANSSymbolReader(&code_, reader); + JXL_RETURN_IF_ERROR(DecodeHistograms( + memory_manager, reader, kNumICCContexts, &code_, &context_map_)); + JXL_ASSIGN_OR_RETURN(ans_reader_, ANSSymbolReader::Create(&code_, reader)); i_ = 0; decompressed_.resize(std::min(i_ + 0x400, enc_size_)); for (; i_ < std::min(2, enc_size_); i_++) { diff --git a/third_party/jpeg-xl/lib/jxl/icc_codec.h b/third_party/jpeg-xl/lib/jxl/icc_codec.h index e57018b4c35b..d531c0a736e2 100644 --- a/third_party/jpeg-xl/lib/jxl/icc_codec.h +++ b/third_party/jpeg-xl/lib/jxl/icc_codec.h @@ -8,6 +8,8 @@ // Compressed representation of ICC profiles. +#include + #include #include #include @@ -20,6 +22,9 @@ namespace jxl { struct ICCReader { + explicit ICCReader(JxlMemoryManager* memory_manager) + : decompressed_(memory_manager) {} + Status Init(BitReader* reader, size_t output_limit); Status Process(BitReader* reader, PaddedBytes* icc); void Reset() { diff --git a/third_party/jpeg-xl/lib/jxl/icc_codec_common.cc b/third_party/jpeg-xl/lib/jxl/icc_codec_common.cc index 1cb454268741..4bea0716ff72 100644 --- a/third_party/jpeg-xl/lib/jxl/icc_codec_common.cc +++ b/third_party/jpeg-xl/lib/jxl/icc_codec_common.cc @@ -5,14 +5,9 @@ #include "lib/jxl/icc_codec_common.h" -#include - -#include -#include -#include +#include #include "lib/jxl/base/byte_order.h" -#include "lib/jxl/fields.h" #include "lib/jxl/padded_bytes.h" namespace jxl { diff --git a/third_party/jpeg-xl/lib/jxl/icc_codec_test.cc b/third_party/jpeg-xl/lib/jxl/icc_codec_test.cc index 175b4768a08d..b563bda2b395 100644 --- a/third_party/jpeg-xl/lib/jxl/icc_codec_test.cc +++ b/third_party/jpeg-xl/lib/jxl/icc_codec_test.cc @@ -3,7 +3,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#include "lib/jxl/icc_codec.h" +#include #include #include @@ -19,7 +19,8 @@ namespace jxl { namespace { void TestProfile(const IccBytes& icc) { - BitWriter writer; + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + BitWriter writer{memory_manager}; ASSERT_TRUE(WriteICC(icc, &writer, 0, nullptr)); writer.ZeroPadToByte(); std::vector dec; diff --git a/third_party/jpeg-xl/lib/jxl/image.cc b/third_party/jpeg-xl/lib/jxl/image.cc index 7468dad130d8..9f14c4377bb3 100644 --- a/third_party/jpeg-xl/lib/jxl/image.cc +++ b/third_party/jpeg-xl/lib/jxl/image.cc @@ -5,6 +5,8 @@ #include "lib/jxl/image.h" +#include + #include // fill, swap #include #include @@ -15,7 +17,7 @@ #if defined(MEMORY_SANITIZER) #include "lib/jxl/base/common.h" -#include "lib/jxl/sanitizers.h" +#include "lib/jxl/base/sanitizers.h" #endif namespace jxl { @@ -66,6 +68,7 @@ PlaneBase::PlaneBase(const size_t xsize, const size_t ysize, orig_xsize_(static_cast(xsize)), orig_ysize_(static_cast(ysize)), bytes_per_row_(BytesPerRow(xsize_, sizeof_t)), + memory_manager_(nullptr), bytes_(nullptr), sizeof_t_(sizeof_t) { // TODO(eustas): turn to error instead of abort. @@ -75,7 +78,9 @@ PlaneBase::PlaneBase(const size_t xsize, const size_t ysize, JXL_ASSERT(sizeof_t == 1 || sizeof_t == 2 || sizeof_t == 4 || sizeof_t == 8); } -Status PlaneBase::Allocate() { +Status PlaneBase::Allocate(JxlMemoryManager* memory_manager) { + JXL_CHECK(memory_manager_ == nullptr); + JXL_CHECK(memory_manager != nullptr); JXL_CHECK(!bytes_.get()); // Dimensions can be zero, e.g. for lazily-allocated images. Only allocate @@ -89,6 +94,7 @@ Status PlaneBase::Allocate() { // TODO(eustas): use specialized OOM error code return JXL_FAILURE("Failed to allocate memory for image surface"); } + memory_manager_ = memory_manager; InitializePadding(*this, sizeof_t_); return true; @@ -100,6 +106,7 @@ void PlaneBase::Swap(PlaneBase& other) { std::swap(orig_xsize_, other.orig_xsize_); std::swap(orig_ysize_, other.orig_ysize_); std::swap(bytes_per_row_, other.bytes_per_row_); + std::swap(memory_manager_, other.memory_manager_); std::swap(bytes_, other.bytes_); } diff --git a/third_party/jpeg-xl/lib/jxl/image.h b/third_party/jpeg-xl/lib/jxl/image.h index 347e070336e4..b3baf375f275 100644 --- a/third_party/jpeg-xl/lib/jxl/image.h +++ b/third_party/jpeg-xl/lib/jxl/image.h @@ -13,6 +13,8 @@ #include // PRIu64 #endif +#include + #include #include #include @@ -37,6 +39,7 @@ struct PlaneBase { orig_xsize_(0), orig_ysize_(0), bytes_per_row_(0), + memory_manager_(nullptr), bytes_(nullptr), sizeof_t_(0) {} @@ -51,6 +54,12 @@ struct PlaneBase { // Move assignment (required for std::vector) PlaneBase& operator=(PlaneBase&& other) noexcept = default; + ~PlaneBase() { + if (bytes_.get()) { + JXL_CHECK(memory_manager_); + } + } + void Swap(PlaneBase& other); // Useful for pre-allocating image with some padding for alignment purposes @@ -73,6 +82,10 @@ struct PlaneBase { // NOTE: do not use this for copying rows - the valid xsize may be much less. JXL_INLINE size_t bytes_per_row() const { return bytes_per_row_; } + JXL_INLINE JxlMemoryManager* memory_manager() const { + return memory_manager_; + } + // Raw access to byte contents, for interfacing with other libraries. // Unsigned char instead of char to avoid surprises (sign extension). JXL_INLINE uint8_t* bytes() { @@ -86,7 +99,7 @@ struct PlaneBase { protected: PlaneBase(size_t xsize, size_t ysize, size_t sizeof_t); - Status Allocate(); + Status Allocate(JxlMemoryManager* memory_manager); // Returns pointer to the start of a row. JXL_INLINE void* VoidRow(const size_t y) const { @@ -108,6 +121,7 @@ struct PlaneBase { uint32_t orig_xsize_; uint32_t orig_ysize_; size_t bytes_per_row_; // Includes padding. + JxlMemoryManager* memory_manager_; CacheAlignedUniquePtr bytes_; size_t sizeof_t_; }; @@ -143,9 +157,10 @@ class Plane : public detail::PlaneBase { Plane() = default; - static StatusOr Create(const size_t xsize, const size_t ysize) { + static StatusOr Create(JxlMemoryManager* memory_manager, + const size_t xsize, const size_t ysize) { Plane plane(xsize, ysize, sizeof(T)); - JXL_RETURN_IF_ERROR(plane.Allocate()); + JXL_RETURN_IF_ERROR(plane.Allocate(memory_manager)); return plane; } @@ -226,12 +241,13 @@ class Image3 { return *this; } - static StatusOr Create(const size_t xsize, const size_t ysize) { - StatusOr plane0 = PlaneT::Create(xsize, ysize); + static StatusOr Create(JxlMemoryManager* memory_manager, + const size_t xsize, const size_t ysize) { + StatusOr plane0 = PlaneT::Create(memory_manager, xsize, ysize); JXL_RETURN_IF_ERROR(plane0.status()); - StatusOr plane1 = PlaneT::Create(xsize, ysize); + StatusOr plane1 = PlaneT::Create(memory_manager, xsize, ysize); JXL_RETURN_IF_ERROR(plane1.status()); - StatusOr plane2 = PlaneT::Create(xsize, ysize); + StatusOr plane2 = PlaneT::Create(memory_manager, xsize, ysize); JXL_RETURN_IF_ERROR(plane2.status()); return Image3(std::move(plane0).value(), std::move(plane1).value(), std::move(plane2).value()); @@ -282,6 +298,9 @@ class Image3 { } // Sizes of all three images are guaranteed to be equal. + JXL_INLINE JxlMemoryManager* memory_manager() const { + return planes_[0].memory_manager(); + } JXL_INLINE size_t xsize() const { return planes_[0].xsize(); } JXL_INLINE size_t ysize() const { return planes_[0].ysize(); } // Returns offset [bytes] from one row to the next row of the same plane. diff --git a/third_party/jpeg-xl/lib/jxl/image_bundle.h b/third_party/jpeg-xl/lib/jxl/image_bundle.h index 00f1eb81bd8c..324385b29df6 100644 --- a/third_party/jpeg-xl/lib/jxl/image_bundle.h +++ b/third_party/jpeg-xl/lib/jxl/image_bundle.h @@ -9,6 +9,7 @@ // The main image or frame consists of a bundle of associated images. #include +#include #include #include @@ -35,24 +36,29 @@ namespace jxl { class ImageBundle { public: // Uninitialized state for use as output parameter. - ImageBundle() : metadata_(nullptr) {} + explicit ImageBundle(JxlMemoryManager* memory_manager) + : memory_manager_(memory_manager), metadata_(nullptr) {} // Caller is responsible for setting metadata before calling Set*. - explicit ImageBundle(const ImageMetadata* metadata) : metadata_(metadata) {} + ImageBundle(JxlMemoryManager* memory_manager, const ImageMetadata* metadata) + : memory_manager_(memory_manager), metadata_(metadata) {} // Move-only (allows storing in std::vector). ImageBundle(ImageBundle&&) = default; ImageBundle& operator=(ImageBundle&&) = default; StatusOr Copy() const { - ImageBundle copy(metadata_); - JXL_ASSIGN_OR_RETURN(copy.color_, - Image3F::Create(color_.xsize(), color_.ysize())); + JxlMemoryManager* memory_manager = this->memory_manager(); + ImageBundle copy(memory_manager, metadata_); + JXL_ASSIGN_OR_RETURN( + copy.color_, + Image3F::Create(memory_manager, color_.xsize(), color_.ysize())); CopyImageTo(color_, ©.color_); copy.c_current_ = c_current_; copy.extra_channels_.reserve(extra_channels_.size()); for (const ImageF& plane : extra_channels_) { - JXL_ASSIGN_OR_RETURN(ImageF ec, - ImageF::Create(plane.xsize(), plane.ysize())); + JXL_ASSIGN_OR_RETURN( + ImageF ec, + ImageF::Create(memory_manager, plane.xsize(), plane.ysize())); CopyImageTo(plane, &ec); copy.extra_channels_.emplace_back(std::move(ec)); } @@ -95,8 +101,12 @@ class ImageBundle { } } + JxlMemoryManager* memory_manager_; + // -- COLOR + JxlMemoryManager* memory_manager() const { return memory_manager_; } + // Whether color() is valid/usable. Returns true in most cases. Even images // with spot colors (one example of when !planes().empty()) typically have a // part that can be converted to RGB. diff --git a/third_party/jpeg-xl/lib/jxl/image_bundle_test.cc b/third_party/jpeg-xl/lib/jxl/image_bundle_test.cc index 1a10598fe2d2..cf94507ac59a 100644 --- a/third_party/jpeg-xl/lib/jxl/image_bundle_test.cc +++ b/third_party/jpeg-xl/lib/jxl/image_bundle_test.cc @@ -3,18 +3,20 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#include "lib/jxl/image_bundle.h" +#include #include "lib/jxl/enc_aux_out.h" #include "lib/jxl/enc_bit_writer.h" +#include "lib/jxl/test_utils.h" #include "lib/jxl/testing.h" namespace jxl { namespace { TEST(ImageBundleTest, ExtraChannelName) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); AuxOut aux_out; - BitWriter writer; + BitWriter writer{memory_manager}; BitWriter::Allotment allotment(&writer, 99); ImageMetadata metadata; diff --git a/third_party/jpeg-xl/lib/jxl/image_ops.cc b/third_party/jpeg-xl/lib/jxl/image_ops.cc index 36d19df9389c..509702805236 100644 --- a/third_party/jpeg-xl/lib/jxl/image_ops.cc +++ b/third_party/jpeg-xl/lib/jxl/image_ops.cc @@ -66,9 +66,11 @@ static void DoDownsampleImage(const ImageF& input, size_t factor, StatusOr DownsampleImage(const ImageF& image, size_t factor) { ImageF downsampled; // Allocate extra space to avoid a reallocation when padding. + JxlMemoryManager* memory_manager = image.memory_manager(); JXL_ASSIGN_OR_RETURN( - downsampled, ImageF::Create(DivCeil(image.xsize(), factor) + kBlockDim, - DivCeil(image.ysize(), factor) + kBlockDim)); + downsampled, + ImageF::Create(memory_manager, DivCeil(image.xsize(), factor) + kBlockDim, + DivCeil(image.ysize(), factor) + kBlockDim)); DoDownsampleImage(image, factor, &downsampled); return downsampled; } @@ -77,8 +79,10 @@ StatusOr DownsampleImage(const Image3F& opsin, size_t factor) { JXL_ASSERT(factor != 1); // Allocate extra space to avoid a reallocation when padding. Image3F downsampled; + JxlMemoryManager* memory_manager = opsin.memory_manager(); JXL_ASSIGN_OR_RETURN( - downsampled, Image3F::Create(DivCeil(opsin.xsize(), factor) + kBlockDim, + downsampled, Image3F::Create(memory_manager, + DivCeil(opsin.xsize(), factor) + kBlockDim, DivCeil(opsin.ysize(), factor) + kBlockDim)); downsampled.ShrinkTo(downsampled.xsize() - kBlockDim, downsampled.ysize() - kBlockDim); diff --git a/third_party/jpeg-xl/lib/jxl/image_ops.h b/third_party/jpeg-xl/lib/jxl/image_ops.h index bee4eb56bda0..91e8750c9777 100644 --- a/third_party/jpeg-xl/lib/jxl/image_ops.h +++ b/third_party/jpeg-xl/lib/jxl/image_ops.h @@ -8,6 +8,8 @@ // Operations on images. +#include + #include #include #include @@ -118,7 +120,9 @@ StatusOr> LinComb(const T lambda1, const Plane& image1, const size_t ysize = image1.ysize(); JXL_CHECK(xsize == image2.xsize()); JXL_CHECK(ysize == image2.ysize()); - JXL_ASSIGN_OR_RETURN(Plane out, Plane::Create(xsize, ysize)); + JxlMemoryManager* memory_manager = image1.memory_manager(); + JXL_ASSIGN_OR_RETURN(Plane out, + Plane::Create(memory_manager, xsize, ysize)); for (size_t y = 0; y < ysize; ++y) { const T* const JXL_RESTRICT row1 = image1.Row(y); const T* const JXL_RESTRICT row2 = image2.Row(y); diff --git a/third_party/jpeg-xl/lib/jxl/image_ops_test.cc b/third_party/jpeg-xl/lib/jxl/image_ops_test.cc index 0d16864282bc..1f969dfcfdbe 100644 --- a/third_party/jpeg-xl/lib/jxl/image_ops_test.cc +++ b/third_party/jpeg-xl/lib/jxl/image_ops_test.cc @@ -16,6 +16,7 @@ #include "lib/jxl/base/status.h" #include "lib/jxl/cache_aligned.h" #include "lib/jxl/image.h" +#include "lib/jxl/test_utils.h" #include "lib/jxl/testing.h" namespace jxl { @@ -84,7 +85,9 @@ template void TestFillT() { for (uint32_t xsize : {0, 1, 15, 16, 31, 32}) { for (uint32_t ysize : {0, 1, 15, 16, 31, 32}) { - JXL_ASSIGN_OR_DIE(Image3 image, Image3::Create(xsize, ysize)); + JXL_ASSIGN_OR_DIE( + Image3 image, + Image3::Create(jxl::test::MemoryManager(), xsize, ysize)); TestFillImpl(&image, "size ctor"); } } @@ -99,7 +102,9 @@ TEST(ImageTest, TestFill) { } TEST(ImageTest, CopyImageToWithPaddingTest) { - JXL_ASSIGN_OR_DIE(Plane src, Plane::Create(100, 61)); + JXL_ASSIGN_OR_DIE( + Plane src, + Plane::Create(jxl::test::MemoryManager(), 100, 61)); for (size_t y = 0; y < src.ysize(); y++) { for (size_t x = 0; x < src.xsize(); x++) { src.Row(y)[x] = x * 1000 + y; @@ -108,7 +113,9 @@ TEST(ImageTest, CopyImageToWithPaddingTest) { Rect src_rect(10, 20, 30, 40); EXPECT_TRUE(src_rect.IsInside(src)); - JXL_ASSIGN_OR_DIE(Plane dst, Plane::Create(60, 50)); + JXL_ASSIGN_OR_DIE( + Plane dst, + Plane::Create(jxl::test::MemoryManager(), 60, 50)); FillImage(0u, &dst); Rect dst_rect(20, 5, 30, 40); EXPECT_TRUE(dst_rect.IsInside(dst)); diff --git a/third_party/jpeg-xl/lib/jxl/inverse_mtf-inl.h b/third_party/jpeg-xl/lib/jxl/inverse_mtf-inl.h index 60ea69ddbdea..8c63316f3f10 100644 --- a/third_party/jpeg-xl/lib/jxl/inverse_mtf-inl.h +++ b/third_party/jpeg-xl/lib/jxl/inverse_mtf-inl.h @@ -14,7 +14,7 @@ #include -#include "lib/jxl/sanitizers.h" +#include "lib/jxl/base/sanitizers.h" HWY_BEFORE_NAMESPACE(); namespace jxl { diff --git a/third_party/jpeg-xl/lib/jxl/jpeg/dec_jpeg_data.cc b/third_party/jpeg-xl/lib/jxl/jpeg/dec_jpeg_data.cc index a971eb3dcc2e..e3b1b4ba2bf2 100644 --- a/third_party/jpeg-xl/lib/jxl/jpeg/dec_jpeg_data.cc +++ b/third_party/jpeg-xl/lib/jxl/jpeg/dec_jpeg_data.cc @@ -7,10 +7,10 @@ #include +#include "lib/jxl/base/sanitizers.h" #include "lib/jxl/base/span.h" #include "lib/jxl/base/status.h" #include "lib/jxl/dec_bit_reader.h" -#include "lib/jxl/sanitizers.h" namespace jxl { namespace jpeg { diff --git a/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data.cc b/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data.cc index d4a5e1d5774d..01b6976a7193 100644 --- a/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data.cc +++ b/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data.cc @@ -6,13 +6,14 @@ #include "lib/jxl/jpeg/enc_jpeg_data.h" #include +#include +#include "lib/jxl/base/sanitizers.h" #include "lib/jxl/codec_in_out.h" #include "lib/jxl/enc_bit_writer.h" #include "lib/jxl/image_bundle.h" #include "lib/jxl/jpeg/enc_jpeg_data_reader.h" #include "lib/jxl/luminance.h" -#include "lib/jxl/sanitizers.h" namespace jxl { namespace jpeg { @@ -302,7 +303,8 @@ Status SetColorTransformFromJpegData(const JPEGData& jpg, return true; } -Status EncodeJPEGData(JPEGData& jpeg_data, std::vector* bytes, +Status EncodeJPEGData(JxlMemoryManager* memory_manager, JPEGData& jpeg_data, + std::vector* bytes, const CompressParams& cparams) { bytes->clear(); jpeg_data.app_marker_type.resize(jpeg_data.app_data.size(), @@ -326,7 +328,7 @@ Status EncodeJPEGData(JPEGData& jpeg_data, std::vector* bytes, total_data += jpeg_data.tail_data.size(); size_t brotli_capacity = BrotliEncoderMaxCompressedSize(total_data); - BitWriter writer; + BitWriter writer{memory_manager}; JXL_RETURN_IF_ERROR(Bundle::Write(jpeg_data, &writer, 0, nullptr)); writer.ZeroPadToByte(); { @@ -378,9 +380,10 @@ Status EncodeJPEGData(JPEGData& jpeg_data, std::vector* bytes, Status DecodeImageJPG(const Span bytes, CodecInOut* io) { if (!IsJPG(bytes)) return false; + JxlMemoryManager* memory_manager = io->memory_manager; io->frames.clear(); io->frames.reserve(1); - io->frames.emplace_back(&io->metadata.m); + io->frames.emplace_back(memory_manager, &io->metadata.m); io->Main().jpeg_data = make_unique(); jpeg::JPEGData* jpeg_data = io->Main().jpeg_data.get(); if (!jpeg::ReadJpeg(bytes.data(), bytes.size(), jpeg::JpegReadMode::kReadAll, @@ -396,8 +399,9 @@ Status DecodeImageJPG(const Span bytes, CodecInOut* io) { io->metadata.m.SetIntensityTarget(kDefaultIntensityTarget); io->metadata.m.SetUintSamples(BITS_IN_JSAMPLE); - JXL_ASSIGN_OR_RETURN(Image3F tmp, - Image3F::Create(jpeg_data->width, jpeg_data->height)); + JXL_ASSIGN_OR_RETURN( + Image3F tmp, + Image3F::Create(memory_manager, jpeg_data->width, jpeg_data->height)); io->SetFromImage(std::move(tmp), io->metadata.m.color_encoding); SetIntensityTarget(&io->metadata.m); return true; diff --git a/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data.h b/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data.h index f9a3a95e2369..3313c1eea194 100644 --- a/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data.h +++ b/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data.h @@ -6,6 +6,8 @@ #ifndef LIB_JXL_JPEG_ENC_JPEG_DATA_H_ #define LIB_JXL_JPEG_ENC_JPEG_DATA_H_ +#include + #include #include @@ -20,7 +22,8 @@ namespace jxl { class CodecInOut; namespace jpeg { -Status EncodeJPEGData(JPEGData& jpeg_data, std::vector* bytes, +Status EncodeJPEGData(JxlMemoryManager* memory_manager, JPEGData& jpeg_data, + std::vector* bytes, const CompressParams& cparams); void SetColorEncodingFromJpegData(const jpeg::JPEGData& jpg, diff --git a/third_party/jpeg-xl/lib/jxl/jxl_test.cc b/third_party/jpeg-xl/lib/jxl/jxl_test.cc index be43d5a23706..378c579631ac 100644 --- a/third_party/jpeg-xl/lib/jxl/jxl_test.cc +++ b/third_party/jpeg-xl/lib/jxl/jxl_test.cc @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -64,14 +65,14 @@ TEST(JxlTest, RoundtripSinglePixel) { TestImage t; t.SetDimensions(1, 1).AddFrame().ZeroFill(); PackedPixelFile ppf_out; - EXPECT_EQ(Roundtrip(t.ppf(), {}, {}, nullptr, &ppf_out), 55); + EXPECT_NEAR(Roundtrip(t.ppf(), {}, {}, nullptr, &ppf_out), 54, 10); } TEST(JxlTest, RoundtripSinglePixelWithAlpha) { TestImage t; t.SetDimensions(1, 1).SetChannels(4).AddFrame().ZeroFill(); PackedPixelFile ppf_out; - EXPECT_EQ(Roundtrip(t.ppf(), {}, {}, nullptr, &ppf_out), 58); + EXPECT_NEAR(Roundtrip(t.ppf(), {}, {}, nullptr, &ppf_out), 57, 10); } // Changing serialized signature causes Decode to fail. @@ -129,7 +130,7 @@ TEST(JxlTest, RoundtripSmallD1) { { PackedPixelFile ppf_out; - EXPECT_NEAR(Roundtrip(t.ppf(), {}, {}, pool, &ppf_out), 745, 20); + EXPECT_NEAR(Roundtrip(t.ppf(), {}, {}, pool, &ppf_out), 723, 20); EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 1.3); EXPECT_EQ(ppf_out.info.intensity_target, t.ppf().info.intensity_target); } @@ -164,7 +165,7 @@ TEST(JxlTest, RoundtripResample2Slow) { PackedPixelFile ppf_out; EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 3888, 200); - EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), 250); + EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), 270); } TEST(JxlTest, RoundtripResample2MT) { @@ -296,13 +297,14 @@ TEST(JxlTest, RoundtripMultiGroup) { auto run_kitten = std::async(std::launch::async, test, SpeedTier::kKitten, 1.0f, 63624u, 8.5); auto run_wombat = std::async(std::launch::async, test, SpeedTier::kWombat, - 2.0f, 38536u, 15.7); + 2.0f, 38887u, 15.5); } TEST(JxlTest, RoundtripRGBToGrayscale) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); ThreadPoolForTests pool(4); const std::vector orig = ReadTestData("jxl/flower/flower.png"); - CodecInOut io; + CodecInOut io{memory_manager}; ASSERT_TRUE(SetFromBytes(Bytes(orig), &io, pool.get())); io.ShrinkTo(600, 1024); @@ -313,7 +315,7 @@ TEST(JxlTest, RoundtripRGBToGrayscale) { JXLDecompressParams dparams; dparams.color_space = "Gra_D65_Rel_SRG"; - CodecInOut io2; + CodecInOut io2{memory_manager}; EXPECT_FALSE(io.Main().IsGray()); size_t compressed_size; JXL_EXPECT_OK( @@ -382,15 +384,9 @@ JXL_X86_64_TEST(JxlTest, RoundtripLargeEmptyModular) { cparams.AddOption(JXL_ENC_FRAME_SETTING_DECODING_SPEED, 2); PackedPixelFile ppf_out; - EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool.get(), &ppf_out), 3474795, + EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool.get(), &ppf_out), 669009, 100000); - EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), -#if JXL_HIGH_PRECISION - 2050 -#else - 12100 -#endif - ); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 0.19); } TEST(JxlTest, RoundtripOutputColorSpace) { @@ -423,7 +419,7 @@ TEST(JxlTest, RoundtripDotsForceEpf) { cparams.AddOption(JXL_ENC_FRAME_SETTING_DOTS, 1); PackedPixelFile ppf_out; - EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool.get(), &ppf_out), 41355, + EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool.get(), &ppf_out), 40999, 300); EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), 18); } @@ -599,10 +595,11 @@ TEST(JxlTest, RoundtripSmallPatches) { // are preserved. #if JXL_FALSE TEST(JxlTest, RoundtripImageBundleOriginalBits) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); // Image does not matter, only io.metadata.m and io2.metadata.m are tested. - JXL_ASSIGN_OR_DIE(Image3F image, Image3F::Create(1, 1)); + JXL_ASSIGN_OR_DIE(Image3F image, Image3F::Create(memory_manager, 1, 1)); ZeroFillImage(&image); - CodecInOut io; + CodecInOut io{memory_manager}; io.metadata.m.color_encoding = ColorEncoding::LinearSRGB(); io.SetFromImage(std::move(image), ColorEncoding::LinearSRGB()); @@ -618,7 +615,7 @@ TEST(JxlTest, RoundtripImageBundleOriginalBits) { } io.metadata.m.SetUintSamples(bit_depth); - CodecInOut io2; + CodecInOut io2{memory_manager}; JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _)); EXPECT_EQ(bit_depth, io2.metadata.m.bit_depth.bits_per_sample); @@ -655,7 +652,7 @@ TEST(JxlTest, RoundtripImageBundleOriginalBits) { io.metadata.m.bit_depth.floating_point_sample = true; io.metadata.m.bit_depth.exponent_bits_per_sample = exponent_bit_depth; - CodecInOut io2; + CodecInOut io2{memory_manager}; JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2)); EXPECT_EQ(bit_depth, io2.metadata.m.bit_depth.bits_per_sample); @@ -668,9 +665,10 @@ TEST(JxlTest, RoundtripImageBundleOriginalBits) { #endif TEST(JxlTest, RoundtripGrayscale) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); const std::vector orig = ReadTestData( "external/wesaturate/500px/cvo9xd_keong_macan_grayscale.png"); - CodecInOut io; + CodecInOut io{memory_manager}; ASSERT_TRUE(SetFromBytes(Bytes(orig), &io)); ASSERT_NE(io.xsize(), 0u); io.ShrinkTo(128, 128); @@ -686,7 +684,7 @@ TEST(JxlTest, RoundtripGrayscale) { std::vector compressed; EXPECT_TRUE(test::EncodeFile(cparams, &io, &compressed)); - CodecInOut io2; + CodecInOut io2{memory_manager}; EXPECT_TRUE(test::DecodeFile({}, Bytes(compressed), &io2)); EXPECT_TRUE(io2.Main().IsGray()); @@ -706,7 +704,7 @@ TEST(JxlTest, RoundtripGrayscale) { std::vector compressed; EXPECT_TRUE(test::EncodeFile(cparams, &io, &compressed)); - CodecInOut io2; + CodecInOut io2{memory_manager}; EXPECT_TRUE(test::DecodeFile({}, Bytes(compressed), &io2)); EXPECT_TRUE(io2.Main().IsGray()); @@ -725,7 +723,7 @@ TEST(JxlTest, RoundtripGrayscale) { std::vector compressed; EXPECT_TRUE(test::EncodeFile(cparams, &io, &compressed)); - CodecInOut io2; + CodecInOut io2{memory_manager}; JXLDecompressParams dparams; dparams.color_space = "RGB_D65_SRG_Rel_SRG"; EXPECT_TRUE(test::DecodeFile(dparams, Bytes(compressed), &io2)); @@ -741,9 +739,10 @@ TEST(JxlTest, RoundtripGrayscale) { } TEST(JxlTest, RoundtripAlpha) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); const std::vector orig = ReadTestData("external/wesaturate/500px/tmshre_riaphotographs_alpha.png"); - CodecInOut io; + CodecInOut io{memory_manager}; ASSERT_TRUE(SetFromBytes(Bytes(orig), &io)); ASSERT_NE(io.xsize(), 0u); @@ -765,7 +764,7 @@ TEST(JxlTest, RoundtripAlpha) { for (bool use_image_callback : {false, true}) { for (bool unpremul_alpha : {false, true}) { - CodecInOut io2; + CodecInOut io2{memory_manager}; JXLDecompressParams dparams; dparams.use_image_callback = use_image_callback; dparams.unpremultiply_alpha = unpremul_alpha; @@ -835,10 +834,11 @@ bool UnpremultiplyAlpha(CodecInOut& io) { } // namespace TEST(JxlTest, RoundtripAlphaPremultiplied) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); const std::vector orig = ReadTestData("external/wesaturate/500px/tmshre_riaphotographs_alpha.png"); - CodecInOut io; - CodecInOut io_nopremul; + CodecInOut io{memory_manager}; + CodecInOut io_nopremul{memory_manager}; ASSERT_TRUE(SetFromBytes(Bytes(orig), &io)); ASSERT_TRUE(SetFromBytes(Bytes(orig), &io_nopremul)); @@ -871,7 +871,7 @@ TEST(JxlTest, RoundtripAlphaPremultiplied) { use_uint8 ? "uint8" : "float", use_image_callback ? "image callback" : "image_buffer", unpremul_alpha ? "un" : ""); - CodecInOut io2; + CodecInOut io2{memory_manager}; JXLDecompressParams dparams; dparams.use_image_callback = use_image_callback; dparams.unpremultiply_alpha = unpremul_alpha; @@ -1180,7 +1180,7 @@ TEST(JxlTest, RoundtripDots) { JXL_TRANSFER_FUNCTION_SRGB); JXLCompressParams cparams; - cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 7); // kSkirrel + cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 7); // kSquirrel cparams.AddOption(JXL_ENC_FRAME_SETTING_DOTS, 1); cparams.distance = 0.04; @@ -1189,6 +1189,27 @@ TEST(JxlTest, RoundtripDots) { EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 0.35); } +TEST(JxlTest, RoundtripDisablePerceptual) { + ThreadPool* pool = nullptr; + const std::vector orig = ReadTestData("jxl/flower/flower.png"); + TestImage t; + t.DecodeFromBytes(orig).ClearMetadata(); + ASSERT_NE(t.ppf().info.xsize, 0); + EXPECT_EQ(t.ppf().info.bits_per_sample, 8); + EXPECT_EQ(t.ppf().color_encoding.transfer_function, + JXL_TRANSFER_FUNCTION_SRGB); + + JXLCompressParams cparams; + cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 7); // kSquirrel + cparams.AddOption(JXL_ENC_FRAME_SETTING_DISABLE_PERCEPTUAL_HEURISTICS, 1); + cparams.distance = 1.0; + + PackedPixelFile ppf_out; + EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 477778, 4000); + // TODO(veluca): figure out why we can't get below this value. + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 11.0); +} + TEST(JxlTest, RoundtripNoise) { ThreadPool* pool = nullptr; const std::vector orig = @@ -1201,7 +1222,7 @@ TEST(JxlTest, RoundtripNoise) { JXL_TRANSFER_FUNCTION_SRGB); JXLCompressParams cparams; - cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 7); // kSkirrel + cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 7); // kSquirrel cparams.AddOption(JXL_ENC_FRAME_SETTING_NOISE, 1); PackedPixelFile ppf_out; @@ -1245,7 +1266,7 @@ TEST(JxlTest, RoundtripAnimation) { dparams.accepted_formats.push_back(t.ppf().frames[0].color.format); PackedPixelFile ppf_out; - EXPECT_SLIGHTLY_BELOW(Roundtrip(t.ppf(), {}, dparams, pool, &ppf_out), 3350); + EXPECT_SLIGHTLY_BELOW(Roundtrip(t.ppf(), {}, dparams, pool, &ppf_out), 3370); t.CoalesceGIFAnimationWithAlpha(); ASSERT_EQ(ppf_out.frames.size(), t.ppf().frames.size()); @@ -1304,7 +1325,7 @@ TEST(JxlTest, RoundtripAnimationPatches) { PackedPixelFile ppf_out; // 40k with no patches, 27k with patch frames encoded multiple times. EXPECT_SLIGHTLY_BELOW(Roundtrip(t.ppf(), cparams, dparams, pool, &ppf_out), - 19300); + 19420); EXPECT_EQ(ppf_out.frames.size(), t.ppf().frames.size()); // >10 with broken patches; not all patches are detected on borders. EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 1.9); diff --git a/third_party/jpeg-xl/lib/jxl/modular/encoding/dec_ma.cc b/third_party/jpeg-xl/lib/jxl/modular/encoding/dec_ma.cc index b53b9a910328..08b3dcac77d1 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/encoding/dec_ma.cc +++ b/third_party/jpeg-xl/lib/jxl/modular/encoding/dec_ma.cc @@ -5,9 +5,12 @@ #include "lib/jxl/modular/encoding/dec_ma.h" +#include + #include #include "lib/jxl/base/printf_macros.h" +#include "lib/jxl/base/status.h" #include "lib/jxl/dec_ans.h" #include "lib/jxl/modular/encoding/ma_common.h" #include "lib/jxl/modular/modular_image.h" @@ -89,16 +92,18 @@ Status DecodeTree(BitReader *br, ANSSymbolReader *reader, } } // namespace -Status DecodeTree(BitReader *br, Tree *tree, size_t tree_size_limit) { +Status DecodeTree(JxlMemoryManager *memory_manager, BitReader *br, Tree *tree, + size_t tree_size_limit) { std::vector tree_context_map; ANSCode tree_code; - JXL_RETURN_IF_ERROR( - DecodeHistograms(br, kNumTreeContexts, &tree_code, &tree_context_map)); + JXL_RETURN_IF_ERROR(DecodeHistograms(memory_manager, br, kNumTreeContexts, + &tree_code, &tree_context_map)); // TODO(eustas): investigate more infinite tree cases. if (tree_code.degenerate_symbols[tree_context_map[kPropertyContext]] > 0) { return JXL_FAILURE("Infinite tree"); } - ANSSymbolReader reader(&tree_code, br); + JXL_ASSIGN_OR_RETURN(ANSSymbolReader reader, + ANSSymbolReader::Create(&tree_code, br)); JXL_RETURN_IF_ERROR(DecodeTree(br, &reader, tree_context_map, tree, std::min(tree_size_limit, kMaxTreeSize))); if (!reader.CheckANSFinalState()) { diff --git a/third_party/jpeg-xl/lib/jxl/modular/encoding/dec_ma.h b/third_party/jpeg-xl/lib/jxl/modular/encoding/dec_ma.h index a910c4deb1f4..1bce21b32b83 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/encoding/dec_ma.h +++ b/third_party/jpeg-xl/lib/jxl/modular/encoding/dec_ma.h @@ -6,9 +6,10 @@ #ifndef LIB_JXL_MODULAR_ENCODING_DEC_MA_H_ #define LIB_JXL_MODULAR_ENCODING_DEC_MA_H_ -#include -#include +#include +#include +#include #include #include "lib/jxl/base/status.h" @@ -59,7 +60,8 @@ struct PropertyDecisionNode { using Tree = std::vector; -Status DecodeTree(BitReader *br, Tree *tree, size_t tree_size_limit); +Status DecodeTree(JxlMemoryManager *memory_manager, BitReader *br, Tree *tree, + size_t tree_size_limit); } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_encoding.cc b/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_encoding.cc index 84d8137d21a8..86b501849efe 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_encoding.cc +++ b/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_encoding.cc @@ -3,9 +3,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#include -#include +#include +#include +#include #include #include @@ -95,6 +96,7 @@ Status GatherTreeData(const Image &image, pixel_type chan, size_t group_id, const ModularOptions &options, TreeSamples &tree_samples, size_t *total_pixels) { const Channel &channel = image.channel[chan]; + JxlMemoryManager *memory_manager = channel.memory_manager(); JXL_DEBUG_V(7, "Learning %" PRIuS "x%" PRIuS " channel %d", channel.w, channel.h, chan); @@ -128,7 +130,8 @@ Status GatherTreeData(const Image &image, pixel_type chan, size_t group_id, const intptr_t onerow = channel.plane.PixelsPerRow(); JXL_ASSIGN_OR_RETURN( Channel references, - Channel::Create(properties.size() - kNumNonrefProperties, channel.w)); + Channel::Create(memory_manager, properties.size() - kNumNonrefProperties, + channel.w)); weighted::State wp_state(wp_header, channel.w, channel.h); tree_samples.PrepareForSamples(pixel_fraction * channel.h * channel.w + 64); const bool multiple_predictors = tree_samples.NumPredictors() != 1; @@ -214,8 +217,8 @@ Tree PredefinedTree(ModularOptions::TreeKind tree_kind, size_t total_pixels) { tree.push_back(PropertyDecisionNode::Split(0, 2, 3)); // 2: c > 0 tree.push_back(PropertyDecisionNode::Split(0, 0, 5)); - // 3: EPF control field (all 0 or 4), top > 0 - tree.push_back(PropertyDecisionNode::Split(6, 0, 21)); + // 3: EPF control field (all 0 or 4), top > 3 + tree.push_back(PropertyDecisionNode::Split(6, 3, 21)); // 4: ACS+QF, y > 0 tree.push_back(PropertyDecisionNode::Split(2, 0, 7)); // 5: CfL x @@ -241,9 +244,9 @@ Tree PredefinedTree(ModularOptions::TreeKind tree_kind, size_t total_pixels) { tree.push_back(PropertyDecisionNode::Leaf(Predictor::Zero)); tree.push_back(PropertyDecisionNode::Leaf(Predictor::Zero)); tree.push_back(PropertyDecisionNode::Leaf(Predictor::Zero)); - // EPF, left > 0 - tree.push_back(PropertyDecisionNode::Split(7, 0, 23)); - tree.push_back(PropertyDecisionNode::Split(7, 0, 25)); + // EPF, left > 3 + tree.push_back(PropertyDecisionNode::Split(7, 3, 23)); + tree.push_back(PropertyDecisionNode::Split(7, 3, 25)); tree.push_back(PropertyDecisionNode::Leaf(Predictor::Zero)); tree.push_back(PropertyDecisionNode::Leaf(Predictor::Zero)); tree.push_back(PropertyDecisionNode::Leaf(Predictor::Zero)); @@ -304,12 +307,14 @@ Status EncodeModularChannelMAANS(const Image &image, pixel_type chan, AuxOut *aux_out, size_t group_id, bool skip_encoder_fast_path) { const Channel &channel = image.channel[chan]; + JxlMemoryManager *memory_manager = channel.memory_manager(); Token *tokenp = *tokenpp; JXL_ASSERT(channel.w != 0 && channel.h != 0); Image3F predictor_img; if (kWantDebug) { - JXL_ASSIGN_OR_RETURN(predictor_img, Image3F::Create(channel.w, channel.h)); + JXL_ASSIGN_OR_RETURN(predictor_img, + Image3F::Create(memory_manager, channel.w, channel.h)); } JXL_DEBUG_V(6, @@ -452,7 +457,8 @@ Status EncodeModularChannelMAANS(const Image &image, pixel_type chan, const intptr_t onerow = channel.plane.PixelsPerRow(); JXL_ASSIGN_OR_RETURN( Channel references, - Channel::Create(properties.size() - kNumNonrefProperties, channel.w)); + Channel::Create(memory_manager, + properties.size() - kNumNonrefProperties, channel.w)); for (size_t y = 0; y < channel.h; y++) { const pixel_type *JXL_RESTRICT p = channel.Row(y); PrecomputeReferences(channel, y, image, chan, &references); @@ -481,7 +487,8 @@ Status EncodeModularChannelMAANS(const Image &image, pixel_type chan, const intptr_t onerow = channel.plane.PixelsPerRow(); JXL_ASSIGN_OR_RETURN( Channel references, - Channel::Create(properties.size() - kNumNonrefProperties, channel.w)); + Channel::Create(memory_manager, + properties.size() - kNumNonrefProperties, channel.w)); weighted::State wp_state(wp_header, channel.w, channel.h); for (size_t y = 0; y < channel.h; y++) { const pixel_type *JXL_RESTRICT p = channel.Row(y); @@ -528,6 +535,7 @@ Status ModularEncode(const Image &image, const ModularOptions &options, GroupHeader *header, std::vector *tokens, size_t *width) { if (image.error) return JXL_FAILURE("Invalid image"); + JxlMemoryManager *memory_manager = image.memory_manager(); size_t nb_channels = image.channel.size(); JXL_DEBUG_V( 2, "Encoding %" PRIuS "-channel, %i-bit, %" PRIuS "x%" PRIuS " image.", @@ -634,9 +642,9 @@ Status ModularEncode(const Image &image, const ModularOptions &options, } */ // Write tree - BuildAndEncodeHistograms(options.histogram_params, kNumTreeContexts, - tree_tokens, &code, &context_map, writer, - kLayerModularTree, aux_out); + BuildAndEncodeHistograms(memory_manager, options.histogram_params, + kNumTreeContexts, tree_tokens, &code, &context_map, + writer, kLayerModularTree, aux_out); WriteTokens(tree_tokens[0], code, context_map, 0, writer, kLayerModularTree, aux_out); } @@ -683,9 +691,9 @@ Status ModularEncode(const Image &image, const ModularOptions &options, std::vector context_map; HistogramParams histo_params = options.histogram_params; histo_params.image_widths.push_back(image_width); - BuildAndEncodeHistograms(histo_params, (tree->size() + 1) / 2, - tokens_storage, &code, &context_map, writer, layer, - aux_out); + BuildAndEncodeHistograms(memory_manager, histo_params, + (tree->size() + 1) / 2, tokens_storage, &code, + &context_map, writer, layer, aux_out); WriteTokens(tokens_storage[0], code, context_map, 0, writer, layer, aux_out); } else { diff --git a/third_party/jpeg-xl/lib/jxl/modular/encoding/encoding.cc b/third_party/jpeg-xl/lib/jxl/modular/encoding/encoding.cc index 7e7aa019e314..5bd56b118921 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/encoding/encoding.cc +++ b/third_party/jpeg-xl/lib/jxl/modular/encoding/encoding.cc @@ -5,13 +5,15 @@ #include "lib/jxl/modular/encoding/encoding.h" -#include -#include +#include +#include +#include #include #include "lib/jxl/base/printf_macros.h" #include "lib/jxl/base/scope_guard.h" +#include "lib/jxl/base/status.h" #include "lib/jxl/dec_ans.h" #include "lib/jxl/dec_bit_reader.h" #include "lib/jxl/frame_dimensions.h" @@ -140,6 +142,7 @@ Status DecodeModularChannelMAANS(BitReader *br, ANSSymbolReader *reader, pixel_type chan, size_t group_id, TreeLut &tree_lut, Image *image) { + JxlMemoryManager *memory_manager = image->memory_manager(); Channel &channel = image->channel[chan]; std::array static_props = { @@ -380,7 +383,8 @@ Status DecodeModularChannelMAANS(BitReader *br, ANSSymbolReader *reader, const intptr_t onerow = channel.plane.PixelsPerRow(); JXL_ASSIGN_OR_RETURN( Channel references, - Channel::Create(properties.size() - kNumNonrefProperties, channel.w)); + Channel::Create(memory_manager, + properties.size() - kNumNonrefProperties, channel.w)); for (size_t y = 0; y < channel.h; y++) { pixel_type *JXL_RESTRICT p = channel.Row(y); PrecomputeReferences(channel, y, *image, chan, &references); @@ -428,7 +432,8 @@ Status DecodeModularChannelMAANS(BitReader *br, ANSSymbolReader *reader, const intptr_t onerow = channel.plane.PixelsPerRow(); JXL_ASSIGN_OR_RETURN( Channel references, - Channel::Create(properties.size() - kNumNonrefProperties, channel.w)); + Channel::Create(memory_manager, + properties.size() - kNumNonrefProperties, channel.w)); weighted::State wp_state(wp_header, channel.w, channel.h); for (size_t y = 0; y < channel.h; y++) { pixel_type *JXL_RESTRICT p = channel.Row(y); @@ -529,6 +534,7 @@ Status ModularDecode(BitReader *br, Image &image, GroupHeader &header, const std::vector *global_ctx_map, const bool allow_truncated_group) { if (image.channel.empty()) return true; + JxlMemoryManager *memory_manager = image.memory_manager(); // decode transforms Status status = Bundle::Read(br, &header); @@ -603,8 +609,10 @@ Status ModularDecode(BitReader *br, Image &image, GroupHeader &header, max_tree_size += pixels; } max_tree_size = std::min(static_cast(1 << 20), max_tree_size); - JXL_RETURN_IF_ERROR(DecodeTree(br, &tree_storage, max_tree_size)); - JXL_RETURN_IF_ERROR(DecodeHistograms(br, (tree_storage.size() + 1) / 2, + JXL_RETURN_IF_ERROR( + DecodeTree(memory_manager, br, &tree_storage, max_tree_size)); + JXL_RETURN_IF_ERROR(DecodeHistograms(memory_manager, br, + (tree_storage.size() + 1) / 2, &code_storage, &context_map_storage)); } else { if (!global_tree || !global_code || !global_ctx_map || @@ -617,7 +625,8 @@ Status ModularDecode(BitReader *br, Image &image, GroupHeader &header, } // Read channels - ANSSymbolReader reader(code, br, distance_multiplier); + JXL_ASSIGN_OR_RETURN(ANSSymbolReader reader, + ANSSymbolReader::Create(code, br, distance_multiplier)); auto tree_lut = jxl::make_unique>(); for (; next_channel < nb_channels; next_channel++) { Channel &channel = image.channel[next_channel]; diff --git a/third_party/jpeg-xl/lib/jxl/modular/modular_image.cc b/third_party/jpeg-xl/lib/jxl/modular/modular_image.cc index 32a5531080c4..fcb922f96473 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/modular_image.cc +++ b/third_party/jpeg-xl/lib/jxl/modular/modular_image.cc @@ -5,6 +5,8 @@ #include "lib/jxl/modular/modular_image.h" +#include + #include #include "lib/jxl/base/status.h" @@ -29,21 +31,33 @@ void Image::undo_transforms(const weighted::Header &wp_header, } } -Image::Image(size_t iw, size_t ih, int bitdepth) - : w(iw), h(ih), bitdepth(bitdepth), nb_meta_channels(0), error(false) {} +Image::Image(JxlMemoryManager *memory_manager, size_t iw, size_t ih, + int bitdepth) + : w(iw), + h(ih), + bitdepth(bitdepth), + nb_meta_channels(0), + error(false), + memory_manager_(memory_manager) {} -StatusOr Image::Create(size_t iw, size_t ih, int bitdepth, - int nb_chans) { - Image result(iw, ih, bitdepth); +StatusOr Image::Create(JxlMemoryManager *memory_manager, size_t iw, + size_t ih, int bitdepth, int nb_chans) { + Image result(memory_manager, iw, ih, bitdepth); for (int i = 0; i < nb_chans; i++) { - StatusOr channel_or = Channel::Create(iw, ih); + StatusOr channel_or = Channel::Create(memory_manager, iw, ih); JXL_RETURN_IF_ERROR(channel_or.status()); result.channel.emplace_back(std::move(channel_or).value()); } return result; } -Image::Image() : w(0), h(0), bitdepth(8), nb_meta_channels(0), error(true) {} +Image::Image(JxlMemoryManager *memory_manager) + : w(0), + h(0), + bitdepth(8), + nb_meta_channels(0), + error(true), + memory_manager_(memory_manager) {} Image &Image::operator=(Image &&other) noexcept { w = other.w; @@ -57,13 +71,14 @@ Image &Image::operator=(Image &&other) noexcept { } StatusOr Image::Clone(const Image &that) { - Image clone(that.w, that.h, that.bitdepth); + JxlMemoryManager *memory_manager = that.memory_manager(); + Image clone(memory_manager, that.w, that.h, that.bitdepth); clone.nb_meta_channels = that.nb_meta_channels; clone.error = that.error; clone.transform = that.transform; for (const Channel &ch : that.channel) { - JXL_ASSIGN_OR_RETURN(Channel a, - Channel::Create(ch.w, ch.h, ch.hshift, ch.vshift)); + JXL_ASSIGN_OR_RETURN(Channel a, Channel::Create(memory_manager, ch.w, ch.h, + ch.hshift, ch.vshift)); CopyImageTo(ch.plane, &a.plane); clone.channel.push_back(std::move(a)); } diff --git a/third_party/jpeg-xl/lib/jxl/modular/modular_image.h b/third_party/jpeg-xl/lib/jxl/modular/modular_image.h index eb95b1cb6c76..fdae8b9c6b0f 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/modular_image.h +++ b/third_party/jpeg-xl/lib/jxl/modular/modular_image.h @@ -6,10 +6,11 @@ #ifndef LIB_JXL_MODULAR_MODULAR_IMAGE_H_ #define LIB_JXL_MODULAR_MODULAR_IMAGE_H_ -#include -#include -#include +#include +#include +#include +#include #include #include #include @@ -38,10 +39,10 @@ class Channel { Channel(const Channel& other) = delete; Channel& operator=(const Channel& other) = delete; - static StatusOr Create(size_t iw, size_t ih, int hsh = 0, - int vsh = 0) { + static StatusOr Create(JxlMemoryManager* memory_manager, size_t iw, + size_t ih, int hsh = 0, int vsh = 0) { JXL_ASSIGN_OR_RETURN(Plane plane, - Plane::Create(iw, ih)); + Plane::Create(memory_manager, iw, ih)); return Channel(std::move(plane), iw, ih, hsh, vsh); } @@ -58,9 +59,12 @@ class Channel { // Move constructor Channel(Channel&& other) noexcept = default; + JxlMemoryManager* memory_manager() const { return plane.memory_manager(); }; + Status shrink() { if (plane.xsize() == w && plane.ysize() == h) return true; - JXL_ASSIGN_OR_RETURN(plane, Plane::Create(w, h)); + JXL_ASSIGN_OR_RETURN(plane, + Plane::Create(memory_manager(), w, h)); return true; } Status shrink(int nw, int nh) { @@ -95,7 +99,7 @@ class Image { size_t nb_meta_channels; // first few channels might contain palette(s) bool error; // true if a fatal error occurred, false otherwise - Image(); + explicit Image(JxlMemoryManager* memory_manager); Image(const Image& other) = delete; Image& operator=(const Image& other) = delete; @@ -103,8 +107,10 @@ class Image { Image& operator=(Image&& other) noexcept; Image(Image&& other) noexcept = default; - static StatusOr Create(size_t iw, size_t ih, int bitdepth, - int nb_chans); + static StatusOr Create(JxlMemoryManager* memory_manager, size_t iw, + size_t ih, int bitdepth, int nb_chans); + + JxlMemoryManager* memory_manager() const { return memory_manager_; } bool empty() const { for (const auto& ch : channel) { @@ -121,7 +127,8 @@ class Image { std::string DebugString() const; private: - Image(size_t iw, size_t ih, int bitdepth); + Image(JxlMemoryManager* memory_manager, size_t iw, size_t ih, int bitdepth); + JxlMemoryManager* memory_manager_; }; } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/modular/transform/enc_palette.cc b/third_party/jpeg-xl/lib/jxl/modular/transform/enc_palette.cc index 86d161e8913c..e2a9ee65d981 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/transform/enc_palette.cc +++ b/third_party/jpeg-xl/lib/jxl/modular/transform/enc_palette.cc @@ -5,6 +5,8 @@ #include "lib/jxl/modular/transform/enc_palette.h" +#include + #include #include #include @@ -166,6 +168,7 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c, PaletteIterationData &palette_iteration_data) { JXL_QUIET_RETURN_IF_ERROR(CheckEqualChannels(input, begin_c, end_c)); JXL_ASSERT(begin_c >= input.nb_meta_channels); + JxlMemoryManager *memory_manager = input.memory_manager(); uint32_t nb = end_c - begin_c + 1; size_t w = input.channel[begin_c].w; @@ -198,7 +201,8 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c, } } JXL_DEBUG_V(6, "Channel %i uses only %i colors.", begin_c, idx); - JXL_ASSIGN_OR_RETURN(Channel pch, Channel::Create(idx, 1)); + JXL_ASSIGN_OR_RETURN(Channel pch, + Channel::Create(memory_manager, idx, 1)); pch.hshift = -1; pch.vshift = -1; nb_colors = idx; @@ -238,7 +242,7 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c, } } JXL_DEBUG_V(6, "Channel %i uses only %i colors.", begin_c, idx); - JXL_ASSIGN_OR_RETURN(Channel pch, Channel::Create(idx, 1)); + JXL_ASSIGN_OR_RETURN(Channel pch, Channel::Create(memory_manager, idx, 1)); pch.hshift = -1; pch.vshift = -1; nb_colors = idx; @@ -261,10 +265,10 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c, return true; } - Image quantized_input; + Image quantized_input(memory_manager); if (lossy) { - JXL_ASSIGN_OR_RETURN(quantized_input, - Image::Create(w, h, input.bitdepth, nb)); + JXL_ASSIGN_OR_RETURN(quantized_input, Image::Create(memory_manager, w, h, + input.bitdepth, nb)); for (size_t c = 0; c < nb; c++) { CopyImageTo(input.channel[begin_c + c].plane, &quantized_input.channel[c].plane); @@ -383,7 +387,8 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c, JXL_DEBUG_V(6, "Channels %i-%i can be represented using a %i-color palette.", begin_c, end_c, nb_colors); - JXL_ASSIGN_OR_RETURN(Channel pch, Channel::Create(nb_colors, nb)); + JXL_ASSIGN_OR_RETURN(Channel pch, + Channel::Create(memory_manager, nb_colors, nb)); pch.hshift = -1; pch.vshift = -1; pixel_type *JXL_RESTRICT p_palette = pch.Row(0); diff --git a/third_party/jpeg-xl/lib/jxl/modular/transform/enc_squeeze.cc b/third_party/jpeg-xl/lib/jxl/modular/transform/enc_squeeze.cc index 73718307430a..34dcd868eecb 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/transform/enc_squeeze.cc +++ b/third_party/jpeg-xl/lib/jxl/modular/transform/enc_squeeze.cc @@ -5,7 +5,9 @@ #include "lib/jxl/modular/transform/enc_squeeze.h" -#include +#include + +#include #include "lib/jxl/base/data_parallel.h" #include "lib/jxl/modular/modular_image.h" @@ -18,16 +20,17 @@ namespace jxl { Status FwdHSqueeze(Image &input, int c, int rc) { const Channel &chin = input.channel[c]; + JxlMemoryManager *memory_manager = input.memory_manager(); JXL_DEBUG_V(4, "Doing horizontal squeeze of channel %i to new channel %i", c, rc); - JXL_ASSIGN_OR_RETURN( - Channel chout, - Channel::Create((chin.w + 1) / 2, chin.h, chin.hshift + 1, chin.vshift)); - JXL_ASSIGN_OR_RETURN( - Channel chout_residual, - Channel::Create(chin.w - chout.w, chout.h, chin.hshift + 1, chin.vshift)); + JXL_ASSIGN_OR_RETURN(Channel chout, + Channel::Create(memory_manager, (chin.w + 1) / 2, chin.h, + chin.hshift + 1, chin.vshift)); + JXL_ASSIGN_OR_RETURN(Channel chout_residual, + Channel::Create(memory_manager, chin.w - chout.w, + chout.h, chin.hshift + 1, chin.vshift)); for (size_t y = 0; y < chout.h; y++) { const pixel_type *JXL_RESTRICT p_in = chin.Row(y); @@ -66,16 +69,17 @@ Status FwdHSqueeze(Image &input, int c, int rc) { Status FwdVSqueeze(Image &input, int c, int rc) { const Channel &chin = input.channel[c]; + JxlMemoryManager *memory_manager = input.memory_manager(); JXL_DEBUG_V(4, "Doing vertical squeeze of channel %i to new channel %i", c, rc); - JXL_ASSIGN_OR_RETURN( - Channel chout, - Channel::Create(chin.w, (chin.h + 1) / 2, chin.hshift, chin.vshift + 1)); - JXL_ASSIGN_OR_RETURN( - Channel chout_residual, - Channel::Create(chin.w, chin.h - chout.h, chin.hshift, chin.vshift + 1)); + JXL_ASSIGN_OR_RETURN(Channel chout, + Channel::Create(memory_manager, chin.w, (chin.h + 1) / 2, + chin.hshift, chin.vshift + 1)); + JXL_ASSIGN_OR_RETURN(Channel chout_residual, + Channel::Create(memory_manager, chin.w, chin.h - chout.h, + chin.hshift, chin.vshift + 1)); intptr_t onerow_in = chin.plane.PixelsPerRow(); for (size_t y = 0; y < chout_residual.h; y++) { const pixel_type *JXL_RESTRICT p_in = chin.Row(y * 2); diff --git a/third_party/jpeg-xl/lib/jxl/modular/transform/palette.cc b/third_party/jpeg-xl/lib/jxl/modular/transform/palette.cc index 7726fd8ff394..1233fb016c1f 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/transform/palette.cc +++ b/third_party/jpeg-xl/lib/jxl/modular/transform/palette.cc @@ -5,6 +5,8 @@ #include "lib/jxl/modular/transform/palette.h" +#include + #include #include "lib/jxl/modular/transform/transform.h" // CheckEqualChannels @@ -14,6 +16,7 @@ namespace jxl { Status InvPalette(Image &input, uint32_t begin_c, uint32_t nb_colors, uint32_t nb_deltas, Predictor predictor, const weighted::Header &wp_header, ThreadPool *pool) { + JxlMemoryManager *memory_manager = input.memory_manager(); if (input.nb_meta_channels < 1) { return JXL_FAILURE("Error: Palette transform without palette."); } @@ -27,8 +30,9 @@ Status InvPalette(Image &input, uint32_t begin_c, uint32_t nb_colors, size_t h = input.channel[c0].h; if (nb < 1) return JXL_FAILURE("Corrupted transforms"); for (int i = 1; i < nb; i++) { - StatusOr channel_or = Channel::Create( - w, h, input.channel[c0].hshift, input.channel[c0].vshift); + StatusOr channel_or = + Channel::Create(memory_manager, w, h, input.channel[c0].hshift, + input.channel[c0].vshift); JXL_RETURN_IF_ERROR(channel_or.status()); input.channel.insert(input.channel.begin() + c0 + 1, std::move(channel_or).value()); @@ -84,7 +88,8 @@ Status InvPalette(Image &input, uint32_t begin_c, uint32_t nb_colors, // Parallelized per channel. ImageI indices; ImageI &plane = input.channel[c0].plane; - JXL_ASSIGN_OR_RETURN(indices, ImageI::Create(plane.xsize(), plane.ysize())); + JXL_ASSIGN_OR_RETURN( + indices, ImageI::Create(memory_manager, plane.xsize(), plane.ysize())); plane.Swap(indices); if (predictor == Predictor::Weighted) { JXL_RETURN_IF_ERROR(RunOnPool( @@ -163,6 +168,7 @@ Status InvPalette(Image &input, uint32_t begin_c, uint32_t nb_colors, Status MetaPalette(Image &input, uint32_t begin_c, uint32_t end_c, uint32_t nb_colors, uint32_t nb_deltas, bool lossy) { JXL_RETURN_IF_ERROR(CheckEqualChannels(input, begin_c, end_c)); + JxlMemoryManager *memory_manager = input.memory_manager(); size_t nb = end_c - begin_c + 1; if (begin_c >= input.nb_meta_channels) { @@ -176,7 +182,8 @@ Status MetaPalette(Image &input, uint32_t begin_c, uint32_t end_c, } input.channel.erase(input.channel.begin() + begin_c + 1, input.channel.begin() + end_c + 1); - JXL_ASSIGN_OR_RETURN(Channel pch, Channel::Create(nb_colors + nb_deltas, nb)); + JXL_ASSIGN_OR_RETURN( + Channel pch, Channel::Create(memory_manager, nb_colors + nb_deltas, nb)); pch.hshift = -1; pch.vshift = -1; input.channel.insert(input.channel.begin(), std::move(pch)); diff --git a/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.cc b/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.cc index b71c8dc248ba..d1516c35a8d8 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.cc +++ b/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.cc @@ -5,7 +5,9 @@ #include "lib/jxl/modular/transform/squeeze.h" -#include +#include + +#include #include "lib/jxl/base/common.h" #include "lib/jxl/base/data_parallel.h" @@ -105,6 +107,7 @@ Status InvHSqueeze(Image &input, uint32_t c, uint32_t rc, ThreadPool *pool) { // These must be valid since we ran MetaApply already. JXL_ASSERT(chin.w == DivCeil(chin.w + chin_residual.w, 2)); JXL_ASSERT(chin.h == chin_residual.h); + JxlMemoryManager *memory_manager = input.memory_manager(); if (chin_residual.w == 0) { // Short-circuit: output channel has same dimensions as input. @@ -114,8 +117,8 @@ Status InvHSqueeze(Image &input, uint32_t c, uint32_t rc, ThreadPool *pool) { // Note: chin.w >= chin_residual.w and at most 1 different. JXL_ASSIGN_OR_RETURN(Channel chout, - Channel::Create(chin.w + chin_residual.w, chin.h, - chin.hshift - 1, chin.vshift)); + Channel::Create(memory_manager, chin.w + chin_residual.w, + chin.h, chin.hshift - 1, chin.vshift)); JXL_DEBUG_V(4, "Undoing horizontal squeeze of channel %i using residuals in " "channel %i (going from width %" PRIuS " to %" PRIuS ")", @@ -216,6 +219,7 @@ Status InvVSqueeze(Image &input, uint32_t c, uint32_t rc, ThreadPool *pool) { // These must be valid since we ran MetaApply already. JXL_ASSERT(chin.h == DivCeil(chin.h + chin_residual.h, 2)); JXL_ASSERT(chin.w == chin_residual.w); + JxlMemoryManager *memory_manager = input.memory_manager(); if (chin_residual.h == 0) { // Short-circuit: output channel has same dimensions as input. @@ -224,9 +228,10 @@ Status InvVSqueeze(Image &input, uint32_t c, uint32_t rc, ThreadPool *pool) { } // Note: chin.h >= chin_residual.h and at most 1 different. - JXL_ASSIGN_OR_RETURN(Channel chout, - Channel::Create(chin.w, chin.h + chin_residual.h, - chin.hshift, chin.vshift - 1)); + JXL_ASSIGN_OR_RETURN( + Channel chout, + Channel::Create(memory_manager, chin.w, chin.h + chin_residual.h, + chin.hshift, chin.vshift - 1)); JXL_DEBUG_V( 4, "Undoing vertical squeeze of channel %i using residuals in channel " @@ -420,6 +425,7 @@ Status CheckMetaSqueezeParams(const SqueezeParams ¶meter, } Status MetaSqueeze(Image &image, std::vector *parameters) { + JxlMemoryManager *memory_manager = image.memory_manager(); if (parameters->empty()) { DefaultSqueezeParameters(parameters, image); } @@ -465,7 +471,8 @@ Status MetaSqueeze(Image &image, std::vector *parameters) { h = h - (h + 1) / 2; } JXL_RETURN_IF_ERROR(image.channel[c].shrink()); - JXL_ASSIGN_OR_RETURN(Channel placeholder, Channel::Create(w, h)); + JXL_ASSIGN_OR_RETURN(Channel placeholder, + Channel::Create(memory_manager, w, h)); placeholder.hshift = image.channel[c].hshift; placeholder.vshift = image.channel[c].vshift; diff --git a/third_party/jpeg-xl/lib/jxl/modular_test.cc b/third_party/jpeg-xl/lib/jxl/modular_test.cc index ceebf59c0bb6..cdb9c30ac90a 100644 --- a/third_party/jpeg-xl/lib/jxl/modular_test.cc +++ b/third_party/jpeg-xl/lib/jxl/modular_test.cc @@ -5,11 +5,13 @@ #include #include +#include #include #include #include #include +#include #include #include #include @@ -20,6 +22,7 @@ #include "lib/extras/enc/jxl.h" #include "lib/extras/metrics.h" #include "lib/extras/packed_image.h" +#include "lib/jxl/base/common.h" #include "lib/jxl/base/compiler_specific.h" #include "lib/jxl/base/data_parallel.h" #include "lib/jxl/base/random.h" @@ -114,6 +117,7 @@ TEST(ModularTest, RoundtripLosslessCustomWpPermuteRCT) { } TEST(ModularTest, RoundtripLossyDeltaPalette) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); const std::vector orig = ReadTestData("external/wesaturate/500px/u76c0g_bliznaca_srgb8.png"); CompressParams cparams; @@ -122,9 +126,9 @@ TEST(ModularTest, RoundtripLossyDeltaPalette) { cparams.lossy_palette = true; cparams.palette_colors = 0; - CodecInOut io_out; + CodecInOut io_out{memory_manager}; - CodecInOut io; + CodecInOut io{memory_manager}; ASSERT_TRUE(SetFromBytes(Bytes(orig), &io)); io.ShrinkTo(300, 100); @@ -138,6 +142,7 @@ TEST(ModularTest, RoundtripLossyDeltaPalette) { 1.5); } TEST(ModularTest, RoundtripLossyDeltaPaletteWP) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); const std::vector orig = ReadTestData("external/wesaturate/500px/u76c0g_bliznaca_srgb8.png"); CompressParams cparams; @@ -147,9 +152,9 @@ TEST(ModularTest, RoundtripLossyDeltaPaletteWP) { // TODO(jon): this is currently ignored, and Avg4 is always used instead cparams.options.predictor = jxl::Predictor::Weighted; - CodecInOut io_out; + CodecInOut io_out{memory_manager}; - CodecInOut io; + CodecInOut io{memory_manager}; ASSERT_TRUE(SetFromBytes(Bytes(orig), &io)); io.ShrinkTo(300, 100); @@ -164,6 +169,7 @@ TEST(ModularTest, RoundtripLossyDeltaPaletteWP) { } TEST(ModularTest, RoundtripLossy) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); const std::vector orig = ReadTestData("external/wesaturate/500px/u76c0g_bliznaca_srgb8.png"); CompressParams cparams; @@ -171,9 +177,9 @@ TEST(ModularTest, RoundtripLossy) { cparams.butteraugli_distance = 2.f; cparams.SetCms(*JxlGetDefaultCms()); - CodecInOut io_out; + CodecInOut io_out{memory_manager}; - CodecInOut io; + CodecInOut io{memory_manager}; ASSERT_TRUE(SetFromBytes(Bytes(orig), &io)); size_t compressed_size; @@ -187,15 +193,16 @@ TEST(ModularTest, RoundtripLossy) { } TEST(ModularTest, RoundtripLossy16) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); const std::vector orig = ReadTestData("external/raw.pixls/DJI-FC6310-16bit_709_v4_krita.png"); CompressParams cparams; cparams.modular_mode = true; cparams.butteraugli_distance = 2.f; - CodecInOut io_out; + CodecInOut io_out{memory_manager}; - CodecInOut io; + CodecInOut io{memory_manager}; ASSERT_TRUE(SetFromBytes(Bytes(orig), &io)); JXL_CHECK(!io.metadata.m.have_preview); JXL_CHECK(io.frames.size() == 1); @@ -214,9 +221,10 @@ TEST(ModularTest, RoundtripLossy16) { } TEST(ModularTest, RoundtripExtraProperties) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); constexpr size_t kSize = 250; - JXL_ASSIGN_OR_DIE(Image image, - Image::Create(kSize, kSize, /*bitdepth=*/8, 3)); + JXL_ASSIGN_OR_DIE(Image image, Image::Create(memory_manager, kSize, kSize, + /*bitdepth=*/8, 3)); ModularOptions options; options.max_properties = 4; options.predictor = Predictor::Zero; @@ -228,15 +236,17 @@ TEST(ModularTest, RoundtripExtraProperties) { } } ZeroFillImage(&image.channel[1].plane); - BitWriter writer; + BitWriter writer{memory_manager}; ASSERT_TRUE(ModularGenericCompress(image, options, &writer)); writer.ZeroPadToByte(); - JXL_ASSIGN_OR_DIE(Image decoded, Image::Create(kSize, kSize, /*bitdepth=*/8, - image.channel.size())); + JXL_ASSIGN_OR_DIE(Image decoded, + Image::Create(memory_manager, kSize, kSize, + /*bitdepth=*/8, image.channel.size())); for (size_t i = 0; i < image.channel.size(); i++) { const Channel& ch = image.channel[i]; - JXL_ASSIGN_OR_DIE(decoded.channel[i], - Channel::Create(ch.w, ch.h, ch.hshift, ch.vshift)); + JXL_ASSIGN_OR_DIE( + decoded.channel[i], + Channel::Create(memory_manager, ch.w, ch.h, ch.hshift, ch.vshift)); } Status status = true; { @@ -288,6 +298,7 @@ JXL_GTEST_INSTANTIATE_TEST_SUITE_P(RoundtripLossless, ModularTestParam, LosslessTestDescription); TEST_P(ModularTestParam, RoundtripLossless) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); RoundtripLosslessConfig config = GetParam(); int bitdepth = config.bitdepth; int responsive = config.responsive; @@ -296,7 +307,7 @@ TEST_P(ModularTestParam, RoundtripLossless) { Rng generator(123); const std::vector orig = ReadTestData("external/wesaturate/500px/u76c0g_bliznaca_srgb8.png"); - CodecInOut io1; + CodecInOut io1{memory_manager}; ASSERT_TRUE(SetFromBytes(Bytes(orig), &io1, pool)); // vary the dimensions a bit, in case of bugs related to @@ -304,14 +315,15 @@ TEST_P(ModularTestParam, RoundtripLossless) { size_t xsize = 423 + bitdepth; size_t ysize = 467 + bitdepth; - CodecInOut io; + CodecInOut io{memory_manager}; io.SetSize(xsize, ysize); io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false); io.metadata.m.SetUintSamples(bitdepth); double factor = ((1lu << bitdepth) - 1lu); double ifactor = 1.0 / factor; - JXL_ASSIGN_OR_DIE(Image3F noise_added, Image3F::Create(xsize, ysize)); + JXL_ASSIGN_OR_DIE(Image3F noise_added, + Image3F::Create(memory_manager, xsize, ysize)); for (size_t c = 0; c < 3; c++) { for (size_t y = 0; y < ysize; y++) { @@ -336,7 +348,7 @@ TEST_P(ModularTestParam, RoundtripLossless) { cparams.options.predictor = {Predictor::Zero}; cparams.speed_tier = SpeedTier::kThunder; cparams.responsive = responsive; - CodecInOut io2; + CodecInOut io2{memory_manager}; size_t compressed_size; JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _, &compressed_size)); EXPECT_LE(compressed_size, bitdepth * xsize * ysize / 3.0 * 1.1); @@ -358,7 +370,8 @@ TEST_P(ModularTestParam, RoundtripLossless) { } TEST(ModularTest, RoundtripLosslessCustomFloat) { - CodecInOut io; + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + CodecInOut io{memory_manager}; size_t xsize = 100; size_t ysize = 300; io.SetSize(xsize, ysize); @@ -369,7 +382,8 @@ TEST(ModularTest, RoundtripLosslessCustomFloat) { ColorEncoding color_encoding; color_encoding.Tf().SetTransferFunction(TransferFunction::kLinear); color_encoding.SetColorSpace(ColorSpace::kRGB); - JXL_ASSIGN_OR_DIE(Image3F testimage, Image3F::Create(xsize, ysize)); + JXL_ASSIGN_OR_DIE(Image3F testimage, + Image3F::Create(memory_manager, xsize, ysize)); float factor = 1.f / (1 << 14); for (size_t c = 0; c < 3; c++) { for (size_t y = 0; y < ysize; y++) { @@ -391,7 +405,7 @@ TEST(ModularTest, RoundtripLosslessCustomFloat) { cparams.speed_tier = SpeedTier::kThunder; cparams.decoding_speed_tier = 2; - CodecInOut io2; + CodecInOut io2{memory_manager}; size_t compressed_size; JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _, &compressed_size)); EXPECT_LE(compressed_size, 23000u); @@ -446,24 +460,26 @@ void WriteHistograms(BitWriter* writer) { } TEST(ModularTest, PredictorIntegerOverflow) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); const size_t xsize = 1; const size_t ysize = 1; - BitWriter writer; + BitWriter writer{memory_manager}; WriteHeaders(&writer, xsize, ysize); - std::vector group_codes(1); + std::vector> group_codes; + group_codes.emplace_back(jxl::make_unique(memory_manager)); { - BitWriter* bw = group_codes.data(); - BitWriter::Allotment allotment(bw, 1 << 20); - WriteHistograms(bw); + std::unique_ptr& bw = group_codes[0]; + BitWriter::Allotment allotment(bw.get(), 1 << 20); + WriteHistograms(bw.get()); GroupHeader header; header.use_global_tree = true; - EXPECT_TRUE(Bundle::Write(header, bw, 0, nullptr)); + EXPECT_TRUE(Bundle::Write(header, bw.get(), 0, nullptr)); // After UnpackSigned this becomes (1 << 31) - 1, the largest pixel_type, // and after adding the offset we get -(1 << 31). bw->Write(8, 119); bw->Write(28, 0xfffffff); bw->ZeroPadToByte(); - allotment.ReclaimAndCharge(bw, 0, nullptr); + allotment.ReclaimAndCharge(bw.get(), 0, nullptr); } EXPECT_TRUE(WriteGroupOffsets(group_codes, {}, &writer, nullptr)); writer.AppendByteAligned(group_codes); @@ -481,16 +497,18 @@ TEST(ModularTest, PredictorIntegerOverflow) { } TEST(ModularTest, UnsqueezeIntegerOverflow) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); // Image width is 9 so we can test both the SIMD and non-vector code paths. const size_t xsize = 9; const size_t ysize = 2; - BitWriter writer; + BitWriter writer{memory_manager}; WriteHeaders(&writer, xsize, ysize); - std::vector group_codes(1); + std::vector> group_codes; + group_codes.emplace_back(jxl::make_unique(memory_manager)); { - BitWriter* bw = group_codes.data(); - BitWriter::Allotment allotment(bw, 1 << 20); - WriteHistograms(bw); + std::unique_ptr& bw = group_codes[0]; + BitWriter::Allotment allotment(bw.get(), 1 << 20); + WriteHistograms(bw.get()); GroupHeader header; header.use_global_tree = true; header.transforms.emplace_back(); @@ -501,7 +519,7 @@ TEST(ModularTest, UnsqueezeIntegerOverflow) { params.begin_c = 0; params.num_c = 1; header.transforms[0].squeezes.emplace_back(params); - EXPECT_TRUE(Bundle::Write(header, bw, 0, nullptr)); + EXPECT_TRUE(Bundle::Write(header, bw.get(), 0, nullptr)); for (size_t i = 0; i < xsize * ysize; ++i) { // After UnpackSigned and adding offset, this becomes (1 << 31) - 1, both // in the image and in the residual channels, and unsqueeze makes them @@ -511,7 +529,7 @@ TEST(ModularTest, UnsqueezeIntegerOverflow) { bw->Write(28, 0xffffffe); } bw->ZeroPadToByte(); - allotment.ReclaimAndCharge(bw, 0, nullptr); + allotment.ReclaimAndCharge(bw.get(), 0, nullptr); } EXPECT_TRUE(WriteGroupOffsets(group_codes, {}, &writer, nullptr)); writer.AppendByteAligned(group_codes); diff --git a/third_party/jpeg-xl/lib/jxl/opsin_image_test.cc b/third_party/jpeg-xl/lib/jxl/opsin_image_test.cc index dff257f9bc22..8a34b8de6681 100644 --- a/third_party/jpeg-xl/lib/jxl/opsin_image_test.cc +++ b/third_party/jpeg-xl/lib/jxl/opsin_image_test.cc @@ -4,6 +4,7 @@ // license that can be found in the LICENSE file. #include +#include #include #include @@ -19,6 +20,7 @@ #include "lib/jxl/image_bundle.h" #include "lib/jxl/image_metadata.h" #include "lib/jxl/opsin_params.h" +#include "lib/jxl/test_utils.h" #include "lib/jxl/testing.h" namespace jxl { @@ -29,7 +31,8 @@ namespace { void LinearSrgbToOpsin(float rgb_r, float rgb_g, float rgb_b, float* JXL_RESTRICT xyb_x, float* JXL_RESTRICT xyb_y, float* JXL_RESTRICT xyb_b) { - JXL_ASSIGN_OR_DIE(Image3F linear, Image3F::Create(1, 1)); + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + JXL_ASSIGN_OR_DIE(Image3F linear, Image3F::Create(memory_manager, 1, 1)); linear.PlaneRow(0, 0)[0] = rgb_r; linear.PlaneRow(1, 0)[0] = rgb_g; linear.PlaneRow(2, 0)[0] = rgb_b; @@ -37,9 +40,9 @@ void LinearSrgbToOpsin(float rgb_r, float rgb_g, float rgb_b, ImageMetadata metadata; metadata.SetFloat32Samples(); metadata.color_encoding = ColorEncoding::LinearSRGB(); - ImageBundle ib(&metadata); + ImageBundle ib(memory_manager, &metadata); ib.SetFromImage(std::move(linear), metadata.color_encoding); - JXL_ASSIGN_OR_DIE(Image3F opsin, Image3F::Create(1, 1)); + JXL_ASSIGN_OR_DIE(Image3F opsin, Image3F::Create(memory_manager, 1, 1)); (void)ToXYB(ib, /*pool=*/nullptr, &opsin, *JxlGetDefaultCms()); *xyb_x = opsin.PlaneRow(0, 0)[0]; @@ -52,11 +55,12 @@ void LinearSrgbToOpsin(float rgb_r, float rgb_g, float rgb_b, void OpsinToLinearSrgb(float xyb_x, float xyb_y, float xyb_b, float* JXL_RESTRICT rgb_r, float* JXL_RESTRICT rgb_g, float* JXL_RESTRICT rgb_b) { - JXL_ASSIGN_OR_DIE(Image3F opsin, Image3F::Create(1, 1)); + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + JXL_ASSIGN_OR_DIE(Image3F opsin, Image3F::Create(memory_manager, 1, 1)); opsin.PlaneRow(0, 0)[0] = xyb_x; opsin.PlaneRow(1, 0)[0] = xyb_y; opsin.PlaneRow(2, 0)[0] = xyb_b; - JXL_ASSIGN_OR_DIE(Image3F linear, Image3F::Create(1, 1)); + JXL_ASSIGN_OR_DIE(Image3F linear, Image3F::Create(memory_manager, 1, 1)); OpsinParams opsin_params; opsin_params.Init(/*intensity_target=*/255.0f); OpsinToLinear(opsin, Rect(opsin), nullptr, &linear, opsin_params); diff --git a/third_party/jpeg-xl/lib/jxl/opsin_inverse_test.cc b/third_party/jpeg-xl/lib/jxl/opsin_inverse_test.cc index 4407658ddf85..b12c80df07b1 100644 --- a/third_party/jpeg-xl/lib/jxl/opsin_inverse_test.cc +++ b/third_party/jpeg-xl/lib/jxl/opsin_inverse_test.cc @@ -4,6 +4,7 @@ // license that can be found in the LICENSE file. #include +#include #include @@ -17,23 +18,26 @@ #include "lib/jxl/image.h" #include "lib/jxl/image_ops.h" #include "lib/jxl/image_test_utils.h" +#include "lib/jxl/test_utils.h" #include "lib/jxl/testing.h" namespace jxl { namespace { TEST(OpsinInverseTest, LinearInverseInverts) { - JXL_ASSIGN_OR_DIE(Image3F linear, Image3F::Create(128, 128)); + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + JXL_ASSIGN_OR_DIE(Image3F linear, Image3F::Create(memory_manager, 128, 128)); RandomFillImage(&linear, 0.0f, 1.0f); - CodecInOut io; + CodecInOut io{memory_manager}; io.metadata.m.SetFloat32Samples(); io.metadata.m.color_encoding = ColorEncoding::LinearSRGB(); - JXL_ASSIGN_OR_DIE(Image3F linear2, Image3F::Create(128, 128)); + JXL_ASSIGN_OR_DIE(Image3F linear2, Image3F::Create(memory_manager, 128, 128)); CopyImageTo(linear, &linear2); io.SetFromImage(std::move(linear2), io.metadata.m.color_encoding); ThreadPool* null_pool = nullptr; - JXL_ASSIGN_OR_DIE(Image3F opsin, Image3F::Create(io.xsize(), io.ysize())); + JXL_ASSIGN_OR_DIE(Image3F opsin, + Image3F::Create(memory_manager, io.xsize(), io.ysize())); (void)ToXYB(io.Main(), null_pool, &opsin, *JxlGetDefaultCms()); OpsinParams opsin_params; @@ -44,16 +48,19 @@ TEST(OpsinInverseTest, LinearInverseInverts) { } TEST(OpsinInverseTest, YcbCrInverts) { - JXL_ASSIGN_OR_DIE(Image3F rgb, Image3F::Create(128, 128)); + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + JXL_ASSIGN_OR_DIE(Image3F rgb, Image3F::Create(memory_manager, 128, 128)); RandomFillImage(&rgb, 0.0f, 1.0f); ThreadPool* null_pool = nullptr; - JXL_ASSIGN_OR_DIE(Image3F ycbcr, Image3F::Create(rgb.xsize(), rgb.ysize())); + JXL_ASSIGN_OR_DIE(Image3F ycbcr, + Image3F::Create(memory_manager, rgb.xsize(), rgb.ysize())); EXPECT_TRUE(RgbToYcbcr(rgb.Plane(0), rgb.Plane(1), rgb.Plane(2), &ycbcr.Plane(1), &ycbcr.Plane(0), &ycbcr.Plane(2), null_pool)); - JXL_ASSIGN_OR_DIE(Image3F rgb2, Image3F::Create(rgb.xsize(), rgb.ysize())); + JXL_ASSIGN_OR_DIE(Image3F rgb2, + Image3F::Create(memory_manager, rgb.xsize(), rgb.ysize())); YcbcrToRgb(ycbcr, &rgb2, Rect(rgb)); JXL_ASSERT_OK(VerifyRelativeError(rgb, rgb2, 4E-5, 4E-7, _)); diff --git a/third_party/jpeg-xl/lib/jxl/padded_bytes.h b/third_party/jpeg-xl/lib/jxl/padded_bytes.h index 38167ed40804..321fb6cd9a74 100644 --- a/third_party/jpeg-xl/lib/jxl/padded_bytes.h +++ b/third_party/jpeg-xl/lib/jxl/padded_bytes.h @@ -8,11 +8,12 @@ // std::vector replacement with padding to reduce bounds checks in WriteBits -#include -#include -#include // memcpy +#include #include // max +#include +#include +#include // memcpy #include #include // swap @@ -29,25 +30,31 @@ namespace jxl { class PaddedBytes { public: // Required for output params. - PaddedBytes() : size_(0), capacity_(0) {} + explicit PaddedBytes(JxlMemoryManager* memory_manager) + : memory_manager_(memory_manager), size_(0), capacity_(0) {} - explicit PaddedBytes(size_t size) : size_(size), capacity_(0) { + PaddedBytes(JxlMemoryManager* memory_manager, size_t size) + : memory_manager_(memory_manager), size_(size), capacity_(0) { reserve(size); } - PaddedBytes(size_t size, uint8_t value) : size_(size), capacity_(0) { + PaddedBytes(JxlMemoryManager* memory_manager, size_t size, uint8_t value) + : memory_manager_(memory_manager), size_(size), capacity_(0) { reserve(size); if (size_ != 0) { memset(data(), value, size); } } - PaddedBytes(const PaddedBytes& other) : size_(other.size_), capacity_(0) { + PaddedBytes(const PaddedBytes& other) + : memory_manager_(other.memory_manager_), + size_(other.size_), + capacity_(0) { reserve(size_); if (data() != nullptr) memcpy(data(), other.data(), size_); } PaddedBytes& operator=(const PaddedBytes& other) { - // Self-assignment is safe. + if (this == &other) return *this; resize(other.size()); if (data() != nullptr) memmove(data(), other.data(), size_); return *this; @@ -55,12 +62,14 @@ class PaddedBytes { // default is not OK - need to set other.size_ to 0! PaddedBytes(PaddedBytes&& other) noexcept - : size_(other.size_), + : memory_manager_(other.memory_manager_), + size_(other.size_), capacity_(other.capacity_), data_(std::move(other.data_)) { other.size_ = other.capacity_ = 0; } PaddedBytes& operator=(PaddedBytes&& other) noexcept { + memory_manager_ = other.memory_manager_; size_ = other.size_; capacity_ = other.capacity_; data_ = std::move(other.data_); @@ -71,7 +80,10 @@ class PaddedBytes { return *this; } + JxlMemoryManager* memory_manager() const { return memory_manager_; } + void swap(PaddedBytes& other) noexcept { + std::swap(memory_manager_, other.memory_manager_); std::swap(size_, other.size_); std::swap(capacity_, other.capacity_); std::swap(data_, other.data_); @@ -198,6 +210,7 @@ class PaddedBytes { JXL_ASSERT(i <= size()); } + JxlMemoryManager* memory_manager_; size_t size_; size_t capacity_; CacheAlignedUniquePtr data_; diff --git a/third_party/jpeg-xl/lib/jxl/padded_bytes_test.cc b/third_party/jpeg-xl/lib/jxl/padded_bytes_test.cc index 83d1da9c254a..0791964d9f12 100644 --- a/third_party/jpeg-xl/lib/jxl/padded_bytes_test.cc +++ b/third_party/jpeg-xl/lib/jxl/padded_bytes_test.cc @@ -5,16 +5,17 @@ #include "lib/jxl/padded_bytes.h" -#include // iota -#include +#include +#include "lib/jxl/test_utils.h" #include "lib/jxl/testing.h" namespace jxl { namespace { TEST(PaddedBytesTest, TestNonEmptyFirstByteZero) { - PaddedBytes pb(1); + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + PaddedBytes pb(memory_manager, 1); EXPECT_EQ(0, pb[0]); // Even after resizing.. pb.resize(20); @@ -25,14 +26,16 @@ TEST(PaddedBytesTest, TestNonEmptyFirstByteZero) { } TEST(PaddedBytesTest, TestEmptyFirstByteZero) { - PaddedBytes pb(0); + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + PaddedBytes pb(memory_manager, 0); // After resizing - new zero is written despite there being nothing to copy. pb.resize(20); EXPECT_EQ(0, pb[0]); } TEST(PaddedBytesTest, TestFillWithoutReserve) { - PaddedBytes pb; + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + PaddedBytes pb{memory_manager}; for (size_t i = 0; i < 170u; ++i) { pb.push_back(i); } @@ -41,7 +44,8 @@ TEST(PaddedBytesTest, TestFillWithoutReserve) { } TEST(PaddedBytesTest, TestFillWithExactReserve) { - PaddedBytes pb; + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + PaddedBytes pb{memory_manager}; pb.reserve(170); for (size_t i = 0; i < 170u; ++i) { pb.push_back(i); @@ -51,7 +55,8 @@ TEST(PaddedBytesTest, TestFillWithExactReserve) { } TEST(PaddedBytesTest, TestFillWithMoreReserve) { - PaddedBytes pb; + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + PaddedBytes pb{memory_manager}; pb.reserve(171); for (size_t i = 0; i < 170u; ++i) { pb.push_back(i); diff --git a/third_party/jpeg-xl/lib/jxl/passes_state.cc b/third_party/jpeg-xl/lib/jxl/passes_state.cc index 5da731b48e8e..2d2a95ab5119 100644 --- a/third_party/jpeg-xl/lib/jxl/passes_state.cc +++ b/third_party/jpeg-xl/lib/jxl/passes_state.cc @@ -5,6 +5,8 @@ #include "lib/jxl/passes_state.h" +#include + #include "lib/jxl/base/status.h" #include "lib/jxl/chroma_from_luma.h" #include "lib/jxl/coeff_order.h" @@ -21,18 +23,21 @@ Status InitializePassesSharedState(const FrameHeader& frame_header, shared->image_features.patches.SetPassesSharedState(shared); const FrameDimensions& frame_dim = shared->frame_dim; + JxlMemoryManager* memory_manager = shared->memory_manager; JXL_ASSIGN_OR_RETURN( shared->ac_strategy, - AcStrategyImage::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); + AcStrategyImage::Create(memory_manager, frame_dim.xsize_blocks, + frame_dim.ysize_blocks)); + JXL_ASSIGN_OR_RETURN(shared->raw_quant_field, + ImageI::Create(memory_manager, frame_dim.xsize_blocks, + frame_dim.ysize_blocks)); + JXL_ASSIGN_OR_RETURN(shared->epf_sharpness, + ImageB::Create(memory_manager, frame_dim.xsize_blocks, + frame_dim.ysize_blocks)); JXL_ASSIGN_OR_RETURN( - shared->raw_quant_field, - ImageI::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); - JXL_ASSIGN_OR_RETURN( - shared->epf_sharpness, - ImageB::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); - JXL_ASSIGN_OR_RETURN(shared->cmap, ColorCorrelationMap::Create( - frame_dim.xsize, frame_dim.ysize)); + shared->cmap, ColorCorrelationMap::Create(memory_manager, frame_dim.xsize, + frame_dim.ysize)); // In the decoder, we allocate coeff orders afterwards, when we know how many // we will actually need. @@ -45,9 +50,9 @@ Status InitializePassesSharedState(const FrameHeader& frame_header, kCoeffOrderMaxSize); } - JXL_ASSIGN_OR_RETURN( - shared->quant_dc, - ImageB::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); + JXL_ASSIGN_OR_RETURN(shared->quant_dc, + ImageB::Create(memory_manager, frame_dim.xsize_blocks, + frame_dim.ysize_blocks)); bool use_dc_frame = ((frame_header.flags & FrameHeader::kUseDcFrame) != 0u); if (!encoder && use_dc_frame) { @@ -65,9 +70,9 @@ Status InitializePassesSharedState(const FrameHeader& frame_header, } ZeroFillImage(&shared->quant_dc); } else { - JXL_ASSIGN_OR_RETURN( - shared->dc_storage, - Image3F::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); + JXL_ASSIGN_OR_RETURN(shared->dc_storage, + Image3F::Create(memory_manager, frame_dim.xsize_blocks, + frame_dim.ysize_blocks)); shared->dc = &shared->dc_storage; } diff --git a/third_party/jpeg-xl/lib/jxl/passes_state.h b/third_party/jpeg-xl/lib/jxl/passes_state.h index ffb213d4a42b..4ec61f259ef1 100644 --- a/third_party/jpeg-xl/lib/jxl/passes_state.h +++ b/third_party/jpeg-xl/lib/jxl/passes_state.h @@ -6,14 +6,19 @@ #ifndef LIB_JXL_PASSES_STATE_H_ #define LIB_JXL_PASSES_STATE_H_ +#include + +#include +#include + #include "lib/jxl/ac_context.h" #include "lib/jxl/ac_strategy.h" +#include "lib/jxl/base/common.h" #include "lib/jxl/chroma_from_luma.h" #include "lib/jxl/dec_patch_dictionary.h" #include "lib/jxl/frame_header.h" #include "lib/jxl/image.h" #include "lib/jxl/image_bundle.h" -#include "lib/jxl/loop_filter.h" #include "lib/jxl/noise.h" #include "lib/jxl/quant_weights.h" #include "lib/jxl/quantizer.h" @@ -33,6 +38,14 @@ struct ImageFeatures { // State common to both encoder and decoder. // NOLINTNEXTLINE(clang-analyzer-optin.performance.Padding) struct PassesSharedState { + explicit PassesSharedState(JxlMemoryManager* memory_manager_) + : memory_manager(memory_manager_) { + for (auto& reference_frame : reference_frames) { + reference_frame.frame = jxl::make_unique(memory_manager_); + } + } + + JxlMemoryManager* memory_manager; const CodecMetadata* metadata; FrameDimensions frame_dim; @@ -68,11 +81,12 @@ struct PassesSharedState { Image3F dc_frames[4]; - struct { - ImageBundle frame; + struct ReferceFrame { + std::unique_ptr frame; // ImageBundle doesn't yet have a simple way to state it is in XYB. bool ib_is_in_xyb = false; - } reference_frames[4] = {}; + }; + std::array reference_frames; // Number of pre-clustered set of histograms (with the same ctx map), per // pass. Encoded as num_histograms_ - 1. diff --git a/third_party/jpeg-xl/lib/jxl/passes_test.cc b/third_party/jpeg-xl/lib/jxl/passes_test.cc index a52598701869..25d06a88665c 100644 --- a/third_party/jpeg-xl/lib/jxl/passes_test.cc +++ b/third_party/jpeg-xl/lib/jxl/passes_test.cc @@ -4,6 +4,7 @@ // license that can be found in the LICENSE file. #include +#include #include #include @@ -35,9 +36,10 @@ using test::ThreadPoolForTests; namespace { TEST(PassesTest, RoundtripSmallPasses) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); const std::vector orig = ReadTestData("external/wesaturate/500px/u76c0g_bliznaca_srgb8.png"); - CodecInOut io; + CodecInOut io{memory_manager}; ASSERT_TRUE(SetFromBytes(Bytes(orig), &io)); io.ShrinkTo(io.xsize() / 8, io.ysize() / 8); @@ -46,7 +48,7 @@ TEST(PassesTest, RoundtripSmallPasses) { cparams.progressive_mode = Override::kOn; cparams.SetCms(*JxlGetDefaultCms()); - CodecInOut io2; + CodecInOut io2{memory_manager}; JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _)); EXPECT_SLIGHTLY_BELOW( ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(), @@ -56,9 +58,10 @@ TEST(PassesTest, RoundtripSmallPasses) { } TEST(PassesTest, RoundtripUnalignedPasses) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); const std::vector orig = ReadTestData("external/wesaturate/500px/u76c0g_bliznaca_srgb8.png"); - CodecInOut io; + CodecInOut io{memory_manager}; ASSERT_TRUE(SetFromBytes(Bytes(orig), &io)); io.ShrinkTo(io.xsize() / 12, io.ysize() / 7); @@ -67,7 +70,7 @@ TEST(PassesTest, RoundtripUnalignedPasses) { cparams.progressive_mode = Override::kOn; cparams.SetCms(*JxlGetDefaultCms()); - CodecInOut io2; + CodecInOut io2{memory_manager}; JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _)); EXPECT_SLIGHTLY_BELOW( ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(), @@ -77,8 +80,9 @@ TEST(PassesTest, RoundtripUnalignedPasses) { } TEST(PassesTest, RoundtripMultiGroupPasses) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); const std::vector orig = ReadTestData("jxl/flower/flower.png"); - CodecInOut io; + CodecInOut io{memory_manager}; { ThreadPoolForTests pool(4); ASSERT_TRUE(SetFromBytes(Bytes(orig), &io, pool.get())); @@ -91,7 +95,7 @@ TEST(PassesTest, RoundtripMultiGroupPasses) { cparams.butteraugli_distance = target_distance; cparams.progressive_mode = Override::kOn; cparams.SetCms(*JxlGetDefaultCms()); - CodecInOut io2; + CodecInOut io2{memory_manager}; JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _, /* compressed_size */ nullptr, pool.get())); EXPECT_SLIGHTLY_BELOW( @@ -106,9 +110,10 @@ TEST(PassesTest, RoundtripMultiGroupPasses) { } TEST(PassesTest, RoundtripLargeFastPasses) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); ThreadPoolForTests pool(8); const std::vector orig = ReadTestData("jxl/flower/flower.png"); - CodecInOut io; + CodecInOut io{memory_manager}; ASSERT_TRUE(SetFromBytes(Bytes(orig), &io, pool.get())); CompressParams cparams; @@ -116,7 +121,7 @@ TEST(PassesTest, RoundtripLargeFastPasses) { cparams.progressive_mode = Override::kOn; cparams.SetCms(*JxlGetDefaultCms()); - CodecInOut io2; + CodecInOut io2{memory_manager}; JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _, /* compressed_size */ nullptr, pool.get())); } @@ -125,9 +130,10 @@ TEST(PassesTest, RoundtripLargeFastPasses) { // which involves additional processing including adaptive reconstruction. // Failing this may be a sign of race conditions or invalid memory accesses. TEST(PassesTest, RoundtripProgressiveConsistent) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); ThreadPoolForTests pool(8); const std::vector orig = ReadTestData("jxl/flower/flower.png"); - CodecInOut io; + CodecInOut io{memory_manager}; ASSERT_TRUE(SetFromBytes(Bytes(orig), &io, pool.get())); CompressParams cparams; @@ -140,11 +146,11 @@ TEST(PassesTest, RoundtripProgressiveConsistent) { for (size_t xsize = 48; xsize > 40; --xsize) { io.ShrinkTo(xsize, 15); - CodecInOut io2; + CodecInOut io2{memory_manager}; size_t size2; JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _, &size2, pool.get())); - CodecInOut io3; + CodecInOut io3{memory_manager}; size_t size3; JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io3, _, &size3, pool.get())); @@ -163,10 +169,11 @@ TEST(PassesTest, RoundtripProgressiveConsistent) { } TEST(PassesTest, AllDownsampleFeasible) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); ThreadPoolForTests pool(8); const std::vector orig = ReadTestData("external/wesaturate/500px/u76c0g_bliznaca_srgb8.png"); - CodecInOut io; + CodecInOut io{memory_manager}; ASSERT_TRUE(SetFromBytes(Bytes(orig), &io, pool.get())); std::vector compressed; @@ -193,7 +200,7 @@ TEST(PassesTest, AllDownsampleFeasible) { const size_t downsampling = downsamplings[task]; extras::JXLDecompressParams dparams; dparams.max_downsampling = downsampling; - CodecInOut output; + CodecInOut output{memory_manager}; ASSERT_TRUE(test::DecodeFile(dparams, Bytes(compressed), &output)); EXPECT_EQ(output.xsize(), io.xsize()) << "downsampling = " << downsampling; EXPECT_EQ(output.ysize(), io.ysize()) << "downsampling = " << downsampling; @@ -208,10 +215,11 @@ TEST(PassesTest, AllDownsampleFeasible) { } TEST(PassesTest, AllDownsampleFeasibleQProgressive) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); ThreadPoolForTests pool(8); const std::vector orig = ReadTestData("external/wesaturate/500px/u76c0g_bliznaca_srgb8.png"); - CodecInOut io; + CodecInOut io{memory_manager}; ASSERT_TRUE(SetFromBytes(Bytes(orig), &io, pool.get())); std::vector compressed; @@ -238,7 +246,7 @@ TEST(PassesTest, AllDownsampleFeasibleQProgressive) { const size_t downsampling = downsamplings[task]; extras::JXLDecompressParams dparams; dparams.max_downsampling = downsampling; - CodecInOut output; + CodecInOut output{memory_manager}; ASSERT_TRUE(test::DecodeFile(dparams, Bytes(compressed), &output)); EXPECT_EQ(output.xsize(), io.xsize()) << "downsampling = " << downsampling; EXPECT_EQ(output.ysize(), io.ysize()) << "downsampling = " << downsampling; @@ -253,17 +261,19 @@ TEST(PassesTest, AllDownsampleFeasibleQProgressive) { } TEST(PassesTest, ProgressiveDownsample2DegradesCorrectlyGrayscale) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); ThreadPoolForTests pool(8); const std::vector orig = ReadTestData( "external/wesaturate/500px/cvo9xd_keong_macan_grayscale.png"); - CodecInOut io_orig; + CodecInOut io_orig{memory_manager}; ASSERT_TRUE(SetFromBytes(Bytes(orig), &io_orig, pool.get())); Rect rect(0, 0, io_orig.xsize(), 128); // need 2 DC groups for the DC frame to actually be progressive. - JXL_ASSIGN_OR_DIE(Image3F large, Image3F::Create(4242, rect.ysize())); + JXL_ASSIGN_OR_DIE(Image3F large, + Image3F::Create(memory_manager, 4242, rect.ysize())); ZeroFillImage(&large); CopyImageTo(rect, *io_orig.Main().color(), rect, &large); - CodecInOut io; + CodecInOut io{memory_manager}; io.metadata = io_orig.metadata; io.SetFromImage(std::move(large), io_orig.Main().c_current()); @@ -281,11 +291,11 @@ TEST(PassesTest, ProgressiveDownsample2DegradesCorrectlyGrayscale) { extras::JXLDecompressParams dparams; dparams.max_downsampling = 1; - CodecInOut output; + CodecInOut output{memory_manager}; ASSERT_TRUE(test::DecodeFile(dparams, Bytes(compressed), &output)); dparams.max_downsampling = 2; - CodecInOut output_d2; + CodecInOut output_d2{memory_manager}; ASSERT_TRUE(test::DecodeFile(dparams, Bytes(compressed), &output_d2)); // 0 if reading all the passes, ~15 if skipping the 8x pass. @@ -298,16 +308,18 @@ TEST(PassesTest, ProgressiveDownsample2DegradesCorrectlyGrayscale) { } TEST(PassesTest, ProgressiveDownsample2DegradesCorrectly) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); ThreadPoolForTests pool(8); const std::vector orig = ReadTestData("jxl/flower/flower.png"); - CodecInOut io_orig; + CodecInOut io_orig{memory_manager}; ASSERT_TRUE(SetFromBytes(Bytes(orig), &io_orig, pool.get())); Rect rect(0, 0, io_orig.xsize(), 128); // need 2 DC groups for the DC frame to actually be progressive. - JXL_ASSIGN_OR_DIE(Image3F large, Image3F::Create(4242, rect.ysize())); + JXL_ASSIGN_OR_DIE(Image3F large, + Image3F::Create(memory_manager, 4242, rect.ysize())); ZeroFillImage(&large); CopyImageTo(rect, *io_orig.Main().color(), rect, &large); - CodecInOut io; + CodecInOut io{memory_manager}; io.SetFromImage(std::move(large), io_orig.Main().c_current()); std::vector compressed; @@ -324,11 +336,11 @@ TEST(PassesTest, ProgressiveDownsample2DegradesCorrectly) { extras::JXLDecompressParams dparams; dparams.max_downsampling = 1; - CodecInOut output; + CodecInOut output{memory_manager}; ASSERT_TRUE(test::DecodeFile(dparams, Bytes(compressed), &output)); dparams.max_downsampling = 2; - CodecInOut output_d2; + CodecInOut output_d2{memory_manager}; ASSERT_TRUE(test::DecodeFile(dparams, Bytes(compressed), &output_d2)); // 0 if reading all the passes, ~15 if skipping the 8x pass. @@ -341,9 +353,10 @@ TEST(PassesTest, ProgressiveDownsample2DegradesCorrectly) { } TEST(PassesTest, NonProgressiveDCImage) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); ThreadPoolForTests pool(8); const std::vector orig = ReadTestData("jxl/flower/flower.png"); - CodecInOut io; + CodecInOut io{memory_manager}; ASSERT_TRUE(SetFromBytes(Bytes(orig), &io, pool.get())); std::vector compressed; @@ -358,7 +371,7 @@ TEST(PassesTest, NonProgressiveDCImage) { // image. extras::JXLDecompressParams dparams; dparams.max_downsampling = 100; - CodecInOut output; + CodecInOut output{memory_manager}; ASSERT_TRUE( test::DecodeFile(dparams, Bytes(compressed), &output, pool.get())); EXPECT_EQ(output.xsize(), io.xsize()); @@ -366,9 +379,10 @@ TEST(PassesTest, NonProgressiveDCImage) { } TEST(PassesTest, RoundtripSmallNoGaborishPasses) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); const std::vector orig = ReadTestData("external/wesaturate/500px/u76c0g_bliznaca_srgb8.png"); - CodecInOut io; + CodecInOut io{memory_manager}; ASSERT_TRUE(SetFromBytes(Bytes(orig), &io)); io.ShrinkTo(io.xsize() / 8, io.ysize() / 8); @@ -378,7 +392,7 @@ TEST(PassesTest, RoundtripSmallNoGaborishPasses) { cparams.progressive_mode = Override::kOn; cparams.SetCms(*JxlGetDefaultCms()); - CodecInOut io2; + CodecInOut io2{memory_manager}; JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _)); EXPECT_SLIGHTLY_BELOW( ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(), diff --git a/third_party/jpeg-xl/lib/jxl/patch_dictionary_test.cc b/third_party/jpeg-xl/lib/jxl/patch_dictionary_test.cc index fd04b7fc2e82..53cf6019b6fd 100644 --- a/third_party/jpeg-xl/lib/jxl/patch_dictionary_test.cc +++ b/third_party/jpeg-xl/lib/jxl/patch_dictionary_test.cc @@ -4,6 +4,7 @@ // license that can be found in the LICENSE file. #include +#include #include #include @@ -25,15 +26,16 @@ using test::ReadTestData; using test::Roundtrip; TEST(PatchDictionaryTest, GrayscaleModular) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); const std::vector orig = ReadTestData("jxl/grayscale_patches.png"); - CodecInOut io; + CodecInOut io{memory_manager}; ASSERT_TRUE(SetFromBytes(Bytes(orig), &io)); CompressParams cparams; cparams.SetLossless(); cparams.patches = jxl::Override::kOn; - CodecInOut io2; + CodecInOut io2{memory_manager}; // Without patches: ~25k size_t compressed_size; JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _, &compressed_size)); @@ -43,14 +45,15 @@ TEST(PatchDictionaryTest, GrayscaleModular) { } TEST(PatchDictionaryTest, GrayscaleVarDCT) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); const std::vector orig = ReadTestData("jxl/grayscale_patches.png"); - CodecInOut io; + CodecInOut io{memory_manager}; ASSERT_TRUE(SetFromBytes(Bytes(orig), &io)); CompressParams cparams; cparams.patches = jxl::Override::kOn; - CodecInOut io2; + CodecInOut io2{memory_manager}; // Without patches: ~47k size_t compressed_size; JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _, &compressed_size)); diff --git a/third_party/jpeg-xl/lib/jxl/preview_test.cc b/third_party/jpeg-xl/lib/jxl/preview_test.cc index fab8943e101b..c56279e339fc 100644 --- a/third_party/jpeg-xl/lib/jxl/preview_test.cc +++ b/third_party/jpeg-xl/lib/jxl/preview_test.cc @@ -4,6 +4,7 @@ // license that can be found in the LICENSE file. #include +#include #include #include @@ -28,9 +29,10 @@ using test::ReadTestData; using test::Roundtrip; TEST(PreviewTest, RoundtripGivenPreview) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); const std::vector orig = ReadTestData("external/wesaturate/500px/u76c0g_bliznaca_srgb8.png"); - CodecInOut io; + CodecInOut io{memory_manager}; ASSERT_TRUE(SetFromBytes(Bytes(orig), &io)); io.ShrinkTo(io.xsize() / 8, io.ysize() / 8); // Same as main image @@ -47,7 +49,7 @@ TEST(PreviewTest, RoundtripGivenPreview) { cparams.speed_tier = SpeedTier::kSquirrel; cparams.SetCms(*JxlGetDefaultCms()); - CodecInOut io2; + CodecInOut io2{memory_manager}; JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _)); EXPECT_EQ(preview_xsize, io2.metadata.m.preview_size.xsize()); EXPECT_EQ(preview_ysize, io2.metadata.m.preview_size.ysize()); diff --git a/third_party/jpeg-xl/lib/jxl/quant_weights.cc b/third_party/jpeg-xl/lib/jxl/quant_weights.cc index 00563c5cbdb6..aa84c839b83b 100644 --- a/third_party/jpeg-xl/lib/jxl/quant_weights.cc +++ b/third_party/jpeg-xl/lib/jxl/quant_weights.cc @@ -4,20 +4,16 @@ // license that can be found in the LICENSE file. #include "lib/jxl/quant_weights.h" -#include -#include +#include -#include #include -#include -#include +#include +#include -#include "lib/jxl/base/bits.h" #include "lib/jxl/base/status.h" #include "lib/jxl/dct_scales.h" #include "lib/jxl/dec_modular.h" #include "lib/jxl/fields.h" -#include "lib/jxl/image.h" #undef HWY_TARGET_INCLUDE #define HWY_TARGET_INCLUDE "lib/jxl/quant_weights.cc" @@ -378,7 +374,8 @@ Status DecodeDctParams(BitReader* br, DctQuantWeightParams* params) { return true; } -Status Decode(BitReader* br, QuantEncoding* encoding, size_t required_size_x, +Status Decode(JxlMemoryManager* memory_manager, BitReader* br, + QuantEncoding* encoding, size_t required_size_x, size_t required_size_y, size_t idx, ModularFrameDecoder* modular_frame_decoder) { size_t required_size = required_size_x * required_size_y; @@ -467,7 +464,7 @@ Status Decode(BitReader* br, QuantEncoding* encoding, size_t required_size_x, // Set mode early, to avoid mem-leak. encoding->mode = QuantEncoding::kQuantModeRAW; JXL_RETURN_IF_ERROR(ModularFrameDecoder::DecodeQuantTable( - required_size_x, required_size_y, br, encoding, idx, + memory_manager, required_size_x, required_size_y, br, encoding, idx, modular_frame_decoder)); break; } @@ -486,16 +483,16 @@ constexpr const std::array DequantMatrices::required_size_y; constexpr const size_t DequantMatrices::kSumRequiredXy; constexpr DequantMatrices::QuantTable DequantMatrices::kQuantTable[]; -Status DequantMatrices::Decode(BitReader* br, +Status DequantMatrices::Decode(JxlMemoryManager* memory_manager, BitReader* br, ModularFrameDecoder* modular_frame_decoder) { size_t all_default = br->ReadBits(1); size_t num_tables = all_default ? 0 : static_cast(kNum); encodings_.clear(); encodings_.resize(kNum, QuantEncoding::Library(0)); for (size_t i = 0; i < num_tables; i++) { - JXL_RETURN_IF_ERROR( - jxl::Decode(br, &encodings_[i], required_size_x[i % kNum], - required_size_y[i % kNum], i, modular_frame_decoder)); + JXL_RETURN_IF_ERROR(jxl::Decode( + memory_manager, br, &encodings_[i], required_size_x[i % kNum], + required_size_y[i % kNum], i, modular_frame_decoder)); } computed_mask_ = 0; return true; diff --git a/third_party/jpeg-xl/lib/jxl/quant_weights.h b/third_party/jpeg-xl/lib/jxl/quant_weights.h index 8192f7144346..68e08e195bf5 100644 --- a/third_party/jpeg-xl/lib/jxl/quant_weights.h +++ b/third_party/jpeg-xl/lib/jxl/quant_weights.h @@ -6,10 +6,11 @@ #ifndef LIB_JXL_QUANT_WEIGHTS_H_ #define LIB_JXL_QUANT_WEIGHTS_H_ -#include -#include +#include #include +#include +#include #include #include @@ -397,7 +398,7 @@ class DequantMatrices { } } - Status Decode(BitReader* br, + Status Decode(JxlMemoryManager* memory_manager, BitReader* br, ModularFrameDecoder* modular_frame_decoder = nullptr); Status DecodeDC(BitReader* br); diff --git a/third_party/jpeg-xl/lib/jxl/quant_weights_test.cc b/third_party/jpeg-xl/lib/jxl/quant_weights_test.cc index 61c812d6200c..28ebc14e5660 100644 --- a/third_party/jpeg-xl/lib/jxl/quant_weights_test.cc +++ b/third_party/jpeg-xl/lib/jxl/quant_weights_test.cc @@ -4,10 +4,11 @@ // license that can be found in the LICENSE file. #include "lib/jxl/quant_weights.h" -#include +#include #include #include +#include #include // HWY_ALIGN_MAX #include #include @@ -25,6 +26,7 @@ #include "lib/jxl/enc_transforms.h" #include "lib/jxl/frame_header.h" #include "lib/jxl/image_metadata.h" +#include "lib/jxl/test_utils.h" #include "lib/jxl/testing.h" namespace jxl { @@ -56,9 +58,10 @@ void CheckSimilar(float a, float b) { } TEST(QuantWeightsTest, DC) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); DequantMatrices mat; float dc_quant[3] = {1e+5, 1e+3, 1e+1}; - DequantMatricesSetCustomDC(&mat, dc_quant); + DequantMatricesSetCustomDC(memory_manager, &mat, dc_quant); for (size_t c = 0; c < 3; c++) { CheckSimilar(mat.InvDCQuant(c), dc_quant[c]); } @@ -69,7 +72,8 @@ void RoundtripMatrices(const std::vector& encodings) { DequantMatrices mat; CodecMetadata metadata; FrameHeader frame_header(&metadata); - ModularFrameEncoder encoder(frame_header, CompressParams{}, false); + ModularFrameEncoder encoder(jxl::test::MemoryManager(), frame_header, + CompressParams{}, false); JXL_CHECK(DequantMatricesSetCustom(&mat, encodings, &encoder)); const std::vector& encodings_dec = mat.encodings(); for (size_t i = 0; i < encodings.size(); i++) { @@ -181,6 +185,7 @@ class QuantWeightsTargetTest : public hwy::TestWithParamTarget {}; HWY_TARGET_INSTANTIATE_TEST_SUITE_P(QuantWeightsTargetTest); TEST_P(QuantWeightsTargetTest, DCTUniform) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); constexpr float kUniformQuant = 4; float weights[3][2] = {{1.0f / kUniformQuant, 0}, {1.0f / kUniformQuant, 0}, @@ -191,13 +196,14 @@ TEST_P(QuantWeightsTargetTest, DCTUniform) { DequantMatrices dequant_matrices; CodecMetadata metadata; FrameHeader frame_header(&metadata); - ModularFrameEncoder encoder(frame_header, CompressParams{}, false); + ModularFrameEncoder encoder(jxl::test::MemoryManager(), frame_header, + CompressParams{}, false); JXL_CHECK(DequantMatricesSetCustom(&dequant_matrices, encodings, &encoder)); JXL_CHECK(dequant_matrices.EnsureComputed(~0u)); const float dc_quant[3] = {1.0f / kUniformQuant, 1.0f / kUniformQuant, 1.0f / kUniformQuant}; - DequantMatricesSetCustomDC(&dequant_matrices, dc_quant); + DequantMatricesSetCustomDC(memory_manager, &dequant_matrices, dc_quant); HWY_ALIGN_MAX float scratch_space[16 * 16 * 5]; diff --git a/third_party/jpeg-xl/lib/jxl/quantizer_test.cc b/third_party/jpeg-xl/lib/jxl/quantizer_test.cc index 5b965b78ca1b..9057da7a29e5 100644 --- a/third_party/jpeg-xl/lib/jxl/quantizer_test.cc +++ b/third_party/jpeg-xl/lib/jxl/quantizer_test.cc @@ -5,6 +5,8 @@ #include "lib/jxl/quantizer.h" +#include + #include #include @@ -16,6 +18,7 @@ #include "lib/jxl/image.h" #include "lib/jxl/image_test_utils.h" #include "lib/jxl/quant_weights.h" +#include "lib/jxl/test_utils.h" #include "lib/jxl/testing.h" namespace jxl { @@ -39,13 +42,15 @@ TEST(QuantizerTest, QuantizerParams) { } TEST(QuantizerTest, BitStreamRoundtripSameQuant) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); const int qxsize = 8; const int qysize = 8; DequantMatrices dequant; Quantizer quantizer1(&dequant); - JXL_ASSIGN_OR_DIE(ImageI raw_quant_field, ImageI::Create(qxsize, qysize)); + JXL_ASSIGN_OR_DIE(ImageI raw_quant_field, + ImageI::Create(jxl::test::MemoryManager(), qxsize, qysize)); quantizer1.SetQuant(0.17f, 0.17f, &raw_quant_field); - BitWriter writer; + BitWriter writer{memory_manager}; QuantizerParams params = quantizer1.GetParams(); EXPECT_TRUE(WriteQuantizerParams(params, &writer, 0, nullptr)); writer.ZeroPadToByte(); @@ -60,17 +65,19 @@ TEST(QuantizerTest, BitStreamRoundtripSameQuant) { } TEST(QuantizerTest, BitStreamRoundtripRandomQuant) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); const int qxsize = 8; const int qysize = 8; DequantMatrices dequant; Quantizer quantizer1(&dequant); - JXL_ASSIGN_OR_DIE(ImageI raw_quant_field, ImageI::Create(qxsize, qysize)); + JXL_ASSIGN_OR_DIE(ImageI raw_quant_field, + ImageI::Create(memory_manager, qxsize, qysize)); quantizer1.SetQuant(0.17f, 0.17f, &raw_quant_field); float quant_dc = 0.17f; - JXL_ASSIGN_OR_DIE(ImageF qf, ImageF::Create(qxsize, qysize)); + JXL_ASSIGN_OR_DIE(ImageF qf, ImageF::Create(memory_manager, qxsize, qysize)); RandomFillImage(&qf, 0.0f, 1.0f); quantizer1.SetQuantField(quant_dc, qf, &raw_quant_field); - BitWriter writer; + BitWriter writer{memory_manager}; QuantizerParams params = quantizer1.GetParams(); EXPECT_TRUE(WriteQuantizerParams(params, &writer, 0, nullptr)); writer.ZeroPadToByte(); diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.cc index f95237dea5ad..1b02cc32666f 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.cc @@ -200,14 +200,15 @@ Status LowMemoryRenderPipeline::EnsureBordersStorage() { 1 << shifts[c].second); Rect horizontal = Rect(0, 0, downsampled_xsize, bordery * num_yborders); if (!SameSize(horizontal, borders_horizontal_[c])) { - JXL_ASSIGN_OR_RETURN( - borders_horizontal_[c], - ImageF::Create(horizontal.xsize(), horizontal.ysize())); + JXL_ASSIGN_OR_RETURN(borders_horizontal_[c], + ImageF::Create(memory_manager_, horizontal.xsize(), + horizontal.ysize())); } Rect vertical = Rect(0, 0, borderx * num_xborders, downsampled_ysize); if (!SameSize(vertical, borders_vertical_[c])) { - JXL_ASSIGN_OR_RETURN(borders_vertical_[c], - ImageF::Create(vertical.xsize(), vertical.ysize())); + JXL_ASSIGN_OR_RETURN( + borders_vertical_[c], + ImageF::Create(memory_manager_, vertical.xsize(), vertical.ysize())); } } return true; @@ -379,7 +380,8 @@ Status LowMemoryRenderPipeline::PrepareForThreadsInternal(size_t num, for (size_t c = 0; c < shifts.size(); c++) { JXL_ASSIGN_OR_RETURN( group_data_[t][c], - ImageF::Create(GroupInputXSize(c) + group_data_x_border_ * 2, + ImageF::Create(memory_manager_, + GroupInputXSize(c) + group_data_x_border_ * 2, GroupInputYSize(c) + group_data_y_border_ * 2)); } } @@ -405,7 +407,8 @@ Status LowMemoryRenderPipeline::PrepareForThreadsInternal(size_t num, next_y_border = stages_[i]->settings_.border_y; JXL_ASSIGN_OR_RETURN( stage_data_[t][c][i], - ImageF::Create(stage_buffer_xsize, stage_buffer_ysize)); + ImageF::Create(memory_manager_, stage_buffer_xsize, + stage_buffer_ysize)); } } } @@ -427,8 +430,9 @@ Status LowMemoryRenderPipeline::PrepareForThreadsInternal(size_t num, std::max(left_padding, std::max(middle_padding, right_padding)); out_of_frame_data_.resize(num); for (size_t t = 0; t < num; t++) { - JXL_ASSIGN_OR_RETURN(out_of_frame_data_[t], - ImageF::Create(out_of_frame_xsize, shifts.size())); + JXL_ASSIGN_OR_RETURN( + out_of_frame_data_[t], + ImageF::Create(memory_manager_, out_of_frame_xsize, shifts.size())); } } return true; diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.h b/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.h index 5f88a34b5e70..1dd6e480ddb1 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.h +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.h @@ -6,6 +6,8 @@ #ifndef LIB_JXL_RENDER_PIPELINE_LOW_MEMORY_RENDER_PIPELINE_H_ #define LIB_JXL_RENDER_PIPELINE_LOW_MEMORY_RENDER_PIPELINE_H_ +#include + #include #include #include @@ -23,6 +25,10 @@ namespace jxl { // A multithreaded, low-memory rendering pipeline that only allocates a minimal // amount of buffers. class LowMemoryRenderPipeline final : public RenderPipeline { + public: + explicit LowMemoryRenderPipeline(JxlMemoryManager* memory_manager) + : RenderPipeline(memory_manager) {} + private: std::vector> PrepareBuffers( size_t group_id, size_t thread_id) override; diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.cc index ed27a2efa0de..d4f9cf68ce4e 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.cc @@ -5,14 +5,16 @@ #include "lib/jxl/render_pipeline/render_pipeline.h" +#include + #include #include #include "lib/jxl/base/rect.h" +#include "lib/jxl/base/sanitizers.h" #include "lib/jxl/base/status.h" #include "lib/jxl/render_pipeline/low_memory_render_pipeline.h" #include "lib/jxl/render_pipeline/simple_render_pipeline.h" -#include "lib/jxl/sanitizers.h" namespace jxl { @@ -35,9 +37,9 @@ StatusOr> RenderPipeline::Builder::Finalize( std::unique_ptr res; if (use_simple_implementation_) { - res = jxl::make_unique(); + res = jxl::make_unique(memory_manager_); } else { - res = jxl::make_unique(); + res = jxl::make_unique(memory_manager_); } res->padding_.resize(stages_.size()); diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.h b/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.h index fa7a1e58c409..04269a1f480a 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.h +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.h @@ -6,6 +6,8 @@ #ifndef LIB_JXL_RENDER_PIPELINE_RENDER_PIPELINE_H_ #define LIB_JXL_RENDER_PIPELINE_RENDER_PIPELINE_H_ +#include + #include #include #include @@ -59,7 +61,10 @@ class RenderPipeline { public: class Builder { public: - explicit Builder(size_t num_c) : num_c_(num_c) { JXL_ASSERT(num_c > 0); } + explicit Builder(JxlMemoryManager* memory_manager, size_t num_c) + : memory_manager_(memory_manager), num_c_(num_c) { + JXL_ASSERT(num_c > 0); + } // Adds a stage to the pipeline. Must be called at least once; the last // added stage cannot have kInOut channels. @@ -75,6 +80,7 @@ class RenderPipeline { FrameDimensions frame_dimensions) &&; private: + JxlMemoryManager* memory_manager_; std::vector> stages_; size_t num_c_; bool use_simple_implementation_ = false; @@ -111,6 +117,10 @@ class RenderPipeline { virtual void ClearDone(size_t i) {} protected: + explicit RenderPipeline(JxlMemoryManager* memory_manager) + : memory_manager_(memory_manager) {} + JxlMemoryManager* memory_manager_; + std::vector> stages_; // Shifts for every channel at the input of each stage. std::vector>> channel_shifts_; diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline_test.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline_test.cc index f5d76352069d..17e5aa627429 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline_test.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline_test.cc @@ -17,6 +17,7 @@ #include #include +#include "jxl/memory_manager.h" #include "lib/extras/codec.h" #include "lib/jxl/base/common.h" #include "lib/jxl/base/compiler_specific.h" @@ -50,6 +51,7 @@ namespace { Status DecodeFile(const Span file, bool use_slow_pipeline, CodecInOut* io, ThreadPool* pool) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); Status ret = true; { BitReader reader(file); @@ -66,14 +68,14 @@ Status DecodeFile(const Span file, bool use_slow_pipeline, JXL_RETURN_IF_ERROR(io->metadata.m.color_encoding.SetICC( std::move(icc), JxlGetDefaultCms())); } - PassesDecoderState dec_state; + PassesDecoderState dec_state(memory_manager); JXL_RETURN_IF_ERROR( dec_state.output_encoding_info.SetFromMetadata(io->metadata)); JXL_RETURN_IF_ERROR(reader.JumpToByteBoundary()); io->frames.clear(); FrameHeader frame_header(&io->metadata); do { - io->frames.emplace_back(&io->metadata.m); + io->frames.emplace_back(memory_manager, &io->metadata.m); // Skip frames that are not displayed. do { size_t frame_start = reader.TotalBitsConsumed() / kBitsPerByte; @@ -102,7 +104,8 @@ Status DecodeFile(const Span file, bool use_slow_pipeline, } TEST(RenderPipelineTest, Build) { - RenderPipeline::Builder builder(/*num_c=*/1); + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + RenderPipeline::Builder builder(memory_manager, /*num_c=*/1); builder.AddStage(jxl::make_unique()); builder.AddStage(jxl::make_unique()); builder.AddStage(jxl::make_unique()); @@ -115,7 +118,8 @@ TEST(RenderPipelineTest, Build) { } TEST(RenderPipelineTest, CallAllGroups) { - RenderPipeline::Builder builder(/*num_c=*/1); + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + RenderPipeline::Builder builder(memory_manager, /*num_c=*/1); builder.AddStage(jxl::make_unique()); builder.AddStage(jxl::make_unique()); builder.AddStage(jxl::make_unique()); @@ -138,7 +142,8 @@ TEST(RenderPipelineTest, CallAllGroups) { } TEST(RenderPipelineTest, BuildFast) { - RenderPipeline::Builder builder(/*num_c=*/1); + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + RenderPipeline::Builder builder(memory_manager, /*num_c=*/1); builder.AddStage(jxl::make_unique()); builder.AddStage(jxl::make_unique()); builder.AddStage(jxl::make_unique()); @@ -150,7 +155,8 @@ TEST(RenderPipelineTest, BuildFast) { } TEST(RenderPipelineTest, CallAllGroupsFast) { - RenderPipeline::Builder builder(/*num_c=*/1); + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + RenderPipeline::Builder builder(memory_manager, /*num_c=*/1); builder.AddStage(jxl::make_unique()); builder.AddStage(jxl::make_unique()); builder.AddStage(jxl::make_unique()); @@ -191,6 +197,7 @@ class RenderPipelineTestParam : public ::testing::TestWithParam {}; TEST_P(RenderPipelineTestParam, PipelineTest) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); RenderPipelineTestInputSettings config = GetParam(); // Use a parallel runner that randomly shuffles tasks to detect possible @@ -199,7 +206,7 @@ TEST_P(RenderPipelineTestParam, PipelineTest) { ThreadPool pool(&JxlFakeParallelRunner, &fake_pool); const std::vector orig = jxl::test::ReadTestData(config.input_path); - CodecInOut io; + CodecInOut io{memory_manager}; if (config.jpeg_transcode) { ASSERT_TRUE(jpeg::DecodeImageJPG(Bytes(orig), &io)); } else { @@ -208,7 +215,8 @@ TEST_P(RenderPipelineTestParam, PipelineTest) { io.ShrinkTo(config.xsize, config.ysize); if (config.add_spot_color) { - JXL_ASSIGN_OR_DIE(ImageF spot, ImageF::Create(config.xsize, config.ysize)); + JXL_ASSIGN_OR_DIE(ImageF spot, ImageF::Create(memory_manager, config.xsize, + config.ysize)); jxl::ZeroFillImage(&spot); for (size_t y = 0; y < config.ysize; y++) { @@ -237,10 +245,10 @@ TEST_P(RenderPipelineTestParam, PipelineTest) { config.cparams.custom_splines = config.splines; ASSERT_TRUE(test::EncodeFile(config.cparams, &io, &compressed, &pool)); - CodecInOut io_default; + CodecInOut io_default{memory_manager}; ASSERT_TRUE(DecodeFile(Bytes(compressed), /*use_slow_pipeline=*/false, &io_default, &pool)); - CodecInOut io_slow_pipeline; + CodecInOut io_slow_pipeline{memory_manager}; ASSERT_TRUE(DecodeFile(Bytes(compressed), /*use_slow_pipeline=*/true, &io_slow_pipeline, &pool)); @@ -541,16 +549,17 @@ JXL_GTEST_INSTANTIATE_TEST_SUITE_P(RenderPipelineTest, RenderPipelineTestParam, PipelineTestDescription); TEST(RenderPipelineDecodingTest, Animation) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); FakeParallelRunner fake_pool(/*order_seed=*/123, /*num_threads=*/8); ThreadPool pool(&JxlFakeParallelRunner, &fake_pool); std::vector compressed = jxl::test::ReadTestData("jxl/blending/cropped_traffic_light.jxl"); - CodecInOut io_default; + CodecInOut io_default{memory_manager}; ASSERT_TRUE(DecodeFile(Bytes(compressed), /*use_slow_pipeline=*/false, &io_default, &pool)); - CodecInOut io_slow_pipeline; + CodecInOut io_slow_pipeline{memory_manager}; ASSERT_TRUE(DecodeFile(Bytes(compressed), /*use_slow_pipeline=*/true, &io_slow_pipeline, &pool)); diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.cc index 88cc04ed0c75..1883c16aa7fb 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.cc @@ -13,10 +13,10 @@ #include #include "lib/jxl/base/rect.h" +#include "lib/jxl/base/sanitizers.h" #include "lib/jxl/base/status.h" #include "lib/jxl/image_ops.h" #include "lib/jxl/render_pipeline/render_pipeline_stage.h" -#include "lib/jxl/sanitizers.h" namespace jxl { @@ -32,6 +32,7 @@ Status SimpleRenderPipeline::PrepareForThreadsInternal(size_t num, JXL_ASSIGN_OR_RETURN( ImageF ch, ImageF::Create( + memory_manager_, ch_size(frame_dimensions_.xsize_upsampled, entry.first), ch_size(frame_dimensions_.ysize_upsampled, entry.second))); channel_data_.push_back(std::move(ch)); @@ -101,7 +102,8 @@ Status SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) { // problems with padding. JXL_ASSIGN_OR_RETURN( new_channels[c], - ImageF::Create(frame_dimensions_.xsize_upsampled_padded + + ImageF::Create(memory_manager_, + frame_dimensions_.xsize_upsampled_padded + kRenderPipelineXOffset * 2 + hwy::kMaxVectorSize * 8, frame_dimensions_.ysize_upsampled_padded + @@ -236,7 +238,8 @@ Status SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) { for (size_t c = 0; c < old_channels.size(); c++) { JXL_ASSIGN_OR_RETURN( ImageF ch, - ImageF::Create(2 * kRenderPipelineXOffset + image_xsize, + ImageF::Create(memory_manager_, + 2 * kRenderPipelineXOffset + image_xsize, 2 * kRenderPipelineXOffset + image_ysize)); channel_data_.emplace_back(std::move(ch)); } diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.h b/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.h index 4fd81755f317..6ddf0a9c9d19 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.h +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.h @@ -6,6 +6,8 @@ #ifndef LIB_JXL_RENDER_PIPELINE_SIMPLE_RENDER_PIPELINE_H_ #define LIB_JXL_RENDER_PIPELINE_SIMPLE_RENDER_PIPELINE_H_ +#include + #include #include #include @@ -33,6 +35,10 @@ class SimpleRenderPipeline : public RenderPipeline { std::vector channel_data_; size_t processed_passes_ = 0; + public: + explicit SimpleRenderPipeline(JxlMemoryManager* memory_manager) + : RenderPipeline(memory_manager) {} + private: Rect MakeChannelRect(size_t group_id, size_t channel); }; diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_blending.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_blending.cc index ef3899d1b39a..a1612f716151 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_blending.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_blending.cc @@ -5,6 +5,8 @@ #include "lib/jxl/render_pipeline/stage_blending.h" +#include + #undef HWY_TARGET_INCLUDE #define HWY_TARGET_INCLUDE "lib/jxl/render_pipeline/stage_blending.cc" #include @@ -32,7 +34,7 @@ class BlendingStage : public RenderPipelineStage { info_ = frame_header_.blending_info; const std::vector& ec_info = frame_header_.extra_channel_blending_info; - const ImageBundle& bg = state_.reference_frames[info_.source].frame; + const ImageBundle& bg = *state_.reference_frames[info_.source].frame; bg_ = &bg; if (bg.xsize() == 0 || bg.ysize() == 0) { zeroes_.resize(image_xsize_, 0.f); @@ -44,7 +46,7 @@ class BlendingStage : public RenderPipelineStage { } else if (std::any_of(ec_info.begin(), ec_info.end(), [this](const BlendingInfo& info) { const ImageBundle& bg = - state_.reference_frames[info.source].frame; + *state_.reference_frames[info.source].frame; return bg.xsize() == 0 || bg.ysize() == 0; })) { zeroes_.resize(image_xsize_, 0.f); @@ -63,7 +65,7 @@ class BlendingStage : public RenderPipelineStage { Status ok = verify_bg_size(bg); for (const auto& info : ec_info) { - const ImageBundle& bg = state_.reference_frames[info.source].frame; + const ImageBundle& bg = *state_.reference_frames[info.source].frame; if (!!ok) ok = verify_bg_size(bg); } if (!ok) { @@ -121,6 +123,7 @@ class BlendingStage : public RenderPipelineStage { size_t xextra, size_t xsize, size_t xpos, size_t ypos, size_t thread_id) const final { JXL_ASSERT(initialized_); + JxlMemoryManager* memory_manager = state_.memory_manager; const FrameOrigin& frame_origin = frame_header_.frame_origin; ssize_t bg_xpos = frame_origin.x0 + static_cast(xpos); ssize_t bg_ypos = frame_origin.y0 + static_cast(ypos); @@ -151,19 +154,20 @@ class BlendingStage : public RenderPipelineStage { : zeroes_.data(); } else { const ImageBundle& ec_bg = - state_ - .reference_frames - [frame_header_.extra_channel_blending_info[c - 3].source] - .frame; + *state_ + .reference_frames + [frame_header_.extra_channel_blending_info[c - 3].source] + .frame; bg_row_ptrs_[c] = ec_bg.xsize() != 0 && ec_bg.ysize() != 0 ? ec_bg.extra_channels()[c - 3].ConstRow(bg_ypos) + bg_xpos : zeroes_.data(); } } - return PerformBlending(bg_row_ptrs_.data(), fg_row_ptrs_.data(), - fg_row_ptrs_.data(), 0, xsize, blending_info_[0], - blending_info_.data() + 1, *extra_channel_info_); + return PerformBlending(memory_manager, bg_row_ptrs_.data(), + fg_row_ptrs_.data(), fg_row_ptrs_.data(), 0, xsize, + blending_info_[0], blending_info_.data() + 1, + *extra_channel_info_); } RenderPipelineChannelMode GetChannelMode(size_t c) const final { @@ -194,10 +198,10 @@ class BlendingStage : public RenderPipelineStage { } for (size_t ec = 0; ec < extra_channel_info_->size(); ++ec) { const ImageBundle& ec_bg = - state_ - .reference_frames[frame_header_.extra_channel_blending_info[ec] - .source] - .frame; + *state_ + .reference_frames[frame_header_.extra_channel_blending_info[ec] + .source] + .frame; if (ec_bg.xsize() == 0 || ec_bg.ysize() == 0) { memset(GetInputRow(output_rows, 3 + ec, 0), 0, xsize * sizeof(float)); } else { diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_from_linear.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_from_linear.cc index 922c7da366c9..9b8a136e16eb 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_from_linear.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_from_linear.cc @@ -10,10 +10,10 @@ #include #include +#include "lib/jxl/base/sanitizers.h" #include "lib/jxl/cms/tone_mapping-inl.h" #include "lib/jxl/cms/transfer_functions-inl.h" #include "lib/jxl/common.h" // JXL_HIGH_PRECISION -#include "lib/jxl/sanitizers.h" HWY_BEFORE_NAMESPACE(); namespace jxl { diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_noise.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_noise.cc index eca679b94858..a347137bcb2d 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_noise.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_noise.cc @@ -5,6 +5,7 @@ #include "lib/jxl/render_pipeline/stage_noise.h" +#include "lib/jxl/base/sanitizers.h" #include "lib/jxl/noise.h" #undef HWY_TARGET_INCLUDE @@ -12,8 +13,6 @@ #include #include -#include "lib/jxl/sanitizers.h" - HWY_BEFORE_NAMESPACE(); namespace jxl { namespace HWY_NAMESPACE { diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_patches.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_patches.cc index e0a66167d429..ab2a9adf49e9 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_patches.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_patches.cc @@ -5,6 +5,13 @@ #include "lib/jxl/render_pipeline/stage_patches.h" +#include +#include +#include + +#include "lib/jxl/dec_patch_dictionary.h" +#include "lib/jxl/render_pipeline/render_pipeline_stage.h" + namespace jxl { namespace { class PatchDictionaryStage : public RenderPipelineStage { diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_spot.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_spot.cc index 18588e2012fb..af8eb0c91c2b 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_spot.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_spot.cc @@ -5,6 +5,13 @@ #include "lib/jxl/render_pipeline/stage_spot.h" +#include +#include + +#include "lib/jxl/base/common.h" +#include "lib/jxl/base/compiler_specific.h" +#include "lib/jxl/render_pipeline/render_pipeline_stage.h" + namespace jxl { class SpotColorStage : public RenderPipelineStage { public: diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_spot.h b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_spot.h index 3e79c7582324..688960b8bfa3 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_spot.h +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_spot.h @@ -6,7 +6,8 @@ #ifndef LIB_JXL_RENDER_PIPELINE_STAGE_SPOT_H_ #define LIB_JXL_RENDER_PIPELINE_STAGE_SPOT_H_ -#include +#include +#include #include "lib/jxl/render_pipeline/render_pipeline_stage.h" diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_to_linear.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_to_linear.cc index c2c5ac484bf0..96c90dd722bf 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_to_linear.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_to_linear.cc @@ -5,6 +5,8 @@ #include "lib/jxl/render_pipeline/stage_to_linear.h" +#include "lib/jxl/base/sanitizers.h" + #undef HWY_TARGET_INCLUDE #define HWY_TARGET_INCLUDE "lib/jxl/render_pipeline/stage_to_linear.cc" #include @@ -12,7 +14,6 @@ #include "lib/jxl/cms/tone_mapping-inl.h" #include "lib/jxl/cms/transfer_functions-inl.h" -#include "lib/jxl/sanitizers.h" HWY_BEFORE_NAMESPACE(); namespace jxl { diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_tone_mapping.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_tone_mapping.cc index e8cd90b24418..126a78d3b296 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_tone_mapping.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_tone_mapping.cc @@ -5,6 +5,8 @@ #include "lib/jxl/render_pipeline/stage_tone_mapping.h" +#include "lib/jxl/base/sanitizers.h" + #undef HWY_TARGET_INCLUDE #define HWY_TARGET_INCLUDE "lib/jxl/render_pipeline/stage_tone_mapping.cc" #include @@ -12,7 +14,6 @@ #include "lib/jxl/cms/tone_mapping-inl.h" #include "lib/jxl/dec_xyb-inl.h" -#include "lib/jxl/sanitizers.h" HWY_BEFORE_NAMESPACE(); namespace jxl { diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_upsampling.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_upsampling.cc index e868f9f8e085..b81411a6408e 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_upsampling.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_upsampling.cc @@ -5,6 +5,7 @@ #include "lib/jxl/render_pipeline/stage_upsampling.h" +#include "lib/jxl/base/sanitizers.h" #include "lib/jxl/base/status.h" #undef HWY_TARGET_INCLUDE @@ -12,7 +13,6 @@ #include #include -#include "lib/jxl/sanitizers.h" #include "lib/jxl/simd_util-inl.h" HWY_BEFORE_NAMESPACE(); diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_write.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_write.cc index 05cdd786a988..3687dab70fab 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_write.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_write.cc @@ -5,16 +5,19 @@ #include "lib/jxl/render_pipeline/stage_write.h" +#include + +#include #include #include "lib/jxl/alpha.h" #include "lib/jxl/base/common.h" +#include "lib/jxl/base/sanitizers.h" #include "lib/jxl/base/status.h" #include "lib/jxl/dec_cache.h" #include "lib/jxl/dec_xyb.h" #include "lib/jxl/image.h" #include "lib/jxl/image_bundle.h" -#include "lib/jxl/sanitizers.h" #undef HWY_TARGET_INCLUDE #define HWY_TARGET_INCLUDE "lib/jxl/render_pipeline/stage_write.cc" @@ -114,7 +117,8 @@ class WriteToOutputStage : public RenderPipelineStage { WriteToOutputStage(const ImageOutput& main_output, size_t width, size_t height, bool has_alpha, bool unpremul_alpha, size_t alpha_c, Orientation undo_orientation, - const std::vector& extra_output) + const std::vector& extra_output, + JxlMemoryManager* memory_manager) : RenderPipelineStage(RenderPipelineStage::Settings()), width_(width), height_(height), @@ -537,10 +541,10 @@ constexpr size_t WriteToOutputStage::kMaxPixelsPerCall; std::unique_ptr GetWriteToOutputStage( const ImageOutput& main_output, size_t width, size_t height, bool has_alpha, bool unpremul_alpha, size_t alpha_c, Orientation undo_orientation, - std::vector& extra_output) { + std::vector& extra_output, JxlMemoryManager* memory_manager) { return jxl::make_unique( main_output, width, height, has_alpha, unpremul_alpha, alpha_c, - undo_orientation, extra_output); + undo_orientation, extra_output, memory_manager); } // NOLINTNEXTLINE(google-readability-namespace-comments) @@ -565,6 +569,7 @@ class WriteToImageBundleStage : public RenderPipelineStage { Status SetInputSizes( const std::vector>& input_sizes) override { + JxlMemoryManager* memory_manager = image_bundle_->memory_manager(); #if JXL_ENABLE_ASSERT JXL_ASSERT(input_sizes.size() >= 3); for (size_t c = 1; c < input_sizes.size(); c++) { @@ -573,14 +578,16 @@ class WriteToImageBundleStage : public RenderPipelineStage { } #endif // TODO(eustas): what should we do in the case of "want only ECs"? - JXL_ASSIGN_OR_RETURN(Image3F tmp, Image3F::Create(input_sizes[0].first, - input_sizes[0].second)); + JXL_ASSIGN_OR_RETURN(Image3F tmp, + Image3F::Create(memory_manager, input_sizes[0].first, + input_sizes[0].second)); image_bundle_->SetFromImage(std::move(tmp), color_encoding_); // TODO(veluca): consider not reallocating ECs if not needed. image_bundle_->extra_channels().clear(); for (size_t c = 3; c < input_sizes.size(); c++) { - JXL_ASSIGN_OR_RETURN(ImageF ch, ImageF::Create(input_sizes[c].first, - input_sizes[c].second)); + JXL_ASSIGN_OR_RETURN(ImageF ch, + ImageF::Create(memory_manager, input_sizes[c].first, + input_sizes[c].second)); image_bundle_->extra_channels().emplace_back(std::move(ch)); } return true; @@ -617,8 +624,10 @@ class WriteToImageBundleStage : public RenderPipelineStage { class WriteToImage3FStage : public RenderPipelineStage { public: - explicit WriteToImage3FStage(Image3F* image) - : RenderPipelineStage(RenderPipelineStage::Settings()), image_(image) {} + WriteToImage3FStage(JxlMemoryManager* memory_manager, Image3F* image) + : RenderPipelineStage(RenderPipelineStage::Settings()), + memory_manager_(memory_manager), + image_(image) {} Status SetInputSizes( const std::vector>& input_sizes) override { @@ -629,8 +638,9 @@ class WriteToImage3FStage : public RenderPipelineStage { JXL_ASSERT(input_sizes[c].second == input_sizes[0].second); } #endif - JXL_ASSIGN_OR_RETURN( - *image_, Image3F::Create(input_sizes[0].first, input_sizes[0].second)); + JXL_ASSIGN_OR_RETURN(*image_, + Image3F::Create(memory_manager_, input_sizes[0].first, + input_sizes[0].second)); return true; } @@ -653,6 +663,7 @@ class WriteToImage3FStage : public RenderPipelineStage { const char* GetName() const override { return "WriteI3F"; } private: + JxlMemoryManager* memory_manager_; Image3F* image_; }; @@ -664,17 +675,18 @@ std::unique_ptr GetWriteToImageBundleStage( output_encoding_info); } -std::unique_ptr GetWriteToImage3FStage(Image3F* image) { - return jxl::make_unique(image); +std::unique_ptr GetWriteToImage3FStage( + JxlMemoryManager* memory_manager, Image3F* image) { + return jxl::make_unique(memory_manager, image); } std::unique_ptr GetWriteToOutputStage( const ImageOutput& main_output, size_t width, size_t height, bool has_alpha, bool unpremul_alpha, size_t alpha_c, Orientation undo_orientation, - std::vector& extra_output) { + std::vector& extra_output, JxlMemoryManager* memory_manager) { return HWY_DYNAMIC_DISPATCH(GetWriteToOutputStage)( main_output, width, height, has_alpha, unpremul_alpha, alpha_c, - undo_orientation, extra_output); + undo_orientation, extra_output, memory_manager); } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_write.h b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_write.h index 32e5fba93274..9a7f2dbb1045 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_write.h +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_write.h @@ -6,6 +6,8 @@ #ifndef LIB_JXL_RENDER_PIPELINE_STAGE_WRITE_H_ #define LIB_JXL_RENDER_PIPELINE_STAGE_WRITE_H_ +#include + #include #include #include @@ -23,13 +25,14 @@ std::unique_ptr GetWriteToImageBundleStage( ImageBundle* image_bundle, const OutputEncodingInfo& output_encoding_info); // Gets a stage to write color channels to an Image3F. -std::unique_ptr GetWriteToImage3FStage(Image3F* image); +std::unique_ptr GetWriteToImage3FStage( + JxlMemoryManager* memory_manager, Image3F* image); // Gets a stage to write to a pixel callback or image buffer. std::unique_ptr GetWriteToOutputStage( const ImageOutput& main_output, size_t width, size_t height, bool has_alpha, bool unpremul_alpha, size_t alpha_c, Orientation undo_orientation, - std::vector& extra_output); + std::vector& extra_output, JxlMemoryManager* memory_manager); } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_xyb.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_xyb.cc index a20e686de635..00ffe3f2e657 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_xyb.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_xyb.cc @@ -5,16 +5,17 @@ #include "lib/jxl/render_pipeline/stage_xyb.h" +#include "lib/jxl/base/common.h" +#include "lib/jxl/base/sanitizers.h" + #undef HWY_TARGET_INCLUDE #define HWY_TARGET_INCLUDE "lib/jxl/render_pipeline/stage_xyb.cc" #include #include -#include "lib/jxl/base/common.h" #include "lib/jxl/cms/opsin_params.h" #include "lib/jxl/common.h" // JXL_HIGH_PRECISION #include "lib/jxl/dec_xyb-inl.h" -#include "lib/jxl/sanitizers.h" HWY_BEFORE_NAMESPACE(); namespace jxl { diff --git a/third_party/jpeg-xl/lib/jxl/roundtrip_test.cc b/third_party/jpeg-xl/lib/jxl/roundtrip_test.cc index fa2395699559..05665fae2ea2 100644 --- a/third_party/jpeg-xl/lib/jxl/roundtrip_test.cc +++ b/third_party/jpeg-xl/lib/jxl/roundtrip_test.cc @@ -47,7 +47,7 @@ jxl::CodecInOut ConvertTestImage(const std::vector& buf, const size_t xsize, const size_t ysize, const JxlPixelFormat& pixel_format, const jxl::Bytes& icc_profile) { - jxl::CodecInOut io; + jxl::CodecInOut io{jxl::test::MemoryManager()}; io.SetSize(xsize, ysize); bool is_gray = pixel_format.num_channels < 3; @@ -237,7 +237,8 @@ void VerifyRoundtripCompression( } } if (alpha_in_extra_channels_vector && !has_interleaved_alpha) { - JXL_ASSIGN_OR_DIE(ImageF alpha_channel, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_DIE(ImageF alpha_channel, + ImageF::Create(jxl::test::MemoryManager(), xsize, ysize)); EXPECT_TRUE(jxl::ConvertFromExternal( extra_channel_bytes.data(), extra_channel_bytes.size(), xsize, ysize, basic_info.bits_per_sample, extra_channel_pixel_format, 0, @@ -946,7 +947,7 @@ JXL_TRANSCODE_JPEG_TEST(RoundtripTest, TestJPEGReconstruction) { TEST_LIBJPEG_SUPPORT(); const std::string jpeg_path = "jxl/flower/flower.png.im_q85_420.jpg"; const std::vector orig = jxl::test::ReadTestData(jpeg_path); - jxl::CodecInOut orig_io; + jxl::CodecInOut orig_io{jxl::test::MemoryManager()}; ASSERT_TRUE(SetFromBytes(jxl::Bytes(orig), &orig_io, /*pool=*/nullptr)); JxlEncoderPtr enc = JxlEncoderMake(nullptr); diff --git a/third_party/jpeg-xl/lib/jxl/splines.cc b/third_party/jpeg-xl/lib/jxl/splines.cc index fd68c15493c0..538859565b71 100644 --- a/third_party/jpeg-xl/lib/jxl/splines.cc +++ b/third_party/jpeg-xl/lib/jxl/splines.cc @@ -5,6 +5,8 @@ #include "lib/jxl/splines.h" +#include + #include #include // PRIu64 #include @@ -567,12 +569,14 @@ void Splines::Clear() { segment_y_start_.clear(); } -Status Splines::Decode(jxl::BitReader* br, const size_t num_pixels) { +Status Splines::Decode(JxlMemoryManager* memory_manager, jxl::BitReader* br, + const size_t num_pixels) { std::vector context_map; ANSCode code; - JXL_RETURN_IF_ERROR( - DecodeHistograms(br, kNumSplineContexts, &code, &context_map)); - ANSSymbolReader decoder(&code, br); + JXL_RETURN_IF_ERROR(DecodeHistograms(memory_manager, br, kNumSplineContexts, + &code, &context_map)); + JXL_ASSIGN_OR_RETURN(ANSSymbolReader decoder, + ANSSymbolReader::Create(&code, br)); size_t num_splines = decoder.ReadHybridUint(kNumSplinesContext, br, context_map); size_t max_control_points = std::min( diff --git a/third_party/jpeg-xl/lib/jxl/splines.h b/third_party/jpeg-xl/lib/jxl/splines.h index b292d6952b55..90dc2260e545 100644 --- a/third_party/jpeg-xl/lib/jxl/splines.h +++ b/third_party/jpeg-xl/lib/jxl/splines.h @@ -6,6 +6,8 @@ #ifndef LIB_JXL_SPLINES_H_ #define LIB_JXL_SPLINES_H_ +#include + #include #include #include @@ -108,7 +110,8 @@ class Splines { void Clear(); - Status Decode(BitReader* br, size_t num_pixels); + Status Decode(JxlMemoryManager* memory_manager, BitReader* br, + size_t num_pixels); void AddTo(Image3F* opsin, const Rect& opsin_rect, const Rect& image_rect) const; diff --git a/third_party/jpeg-xl/lib/jxl/splines_gbench.cc b/third_party/jpeg-xl/lib/jxl/splines_gbench.cc index 95f890e3ce9d..45e10c09ce64 100644 --- a/third_party/jpeg-xl/lib/jxl/splines_gbench.cc +++ b/third_party/jpeg-xl/lib/jxl/splines_gbench.cc @@ -11,6 +11,7 @@ #include "lib/jxl/chroma_from_luma.h" #include "lib/jxl/image_ops.h" #include "lib/jxl/splines.h" +#include "tools/no_memory_manager.h" namespace jxl { namespace { @@ -41,7 +42,9 @@ void BM_Splines(benchmark::State& state) { Splines splines(kQuantizationAdjustment, std::move(quantized_splines), std::move(starting_points)); - JXL_ASSIGN_OR_DIE(Image3F drawing_area, Image3F::Create(320, 320)); + JXL_ASSIGN_OR_DIE( + Image3F drawing_area, + Image3F::Create(jpegxl::tools::NoMemoryManager(), 320, 320)); ZeroFillImage(&drawing_area); for (auto _ : state) { for (size_t i = 0; i < n; ++i) { diff --git a/third_party/jpeg-xl/lib/jxl/splines_test.cc b/third_party/jpeg-xl/lib/jxl/splines_test.cc index 56f2344b4ac7..843005f0df5a 100644 --- a/third_party/jpeg-xl/lib/jxl/splines_test.cc +++ b/third_party/jpeg-xl/lib/jxl/splines_test.cc @@ -6,6 +6,7 @@ #include "lib/jxl/splines.h" #include +#include #include #include @@ -72,6 +73,7 @@ std::vector DequantizeSplines(const Splines& splines) { } // namespace TEST(SplinesTest, Serialization) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); Spline spline1{ /*control_points=*/{ {109, 54}, {218, 159}, {80, 3}, {110, 274}, {94, 185}, {17, 277}}, @@ -165,7 +167,7 @@ TEST(SplinesTest, Serialization) { } } - BitWriter writer; + BitWriter writer{memory_manager}; EncodeSplines(splines, &writer, kLayerSplines, HistogramParams(), nullptr); writer.ZeroPadToByte(); const size_t bits_written = writer.BitsWritten(); @@ -174,7 +176,8 @@ TEST(SplinesTest, Serialization) { BitReader reader(writer.GetSpan()); Splines decoded_splines; - ASSERT_TRUE(decoded_splines.Decode(&reader, /*num_pixels=*/1000)); + ASSERT_TRUE( + decoded_splines.Decode(memory_manager, &reader, /*num_pixels=*/1000)); ASSERT_TRUE(reader.JumpToByteBoundary()); EXPECT_EQ(reader.TotalBitsConsumed(), bits_written); ASSERT_TRUE(reader.Close()); @@ -210,6 +213,7 @@ TEST(SplinesTest, DISABLED_TooManySplinesTest) { #else TEST(SplinesTest, TooManySplinesTest) { #endif + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); // This is more than the limit for 1000 pixels. const size_t kNumSplines = 300; @@ -228,14 +232,15 @@ TEST(SplinesTest, TooManySplinesTest) { Splines splines(kQuantizationAdjustment, std::move(quantized_splines), std::move(starting_points)); - BitWriter writer; + BitWriter writer{memory_manager}; EncodeSplines(splines, &writer, kLayerSplines, HistogramParams(SpeedTier::kFalcon, 1), nullptr); writer.ZeroPadToByte(); // Re-read splines. BitReader reader(writer.GetSpan()); Splines decoded_splines; - EXPECT_FALSE(decoded_splines.Decode(&reader, /*num_pixels=*/1000)); + EXPECT_FALSE( + decoded_splines.Decode(memory_manager, &reader, /*num_pixels=*/1000)); EXPECT_TRUE(reader.Close()); } @@ -244,6 +249,7 @@ TEST(SplinesTest, DISABLED_DuplicatePoints) { #else TEST(SplinesTest, DuplicatePoints) { #endif + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); std::vector control_points{ {9, 54}, {118, 159}, {97, 3}, // Repeated. {97, 3}, {10, 40}, {150, 25}, {120, 300}}; @@ -263,14 +269,15 @@ TEST(SplinesTest, DuplicatePoints) { Splines splines(kQuantizationAdjustment, std::move(quantized_splines), std::move(starting_points)); - JXL_ASSIGN_OR_DIE(Image3F image, Image3F::Create(320, 320)); + JXL_ASSIGN_OR_DIE(Image3F image, Image3F::Create(memory_manager, 320, 320)); ZeroFillImage(&image); EXPECT_FALSE( splines.InitializeDrawCache(image.xsize(), image.ysize(), *cmap)); } TEST(SplinesTest, Drawing) { - CodecInOut io_expected; + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + CodecInOut io_expected{memory_manager}; const std::vector orig = ReadTestData("jxl/splines.pfm"); ASSERT_TRUE(SetFromBytes(Bytes(orig), &io_expected, /*pool=*/nullptr)); @@ -299,13 +306,13 @@ TEST(SplinesTest, Drawing) { Splines splines(kQuantizationAdjustment, std::move(quantized_splines), std::move(starting_points)); - JXL_ASSIGN_OR_DIE(Image3F image, Image3F::Create(320, 320)); + JXL_ASSIGN_OR_DIE(Image3F image, Image3F::Create(memory_manager, 320, 320)); ZeroFillImage(&image); ASSERT_TRUE(splines.InitializeDrawCache(image.xsize(), image.ysize(), *cmap)); splines.AddTo(&image, Rect(image), Rect(image)); - CodecInOut io_actual; - JXL_ASSIGN_OR_DIE(Image3F image2, Image3F::Create(320, 320)); + CodecInOut io_actual{memory_manager}; + JXL_ASSIGN_OR_DIE(Image3F image2, Image3F::Create(memory_manager, 320, 320)); CopyImageTo(image, &image2); io_actual.SetFromImage(std::move(image2), ColorEncoding::SRGB()); ASSERT_TRUE(io_actual.frames[0].TransformTo(io_expected.Main().c_current(), @@ -316,12 +323,13 @@ TEST(SplinesTest, Drawing) { } TEST(SplinesTest, ClearedEveryFrame) { - CodecInOut io_expected; + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + CodecInOut io_expected{memory_manager}; const std::vector bytes_expected = ReadTestData("jxl/spline_on_first_frame.png"); ASSERT_TRUE(SetFromBytes(Bytes(bytes_expected), &io_expected, /*pool=*/nullptr)); - CodecInOut io_actual; + CodecInOut io_actual{memory_manager}; const std::vector bytes_actual = ReadTestData("jxl/spline_on_first_frame.jxl"); ASSERT_TRUE(test::DecodeFile({}, Bytes(bytes_actual), &io_actual)); diff --git a/third_party/jpeg-xl/lib/jxl/test_utils.cc b/third_party/jpeg-xl/lib/jxl/test_utils.cc index 308efb9a13b7..66124eae8c0d 100644 --- a/third_party/jpeg-xl/lib/jxl/test_utils.cc +++ b/third_party/jpeg-xl/lib/jxl/test_utils.cc @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -280,7 +281,8 @@ std::vector AllEncodings() { jxl::CodecInOut SomeTestImageToCodecInOut(const std::vector& buf, size_t num_channels, size_t xsize, size_t ysize) { - jxl::CodecInOut io; + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + jxl::CodecInOut io{memory_manager}; io.SetSize(xsize, ysize); io.metadata.m.SetAlphaBits(16); io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB( @@ -557,9 +559,10 @@ double DistanceRMS(const uint8_t* a, const uint8_t* b, size_t xsize, float ButteraugliDistance(const extras::PackedPixelFile& a, const extras::PackedPixelFile& b, ThreadPool* pool) { - CodecInOut io0; + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + CodecInOut io0{memory_manager}; JXL_CHECK(ConvertPackedPixelFileToCodecInOut(a, pool, &io0)); - CodecInOut io1; + CodecInOut io1{memory_manager}; JXL_CHECK(ConvertPackedPixelFileToCodecInOut(b, pool, &io1)); // TODO(eustas): simplify? return ButteraugliDistance(io0.frames, io1.frames, ButteraugliParams(), @@ -597,9 +600,10 @@ float ButteraugliDistance(const std::vector& frames0, float Butteraugli3Norm(const extras::PackedPixelFile& a, const extras::PackedPixelFile& b, ThreadPool* pool) { - CodecInOut io0; + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + CodecInOut io0{memory_manager}; JXL_CHECK(ConvertPackedPixelFileToCodecInOut(a, pool, &io0)); - CodecInOut io1; + CodecInOut io1{memory_manager}; JXL_CHECK(ConvertPackedPixelFileToCodecInOut(b, pool, &io1)); ButteraugliParams ba; ImageF distmap; @@ -610,18 +614,20 @@ float Butteraugli3Norm(const extras::PackedPixelFile& a, float ComputeDistance2(const extras::PackedPixelFile& a, const extras::PackedPixelFile& b) { - CodecInOut io0; + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + CodecInOut io0{memory_manager}; JXL_CHECK(ConvertPackedPixelFileToCodecInOut(a, nullptr, &io0)); - CodecInOut io1; + CodecInOut io1{memory_manager}; JXL_CHECK(ConvertPackedPixelFileToCodecInOut(b, nullptr, &io1)); return ComputeDistance2(io0.Main(), io1.Main(), *JxlGetDefaultCms()); } float ComputePSNR(const extras::PackedPixelFile& a, const extras::PackedPixelFile& b) { - CodecInOut io0; + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + CodecInOut io0{memory_manager}; JXL_CHECK(ConvertPackedPixelFileToCodecInOut(a, nullptr, &io0)); - CodecInOut io1; + CodecInOut io1{memory_manager}; JXL_CHECK(ConvertPackedPixelFileToCodecInOut(b, nullptr, &io1)); return ComputePSNR(io0.Main(), io1.Main(), *JxlGetDefaultCms()); } @@ -715,9 +721,10 @@ bool SamePixels(const extras::PackedPixelFile& a, Status ReadICC(BitReader* JXL_RESTRICT reader, std::vector* JXL_RESTRICT icc, size_t output_limit) { + JxlMemoryManager* memort_manager = jxl::test::MemoryManager(); icc->clear(); - ICCReader icc_reader; - PaddedBytes icc_buffer; + ICCReader icc_reader{memort_manager}; + PaddedBytes icc_buffer{memort_manager}; JXL_RETURN_IF_ERROR(icc_reader.Init(reader, output_limit)); JXL_RETURN_IF_ERROR(icc_reader.Process(reader, &icc_buffer)); Bytes(icc_buffer).AppendTo(*icc); @@ -752,7 +759,8 @@ Status PrepareCodecMetadataFromIO(const CompressParams& cparams, Status EncodePreview(const CompressParams& cparams, const ImageBundle& ib, const CodecMetadata* metadata, const JxlCmsInterface& cms, ThreadPool* pool, BitWriter* JXL_RESTRICT writer) { - BitWriter preview_writer; + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); + BitWriter preview_writer{memory_manager}; // TODO(janwas): also support generating preview by downsampling if (ib.HasColor()) { AuxOut aux_out; @@ -761,8 +769,9 @@ Status EncodePreview(const CompressParams& cparams, const ImageBundle& ib, // encoding this frame is warrented. FrameInfo frame_info; frame_info.is_preview = true; - JXL_RETURN_IF_ERROR(EncodeFrame(cparams, frame_info, metadata, ib, cms, - pool, &preview_writer, &aux_out)); + JXL_RETURN_IF_ERROR(EncodeFrame(memory_manager, cparams, frame_info, + metadata, ib, cms, pool, &preview_writer, + &aux_out)); preview_writer.ZeroPadToByte(); } @@ -778,10 +787,11 @@ Status EncodePreview(const CompressParams& cparams, const ImageBundle& ib, Status EncodeFile(const CompressParams& params, const CodecInOut* io, std::vector* compressed, ThreadPool* pool) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); compressed->clear(); const JxlCmsInterface& cms = *JxlGetDefaultCms(); io->CheckMetadata(); - BitWriter writer; + BitWriter writer{memory_manager}; CompressParams cparams = params; if (io->Main().color_transform != ColorTransform::kNone) { @@ -818,8 +828,9 @@ Status EncodeFile(const CompressParams& params, const CodecInOut* io, if (io->frames[i].use_for_next_frame) { info.save_as_reference = 1; } - JXL_RETURN_IF_ERROR(EncodeFrame(cparams, info, metadata.get(), - io->frames[i], cms, pool, &writer, + JXL_RETURN_IF_ERROR(EncodeFrame(memory_manager, cparams, info, + metadata.get(), io->frames[i], cms, pool, + &writer, /* aux_out */ nullptr)); } @@ -828,6 +839,14 @@ Status EncodeFile(const CompressParams& params, const CodecInOut* io, return true; } +namespace { +void* TestAlloc(void* /* opaque*/, size_t size) { return malloc(size); } +void TestFree(void* /* opaque*/, void* address) { free(address); } +JxlMemoryManager kMemoryManager{nullptr, &TestAlloc, &TestFree}; +} // namespace + +JxlMemoryManager* MemoryManager() { return &kMemoryManager; }; + } // namespace test bool operator==(const jxl::Bytes& a, const jxl::Bytes& b) { diff --git a/third_party/jpeg-xl/lib/jxl/test_utils.h b/third_party/jpeg-xl/lib/jxl/test_utils.h index 0d0d8004c14e..7d63b83a3115 100644 --- a/third_party/jpeg-xl/lib/jxl/test_utils.h +++ b/third_party/jpeg-xl/lib/jxl/test_utils.h @@ -11,6 +11,7 @@ // Macros and functions useful for tests. #include +#include #include #include @@ -202,6 +203,8 @@ Status EncodeFile(const CompressParams& params, const CodecInOut* io, constexpr const char* BoolToCStr(bool b) { return b ? "true" : "false"; } +JxlMemoryManager* MemoryManager(); + } // namespace test bool operator==(const jxl::Bytes& a, const jxl::Bytes& b); diff --git a/third_party/jpeg-xl/lib/jxl/toc.cc b/third_party/jpeg-xl/lib/jxl/toc.cc index 72c8ac01cd75..feeab99cfb7e 100644 --- a/third_party/jpeg-xl/lib/jxl/toc.cc +++ b/third_party/jpeg-xl/lib/jxl/toc.cc @@ -5,6 +5,7 @@ #include "lib/jxl/toc.h" +#include #include #include "lib/jxl/base/common.h" @@ -19,7 +20,8 @@ size_t MaxBits(const size_t num_sizes) { return 1 + kBitsPerByte + entry_bits + kBitsPerByte; } -Status ReadToc(size_t toc_entries, BitReader* JXL_RESTRICT reader, +Status ReadToc(JxlMemoryManager* memory_manager, size_t toc_entries, + BitReader* JXL_RESTRICT reader, std::vector* JXL_RESTRICT sizes, std::vector* JXL_RESTRICT permutation) { if (toc_entries > 65536) { @@ -51,8 +53,8 @@ Status ReadToc(size_t toc_entries, BitReader* JXL_RESTRICT reader, if (reader->ReadFixedBits<1>() == 1) { JXL_RETURN_IF_ERROR(check_bit_budget(toc_entries)); permutation->resize(toc_entries); - JXL_RETURN_IF_ERROR(DecodePermutation(/*skip=*/0, toc_entries, - permutation->data(), reader)); + JXL_RETURN_IF_ERROR(DecodePermutation( + memory_manager, /*skip=*/0, toc_entries, permutation->data(), reader)); } JXL_RETURN_IF_ERROR(reader->JumpToByteBoundary()); JXL_RETURN_IF_ERROR(check_bit_budget(toc_entries)); @@ -64,12 +66,14 @@ Status ReadToc(size_t toc_entries, BitReader* JXL_RESTRICT reader, return true; } -Status ReadGroupOffsets(size_t toc_entries, BitReader* JXL_RESTRICT reader, +Status ReadGroupOffsets(JxlMemoryManager* memory_manager, size_t toc_entries, + BitReader* JXL_RESTRICT reader, std::vector* JXL_RESTRICT offsets, std::vector* JXL_RESTRICT sizes, uint64_t* total_size) { std::vector permutation; - JXL_RETURN_IF_ERROR(ReadToc(toc_entries, reader, sizes, &permutation)); + JXL_RETURN_IF_ERROR( + ReadToc(memory_manager, toc_entries, reader, sizes, &permutation)); offsets->clear(); offsets->resize(toc_entries); diff --git a/third_party/jpeg-xl/lib/jxl/toc.h b/third_party/jpeg-xl/lib/jxl/toc.h index f5b9c65763d9..bbc0549e67ab 100644 --- a/third_party/jpeg-xl/lib/jxl/toc.h +++ b/third_party/jpeg-xl/lib/jxl/toc.h @@ -6,6 +6,7 @@ #ifndef LIB_JXL_TOC_H_ #define LIB_JXL_TOC_H_ +#include #include #include @@ -39,11 +40,13 @@ static JXL_INLINE size_t NumTocEntries(size_t num_groups, size_t num_dc_groups, num_groups * num_passes; } -Status ReadToc(size_t toc_entries, BitReader* JXL_RESTRICT reader, +Status ReadToc(JxlMemoryManager* memory_manager, size_t toc_entries, + BitReader* JXL_RESTRICT reader, std::vector* JXL_RESTRICT sizes, std::vector* JXL_RESTRICT permutation); -Status ReadGroupOffsets(size_t toc_entries, BitReader* JXL_RESTRICT reader, +Status ReadGroupOffsets(JxlMemoryManager* memory_manager, size_t toc_entries, + BitReader* JXL_RESTRICT reader, std::vector* JXL_RESTRICT offsets, std::vector* JXL_RESTRICT sizes, uint64_t* total_size); diff --git a/third_party/jpeg-xl/lib/jxl/toc_test.cc b/third_party/jpeg-xl/lib/jxl/toc_test.cc index 8c95f8bc2692..eaefc97bfd88 100644 --- a/third_party/jpeg-xl/lib/jxl/toc_test.cc +++ b/third_party/jpeg-xl/lib/jxl/toc_test.cc @@ -5,20 +5,25 @@ #include "lib/jxl/toc.h" +#include + +#include #include #include "lib/jxl/base/common.h" #include "lib/jxl/base/random.h" -#include "lib/jxl/base/span.h" #include "lib/jxl/coeff_order_fwd.h" #include "lib/jxl/enc_aux_out.h" +#include "lib/jxl/enc_bit_writer.h" #include "lib/jxl/enc_toc.h" +#include "lib/jxl/test_utils.h" #include "lib/jxl/testing.h" namespace jxl { namespace { void Roundtrip(size_t num_entries, bool permute, Rng* rng) { + JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); // Generate a random permutation. std::vector permutation; std::vector inv_permutation(num_entries); @@ -37,24 +42,28 @@ void Roundtrip(size_t num_entries, bool permute, Rng* rng) { } // Generate num_entries groups of random (byte-aligned) length - std::vector group_codes(num_entries); - for (BitWriter& writer : group_codes) { + std::vector> group_codes; + group_codes.reserve(num_entries); + for (size_t i = 0; i < num_entries; ++i) { + group_codes.emplace_back(jxl::make_unique(memory_manager)); + } + for (std::unique_ptr& writer : group_codes) { const size_t max_bits = (*rng)() & 0xFFF; - BitWriter::Allotment allotment(&writer, max_bits + kBitsPerByte); + BitWriter::Allotment allotment(writer.get(), max_bits + kBitsPerByte); size_t i = 0; for (; i + BitWriter::kMaxBitsPerCall < max_bits; i += BitWriter::kMaxBitsPerCall) { - writer.Write(BitWriter::kMaxBitsPerCall, 0); + writer->Write(BitWriter::kMaxBitsPerCall, 0); } for (; i < max_bits; i += 1) { - writer.Write(/*n_bits=*/1, 0); + writer->Write(/*n_bits=*/1, 0); } - writer.ZeroPadToByte(); + writer->ZeroPadToByte(); AuxOut aux_out; - allotment.ReclaimAndCharge(&writer, 0, &aux_out); + allotment.ReclaimAndCharge(writer.get(), 0, &aux_out); } - BitWriter writer; + BitWriter writer{memory_manager}; AuxOut aux_out; ASSERT_TRUE(WriteGroupOffsets(group_codes, permutation, &writer, &aux_out)); @@ -62,8 +71,8 @@ void Roundtrip(size_t num_entries, bool permute, Rng* rng) { std::vector group_offsets; std::vector group_sizes; uint64_t total_size; - ASSERT_TRUE(ReadGroupOffsets(num_entries, &reader, &group_offsets, - &group_sizes, &total_size)); + ASSERT_TRUE(ReadGroupOffsets(memory_manager, num_entries, &reader, + &group_offsets, &group_sizes, &total_size)); ASSERT_EQ(num_entries, group_offsets.size()); ASSERT_EQ(num_entries, group_sizes.size()); EXPECT_TRUE(reader.Close()); @@ -72,8 +81,8 @@ void Roundtrip(size_t num_entries, bool permute, Rng* rng) { for (size_t i = 0; i < num_entries; ++i) { EXPECT_EQ(prefix_sum, group_offsets[inv_permutation[i]]); - EXPECT_EQ(0u, group_codes[i].BitsWritten() % kBitsPerByte); - prefix_sum += group_codes[i].BitsWritten() / kBitsPerByte; + EXPECT_EQ(0u, group_codes[i]->BitsWritten() % kBitsPerByte); + prefix_sum += group_codes[i]->BitsWritten() / kBitsPerByte; if (i + 1 < num_entries) { EXPECT_EQ( diff --git a/third_party/jpeg-xl/lib/jxl_benchmark.cmake b/third_party/jpeg-xl/lib/jxl_benchmark.cmake index 67f8e7fe6409..d7a5a1144e80 100644 --- a/third_party/jpeg-xl/lib/jxl_benchmark.cmake +++ b/third_party/jpeg-xl/lib/jxl_benchmark.cmake @@ -5,10 +5,16 @@ include(jxl_lists.cmake) +if(SANITIZER STREQUAL "msan") + message(STATUS "NOT building benchmarks under MSAN") + return() +endif() + # This is the Google benchmark project (https://github.com/google/benchmark). -find_package(benchmark QUIET) +find_package(benchmark) if(benchmark_FOUND) + message(STATUS "benchmark found") if(JPEGXL_STATIC AND NOT MINGW) # benchmark::benchmark hardcodes the librt.so which obviously doesn't # compile in static mode. @@ -30,6 +36,9 @@ if(benchmark_FOUND) target_link_libraries(jxl_gbench jxl_extras-internal jxl-internal + jxl_tool benchmark::benchmark ) +else() + message(STATUS "benchmark NOT found") endif() # benchmark_FOUND diff --git a/third_party/jpeg-xl/lib/jxl_lists.bzl b/third_party/jpeg-xl/lib/jxl_lists.bzl index 41f97e434752..13d5bc2a0919 100644 --- a/third_party/jpeg-xl/lib/jxl_lists.bzl +++ b/third_party/jpeg-xl/lib/jxl_lists.bzl @@ -29,6 +29,7 @@ libjxl_base_sources = [ "jxl/base/rational_polynomial-inl.h", "jxl/base/rect.h", "jxl/base/sanitizer_definitions.h", + "jxl/base/sanitizers.h", "jxl/base/scope_guard.h", "jxl/base/span.h", "jxl/base/status.h", @@ -295,7 +296,6 @@ libjxl_dec_sources = [ "jxl/render_pipeline/stage_xyb.h", "jxl/render_pipeline/stage_ycbcr.cc", "jxl/render_pipeline/stage_ycbcr.h", - "jxl/sanitizers.h", "jxl/simd_util-inl.h", "jxl/simd_util.cc", "jxl/simd_util.h", @@ -317,8 +317,6 @@ libjxl_enc_sources = [ "jxl/enc_ans.cc", "jxl/enc_ans.h", "jxl/enc_ans_params.h", - "jxl/enc_ar_control_field.cc", - "jxl/enc_ar_control_field.h", "jxl/enc_aux_out.cc", "jxl/enc_aux_out.h", "jxl/enc_bit_writer.cc", diff --git a/third_party/jpeg-xl/lib/jxl_lists.cmake b/third_party/jpeg-xl/lib/jxl_lists.cmake index f7e36692df92..8f0b8a4304cb 100644 --- a/third_party/jpeg-xl/lib/jxl_lists.cmake +++ b/third_party/jpeg-xl/lib/jxl_lists.cmake @@ -26,6 +26,7 @@ set(JPEGXL_INTERNAL_BASE_SOURCES jxl/base/rational_polynomial-inl.h jxl/base/rect.h jxl/base/sanitizer_definitions.h + jxl/base/sanitizers.h jxl/base/scope_guard.h jxl/base/span.h jxl/base/status.h @@ -292,7 +293,6 @@ set(JPEGXL_INTERNAL_DEC_SOURCES jxl/render_pipeline/stage_xyb.h jxl/render_pipeline/stage_ycbcr.cc jxl/render_pipeline/stage_ycbcr.h - jxl/sanitizers.h jxl/simd_util-inl.h jxl/simd_util.cc jxl/simd_util.h @@ -314,8 +314,6 @@ set(JPEGXL_INTERNAL_ENC_SOURCES jxl/enc_ans.cc jxl/enc_ans.h jxl/enc_ans_params.h - jxl/enc_ar_control_field.cc - jxl/enc_ar_control_field.h jxl/enc_aux_out.cc jxl/enc_aux_out.h jxl/enc_bit_writer.cc