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