blob: 2d6f4f847457b3269075082b7ce3ffcd4d02af92 [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 CodedBlock.
#include "include/helpers.h"
#include "src/common/lossy/block.h"
#include "src/common/lossy/block_size.h"
#include "src/common/lossy/transforms.h"
#include "src/enc/wp2_enc_i.h"
#include "src/utils/plane.h"
#include "src/utils/random.h"
namespace WP2 {
namespace {
using ::testing::ElementsAre;
struct FrontMgrLexicoTest {
typedef FrontMgrLexico Type;
static const uint32_t kId = 0;
};
struct FrontMgrMaxTest {
typedef FrontMgrMax Type;
static const uint32_t kId = 1;
};
typedef ::testing::Types<FrontMgrLexicoTest, FrontMgrMaxTest> FrontMgrs;
template <class T> class CodedBlockTest : public ::testing::Test {
void SetUp() override {
WP2DspReset();
WP2QuantizeInit();
}
};
TYPED_TEST_SUITE(CodedBlockTest, FrontMgrs);
TYPED_TEST(CodedBlockTest, FindBestModes) {
WP2::ANSInit();
WP2MathInit();
WP2PSNRInit();
WP2TransformInit();
WP2::PredictionInit();
constexpr uint32_t kHasAlpha = false;
constexpr uint32_t kNumChannels = (kHasAlpha ? 4 : 3);
const uint32_t height = kPredHeight * 2;
const uint32_t width = kPredWidth * 2;
const Rectangle tile_rect = {0, 0, width, height};
typename TypeParam::Type mgr;
EXPECT_WP2_OK(mgr.Init(ALL_RECTS, /*snapped=*/false, width, height));
const int16_t v1 = 10;
const int16_t v2 = 11;
YUVPlane in;
ASSERT_WP2_OK(in.Resize(width, height));
in.Y.Fill(v1);
in.Y.Fill({0, 0, kPredWidth, kPredHeight}, v2);
YUVPlane out;
ASSERT_WP2_OK(out.Resize(width, height));
CodedBlock cb;
cb.id_ = 0;
cb.SetRange(/*yuv_min=*/-512, /*yuv_max=*/511);
cb.SetDim(/*block=*/{/*x=*/0, /*y=*/0, /*dim=*/BLK_8x8}, mgr);
cb.SetSrcInput(in);
// No context is available so no SetContextInput().
cb.SetReconstructedOutput(&out);
ContextCache context_cache;
cb.SetContextCache(&context_cache);
Vector_u8 fake_map;
ASSERT_TRUE(fake_map.resize(width * height));
for (auto& m : fake_map) m = 0;
YPredictors predictors;
ASSERT_WP2_OK(predictors.Fill(/*min_value=*/-512, /*max_value=*/511));
Segment segment;
ASSERT_WP2_OK(segment.AllocateForEncoder());
const uint16_t quants[kNumQuantZones] = {128, 128, 128, 128};
segment.quant_y_.Init(/*max_residual=*/1024, /*q_scale=*/1, quants);
// Force lambda value to make test less brittle.
segment.quant_y_.SetLambda(1.f);
SymbolsInfo symbols_info;
ASSERT_WP2_OK(
symbols_info.InitLossy(/*num_segments=*/1, ALL_RECTS, kHasAlpha,
GetQualityHint(EncoderConfig::kDefault.quality),
/*use_aom_coeffs=*/false));
SymbolRecorder recorder;
symbols_info.SetInfo(kSymbolModeY, /*min=*/0, /*max=*/kAPredModeNum - 1,
/*num_clusters=*/1, SymbolsInfo::StorageMethod::kAuto);
ASSERT_WP2_OK(recorder.Allocate(symbols_info, /*num_records=*/0));
Counters counters;
ASSERT_WP2_OK(counters.Init(recorder));
Vector<TransformPair> transforms;
ASSERT_TRUE(transforms.push_back(kDctDct));
BlockContext context;
WP2_ASSERT_STATUS(context.Init(/*use_aom=*/false));
ASSERT_WP2_OK(cb.FindBestPredTf(
EncoderConfig::kDefault, tile_rect, predictors, segment, context,
kYChannel, kNumChannels, /*reduced=*/false, transforms, &counters));
}
// Turns two coordinates into a number.
static int16_t Pix(uint32_t x, uint32_t y) {
assert(y < 100);
return x * 100 + y;
}
template <class T>
static void SetCoords4x4(uint32_t num_blocks, uint32_t x, uint32_t y,
T* const mgr, YUVPlane* const yuv,
CodedBlock* const cb) {
for (uint32_t i = 0; i < num_blocks; ++i) {
Block blk;
ASSERT_TRUE(mgr->UseSize(BLK_4x4, /*ind=*/1u, &blk));
mgr->Use(blk);
}
cb->SetDim(/*block=*/{x, y, /*dim=*/BLK_4x4}, *mgr);
cb->SetSrcInput(*yuv);
cb->SetContextInput(*yuv);
cb->SetReconstructedOutput(yuv);
}
// Expects that the first elements of 'array' match the given list.
#define EXPECT_THAT_FIRST_ELEMENTS_ARE(array, ...) \
do { \
int16_t truncated[]{__VA_ARGS__}; \
const uint32_t num = (sizeof(truncated) / sizeof(truncated[0])); \
std::copy(array, array + num, truncated); \
EXPECT_THAT(truncated, ElementsAre(__VA_ARGS__)); \
} while (0)
TYPED_TEST(CodedBlockTest, GetContext_FirstBlock) {
const uint32_t kSize = 16;
YUVPlane yuv;
ASSERT_WP2_OK(yuv.Resize(kSize, kSize));
for (uint32_t j = 0; j < kSize; ++j) {
for (uint32_t i = 0; i < kSize; ++i) yuv.Y.At(i, j) = Pix(i, j);
}
yuv.U.Fill(0);
yuv.V.Fill(0);
typename TypeParam::Type mgr;
EXPECT_WP2_OK(mgr.Init(ALL_RECTS, /*snapped=*/false, kSize, kSize));
CodedBlock cb;
cb.SetRange(/*yuv_min=*/-512, /*yuv_max=*/512);
cb.SetDim(/*block=*/{/*x=*/0, /*y=*/0, /*dim=*/BLK_16x8}, mgr);
cb.SetSrcInput(yuv);
ContextCache context_cache;
cb.SetContextInput(yuv, &context_cache);
cb.SetReconstructedOutput(&yuv);
const int16_t missing = CodedBlock::kMissing;
for (ContextType context_type : {kContextSmall, kContextExtendRight}) {
EXPECT_THAT_FIRST_ELEMENTS_ARE(cb.GetContext(kYChannel, context_type),
// Left.
0, 0, 0, 0, 0, 0, 0, 0,
// Top left.
0,
// Top.
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
// Top right.
0,
// Right.
0, 0, 0, 0, 0, 0, 0, 0);
}
EXPECT_THAT_FIRST_ELEMENTS_ARE(cb.GetContext(kYChannel, kContextExtendLeft),
0, 0, 0, 0, 0, 0, 0, 0, // Extended left.
0, 0, 0, 0, 0, 0, 0, 0); // Left.
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, kContextSmallNoFillIn),
// Left.
missing, missing, missing, missing, missing, missing, missing, missing,
// Top left.
missing,
// Top.
missing, missing, missing, missing, missing, missing, missing, missing,
missing, missing, missing, missing, missing, missing, missing, missing,
// Top right.
missing,
// Right.
missing, missing, missing, missing, missing, missing, missing, missing);
// This is the first block of the image. The first (top left) sub-block has
// therefore zero context.
SetCoords4x4(/*num_blocks=*/0, /*x=*/0, /*y=*/0, &mgr, &yuv, &cb);
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, kContextSmallNoFillIn), missing, missing,
missing, missing, // Left.
missing, // Top left.
missing, missing, missing, missing, // Top.
missing, // Top right.
missing, missing, missing, missing); // Right.
EXPECT_THAT_FIRST_ELEMENTS_ARE(cb.GetContext(kYChannel, kContextSmall),
0, 0, 0, 0, // Left.
0, // Top left.
0, 0, 0, 0, // Top.
0, // Top right.
0, 0, 0, 0); // Right.
// Top middle sub block. Only left context is available.
SetCoords4x4(/*num_blocks=*/1, /*x=*/1, /*y=*/0, &mgr, &yuv, &cb);
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, kContextSmallNoFillIn), Pix(3, 3), Pix(3, 2),
Pix(3, 1), Pix(3, 0), // Left.
missing, // Top left.
missing, missing, missing, missing, // Top.
missing, // Top right.
missing, missing, missing, missing); // Right.
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, kContextSmall), Pix(3, 3), Pix(3, 2), Pix(3, 1),
Pix(3, 0), // Left.
Pix(3, 0), // Top left.
Pix(3, 0), Pix(3, 0), Pix(3, 0), Pix(3, 0), // Top.
Pix(3, 0), // Top right.
Pix(3, 0), Pix(3, 0), Pix(3, 0), Pix(3, 0)); // Right.
// Bottom left sub block. Only top context is available.
SetCoords4x4(/*num_blocks=*/3, /*x=*/0, /*y=*/1, &mgr, &yuv, &cb);
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, kContextSmallNoFillIn), missing, missing,
missing, missing, // Left.
missing, // Top left.
Pix(0, 3), Pix(1, 3), Pix(2, 3), Pix(3, 3), // Top.
Pix(4, 3), // Top right.
missing, missing, missing, missing); // Right.
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, kContextSmall), Pix(0, 3), Pix(0, 3), Pix(0, 3),
Pix(0, 3), // Left.
Pix(0, 3), // Top left.
Pix(0, 3), Pix(1, 3), Pix(2, 3), Pix(3, 3), // Top.
Pix(4, 3), // Top right.
Pix(4, 3), Pix(4, 3), Pix(4, 3), Pix(4, 3)); // Right.
// Bottom middle sub block. Left and top contexts are available.
SetCoords4x4(/*num_blocks=*/1, /*x=*/1, /*y=*/1, &mgr, &yuv, &cb);
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, kContextSmallNoFillIn), Pix(3, 7), Pix(3, 6),
Pix(3, 5), Pix(3, 4), // Left.
Pix(3, 3), // Top left.
Pix(4, 3), Pix(5, 3), Pix(6, 3), Pix(7, 3), // Top.
Pix(8, 3), // Top right.
missing, missing, missing, missing); // Right.
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, kContextSmall), Pix(3, 7), Pix(3, 6), Pix(3, 5),
Pix(3, 4), // Left.
Pix(3, 3), // Top left.
Pix(4, 3), Pix(5, 3), Pix(6, 3), Pix(7, 3), // Top.
Pix(8, 3), // Top right.
Pix(8, 3), Pix(8, 3), Pix(8, 3), Pix(8, 3)); // Right.
}
TYPED_TEST(CodedBlockTest, GetContext_HasRight) {
const uint32_t kSize = 16;
YUVPlane yuv;
ASSERT_WP2_OK(yuv.Resize(kSize, kSize));
for (uint32_t j = 0; j < kSize; ++j) {
for (uint32_t i = 0; i < kSize; ++i) yuv.Y.At(i, j) = Pix(i, j);
}
// The image has 6 blocks, laid out like this:
// 1 2 2 3
// 1 4 4 3
// 5 4 4 6
typename TypeParam::Type mgr;
EXPECT_WP2_OK(mgr.Init(ALL_RECTS, /*snapped=*/false, kSize, kSize));
Block blk;
// Mark block 1 as seen.
ASSERT_TRUE(mgr.UseSize(BLK_4x8, /*ind=*/0u, &blk));
mgr.Use(blk);
// Test extended left context on block 2.
CodedBlock cb;
cb.SetRange(/*yuv_min=*/-512, /*yuv_max=*/512);
cb.SetDim(/*block=*/{/*x=*/1, /*y=*/0, /*dim=*/BLK_8x4}, mgr);
cb.SetSrcInput(yuv);
ContextCache context_cache;
cb.SetContextInput(yuv, &context_cache);
int8_t left, right, top_context_extent;
cb.GetOccupancy(&left, &right, &top_context_extent);
EXPECT_EQ(left, 2);
EXPECT_EQ(right, 0);
EXPECT_EQ(top_context_extent, 0);
EXPECT_THAT_FIRST_ELEMENTS_ARE(cb.GetContext(kYChannel, kContextExtendLeft),
// Extended left context.
Pix(3, 7), Pix(3, 6), Pix(3, 5), Pix(3, 4),
// Left.
Pix(3, 3), Pix(3, 2), Pix(3, 1), Pix(3, 0));
// Mark block 2 as seen.
ASSERT_TRUE(mgr.UseSize(BLK_8x4, /*ind=*/1u, &blk));
mgr.Use(blk);
// Test an imaginary 4x4 block at (0, 1).
cb.SetDim(/*block=*/{/*x=*/0, /*y=*/1, /*dim=*/BLK_4x4}, mgr);
cb.SetSrcInput(yuv);
cb.SetContextInput(yuv, &context_cache);
context_cache.Reset();
cb.GetOccupancy(&left, &right, &top_context_extent);
EXPECT_EQ(left, -1);
EXPECT_EQ(right, 0);
EXPECT_EQ(top_context_extent, 2);
// Mark block 3 as seen.
ASSERT_TRUE(mgr.UseSize(BLK_4x8, /*ind=*/2u, &blk));
mgr.Use(blk);
// Block number 4.
cb.SetDim(/*block=*/{/*x=*/1, /*y=*/1, /*dim=*/BLK_8x8}, mgr);
cb.SetSrcInput(yuv);
cb.SetContextInput(yuv, &context_cache);
context_cache.Reset();
cb.GetOccupancy(&left, &right, &top_context_extent);
EXPECT_EQ(left, 1);
EXPECT_EQ(right, 1);
EXPECT_EQ(top_context_extent, 1);
const int16_t missing = CodedBlock::kMissing;
// Check full context.
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, kContextSmallNoFillIn),
// Left.
missing, missing, missing, missing, Pix(3, 7), Pix(3, 6), Pix(3, 5),
Pix(3, 4),
// Top left
Pix(3, 3),
// Top
Pix(4, 3), Pix(5, 3), Pix(6, 3), Pix(7, 3), Pix(8, 3), Pix(9, 3),
Pix(10, 3), Pix(11, 3),
// Top right
Pix(12, 3),
// Right
Pix(12, 4), Pix(12, 5), Pix(12, 6), Pix(12, 7), missing, missing, missing,
missing);
EXPECT_THAT_FIRST_ELEMENTS_ARE(cb.GetContext(kYChannel, kContextSmall),
// Left
Pix(3, 7), Pix(3, 7), Pix(3, 7), Pix(3, 7),
Pix(3, 7), Pix(3, 6), Pix(3, 5), Pix(3, 4),
// Top left
Pix(3, 3),
// Top
Pix(4, 3), Pix(5, 3), Pix(6, 3), Pix(7, 3),
Pix(8, 3), Pix(9, 3), Pix(10, 3), Pix(11, 3),
// Top right
Pix(12, 3),
// Right
Pix(12, 4), Pix(12, 5), Pix(12, 6), Pix(12, 7),
Pix(12, 7), Pix(12, 7), Pix(12, 7),
Pix(12, 7));
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, kContextExtendRight),
// Left (first 4 pixels are missing and filled in).
Pix(3, 7), Pix(3, 7), Pix(3, 7), Pix(3, 7), Pix(3, 7), Pix(3, 6),
Pix(3, 5), Pix(3, 4),
// Top left
Pix(3, 3),
// Top
Pix(4, 3), Pix(5, 3), Pix(6, 3), Pix(7, 3), Pix(8, 3), Pix(9, 3),
Pix(10, 3), Pix(11, 3),
// Extended top right
Pix(12, 3), Pix(13, 3), Pix(14, 3), Pix(15, 3),
// Further (missing, filled in)
Pix(15, 3), Pix(15, 3), Pix(15, 3), Pix(15, 3), Pix(15, 3));
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, kContextExtendLeft),
// Extended left context (missing, filled in).
Pix(3, 7), Pix(3, 7), Pix(3, 7), Pix(3, 7), Pix(3, 7), Pix(3, 7),
Pix(3, 7), Pix(3, 7),
// Left (first 4 pixels are missing and filled in).
Pix(3, 7), Pix(3, 7), Pix(3, 7), Pix(3, 7), Pix(3, 7), Pix(3, 6),
Pix(3, 5), Pix(3, 4));
// Top left block. Left and top contexts are available
// (from previously decoded blocks 1 and 2).
SetCoords4x4(/*num_blocks=*/0, /*x=*/1, /*y=*/1, &mgr, &yuv, &cb);
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, kContextSmallNoFillIn), Pix(3, 7), Pix(3, 6),
Pix(3, 5), Pix(3, 4), // Left.
Pix(3, 3), // Top left.
Pix(4, 3), Pix(5, 3), Pix(6, 3), Pix(7, 3), // Top.
Pix(8, 3), // Top right.
missing, missing, missing, missing); // Right.
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, kContextSmall), Pix(3, 7), Pix(3, 6), Pix(3, 5),
Pix(3, 4), // Left.
Pix(3, 3), // Top left.
Pix(4, 3), Pix(5, 3), Pix(6, 3), Pix(7, 3), // Top.
Pix(8, 3), // Top right.
Pix(8, 3), Pix(8, 3), Pix(8, 3), Pix(8, 3)); // Right.
// Top right sub block. Full context (left, top and right) is available.
SetCoords4x4(/*num_blocks=*/1, /*x=*/2, /*y=*/1, &mgr, &yuv, &cb);
if (TypeParam::kId == 0) {
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, kContextSmallNoFillIn), Pix(7, 7), Pix(7, 6),
Pix(7, 5), Pix(7, 4), // Left.
Pix(7, 3), // Top left.
Pix(8, 3), Pix(9, 3), Pix(10, 3), Pix(11, 3), // Top.
Pix(12, 3), // Top right.
Pix(12, 4), Pix(12, 5), Pix(12, 6), Pix(12, 7)); // Right.
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, kContextSmall), Pix(7, 7), Pix(7, 6),
Pix(7, 5), Pix(7, 4), // Left.
Pix(7, 3), // Top left.
Pix(8, 3), Pix(9, 3), Pix(10, 3), Pix(11, 3), // Top.
Pix(12, 3), // Top right.
Pix(12, 4), Pix(12, 5), Pix(12, 6), Pix(12, 7)); // Right.
} else {
assert(TypeParam::kId == 1);
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, kContextSmallNoFillIn), missing, missing,
missing, missing, // Left.
Pix(7, 3), // Top left.
Pix(8, 3), Pix(9, 3), Pix(10, 3), Pix(11, 3), // Top.
Pix(12, 3), // Top right.
Pix(12, 4), Pix(12, 5), Pix(12, 6), Pix(12, 7)); // Right.
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, kContextSmall), Pix(7, 3), Pix(7, 3),
Pix(7, 3), Pix(7, 3), // Left.
Pix(7, 3), // Top left.
Pix(8, 3), Pix(9, 3), Pix(10, 3), Pix(11, 3), // Top.
Pix(12, 3), // Top right.
Pix(12, 4), Pix(12, 5), Pix(12, 6), Pix(12, 7)); // Right.
}
// Bottom left sub block. Only top context is available.
SetCoords4x4(/*num_blocks=*/1, /*x=*/1, /*y=*/2, &mgr, &yuv, &cb);
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, kContextSmallNoFillIn), missing, missing,
missing, missing, // Left.
Pix(3, 7), // Top left.
Pix(4, 7), Pix(5, 7), Pix(6, 7), Pix(7, 7), // Top.
Pix(8, 7), // Top right.
missing, missing, missing, missing); // Right.
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, kContextSmall), Pix(3, 7), Pix(3, 7), Pix(3, 7),
Pix(3, 7), // Left.
Pix(3, 7), // Top left.
Pix(4, 7), Pix(5, 7), Pix(6, 7), Pix(7, 7), // Top.
Pix(8, 7), // Top right.
Pix(8, 7), Pix(8, 7), Pix(8, 7), Pix(8, 7)); // Right.
// Bottom right sub block. Left and top contexts are available.
SetCoords4x4(/*num_blocks=*/2, /*x=*/2, /*y=*/2, &mgr, &yuv, &cb);
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, kContextSmallNoFillIn), Pix(7, 11), Pix(7, 10),
Pix(7, 9), Pix(7, 8), // Left.
Pix(7, 7), // Top left.
Pix(8, 7), Pix(9, 7), Pix(10, 7), Pix(11, 7), // Top.
Pix(12, 7), // Top right.
missing, missing, missing, missing); // Right.
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, kContextSmall), Pix(7, 11), Pix(7, 10),
Pix(7, 9), Pix(7, 8), // Left.
Pix(7, 7), // Top left.
Pix(8, 7), Pix(9, 7), Pix(10, 7), Pix(11, 7), // Top.
Pix(12, 7), // Top right.
Pix(12, 7), Pix(12, 7), Pix(12, 7), Pix(12, 7)); // Right.
}
TYPED_TEST(CodedBlockTest, GetContext_Large) {
// The grid is 16 by 16 and we only add 8x8 blocks. We will number them:
// 0 1
// 2 3
const uint32_t kSize = 16;
YUVPlane yuv;
ASSERT_WP2_OK(yuv.Resize(kSize, kSize));
for (uint32_t j = 0; j < kSize; ++j) {
for (uint32_t i = 0; i < kSize; ++i) yuv.Y.At(i, j) = Pix(i, j);
}
typename TypeParam::Type mgr;
EXPECT_WP2_OK(mgr.Init(ALL_RECTS, /*snapped=*/false, kSize, kSize));
// Add block 0.
ASSERT_TRUE(mgr.UseSize(BLK_8x8, /*ind=*/0u, /*block=*/nullptr));
// Create block 2.
CodedBlock cb;
cb.SetRange(/*yuv_min=*/-512, /*yuv_max=*/512);
cb.SetDim(/*block=*/{/*x=*/0, /*y=*/2, /*dim=*/BLK_8x8}, mgr);
cb.SetSrcInput(yuv);
ContextCache context_cache;
cb.SetContextInput(yuv, &context_cache);
int8_t left, right, top_context_extent;
cb.GetOccupancy(&left, &right, &top_context_extent);
EXPECT_EQ(left, -1);
EXPECT_EQ(right, -1);
EXPECT_EQ(top_context_extent, 0);
const int16_t missing = CodedBlock::kMissing;
// The context of block 2 is only the bottom of block 0.
for (ContextType context_type : {kContextSmall, kContextExtendRight}) {
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, context_type),
// Left.
Pix(0, 7), Pix(0, 7), Pix(0, 7), Pix(0, 7), Pix(0, 7), Pix(0, 7),
Pix(0, 7), Pix(0, 7), Pix(0, 7),
// Top.
Pix(0, 7), Pix(1, 7), Pix(2, 7), Pix(3, 7), Pix(4, 7), Pix(5, 7),
Pix(6, 7), Pix(7, 7),
// Right.
Pix(7, 7), Pix(7, 7), Pix(7, 7), Pix(7, 7), Pix(7, 7), Pix(7, 7),
Pix(7, 7), Pix(7, 7), Pix(7, 7));
}
EXPECT_THAT_FIRST_ELEMENTS_ARE(cb.GetContext(kYChannel, kContextExtendLeft),
// Extended left.
Pix(0, 7), Pix(0, 7), Pix(0, 7), Pix(0, 7),
Pix(0, 7), Pix(0, 7), Pix(0, 7), Pix(0, 7),
// Left.
Pix(0, 7), Pix(0, 7), Pix(0, 7), Pix(0, 7),
Pix(0, 7), Pix(0, 7), Pix(0, 7), Pix(0, 7));
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, kContextSmallNoFillIn),
// Left.
missing, missing, missing, missing, missing, missing, missing, missing,
missing,
// Top.
Pix(0, 7), Pix(1, 7), Pix(2, 7), Pix(3, 7), Pix(4, 7), Pix(5, 7),
Pix(6, 7), Pix(7, 7),
// Right.
missing, missing, missing, missing, missing, missing, missing, missing,
missing);
// Add block 1 and redefine block 2.
Block blk;
ASSERT_TRUE(mgr.UseSize(BLK_8x8, /*ind=*/1u, &blk));
mgr.Use(blk);
cb.SetDim({/*x=*/0, /*y=*/2, /*dim=*/BLK_8x8}, mgr);
cb.ResetContextCache();
// The context of block 2 now has a top right context.
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, kContextSmallNoFillIn),
// Left.
missing, missing, missing, missing, missing, missing, missing, missing,
missing,
// Top.
Pix(0, 7), Pix(1, 7), Pix(2, 7), Pix(3, 7), Pix(4, 7), Pix(5, 7),
Pix(6, 7), Pix(7, 7),
// Right.
Pix(8, 7), missing, missing, missing, missing, missing, missing, missing,
missing);
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, kContextSmall),
// Left.
Pix(0, 7), Pix(0, 7), Pix(0, 7), Pix(0, 7), Pix(0, 7), Pix(0, 7),
Pix(0, 7), Pix(0, 7), Pix(0, 7),
// Top.
Pix(0, 7), Pix(1, 7), Pix(2, 7), Pix(3, 7), Pix(4, 7), Pix(5, 7),
Pix(6, 7), Pix(7, 7),
// Right.
Pix(8, 7), Pix(8, 7), Pix(8, 7), Pix(8, 7), Pix(8, 7), Pix(8, 7),
Pix(8, 7), Pix(8, 7), Pix(8, 7));
EXPECT_THAT_FIRST_ELEMENTS_ARE(
cb.GetContext(kYChannel, kContextExtendRight),
// Left (missing, filled in).
Pix(0, 7), Pix(0, 7), Pix(0, 7), Pix(0, 7), Pix(0, 7), Pix(0, 7),
Pix(0, 7), Pix(0, 7), Pix(0, 7),
// Top.
Pix(0, 7), Pix(1, 7), Pix(2, 7), Pix(3, 7), Pix(4, 7), Pix(5, 7),
Pix(6, 7), Pix(7, 7),
// Top right.
Pix(8, 7), Pix(9, 7), Pix(10, 7), Pix(11, 7), Pix(12, 7), Pix(13, 7),
Pix(14, 7), Pix(15, 7), Pix(15, 7));
}
TYPED_TEST(CodedBlockTest, ContextIsConstant) {
const uint32_t kSize = 32;
YUVPlane yuv;
ASSERT_WP2_OK(yuv.Resize(kSize, kSize));
// Y plane is constant.
yuv.Y.Fill(42);
// U plane has a bunch of different values.
for (uint32_t j = 0; j < kSize; ++j) {
for (uint32_t i = 0; i < kSize; ++i) yuv.U.At(i, j) = Pix(i, j);
}
typename TypeParam::Type mgr;
EXPECT_WP2_OK(mgr.Init(ALL_RECTS, /*snapped=*/false, kSize, kSize));
Block blk;
ASSERT_TRUE(mgr.UseSize(BLK_32x8, /*ind=*/0u, &blk));
mgr.Use(blk);
ASSERT_TRUE(mgr.UseSize(BLK_4x4, /*ind=*/1u, &blk));
mgr.Use(blk);
CodedBlock cb;
cb.SetRange(/*yuv_min=*/-512, /*yuv_max=*/512);
cb.SetDim(/*block=*/{/*x=*/1, /*y=*/2, /*dim=*/BLK_16x8}, mgr);
cb.SetSrcInput(yuv);
ContextCache context_cache;
cb.SetContextInput(yuv, &context_cache);
EXPECT_TRUE(cb.ContextIsConstant(kYChannel));
EXPECT_FALSE(cb.ContextIsConstant(kUChannel));
// Change a pixel inside the block.
yuv.Y.At(4, 8) = 666;
EXPECT_TRUE(cb.ContextIsConstant(kYChannel));
// Change a pixel outside the block (in the context).
yuv.Y.At(3, 7) = 666;
cb.ResetContextCache();
EXPECT_FALSE(cb.ContextIsConstant(kYChannel));
}
//------------------------------------------------------------------------------
// Make sure that the computed distortion does not vary based on partitioning.
TEST(GetDistoTest, BlockSizeDoesNotMatter) {
WP2PSNRInit();
constexpr uint32_t kNumSubX = 4, kNumSubY = 2;
CodedBlock cb;
cb.SetDimDefault(Block(0, 0, BLK_32x32));
const uint32_t sub_w = cb.w() / kNumSubX, sub_h = cb.h() / kNumSubY;
YUVPlane original, decoded;
ASSERT_WP2_OK(
original.Resize(cb.w_pix(), cb.h_pix(), /*pad=*/1, /*has_alpha=*/true));
original.Fill(Ayuv38b{200, 321, 123, -10});
ASSERT_WP2_OK(decoded.Copy(original, /*resize_if_needed=*/true));
UniformIntDistribution random(/*seed==*/666);
for (Channel channel : {kYChannel, kUChannel, kVChannel, kAChannel}) {
for (uint32_t y = 0; y < original.GetHeight(); ++y) {
for (uint32_t x = 0; x < original.GetWidth(); ++x) {
decoded.GetChannel(channel).At(x, y) += random.Get(-37, 50);
}
}
uint32_t total_sub_disto = 0;
for (uint32_t y = 0; y < kNumSubY; ++y) {
for (uint32_t x = 0; x < kNumSubX; ++x) {
CodedBlock sub_cb;
sub_cb.SetDimDefault(
Block(x * sub_w, y * sub_h, GetBlockSize(sub_w, sub_h)));
sub_cb.SetSrcInput(original); // Must be called after SetDimDefault().
sub_cb.SetReconstructedOutput(&decoded);
total_sub_disto += sub_cb.GetDisto(channel, cb.blk().rect_pix());
}
}
cb.SetSrcInput(original);
cb.SetReconstructedOutput(&decoded);
EXPECT_EQ(total_sub_disto, cb.GetDisto(channel, cb.blk().rect_pix()));
}
}
} // namespace
} // namespace WP2