libjxl

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

color_transform.cc (9894B)


      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/jpegli/color_transform.h"
      7 
      8 #undef HWY_TARGET_INCLUDE
      9 #define HWY_TARGET_INCLUDE "lib/jpegli/color_transform.cc"
     10 #include <hwy/foreach_target.h>
     11 #include <hwy/highway.h>
     12 
     13 #include "lib/jpegli/decode_internal.h"
     14 #include "lib/jpegli/encode_internal.h"
     15 #include "lib/jpegli/error.h"
     16 #include "lib/jxl/base/compiler_specific.h"
     17 
     18 HWY_BEFORE_NAMESPACE();
     19 namespace jpegli {
     20 namespace HWY_NAMESPACE {
     21 
     22 // These templates are not found via ADL.
     23 using hwy::HWY_NAMESPACE::Add;
     24 using hwy::HWY_NAMESPACE::Div;
     25 using hwy::HWY_NAMESPACE::Mul;
     26 using hwy::HWY_NAMESPACE::MulAdd;
     27 using hwy::HWY_NAMESPACE::Sub;
     28 
     29 void YCbCrToRGB(float* row[kMaxComponents], size_t xsize) {
     30   const HWY_CAPPED(float, 8) df;
     31   float* JXL_RESTRICT row0 = row[0];
     32   float* JXL_RESTRICT row1 = row[1];
     33   float* JXL_RESTRICT row2 = row[2];
     34 
     35   // Full-range BT.601 as defined by JFIF Clause 7:
     36   // https://www.itu.int/rec/T-REC-T.871-201105-I/en
     37   const auto crcr = Set(df, 1.402f);
     38   const auto cgcb = Set(df, -0.114f * 1.772f / 0.587f);
     39   const auto cgcr = Set(df, -0.299f * 1.402f / 0.587f);
     40   const auto cbcb = Set(df, 1.772f);
     41 
     42   for (size_t x = 0; x < xsize; x += Lanes(df)) {
     43     const auto y_vec = Load(df, row0 + x);
     44     const auto cb_vec = Load(df, row1 + x);
     45     const auto cr_vec = Load(df, row2 + x);
     46     const auto r_vec = MulAdd(crcr, cr_vec, y_vec);
     47     const auto g_vec = MulAdd(cgcr, cr_vec, MulAdd(cgcb, cb_vec, y_vec));
     48     const auto b_vec = MulAdd(cbcb, cb_vec, y_vec);
     49     Store(r_vec, df, row0 + x);
     50     Store(g_vec, df, row1 + x);
     51     Store(b_vec, df, row2 + x);
     52   }
     53 }
     54 
     55 void YCCKToCMYK(float* row[kMaxComponents], size_t xsize) {
     56   const HWY_CAPPED(float, 8) df;
     57   float* JXL_RESTRICT row0 = row[0];
     58   float* JXL_RESTRICT row1 = row[1];
     59   float* JXL_RESTRICT row2 = row[2];
     60   YCbCrToRGB(row, xsize);
     61   const auto offset = Set(df, -1.0f / 255.0f);
     62   for (size_t x = 0; x < xsize; x += Lanes(df)) {
     63     Store(Sub(offset, Load(df, row0 + x)), df, row0 + x);
     64     Store(Sub(offset, Load(df, row1 + x)), df, row1 + x);
     65     Store(Sub(offset, Load(df, row2 + x)), df, row2 + x);
     66   }
     67 }
     68 
     69 void RGBToYCbCr(float* row[kMaxComponents], size_t xsize) {
     70   const HWY_CAPPED(float, 8) df;
     71   float* JXL_RESTRICT row0 = row[0];
     72   float* JXL_RESTRICT row1 = row[1];
     73   float* JXL_RESTRICT row2 = row[2];
     74   // Full-range BT.601 as defined by JFIF Clause 7:
     75   // https://www.itu.int/rec/T-REC-T.871-201105-I/en
     76   const auto c128 = Set(df, 128.0f);
     77   const auto kR = Set(df, 0.299f);  // NTSC luma
     78   const auto kG = Set(df, 0.587f);
     79   const auto kB = Set(df, 0.114f);
     80   const auto kAmpR = Set(df, 0.701f);
     81   const auto kAmpB = Set(df, 0.886f);
     82   const auto kDiffR = Add(kAmpR, kR);
     83   const auto kDiffB = Add(kAmpB, kB);
     84   const auto kNormR = Div(Set(df, 1.0f), (Add(kAmpR, Add(kG, kB))));
     85   const auto kNormB = Div(Set(df, 1.0f), (Add(kR, Add(kG, kAmpB))));
     86 
     87   for (size_t x = 0; x < xsize; x += Lanes(df)) {
     88     const auto r = Load(df, row0 + x);
     89     const auto g = Load(df, row1 + x);
     90     const auto b = Load(df, row2 + x);
     91     const auto r_base = Mul(r, kR);
     92     const auto r_diff = Mul(r, kDiffR);
     93     const auto g_base = Mul(g, kG);
     94     const auto b_base = Mul(b, kB);
     95     const auto b_diff = Mul(b, kDiffB);
     96     const auto y_base = Add(r_base, Add(g_base, b_base));
     97     const auto cb_vec = MulAdd(Sub(b_diff, y_base), kNormB, c128);
     98     const auto cr_vec = MulAdd(Sub(r_diff, y_base), kNormR, c128);
     99     Store(y_base, df, row0 + x);
    100     Store(cb_vec, df, row1 + x);
    101     Store(cr_vec, df, row2 + x);
    102   }
    103 }
    104 
    105 void CMYKToYCCK(float* row[kMaxComponents], size_t xsize) {
    106   const HWY_CAPPED(float, 8) df;
    107   float* JXL_RESTRICT row0 = row[0];
    108   float* JXL_RESTRICT row1 = row[1];
    109   float* JXL_RESTRICT row2 = row[2];
    110   const auto unity = Set(df, 255.0f);
    111   for (size_t x = 0; x < xsize; x += Lanes(df)) {
    112     Store(Sub(unity, Load(df, row0 + x)), df, row0 + x);
    113     Store(Sub(unity, Load(df, row1 + x)), df, row1 + x);
    114     Store(Sub(unity, Load(df, row2 + x)), df, row2 + x);
    115   }
    116   RGBToYCbCr(row, xsize);
    117 }
    118 
    119 // NOLINTNEXTLINE(google-readability-namespace-comments)
    120 }  // namespace HWY_NAMESPACE
    121 }  // namespace jpegli
    122 HWY_AFTER_NAMESPACE();
    123 
    124 #if HWY_ONCE
    125 namespace jpegli {
    126 
    127 HWY_EXPORT(CMYKToYCCK);
    128 HWY_EXPORT(YCCKToCMYK);
    129 HWY_EXPORT(YCbCrToRGB);
    130 HWY_EXPORT(RGBToYCbCr);
    131 
    132 bool CheckColorSpaceComponents(int num_components, J_COLOR_SPACE colorspace) {
    133   switch (colorspace) {
    134     case JCS_GRAYSCALE:
    135       return num_components == 1;
    136     case JCS_RGB:
    137     case JCS_YCbCr:
    138     case JCS_EXT_RGB:
    139     case JCS_EXT_BGR:
    140       return num_components == 3;
    141     case JCS_CMYK:
    142     case JCS_YCCK:
    143     case JCS_EXT_RGBX:
    144     case JCS_EXT_BGRX:
    145     case JCS_EXT_XBGR:
    146     case JCS_EXT_XRGB:
    147     case JCS_EXT_RGBA:
    148     case JCS_EXT_BGRA:
    149     case JCS_EXT_ABGR:
    150     case JCS_EXT_ARGB:
    151       return num_components == 4;
    152     default:
    153       // Unrecognized colorspaces can have any number of channels, since no
    154       // color transform will be performed on them.
    155       return true;
    156   }
    157 }
    158 
    159 void NullTransform(float* row[kMaxComponents], size_t len) {}
    160 
    161 void GrayscaleToRGB(float* row[kMaxComponents], size_t len) {
    162   memcpy(row[1], row[0], len * sizeof(row[1][0]));
    163   memcpy(row[2], row[0], len * sizeof(row[2][0]));
    164 }
    165 
    166 void GrayscaleToYCbCr(float* row[kMaxComponents], size_t len) {
    167   memset(row[1], 0, len * sizeof(row[1][0]));
    168   memset(row[2], 0, len * sizeof(row[2][0]));
    169 }
    170 
    171 void ChooseColorTransform(j_compress_ptr cinfo) {
    172   jpeg_comp_master* m = cinfo->master;
    173   if (!CheckColorSpaceComponents(cinfo->input_components,
    174                                  cinfo->in_color_space)) {
    175     JPEGLI_ERROR("Invalid number of input components %d for colorspace %d",
    176                  cinfo->input_components, cinfo->in_color_space);
    177   }
    178   if (!CheckColorSpaceComponents(cinfo->num_components,
    179                                  cinfo->jpeg_color_space)) {
    180     JPEGLI_ERROR("Invalid number of components %d for colorspace %d",
    181                  cinfo->num_components, cinfo->jpeg_color_space);
    182   }
    183   if (cinfo->jpeg_color_space == cinfo->in_color_space) {
    184     if (cinfo->num_components != cinfo->input_components) {
    185       JPEGLI_ERROR("Input/output components mismatch:  %d vs %d",
    186                    cinfo->input_components, cinfo->num_components);
    187     }
    188     // No color transform requested.
    189     m->color_transform = NullTransform;
    190     return;
    191   }
    192 
    193   if (cinfo->in_color_space == JCS_RGB && m->xyb_mode) {
    194     JPEGLI_ERROR("Color transform on XYB colorspace is not supported.");
    195   }
    196 
    197   m->color_transform = nullptr;
    198   if (cinfo->jpeg_color_space == JCS_GRAYSCALE) {
    199     if (cinfo->in_color_space == JCS_RGB) {
    200       m->color_transform = HWY_DYNAMIC_DISPATCH(RGBToYCbCr);
    201     } else if (cinfo->in_color_space == JCS_YCbCr ||
    202                cinfo->in_color_space == JCS_YCCK) {
    203       // Since the first luminance channel is the grayscale version of the
    204       // image, nothing to do here
    205       m->color_transform = NullTransform;
    206     }
    207   } else if (cinfo->jpeg_color_space == JCS_RGB) {
    208     if (cinfo->in_color_space == JCS_GRAYSCALE) {
    209       m->color_transform = GrayscaleToRGB;
    210     }
    211   } else if (cinfo->jpeg_color_space == JCS_YCbCr) {
    212     if (cinfo->in_color_space == JCS_RGB) {
    213       m->color_transform = HWY_DYNAMIC_DISPATCH(RGBToYCbCr);
    214     } else if (cinfo->in_color_space == JCS_GRAYSCALE) {
    215       m->color_transform = GrayscaleToYCbCr;
    216     }
    217   } else if (cinfo->jpeg_color_space == JCS_YCCK) {
    218     if (cinfo->in_color_space == JCS_CMYK) {
    219       m->color_transform = HWY_DYNAMIC_DISPATCH(CMYKToYCCK);
    220     }
    221   }
    222 
    223   if (m->color_transform == nullptr) {
    224     // TODO(szabadka) Support more color transforms.
    225     JPEGLI_ERROR("Unsupported color transform %d -> %d", cinfo->in_color_space,
    226                  cinfo->jpeg_color_space);
    227   }
    228 }
    229 
    230 void ChooseColorTransform(j_decompress_ptr cinfo) {
    231   jpeg_decomp_master* m = cinfo->master;
    232   if (!CheckColorSpaceComponents(cinfo->out_color_components,
    233                                  cinfo->out_color_space)) {
    234     JPEGLI_ERROR("Invalid number of output components %d for colorspace %d",
    235                  cinfo->out_color_components, cinfo->out_color_space);
    236   }
    237   if (!CheckColorSpaceComponents(cinfo->num_components,
    238                                  cinfo->jpeg_color_space)) {
    239     JPEGLI_ERROR("Invalid number of components %d for colorspace %d",
    240                  cinfo->num_components, cinfo->jpeg_color_space);
    241   }
    242   if (cinfo->jpeg_color_space == cinfo->out_color_space) {
    243     if (cinfo->num_components != cinfo->out_color_components) {
    244       JPEGLI_ERROR("Input/output components mismatch:  %d vs %d",
    245                    cinfo->num_components, cinfo->out_color_components);
    246     }
    247     // No color transform requested.
    248     m->color_transform = NullTransform;
    249     return;
    250   }
    251 
    252   m->color_transform = nullptr;
    253   if (cinfo->jpeg_color_space == JCS_GRAYSCALE) {
    254     if (cinfo->out_color_space == JCS_RGB) {
    255       m->color_transform = GrayscaleToRGB;
    256     }
    257   } else if (cinfo->jpeg_color_space == JCS_RGB) {
    258     if (cinfo->out_color_space == JCS_GRAYSCALE) {
    259       m->color_transform = HWY_DYNAMIC_DISPATCH(RGBToYCbCr);
    260     }
    261   } else if (cinfo->jpeg_color_space == JCS_YCbCr) {
    262     if (cinfo->out_color_space == JCS_RGB) {
    263       m->color_transform = HWY_DYNAMIC_DISPATCH(YCbCrToRGB);
    264     } else if (cinfo->out_color_space == JCS_GRAYSCALE) {
    265       m->color_transform = NullTransform;
    266     }
    267   } else if (cinfo->jpeg_color_space == JCS_YCCK) {
    268     if (cinfo->out_color_space == JCS_CMYK) {
    269       m->color_transform = HWY_DYNAMIC_DISPATCH(YCCKToCMYK);
    270     }
    271   }
    272 
    273   if (m->color_transform == nullptr) {
    274     // TODO(szabadka) Support more color transforms.
    275     JPEGLI_ERROR("Unsupported color transform %d -> %d",
    276                  cinfo->jpeg_color_space, cinfo->out_color_space);
    277   }
    278 }
    279 
    280 }  // namespace jpegli
    281 #endif  // HWY_ONCE