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