// Copyright 2019 The libgav1 Authors // // 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 // // http://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 #include #include #include "src/dsp/constants.h" #include "src/obu_parser.h" #include "src/symbol_decoder_context.h" #include "src/tile.h" #include "src/utils/array_2d.h" #include "src/utils/block_parameters_holder.h" #include "src/utils/common.h" #include "src/utils/constants.h" #include "src/utils/entropy_decoder.h" #include "src/utils/segmentation.h" #include "src/utils/stack.h" #include "src/utils/types.h" namespace libgav1 { namespace { constexpr uint8_t kMaxVariableTransformTreeDepth = 2; // Max_Tx_Depth array from section 5.11.5 in the spec with the following // modification: If the element is not zero, it is subtracted by one. That is // the only way in which this array is being used. constexpr int kTxDepthCdfIndex[kMaxBlockSizes] = { 0, 0, 1, 0, 0, 1, 2, 1, 1, 1, 2, 3, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3}; constexpr TransformSize kMaxTransformSizeRectangle[kMaxBlockSizes] = { kTransformSize4x4, kTransformSize4x8, kTransformSize4x16, kTransformSize8x4, kTransformSize8x8, kTransformSize8x16, kTransformSize8x32, kTransformSize16x4, kTransformSize16x8, kTransformSize16x16, kTransformSize16x32, kTransformSize16x64, kTransformSize32x8, kTransformSize32x16, kTransformSize32x32, kTransformSize32x64, kTransformSize64x16, kTransformSize64x32, kTransformSize64x64, kTransformSize64x64, kTransformSize64x64, kTransformSize64x64}; TransformSize GetSquareTransformSize(uint8_t pixels) { switch (pixels) { case 128: case 64: return kTransformSize64x64; case 32: return kTransformSize32x32; case 16: return kTransformSize16x16; case 8: return kTransformSize8x8; default: return kTransformSize4x4; } } } // namespace int Tile::GetTopTransformWidth(const Block& block, int row4x4, int column4x4, bool ignore_skip) { if (row4x4 == block.row4x4) { if (!block.top_available[kPlaneY]) return 64; const BlockParameters& bp_top = *block_parameters_holder_.Find(row4x4 - 1, column4x4); if ((ignore_skip || bp_top.skip) && bp_top.is_inter) { return kBlockWidthPixels[bp_top.size]; } } return kTransformWidth[inter_transform_sizes_[row4x4 - 1][column4x4]]; } int Tile::GetLeftTransformHeight(const Block& block, int row4x4, int column4x4, bool ignore_skip) { if (column4x4 == block.column4x4) { if (!block.left_available[kPlaneY]) return 64; const BlockParameters& bp_left = *block_parameters_holder_.Find(row4x4, column4x4 - 1); if ((ignore_skip || bp_left.skip) && bp_left.is_inter) { return kBlockHeightPixels[bp_left.size]; } } return kTransformHeight[inter_transform_sizes_[row4x4][column4x4 - 1]]; } TransformSize Tile::ReadFixedTransformSize(const Block& block) { BlockParameters& bp = *block.bp; if (frame_header_.segmentation.lossless[bp.segment_id]) { return kTransformSize4x4; } const TransformSize max_rect_tx_size = kMaxTransformSizeRectangle[block.size]; const bool allow_select = !bp.skip || !bp.is_inter; if (block.size == kBlock4x4 || !allow_select || frame_header_.tx_mode != kTxModeSelect) { return max_rect_tx_size; } const int max_tx_width = kTransformWidth[max_rect_tx_size]; const int max_tx_height = kTransformHeight[max_rect_tx_size]; const int top_width = block.top_available[kPlaneY] ? GetTopTransformWidth(block, block.row4x4, block.column4x4, true) : 0; const int left_height = block.left_available[kPlaneY] ? GetLeftTransformHeight(block, block.row4x4, block.column4x4, true) : 0; const auto context = static_cast(top_width >= max_tx_width) + static_cast(left_height >= max_tx_height); const int cdf_index = kTxDepthCdfIndex[block.size]; uint16_t* const cdf = symbol_decoder_context_.tx_depth_cdf[cdf_index][context]; const int tx_depth = (cdf_index == 0) ? static_cast(reader_.ReadSymbol(cdf)) : reader_.ReadSymbol<3>(cdf); assert(tx_depth < 3); TransformSize tx_size = max_rect_tx_size; if (tx_depth == 0) return tx_size; tx_size = kSplitTransformSize[tx_size]; if (tx_depth == 1) return tx_size; return kSplitTransformSize[tx_size]; } void Tile::ReadVariableTransformTree(const Block& block, int row4x4, int column4x4, TransformSize tx_size) { const uint8_t pixels = std::max(block.width, block.height); const TransformSize max_tx_size = GetSquareTransformSize(pixels); const int context_delta = (kNumSquareTransformSizes - 1 - TransformSizeToSquareTransformIndex(max_tx_size)) * 6; // Branching factor is 4 and maximum depth is 2. So the maximum stack size // necessary is (4 - 1) + 4 = 7. Stack stack; stack.Push(TransformTreeNode(column4x4, row4x4, tx_size, 0)); do { TransformTreeNode node = stack.Pop(); const int tx_width4x4 = kTransformWidth4x4[node.tx_size]; const int tx_height4x4 = kTransformHeight4x4[node.tx_size]; if (node.tx_size != kTransformSize4x4 && node.depth != kMaxVariableTransformTreeDepth) { const auto top = static_cast(GetTopTransformWidth(block, node.y, node.x, false) < kTransformWidth[node.tx_size]); const auto left = static_cast( GetLeftTransformHeight(block, node.y, node.x, false) < kTransformHeight[node.tx_size]); const int context = static_cast(max_tx_size > kTransformSize8x8 && kTransformSizeSquareMax[node.tx_size] != max_tx_size) * 3 + context_delta + top + left; // tx_split. if (reader_.ReadSymbol(symbol_decoder_context_.tx_split_cdf[context])) { const TransformSize sub_tx_size = kSplitTransformSize[node.tx_size]; const int step_width4x4 = kTransformWidth4x4[sub_tx_size]; const int step_height4x4 = kTransformHeight4x4[sub_tx_size]; // The loops have to run in reverse order because we use a stack for // DFS. for (int i = tx_height4x4 - step_height4x4; i >= 0; i -= step_height4x4) { for (int j = tx_width4x4 - step_width4x4; j >= 0; j -= step_width4x4) { if (node.y + i >= frame_header_.rows4x4 || node.x + j >= frame_header_.columns4x4) { continue; } stack.Push(TransformTreeNode(node.x + j, node.y + i, sub_tx_size, node.depth + 1)); } } continue; } } // tx_split is false. for (int i = 0; i < tx_height4x4; ++i) { static_assert(sizeof(TransformSize) == 1, ""); memset(&inter_transform_sizes_[node.y + i][node.x], node.tx_size, tx_width4x4); } block_parameters_holder_.Find(node.y, node.x)->transform_size = node.tx_size; } while (!stack.Empty()); } void Tile::DecodeTransformSize(const Block& block) { BlockParameters& bp = *block.bp; if (frame_header_.tx_mode == kTxModeSelect && block.size > kBlock4x4 && bp.is_inter && !bp.skip && !frame_header_.segmentation.lossless[bp.segment_id]) { const TransformSize max_tx_size = kMaxTransformSizeRectangle[block.size]; const int tx_width4x4 = kTransformWidth4x4[max_tx_size]; const int tx_height4x4 = kTransformHeight4x4[max_tx_size]; for (int row = block.row4x4; row < block.row4x4 + block.height4x4; row += tx_height4x4) { for (int column = block.column4x4; column < block.column4x4 + block.width4x4; column += tx_width4x4) { ReadVariableTransformTree(block, row, column, max_tx_size); } } } else { bp.transform_size = ReadFixedTransformSize(block); for (int row = block.row4x4; row < block.row4x4 + block.height4x4; ++row) { static_assert(sizeof(TransformSize) == 1, ""); memset(&inter_transform_sizes_[row][block.column4x4], bp.transform_size, block.width4x4); } } } } // namespace libgav1