Backed out 3 changesets (bug 1790375) for causing wd failures on fetch_error.py....
[gecko.git] / third_party / jpeg-xl / lib / jpegli / color_transform.cc
blob020a6fd80c8db991dbaa72bf597cb759b90f82e5
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.
6 #include "lib/jpegli/color_transform.h"
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>
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"
18 HWY_BEFORE_NAMESPACE();
19 namespace jpegli {
20 namespace HWY_NAMESPACE {
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;
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];
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);
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);
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);
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))));
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);
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);
116 RGBToYCbCr(row, xsize);
119 // NOLINTNEXTLINE(google-readability-namespace-comments)
120 } // namespace HWY_NAMESPACE
121 } // namespace jpegli
122 HWY_AFTER_NAMESPACE();
124 #if HWY_ONCE
125 namespace jpegli {
127 HWY_EXPORT(CMYKToYCCK);
128 HWY_EXPORT(YCCKToCMYK);
129 HWY_EXPORT(YCbCrToRGB);
130 HWY_EXPORT(RGBToYCbCr);
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;
159 void NullTransform(float* row[kMaxComponents], size_t len) {}
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]));
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]));
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);
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);
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);
188 // No color transform requested.
189 m->color_transform = NullTransform;
190 return;
193 if (cinfo->in_color_space == JCS_RGB && m->xyb_mode) {
194 JPEGLI_ERROR("Color transform on XYB colorspace is not supported.");
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;
207 } else if (cinfo->jpeg_color_space == JCS_RGB) {
208 if (cinfo->in_color_space == JCS_GRAYSCALE) {
209 m->color_transform = GrayscaleToRGB;
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;
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);
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);
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);
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);
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);
247 // No color transform requested.
248 m->color_transform = NullTransform;
249 return;
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;
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);
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;
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);
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);
280 } // namespace jpegli
281 #endif // HWY_ONCE