aboutsummaryrefslogtreecommitdiff
path: root/src/decoder/logical_astc_block.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/decoder/logical_astc_block.cc')
-rw-r--r--src/decoder/logical_astc_block.cc262
1 files changed, 262 insertions, 0 deletions
diff --git a/src/decoder/logical_astc_block.cc b/src/decoder/logical_astc_block.cc
new file mode 100644
index 0000000..9271e18
--- /dev/null
+++ b/src/decoder/logical_astc_block.cc
@@ -0,0 +1,262 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/decoder/logical_astc_block.h"
+#include "src/decoder/endpoint_codec.h"
+#include "src/decoder/footprint.h"
+#include "src/decoder/integer_sequence_codec.h"
+#include "src/decoder/quantization.h"
+#include "src/decoder/weight_infill.h"
+
+namespace astc_codec {
+
+namespace {
+
+Partition GenerateSinglePartition(Footprint footprint) {
+ return Partition { footprint, /* num_parts = */ 1, /* partition_id = */ 0,
+ std::vector<int>(footprint.NumPixels(), 0) };
+}
+
+static std::vector<EndpointPair> DecodeEndpoints(const IntermediateBlockData& block) {
+ const int endpoint_range = block.endpoint_range
+ ? block.endpoint_range.value()
+ : EndpointRangeForBlock(block);
+ assert(endpoint_range > 0);
+
+ std::vector<EndpointPair> endpoints;
+ for (const auto& eps : block.endpoints) {
+ RgbaColor decmp_one_rgba, decmp_two_rgba;
+ DecodeColorsForMode(eps.colors, endpoint_range, eps.mode,
+ &decmp_one_rgba, &decmp_two_rgba);
+ endpoints.emplace_back(decmp_one_rgba, decmp_two_rgba);
+ }
+ return endpoints;
+}
+
+static std::vector<EndpointPair> DecodeEndpoints(const VoidExtentData& block) {
+ EndpointPair eps;
+ eps.first[0] = eps.second[0] = (block.r * 255) / 65535;
+ eps.first[1] = eps.second[1] = (block.g * 255) / 65535;
+ eps.first[2] = eps.second[2] = (block.b * 255) / 65535;
+ eps.first[3] = eps.second[3] = (block.a * 255) / 65535;
+
+ std::vector<EndpointPair> endpoints;
+ endpoints.emplace_back(eps);
+ return endpoints;
+}
+
+Partition ComputePartition(const Footprint& footprint,
+ const IntermediateBlockData& block) {
+ if (block.partition_id) {
+ const int part_id = block.partition_id.value();
+ const size_t num_parts = block.endpoints.size();
+ return GetASTCPartition(footprint, num_parts, part_id);
+ } else {
+ return GenerateSinglePartition(footprint);
+ }
+}
+
+Partition ComputePartition(const Footprint& footprint, const VoidExtentData&) {
+ return GenerateSinglePartition(footprint);
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+
+LogicalASTCBlock::LogicalASTCBlock(const Footprint& footprint)
+ : endpoints_(1),
+ weights_(footprint.NumPixels(), 0),
+ partition_(GenerateSinglePartition(footprint)) { }
+
+LogicalASTCBlock::LogicalASTCBlock(const Footprint& footprint,
+ const IntermediateBlockData& block)
+ : endpoints_(DecodeEndpoints(block)),
+ partition_(ComputePartition(footprint, block)) {
+ CalculateWeights(footprint, block);
+}
+
+LogicalASTCBlock::LogicalASTCBlock(const Footprint& footprint,
+ const VoidExtentData& block)
+ : endpoints_(DecodeEndpoints(block)),
+ partition_(ComputePartition(footprint, block)) {
+ CalculateWeights(footprint, block);
+}
+
+void LogicalASTCBlock::CalculateWeights(const Footprint& footprint,
+ const IntermediateBlockData& block) {
+ const int grid_size_x = block.weight_grid_dim_x;
+ const int grid_size_y = block.weight_grid_dim_y;
+ const int weight_grid_size = grid_size_x * grid_size_y;
+
+ // Either we have a dual plane and we have twice as many weights as
+ // specified or we don't
+ assert(block.dual_plane_channel
+ ? block.weights.size() == weight_grid_size * 2
+ : block.weights.size() == weight_grid_size);
+
+ std::vector<int> unquantized;
+ unquantized.reserve(weight_grid_size);
+
+ // According to C.2.16, if we have dual-plane weights, then we have two
+ // weights per texel -- one adjacent to the other. Hence, we have to skip
+ // some when we decode the separate weight values.
+ const int weight_frequency = (block.dual_plane_channel) ? 2 : 1;
+ const int weight_range = block.weight_range;
+
+ for (int i = 0; i < weight_grid_size; ++i) {
+ const int weight = block.weights[i * weight_frequency];
+ unquantized.push_back(UnquantizeWeightFromRange(weight, weight_range));
+ }
+ weights_ = InfillWeights(unquantized, footprint, grid_size_x, grid_size_y);
+
+ if (block.dual_plane_channel) {
+ SetDualPlaneChannel(block.dual_plane_channel.value());
+ for (int i = 0; i < weight_grid_size; ++i) {
+ const int weight = block.weights[i * weight_frequency + 1];
+ unquantized[i] = UnquantizeWeightFromRange(weight, weight_range);
+ }
+ dual_plane_->weights =
+ InfillWeights(unquantized, footprint, grid_size_x, grid_size_y);
+ }
+}
+
+void LogicalASTCBlock::CalculateWeights(const Footprint& footprint,
+ const VoidExtentData&) {
+ weights_ = std::vector<int>(footprint.NumPixels(), 0);
+}
+
+void LogicalASTCBlock::SetWeightAt(int x, int y, int weight) {
+ assert(weight >= 0);
+ assert(weight <= 64);
+ weights_.at(y * GetFootprint().Width() + x) = weight;
+}
+
+int LogicalASTCBlock::WeightAt(int x, int y) const {
+ return weights_.at(y * GetFootprint().Width() + x);
+}
+
+void LogicalASTCBlock::SetDualPlaneWeightAt(int channel, int x, int y,
+ int weight) {
+ assert(weight >= 0);
+ assert(weight <= 64);
+
+ // If it's not a dual plane, then this has no logical meaning
+ assert(IsDualPlane());
+
+ // If it is a dual plane and the passed channel matches the query, then we
+ // return the specialized weights
+ if (dual_plane_->channel == channel) {
+ dual_plane_->weights.at(y * GetFootprint().Width() + x) = weight;
+ } else {
+ // If the channel is not the special channel, then return the general weight
+ SetWeightAt(x, y, weight);
+ }
+}
+
+int LogicalASTCBlock::DualPlaneWeightAt(int channel, int x, int y) const {
+ // If it's not a dual plane, then we just return the weight for all channels
+ if (!IsDualPlane()) {
+ // TODO(google): Log warning, Requesting dual-plane channel for a non
+ // dual-plane block!
+ return WeightAt(x, y);
+ }
+
+ // If it is a dual plane and the passed channel matches the query, then we
+ // return the specialized weights
+ if (dual_plane_->channel == channel) {
+ return dual_plane_->weights.at(y * GetFootprint().Width() + x);
+ }
+
+ // If the channel is not the special channel, then return the general weight
+ return WeightAt(x, y);
+}
+
+void LogicalASTCBlock::SetDualPlaneChannel(int channel) {
+ if (channel < 0) {
+ dual_plane_.clear();
+ } else if (dual_plane_) {
+ dual_plane_->channel = channel;
+ } else {
+ dual_plane_ = DualPlaneData {channel, weights_};
+ }
+}
+
+RgbaColor LogicalASTCBlock::ColorAt(int x, int y) const {
+ const auto footprint = GetFootprint();
+ assert(x >= 0); assert(x < footprint.Width());
+ assert(y >= 0); assert(y < footprint.Height());
+
+ const int texel_idx = y * footprint.Width() + x;
+ const int part = partition_.assignment[texel_idx];
+ const auto& endpoints = endpoints_[part];
+
+ RgbaColor result;
+ for (int channel = 0; channel < 4; ++channel) {
+ const int weight = (dual_plane_ && dual_plane_->channel == channel)
+ ? dual_plane_->weights[texel_idx]
+ : weights_[texel_idx];
+ const int p0 = endpoints.first[channel];
+ const int p1 = endpoints.second[channel];
+
+ assert(p0 >= 0); assert(p0 < 256);
+ assert(p1 >= 0); assert(p1 < 256);
+
+ // According to C.2.19
+ const int c0 = (p0 << 8) | p0;
+ const int c1 = (p1 << 8) | p1;
+ const int c = (c0 * (64 - weight) + c1 * weight + 32) / 64;
+ // TODO(google): Handle conversion to sRGB or FP16 per C.2.19.
+ const int quantized = ((c * 255) + 32767) / 65536;
+ assert(quantized < 256);
+ result[channel] = quantized;
+ }
+
+ return result;
+}
+
+void LogicalASTCBlock::SetPartition(const Partition& p) {
+ assert(p.footprint == partition_.footprint &&
+ "New partitions may not be for a different footprint");
+ partition_ = p;
+ endpoints_.resize(p.num_parts);
+}
+
+void LogicalASTCBlock::SetEndpoints(const EndpointPair& eps, int subset) {
+ assert(subset < partition_.num_parts);
+ assert(subset < endpoints_.size());
+
+ endpoints_[subset] = eps;
+}
+
+base::Optional<LogicalASTCBlock> UnpackLogicalBlock(
+ const Footprint& footprint, const PhysicalASTCBlock& pb) {
+ if (pb.IsVoidExtent()) {
+ base::Optional<VoidExtentData> ve = UnpackVoidExtent(pb);
+ if (!ve) {
+ return {};
+ }
+
+ return LogicalASTCBlock(footprint, ve.value());
+ } else {
+ base::Optional<IntermediateBlockData> ib = UnpackIntermediateBlock(pb);
+ if (!ib) {
+ return {};
+ }
+
+ return LogicalASTCBlock(footprint, ib.value());
+ }
+}
+
+} // namespace astc_codec