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