blob: 4ddfb2e903c1c12ec9573082b9ff588d8f1d0d93 [file] [log] [blame]
// Copyright 2019 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.
// -----------------------------------------------------------------------------
//
// Common code for AOM residuals, branched from libgav1, with as little
// changes as possible.
//
// Author: Vincent Rabaud (vrabaud@google.com)
#include "src/common/lossy/residuals_aom.h"
#include <numeric>
#include "src/common/lossy/aom/cdfs.inc"
#include "src/utils/utils.h"
namespace WP2 {
namespace libgav1 {
//------------------------------------------------------------------------------
// From libgav1/src/symbol_decoder_context.cc
#define CDF_COPY(source, destination) \
memcpy(destination, source, sizeof(source))
void SymbolDecoderContext::Initialize(int quantizer_context) {
CDF_COPY(kDefaultAllZeroCdf[quantizer_context], all_zero_cdf);
CDF_COPY(kDefaultEobPt16Cdf[quantizer_context], eob_pt_16_cdf);
CDF_COPY(kDefaultEobPt32Cdf[quantizer_context], eob_pt_32_cdf);
CDF_COPY(kDefaultEobPt64Cdf[quantizer_context], eob_pt_64_cdf);
CDF_COPY(kDefaultEobPt128Cdf[quantizer_context], eob_pt_128_cdf);
CDF_COPY(kDefaultEobPt256Cdf[quantizer_context], eob_pt_256_cdf);
CDF_COPY(kDefaultEobPt512Cdf[quantizer_context], eob_pt_512_cdf);
CDF_COPY(kDefaultEobPt1024Cdf[quantizer_context], eob_pt_1024_cdf);
CDF_COPY(kDefaultEobExtraCdf[quantizer_context], eob_extra_cdf);
CDF_COPY(kDefaultCoeffBaseEobCdf[quantizer_context], coeff_base_eob_cdf);
CDF_COPY(kDefaultCoeffBaseCdf[quantizer_context], coeff_base_cdf);
CDF_COPY(kDefaultCoeffBaseRangeCdf[quantizer_context], coeff_base_range_cdf);
CDF_COPY(kDefaultDcSignCdf[quantizer_context], dc_sign_cdf);
}
#undef CDF_COPY
//------------------------------------------------------------------------------
// From libgav1/src/utils/constants.cc
const uint8_t kBlockWidthPixels[kMaxBlockSizes] = {
4, 4, 4, 8, 8, 8, 8, 16, 16, 16, 16,
16, 32, 32, 32, 32, 64, 64, 64, 64, 128, 128};
const uint8_t kBlockHeightPixels[kMaxBlockSizes] = {
4, 8, 16, 4, 8, 16, 32, 4, 8, 16, 32,
64, 8, 16, 32, 64, 16, 32, 64, 128, 64, 128};
const uint8_t kTransformWidth[kNumTransformSizes] = {
4, 4, 4, 8, 8, 8, 8, 16, 16, 16, 16, 16, 32, 32, 32, 32, 64, 64, 64};
const uint8_t kTransformHeight[kNumTransformSizes] = {
4, 8, 16, 4, 8, 16, 32, 4, 8, 16, 32, 64, 8, 16, 32, 64, 16, 32, 64};
const uint8_t kTransformWidth4x4[kNumTransformSizes] = {
1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 8, 8, 8, 8, 16, 16, 16};
const uint8_t kTransformHeight4x4[kNumTransformSizes] = {
1, 2, 4, 1, 2, 4, 8, 1, 2, 4, 8, 16, 2, 4, 8, 16, 4, 8, 16};
const uint8_t kTransformWidthLog2[kNumTransformSizes] = {
2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6};
//------------------------------------------------------------------------------
namespace {
/* clang-format off */
constexpr uint8_t kCoeffBaseContextOffset[kNumTransformSizes][5][5] = {
{{0, 1, 6, 6, 0},
{1, 6, 6, 21, 0},
{6, 6, 21, 21, 0},
{6, 21, 21, 21, 0},
{0, 0, 0, 0, 0}}, // 4x4
{{ 0, 11, 11, 11, 0},
{11, 11, 11, 11, 0},
{ 6, 6, 21, 21, 0},
{ 6, 21, 21, 21, 0},
{21, 21, 21, 21, 0}}, // 4x8
{{ 0, 11, 11, 11, 0},
{11, 11, 11, 11, 0},
{ 6, 6, 21, 21, 0},
{ 6, 21, 21, 21, 0},
{21, 21, 21, 21, 0}}, // 4x16
{{ 0, 16, 6, 6, 21},
{16, 16, 6, 21, 21},
{16, 16, 21, 21, 21},
{16, 16, 21, 21, 21},
{ 0, 0, 0, 0, 0}}, // 8x4
{{ 0, 1, 6, 6, 21},
{ 1, 6, 6, 21, 21},
{ 6, 6, 21, 21, 21},
{ 6, 21, 21, 21, 21},
{21, 21, 21, 21, 21}}, // 8x8
{{ 0, 11, 11, 11, 11},
{11, 11, 11, 11, 11},
{ 6, 6, 21, 21, 21},
{ 6, 21, 21, 21, 21},
{21, 21, 21, 21, 21}}, // 8x16
{{ 0, 11, 11, 11, 11},
{11, 11, 11, 11, 11},
{ 6, 6, 21, 21, 21},
{ 6, 21, 21, 21, 21},
{21, 21, 21, 21, 21}}, // 8x32
{{ 0, 16, 6, 6, 21},
{16, 16, 6, 21, 21},
{16, 16, 21, 21, 21},
{16, 16, 21, 21, 21},
{ 0, 0, 0, 0, 0}}, // 16x4
{{ 0, 16, 6, 6, 21},
{16, 16, 6, 21, 21},
{16, 16, 21, 21, 21},
{16, 16, 21, 21, 21},
{16, 16, 21, 21, 21}}, // 16x8
{{ 0, 1, 6, 6, 21},
{ 1, 6, 6, 21, 21},
{ 6, 6, 21, 21, 21},
{ 6, 21, 21, 21, 21},
{21, 21, 21, 21, 21}}, // 16x16
{{ 0, 11, 11, 11, 11},
{11, 11, 11, 11, 11},
{ 6, 6, 21, 21, 21},
{ 6, 21, 21, 21, 21},
{21, 21, 21, 21, 21}}, // 16x32
{{ 0, 11, 11, 11, 11},
{11, 11, 11, 11, 11},
{ 6, 6, 21, 21, 21},
{ 6, 21, 21, 21, 21},
{21, 21, 21, 21, 21}}, // 16x64
{{ 0, 16, 6, 6, 21},
{16, 16, 6, 21, 21},
{16, 16, 21, 21, 21},
{16, 16, 21, 21, 21},
{16, 16, 21, 21, 21}}, // 32x8
{{ 0, 16, 6, 6, 21},
{16, 16, 6, 21, 21},
{16, 16, 21, 21, 21},
{16, 16, 21, 21, 21},
{16, 16, 21, 21, 21}}, // 32x16
{{ 0, 1, 6, 6, 21},
{ 1, 6, 6, 21, 21},
{ 6, 6, 21, 21, 21},
{ 6, 21, 21, 21, 21},
{21, 21, 21, 21, 21}}, // 32x32
{{ 0, 11, 11, 11, 11},
{11, 11, 11, 11, 11},
{ 6, 6, 21, 21, 21},
{ 6, 21, 21, 21, 21},
{21, 21, 21, 21, 21}}, // 32x64
{{ 0, 16, 6, 6, 21},
{16, 16, 6, 21, 21},
{16, 16, 21, 21, 21},
{16, 16, 21, 21, 21},
{16, 16, 21, 21, 21}}, // 64x16
{{ 0, 16, 6, 6, 21},
{16, 16, 6, 21, 21},
{16, 16, 21, 21, 21},
{16, 16, 21, 21, 21},
{16, 16, 21, 21, 21}}, // 64x32
{{ 0, 1, 6, 6, 21},
{ 1, 6, 6, 21, 21},
{ 6, 6, 21, 21, 21},
{ 6, 21, 21, 21, 21},
{21, 21, 21, 21, 21}} // 64x64
};
/* clang-format on */
constexpr uint8_t kCoeffBasePositionContextOffset[3] = {26, 31, 36};
} // namespace
// Section 8.3.2 in the spec, under coeff_base_eob.
int AOMResidualIO::GetCoeffBaseContextEob(TransformSize tx_size, int index) {
if (index == 0) return 0;
const TransformSize adjusted_tx_size = kAdjustedTransformSize[tx_size];
const int tx_width_log2 = kTransformWidthLog2[adjusted_tx_size];
const int tx_height = kTransformHeight[adjusted_tx_size];
if (index <= DivideBy8(tx_height << tx_width_log2)) return 1;
if (index <= DivideBy4(tx_height << tx_width_log2)) return 2;
return 3;
}
// Section 8.3.2 in the spec, under coeff_base.
static int GetCoeffBaseContext2D(const uint8_t qlevels[],
TransformSize tx_size,
int adjusted_tx_width_log2,
uint16_t pos) {
if (pos == 0) return 0;
const int tx_width = 1 << adjusted_tx_width_log2;
const int padded_tx_width = tx_width + kQuantizedCoefficientBufferPadding;
const uint8_t* const levels =
&qlevels[PaddedIndex(pos, adjusted_tx_width_log2)];
const int context = std::min(
4, DivideBy2(1 + levels[1] + levels[2] +
levels[padded_tx_width] +
levels[padded_tx_width + 1] +
levels[2 * padded_tx_width]));
const int row = pos >> adjusted_tx_width_log2;
const int column = pos & (tx_width - 1);
return context + kCoeffBaseContextOffset[tx_size][std::min(row, 4)]
[std::min(column, 4)];
}
// Section 8.3.2 in the spec, under coeff_base.
static int GetCoeffBaseContextHorizontal(
const uint8_t qlevels[], TransformSize /*tx_size*/,
int adjusted_tx_width_log2, uint16_t pos) {
const int tx_width = 1 << adjusted_tx_width_log2;
const int padded_tx_width = tx_width + kQuantizedCoefficientBufferPadding;
const uint8_t* const levels =
&qlevels[PaddedIndex(pos, adjusted_tx_width_log2)];
const int context = std::min(
4, DivideBy2(1 + levels[1] + levels[2] + levels[3] + levels[4] +
levels[padded_tx_width]));
const int index = pos & (tx_width - 1);
return context + kCoeffBasePositionContextOffset[std::min(index, 2)];
}
// Section 8.3.2 in the spec, under coeff_base.
static int GetCoeffBaseContextVertical(
const uint8_t qlevels[], TransformSize /*tx_size*/,
int adjusted_tx_width_log2, uint16_t pos) {
const int tx_width = 1 << adjusted_tx_width_log2;
const int padded_tx_width = tx_width + kQuantizedCoefficientBufferPadding;
const uint8_t* const levels =
&qlevels[PaddedIndex(pos, adjusted_tx_width_log2)];
const int context = std::min(
4, DivideBy2(1 + levels[1] +
levels[1 * padded_tx_width] +
levels[2 * padded_tx_width] +
levels[3 * padded_tx_width] +
levels[4 * padded_tx_width]));
const int index = pos >> adjusted_tx_width_log2;
return context + kCoeffBasePositionContextOffset[std::min(index, 2)];
}
static constexpr CoeffBaseContextFunc kCoeffBaseContextFuncs[] = {
GetCoeffBaseContext2D,
GetCoeffBaseContextHorizontal,
GetCoeffBaseContextVertical
};
CoeffBaseContextFunc
AOMResidualIO::GetCoeffsBaseContextFunc(uint32_t tx_class) {
return kCoeffBaseContextFuncs[tx_class];
}
// Section 8.3.2 in the spec, under coeff_br.
static int GetCoeffBaseRangeContext2D(
const uint8_t qlevels[], int adjusted_tx_width_log2,
int pos) {
const uint8_t tx_width = 1 << adjusted_tx_width_log2;
const int padded_tx_width = tx_width + kQuantizedCoefficientBufferPadding;
const uint8_t* const levels =
&qlevels[PaddedIndex(pos, adjusted_tx_width_log2)];
const int context = std::min(
6, DivideBy2(1 + levels[1] +
levels[padded_tx_width] +
levels[padded_tx_width + 1]));
if (pos == 0) return context;
const int row = pos >> adjusted_tx_width_log2;
const int column = pos & (tx_width - 1);
return context + (((row | column) < 2) ? 7 : 14);
}
// Section 8.3.2 in the spec, under coeff_br.
static int GetCoeffBaseRangeContextHorizontal(
const uint8_t qlevels[], int adjusted_tx_width_log2, int pos) {
const uint8_t tx_width = 1 << adjusted_tx_width_log2;
const int padded_tx_width = tx_width + kQuantizedCoefficientBufferPadding;
const uint8_t* const levels =
&qlevels[PaddedIndex(pos, adjusted_tx_width_log2)];
const int context = std::min(
6, DivideBy2(1 + levels[1] + levels[2] + levels[padded_tx_width]));
if (pos == 0) return context;
const int column = pos & (tx_width - 1);
return context + ((column == 0) ? 7 : 14);
}
// Section 8.3.2 in the spec, under coeff_br.
static int GetCoeffBaseRangeContextVertical(
const uint8_t qlevels[], int adjusted_tx_width_log2, int pos) {
const uint8_t tx_width = 1 << adjusted_tx_width_log2;
const int padded_tx_width = tx_width + kQuantizedCoefficientBufferPadding;
const uint8_t* const levels =
&qlevels[PaddedIndex(pos, adjusted_tx_width_log2)];
const int context = std::min(
6, DivideBy2(1 + levels[1] +
levels[padded_tx_width] + levels[2 * padded_tx_width]));
if (pos == 0) return context;
const int row = pos >> adjusted_tx_width_log2;
return context + ((row == 0) ? 7 : 14);
}
static constexpr CoeffBaseRangeContextFunc kCoeffBaseRangeContextFuncs[] = {
GetCoeffBaseRangeContext2D,
GetCoeffBaseRangeContextHorizontal,
GetCoeffBaseRangeContextVertical
};
CoeffBaseRangeContextFunc
AOMResidualIO::GetCoeffBaseRangeContextFunc(uint32_t tx_class) {
return kCoeffBaseRangeContextFuncs[tx_class];
}
// Section 8.3.2 in the spec, under coeff_br. Optimized for end of block based
// on the fact that {0, 1}, {1, 0}, {1, 1}, {0, 2} and {2, 0} will all be 0 in
// the end of block case.
int AOMResidualIO::GetCoeffBaseRangeContextEob(int adjusted_tx_width_log2,
int pos) {
if (pos == 0) return 0;
const uint8_t tx_width = 1 << adjusted_tx_width_log2;
const int row = pos >> adjusted_tx_width_log2;
const int column = pos & (tx_width - 1);
return ((row | column) < 2) ? 7 : 14;
}
} // namespace libgav1
} // namespace WP2