blob: 2438ca9ab27c92532d0b525cd41af4c3a8259be5 [file] [log] [blame]
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <cstring>
#include <iterator>
#include <memory>
#include "src/obu_parser.h"
#include "src/symbol_decoder_context.h"
#include "src/tile.h"
#include "src/utils/bit_mask_set.h"
#include "src/utils/common.h"
#include "src/utils/constants.h"
#include "src/utils/entropy_decoder.h"
#include "src/utils/memory.h"
#include "src/utils/types.h"
namespace libgav1 {
int Tile::GetPaletteCache(const Block& block, PlaneType plane_type,
uint16_t* const cache) {
const int top_size =
(block.top_available && Mod64(MultiplyBy4(block.row4x4)) != 0)
? block.bp_top->palette_mode_info.size[plane_type]
: 0;
const int left_size = block.left_available
? block.bp_left->palette_mode_info.size[plane_type]
: 0;
if (left_size == 0 && top_size == 0) return 0;
// Merge the left and top colors in sorted order and store them in |cache|.
uint16_t dummy[1];
uint16_t* top = (top_size > 0)
? block.bp_top->palette_mode_info.color[plane_type]
: dummy;
uint16_t* left = (left_size > 0)
? block.bp_left->palette_mode_info.color[plane_type]
: dummy;
std::merge(top, top + top_size, left, left + left_size, cache);
// Deduplicate the entries in |cache| and return the number of unique
// entries.
return static_cast<int>(
std::distance(cache, std::unique(cache, cache + left_size + top_size)));
}
void Tile::ReadPaletteColors(const Block& block, Plane plane) {
const PlaneType plane_type = GetPlaneType(plane);
uint16_t cache[2 * kMaxPaletteSize];
const int n = GetPaletteCache(block, plane_type, cache);
BlockParameters& bp = *block.bp;
const uint8_t palette_size = bp.palette_mode_info.size[plane_type];
uint16_t* const palette_color = bp.palette_mode_info.color[plane];
const int8_t bitdepth = sequence_header_.color_config.bitdepth;
int index = 0;
for (int i = 0; i < n && index < palette_size; ++i) {
if (reader_.ReadBit() != 0) { // use_palette_color_cache.
palette_color[index++] = cache[i];
}
}
const int merge_pivot = index;
if (index < palette_size) {
palette_color[index++] =
static_cast<uint16_t>(reader_.ReadLiteral(bitdepth));
}
const int max_value = (1 << bitdepth) - 1;
if (index < palette_size) {
int bits = bitdepth - 3 + static_cast<int>(reader_.ReadLiteral(2));
do {
const int delta = static_cast<int>(reader_.ReadLiteral(bits)) +
(plane_type == kPlaneTypeY ? 1 : 0);
palette_color[index] =
std::min(palette_color[index - 1] + delta, max_value);
if (palette_color[index] + (plane_type == kPlaneTypeY ? 1 : 0) >=
max_value) {
// Once the color exceeds max_value, all others can be set to max_value
// (since they are computed as a delta on top of the current color and
// then clipped).
Memset(&palette_color[index + 1], max_value, palette_size - index - 1);
break;
}
const int range = (1 << bitdepth) - palette_color[index] -
(plane_type == kPlaneTypeY ? 1 : 0);
bits = std::min(bits, CeilLog2(range));
} while (++index < palette_size);
}
// Palette colors are generated using two ascending arrays. So sorting them is
// simply a matter of merging the two sorted portions of the array.
std::inplace_merge(palette_color, palette_color + merge_pivot,
palette_color + palette_size);
if (plane_type == kPlaneTypeUV) {
uint16_t* const palette_color_v = bp.palette_mode_info.color[kPlaneV];
if (reader_.ReadBit() != 0) { // delta_encode_palette_colors_v.
const int bits = bitdepth - 4 + static_cast<int>(reader_.ReadLiteral(2));
palette_color_v[0] = reader_.ReadLiteral(bitdepth);
for (int i = 1; i < palette_size; ++i) {
int delta = static_cast<int>(reader_.ReadLiteral(bits));
if (delta != 0 && reader_.ReadBit() != 0) delta = -delta;
// This line is equivalent to the following lines in the spec:
// val = palette_colors_v[ idx - 1 ] + palette_delta_v
// if ( val < 0 ) val += maxVal
// if ( val >= maxVal ) val -= maxVal
// palette_colors_v[ idx ] = Clip1( val )
//
// The difference is that in the code, max_value is (1 << bitdepth) - 1.
// So "& max_value" has the desired effect of computing both the "if"
// conditions and the Clip.
palette_color_v[i] = (palette_color_v[i - 1] + delta) & max_value;
}
} else {
for (int i = 0; i < palette_size; ++i) {
palette_color_v[i] =
static_cast<uint16_t>(reader_.ReadLiteral(bitdepth));
}
}
}
}
void Tile::ReadPaletteModeInfo(const Block& block) {
BlockParameters& bp = *block.bp;
if (IsBlockSmallerThan8x8(block.size) || block.size > kBlock64x64 ||
!frame_header_.allow_screen_content_tools) {
bp.palette_mode_info.size[kPlaneTypeY] = 0;
bp.palette_mode_info.size[kPlaneTypeUV] = 0;
return;
}
const int block_size_context =
k4x4WidthLog2[block.size] + k4x4HeightLog2[block.size] - 2;
if (bp.y_mode == kPredictionModeDc) {
const int context =
static_cast<int>(block.top_available &&
block.bp_top->palette_mode_info.size[kPlaneTypeY] >
0) +
static_cast<int>(block.left_available &&
block.bp_left->palette_mode_info.size[kPlaneTypeY] >
0);
const bool has_palette_y = reader_.ReadSymbol(
symbol_decoder_context_.has_palette_y_cdf[block_size_context][context]);
if (has_palette_y) {
bp.palette_mode_info.size[kPlaneTypeY] =
kMinPaletteSize +
reader_.ReadSymbol<kPaletteSizeSymbolCount>(
symbol_decoder_context_.palette_y_size_cdf[block_size_context]);
ReadPaletteColors(block, kPlaneY);
}
}
if (bp.uv_mode == kPredictionModeDc && block.HasChroma()) {
const int context =
static_cast<int>(bp.palette_mode_info.size[kPlaneTypeY] > 0);
const bool has_palette_uv =
reader_.ReadSymbol(symbol_decoder_context_.has_palette_uv_cdf[context]);
if (has_palette_uv) {
bp.palette_mode_info.size[kPlaneTypeUV] =
kMinPaletteSize +
reader_.ReadSymbol<kPaletteSizeSymbolCount>(
symbol_decoder_context_.palette_uv_size_cdf[block_size_context]);
ReadPaletteColors(block, kPlaneU);
}
}
}
void Tile::PopulatePaletteColorContexts(
const Block& block, PlaneType plane_type, int i, int start, int end,
uint8_t color_order[kMaxPaletteSquare][kMaxPaletteSize],
uint8_t color_context[kMaxPaletteSquare]) {
const PredictionParameters& prediction_parameters =
*block.bp->prediction_parameters;
for (int column = start, counter = 0; column >= end; --column, ++counter) {
const int row = i - column;
assert(row > 0 || column > 0);
const uint8_t top =
(row > 0)
? prediction_parameters.color_index_map[plane_type][row - 1][column]
: 0;
const uint8_t left =
(column > 0)
? prediction_parameters.color_index_map[plane_type][row][column - 1]
: 0;
uint8_t index_mask;
static_assert(kMaxPaletteSize <= 8, "");
int index;
if (column <= 0) {
color_context[counter] = 0;
color_order[counter][0] = top;
index_mask = 1 << top;
index = 1;
} else if (row <= 0) {
color_context[counter] = 0;
color_order[counter][0] = left;
index_mask = 1 << left;
index = 1;
} else {
const uint8_t top_left =
prediction_parameters
.color_index_map[plane_type][row - 1][column - 1];
index_mask = (1 << top) | (1 << left) | (1 << top_left);
if (top == left && top == top_left) {
color_context[counter] = 4;
color_order[counter][0] = top;
index = 1;
} else if (top == left) {
color_context[counter] = 3;
color_order[counter][0] = top;
color_order[counter][1] = top_left;
index = 2;
} else if (top == top_left) {
color_context[counter] = 2;
color_order[counter][0] = top_left;
color_order[counter][1] = left;
index = 2;
} else if (left == top_left) {
color_context[counter] = 2;
color_order[counter][0] = top_left;
color_order[counter][1] = top;
index = 2;
} else {
color_context[counter] = 1;
color_order[counter][0] = std::min(top, left);
color_order[counter][1] = std::max(top, left);
color_order[counter][2] = top_left;
index = 3;
}
}
// Even though only the first |palette_size| entries of this array are ever
// used, it is faster to populate all 8 because of the vectorization of the
// constant sized loop.
for (uint8_t j = 0; j < kMaxPaletteSize; ++j) {
if (BitMaskSet::MaskContainsValue(index_mask, j)) continue;
color_order[counter][index++] = j;
}
}
}
bool Tile::ReadPaletteTokens(const Block& block) {
const PaletteModeInfo& palette_mode_info = block.bp->palette_mode_info;
PredictionParameters& prediction_parameters =
*block.bp->prediction_parameters;
for (int plane_type = kPlaneTypeY;
plane_type < (block.HasChroma() ? kNumPlaneTypes : kPlaneTypeUV);
++plane_type) {
const int palette_size = palette_mode_info.size[plane_type];
if (palette_size == 0) continue;
int block_height = kBlockHeightPixels[block.size];
int block_width = kBlockWidthPixels[block.size];
int screen_height = std::min(
block_height, MultiplyBy4(frame_header_.rows4x4 - block.row4x4));
int screen_width = std::min(
block_width, MultiplyBy4(frame_header_.columns4x4 - block.column4x4));
if (plane_type == kPlaneTypeUV) {
block_height >>= sequence_header_.color_config.subsampling_y;
block_width >>= sequence_header_.color_config.subsampling_x;
screen_height >>= sequence_header_.color_config.subsampling_y;
screen_width >>= sequence_header_.color_config.subsampling_x;
if (block_height < 4) {
block_height += 2;
screen_height += 2;
}
if (block_width < 4) {
block_width += 2;
screen_width += 2;
}
}
if (!prediction_parameters.color_index_map[plane_type].Reset(
block_height, block_width, /*zero_initialize=*/false)) {
return false;
}
int first_value = 0;
reader_.DecodeUniform(palette_size, &first_value);
prediction_parameters.color_index_map[plane_type][0][0] = first_value;
for (int i = 1; i < screen_height + screen_width - 1; ++i) {
const int start = std::min(i, screen_width - 1);
const int end = std::max(0, i - screen_height + 1);
uint8_t color_order[kMaxPaletteSquare][kMaxPaletteSize];
uint8_t color_context[kMaxPaletteSquare];
PopulatePaletteColorContexts(block, static_cast<PlaneType>(plane_type), i,
start, end, color_order, color_context);
for (int j = start, counter = 0; j >= end; --j, ++counter) {
uint16_t* const cdf =
symbol_decoder_context_
.palette_color_index_cdf[plane_type]
[palette_size - kMinPaletteSize]
[color_context[counter]];
const int color_order_index = reader_.ReadSymbol(cdf, palette_size);
prediction_parameters.color_index_map[plane_type][i - j][j] =
color_order[counter][color_order_index];
}
}
if (screen_width < block_width) {
for (int i = 0; i < screen_height; ++i) {
memset(
&prediction_parameters.color_index_map[plane_type][i][screen_width],
prediction_parameters
.color_index_map[plane_type][i][screen_width - 1],
block_width - screen_width);
}
}
for (int i = screen_height; i < block_height; ++i) {
memcpy(
prediction_parameters.color_index_map[plane_type][i],
prediction_parameters.color_index_map[plane_type][screen_height - 1],
block_width);
}
}
return true;
}
} // namespace libgav1