blob: 94afa8b9d509d8113e970d35195a8b61b0eed639 [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.
// -----------------------------------------------------------------------------
//
// WP2 lossy decoding of syntax.
//
// Author: Skal (pascal.massimino@gmail.com)
#include "src/common/constants.h"
#include "src/common/lossy/block_size.h"
#include "src/dec/tile_dec.h"
#include "src/dec/wp2_dec_i.h"
#include "src/utils/ans_utils.h"
#include "src/utils/front_mgr.h"
#include "src/utils/utils.h"
#include "src/wp2/decode.h"
#include "src/wp2/format_constants.h"
namespace WP2 {
//------------------------------------------------------------------------------
SyntaxReader::SyntaxReader(ANSDec* const dec, const Rectangle& rect)
: dec_(dec), width_(rect.width), height_(rect.height) {}
WP2Status SyntaxReader::ReadHeader(const BitstreamFeatures& features,
const GlobalParams& gparams,
const Tile& tile) {
// TODO(skal): use differential coding to update gparam locally
gparams_ = &gparams;
if (gparams_->has_alpha_) {
alpha_reader_.reset(new (WP2Allocable::nothrow) AlphaReader(dec_, tile));
WP2_CHECK_ALLOC_OK(alpha_reader_ != nullptr);
WP2_CHECK_STATUS(alpha_reader_->Allocate());
}
ANSDebugPrefix prefix(dec_, "GlobalHeader");
const bool use_aom = dec_->ReadBool("use_aom_coeffs");
residual_reader_.Init(use_aom);
use_splits_ =
(gparams.partition_snapping_ ? dec_->ReadBool("use_splits") : false);
WP2_CHECK_STATUS(symbols_info_.InitLossy(
(uint32_t)gparams_->segments_.size(), gparams.partition_set_,
gparams_->maybe_use_lossy_alpha_, use_aom, use_splits_));
const uint32_t num_channels = (gparams_->maybe_use_lossy_alpha_ ? 4 : 3);
for (Channel c : {kYChannel, kUChannel, kVChannel, kAChannel}) {
if (c == kAChannel && !gparams_->maybe_use_lossy_alpha_) continue;
WP2_CHECK_STATUS(symbols_info_.SetMinMax(
kSymbolDC, ResidualIO::GetCluster(c, num_channels), /*min=*/0,
/*max=*/gparams_->GetMaxDCRange(c)));
}
symbols_info_.SetClusters(kSymbolDC, symbols_info_.NumClusters(kSymbolDC));
WP2_CHECK_STATUS(sr_.Init(symbols_info_, dec_));
WP2_CHECK_STATUS(sr_.Allocate());
const uint32_t max_num_blocks = SizeBlocks(width_) * SizeBlocks(height_);
const uint32_t min_num_blocks = std::max(1u, max_num_blocks / kMaxBlockSize2);
num_blocks_ =
ReadLargeRange(min_num_blocks, max_num_blocks, dec_, "num_blocks");
num_transforms_ = std::min(num_blocks_ * 4, max_num_blocks);
if (num_blocks_ == 1) {
chroma_subsampling_ = ChromaSubsampling::kSingleBlock;
} else {
chroma_subsampling_ = (ChromaSubsampling)dec_->ReadRValue(
(int)ChromaSubsampling::kSingleBlock, "chroma_subsampling");
}
WP2_CHECK_STATUS(LoadDictionaries());
WP2_CHECK_STATUS(dec_->GetStatus());
return WP2_STATUS_OK;
}
//------------------------------------------------------------------------------
WP2Status SyntaxReader::LoadDictionaries() {
const uint32_t max_num_blocks = SizeBlocks(width_) * SizeBlocks(height_);
if (gparams_->explicit_segment_ids_) {
WP2_CHECK_STATUS(
sr_.ReadHeader(kSymbolSegmentId, num_blocks_, "segment_id"));
}
if (gparams_->use_rnd_mtx_) {
WP2_CHECK_STATUS(sr_.ReadHeader(kSymbolRndMtx0, num_blocks_, "rnd_mtx"));
WP2_CHECK_STATUS(
sr_.ReadHeader(kSymbolUseRandomMatrix, num_blocks_, "use_rnd_mtx"));
}
WP2_CHECK_STATUS(
sr_.ReadHeader(kSymbolSplitTransform, num_blocks_, "split_tf"));
if (chroma_subsampling_ == ChromaSubsampling::kSingleBlock ||
chroma_subsampling_ == ChromaSubsampling::kAdaptive) {
WP2_CHECK_STATUS(sr_.ReadHeader(kSymbolYuv420, num_blocks_, "yuv420"));
}
WP2_CHECK_STATUS(sr_.ReadHeader(kSymbolTransform, num_blocks_, "transform"));
sr_.SetRange(kSymbolModeY, /*min=*/0,
/*max=*/gparams_->y_preds_.GetMaxMode());
WP2_CHECK_STATUS(sr_.ReadHeader(kSymbolModeY, num_blocks_, "mode"));
sr_.SetRange(kSymbolModeUV, /*min=*/0,
/*max=*/gparams_->uv_preds_.GetMaxMode());
WP2_CHECK_STATUS(sr_.ReadHeader(kSymbolModeUV, num_blocks_, "mode"));
const uint32_t num_channels_using_cfl =
gparams_->maybe_use_lossy_alpha_ ? 3 : 2;
WP2_CHECK_STATUS(sr_.ReadHeader(kSymbolCflSlope,
num_channels_using_cfl * num_blocks_, "cfl"));
WP2_CHECK_STATUS(sr_.ReadHeader(kSymbolBlockSize, num_blocks_, "block_size"));
const uint32_t num_coeffs_max =
max_num_blocks * kMinBlockSizePix * kMinBlockSizePix;
const uint32_t num_coeffs_max_uv =
(chroma_subsampling_ == ChromaSubsampling::k420) ? num_coeffs_max / 4
: num_coeffs_max;
WP2_CHECK_STATUS(
segment_ids_.ReadHeader(dec_, (uint32_t)gparams_->segments_.size(),
gparams_->explicit_segment_ids_, width_));
if (gparams_->has_alpha_) {
WP2_CHECK_STATUS(alpha_reader_->ReadHeader(*gparams_));
if (alpha_reader_->GetAlphaMode() == kAlphaModeLossy) {
WP2_CHECK_STATUS(sr_.ReadHeader(kSymbolBlockAlphaMode, num_blocks_,
"block_alpha_mode"));
}
if (alpha_reader_->GetAlphaMode() != kAlphaModeLossless) {
sr_.SetRange(kSymbolModeA, /*min=*/0, /*max=*/kAPredModeNum - 1);
WP2_CHECK_STATUS(sr_.ReadHeader(kSymbolModeA, num_blocks_, "mode"));
}
}
WP2_CHECK_STATUS(residual_reader_.ReadHeader(
&sr_, num_coeffs_max, num_coeffs_max_uv, num_transforms_,
gparams_->maybe_use_lossy_alpha_,
gparams_->has_alpha_ &&
(alpha_reader_->GetAlphaMode() != kAlphaModeLossless)));
sr_.Clear(); // reclaim some tmp memory
return WP2_STATUS_OK;
}
//------------------------------------------------------------------------------
WP2Status SyntaxReader::ReadPredModes(CodedBlockBase* const cb) {
ANSDebugPrefix prefix(dec_, "pred_modes");
cb->y_context_is_constant_ = cb->ContextIsConstant(kYChannel);
// no ReadParams() for kVChannel (supposed to be the same as kUChannel)
for (Channel channel : {kYChannel, kAChannel, kUChannel}) {
if (channel == kAChannel && !cb->HasLossyAlpha()) continue;
const Predictors& preds = gparams_->GetPredictors(channel);
if (channel == kYChannel && cb->y_context_is_constant_) {
cb->GetCodingParams(kYChannel)->pred = preds[0];
} else {
const uint32_t mode =
(channel == kYChannel) ? sr_.Read(kSymbolModeY, "y") :
(channel == kAChannel) ? sr_.Read(kSymbolModeA, "a") :
sr_.Read(kSymbolModeUV, "uv");
const uint32_t sub_mode =
preds.GetPred(mode)->ReadParams(cb, channel, &sr_, dec_);
if (channel == kUChannel) {
// we need to read the 'params' of the v-channel, even if it
// doesn't change the predictor. Useful mostly for SignalingCfl, where
// U and V channel have a different param in cfl_[].
preds.GetPred(mode)->ReadParams(cb, kVChannel, &sr_, dec_);
}
cb->GetCodingParams(channel)->pred = preds.GetPred(mode, sub_mode);
}
}
return WP2_STATUS_OK;
}
//------------------------------------------------------------------------------
void SyntaxReader::ReadSplitTransform(Channel channel,
CodedBlockBase* const cb) {
cb->GetCodingParams(channel)->split_tf =
(channel == kYChannel &&
GetSplitSize(cb->dim(), /*split=*/true) != cb->dim() &&
(bool)sr_.Read(kSymbolSplitTransform, "split_tf"));
}
void SyntaxReader::ReadHasCoeffs(Channel channel, CodedBlockBase* const cb) {
for (uint32_t tf_i = 0; tf_i < cb->GetNumTransforms(channel); ++tf_i) {
// TODO(yguyon): If '!is420_' was signaled, deduce 'has_coeffs'.
// TODO(yguyon): If 'has_lossy_alpha_' was signaled, deduce 'has_coeffs'.
const uint32_t cluster = (uint32_t)channel;
const bool tf_has_coeffs =
(bool)sr_.Read(kSymbolHasCoeffs, cluster, "has_coeffs", nullptr);
// 1 means "at least one coeff is not zero", 0 means "all coeffs are 0".
cb->num_coeffs_[channel][tf_i] = tf_has_coeffs ? 1 : 0;
}
}
void SyntaxReader::ReadTransform(Channel channel, CodedBlockBase* const cb) {
CodedBlockBase::CodingParams* params = cb->GetCodingParams(channel);
if (channel == kUChannel || channel == kAChannel) {
params->tf = kDctDct;
} else if (channel == kVChannel) {
assert(params->tf == kDctDct);
} else if (cb->HasCoeffs(channel)) {
params->tf = (TransformPair)sr_.Read(kSymbolTransform, "transform");
} else {
// 0 coeff, transform does not matter.
// TODO(maryla): we could have a noop transform to avoid doing any
// computation.
params->tf = kIdentityIdentity;
}
}
void SyntaxReader::ReadIs420(CodedBlockBase* const cb) {
dec_->PushBitTracesCustomPrefix("is_420");
if (chroma_subsampling_ == ChromaSubsampling::kSingleBlock ||
chroma_subsampling_ == ChromaSubsampling::kAdaptive) {
cb->is420_ = (bool)sr_.Read(kSymbolYuv420, "yuv420");
} else if (chroma_subsampling_ == ChromaSubsampling::k420) {
cb->is420_ = true;
} else if (chroma_subsampling_ == ChromaSubsampling::k444) {
cb->is420_ = false;
} else {
assert(false);
}
dec_->PopBitTracesCustomPrefix("is_420");
}
WP2Status SyntaxReader::GetBlock(CodedBlockBase* const cb, YUVPlane* const out,
BlockInfo* const info) {
// Read some block data.
dec_->AddDebugPrefix("BlockHeader");
// Initialize the views now that 'blk_.dim' is known.
cb->SetContextInput(*out); // Predict from other reconstructed pixels.
cb->SetReconstructedOutput(out);
dec_->PushBitTracesCustomPrefix("segment_id");
segment_ids_.ReadId(&sr_, cb); // Look at surrounding availability.
dec_->PopBitTracesCustomPrefix("segment_id");
if (gparams_->has_alpha_) {
alpha_reader_->GetBlockHeader(&sr_, cb);
} else {
cb->alpha_mode_ = kBlockAlphaLossless;
}
// Decode the predictor used and thus get access to the implicit tf, if any.
dec_->PushBitTracesCustomPrefix("mode");
WP2_CHECK_STATUS(ReadPredModes(cb));
dec_->PopBitTracesCustomPrefix("mode");
// Bound the number of coeffs to decode for chroma.
ReadIs420(cb);
// If there is at least one coeff to read and if the transform is not
// implicit, decode the transform. The coeff method may differ depending on
// the transform used.
dec_->PushBitTracesCustomPrefix("transforms");
for (Channel channel : {kYChannel, kUChannel, kVChannel, kAChannel}) {
if (channel == kAChannel && !cb->HasLossyAlpha()) continue;
ReadSplitTransform(channel, cb);
ReadHasCoeffs(channel, cb);
ReadTransform(channel, cb);
}
dec_->PopBitTracesCustomPrefix("transforms");
// Signal the method with which coeffs were encoded.
if (!residual_reader_.use_aom()) {
ANSDebugPrefix prefix(dec_, "coeff_method");
const Segment& segment = gparams_->segments_[cb->id_];
WP2_CHECK_STATUS(segment.ReadEncodingMethods(&sr_, cb));
}
// Read random matrix.
// TODO(skal): check rnd_mtx behavior when no coeff and/or identity transform
dec_->PushBitTracesCustomPrefix("rnd_mtx");
if (gparams_->use_rnd_mtx_) {
#if defined(ENABLE_RND_MTX)
const bool can_use =
gparams_->mtx_set_.IsOk(cb->mtx_[kYChannel], cb->tdim(kYChannel));
cb->use_mtx_ =
can_use && (bool)sr_.Read(kSymbolUseRandomMatrix, "use_rnd_mtx");
if (cb->use_mtx_) {
cb->mtx_[kYChannel] = (uint8_t)sr_.Read(kSymbolRndMtx0, "rnd_mtx_y");
}
#else
cb->use_mtx_ = false;
#endif
}
dec_->PopBitTracesCustomPrefix("rnd_mtx");
dec_->PopDebugPrefix();
// Read coefficients.
for (Channel c : {kYChannel, kUChannel, kVChannel, kAChannel}) {
if (c == kAChannel && !cb->HasLossyAlpha()) continue;
dec_->PushBitTracesCustomPrefix(kCoeffsStr[c]);
const Segment& segment =
gparams_->segments_[(c == kAChannel) ? 0 : cb->id_];
const int16_t* const dequant = segment.GetQuant(c).Dequant();
WP2_CHECK_STATUS(
residual_reader_.ReadCoeffs(c, dequant, &sr_, cb, info));
dec_->PopBitTracesCustomPrefix(kCoeffsStr[c]);
}
// alpha
if (gparams_->has_alpha_) {
dec_->PushBitTracesCustomPrefix("alpha");
WP2_CHECK_STATUS(alpha_reader_->GetBlock(cb));
dec_->PopBitTracesCustomPrefix("alpha");
}
WP2_CHECK_STATUS(dec_->GetStatus());
return WP2_STATUS_OK;
}
BlockSize SyntaxReader::GetBlockSize(const FrontMgrDoubleOrderBase& mgr) {
assert(!use_splits_);
ANSDebugPrefix prefix(dec_, "BlockHeader");
return ReadBlockSize(mgr, &sr_);
}
WP2Status SyntaxReader::GetBlockSplit(const SplitIteratorBase& it,
uint32_t* const split_idx) {
assert(use_splits_);
ANSDebugPrefix prefix(dec_, "BlockHeader");
return ReadBlockSplit(it, &sr_, split_idx);
}
//------------------------------------------------------------------------------
} // namespace WP2