390 строки
12 KiB
C++
390 строки
12 KiB
C++
/*
|
|
* Copyright (c) 2012 The WebM project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
|
|
#include <string.h>
|
|
#include "test/acm_random.h"
|
|
#include "test/clear_system_state.h"
|
|
#include "test/register_state_check.h"
|
|
#include "third_party/googletest/src/include/gtest/gtest.h"
|
|
extern "C" {
|
|
#include "./vpx_config.h"
|
|
#include "./vp8_rtcd.h"
|
|
#include "vp8/common/blockd.h"
|
|
#include "vpx_mem/vpx_mem.h"
|
|
}
|
|
|
|
namespace {
|
|
|
|
using libvpx_test::ACMRandom;
|
|
|
|
class IntraPredBase {
|
|
public:
|
|
virtual ~IntraPredBase() {}
|
|
|
|
virtual void TearDown() {
|
|
libvpx_test::ClearSystemState();
|
|
}
|
|
|
|
protected:
|
|
void SetupMacroblock(MACROBLOCKD *mbptr,
|
|
MODE_INFO *miptr,
|
|
uint8_t *data,
|
|
int block_size,
|
|
int stride,
|
|
int num_planes) {
|
|
mbptr_ = mbptr;
|
|
miptr_ = miptr;
|
|
mbptr_->up_available = 1;
|
|
mbptr_->left_available = 1;
|
|
mbptr_->mode_info_context = miptr_;
|
|
stride_ = stride;
|
|
block_size_ = block_size;
|
|
num_planes_ = num_planes;
|
|
for (int p = 0; p < num_planes; p++)
|
|
data_ptr_[p] = data + stride * (block_size + 1) * p +
|
|
stride + block_size;
|
|
}
|
|
|
|
void FillRandom() {
|
|
// Fill edges with random data
|
|
ACMRandom rnd(ACMRandom::DeterministicSeed());
|
|
for (int p = 0; p < num_planes_; p++) {
|
|
for (int x = -1 ; x <= block_size_; x++)
|
|
data_ptr_[p][x - stride_] = rnd.Rand8();
|
|
for (int y = 0; y < block_size_; y++)
|
|
data_ptr_[p][y * stride_ - 1] = rnd.Rand8();
|
|
}
|
|
}
|
|
|
|
virtual void Predict(MB_PREDICTION_MODE mode) = 0;
|
|
|
|
void SetLeftUnavailable() {
|
|
mbptr_->left_available = 0;
|
|
for (int p = 0; p < num_planes_; p++)
|
|
for (int i = -1; i < block_size_; ++i)
|
|
data_ptr_[p][stride_ * i - 1] = 129;
|
|
}
|
|
|
|
void SetTopUnavailable() {
|
|
mbptr_->up_available = 0;
|
|
for (int p = 0; p < num_planes_; p++)
|
|
memset(&data_ptr_[p][-1 - stride_], 127, block_size_ + 2);
|
|
}
|
|
|
|
void SetTopLeftUnavailable() {
|
|
SetLeftUnavailable();
|
|
SetTopUnavailable();
|
|
}
|
|
|
|
int BlockSizeLog2Min1() const {
|
|
switch (block_size_) {
|
|
case 16:
|
|
return 3;
|
|
case 8:
|
|
return 2;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// check DC prediction output against a reference
|
|
void CheckDCPrediction() const {
|
|
for (int p = 0; p < num_planes_; p++) {
|
|
// calculate expected DC
|
|
int expected;
|
|
if (mbptr_->up_available || mbptr_->left_available) {
|
|
int sum = 0, shift = BlockSizeLog2Min1() + mbptr_->up_available +
|
|
mbptr_->left_available;
|
|
if (mbptr_->up_available)
|
|
for (int x = 0; x < block_size_; x++)
|
|
sum += data_ptr_[p][x - stride_];
|
|
if (mbptr_->left_available)
|
|
for (int y = 0; y < block_size_; y++)
|
|
sum += data_ptr_[p][y * stride_ - 1];
|
|
expected = (sum + (1 << (shift - 1))) >> shift;
|
|
} else {
|
|
expected = 0x80;
|
|
}
|
|
// check that all subsequent lines are equal to the first
|
|
for (int y = 1; y < block_size_; ++y)
|
|
ASSERT_EQ(0, memcmp(data_ptr_[p], &data_ptr_[p][y * stride_],
|
|
block_size_));
|
|
// within the first line, ensure that each pixel has the same value
|
|
for (int x = 1; x < block_size_; ++x)
|
|
ASSERT_EQ(data_ptr_[p][0], data_ptr_[p][x]);
|
|
// now ensure that that pixel has the expected (DC) value
|
|
ASSERT_EQ(expected, data_ptr_[p][0]);
|
|
}
|
|
}
|
|
|
|
// check V prediction output against a reference
|
|
void CheckVPrediction() const {
|
|
// check that all lines equal the top border
|
|
for (int p = 0; p < num_planes_; p++)
|
|
for (int y = 0; y < block_size_; y++)
|
|
ASSERT_EQ(0, memcmp(&data_ptr_[p][-stride_],
|
|
&data_ptr_[p][y * stride_], block_size_));
|
|
}
|
|
|
|
// check H prediction output against a reference
|
|
void CheckHPrediction() const {
|
|
// for each line, ensure that each pixel is equal to the left border
|
|
for (int p = 0; p < num_planes_; p++)
|
|
for (int y = 0; y < block_size_; y++)
|
|
for (int x = 0; x < block_size_; x++)
|
|
ASSERT_EQ(data_ptr_[p][-1 + y * stride_],
|
|
data_ptr_[p][x + y * stride_]);
|
|
}
|
|
|
|
static int ClipByte(int value) {
|
|
if (value > 255)
|
|
return 255;
|
|
else if (value < 0)
|
|
return 0;
|
|
return value;
|
|
}
|
|
|
|
// check TM prediction output against a reference
|
|
void CheckTMPrediction() const {
|
|
for (int p = 0; p < num_planes_; p++)
|
|
for (int y = 0; y < block_size_; y++)
|
|
for (int x = 0; x < block_size_; x++) {
|
|
const int expected = ClipByte(data_ptr_[p][x - stride_]
|
|
+ data_ptr_[p][stride_ * y - 1]
|
|
- data_ptr_[p][-1 - stride_]);
|
|
ASSERT_EQ(expected, data_ptr_[p][y * stride_ + x]);
|
|
}
|
|
}
|
|
|
|
// Actual test
|
|
void RunTest() {
|
|
{
|
|
SCOPED_TRACE("DC_PRED");
|
|
FillRandom();
|
|
Predict(DC_PRED);
|
|
CheckDCPrediction();
|
|
}
|
|
{
|
|
SCOPED_TRACE("DC_PRED LEFT");
|
|
FillRandom();
|
|
SetLeftUnavailable();
|
|
Predict(DC_PRED);
|
|
CheckDCPrediction();
|
|
}
|
|
{
|
|
SCOPED_TRACE("DC_PRED TOP");
|
|
FillRandom();
|
|
SetTopUnavailable();
|
|
Predict(DC_PRED);
|
|
CheckDCPrediction();
|
|
}
|
|
{
|
|
SCOPED_TRACE("DC_PRED TOP_LEFT");
|
|
FillRandom();
|
|
SetTopLeftUnavailable();
|
|
Predict(DC_PRED);
|
|
CheckDCPrediction();
|
|
}
|
|
{
|
|
SCOPED_TRACE("H_PRED");
|
|
FillRandom();
|
|
Predict(H_PRED);
|
|
CheckHPrediction();
|
|
}
|
|
{
|
|
SCOPED_TRACE("V_PRED");
|
|
FillRandom();
|
|
Predict(V_PRED);
|
|
CheckVPrediction();
|
|
}
|
|
{
|
|
SCOPED_TRACE("TM_PRED");
|
|
FillRandom();
|
|
Predict(TM_PRED);
|
|
CheckTMPrediction();
|
|
}
|
|
}
|
|
|
|
MACROBLOCKD *mbptr_;
|
|
MODE_INFO *miptr_;
|
|
uint8_t *data_ptr_[2]; // in the case of Y, only [0] is used
|
|
int stride_;
|
|
int block_size_;
|
|
int num_planes_;
|
|
};
|
|
|
|
typedef void (*intra_pred_y_fn_t)(MACROBLOCKD *x,
|
|
uint8_t *yabove_row,
|
|
uint8_t *yleft,
|
|
int left_stride,
|
|
uint8_t *ypred_ptr,
|
|
int y_stride);
|
|
|
|
class IntraPredYTest : public ::testing::TestWithParam<intra_pred_y_fn_t>,
|
|
protected IntraPredBase {
|
|
public:
|
|
static void SetUpTestCase() {
|
|
mb_ = reinterpret_cast<MACROBLOCKD*>(
|
|
vpx_memalign(32, sizeof(MACROBLOCKD)));
|
|
mi_ = reinterpret_cast<MODE_INFO*>(
|
|
vpx_memalign(32, sizeof(MODE_INFO)));
|
|
data_array_ = reinterpret_cast<uint8_t*>(
|
|
vpx_memalign(kDataAlignment, kDataBufferSize));
|
|
}
|
|
|
|
static void TearDownTestCase() {
|
|
vpx_free(data_array_);
|
|
vpx_free(mi_);
|
|
vpx_free(mb_);
|
|
data_array_ = NULL;
|
|
}
|
|
|
|
protected:
|
|
static const int kBlockSize = 16;
|
|
static const int kDataAlignment = 16;
|
|
static const int kStride = kBlockSize * 3;
|
|
// We use 48 so that the data pointer of the first pixel in each row of
|
|
// each macroblock is 16-byte aligned, and this gives us access to the
|
|
// top-left and top-right corner pixels belonging to the top-left/right
|
|
// macroblocks.
|
|
// We use 17 lines so we have one line above us for top-prediction.
|
|
static const int kDataBufferSize = kStride * (kBlockSize + 1);
|
|
|
|
virtual void SetUp() {
|
|
pred_fn_ = GetParam();
|
|
SetupMacroblock(mb_, mi_, data_array_, kBlockSize, kStride, 1);
|
|
}
|
|
|
|
virtual void Predict(MB_PREDICTION_MODE mode) {
|
|
mbptr_->mode_info_context->mbmi.mode = mode;
|
|
REGISTER_STATE_CHECK(pred_fn_(mbptr_,
|
|
data_ptr_[0] - kStride,
|
|
data_ptr_[0] - 1, kStride,
|
|
data_ptr_[0], kStride));
|
|
}
|
|
|
|
intra_pred_y_fn_t pred_fn_;
|
|
static uint8_t* data_array_;
|
|
static MACROBLOCKD * mb_;
|
|
static MODE_INFO *mi_;
|
|
};
|
|
|
|
MACROBLOCKD* IntraPredYTest::mb_ = NULL;
|
|
MODE_INFO* IntraPredYTest::mi_ = NULL;
|
|
uint8_t* IntraPredYTest::data_array_ = NULL;
|
|
|
|
TEST_P(IntraPredYTest, IntraPredTests) {
|
|
RunTest();
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(C, IntraPredYTest,
|
|
::testing::Values(
|
|
vp8_build_intra_predictors_mby_s_c));
|
|
#if HAVE_SSE2
|
|
INSTANTIATE_TEST_CASE_P(SSE2, IntraPredYTest,
|
|
::testing::Values(
|
|
vp8_build_intra_predictors_mby_s_sse2));
|
|
#endif
|
|
#if HAVE_SSSE3
|
|
INSTANTIATE_TEST_CASE_P(SSSE3, IntraPredYTest,
|
|
::testing::Values(
|
|
vp8_build_intra_predictors_mby_s_ssse3));
|
|
#endif
|
|
|
|
typedef void (*intra_pred_uv_fn_t)(MACROBLOCKD *x,
|
|
uint8_t *uabove_row,
|
|
uint8_t *vabove_row,
|
|
uint8_t *uleft,
|
|
uint8_t *vleft,
|
|
int left_stride,
|
|
uint8_t *upred_ptr,
|
|
uint8_t *vpred_ptr,
|
|
int pred_stride);
|
|
|
|
class IntraPredUVTest : public ::testing::TestWithParam<intra_pred_uv_fn_t>,
|
|
protected IntraPredBase {
|
|
public:
|
|
static void SetUpTestCase() {
|
|
mb_ = reinterpret_cast<MACROBLOCKD*>(
|
|
vpx_memalign(32, sizeof(MACROBLOCKD)));
|
|
mi_ = reinterpret_cast<MODE_INFO*>(
|
|
vpx_memalign(32, sizeof(MODE_INFO)));
|
|
data_array_ = reinterpret_cast<uint8_t*>(
|
|
vpx_memalign(kDataAlignment, kDataBufferSize));
|
|
}
|
|
|
|
static void TearDownTestCase() {
|
|
vpx_free(data_array_);
|
|
vpx_free(mi_);
|
|
vpx_free(mb_);
|
|
data_array_ = NULL;
|
|
}
|
|
|
|
protected:
|
|
static const int kBlockSize = 8;
|
|
static const int kDataAlignment = 8;
|
|
static const int kStride = kBlockSize * 3;
|
|
// We use 24 so that the data pointer of the first pixel in each row of
|
|
// each macroblock is 8-byte aligned, and this gives us access to the
|
|
// top-left and top-right corner pixels belonging to the top-left/right
|
|
// macroblocks.
|
|
// We use 9 lines so we have one line above us for top-prediction.
|
|
// [0] = U, [1] = V
|
|
static const int kDataBufferSize = 2 * kStride * (kBlockSize + 1);
|
|
|
|
virtual void SetUp() {
|
|
pred_fn_ = GetParam();
|
|
SetupMacroblock(mb_, mi_, data_array_, kBlockSize, kStride, 2);
|
|
}
|
|
|
|
virtual void Predict(MB_PREDICTION_MODE mode) {
|
|
mbptr_->mode_info_context->mbmi.uv_mode = mode;
|
|
pred_fn_(mbptr_, data_ptr_[0] - kStride, data_ptr_[1] - kStride,
|
|
data_ptr_[0] - 1, data_ptr_[1] - 1, kStride,
|
|
data_ptr_[0], data_ptr_[1], kStride);
|
|
}
|
|
|
|
intra_pred_uv_fn_t pred_fn_;
|
|
// We use 24 so that the data pointer of the first pixel in each row of
|
|
// each macroblock is 8-byte aligned, and this gives us access to the
|
|
// top-left and top-right corner pixels belonging to the top-left/right
|
|
// macroblocks.
|
|
// We use 9 lines so we have one line above us for top-prediction.
|
|
// [0] = U, [1] = V
|
|
static uint8_t* data_array_;
|
|
static MACROBLOCKD* mb_;
|
|
static MODE_INFO* mi_;
|
|
};
|
|
|
|
MACROBLOCKD* IntraPredUVTest::mb_ = NULL;
|
|
MODE_INFO* IntraPredUVTest::mi_ = NULL;
|
|
uint8_t* IntraPredUVTest::data_array_ = NULL;
|
|
|
|
TEST_P(IntraPredUVTest, IntraPredTests) {
|
|
RunTest();
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(C, IntraPredUVTest,
|
|
::testing::Values(
|
|
vp8_build_intra_predictors_mbuv_s_c));
|
|
#if HAVE_SSE2
|
|
INSTANTIATE_TEST_CASE_P(SSE2, IntraPredUVTest,
|
|
::testing::Values(
|
|
vp8_build_intra_predictors_mbuv_s_sse2));
|
|
#endif
|
|
#if HAVE_SSSE3
|
|
INSTANTIATE_TEST_CASE_P(SSSE3, IntraPredUVTest,
|
|
::testing::Values(
|
|
vp8_build_intra_predictors_mbuv_s_ssse3));
|
|
#endif
|
|
|
|
} // namespace
|