libjxl

FORK: libjxl patches used on blog
git clone https://git.neptards.moe/blog/libjxl.git
Log | Files | Refs | Submodules | README | LICENSE

enc_image_bundle.cc (6289B)


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