tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

enc_image_bundle.cc (6465B)


      1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
      2 //
      3 // Use of this source code is governed by a BSD-style
      4 // license that can be found in the LICENSE file.
      5 
      6 #include "lib/jxl/enc_image_bundle.h"
      7 
      8 #include <jxl/cms_interface.h>
      9 #include <jxl/memory_manager.h>
     10 
     11 #include <utility>
     12 
     13 #include "lib/jxl/base/rect.h"
     14 #include "lib/jxl/base/status.h"
     15 #include "lib/jxl/color_encoding_internal.h"
     16 #include "lib/jxl/image_bundle.h"
     17 
     18 namespace jxl {
     19 
     20 Status ApplyColorTransform(const ColorEncoding& c_current,
     21                           float intensity_target, const Image3F& color,
     22                           const ImageF* black, const Rect& rect,
     23                           const ColorEncoding& c_desired,
     24                           const JxlCmsInterface& cms, ThreadPool* pool,
     25                           Image3F* out) {
     26  ColorSpaceTransform c_transform(cms);
     27  // Changing IsGray is probably a bug.
     28  JXL_ENSURE(c_current.IsGray() == c_desired.IsGray());
     29  bool is_gray = c_current.IsGray();
     30  JxlMemoryManager* memory_amanger = color.memory_manager();
     31  if (out->xsize() < rect.xsize() || out->ysize() < rect.ysize()) {
     32    JXL_ASSIGN_OR_RETURN(
     33        *out, Image3F::Create(memory_amanger, rect.xsize(), rect.ysize()));
     34  } else {
     35    JXL_RETURN_IF_ERROR(out->ShrinkTo(rect.xsize(), rect.ysize()));
     36  }
     37  const auto init = [&](const size_t num_threads) -> Status {
     38    JXL_RETURN_IF_ERROR(c_transform.Init(c_current, c_desired, intensity_target,
     39                                         rect.xsize(), num_threads));
     40    return true;
     41  };
     42  const auto transform_row = [&](const uint32_t y,
     43                                 const size_t thread) -> Status {
     44    float* mutable_src_buf = c_transform.BufSrc(thread);
     45    const float* src_buf = mutable_src_buf;
     46    // Interleave input.
     47    if (is_gray) {
     48      src_buf = rect.ConstPlaneRow(color, 0, y);
     49    } else if (c_current.IsCMYK()) {
     50      if (!black)
     51        return JXL_FAILURE("Black plane is missing for CMYK transform");
     52      const float* JXL_RESTRICT row_in0 = rect.ConstPlaneRow(color, 0, y);
     53      const float* JXL_RESTRICT row_in1 = rect.ConstPlaneRow(color, 1, y);
     54      const float* JXL_RESTRICT row_in2 = rect.ConstPlaneRow(color, 2, y);
     55      const float* JXL_RESTRICT row_in3 = rect.ConstRow(*black, y);
     56      for (size_t x = 0; x < rect.xsize(); x++) {
     57        // CMYK convention in JXL: 0 = max ink, 1 = white
     58        mutable_src_buf[4 * x + 0] = row_in0[x];
     59        mutable_src_buf[4 * x + 1] = row_in1[x];
     60        mutable_src_buf[4 * x + 2] = row_in2[x];
     61        mutable_src_buf[4 * x + 3] = row_in3[x];
     62      }
     63    } else {
     64      const float* JXL_RESTRICT row_in0 = rect.ConstPlaneRow(color, 0, y);
     65      const float* JXL_RESTRICT row_in1 = rect.ConstPlaneRow(color, 1, y);
     66      const float* JXL_RESTRICT row_in2 = rect.ConstPlaneRow(color, 2, y);
     67      for (size_t x = 0; x < rect.xsize(); x++) {
     68        mutable_src_buf[3 * x + 0] = row_in0[x];
     69        mutable_src_buf[3 * x + 1] = row_in1[x];
     70        mutable_src_buf[3 * x + 2] = row_in2[x];
     71      }
     72    }
     73    float* JXL_RESTRICT dst_buf = c_transform.BufDst(thread);
     74    JXL_RETURN_IF_ERROR(
     75        c_transform.Run(thread, src_buf, dst_buf, rect.xsize()));
     76    float* JXL_RESTRICT row_out0 = out->PlaneRow(0, y);
     77    float* JXL_RESTRICT row_out1 = out->PlaneRow(1, y);
     78    float* JXL_RESTRICT row_out2 = out->PlaneRow(2, y);
     79    // De-interleave output and convert type.
     80    if (is_gray) {
     81      for (size_t x = 0; x < rect.xsize(); x++) {
     82        row_out0[x] = dst_buf[x];
     83        row_out1[x] = dst_buf[x];
     84        row_out2[x] = dst_buf[x];
     85      }
     86    } else {
     87      for (size_t x = 0; x < rect.xsize(); x++) {
     88        row_out0[x] = dst_buf[3 * x + 0];
     89        row_out1[x] = dst_buf[3 * x + 1];
     90        row_out2[x] = dst_buf[3 * x + 2];
     91      }
     92    }
     93    return true;
     94  };
     95  JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, rect.ysize(), init, transform_row,
     96                                "Colorspace transform"));
     97  return true;
     98 }
     99 
    100 namespace {
    101 
    102 // Copies ib:rect, converts, and copies into out.
    103 Status CopyToT(const ImageMetadata* metadata, const ImageBundle* ib,
    104               const Rect& rect, const ColorEncoding& c_desired,
    105               const JxlCmsInterface& cms, ThreadPool* pool, Image3F* out) {
    106  return ApplyColorTransform(ib->c_current(), metadata->IntensityTarget(),
    107                             ib->color(), ib->black(), rect, c_desired, cms,
    108                             pool, out);
    109 }
    110 
    111 }  // namespace
    112 
    113 Status ImageBundle::TransformTo(const ColorEncoding& c_desired,
    114                                const JxlCmsInterface& cms, ThreadPool* pool) {
    115  JXL_RETURN_IF_ERROR(CopyTo(Rect(color_), c_desired, cms, &color_, pool));
    116  c_current_ = c_desired;
    117  return true;
    118 }
    119 Status ImageBundle::CopyTo(const Rect& rect, const ColorEncoding& c_desired,
    120                           const JxlCmsInterface& cms, Image3F* out,
    121                           ThreadPool* pool) const {
    122  return CopyToT(metadata_, this, rect, c_desired, cms, pool, out);
    123 }
    124 Status TransformIfNeeded(const ImageBundle& in, const ColorEncoding& c_desired,
    125                         const JxlCmsInterface& cms, ThreadPool* pool,
    126                         ImageBundle* store, const ImageBundle** out) {
    127  if (in.c_current().SameColorEncoding(c_desired) && !in.HasBlack()) {
    128    *out = &in;
    129    return true;
    130  }
    131  JxlMemoryManager* memory_manager = in.memory_manager();
    132  // TODO(janwas): avoid copying via createExternal+copyBackToIO
    133  // instead of copy+createExternal+copyBackToIO
    134  JXL_ASSIGN_OR_RETURN(
    135      Image3F color,
    136      Image3F::Create(memory_manager, in.color().xsize(), in.color().ysize()));
    137  JXL_RETURN_IF_ERROR(CopyImageTo(in.color(), &color));
    138  JXL_RETURN_IF_ERROR(store->SetFromImage(std::move(color), in.c_current()));
    139 
    140  // Must at least copy the alpha channel for use by external_image.
    141  if (in.HasExtraChannels()) {
    142    std::vector<ImageF> extra_channels;
    143    for (const ImageF& extra_channel : in.extra_channels()) {
    144      JXL_ASSIGN_OR_RETURN(ImageF ec,
    145                           ImageF::Create(memory_manager, extra_channel.xsize(),
    146                                          extra_channel.ysize()));
    147      JXL_RETURN_IF_ERROR(CopyImageTo(extra_channel, &ec));
    148      extra_channels.emplace_back(std::move(ec));
    149    }
    150    JXL_RETURN_IF_ERROR(store->SetExtraChannels(std::move(extra_channels)));
    151  }
    152 
    153  if (!store->TransformTo(c_desired, cms, pool)) {
    154    return false;
    155  }
    156  *out = store;
    157  return true;
    158 }
    159 
    160 }  // namespace jxl