blob: 09b55879734d4ae0251c0007e4ac9957a57248c5 [file] [log] [blame]
// Copyright 2020 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
//
// 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.
// Test SyntaxWriter class.
#include "imageio/image_dec.h"
#include "include/helpers.h"
#include "src/common/global_params.h"
#include "src/enc/analysis.h"
#include "src/enc/wp2_enc_i.h"
#include "src/utils/ans.h"
#include "src/utils/vector.h"
#include "src/wp2/base.h"
#include "src/wp2/encode.h"
namespace WP2 {
namespace {
//------------------------------------------------------------------------------
// Verifies SyntaxWriter::CopyFrom() creates a valid deep clone.
TEST(TestSyntaxWriter, CopyFrom) {
WP2DspReset();
WP2QuantizeInit();
WP2MathInit();
ANSInit();
// Settings.
const EncoderConfig& config = EncoderConfig::kDefault;
const Rectangle tile_rect = {0, 0, 64, 64};
const std::array<BlockSize, 4> kBlocks = {BLK_8x16, BLK_4x4, BLK_4x4,
BLK_16x8};
const uint32_t kCopyAtBlockIndex = kBlocks.size() / 2;
// Load the sample image and convert it to luma/chroma.
ArgbBuffer rgb;
ASSERT_WP2_OK(
ReadImage(testutil::GetTestDataPath("source1.png").c_str(), &rgb));
ASSERT_WP2_OK(rgb.SetView(rgb, tile_rect));
CSPTransform csp_transform;
YUVPlane yuv, out;
ASSERT_WP2_OK(yuv.Import(rgb, rgb.HasTransparency(), csp_transform,
/*resize_if_needed=*/true));
ASSERT_WP2_OK(
out.Resize(yuv.GetWidth(), yuv.GetHeight(), /*pad=*/1, yuv.HasAlpha()));
// Create and initialize mockups.
GlobalParams gparams;
FeatureMap features;
gparams.features_ = &features;
ASSERT_WP2_OK(GlobalAnalysis(rgb, yuv, csp_transform, config, &gparams));
ContextCache context_cache;
// Declare a SyntaxWriter instance and a clone; the latter outlives the former
// to verify there is no hidden dependency left.
SyntaxWriter syntax_writer_copy;
ANSDictionaries dicts_copy;
// Instances that will be needed after 'syntax_writer' is deleted.
Vector<CodedBlock> cblocks;
Vector_u16 size_order_indices;
FrontMgrLexico mgr;
float cost, cost_dicts;
uint32_t num_tokens;
Vector_u8 buffer;
{
SyntaxWriter syntax_writer;
ANSDictionaries dicts;
ASSERT_WP2_OK(syntax_writer.Init(&dicts, config, gparams, yuv,
ChromaSubsampling::k420, tile_rect,
kBlocks.size(), /*use_aom_coeffs=*/true,
/*use_splits=*/false, ProgressRange()));
ASSERT_WP2_OK(syntax_writer.InitPass());
// Encode a bunch of blocks.
ASSERT_WP2_OK(mgr.Init(config.partition_set, /*snapped=*/false,
tile_rect.width, tile_rect.height));
for (uint32_t i = 0; i < kBlocks.size(); ++i) {
Block block;
ASSERT_TRUE(mgr.TryGetNextBlock(kBlocks[i], &block));
ASSERT_TRUE(cblocks.resize(cblocks.size() + 1));
ASSERT_TRUE(size_order_indices.resize(cblocks.size()));
std::iota(size_order_indices.begin(), size_order_indices.end(), 0);
CodedBlock& cb = cblocks.back();
cb.is420_ = false;
cb.id_ = AssignSegmentId(config, gparams, tile_rect, block);
cb.SetRange(gparams.transf_.GetYUVMin(), gparams.transf_.GetYUVMax());
cb.SetDim(block, mgr);
cb.GetCodingParams(kYChannel)->pred = gparams.y_preds_[0];
cb.GetCodingParams(kUChannel)->pred = gparams.uv_preds_[0];
if (!gparams.a_preds_.empty()) {
cb.GetCodingParams(kAChannel)->pred = gparams.a_preds_[0];
}
cb.SetSrcInput(yuv);
cb.SetContextInput(out, &context_cache);
cb.SetReconstructedOutput(&out);
cb.y_context_is_constant_ = false;
cb.QuantizeAll(config, syntax_writer.context(), gparams.segments_[cb.id_],
kYChannel, false, syntax_writer.counters());
cb.QuantizeAll(config, syntax_writer.context(), gparams.segments_[cb.id_],
kUChannel, cb.is420_, syntax_writer.counters());
cb.QuantizeAll(config, syntax_writer.context(), gparams.segments_[cb.id_],
kVChannel, cb.is420_, syntax_writer.counters());
if (!gparams.a_preds_.empty()) {
cb.QuantizeAll(config, syntax_writer.context(),
gparams.segments_[cb.id_], kAChannel, false,
syntax_writer.counters());
}
// All blocks before 'kCopyAtBlockIndex' are only recorded by
// 'syntax_writer', all blocks after are also recorded by the clone.
if (i == kCopyAtBlockIndex) {
ASSERT_WP2_OK(dicts_copy.CopyFrom(dicts));
ASSERT_WP2_OK(syntax_writer_copy.CopyFrom(syntax_writer, &dicts_copy));
}
syntax_writer.FindBestEncodingMethods(&cb);
syntax_writer.Record(cb);
syntax_writer.RecordSize(mgr, cb.dim());
if (i >= kCopyAtBlockIndex) {
syntax_writer_copy.FindBestEncodingMethods(&cb);
syntax_writer_copy.Record(cb);
syntax_writer_copy.RecordSize(mgr, cb.dim());
}
ASSERT_TRUE(mgr.UseSize(block.dim(), &block));
mgr.Use(block);
}
ANSEnc enc;
mgr.Clear();
ASSERT_WP2_OK(syntax_writer.WriteHeader(&enc));
ASSERT_WP2_OK(
syntax_writer.WriteBlocks(cblocks, size_order_indices, &mgr, &enc));
cost = enc.GetCost();
cost_dicts = enc.GetCost(dicts);
num_tokens = enc.NumTokens();
ASSERT_WP2_OK(enc.AssembleToBitstream());
Vector_u8 tmp;
ASSERT_WP2_OK(enc.WriteBitstreamTo(tmp));
ASSERT_TRUE(buffer.resize(enc.GetBitstreamSize()));
std::copy(tmp.begin(), tmp.end(), buffer.data());
}
// 'syntax_writer' does not exist anymore, compare the clone output now.
ANSEnc enc_copy;
mgr.Clear();
ASSERT_WP2_OK(syntax_writer_copy.WriteHeader(&enc_copy));
ASSERT_WP2_OK(syntax_writer_copy.WriteBlocks(cblocks, size_order_indices,
&mgr, &enc_copy));
ASSERT_EQ(cost, enc_copy.GetCost());
ASSERT_EQ(cost_dicts, enc_copy.GetCost(dicts_copy));
ASSERT_EQ(num_tokens, enc_copy.NumTokens());
ASSERT_WP2_OK(enc_copy.AssembleToBitstream());
Vector_u8 tmp;
ASSERT_WP2_OK(enc_copy.WriteBitstreamTo(tmp));
ASSERT_EQ(buffer.size(), enc_copy.GetBitstreamSize());
ASSERT_TRUE(std::equal(tmp.begin(), tmp.end(), buffer.begin()));
}
//------------------------------------------------------------------------------
} // namespace
} // namespace WP2