| /* | 
 |  *  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/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 { | 
 |  protected: | 
 |   void SetupMacroblock(uint8_t *data, int block_size, int stride, | 
 |                        int num_planes) { | 
 |     memset(&mb_, 0, sizeof(mb_)); | 
 |     memset(&mi_, 0, sizeof(mi_)); | 
 |     mb_.up_available = 1; | 
 |     mb_.left_available = 1; | 
 |     mb_.mode_info_context = &mi_; | 
 |     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() { | 
 |     mb_.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() { | 
 |     mb_.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 (mb_.up_available || mb_.left_available) { | 
 |         int sum = 0, shift = BlockSizeLog2Min1() + mb_.up_available + | 
 |                              mb_.left_available; | 
 |         if (mb_.up_available) | 
 |           for (int x = 0; x < block_size_; x++) | 
 |             sum += data_ptr_[p][x - stride_]; | 
 |         if (mb_.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 mb_; | 
 |   MODE_INFO mi_; | 
 |   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() { | 
 |     data_array_ = reinterpret_cast<uint8_t*>( | 
 |         vpx_memalign(kDataAlignment, kDataBufferSize)); | 
 |   } | 
 |  | 
 |   static void TearDownTestCase() { | 
 |     vpx_free(data_array_); | 
 |     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(data_array_, kBlockSize, kStride, 1); | 
 |   } | 
 |  | 
 |   virtual void Predict(MB_PREDICTION_MODE mode) { | 
 |     mb_.mode_info_context->mbmi.mode = mode; | 
 |     REGISTER_STATE_CHECK(pred_fn_(&mb_, | 
 |                                   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_; | 
 | }; | 
 |  | 
 | 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() { | 
 |     data_array_ = reinterpret_cast<uint8_t*>( | 
 |         vpx_memalign(kDataAlignment, kDataBufferSize)); | 
 |   } | 
 |  | 
 |   static void TearDownTestCase() { | 
 |     vpx_free(data_array_); | 
 |     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(data_array_, kBlockSize, kStride, 2); | 
 |   } | 
 |  | 
 |   virtual void Predict(MB_PREDICTION_MODE mode) { | 
 |     mb_.mode_info_context->mbmi.uv_mode = mode; | 
 |     pred_fn_(&mb_, 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_; | 
 | }; | 
 |  | 
 | 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 |