1 // Copyright 2018 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "src/decoder/endpoint_codec.h"
16 #include "src/decoder/quantization.h"
17
18 #include <algorithm>
19 #include <array>
20 #include <numeric>
21 #include <utility>
22
23 namespace astc_codec {
24
25 namespace {
26
27 template<typename T>
Clamp(T value,T min,T max)28 T Clamp(T value, T min, T max) {
29 return value < min ? min : (value > max ? max : value);
30 }
31
32 // This is the 'blue_contract' function defined in Section C.2.14 of the ASTC
33 // specification.
34 template<typename ArrayType>
BlueContract(ArrayType * const cptr)35 void BlueContract(ArrayType* const cptr) {
36 ArrayType& c = *cptr;
37 c[0] = (c[0] + c[2]) >> 1;
38 c[1] = (c[1] + c[2]) >> 1;
39 }
40
41 // Returns the inverse of values in BlueContract, subjected to the constraint
42 // that the new values are stored in the range [0, 255].
43 template<typename ArrayType>
InvertBlueContract(const ArrayType & c)44 ArrayType InvertBlueContract(const ArrayType& c) {
45 ArrayType result = c;
46 result[0] = Clamp(2 * c[0] - c[2], 0, 255);
47 result[1] = Clamp(2 * c[1] - c[2], 0, 255);
48 return result;
49 }
50
51 // This is the 'bit_transfer_signed' function defined in Section C.2.14 of the
52 // ASTC specification.
BitTransferSigned(int * const a,int * const b)53 void BitTransferSigned(int* const a, int* const b) {
54 *b >>= 1;
55 *b |= *a & 0x80;
56 *a >>= 1;
57 *a &= 0x3F;
58 if ((*a & 0x20) != 0) {
59 *a -= 0x40;
60 }
61 }
62
63 // Takes two values, |a| in the range [-32, 31], and |b| in the range [0, 255],
64 // and returns the two values in [0, 255] that will reconstruct |a| and |b| when
65 // passed to the BitTransferSigned function.
InvertBitTransferSigned(int * const a,int * const b)66 void InvertBitTransferSigned(int* const a, int* const b) {
67 assert(*a >= -32); assert(*a < 32);
68 assert(*b >= 0); assert(*b < 256);
69
70 if (*a < 0) {
71 *a += 0x40;
72 }
73 *a <<= 1;
74 *a |= (*b & 0x80);
75 *b <<= 1;
76 *b &= 0xff;
77 }
78
79 template<typename ContainerType>
Quantize(ContainerType * const c,size_t max_value)80 void Quantize(ContainerType* const c, size_t max_value) {
81 for (auto& x : *c) {
82 x = QuantizeCEValueToRange(x, int(max_value));
83 }
84 }
85
86 template<typename ArrayType>
QuantizeColor(const ArrayType & c,size_t max_value)87 ArrayType QuantizeColor(const ArrayType& c, size_t max_value) {
88 ArrayType result = c;
89 Quantize(&result, max_value);
90 return result;
91 }
92
93 template<typename ContainerType>
Unquantize(ContainerType * const c,size_t max_value)94 void Unquantize(ContainerType* const c, size_t max_value) {
95 for (auto& x : *c) {
96 x = UnquantizeCEValueFromRange(x, int(max_value));
97 }
98 }
99
100 template<typename ArrayType>
UnquantizeColor(const ArrayType & c,size_t max_value)101 ArrayType UnquantizeColor(const ArrayType& c, size_t max_value) {
102 ArrayType result = c;
103 Unquantize(&result, max_value);
104 return result;
105 }
106
107 // Returns the average of the three RGB channels.
108 template<typename ContainerType>
AverageRGB(const ContainerType & c)109 int AverageRGB(const ContainerType& c) {
110 // Each channel can be in the range [0, 255], and we need to divide by three.
111 // However, we want to round the error properly. Both (x + 1) / 3 and
112 // (x + 2) / 3 are relatively imprecise when it comes to rounding, so instead
113 // we increase the precision by multiplying our numerator by some arbitrary
114 // number. Here, we choose 256 to get 8 additional bits and maintain
115 // performance since it turns into a shift rather than a multiply. Our
116 // denominator then becomes 3 * 256 = 768.
117 return (std::accumulate(c.begin(), c.begin() + 3, 0) * 256 + 384) / 768;
118 }
119
120 // Returns the sum of squared differences between each element of |a| and |b|,
121 // which are assumed to contain the same number of elements.
122 template<typename ContainerType>
SquaredError(const ContainerType & a,const ContainerType & b,size_t num_channels=std::tuple_size<ContainerType>::value)123 const typename ContainerType::value_type SquaredError(
124 const ContainerType& a, const ContainerType& b,
125 size_t num_channels = std::tuple_size<ContainerType>::value) {
126 using ValueTy = typename ContainerType::value_type;
127 static_assert(std::is_signed<ValueTy>::value,
128 "Value type assumed to be signed to avoid branch below.");
129 ValueTy result = ValueTy(0);
130 for (size_t i = 0; i < num_channels; ++i) {
131 ValueTy error = a[i] - b[i];
132 result += error * error;
133 }
134 return result;
135 }
136
MaxValuesForModes(ColorEndpointMode mode_a,ColorEndpointMode mode_b)137 constexpr int MaxValuesForModes(ColorEndpointMode mode_a,
138 ColorEndpointMode mode_b) {
139 return (NumColorValuesForEndpointMode(mode_a) >
140 NumColorValuesForEndpointMode(mode_b))
141 ? NumColorValuesForEndpointMode(mode_a)
142 : NumColorValuesForEndpointMode(mode_b);
143 }
144
145 // This function takes the two colors in |endpoint_low| and |endpoint_high| and
146 // encodes them into |vals| according to the ASTC spec in section C.2.14. It
147 // assumes that the two colors are close enough to grayscale that the encoding
148 // should use the ColorEndpointMode kLDRLumaBaseOffset or kLDRLumaDirect. Which
149 // one is chosen depends on which produces smaller error for the given
150 // quantization value stored in |max_value|
EncodeColorsLuma(const RgbaColor & endpoint_low,const RgbaColor & endpoint_high,int max_value,ColorEndpointMode * const astc_mode,std::vector<int> * const vals)151 bool EncodeColorsLuma(const RgbaColor& endpoint_low,
152 const RgbaColor& endpoint_high,
153 int max_value, ColorEndpointMode* const astc_mode,
154 std::vector<int>* const vals) {
155 assert(vals->size() ==
156 size_t(NumValuesForEncodingMode(EndpointEncodingMode::kDirectLuma)));
157 int avg1 = AverageRGB(endpoint_low);
158 int avg2 = AverageRGB(endpoint_high);
159
160 // For the offset mode, L1 is strictly greater than L2, so if we are using
161 // it to encode the color values, we need to swap the weights and
162 // endpoints so that the larger of the two is the second endpoint.
163 bool needs_weight_swap = false;
164 if (avg1 > avg2) {
165 needs_weight_swap = true;
166 std::swap(avg1, avg2);
167 }
168 assert(avg1 <= avg2);
169
170 // Now, the first endpoint is based on the low-order six bits of the first
171 // value, and the high order two bits of the second value. The low order
172 // six bits of the second value are used as the (strictly positive) offset
173 // from the first value.
174 const int offset = std::min(avg2 - avg1, 0x3F);
175 const int quant_off_low =
176 QuantizeCEValueToRange((avg1 & 0x3F) << 2, max_value);
177 const int quant_off_high =
178 QuantizeCEValueToRange((avg1 & 0xC0) | offset, max_value);
179
180 const int quant_low = QuantizeCEValueToRange(avg1, max_value);
181 const int quant_high = QuantizeCEValueToRange(avg2, max_value);
182
183 RgbaColor unquant_off_low, unquant_off_high;
184 RgbaColor unquant_low, unquant_high;
185
186 (*vals)[0] = quant_off_low;
187 (*vals)[1] = quant_off_high;
188 DecodeColorsForMode(
189 *vals, max_value, ColorEndpointMode::kLDRLumaBaseOffset,
190 &unquant_off_low, &unquant_off_high);
191
192 (*vals)[0] = quant_low;
193 (*vals)[1] = quant_high;
194 DecodeColorsForMode(*vals, max_value, ColorEndpointMode::kLDRLumaDirect,
195 &unquant_low, &unquant_high);
196
197 const auto calculate_error =
198 [needs_weight_swap, &endpoint_low, &endpoint_high]
199 (const RgbaColor& low, const RgbaColor& high) {
200 int error = 0;
201 if (needs_weight_swap) {
202 error += SquaredError(low, endpoint_high);
203 error += SquaredError(high, endpoint_low);
204 } else {
205 error += SquaredError(low, endpoint_low);
206 error += SquaredError(high, endpoint_high);
207 }
208 return error;
209 };
210
211 const int direct_error = calculate_error(unquant_low, unquant_high);
212 const int off_error = calculate_error(unquant_off_low, unquant_off_high);
213
214 if (direct_error <= off_error) {
215 (*vals)[0] = quant_low;
216 (*vals)[1] = quant_high;
217 *astc_mode = ColorEndpointMode::kLDRLumaDirect;
218 } else {
219 (*vals)[0] = quant_off_low;
220 (*vals)[1] = quant_off_high;
221 *astc_mode = ColorEndpointMode::kLDRLumaBaseOffset;
222 }
223
224 return needs_weight_swap;
225 }
226
227 class QuantizedEndpointPair {
228 public:
QuantizedEndpointPair(const RgbaColor & c_low,const RgbaColor & c_high,int max_value)229 QuantizedEndpointPair(const RgbaColor& c_low, const RgbaColor& c_high,
230 int max_value)
231 : orig_low_(c_low),
232 orig_high_(c_high),
233 quant_low_(QuantizeColor(c_low, max_value)),
234 quant_high_(QuantizeColor(c_high, max_value)),
235 unquant_low_(UnquantizeColor(quant_low_, max_value)),
236 unquant_high_(UnquantizeColor(quant_high_, max_value)) { }
237
QuantizedLow() const238 const RgbaColor& QuantizedLow() const { return quant_low_; }
QuantizedHigh() const239 const RgbaColor& QuantizedHigh() const { return quant_high_; }
240
UnquantizedLow() const241 const RgbaColor& UnquantizedLow() const { return unquant_low_; }
UnquantizedHigh() const242 const RgbaColor& UnquantizedHigh() const { return unquant_high_; }
243
OriginalLow() const244 const RgbaColor& OriginalLow() const { return orig_low_; }
OriginalHigh() const245 const RgbaColor& OriginalHigh() const { return orig_high_; }
246
247 private:
248 RgbaColor orig_low_;
249 RgbaColor orig_high_;
250
251 RgbaColor quant_low_;
252 RgbaColor quant_high_;
253
254 RgbaColor unquant_low_;
255 RgbaColor unquant_high_;
256 };
257
258 class CEEncodingOption {
259 public:
CEEncodingOption()260 CEEncodingOption() { }
CEEncodingOption(int squared_error,const QuantizedEndpointPair * quantized_endpoints,bool swap_endpoints,bool blue_contract,bool use_offset_mode)261 CEEncodingOption(
262 int squared_error, const QuantizedEndpointPair* quantized_endpoints,
263 bool swap_endpoints, bool blue_contract, bool use_offset_mode)
264 : squared_error_(squared_error),
265 quantized_endpoints_(quantized_endpoints),
266 swap_endpoints_(swap_endpoints),
267 blue_contract_(blue_contract),
268 use_offset_mode_(use_offset_mode) { }
269
270 // Returns true if able to generate valid |astc_mode| and |vals|. In some
271 // instances, such as if the endpoints reprsent a base/offset pair, we may not
272 // be able to guarantee blue-contract encoding due to how the base/offset pair
273 // are represented and the specifics of the decoding procedure. Similarly,
274 // some direct RGBA encodings also may not be able to emit blue-contract modes
275 // due to an unlucky combination of channels. In these instances, this
276 // function will return false, and all pointers will remain unmodified.
Pack(bool with_alpha,ColorEndpointMode * const astc_mode,std::vector<int> * const vals,bool * const needs_weight_swap) const277 bool Pack(bool with_alpha, ColorEndpointMode* const astc_mode,
278 std::vector<int>* const vals, bool* const needs_weight_swap) const {
279 auto unquantized_low = quantized_endpoints_->UnquantizedLow();
280 auto unquantized_high = quantized_endpoints_->UnquantizedHigh();
281
282 // In offset mode, we do BitTransferSigned before analyzing the values
283 // of the endpoints in order to determine whether or not we're going to
284 // be using blue-contract mode.
285 if (use_offset_mode_) {
286 for (size_t i = 0; i < std::tuple_size<RgbaColor>::value; ++i) {
287 BitTransferSigned(&unquantized_high[i], &unquantized_low[i]);
288 }
289 }
290
291 // Define variables as outlined in the ASTC spec C.2.14 for the RGB[A]
292 // direct and base-offset modes
293 int s0 = 0, s1 = 0;
294 for (int i = 0; i < 3; ++i) {
295 s0 += unquantized_low[i];
296 s1 += unquantized_high[i];
297 }
298
299 // Can we guarantee a blue-contract mode if we want it? In other words,
300 // if we swap which endpoint is high and which endpoint is low, can we
301 // guarantee that we will hit the corresponding decode path?
302 bool swap_vals = false;
303 if (use_offset_mode_) {
304 if (blue_contract_) {
305 swap_vals = s1 >= 0;
306 } else {
307 swap_vals = s1 < 0;
308 }
309
310 // In offset mode, we have two different measurements that swap the
311 // endpoints prior to encoding, so we don't need to swap them here.
312 // If we need to swap them to guarantee a blue-contract mode, then
313 // abort and wait until we get the other error measurement.
314 if (swap_vals) {
315 return false;
316 }
317 } else {
318 if (blue_contract_) {
319 // If we want a blue_contract path, but s1 == s0, then swapping the
320 // values will have no effect.
321 if (s1 == s0) {
322 return false;
323 }
324
325 swap_vals = s1 > s0;
326 // If we're encoding blue contract mode directly, then we implicitly
327 // swap the endpoints during decode, meaning that we need to take
328 // note of that here.
329 *needs_weight_swap = !(*needs_weight_swap);
330 } else {
331 swap_vals = s1 < s0;
332 }
333 }
334
335 const auto* quantized_low = &(quantized_endpoints_->QuantizedLow());
336 const auto* quantized_high = &(quantized_endpoints_->QuantizedHigh());
337
338 if (swap_vals) {
339 assert(!use_offset_mode_);
340 std::swap(quantized_low, quantized_high);
341 *needs_weight_swap = !(*needs_weight_swap);
342 }
343
344 (*vals)[0] = quantized_low->at(0);
345 (*vals)[1] = quantized_high->at(0);
346 (*vals)[2] = quantized_low->at(1);
347 (*vals)[3] = quantized_high->at(1);
348 (*vals)[4] = quantized_low->at(2);
349 (*vals)[5] = quantized_high->at(2);
350
351 if (use_offset_mode_) {
352 *astc_mode = ColorEndpointMode::kLDRRGBBaseOffset;
353 } else {
354 *astc_mode = ColorEndpointMode::kLDRRGBDirect;
355 }
356
357 if (with_alpha) {
358 (*vals)[6] = quantized_low->at(3);
359 (*vals)[7] = quantized_high->at(3);
360
361 if (use_offset_mode_) {
362 *astc_mode = ColorEndpointMode::kLDRRGBABaseOffset;
363 } else {
364 *astc_mode = ColorEndpointMode::kLDRRGBADirect;
365 }
366 }
367
368 // If we swapped them to measure, then they need to be swapped after
369 // decoding
370 if (swap_endpoints_) {
371 *needs_weight_swap = !(*needs_weight_swap);
372 }
373
374 return true;
375 }
376
BlueContract() const377 bool BlueContract() const { return blue_contract_; }
Error() const378 int Error() const { return squared_error_; }
379
380 private:
381 int squared_error_;
382 const QuantizedEndpointPair* quantized_endpoints_;
383 bool swap_endpoints_;
384 bool blue_contract_;
385 bool use_offset_mode_;
386 };
387
EncodeColorsRGBA(const RgbaColor & endpoint_low_rgba,const RgbaColor & endpoint_high_rgba,int max_value,bool with_alpha,ColorEndpointMode * const astc_mode,std::vector<int> * const vals)388 bool EncodeColorsRGBA(const RgbaColor& endpoint_low_rgba,
389 const RgbaColor& endpoint_high_rgba,
390 int max_value, bool with_alpha,
391 ColorEndpointMode* const astc_mode,
392 std::vector<int>* const vals) {
393 const size_t num_channels = with_alpha ? std::tuple_size<RgbaColor>::value : 3;
394 // The difficulty of encoding into this mode is determining whether or
395 // not we'd like to use the 'blue contract' function to reconstruct
396 // the endpoints and whether or not we'll be more accurate by using the
397 // base/offset color modes instead of quantizing the color channels
398 // directly. With that in mind, we:
399 // 1. Generate the inverted values for blue-contract and offset modes.
400 // 2. Quantize all of the different endpoints.
401 // 3. Unquantize each sets and decide which one gives least error
402 // 4. Encode the values correspondingly.
403
404 // 1. Generate the inverted values for blue-contract and offset modes.
405 const auto inv_bc_low = InvertBlueContract(endpoint_low_rgba);
406 const auto inv_bc_high = InvertBlueContract(endpoint_high_rgba);
407
408 RgbaColor direct_base, direct_offset;
409 for (size_t i = 0; i < std::tuple_size<RgbaColor>::value; ++i) {
410 direct_base[i] = endpoint_low_rgba[i];
411 direct_offset[i] =
412 Clamp(endpoint_high_rgba[i] - endpoint_low_rgba[i], -32, 31);
413 InvertBitTransferSigned(&direct_offset[i], &direct_base[i]);
414 }
415
416 RgbaColor inv_bc_base, inv_bc_offset;
417 for (size_t i = 0; i < std::tuple_size<RgbaColor>::value; ++i) {
418 // Remember, for blue-contract'd offset modes, the base is compared
419 // against the second endpoint and not the first.
420 inv_bc_base[i] = inv_bc_high[i];
421 inv_bc_offset[i] = Clamp(inv_bc_low[i] - inv_bc_high[i], -32, 31);
422 InvertBitTransferSigned(&inv_bc_offset[i], &inv_bc_base[i]);
423 }
424
425 // The order of the endpoints for offset modes may determine how well they
426 // approximate the given endpoints. It may be that the quantization value
427 // produces more accurate values for the base than the offset or
428 // vice/versa. For this reason, we need to generate quantized versions of
429 // the endpoints as if they were swapped to see if we get better error
430 // out of it.
431
432 RgbaColor direct_base_swapped, direct_offset_swapped;
433 for (size_t i = 0; i < std::tuple_size<RgbaColor>::value; ++i) {
434 direct_base_swapped[i] = endpoint_high_rgba[i];
435 direct_offset_swapped[i] =
436 Clamp(endpoint_low_rgba[i] - endpoint_high_rgba[i], -32, 31);
437 InvertBitTransferSigned(&direct_offset_swapped[i], &direct_base_swapped[i]);
438 }
439
440 RgbaColor inv_bc_base_swapped, inv_bc_offset_swapped;
441 for (size_t i = 0; i < std::tuple_size<RgbaColor>::value; ++i) {
442 // Remember, for blue-contract'd offset modes, the base is compared
443 // against the second endpoint and not the first. Hence, the swapped
444 // version will compare the base against the first endpoint.
445 inv_bc_base_swapped[i] = inv_bc_low[i];
446 inv_bc_offset_swapped[i] = Clamp(inv_bc_high[i] - inv_bc_low[i], -32, 31);
447 InvertBitTransferSigned(&inv_bc_offset_swapped[i], &inv_bc_base_swapped[i]);
448 }
449
450 // 2. Quantize the endpoints directly.
451 const QuantizedEndpointPair direct_quantized(
452 endpoint_low_rgba, endpoint_high_rgba, max_value);
453 const QuantizedEndpointPair bc_quantized(
454 inv_bc_low, inv_bc_high, max_value);
455
456 const QuantizedEndpointPair offset_quantized(
457 direct_base, direct_offset, max_value);
458 const QuantizedEndpointPair bc_offset_quantized(
459 inv_bc_base, inv_bc_offset, max_value);
460
461 const QuantizedEndpointPair offset_swapped_quantized(
462 direct_base_swapped, direct_offset_swapped, max_value);
463 const QuantizedEndpointPair bc_offset_swapped_quantized(
464 inv_bc_base_swapped, inv_bc_offset_swapped, max_value);
465
466 // 3. Unquantize each set and decide which one gives least error.
467 std::array<CEEncodingOption, 6> errors;
468 auto errors_itr = errors.begin();
469
470 // 3.1 regular unquantized error
471 {
472 const auto rgba_low = direct_quantized.UnquantizedLow();
473 const auto rgba_high = direct_quantized.UnquantizedHigh();
474
475 const int sq_rgb_error =
476 SquaredError(rgba_low, endpoint_low_rgba, num_channels) +
477 SquaredError(rgba_high, endpoint_high_rgba, num_channels);
478
479 const bool swap_endpoints = false;
480 const bool blue_contract = false;
481 const bool offset_mode = false;
482 *(errors_itr++) = CEEncodingOption(
483 sq_rgb_error, &direct_quantized,
484 swap_endpoints, blue_contract, offset_mode);
485 }
486
487 // 3.2 Compute blue-contract'd error.
488 {
489 auto bc_low = bc_quantized.UnquantizedLow();
490 auto bc_high = bc_quantized.UnquantizedHigh();
491 BlueContract(&bc_low);
492 BlueContract(&bc_high);
493
494 const int sq_bc_error =
495 SquaredError(bc_low, endpoint_low_rgba, num_channels) +
496 SquaredError(bc_high, endpoint_high_rgba, num_channels);
497
498 const bool swap_endpoints = false;
499 const bool blue_contract = true;
500 const bool offset_mode = false;
501 *(errors_itr++) = CEEncodingOption(
502 sq_bc_error, &bc_quantized,
503 swap_endpoints, blue_contract, offset_mode);
504 }
505
506 // 3.3 Compute base/offset unquantized error.
507 const auto compute_base_offset_error =
508 [num_channels, &errors_itr, &endpoint_low_rgba, &endpoint_high_rgba]
509 (const QuantizedEndpointPair& pair, bool swapped) {
510 auto base = pair.UnquantizedLow();
511 auto offset = pair.UnquantizedHigh();
512
513 for (size_t i = 0; i < num_channels; ++i) {
514 BitTransferSigned(&offset[i], &base[i]);
515 offset[i] = Clamp(base[i] + offset[i], 0, 255);
516 }
517
518 int base_offset_error = 0;
519 // If we swapped the endpoints going in, then without blue contract
520 // we should be comparing the base against the high endpoint.
521 if (swapped) {
522 base_offset_error =
523 SquaredError(base, endpoint_high_rgba, num_channels) +
524 SquaredError(offset, endpoint_low_rgba, num_channels);
525 } else {
526 base_offset_error =
527 SquaredError(base, endpoint_low_rgba, num_channels) +
528 SquaredError(offset, endpoint_high_rgba, num_channels);
529 }
530
531 const bool blue_contract = false;
532 const bool offset_mode = true;
533 *(errors_itr++) = CEEncodingOption(
534 base_offset_error, &pair, swapped, blue_contract, offset_mode);
535 };
536
537 compute_base_offset_error(offset_quantized, false);
538
539 // 3.4 Compute base/offset blue-contract error.
540 const auto compute_base_offset_blue_contract_error =
541 [num_channels, &errors_itr, &endpoint_low_rgba, &endpoint_high_rgba]
542 (const QuantizedEndpointPair& pair, bool swapped) {
543 auto base = pair.UnquantizedLow();
544 auto offset = pair.UnquantizedHigh();
545
546 for (size_t i = 0; i < num_channels; ++i) {
547 BitTransferSigned(&offset[i], &base[i]);
548 offset[i] = Clamp(base[i] + offset[i], 0, 255);
549 }
550
551 BlueContract(&base);
552 BlueContract(&offset);
553
554 int sq_bc_error = 0;
555 // Remember, for blue-contract'd offset modes, the base is compared
556 // against the second endpoint and not the first. So, we compare
557 // against the first if we swapped the endpoints going in.
558 if (swapped) {
559 sq_bc_error =
560 SquaredError(base, endpoint_low_rgba, num_channels) +
561 SquaredError(offset, endpoint_high_rgba, num_channels);
562 } else {
563 sq_bc_error =
564 SquaredError(base, endpoint_high_rgba, num_channels) +
565 SquaredError(offset, endpoint_low_rgba, num_channels);
566 }
567
568 const bool blue_contract = true;
569 const bool offset_mode = true;
570 *(errors_itr++) = CEEncodingOption(sq_bc_error, &pair,
571 swapped, blue_contract, offset_mode);
572 };
573
574 compute_base_offset_blue_contract_error(bc_offset_quantized, false);
575
576 // 3.5 Compute swapped base/offset error.
577 compute_base_offset_error(offset_swapped_quantized, true);
578
579 // 3.6 Compute swapped base/offset blue-contract error.
580 compute_base_offset_blue_contract_error(
581 bc_offset_swapped_quantized, true);
582
583 std::sort(errors.begin(), errors.end(),
584 [](const CEEncodingOption& a, const CEEncodingOption& b) {
585 return a.Error() < b.Error();
586 });
587
588 // 4. Encode the values correspondingly.
589 // For this part, we go through each measurement in order of increasing
590 // error. Based on the properties of each measurement, we decide how to
591 // best encode the quantized endpoints that produced that error value. If
592 // for some reason we cannot encode that metric, then we skip it and move
593 // to the next one.
594 for (const auto& measurement : errors) {
595 bool needs_weight_swap = false;
596 if (measurement.Pack(with_alpha, astc_mode, vals, &needs_weight_swap)) {
597 // Make sure that if we ask for a blue-contract mode that we get it *and*
598 // if we don't ask for it then we don't get it.
599 assert(!(measurement.BlueContract() ^
600 UsesBlueContract(max_value, *astc_mode, *vals)));
601
602 // We encoded what we got.
603 return needs_weight_swap;
604 }
605 }
606
607 assert(false && "Shouldn't have reached this point -- some combination of "
608 "endpoints should be possible to encode!");
609 return false;
610 }
611
612 } // namespace
613
614 ////////////////////////////////////////////////////////////////////////////////
615
UsesBlueContract(int max_value,ColorEndpointMode mode,const std::vector<int> & vals)616 bool UsesBlueContract(int max_value, ColorEndpointMode mode,
617 const std::vector<int>& vals) {
618 assert(vals.size() >= size_t(NumColorValuesForEndpointMode(mode)));
619
620 switch (mode) {
621 case ColorEndpointMode::kLDRRGBDirect:
622 case ColorEndpointMode::kLDRRGBADirect: {
623 constexpr int kNumVals = MaxValuesForModes(
624 ColorEndpointMode::kLDRRGBDirect, ColorEndpointMode::kLDRRGBADirect);
625 std::array<int, kNumVals> v {};
626 std::copy(vals.begin(), vals.end(), v.begin());
627 Unquantize(&v, max_value);
628
629 const int s0 = v[0] + v[2] + v[4];
630 const int s1 = v[1] + v[3] + v[5];
631
632 return s0 > s1;
633 }
634
635 case ColorEndpointMode::kLDRRGBBaseOffset:
636 case ColorEndpointMode::kLDRRGBABaseOffset: {
637 constexpr int kNumVals = MaxValuesForModes(
638 ColorEndpointMode::kLDRRGBBaseOffset,
639 ColorEndpointMode::kLDRRGBABaseOffset);
640 std::array<int, kNumVals> v {};
641 std::copy(vals.begin(), vals.end(), v.begin());
642 Unquantize(&v, max_value);
643
644 BitTransferSigned(&v[1], &v[0]);
645 BitTransferSigned(&v[3], &v[2]);
646 BitTransferSigned(&v[5], &v[4]);
647
648 return v[1] + v[3] + v[5] < 0;
649 }
650
651 default:
652 return false;
653 }
654 }
655
EncodeColorsForMode(const RgbaColor & endpoint_low_rgba,const RgbaColor & endpoint_high_rgba,int max_value,EndpointEncodingMode encoding_mode,ColorEndpointMode * const astc_mode,std::vector<int> * const vals)656 bool EncodeColorsForMode(
657 const RgbaColor& endpoint_low_rgba, const RgbaColor& endpoint_high_rgba,
658 int max_value, EndpointEncodingMode encoding_mode,
659 ColorEndpointMode* const astc_mode, std::vector<int>* const vals) {
660 bool needs_weight_swap = false;
661 vals->resize(NumValuesForEncodingMode(encoding_mode));
662
663 switch (encoding_mode) {
664 case EndpointEncodingMode::kDirectLuma:
665 return EncodeColorsLuma(
666 endpoint_low_rgba, endpoint_high_rgba, max_value, astc_mode, vals);
667
668 case EndpointEncodingMode::kDirectLumaAlpha: {
669 // TODO(google): See if luma-alpha base-offset is better
670 const int avg1 = AverageRGB(endpoint_low_rgba);
671 const int avg2 = AverageRGB(endpoint_high_rgba);
672
673 (*vals)[0] = QuantizeCEValueToRange(avg1, max_value);
674 (*vals)[1] = QuantizeCEValueToRange(avg2, max_value);
675 (*vals)[2] = QuantizeCEValueToRange(endpoint_low_rgba[3], max_value);
676 (*vals)[3] = QuantizeCEValueToRange(endpoint_high_rgba[3], max_value);
677 *astc_mode = ColorEndpointMode::kLDRLumaAlphaDirect;
678 }
679 break;
680
681 case EndpointEncodingMode::kBaseScaleRGB:
682 case EndpointEncodingMode::kBaseScaleRGBA: {
683 RgbaColor base = endpoint_high_rgba;
684 RgbaColor scaled = endpoint_low_rgba;
685
686 // Similar to luma base-offset, the scaled value is strictly less than
687 // the base value here according to the decode procedure. In this case,
688 // if the base is larger than the scale then we need to swap.
689 int num_channels_ge = 0;
690 for (int i = 0; i < 3; ++i) {
691 num_channels_ge +=
692 static_cast<int>(endpoint_high_rgba[i] >= endpoint_low_rgba[i]);
693 }
694
695 if (num_channels_ge < 2) {
696 needs_weight_swap = true;
697 std::swap(base, scaled);
698 }
699
700 // Since the second endpoint is just a direct copy of the RGB values, we
701 // can start by quantizing them.
702 const auto q_base = QuantizeColor(base, max_value);
703 const auto uq_base = UnquantizeColor(q_base, max_value);
704
705 // The first endpoint (scaled) is defined by piecewise multiplying the
706 // second endpoint (base) by the scale factor and then dividing by 256.
707 // This means that the inverse operation is to first piecewise multiply
708 // the first endpoint by 256 and then divide by the unquantized second
709 // endpoint. We take the average of each of each of these scale values as
710 // our final scale value.
711 // TODO(google): Is this the best way to determine the scale factor?
712 int num_samples = 0;
713 int scale_sum = 0;
714 for (int i = 0; i < 3; ++i) {
715 int x = uq_base[i];
716 if (x != 0) {
717 ++num_samples;
718 scale_sum += (scaled[i] * 256) / x;
719 }
720 }
721
722 (*vals)[0] = q_base[0];
723 (*vals)[1] = q_base[1];
724 (*vals)[2] = q_base[2];
725 if (num_samples > 0) {
726 const int avg_scale = Clamp(scale_sum / num_samples, 0, 255);
727 (*vals)[3] = QuantizeCEValueToRange(avg_scale, max_value);
728 } else {
729 // In this case, all of the base values are zero, so we can use whatever
730 // we want as the scale -- it won't affect the outcome.
731 (*vals)[3] = max_value;
732 }
733 *astc_mode = ColorEndpointMode::kLDRRGBBaseScale;
734
735 if (encoding_mode == EndpointEncodingMode::kBaseScaleRGBA) {
736 (*vals)[4] = QuantizeCEValueToRange(scaled[3], max_value);
737 (*vals)[5] = QuantizeCEValueToRange(base[3], max_value);
738 *astc_mode = ColorEndpointMode::kLDRRGBBaseScaleTwoA;
739 }
740 }
741 break;
742
743 case EndpointEncodingMode::kDirectRGB:
744 case EndpointEncodingMode::kDirectRGBA:
745 return EncodeColorsRGBA(
746 endpoint_low_rgba, endpoint_high_rgba, max_value,
747 encoding_mode == EndpointEncodingMode::kDirectRGBA, astc_mode, vals);
748
749 default:
750 assert(false && "Unimplemented color encoding.");
751 }
752
753 return needs_weight_swap;
754 }
755
756 // These decoding procedures follow the code outlined in Section C.2.14 of
757 // the ASTC specification.
DecodeColorsForMode(const std::vector<int> & vals,int max_value,ColorEndpointMode mode,RgbaColor * const endpoint_low_rgba,RgbaColor * const endpoint_high_rgba)758 void DecodeColorsForMode(const std::vector<int>& vals,
759 int max_value, ColorEndpointMode mode,
760 RgbaColor* const endpoint_low_rgba,
761 RgbaColor* const endpoint_high_rgba) {
762 assert(vals.size() >= size_t(NumColorValuesForEndpointMode(mode)));
763 switch (mode) {
764 case ColorEndpointMode::kLDRLumaDirect: {
765 const int l0 = UnquantizeCEValueFromRange(vals[0], max_value);
766 const int l1 = UnquantizeCEValueFromRange(vals[1], max_value);
767
768 *endpoint_low_rgba = {{ l0, l0, l0, 255 }};
769 *endpoint_high_rgba = {{ l1, l1, l1, 255 }};
770 }
771 break;
772
773 case ColorEndpointMode::kLDRLumaBaseOffset: {
774 const int v0 = UnquantizeCEValueFromRange(vals[0], max_value);
775 const int v1 = UnquantizeCEValueFromRange(vals[1], max_value);
776
777 const int l0 = (v0 >> 2) | (v1 & 0xC0);
778 const int l1 = std::min(l0 + (v1 & 0x3F), 0xFF);
779
780 *endpoint_low_rgba = {{ l0, l0, l0, 255 }};
781 *endpoint_high_rgba = {{ l1, l1, l1, 255 }};
782 }
783 break;
784
785 case ColorEndpointMode::kLDRLumaAlphaDirect: {
786 constexpr int kNumVals =
787 NumColorValuesForEndpointMode(ColorEndpointMode::kLDRLumaAlphaDirect);
788
789 std::array<int, kNumVals> v;
790 std::copy(vals.begin(), vals.end(), v.begin());
791 Unquantize(&v, max_value);
792
793 *endpoint_low_rgba = {{ v[0], v[0], v[0], v[2] }};
794 *endpoint_high_rgba = {{ v[1], v[1], v[1], v[3] }};
795 }
796 break;
797
798 case ColorEndpointMode::kLDRLumaAlphaBaseOffset: {
799 constexpr int kNumVals = NumColorValuesForEndpointMode(
800 ColorEndpointMode::kLDRLumaAlphaBaseOffset);
801
802 std::array<int, kNumVals> v;
803 std::copy(vals.begin(), vals.end(), v.begin());
804 Unquantize(&v, max_value);
805
806 BitTransferSigned(&v[1], &v[0]);
807 BitTransferSigned(&v[3], &v[2]);
808
809 *endpoint_low_rgba = {{ v[0], v[0], v[0], v[2] }};
810 const int high_luma = v[0] + v[1];
811 *endpoint_high_rgba = {{ high_luma, high_luma, high_luma, v[2] + v[3] }};
812
813 for (auto& c : *endpoint_low_rgba) { c = Clamp(c, 0, 255); }
814 for (auto& c : *endpoint_high_rgba) { c = Clamp(c, 0, 255); }
815 }
816 break;
817
818 case ColorEndpointMode::kLDRRGBBaseScale: {
819 constexpr int kNumVals =
820 NumColorValuesForEndpointMode(ColorEndpointMode::kLDRRGBBaseScale);
821
822 std::array<int, kNumVals> v;
823 std::copy(vals.begin(), vals.end(), v.begin());
824 Unquantize(&v, max_value);
825
826 *endpoint_high_rgba = {{ v[0], v[1], v[2], 255 }};
827 for (int i = 0; i < 3; ++i) {
828 const int x = endpoint_high_rgba->at(i);
829 endpoint_low_rgba->at(i) = (x * v[3]) >> 8;
830 }
831 endpoint_low_rgba->at(3) = 255;
832 }
833 break;
834
835 case ColorEndpointMode::kLDRRGBDirect: {
836 constexpr int kNumVals =
837 NumColorValuesForEndpointMode(ColorEndpointMode::kLDRRGBDirect);
838
839 std::array<int, kNumVals> v;
840 std::copy(vals.begin(), vals.end(), v.begin());
841 Unquantize(&v, max_value);
842
843 const int s0 = v[0] + v[2] + v[4];
844 const int s1 = v[1] + v[3] + v[5];
845
846 *endpoint_low_rgba = {{ v[0], v[2], v[4], 255 }};
847 *endpoint_high_rgba = {{ v[1], v[3], v[5], 255 }};
848
849 if (s1 < s0) {
850 std::swap(*endpoint_low_rgba, *endpoint_high_rgba);
851 BlueContract(endpoint_low_rgba);
852 BlueContract(endpoint_high_rgba);
853 }
854 }
855 break;
856
857 case ColorEndpointMode::kLDRRGBBaseOffset: {
858 constexpr int kNumVals =
859 NumColorValuesForEndpointMode(ColorEndpointMode::kLDRRGBBaseOffset);
860
861 std::array<int, kNumVals> v;
862 std::copy(vals.begin(), vals.end(), v.begin());
863 Unquantize(&v, max_value);
864
865 BitTransferSigned(&v[1], &v[0]);
866 BitTransferSigned(&v[3], &v[2]);
867 BitTransferSigned(&v[5], &v[4]);
868
869 *endpoint_low_rgba = {{ v[0], v[2], v[4], 255 }};
870 *endpoint_high_rgba = {{ v[0] + v[1], v[2] + v[3], v[4] + v[5], 255 }};
871
872 if (v[1] + v[3] + v[5] < 0) {
873 std::swap(*endpoint_low_rgba, *endpoint_high_rgba);
874 BlueContract(endpoint_low_rgba);
875 BlueContract(endpoint_high_rgba);
876 }
877
878 for (auto& c : *endpoint_low_rgba) { c = Clamp(c, 0, 255); }
879 for (auto& c : *endpoint_high_rgba) { c = Clamp(c, 0, 255); }
880 }
881 break;
882
883 case ColorEndpointMode::kLDRRGBBaseScaleTwoA: {
884 constexpr int kNumVals = NumColorValuesForEndpointMode(
885 ColorEndpointMode::kLDRRGBBaseScaleTwoA);
886
887 std::array<int, kNumVals> v;
888 std::copy(vals.begin(), vals.end(), v.begin());
889 Unquantize(&v, max_value);
890
891 // Base
892 *endpoint_low_rgba = *endpoint_high_rgba = {{ v[0], v[1], v[2], 255 }};
893
894 // Scale
895 for (int i = 0; i < 3; ++i) {
896 auto& x = endpoint_low_rgba->at(i);
897 x = (x * v[3]) >> 8;
898 }
899
900 // Two A
901 endpoint_low_rgba->at(3) = v[4];
902 endpoint_high_rgba->at(3) = v[5];
903 }
904 break;
905
906 case ColorEndpointMode::kLDRRGBADirect: {
907 constexpr int kNumVals =
908 NumColorValuesForEndpointMode(ColorEndpointMode::kLDRRGBADirect);
909
910 std::array<int, kNumVals> v;
911 std::copy(vals.begin(), vals.end(), v.begin());
912 Unquantize(&v, max_value);
913
914 const int s0 = v[0] + v[2] + v[4];
915 const int s1 = v[1] + v[3] + v[5];
916
917 *endpoint_low_rgba = {{ v[0], v[2], v[4], v[6] }};
918 *endpoint_high_rgba = {{ v[1], v[3], v[5], v[7] }};
919
920 if (s1 < s0) {
921 std::swap(*endpoint_low_rgba, *endpoint_high_rgba);
922 BlueContract(endpoint_low_rgba);
923 BlueContract(endpoint_high_rgba);
924 }
925 }
926 break;
927
928 case ColorEndpointMode::kLDRRGBABaseOffset: {
929 constexpr int kNumVals =
930 NumColorValuesForEndpointMode(ColorEndpointMode::kLDRRGBABaseOffset);
931
932 std::array<int, kNumVals> v;
933 std::copy(vals.begin(), vals.end(), v.begin());
934 Unquantize(&v, max_value);
935
936 BitTransferSigned(&v[1], &v[0]);
937 BitTransferSigned(&v[3], &v[2]);
938 BitTransferSigned(&v[5], &v[4]);
939 BitTransferSigned(&v[7], &v[6]);
940
941 *endpoint_low_rgba = {{ v[0], v[2], v[4], v[6] }};
942 *endpoint_high_rgba = {{
943 v[0] + v[1], v[2] + v[3], v[4] + v[5], v[6] + v[7] }};
944
945 if (v[1] + v[3] + v[5] < 0) {
946 std::swap(*endpoint_low_rgba, *endpoint_high_rgba);
947 BlueContract(endpoint_low_rgba);
948 BlueContract(endpoint_high_rgba);
949 }
950
951 for (auto& c : *endpoint_low_rgba) { c = Clamp(c, 0, 255); }
952 for (auto& c : *endpoint_high_rgba) { c = Clamp(c, 0, 255); }
953 }
954 break;
955
956 default:
957 // Unimplemented color encoding.
958 // TODO(google): Is this the correct error handling?
959 *endpoint_high_rgba = *endpoint_low_rgba = {{ 0, 0, 0, 0 }};
960 }
961 }
962
963 } // namespace astc_codec
964