Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/aom/test/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 41 kB image not shown  

Quelle  convolve_test.cc   Sprache: C

 
/*
 * Copyright (c) 2016, Alliance for Open Media. All rights reserved.
 *
 * This source code is subject to the terms of the BSD 2 Clause License and
 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
 * was not distributed with this source code in the LICENSE file, you can
 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
 * Media Patent License 1.0 was not distributed with this source code in the
 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
 */


#include <string.h>
#include <tuple>

#include "gtest/gtest.h"

#include "config/aom_config.h"
#include "config/aom_dsp_rtcd.h"

#include "aom_dsp/aom_dsp_common.h"
#include "aom_dsp/aom_filter.h"
#include "aom_mem/aom_mem.h"
#include "aom_ports/aom_timer.h"
#include "aom_ports/mem.h"
#include "av1/common/filter.h"
#include "test/acm_random.h"
#include "test/register_state_check.h"
#include "test/util.h"

namespace {

static const unsigned int kMaxDimension = MAX_SB_SIZE;
static const int kDataAlignment = 16;
static const int kOuterBlockSize = 4 * kMaxDimension;
static const int kInputStride = kOuterBlockSize;
static const int kOutputStride = kOuterBlockSize;
static const int kInputBufferSize = kOuterBlockSize * kOuterBlockSize;
static const int kOutputBufferSize = kOuterBlockSize * kOuterBlockSize;
static const int16_t kInvalidFilter[8] = {};
static const int kNumFilterBanks = SWITCHABLE_FILTERS;
static const int kNumFilters = 16;

typedef void (*ConvolveFunc)(const uint8_t *src, ptrdiff_t src_stride,
                             uint8_t *dst, ptrdiff_t dst_stride,
                             const int16_t *filter_x, int filter_x_stride,
                             const int16_t *filter_y, int filter_y_stride,
                             int w, int h);

struct ConvolveFunctions {
  ConvolveFunctions(ConvolveFunc h8, ConvolveFunc v8, int bd)
      : h8_(h8), v8_(v8), use_highbd_(bd) {}

  ConvolveFunc h8_;
  ConvolveFunc v8_;
  int use_highbd_;  // 0 if high bitdepth not used, else the actual bit depth.
};

typedef std::tuple<intintconst ConvolveFunctions *> ConvolveParam;

#define ALL_SIZES_64(convolve_fn)                                         \
  make_tuple(4, 4, &convolve_fn), make_tuple(8, 4, &convolve_fn),         \
      make_tuple(4, 8, &convolve_fn), make_tuple(8, 8, &convolve_fn),     \
      make_tuple(16, 8, &convolve_fn), make_tuple(8, 16, &convolve_fn),   \
      make_tuple(16, 16, &convolve_fn), make_tuple(32, 16, &convolve_fn), \
      make_tuple(16, 32, &convolve_fn), make_tuple(32, 32, &convolve_fn), \
      make_tuple(64, 32, &convolve_fn), make_tuple(32, 64, &convolve_fn), \
      make_tuple(64, 64, &convolve_fn)

#define ALL_SIZES(convolve_fn)                                          \
  make_tuple(128, 64, &convolve_fn), make_tuple(64, 128, &convolve_fn), \
      make_tuple(128, 128, &convolve_fn), ALL_SIZES_64(convolve_fn)

// Reference 8-tap subpixel filter, slightly modified to fit into this test.
#define AV1_FILTER_WEIGHT 128
#define AV1_FILTER_SHIFT 7
uint8_t clip_pixel(int x) { return x < 0 ? 0 : x > 255 ? 255 : x; }

void filter_block2d_8_c(const uint8_t *src_ptr, unsigned int src_stride,
                        const int16_t *HFilter, const int16_t *VFilter,
                        uint8_t *dst_ptr, unsigned int dst_stride,
                        unsigned int output_width, unsigned int output_height) {
  // Between passes, we use an intermediate buffer whose height is extended to
  // have enough horizontally filtered values as input for the vertical pass.
  // This buffer is allocated to be big enough for the largest block type we
  // support.
  const int kInterp_Extend = 4;
  const unsigned int intermediate_height =
      (kInterp_Extend - 1) + output_height + kInterp_Extend;
  unsigned int i, j;

  assert(intermediate_height > 7);

  // Size of intermediate_buffer is max_intermediate_height * filter_max_width,
  // where max_intermediate_height = (kInterp_Extend - 1) + filter_max_height
  //                                 + kInterp_Extend
  //                               = 3 + 16 + 4
  //                               = 23
  // and filter_max_width          = 16
  //
  uint8_t intermediate_buffer[(kMaxDimension + 8) * kMaxDimension];
  const int intermediate_next_stride =
      1 - static_cast<int>(intermediate_height * output_width);

  // Horizontal pass (src -> transposed intermediate).
  uint8_t *output_ptr = intermediate_buffer;
  const int src_next_row_stride = src_stride - output_width;
  src_ptr -= (kInterp_Extend - 1) * src_stride + (kInterp_Extend - 1);
  for (i = 0; i < intermediate_height; ++i) {
    for (j = 0; j < output_width; ++j) {
      // Apply filter...
      const int temp = (src_ptr[0] * HFilter[0]) + (src_ptr[1] * HFilter[1]) +
                       (src_ptr[2] * HFilter[2]) + (src_ptr[3] * HFilter[3]) +
                       (src_ptr[4] * HFilter[4]) + (src_ptr[5] * HFilter[5]) +
                       (src_ptr[6] * HFilter[6]) + (src_ptr[7] * HFilter[7]) +
                       (AV1_FILTER_WEIGHT >> 1);  // Rounding

      // Normalize back to 0-255...
      *output_ptr = clip_pixel(temp >> AV1_FILTER_SHIFT);
      ++src_ptr;
      output_ptr += intermediate_height;
    }
    src_ptr += src_next_row_stride;
    output_ptr += intermediate_next_stride;
  }

  // Vertical pass (transposed intermediate -> dst).
  src_ptr = intermediate_buffer;
  const int dst_next_row_stride = dst_stride - output_width;
  for (i = 0; i < output_height; ++i) {
    for (j = 0; j < output_width; ++j) {
      // Apply filter...
      const int temp = (src_ptr[0] * VFilter[0]) + (src_ptr[1] * VFilter[1]) +
                       (src_ptr[2] * VFilter[2]) + (src_ptr[3] * VFilter[3]) +
                       (src_ptr[4] * VFilter[4]) + (src_ptr[5] * VFilter[5]) +
                       (src_ptr[6] * VFilter[6]) + (src_ptr[7] * VFilter[7]) +
                       (AV1_FILTER_WEIGHT >> 1);  // Rounding

      // Normalize back to 0-255...
      *dst_ptr++ = clip_pixel(temp >> AV1_FILTER_SHIFT);
      src_ptr += intermediate_height;
    }
    src_ptr += intermediate_next_stride;
    dst_ptr += dst_next_row_stride;
  }
}

void block2d_average_c(uint8_t *src, unsigned int src_stride,
                       uint8_t *output_ptr, unsigned int output_stride,
                       unsigned int output_width, unsigned int output_height) {
  unsigned int i, j;
  for (i = 0; i < output_height; ++i) {
    for (j = 0; j < output_width; ++j) {
      output_ptr[j] = (output_ptr[j] + src[i * src_stride + j] + 1) >> 1;
    }
    output_ptr += output_stride;
  }
}

void filter_average_block2d_8_c(const uint8_t *src_ptr,
                                const unsigned int src_stride,
                                const int16_t *HFilter, const int16_t *VFilter,
                                uint8_t *dst_ptr, unsigned int dst_stride,
                                unsigned int output_width,
                                unsigned int output_height) {
  uint8_t tmp[kMaxDimension * kMaxDimension];

  assert(output_width <= kMaxDimension);
  assert(output_height <= kMaxDimension);
  filter_block2d_8_c(src_ptr, src_stride, HFilter, VFilter, tmp, kMaxDimension,
                     output_width, output_height);
  block2d_average_c(tmp, kMaxDimension, dst_ptr, dst_stride, output_width,
                    output_height);
}

void highbd_filter_block2d_8_c(const uint16_t *src_ptr,
                               const unsigned int src_stride,
                               const int16_t *HFilter, const int16_t *VFilter,
                               uint16_t *dst_ptr, unsigned int dst_stride,
                               unsigned int output_width,
                               unsigned int output_height, int bd) {
  // Between passes, we use an intermediate buffer whose height is extended to
  // have enough horizontally filtered values as input for the vertical pass.
  // This buffer is allocated to be big enough for the largest block type we
  // support.
  const int kInterp_Extend = 4;
  const unsigned int intermediate_height =
      (kInterp_Extend - 1) + output_height + kInterp_Extend;

  /* Size of intermediate_buffer is max_intermediate_height * filter_max_width,
   * where max_intermediate_height = (kInterp_Extend - 1) + filter_max_height
   *                                 + kInterp_Extend
   *                               = 3 + 16 + 4
   *                               = 23
   * and filter_max_width = 16
   */

  uint16_t intermediate_buffer[(kMaxDimension + 8) * kMaxDimension] = { 0 };
  const int intermediate_next_stride =
      1 - static_cast<int>(intermediate_height * output_width);

  // Horizontal pass (src -> transposed intermediate).
  {
    uint16_t *output_ptr = intermediate_buffer;
    const int src_next_row_stride = src_stride - output_width;
    unsigned int i, j;
    src_ptr -= (kInterp_Extend - 1) * src_stride + (kInterp_Extend - 1);
    for (i = 0; i < intermediate_height; ++i) {
      for (j = 0; j < output_width; ++j) {
        // Apply filter...
        const int temp = (src_ptr[0] * HFilter[0]) + (src_ptr[1] * HFilter[1]) +
                         (src_ptr[2] * HFilter[2]) + (src_ptr[3] * HFilter[3]) +
                         (src_ptr[4] * HFilter[4]) + (src_ptr[5] * HFilter[5]) +
                         (src_ptr[6] * HFilter[6]) + (src_ptr[7] * HFilter[7]) +
                         (AV1_FILTER_WEIGHT >> 1);  // Rounding

        // Normalize back to 0-255...
        *output_ptr = clip_pixel_highbd(temp >> AV1_FILTER_SHIFT, bd);
        ++src_ptr;
        output_ptr += intermediate_height;
      }
      src_ptr += src_next_row_stride;
      output_ptr += intermediate_next_stride;
    }
  }

  // Vertical pass (transposed intermediate -> dst).
  {
    const uint16_t *interm_ptr = intermediate_buffer;
    const int dst_next_row_stride = dst_stride - output_width;
    unsigned int i, j;
    for (i = 0; i < output_height; ++i) {
      for (j = 0; j < output_width; ++j) {
        // Apply filter...
        const int temp =
            (interm_ptr[0] * VFilter[0]) + (interm_ptr[1] * VFilter[1]) +
            (interm_ptr[2] * VFilter[2]) + (interm_ptr[3] * VFilter[3]) +
            (interm_ptr[4] * VFilter[4]) + (interm_ptr[5] * VFilter[5]) +
            (interm_ptr[6] * VFilter[6]) + (interm_ptr[7] * VFilter[7]) +
            (AV1_FILTER_WEIGHT >> 1);  // Rounding

        // Normalize back to 0-255...
        *dst_ptr++ = clip_pixel_highbd(temp >> AV1_FILTER_SHIFT, bd);
        interm_ptr += intermediate_height;
      }
      interm_ptr += intermediate_next_stride;
      dst_ptr += dst_next_row_stride;
    }
  }
}

void highbd_block2d_average_c(uint16_t *src, unsigned int src_stride,
                              uint16_t *output_ptr, unsigned int output_stride,
                              unsigned int output_width,
                              unsigned int output_height) {
  unsigned int i, j;
  for (i = 0; i < output_height; ++i) {
    for (j = 0; j < output_width; ++j) {
      output_ptr[j] = (output_ptr[j] + src[i * src_stride + j] + 1) >> 1;
    }
    output_ptr += output_stride;
  }
}

void highbd_filter_average_block2d_8_c(
    const uint16_t *src_ptr, unsigned int src_stride, const int16_t *HFilter,
    const int16_t *VFilter, uint16_t *dst_ptr, unsigned int dst_stride,
    unsigned int output_width, unsigned int output_height, int bd) {
  uint16_t tmp[kMaxDimension * kMaxDimension];

  assert(output_width <= kMaxDimension);
  assert(output_height <= kMaxDimension);
  highbd_filter_block2d_8_c(src_ptr, src_stride, HFilter, VFilter, tmp,
                            kMaxDimension, output_width, output_height, bd);
  highbd_block2d_average_c(tmp, kMaxDimension, dst_ptr, dst_stride,
                           output_width, output_height);
}

class ConvolveTestBase : public ::testing::TestWithParam<ConvolveParam> {
 public:
  static void SetUpTestSuite() {
    // Force input_ to be unaligned, output to be 16 byte aligned.
    input_ = reinterpret_cast<uint8_t *>(
                 aom_memalign(kDataAlignment, kInputBufferSize + 1)) +
             1;
    ASSERT_NE(input_, nullptr);
    ref8_ = reinterpret_cast<uint8_t *>(
        aom_memalign(kDataAlignment, kOutputStride * kMaxDimension));
    ASSERT_NE(ref8_, nullptr);
    output_ = reinterpret_cast<uint8_t *>(
        aom_memalign(kDataAlignment, kOutputBufferSize));
    ASSERT_NE(output_, nullptr);
    output_ref_ = reinterpret_cast<uint8_t *>(
        aom_memalign(kDataAlignment, kOutputBufferSize));
    ASSERT_NE(output_ref_, nullptr);
    input16_ = reinterpret_cast<uint16_t *>(aom_memalign(
                   kDataAlignment, (kInputBufferSize + 1) * sizeof(uint16_t))) +
               1;
    ASSERT_NE(input16_, nullptr);
    ref16_ = reinterpret_cast<uint16_t *>(aom_memalign(
        kDataAlignment, kOutputStride * kMaxDimension * sizeof(uint16_t)));
    ASSERT_NE(ref16_, nullptr);
    output16_ = reinterpret_cast<uint16_t *>(
        aom_memalign(kDataAlignment, (kOutputBufferSize) * sizeof(uint16_t)));
    ASSERT_NE(output16_, nullptr);
    output16_ref_ = reinterpret_cast<uint16_t *>(
        aom_memalign(kDataAlignment, (kOutputBufferSize) * sizeof(uint16_t)));
    ASSERT_NE(output16_ref_, nullptr);
  }

  static void TearDownTestSuite() {
    aom_free(input_ - 1);
    input_ = nullptr;
    aom_free(ref8_);
    ref8_ = nullptr;
    aom_free(output_);
    output_ = nullptr;
    aom_free(output_ref_);
    output_ref_ = nullptr;
    aom_free(input16_ - 1);
    input16_ = nullptr;
    aom_free(ref16_);
    ref16_ = nullptr;
    aom_free(output16_);
    output16_ = nullptr;
    aom_free(output16_ref_);
    output16_ref_ = nullptr;
  }

 protected:
  int Width() const { return GET_PARAM(0); }
  int Height() const { return GET_PARAM(1); }
  int BorderLeft() const {
    const int center = (kOuterBlockSize - Width()) / 2;
    return (center + (kDataAlignment - 1)) & ~(kDataAlignment - 1);
  }
  int BorderTop() const { return (kOuterBlockSize - Height()) / 2; }

  bool IsIndexInBorder(int i) {
    return (i < BorderTop() * kOuterBlockSize ||
            i >= (BorderTop() + Height()) * kOuterBlockSize ||
            i % kOuterBlockSize < BorderLeft() ||
            i % kOuterBlockSize >= (BorderLeft() + Width()));
  }

  void SetUp() override {
    UUT_ = GET_PARAM(2);
    if (UUT_->use_highbd_ != 0)
      mask_ = (1 << UUT_->use_highbd_) - 1;
    else
      mask_ = 255;
    /* Set up guard blocks for an inner block centered in the outer block */
    for (int i = 0; i < kOutputBufferSize; ++i) {
      if (IsIndexInBorder(i)) {
        output_[i] = 255;
        output16_[i] = mask_;
      } else {
        output_[i] = 0;
        output16_[i] = 0;
      }
    }

    ::libaom_test::ACMRandom prng;
    for (int i = 0; i < kInputBufferSize; ++i) {
      if (i & 1) {
        input_[i] = 255;
        input16_[i] = mask_;
      } else {
        input_[i] = prng.Rand8Extremes();
        input16_[i] = prng.Rand16() & mask_;
      }
    }
  }

  void SetConstantInput(int value) {
    memset(input_, value, kInputBufferSize);
    aom_memset16(input16_, value, kInputBufferSize);
  }

  void CopyOutputToRef() {
    memcpy(output_ref_, output_, kOutputBufferSize);
    // Copy 16-bit pixels values. The effective number of bytes is double.
    memcpy(output16_ref_, output16_, sizeof(output16_[0]) * kOutputBufferSize);
  }

  void CheckGuardBlocks() {
    for (int i = 0; i < kOutputBufferSize; ++i) {
      if (IsIndexInBorder(i)) {
        EXPECT_EQ(255, output_[i]);
      }
    }
  }

  uint8_t *input() const {
    const int offset = BorderTop() * kOuterBlockSize + BorderLeft();
    if (UUT_->use_highbd_ == 0) {
      return input_ + offset;
    } else {
      return CONVERT_TO_BYTEPTR(input16_) + offset;
    }
  }

  uint8_t *output() const {
    const int offset = BorderTop() * kOuterBlockSize + BorderLeft();
    if (UUT_->use_highbd_ == 0) {
      return output_ + offset;
    } else {
      return CONVERT_TO_BYTEPTR(output16_) + offset;
    }
  }

  uint8_t *output_ref() const {
    const int offset = BorderTop() * kOuterBlockSize + BorderLeft();
    if (UUT_->use_highbd_ == 0) {
      return output_ref_ + offset;
    } else {
      return CONVERT_TO_BYTEPTR(output16_ref_) + offset;
    }
  }

  uint16_t lookup(uint8_t *list, int index) const {
    if (UUT_->use_highbd_ == 0) {
      return list[index];
    } else {
      return CONVERT_TO_SHORTPTR(list)[index];
    }
  }

  void assign_val(uint8_t *list, int index, uint16_t val) const {
    if (UUT_->use_highbd_ == 0) {
      list[index] = (uint8_t)val;
    } else {
      CONVERT_TO_SHORTPTR(list)[index] = val;
    }
  }

  void wrapper_filter_average_block2d_8_c(
      const uint8_t *src_ptr, unsigned int src_stride, const int16_t *HFilter,
      const int16_t *VFilter, uint8_t *dst_ptr, unsigned int dst_stride,
      unsigned int output_width, unsigned int output_height) {
    if (UUT_->use_highbd_ == 0) {
      filter_average_block2d_8_c(src_ptr, src_stride, HFilter, VFilter, dst_ptr,
                                 dst_stride, output_width, output_height);
    } else {
      highbd_filter_average_block2d_8_c(
          CONVERT_TO_SHORTPTR(src_ptr), src_stride, HFilter, VFilter,
          CONVERT_TO_SHORTPTR(dst_ptr), dst_stride, output_width, output_height,
          UUT_->use_highbd_);
    }
  }

  void wrapper_filter_block2d_8_c(
      const uint8_t *src_ptr, unsigned int src_stride, const int16_t *HFilter,
      const int16_t *VFilter, uint8_t *dst_ptr, unsigned int dst_stride,
      unsigned int output_width, unsigned int output_height) {
    if (UUT_->use_highbd_ == 0) {
      filter_block2d_8_c(src_ptr, src_stride, HFilter, VFilter, dst_ptr,
                         dst_stride, output_width, output_height);
    } else {
      highbd_filter_block2d_8_c(CONVERT_TO_SHORTPTR(src_ptr), src_stride,
                                HFilter, VFilter, CONVERT_TO_SHORTPTR(dst_ptr),
                                dst_stride, output_width, output_height,
                                UUT_->use_highbd_);
    }
  }

  void MatchesReferenceSubpixelFilter() {
    uint8_t *const in = input();
    uint8_t *const out = output();
    uint8_t *ref;
    if (UUT_->use_highbd_ == 0) {
      ref = ref8_;
    } else {
      ref = CONVERT_TO_BYTEPTR(ref16_);
    }
    int subpel_search;
    for (subpel_search = USE_2_TAPS; subpel_search <= USE_8_TAPS;
         ++subpel_search) {
      for (int filter_bank = 0; filter_bank < kNumFilterBanks; ++filter_bank) {
        const InterpFilter filter = (InterpFilter)filter_bank;
        const InterpKernel *filters =
            (const InterpKernel *)av1_get_interp_filter_kernel(filter,
                                                               subpel_search);
        for (int filter_x = 0; filter_x < kNumFilters; ++filter_x) {
          for (int filter_y = 0; filter_y < kNumFilters; ++filter_y) {
            wrapper_filter_block2d_8_c(in, kInputStride, filters[filter_x],
                                       filters[filter_y], ref, kOutputStride,
                                       Width(), Height());

            if (filter_x && filter_y)
              continue;
            else if (filter_y)
              UUT_->v8_(in, kInputStride, out, kOutputStride, kInvalidFilter,
                        16, filters[filter_y], 16, Width(), Height());
            else if (filter_x)
              API_REGISTER_STATE_CHECK(UUT_->h8_(
                  in, kInputStride, out, kOutputStride, filters[filter_x], 16,
                  kInvalidFilter, 16, Width(), Height()));
            else
              continue;

            CheckGuardBlocks();

            for (int y = 0; y < Height(); ++y)
              for (int x = 0; x < Width(); ++x)
                ASSERT_EQ(lookup(ref, y * kOutputStride + x),
                          lookup(out, y * kOutputStride + x))
                    << "mismatch at (" << x << "," << y << "), "
                    << "filters (" << filter_bank << "," << filter_x << ","
                    << filter_y << ")";
          }
        }
      }
    }
  }

  void FilterExtremes() {
    uint8_t *const in = input();
    uint8_t *const out = output();
    uint8_t *ref;
    if (UUT_->use_highbd_ == 0) {
      ref = ref8_;
    } else {
      ref = CONVERT_TO_BYTEPTR(ref16_);
    }

    // Populate ref and out with some random data
    ::libaom_test::ACMRandom prng;
    for (int y = 0; y < Height(); ++y) {
      for (int x = 0; x < Width(); ++x) {
        uint16_t r;
        if (UUT_->use_highbd_ == 0 || UUT_->use_highbd_ == 8) {
          r = prng.Rand8Extremes();
        } else {
          r = prng.Rand16() & mask_;
        }
        assign_val(out, y * kOutputStride + x, r);
        assign_val(ref, y * kOutputStride + x, r);
      }
    }

    for (int axis = 0; axis < 2; axis++) {
      int seed_val = 0;
      while (seed_val < 256) {
        for (int y = 0; y < 8; ++y) {
          for (int x = 0; x < 8; ++x) {
            assign_val(in, y * kOutputStride + x - SUBPEL_TAPS / 2 + 1,
                       ((seed_val >> (axis ? y : x)) & 1) * mask_);
            if (axis) seed_val++;
          }
          if (axis)
            seed_val -= 8;
          else
            seed_val++;
        }
        if (axis) seed_val += 8;
        int subpel_search;
        for (subpel_search = USE_2_TAPS; subpel_search <= USE_8_TAPS;
             ++subpel_search) {
          for (int filter_bank = 0; filter_bank < kNumFilterBanks;
               ++filter_bank) {
            const InterpFilter filter = (InterpFilter)filter_bank;
            const InterpKernel *filters =
                (const InterpKernel *)av1_get_interp_filter_kernel(
                    filter, subpel_search);
            for (int filter_x = 0; filter_x < kNumFilters; ++filter_x) {
              for (int filter_y = 0; filter_y < kNumFilters; ++filter_y) {
                wrapper_filter_block2d_8_c(in, kInputStride, filters[filter_x],
                                           filters[filter_y], ref,
                                           kOutputStride, Width(), Height());
                if (filter_x && filter_y)
                  continue;
                else if (filter_y)
                  API_REGISTER_STATE_CHECK(UUT_->v8_(
                      in, kInputStride, out, kOutputStride, kInvalidFilter, 16,
                      filters[filter_y], 16, Width(), Height()));
                else if (filter_x)
                  API_REGISTER_STATE_CHECK(UUT_->h8_(
                      in, kInputStride, out, kOutputStride, filters[filter_x],
                      16, kInvalidFilter, 16, Width(), Height()));
                else
                  continue;

                for (int y = 0; y < Height(); ++y)
                  for (int x = 0; x < Width(); ++x)
                    ASSERT_EQ(lookup(ref, y * kOutputStride + x),
                              lookup(out, y * kOutputStride + x))
                        << "mismatch at (" << x << "," << y << "), "
                        << "filters (" << filter_bank << "," << filter_x << ","
                        << filter_y << ")";
              }
            }
          }
        }
      }
    }
  }

  void SpeedTest() {
    uint8_t *const in = input();
    uint8_t *const out = output();
    uint8_t *ref;
    if (UUT_->use_highbd_ == 0) {
      ref = ref8_;
    } else {
      ref = CONVERT_TO_BYTEPTR(ref16_);
    }

    // Populate ref and out with some random data
    ::libaom_test::ACMRandom prng;
    for (int y = 0; y < Height(); ++y) {
      for (int x = 0; x < Width(); ++x) {
        uint16_t r;
        if (UUT_->use_highbd_ == 0 || UUT_->use_highbd_ == 8) {
          r = prng.Rand8Extremes();
        } else {
          r = prng.Rand16() & mask_;
        }
        assign_val(out, y * kOutputStride + x, r);
        assign_val(ref, y * kOutputStride + x, r);
      }
    }

    InterpFilter filter = (InterpFilter)1;
    const InterpKernel *filters =
        (const InterpKernel *)av1_get_interp_filter_kernel(filter, USE_8_TAPS);
    wrapper_filter_average_block2d_8_c(in, kInputStride, filters[1], filters[1],
                                       out, kOutputStride, Width(), Height());

    aom_usec_timer timer;
    int tests_num = 1000;

    aom_usec_timer_start(&timer);
    while (tests_num > 0) {
      for (int filter_bank = 0; filter_bank < kNumFilterBanks; ++filter_bank) {
        filter = (InterpFilter)filter_bank;
        filters = (const InterpKernel *)av1_get_interp_filter_kernel(
            filter, USE_8_TAPS);
        for (int filter_x = 0; filter_x < kNumFilters; ++filter_x) {
          for (int filter_y = 0; filter_y < kNumFilters; ++filter_y) {
            if (filter_x && filter_y) continue;
            if (filter_y)
              API_REGISTER_STATE_CHECK(UUT_->v8_(
                  in, kInputStride, out, kOutputStride, kInvalidFilter, 16,
                  filters[filter_y], 16, Width(), Height()));
            else if (filter_x)
              API_REGISTER_STATE_CHECK(UUT_->h8_(
                  in, kInputStride, out, kOutputStride, filters[filter_x], 16,
                  kInvalidFilter, 16, Width(), Height()));
          }
        }
      }
      tests_num--;
    }
    aom_usec_timer_mark(&timer);

    const int elapsed_time =
        static_cast<int>(aom_usec_timer_elapsed(&timer) / 1000);
    printf("%dx%d (bitdepth %d) time: %5d ms\n", Width(), Height(),
           UUT_->use_highbd_, elapsed_time);
  }

  const ConvolveFunctions *UUT_;
  static uint8_t *input_;
  static uint8_t *ref8_;
  static uint8_t *output_;
  static uint8_t *output_ref_;
  static uint16_t *input16_;
  static uint16_t *ref16_;
  static uint16_t *output16_;
  static uint16_t *output16_ref_;
  int mask_;
};

uint8_t *ConvolveTestBase::input_ = nullptr;
uint8_t *ConvolveTestBase::ref8_ = nullptr;
uint8_t *ConvolveTestBase::output_ = nullptr;
uint8_t *ConvolveTestBase::output_ref_ = nullptr;
uint16_t *ConvolveTestBase::input16_ = nullptr;
uint16_t *ConvolveTestBase::ref16_ = nullptr;
uint16_t *ConvolveTestBase::output16_ = nullptr;
uint16_t *ConvolveTestBase::output16_ref_ = nullptr;

using LowbdConvolveTest = ConvolveTestBase;

TEST_P(LowbdConvolveTest, GuardBlocks) { CheckGuardBlocks(); }

void FiltersWontSaturateWhenAddedPairwise() {
  int subpel_search;
  for (subpel_search = USE_2_TAPS; subpel_search <= USE_8_TAPS;
       ++subpel_search) {
    for (int filter_bank = 0; filter_bank < kNumFilterBanks; ++filter_bank) {
      const InterpFilter filter = (InterpFilter)filter_bank;
      const InterpKernel *filters =
          (const InterpKernel *)av1_get_interp_filter_kernel(filter,
                                                             subpel_search);
      for (int i = 0; i < kNumFilters; i++) {
        const int p0 = filters[i][0] + filters[i][1];
        const int p1 = filters[i][2] + filters[i][3];
        const int p2 = filters[i][4] + filters[i][5];
        const int p3 = filters[i][6] + filters[i][7];
        EXPECT_LE(p0, 128);
        EXPECT_LE(p1, 128);
        EXPECT_LE(p2, 128);
        EXPECT_LE(p3, 128);
        EXPECT_LE(p0 + p3, 128);
        EXPECT_LE(p0 + p3 + p1, 128);
        EXPECT_LE(p0 + p3 + p1 + p2, 128);
        EXPECT_EQ(p0 + p1 + p2 + p3, 128);
      }
    }
  }
}

TEST(LowbdConvolveTest, FiltersWontSaturateWhenAddedPairwise) {
  FiltersWontSaturateWhenAddedPairwise();
}

TEST_P(LowbdConvolveTest, MatchesReferenceSubpixelFilter) {
  MatchesReferenceSubpixelFilter();
}

TEST_P(LowbdConvolveTest, FilterExtremes) { FilterExtremes(); }

TEST_P(LowbdConvolveTest, DISABLED_Speed) { SpeedTest(); }

using std::make_tuple;

// WRAP macro is only used for high bitdepth build.
#if CONFIG_AV1_HIGHBITDEPTH
#define WRAP(func, bd)                                                       \
  static void wrap_##func##_##bd(                                            \
      const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst,                \
      ptrdiff_t dst_stride, const int16_t *filter_x, int filter_x_stride,    \
      const int16_t *filter_y, int filter_y_stride, int w, int h) {          \
    aom_highbd_##func(src, src_stride, dst, dst_stride, filter_x,            \
                      filter_x_stride, filter_y, filter_y_stride, w, h, bd); \
  }
#if HAVE_SSE2 && AOM_ARCH_X86_64
WRAP(convolve8_horiz_sse2, 8)
WRAP(convolve8_vert_sse2, 8)
WRAP(convolve8_horiz_sse2, 10)
WRAP(convolve8_vert_sse2, 10)
WRAP(convolve8_horiz_sse2, 12)
WRAP(convolve8_vert_sse2, 12)
#endif  // HAVE_SSE2 && AOM_ARCH_X86_64

WRAP(convolve8_horiz_c, 8)
WRAP(convolve8_vert_c, 8)
WRAP(convolve8_horiz_c, 10)
WRAP(convolve8_vert_c, 10)
WRAP(convolve8_horiz_c, 12)
WRAP(convolve8_vert_c, 12)

#if HAVE_AVX2
WRAP(convolve8_horiz_avx2, 8)
WRAP(convolve8_vert_avx2, 8)

WRAP(convolve8_horiz_avx2, 10)
WRAP(convolve8_vert_avx2, 10)

WRAP(convolve8_horiz_avx2, 12)
WRAP(convolve8_vert_avx2, 12)
#endif  // HAVE_AVX2

#if HAVE_NEON
WRAP(convolve8_horiz_neon, 8)
WRAP(convolve8_vert_neon, 8)

WRAP(convolve8_horiz_neon, 10)
WRAP(convolve8_vert_neon, 10)

WRAP(convolve8_horiz_neon, 12)
WRAP(convolve8_vert_neon, 12)
#endif  // HAVE_NEON

#if HAVE_SVE
WRAP(convolve8_horiz_sve, 8)
WRAP(convolve8_vert_sve, 8)

WRAP(convolve8_horiz_sve, 10)
WRAP(convolve8_vert_sve, 10)

WRAP(convolve8_horiz_sve, 12)
WRAP(convolve8_vert_sve, 12)
#endif  // HAVE_SVE
#endif  // CONFIG_AV1_HIGHBITDEPTH

#undef WRAP

#if CONFIG_AV1_HIGHBITDEPTH

using HighbdConvolveTest = ConvolveTestBase;

TEST_P(HighbdConvolveTest, GuardBlocks) { CheckGuardBlocks(); }

TEST(HighbdConvolveTest, FiltersWontSaturateWhenAddedPairwise) {
  FiltersWontSaturateWhenAddedPairwise();
}

TEST_P(HighbdConvolveTest, MatchesReferenceSubpixelFilter) {
  MatchesReferenceSubpixelFilter();
}

TEST_P(HighbdConvolveTest, FilterExtremes) { FilterExtremes(); }

TEST_P(HighbdConvolveTest, DISABLED_Speed) { SpeedTest(); }

const ConvolveFunctions wrap_convolve8_c(wrap_convolve8_horiz_c_8,
                                         wrap_convolve8_vert_c_8, 8);
const ConvolveFunctions wrap_convolve10_c(wrap_convolve8_horiz_c_10,
                                          wrap_convolve8_vert_c_10, 10);
const ConvolveFunctions wrap_convolve12_c(wrap_convolve8_horiz_c_12,
                                          wrap_convolve8_vert_c_12, 12);
const ConvolveParam kArrayHighbdConvolve_c[] = { ALL_SIZES(wrap_convolve8_c),
                                                 ALL_SIZES(wrap_convolve10_c),
                                                 ALL_SIZES(wrap_convolve12_c) };

INSTANTIATE_TEST_SUITE_P(C, HighbdConvolveTest,
                         ::testing::ValuesIn(kArrayHighbdConvolve_c));
#endif  // CONFIG_AV1_HIGHBITDEPTH

const ConvolveFunctions convolve8_c(aom_convolve8_horiz_c, aom_convolve8_vert_c,
                                    0);
const ConvolveParam kArrayConvolve_c[] = { ALL_SIZES(convolve8_c) };

INSTANTIATE_TEST_SUITE_P(C, LowbdConvolveTest,
                         ::testing::ValuesIn(kArrayConvolve_c));

#if HAVE_SSE2 && AOM_ARCH_X86_64
#if CONFIG_AV1_HIGHBITDEPTH
const ConvolveFunctions wrap_convolve8_sse2(wrap_convolve8_horiz_sse2_8,
                                            wrap_convolve8_vert_sse2_8, 8);
const ConvolveFunctions wrap_convolve10_sse2(wrap_convolve8_horiz_sse2_10,
                                             wrap_convolve8_vert_sse2_10, 10);
const ConvolveFunctions wrap_convolve12_sse2(wrap_convolve8_horiz_sse2_12,
                                             wrap_convolve8_vert_sse2_12, 12);
const ConvolveParam kArrayHighbdConvolve_sse2[] = {
  ALL_SIZES(wrap_convolve8_sse2), ALL_SIZES(wrap_convolve10_sse2),
  ALL_SIZES(wrap_convolve12_sse2)
};

INSTANTIATE_TEST_SUITE_P(SSE2, HighbdConvolveTest,
                         ::testing::ValuesIn(kArrayHighbdConvolve_sse2));
#endif
#endif

#if HAVE_SSSE3
const ConvolveFunctions convolve8_ssse3(aom_convolve8_horiz_ssse3,
                                        aom_convolve8_vert_ssse3, 0);

const ConvolveParam kArrayConvolve8_ssse3[] = { ALL_SIZES(convolve8_ssse3) };

INSTANTIATE_TEST_SUITE_P(SSSE3, LowbdConvolveTest,
                         ::testing::ValuesIn(kArrayConvolve8_ssse3));
#endif

#if HAVE_AVX2
#if CONFIG_AV1_HIGHBITDEPTH
const ConvolveFunctions wrap_convolve8_avx2(wrap_convolve8_horiz_avx2_8,
                                            wrap_convolve8_vert_avx2_8, 8);
const ConvolveFunctions wrap_convolve10_avx2(wrap_convolve8_horiz_avx2_10,
                                             wrap_convolve8_vert_avx2_10, 10);
const ConvolveFunctions wrap_convolve12_avx2(wrap_convolve8_horiz_avx2_12,
                                             wrap_convolve8_vert_avx2_12, 12);
const ConvolveParam kArray_HighbdConvolve8_avx2[] = {
  ALL_SIZES_64(wrap_convolve8_avx2), ALL_SIZES_64(wrap_convolve10_avx2),
  ALL_SIZES_64(wrap_convolve12_avx2)
};

INSTANTIATE_TEST_SUITE_P(AVX2, HighbdConvolveTest,
                         ::testing::ValuesIn(kArray_HighbdConvolve8_avx2));
#endif
const ConvolveFunctions convolve8_avx2(aom_convolve8_horiz_avx2,
                                       aom_convolve8_vert_avx2, 0);
const ConvolveParam kArray_Convolve8_avx2[] = { ALL_SIZES(convolve8_avx2) };

INSTANTIATE_TEST_SUITE_P(AVX2, LowbdConvolveTest,
                         ::testing::ValuesIn(kArray_Convolve8_avx2));
#endif  // HAVE_AVX2

#if HAVE_NEON
#if CONFIG_AV1_HIGHBITDEPTH
const ConvolveFunctions wrap_convolve8_neon(wrap_convolve8_horiz_neon_8,
                                            wrap_convolve8_vert_neon_8, 8);
const ConvolveFunctions wrap_convolve10_neon(wrap_convolve8_horiz_neon_10,
                                             wrap_convolve8_vert_neon_10, 10);
const ConvolveFunctions wrap_convolve12_neon(wrap_convolve8_horiz_neon_12,
                                             wrap_convolve8_vert_neon_12, 12);
const ConvolveParam kArray_HighbdConvolve8_neon[] = {
  ALL_SIZES_64(wrap_convolve8_neon), ALL_SIZES_64(wrap_convolve10_neon),
  ALL_SIZES_64(wrap_convolve12_neon)
};

INSTANTIATE_TEST_SUITE_P(NEON, HighbdConvolveTest,
                         ::testing::ValuesIn(kArray_HighbdConvolve8_neon));
#endif
const ConvolveFunctions convolve8_neon(aom_convolve8_horiz_neon,
                                       aom_convolve8_vert_neon, 0);
const ConvolveParam kArray_Convolve8_neon[] = { ALL_SIZES(convolve8_neon) };

INSTANTIATE_TEST_SUITE_P(NEON, LowbdConvolveTest,
                         ::testing::ValuesIn(kArray_Convolve8_neon));
#endif  // HAVE_NEON

#if HAVE_NEON_DOTPROD
const ConvolveFunctions convolve8_neon_dotprod(aom_convolve8_horiz_neon_dotprod,
                                               aom_convolve8_vert_neon_dotprod,
                                               0);
const ConvolveParam kArray_Convolve8_neon_dotprod[] = { ALL_SIZES(
    convolve8_neon_dotprod) };

INSTANTIATE_TEST_SUITE_P(NEON_DOTPROD, LowbdConvolveTest,
                         ::testing::ValuesIn(kArray_Convolve8_neon_dotprod));
#endif  // HAVE_NEON_DOTPROD

#if HAVE_NEON_I8MM
const ConvolveFunctions convolve8_neon_i8mm(aom_convolve8_horiz_neon_i8mm,
                                            aom_convolve8_vert_neon_i8mm, 0);
const ConvolveParam kArray_Convolve8_neon_i8mm[] = { ALL_SIZES(
    convolve8_neon_i8mm) };

INSTANTIATE_TEST_SUITE_P(NEON_I8MM, LowbdConvolveTest,
                         ::testing::ValuesIn(kArray_Convolve8_neon_i8mm));
#endif  // HAVE_NEON_I8MM

#if HAVE_SVE
#if CONFIG_AV1_HIGHBITDEPTH
const ConvolveFunctions wrap_convolve8_sve(wrap_convolve8_horiz_sve_8,
                                           wrap_convolve8_vert_sve_8, 8);
const ConvolveFunctions wrap_convolve10_sve(wrap_convolve8_horiz_sve_10,
                                            wrap_convolve8_vert_sve_10, 10);
const ConvolveFunctions wrap_convolve12_sve(wrap_convolve8_horiz_sve_12,
                                            wrap_convolve8_vert_sve_12, 12);
const ConvolveParam kArray_HighbdConvolve8_sve[] = {
  ALL_SIZES_64(wrap_convolve8_sve), ALL_SIZES_64(wrap_convolve10_sve),
  ALL_SIZES_64(wrap_convolve12_sve)
};

INSTANTIATE_TEST_SUITE_P(SVE, HighbdConvolveTest,
                         ::testing::ValuesIn(kArray_HighbdConvolve8_sve));
#endif
#endif  // HAVE_SVE

typedef void (*ConvolveScale2DFunc)(const uint8_t *src, ptrdiff_t src_stride,
                                    uint8_t *dst, ptrdiff_t dst_stride,
                                    const InterpKernel *filter, int x0_q4,
                                    int x_step_q4, int y0_q4, int y_step_q4,
                                    int w, int h);

typedef std::tuple<intint, ConvolveScale2DFunc> ConvolveScale2DParam;

class ConvolveScale2DTest
    : public ::testing::TestWithParam<ConvolveScale2DParam> {
 public:
  int Width() const { return GET_PARAM(0); }
  int Height() const { return GET_PARAM(1); }
  int BorderLeft() const {
    const int center = (kOuterBlockSize - Width()) / 2;
    return (center + (kDataAlignment - 1)) & ~(kDataAlignment - 1);
  }
  int BorderTop() const { return (kOuterBlockSize - Height()) / 2; }

  bool IsIndexInBorder(int i) {
    return (i < BorderTop() * kOuterBlockSize ||
            i >= (BorderTop() + Height()) * kOuterBlockSize ||
            i % kOuterBlockSize < BorderLeft() ||
            i % kOuterBlockSize >= (BorderLeft() + Width()));
  }

  void SetUp() override {
    // Force input_ to be unaligned, output to be 16 byte aligned.
    input_ = reinterpret_cast<uint8_t *>(
                 aom_memalign(kDataAlignment, kInputBufferSize + 1)) +
             1;
    output_ = reinterpret_cast<uint8_t *>(
        aom_memalign(kDataAlignment, kOutputBufferSize));
    output_ref_ = reinterpret_cast<uint8_t *>(
        aom_memalign(kDataAlignment, kOutputBufferSize));

    ASSERT_NE(input_, nullptr);
    ASSERT_NE(output_, nullptr);
    ASSERT_NE(output_ref_, nullptr);

    test_func_ = GET_PARAM(2);
    /* Set up guard blocks for an inner block centered in the outer block */
    for (int i = 0; i < kOutputBufferSize; ++i) {
      if (IsIndexInBorder(i)) {
        output_[i] = 255;
      } else {
        output_[i] = 0;
      }
    }

    ::libaom_test::ACMRandom prng;
    for (int i = 0; i < kInputBufferSize; ++i) {
      if (i & 1) {
        input_[i] = 255;
      } else {
        input_[i] = prng.Rand8Extremes();
      }
    }
  }

  void TearDown() override {
    aom_free(input_ - 1);
    input_ = nullptr;
    aom_free(output_);
    output_ = nullptr;
    aom_free(output_ref_);
    output_ref_ = nullptr;
  }

  void SetConstantInput(int value) { memset(input_, value, kInputBufferSize); }

  void CopyOutputToRef() { memcpy(output_ref_, output_, kOutputBufferSize); }

  void CheckGuardBlocks() {
    for (int i = 0; i < kOutputBufferSize; ++i) {
      if (IsIndexInBorder(i)) {
        EXPECT_EQ(255, output_[i]);
      }
    }
  }

  uint8_t *input() const {
    const int offset = BorderTop() * kOuterBlockSize + BorderLeft();
    return input_ + offset;
  }

  uint8_t *output() const {
    const int offset = BorderTop() * kOuterBlockSize + BorderLeft();
    return output_ + offset;
  }

  uint8_t *output_ref() const {
    const int offset = BorderTop() * kOuterBlockSize + BorderLeft();
    return output_ref_ + offset;
  }

  uint16_t lookup(uint8_t *list, int index) const { return list[index]; }

  void assign_val(uint8_t *list, int index, uint16_t val) const {
    list[index] = (uint8_t)val;
  }

  ConvolveScale2DFunc test_func_;
  uint8_t *input_;
  uint8_t *output_;
  uint8_t *output_ref_;
};

TEST_P(ConvolveScale2DTest, DISABLED_Speed) {
  const uint8_t *const in = input();
  uint8_t *const out = output();
  const InterpKernel *const filter =
      (const InterpKernel *)av1_get_interp_filter_kernel(EIGHTTAP_REGULAR,
                                                         USE_8_TAPS);
  const int kNumTests = 10000;
  const int width = Width();
  const int height = Height();
  const int frac = 8;
  const int step = 16;
  aom_usec_timer timer;

  aom_usec_timer_start(&timer);
  for (int n = 0; n < kNumTests; ++n) {
    test_func_(in, kInputStride, out, kOutputStride, filter, frac, step, frac,
               step, width, height);
  }
  aom_usec_timer_mark(&timer);

  const int elapsed_time = static_cast<int>(aom_usec_timer_elapsed(&timer));
  printf("convolve_scale_2d_%dx%d_%d: %d us\n", width, height, 8, elapsed_time);
}

TEST_P(ConvolveScale2DTest, Correctness) {
  uint8_t *const in = input();
  uint8_t *const out = output();
  uint8_t ref[kOutputStride * kMaxDimension];

  ::libaom_test::ACMRandom prng;
  for (int y = 0; y < Height(); ++y) {
    for (int x = 0; x < Width(); ++x) {
      const uint16_t r = prng.Rand8Extremes();
      assign_val(in, y * kInputStride + x, r);
    }
  }

  for (int subpel_search = USE_2_TAPS; subpel_search <= USE_8_TAPS;
       ++subpel_search) {
    for (int filter_bank = 0; filter_bank < kNumFilterBanks; ++filter_bank) {
      const InterpFilter filter = static_cast<InterpFilter>(filter_bank);
      const InterpKernel *filters =
          (const InterpKernel *)av1_get_interp_filter_kernel(filter,
                                                             subpel_search);
      for (int frac = 0; frac < 16; ++frac) {
        for (int step = 1; step <= 32; ++step) {
          aom_scaled_2d_c(in, kInputStride, ref, kOutputStride, filters, frac,
                          step, frac, step, Width(), Height());
          API_REGISTER_STATE_CHECK(
              test_func_(in, kInputStride, out, kOutputStride, filters, frac,
                         step, frac, step, Width(), Height()));

          CheckGuardBlocks();

          for (int y = 0; y < Height(); ++y) {
            for (int x = 0; x < Width(); ++x) {
              ASSERT_EQ(lookup(ref, y * kOutputStride + x),
                        lookup(out, y * kOutputStride + x))
                  << "x == " << x << ", y == " << y << ", frac == " << frac
                  << ", step == " << step;
            }
          }
        }
      }
    }
  }
}

INSTANTIATE_TEST_SUITE_P(C, ConvolveScale2DTest,
                         ::testing::Values(ALL_SIZES_64(aom_scaled_2d_c)));

#if HAVE_NEON
INSTANTIATE_TEST_SUITE_P(NEON, ConvolveScale2DTest,
                         ::testing::Values(ALL_SIZES_64(aom_scaled_2d_neon)));
#endif  // HAVE_NEON

#if HAVE_NEON_DOTPROD
INSTANTIATE_TEST_SUITE_P(
    NEON_DOTPROD, ConvolveScale2DTest,
    ::testing::Values(ALL_SIZES_64(aom_scaled_2d_neon_dotprod)));
#endif  // HAVE_NEON_DOTPROD

#if HAVE_NEON_I8MM
INSTANTIATE_TEST_SUITE_P(
    NEON_I8MM, ConvolveScale2DTest,
    ::testing::Values(ALL_SIZES_64(aom_scaled_2d_neon_i8mm)));
#endif  // HAVE_NEON_I8MM

#if HAVE_SSSE3
INSTANTIATE_TEST_SUITE_P(SSSE3, ConvolveScale2DTest,
                         ::testing::Values(ALL_SIZES_64(aom_scaled_2d_ssse3)));
#endif  // HAVE_SSSE3

}  // namespace

Messung V0.5
C=96 H=92 G=93

¤ Dauer der Verarbeitung: 0.20 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.