• 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/intermediate_astc_block.h"
16 #include "src/decoder/integer_sequence_codec.h"
17 #include "src/base/bit_stream.h"
18 #include "src/base/math_utils.h"
19 #include "src/base/optional.h"
20 #include "src/base/uint128.h"
21 
22 #include <algorithm>
23 #include <numeric>
24 #include <sstream>
25 
26 namespace astc_codec {
27 
28 namespace {
29 
30 constexpr int kEndpointRange_ReturnInvalidWeightDims = -1;
31 constexpr int kEndpointRange_ReturnNotEnoughColorBits = -2;
32 
PackVoidExtentBlock(uint16_t r,uint16_t g,uint16_t b,uint16_t a,std::array<uint16_t,4> coords)33 base::UInt128 PackVoidExtentBlock(uint16_t r, uint16_t g, uint16_t b,
34                                   uint16_t a, std::array<uint16_t, 4> coords) {
35   base::BitStream<base::UInt128> bit_sink;
36 
37   // Put void extent mode...
38   bit_sink.PutBits(0xDFC, 12);
39 
40   // Each of the coordinates goes in 13 bits at a time.
41   for (auto coord : coords) {
42     assert(coord < 1 << 13);
43     bit_sink.PutBits(coord, 13);
44   }
45   assert(bit_sink.Bits() == 64);
46 
47   // Then we add R, G, B, and A in order
48   bit_sink.PutBits(r, 16);
49   bit_sink.PutBits(g, 16);
50   bit_sink.PutBits(b, 16);
51   bit_sink.PutBits(a, 16);
52 
53   assert(bit_sink.Bits() == 128);
54 
55   base::UInt128 result;
56   bit_sink.GetBits(128, &result);
57   return result;
58 }
59 
GetEncodedWeightRange(int range,std::array<int,3> * const r)60 base::Optional<std::string> GetEncodedWeightRange(int range,
61                                                   std::array<int, 3>* const r) {
62   const std::array<std::array<int, 3>, 12> kValidRangeEncodings =
63       {{ {{ 0, 1, 0 }}, {{ 1, 1, 0 }}, {{ 0, 0, 1 }},
64          {{ 1, 0, 1 }}, {{ 0, 1, 1 }}, {{ 1, 1, 1 }},
65          {{ 0, 1, 0 }}, {{ 1, 1, 0 }}, {{ 0, 0, 1 }},
66          {{ 1, 0, 1 }}, {{ 0, 1, 1 }}, {{ 1, 1, 1 }} }};
67 
68   // If our range is larger than all available ranges, this is an error.
69   const int smallest_range = kValidWeightRanges.front();
70   const int largest_range = kValidWeightRanges.back();
71   if (range < smallest_range || largest_range < range) {
72     std::stringstream strm;
73     strm << "Could not find block mode. Invalid weight range: "
74          << range << " not in [" << smallest_range << ", "
75          << largest_range << std::endl;
76     return strm.str();
77   }
78 
79   // Find the upper bound on the range, otherwise.
80   const auto range_iter = std::lower_bound(
81       kValidWeightRanges.cbegin(), kValidWeightRanges.cend(), range);
82   auto enc_iter = kValidRangeEncodings.cbegin();
83   enc_iter += std::distance(kValidWeightRanges.cbegin(), range_iter);
84   *r = *enc_iter;
85   return {};
86 }
87 
88 struct BlockModeInfo {
89   int min_weight_grid_dim_x;
90   int max_weight_grid_dim_x;
91   int min_weight_grid_dim_y;
92   int max_weight_grid_dim_y;
93   int r0_bit_pos;
94   int r1_bit_pos;
95   int r2_bit_pos;
96   int weight_grid_x_offset_bit_pos;
97   int weight_grid_y_offset_bit_pos;
98   bool require_single_plane_low_prec;
99 };
100 
101 constexpr int kNumBlockModes = 10;
102 const std::array<BlockModeInfo, kNumBlockModes> kBlockModeInfo {{
103   { 4, 7, 2, 5, 4, 0, 1, 7, 5, false },      // B+4 A+2
104   { 8, 11, 2, 5, 4, 0, 1, 7, 5, false },     // B+8 A+2
105   { 2, 5, 8, 11, 4, 0, 1, 5, 7, false },     // A+2 B+8
106   { 2, 5, 6, 7, 4, 0, 1, 5, 7, false },      // A+2 B+6
107   { 2, 3, 2, 5, 4, 0, 1, 7, 5, false },      // B+2 A+2
108   { 12, 12, 2, 5, 4, 2, 3, -1, 5, false },   // 12  A+2
109   { 2, 5, 12, 12, 4, 2, 3, 5, -1, false },   // A+2 12
110   { 6, 6, 10, 10, 4, 2, 3, -1, -1, false },  // 6   10
111   { 10, 10, 6, 6, 4, 2, 3, -1, -1, false },  // 10  6
112   { 6, 9, 6, 9, 4, 2, 3, 5, 9, true }        // A+6 B+6
113 }};
114 
115 // These are the bits that must be set for ASTC to recognize a given
116 // block mode. They are the 1's set in table C.2.8 of the spec.
117 const std::array<int, kNumBlockModes> kBlockModeMask = {{
118   0x0, 0x4, 0x8, 0xC, 0x10C, 0x0, 0x80, 0x180, 0x1A0, 0x100
119 }};
120 
PackBlockMode(int dim_x,int dim_y,int range,bool dual_plane,base::BitStream<base::UInt128> * const bit_sink)121 static base::Optional<std::string> PackBlockMode(int dim_x, int dim_y, int range,
122                                           bool dual_plane,
123                                           base::BitStream<base::UInt128>* const bit_sink) {
124   // We need to set the high precision bit if our range is too high...
125   bool high_prec = range > 7;
126 
127   std::array<int, 3> r;
128   const auto result = GetEncodedWeightRange(range, &r);
129   if (result) {
130     return result;
131   }
132 
133   // The high two bits of R must not be zero. If this happens then it's
134   // an illegal encoding according to Table C.2.7 that should have gotten
135   // caught in GetEncodedWeightRange
136   assert((r[1] | r[2]) > 0);
137 
138   // Just go through the table and see if any of the modes can handle
139   // the given dimensions.
140   for (int mode = 0; mode < kNumBlockModes; ++mode) {
141     const BlockModeInfo& block_mode = kBlockModeInfo[mode];
142 
143     bool is_valid_mode = true;
144     is_valid_mode &= block_mode.min_weight_grid_dim_x <= dim_x;
145     is_valid_mode &= dim_x <= block_mode.max_weight_grid_dim_x;
146     is_valid_mode &= block_mode.min_weight_grid_dim_y <= dim_y;
147     is_valid_mode &= dim_y <= block_mode.max_weight_grid_dim_y;
148     is_valid_mode &= !(block_mode.require_single_plane_low_prec && dual_plane);
149     is_valid_mode &= !(block_mode.require_single_plane_low_prec && high_prec);
150 
151     if (!is_valid_mode) {
152       continue;
153     }
154 
155     // Initialize to the bits we must set.
156     uint32_t encoded_mode = kBlockModeMask[mode];
157     auto setBit = [&encoded_mode](const uint32_t value, const uint32_t offset) {
158       encoded_mode = (encoded_mode & ~(1 << offset)) | ((value & 1) << offset);
159     };
160 
161     // Set all the bits we need to set
162     setBit(r[0], block_mode.r0_bit_pos);
163     setBit(r[1], block_mode.r1_bit_pos);
164     setBit(r[2], block_mode.r2_bit_pos);
165 
166     // Find our width and height offset from the base width and height weight
167     // grid dimension for the given block mode. These are the 1-2 bits that
168     // get encoded in the block mode used to calculate the final weight grid
169     // width and height.
170     const int offset_x = dim_x - block_mode.min_weight_grid_dim_x;
171     const int offset_y = dim_y - block_mode.min_weight_grid_dim_y;
172 
173     // If we don't have an offset position then our offset better be zero.
174     // If this isn't the case, then this isn't a viable block mode and we
175     // should have caught this sooner.
176     assert(block_mode.weight_grid_x_offset_bit_pos >= 0 || offset_x == 0);
177     assert(block_mode.weight_grid_y_offset_bit_pos >= 0 || offset_y == 0);
178 
179     encoded_mode |= offset_x << block_mode.weight_grid_x_offset_bit_pos;
180     encoded_mode |= offset_y << block_mode.weight_grid_y_offset_bit_pos;
181 
182     if (!block_mode.require_single_plane_low_prec) {
183       setBit(high_prec, 9);
184       setBit(dual_plane, 10);
185     }
186 
187     // Make sure that the mode is the first thing the bit sink is writing to
188     assert(bit_sink->Bits() == 0);
189     bit_sink->PutBits(encoded_mode, 11);
190 
191     return {};
192   }
193 
194   return std::string("Could not find viable block mode");
195 }
196 
197 // Returns true if all endpoint modes are equal.
SharedEndpointModes(const IntermediateBlockData & data)198 bool SharedEndpointModes(const IntermediateBlockData& data) {
199   return std::accumulate(
200       data.endpoints.begin(), data.endpoints.end(), true,
201       [&data](const bool& a, const IntermediateEndpointData& b) {
202         return a && b.mode == data.endpoints[0].mode;
203       });
204 }
205 
206 // Returns the starting bit (between 0 and 128) where the extra CEM and
207 // dual plane info is stored in the ASTC block.
ExtraConfigBitPosition(const IntermediateBlockData & data)208 int ExtraConfigBitPosition(const IntermediateBlockData& data) {
209   const bool has_dual_channel = data.dual_plane_channel.hasValue();
210   const int num_weights = data.weight_grid_dim_x * data.weight_grid_dim_y *
211       (has_dual_channel ? 2 : 1);
212   const int num_weight_bits =
213       IntegerSequenceCodec::GetBitCountForRange(num_weights, data.weight_range);
214 
215   int extra_config_bits = 0;
216   if (!SharedEndpointModes(data)) {
217     const int num_encoded_cem_bits = 2 + data.endpoints.size() * 3;
218     extra_config_bits = num_encoded_cem_bits - 6;
219   }
220 
221   if (has_dual_channel) {
222     extra_config_bits += 2;
223   }
224 
225   return 128 - num_weight_bits - extra_config_bits;
226 }
227 
228 }  // namespace
229 
230 ////////////////////////////////////////////////////////////////////////////////
231 
UnpackIntermediateBlock(const PhysicalASTCBlock & pb)232 base::Optional<IntermediateBlockData> UnpackIntermediateBlock(
233     const PhysicalASTCBlock& pb) {
234   if (pb.IsIllegalEncoding()) {
235     return {};
236   }
237 
238   if (pb.IsVoidExtent()) {
239     return {};
240   }
241 
242   // Non void extent? Then let's try to decode everything else.
243   IntermediateBlockData data;
244 
245   // All blocks have color values...
246   const base::UInt128 color_bits_mask =
247       (base::UInt128(1) << pb.NumColorBits().value()) - 1;
248   const base::UInt128 color_bits =
249       (pb.GetBlockBits() >> pb.ColorStartBit().value()) & color_bits_mask;
250   base::BitStream<base::UInt128> bit_src(color_bits, 128);
251 
252   IntegerSequenceDecoder color_decoder(pb.ColorValuesRange().value());
253   const int num_colors_in_block = pb.NumColorValues().value();
254   std::vector<int> colors = color_decoder.Decode(num_colors_in_block, &bit_src);
255 
256   // Decode simple info
257   const auto weight_dims = pb.WeightGridDims();
258   data.weight_grid_dim_x = weight_dims->at(0);
259   data.weight_grid_dim_y = weight_dims->at(1);
260   data.weight_range = pb.WeightRange().value();
261 
262   data.partition_id = pb.PartitionID();
263   data.dual_plane_channel = pb.DualPlaneChannel();
264 
265   auto colors_iter = colors.begin();
266   for (int i = 0; i < pb.NumPartitions().value(); ++i) {
267     IntermediateEndpointData ep_data;
268     ep_data.mode = pb.GetEndpointMode(i).value();
269 
270     const int num_colors = NumColorValuesForEndpointMode(ep_data.mode);
271     ep_data.colors.insert(ep_data.colors.end(), colors_iter,
272                           colors_iter + num_colors);
273     colors_iter += num_colors;
274 
275     data.endpoints.push_back(ep_data);
276   }
277   assert(colors_iter == colors.end());
278   data.endpoint_range = pb.ColorValuesRange().value();
279 
280   // Finally decode the weights
281   const base::UInt128 weight_bits_mask =
282       (base::UInt128(1) << pb.NumWeightBits().value()) - 1;
283   const base::UInt128 weight_bits =
284       base::ReverseBits(pb.GetBlockBits()) & weight_bits_mask;
285   bit_src = base::BitStream<base::UInt128>(weight_bits, 128);
286 
287   IntegerSequenceDecoder weight_decoder(data.weight_range);
288   int num_weights = data.weight_grid_dim_x * data.weight_grid_dim_y;
289   num_weights *= pb.IsDualPlane() ? 2 : 1;
290   data.weights = weight_decoder.Decode(num_weights, &bit_src);
291 
292   return data;
293 }
294 
EndpointRangeForBlock(const IntermediateBlockData & data)295 int EndpointRangeForBlock(const IntermediateBlockData& data) {
296   // First check to see if we exceed the number of bits allotted for weights, as
297   // specified in C.2.24. If so, then the endpoint range is meaningless, but not
298   // because we had an overzealous color endpoint mode, so return a different
299   // error code.
300   if (IntegerSequenceCodec::GetBitCountForRange(
301           data.weight_grid_dim_x * data.weight_grid_dim_y *
302           (data.dual_plane_channel.hasValue() ? 2 : 1),
303           data.weight_range) > 96) {
304     return kEndpointRange_ReturnInvalidWeightDims;
305   }
306 
307   const int num_partitions = data.endpoints.size();
308 
309   // Calculate the number of bits that we would write prior to getting to the
310   // color endpoint data
311   const int bits_written =
312       11   // Block mode
313       + 2  // Num partitions
314       + ((num_partitions > 1) ? 10 : 0)  // Partition ID
315       + ((num_partitions == 1) ? 4 : 6);  // Shared CEM bits
316 
317   // We can determine the range based on how many bits we have between the start
318   // of the color endpoint data and the next section, which is the extra config
319   // bit position
320   const int color_bits_available = ExtraConfigBitPosition(data) - bits_written;
321 
322   int num_color_values = 0;
323   for (const auto& ep_data : data.endpoints) {
324     num_color_values += NumColorValuesForEndpointMode(ep_data.mode);
325   }
326 
327   // There's no way any valid ASTC encoding has no room left for any color
328   // values. If we hit this then something is wrong in the caller -- abort.
329   // According to section C.2.24, the smallest number of bits available is
330   // ceil(13*C/5), where C is the number of color endpoint integers needed.
331   const int bits_needed = (13 * num_color_values + 4) / 5;
332   if (color_bits_available < bits_needed) {
333     return kEndpointRange_ReturnNotEnoughColorBits;
334   }
335 
336   int color_value_range = 255;
337   for (; color_value_range > 1; --color_value_range) {
338     const int bits_for_range = IntegerSequenceCodec::GetBitCountForRange(
339         num_color_values, color_value_range);
340     if (bits_for_range <= color_bits_available) {
341       break;
342     }
343   }
344 
345   return color_value_range;
346 }
347 
UnpackVoidExtent(const PhysicalASTCBlock & pb)348 base::Optional<VoidExtentData> UnpackVoidExtent(const PhysicalASTCBlock& pb) {
349   if (pb.IsIllegalEncoding()) {
350     return {};
351   }
352 
353   if (!pb.IsVoidExtent()) {
354     return {};
355   }
356 
357   // All blocks have color values...
358   const base::UInt128 color_bits_mask =
359       (base::UInt128(1) << pb.NumColorBits().value()) - 1;
360   const uint64_t color_bits = (
361       (pb.GetBlockBits() >> pb.ColorStartBit().value()) & color_bits_mask).LowBits();
362 
363   assert(pb.NumColorValues().value() == 4);
364   VoidExtentData data;
365   data.r = static_cast<uint16_t>((color_bits >>  0) & 0xFFFF);
366   data.g = static_cast<uint16_t>((color_bits >> 16) & 0xFFFF);
367   data.b = static_cast<uint16_t>((color_bits >> 32) & 0xFFFF);
368   data.a = static_cast<uint16_t>((color_bits >> 48) & 0xFFFF);
369 
370   const auto void_extent_coords = pb.VoidExtentCoords();
371   if (void_extent_coords) {
372     data.coords[0] = void_extent_coords->at(0);
373     data.coords[1] = void_extent_coords->at(1);
374     data.coords[2] = void_extent_coords->at(2);
375     data.coords[3] = void_extent_coords->at(3);
376   } else {
377     uint16_t all_ones = (1 << 13) - 1;
378     for (auto& coord : data.coords) {
379       coord = all_ones;
380     }
381   }
382 
383   return data;
384 }
385 
386 // Packs the given intermediate block into a physical block. Returns false if
387 // the provided values in the intermediate block emit an illegal ASTC
388 // encoding.
Pack(const IntermediateBlockData & data,base::UInt128 * pb)389 base::Optional<std::string> Pack(const IntermediateBlockData& data,
390                                  base::UInt128* pb) {
391   if (data.weights.size() !=
392       data.weight_grid_dim_x * data.weight_grid_dim_y *
393       (data.dual_plane_channel.hasValue() ? 2 : 1)) {
394     return std::string("Incorrect number of weights!");
395   }
396 
397   // If it's not a void extent block, then it gets a bit more tricky...
398   base::BitStream<base::UInt128> bit_sink;
399 
400   // First we need to encode the block mode.
401   const auto error_string = PackBlockMode(
402       data.weight_grid_dim_x, data.weight_grid_dim_y, data.weight_range,
403       data.dual_plane_channel.hasValue(), &bit_sink);
404   if (error_string) {
405     return error_string;
406   }
407 
408   // Next, we place the number of partitions minus one.
409   const int num_partitions = data.endpoints.size();
410   bit_sink.PutBits(num_partitions - 1, 2);
411 
412   // If we have more than one partition, then we also have a partition ID.
413   if (num_partitions > 1) {
414     const int id = data.partition_id.value();
415     assert(id >= 0);
416     bit_sink.PutBits(id, 10);
417   }
418 
419   // Take a detour, let's encode the weights so that we know how many bits they
420   // consume.
421   base::BitStream<base::UInt128> weight_sink;
422 
423   IntegerSequenceEncoder weight_enc(data.weight_range);
424   for (auto weight : data.weights) {
425     weight_enc.AddValue(weight);
426   }
427   weight_enc.Encode(&weight_sink);
428 
429   const int num_weight_bits = weight_sink.Bits();
430   assert(num_weight_bits ==
431            IntegerSequenceCodec::GetBitCountForRange(
432                data.weights.size(), data.weight_range));
433 
434   // Let's continue... how much after the color data do we need to write?
435   int extra_config = 0;
436 
437   // Determine if all endpoint pairs share the same endpoint mode
438   assert(data.endpoints.size() > 0);
439   bool shared_endpoint_mode = SharedEndpointModes(data);
440 
441   // The first part of the endpoint mode (CEM) comes directly after the
442   // partition info, if it exists. If there is no partition info, the CEM comes
443   // right after the block mode. In the single-partition case, we just write out
444   // the entire singular CEM, but in the multi-partition case, if all CEMs are
445   // the same then their shared CEM is specified directly here, too. In both
446   // cases, shared_endpoint_mode is true (in the singular case,
447   // shared_endpoint_mode is trivially true).
448   if (shared_endpoint_mode) {
449     if (num_partitions > 1) {
450       bit_sink.PutBits(0, 2);
451     }
452     bit_sink.PutBits(static_cast<int>(data.endpoints[0].mode), 4);
453   } else {
454     // Here, the CEM is not shared across all endpoint pairs, and we need to
455     // figure out what to place here, and what to place in the extra config
456     // bits before the weight data...
457 
458     // Non-shared config modes must all be within the same class (out of four)
459     // See Section C.2.11
460     int min_class = 2;  // We start with 2 here instead of three because it's
461                         // the highest that can be encoded -- even if all modes
462                         // are class 3.
463     int max_class = 0;
464     for (const auto& ep_data : data.endpoints) {
465       const int ep_mode_class = static_cast<int>(ep_data.mode) >> 2;
466       min_class = std::min(min_class, ep_mode_class);
467       max_class = std::max(max_class, ep_mode_class);
468     }
469 
470     assert(max_class >= min_class);
471 
472     if (max_class - min_class > 1) {
473       return std::string("Endpoint modes are invalid");
474     }
475 
476     // Construct the CEM mode -- six of its bits will fit here, but otherwise
477     // the rest will go in the extra configuration bits.
478     base::BitStream<uint32_t> cem_encoder;
479 
480     // First encode the base class
481     assert(min_class >= 0);
482     assert(min_class < 3);
483     cem_encoder.PutBits(min_class + 1, 2);
484 
485     // Next, encode the class selector bits -- this is simply the offset
486     // from the base class
487     for (const auto& ep_data : data.endpoints) {
488       const int ep_mode_class = static_cast<int>(ep_data.mode) >> 2;
489       const int class_selector_bit = ep_mode_class - min_class;
490       assert(class_selector_bit == 0 || class_selector_bit == 1);
491       cem_encoder.PutBits(class_selector_bit, 1);
492     }
493 
494     // Finally, we need to choose from each class which actual mode
495     // we belong to and encode those.
496     for (const auto& ep_data : data.endpoints) {
497       const int ep_mode = static_cast<int>(ep_data.mode) & 3;
498       assert(ep_mode < 4);
499       cem_encoder.PutBits(ep_mode, 2);
500     }
501     assert(cem_encoder.Bits() == 2 + num_partitions * 3);
502 
503     uint32_t encoded_cem;
504     cem_encoder.GetBits(2 + num_partitions * 3, &encoded_cem);
505 
506     // Since only six bits fit here before the color endpoint data, the rest
507     // need to go in the extra config data.
508     extra_config = encoded_cem >> 6;
509 
510     // Write out the six bits we had
511     bit_sink.PutBits(encoded_cem, 6);
512   }
513 
514   // If we have a dual-plane channel, we can tack that onto our extra config
515   // data
516   if (data.dual_plane_channel.hasValue()) {
517     const int channel = data.dual_plane_channel.value();
518     assert(channel < 4);
519     extra_config <<= 2;
520     extra_config |= channel;
521   }
522 
523   // Get the range of endpoint values. It can't be -1 because we should have
524   // checked for that much earlier.
525   const int color_value_range = data.endpoint_range
526       ? data.endpoint_range.value()
527       : EndpointRangeForBlock(data);
528 
529   assert(color_value_range != kEndpointRange_ReturnInvalidWeightDims);
530   if (color_value_range == kEndpointRange_ReturnNotEnoughColorBits) {
531     return { "Intermediate block emits illegal color range" };
532   }
533 
534   IntegerSequenceEncoder color_enc(color_value_range);
535   for (const auto& ep_data : data.endpoints) {
536     for (int color : ep_data.colors) {
537       if (color > color_value_range) {
538         return { "Color outside available color range!" };
539       }
540 
541       color_enc.AddValue(color);
542     }
543   }
544   color_enc.Encode(&bit_sink);
545 
546   // Now we need to skip some bits to get to the extra configuration bits. The
547   // number of bits we need to skip depends on where we are in the stream and
548   // where we need to get to.
549   const int extra_config_bit_position = ExtraConfigBitPosition(data);
550   const int extra_config_bits =
551       128 - num_weight_bits - extra_config_bit_position;
552   assert(extra_config_bits >= 0);
553   assert(extra_config < 1 << extra_config_bits);
554 
555   // Make sure the color encoder didn't write more than we thought it would.
556   int bits_to_skip = extra_config_bit_position - bit_sink.Bits();
557   assert(bits_to_skip >= 0);
558 
559   while (bits_to_skip > 0) {
560     const int skipping = std::min(32, bits_to_skip);
561     bit_sink.PutBits(0, skipping);
562     bits_to_skip -= skipping;
563   }
564 
565   // Finally, write out the rest of the config bits.
566   bit_sink.PutBits(extra_config, extra_config_bits);
567 
568   // We should be right up to the weight bits...
569   assert(bit_sink.Bits() == 128 - num_weight_bits);
570 
571   // Flush out our bit writer and write out the weight bits
572   base::UInt128 astc_bits;
573   bit_sink.GetBits(128 - num_weight_bits, &astc_bits);
574 
575   base::UInt128 rev_weight_bits;
576   weight_sink.GetBits(weight_sink.Bits(), &rev_weight_bits);
577 
578   astc_bits |= base::ReverseBits(rev_weight_bits);
579 
580   // And we're done! Whew!
581   *pb = astc_bits;
582   return PhysicalASTCBlock(*pb).IsIllegalEncoding();
583 }
584 
Pack(const VoidExtentData & data,base::UInt128 * pb)585 base::Optional<std::string> Pack(const VoidExtentData& data,
586                                  base::UInt128* pb) {
587   *pb = PackVoidExtentBlock(data.r, data.g, data.b, data.a, data.coords);
588   return PhysicalASTCBlock(*pb).IsIllegalEncoding();
589 }
590 
591 }  // namespace astc_codec
592