| #include "src/loop_restoration_info.h" |
| |
| #include <algorithm> |
| #include <array> |
| #include <cassert> |
| #include <cstddef> |
| #include <cstdint> |
| #include <memory> |
| #include <new> |
| |
| #include "src/utils/common.h" |
| #include "src/utils/logging.h" |
| |
| namespace libgav1 { |
| |
| // Controls how self guided deltas are read. |
| constexpr int kSgrProjReadControl = 4; |
| // Maps the restoration type encoded in the compressed headers (restoration_type |
| // element in the spec) of the bitstream to LoopRestorationType. This is used |
| // only when the restoration type in the frame header is |
| // LoopRestorationTypeSwitchable. |
| constexpr LoopRestorationType kBitstreamRestorationTypeMap[] = { |
| kLoopRestorationTypeNone, kLoopRestorationTypeWiener, |
| kLoopRestorationTypeSgrProj}; |
| |
| bool LoopRestorationInfo::Allocate() { |
| const int num_planes = is_monochrome_ ? kMaxPlanesMonochrome : kMaxPlanes; |
| int total_num_units = 0; |
| for (int plane = kPlaneY; plane < num_planes; ++plane) { |
| if (loop_restoration_.type[plane] == kLoopRestorationTypeNone) { |
| plane_needs_filtering_[plane] = false; |
| continue; |
| } |
| plane_needs_filtering_[plane] = true; |
| const int width = (plane == kPlaneY) |
| ? width_ |
| : RightShiftWithRounding(width_, subsampling_x_); |
| const int height = (plane == kPlaneY) |
| ? height_ |
| : RightShiftWithRounding(height_, subsampling_y_); |
| num_horizontal_units_[plane] = |
| std::max(1, (width + DivideBy2(loop_restoration_.unit_size[plane])) / |
| loop_restoration_.unit_size[plane]); |
| num_vertical_units_[plane] = |
| std::max(1, (height + DivideBy2(loop_restoration_.unit_size[plane])) / |
| loop_restoration_.unit_size[plane]); |
| num_units_[plane] = |
| num_horizontal_units_[plane] * num_vertical_units_[plane]; |
| total_num_units += num_units_[plane]; |
| } |
| // Allocate the RestorationUnitInfo arrays for all planes in a single heap |
| // allocation and divide up the buffer into arrays of the right sizes. |
| loop_restoration_info_buffer_.reset(new (std::nothrow) |
| RestorationUnitInfo[total_num_units]); |
| if (loop_restoration_info_buffer_ == nullptr) return false; |
| RestorationUnitInfo* loop_restoration_info = |
| loop_restoration_info_buffer_.get(); |
| for (int plane = kPlaneY; plane < num_planes; ++plane) { |
| if (loop_restoration_.type[plane] == kLoopRestorationTypeNone) { |
| continue; |
| } |
| loop_restoration_info_[plane] = loop_restoration_info; |
| loop_restoration_info += num_units_[plane]; |
| } |
| return true; |
| } |
| |
| bool LoopRestorationInfo::PopulateUnitInfoForSuperBlock( |
| Plane plane, BlockSize block_size, bool is_superres_scaled, |
| uint8_t superres_scale_denominator, int row4x4, int column4x4, |
| LoopRestorationUnitInfo* const unit_info) const { |
| assert(unit_info != nullptr); |
| if (!plane_needs_filtering_[plane]) return false; |
| const int denominator_column = |
| is_superres_scaled |
| ? loop_restoration_.unit_size[plane] * kSuperResScaleNumerator |
| : loop_restoration_.unit_size[plane]; |
| const int numerator_column = |
| is_superres_scaled ? superres_scale_denominator : 1; |
| const int pixel_column_start = |
| RowOrColumn4x4ToPixel(column4x4, plane, subsampling_x_); |
| const int pixel_column_end = RowOrColumn4x4ToPixel( |
| column4x4 + kNum4x4BlocksWide[block_size], plane, subsampling_x_); |
| const int unit_row = loop_restoration_.unit_size[plane]; |
| const int pixel_row_start = |
| RowOrColumn4x4ToPixel(row4x4, plane, subsampling_y_); |
| const int pixel_row_end = RowOrColumn4x4ToPixel( |
| row4x4 + kNum4x4BlocksHigh[block_size], plane, subsampling_y_); |
| unit_info->column_start = |
| (pixel_column_start * numerator_column + denominator_column - 1) / |
| denominator_column; |
| unit_info->column_end = |
| (pixel_column_end * numerator_column + denominator_column - 1) / |
| denominator_column; |
| unit_info->row_start = (pixel_row_start + unit_row - 1) / unit_row; |
| unit_info->row_end = (pixel_row_end + unit_row - 1) / unit_row; |
| unit_info->column_end = |
| std::min(unit_info->column_end, num_horizontal_units_[plane]); |
| unit_info->row_end = std::min(unit_info->row_end, num_vertical_units_[plane]); |
| return true; |
| } |
| |
| void LoopRestorationInfo::ReadUnitCoefficients( |
| DaalaBitReader* const reader, |
| SymbolDecoderContext* const symbol_decoder_context, Plane plane, |
| int unit_id, |
| std::array<RestorationUnitInfo, kMaxPlanes>* const reference_unit_info) { |
| LoopRestorationType unit_restoration_type = kLoopRestorationTypeNone; |
| if (loop_restoration_.type[plane] == kLoopRestorationTypeSwitchable) { |
| unit_restoration_type = kBitstreamRestorationTypeMap |
| [reader->ReadSymbol<kRestorationTypeSymbolCount>( |
| symbol_decoder_context->restoration_type_cdf)]; |
| } else if (loop_restoration_.type[plane] == kLoopRestorationTypeWiener) { |
| const bool use_wiener = |
| reader->ReadSymbol(symbol_decoder_context->use_wiener_cdf); |
| if (use_wiener) unit_restoration_type = kLoopRestorationTypeWiener; |
| } else if (loop_restoration_.type[plane] == kLoopRestorationTypeSgrProj) { |
| const bool use_sgrproj = |
| reader->ReadSymbol(symbol_decoder_context->use_sgrproj_cdf); |
| if (use_sgrproj) unit_restoration_type = kLoopRestorationTypeSgrProj; |
| } |
| loop_restoration_info_[plane][unit_id].type = unit_restoration_type; |
| |
| if (unit_restoration_type == kLoopRestorationTypeWiener) { |
| ReadWienerInfo(reader, plane, unit_id, reference_unit_info); |
| } else if (unit_restoration_type == kLoopRestorationTypeSgrProj) { |
| ReadSgrProjInfo(reader, plane, unit_id, reference_unit_info); |
| } |
| } |
| |
| void LoopRestorationInfo::ReadWienerInfo( |
| DaalaBitReader* const reader, Plane plane, int unit_id, |
| std::array<RestorationUnitInfo, kMaxPlanes>* const reference_unit_info) { |
| for (int i = WienerInfo::kVertical; i <= WienerInfo::kHorizontal; ++i) { |
| if (plane != kPlaneY) { |
| loop_restoration_info_[plane][unit_id].wiener_info.filter[i][0] = 0; |
| } |
| for (int j = static_cast<int>(plane != kPlaneY); j < kNumWienerCoefficients; |
| ++j) { |
| const int8_t wiener_min = kWienerTapsMin[j]; |
| const int8_t wiener_max = kWienerTapsMax[j]; |
| const int control = j + 1; |
| int value; |
| if (!reader->DecodeSignedSubexpWithReference( |
| wiener_min, wiener_max + 1, |
| (*reference_unit_info)[plane].wiener_info.filter[i][j], control, |
| &value)) { |
| LIBGAV1_DLOG( |
| ERROR, |
| "Error decoding Wiener filter coefficients: plane %d, unit_id %d", |
| static_cast<int>(plane), unit_id); |
| return; |
| } |
| loop_restoration_info_[plane][unit_id].wiener_info.filter[i][j] = value; |
| (*reference_unit_info)[plane].wiener_info.filter[i][j] = value; |
| } |
| } |
| } |
| |
| void LoopRestorationInfo::ReadSgrProjInfo( |
| DaalaBitReader* const reader, Plane plane, int unit_id, |
| std::array<RestorationUnitInfo, kMaxPlanes>* const reference_unit_info) { |
| const int sgr_proj_index = |
| static_cast<int>(reader->ReadLiteral(kSgrProjParamsBits)); |
| loop_restoration_info_[plane][unit_id].sgr_proj_info.index = sgr_proj_index; |
| for (int i = 0; i < 2; ++i) { |
| const uint8_t radius = kSgrProjParams[sgr_proj_index][i * 2]; |
| const int8_t multiplier_min = kSgrProjMultiplierMin[i]; |
| const int8_t multiplier_max = kSgrProjMultiplierMax[i]; |
| int multiplier; |
| if (radius != 0) { |
| if (!reader->DecodeSignedSubexpWithReference( |
| multiplier_min, multiplier_max + 1, |
| (*reference_unit_info)[plane].sgr_proj_info.multiplier[i], |
| kSgrProjReadControl, &multiplier)) { |
| LIBGAV1_DLOG(ERROR, |
| "Error decoding Self-guided filter coefficients: plane " |
| "%d, unit_id %d", |
| static_cast<int>(plane), unit_id); |
| return; |
| } |
| } else { |
| multiplier = 0; |
| if (i == 1) { |
| multiplier = |
| Clip3((1 << kSgrProjPrecisionBits) - |
| (*reference_unit_info)[plane].sgr_proj_info.multiplier[0], |
| multiplier_min, multiplier_max); |
| } |
| } |
| loop_restoration_info_[plane][unit_id].sgr_proj_info.multiplier[i] = |
| multiplier; |
| (*reference_unit_info)[plane].sgr_proj_info.multiplier[i] = multiplier; |
| } |
| } |
| |
| } // namespace libgav1 |