tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TestADAM7InterpolatingFilter.cpp (24364B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include <algorithm>
      8 #include <vector>
      9 
     10 #include "gtest/gtest.h"
     11 
     12 #include "mozilla/gfx/2D.h"
     13 #include "mozilla/Maybe.h"
     14 #include "Common.h"
     15 #include "Decoder.h"
     16 #include "DecoderFactory.h"
     17 #include "SourceBuffer.h"
     18 #include "SurfaceFilters.h"
     19 #include "SurfacePipe.h"
     20 
     21 using namespace mozilla;
     22 using namespace mozilla::gfx;
     23 using namespace mozilla::image;
     24 
     25 using std::generate;
     26 using std::vector;
     27 
     28 template <typename Func>
     29 void WithADAM7InterpolatingFilter(const IntSize& aSize, Func aFunc) {
     30  RefPtr<image::Decoder> decoder = CreateTrivialDecoder();
     31  ASSERT_TRUE(bool(decoder));
     32 
     33  WithFilterPipeline(
     34      decoder, std::forward<Func>(aFunc), ADAM7InterpolatingConfig{},
     35      SurfaceConfig{decoder, aSize, SurfaceFormat::OS_RGBA, false});
     36 }
     37 
     38 void AssertConfiguringADAM7InterpolatingFilterFails(const IntSize& aSize) {
     39  RefPtr<image::Decoder> decoder = CreateTrivialDecoder();
     40  ASSERT_TRUE(bool(decoder));
     41 
     42  AssertConfiguringPipelineFails(
     43      decoder, ADAM7InterpolatingConfig{},
     44      SurfaceConfig{decoder, aSize, SurfaceFormat::OS_RGBA, false});
     45 }
     46 
     47 uint8_t InterpolateByte(uint8_t aByteA, uint8_t aByteB, float aWeight) {
     48  return uint8_t(aByteA * aWeight + aByteB * (1.0f - aWeight));
     49 }
     50 
     51 BGRAColor InterpolateColors(BGRAColor aColor1, BGRAColor aColor2,
     52                            float aWeight) {
     53  return BGRAColor(InterpolateByte(aColor1.mBlue, aColor2.mBlue, aWeight),
     54                   InterpolateByte(aColor1.mGreen, aColor2.mGreen, aWeight),
     55                   InterpolateByte(aColor1.mRed, aColor2.mRed, aWeight),
     56                   InterpolateByte(aColor1.mAlpha, aColor2.mAlpha, aWeight));
     57 }
     58 
     59 enum class ShouldInterpolate { eYes, eNo };
     60 
     61 BGRAColor HorizontallyInterpolatedPixel(uint32_t aCol, uint32_t aWidth,
     62                                        const vector<float>& aWeights,
     63                                        ShouldInterpolate aShouldInterpolate,
     64                                        const vector<BGRAColor>& aColors) {
     65  // We cycle through the vector of weights forever.
     66  float weight = aWeights[aCol % aWeights.size()];
     67 
     68  // Find the columns of the two final pixels for this set of weights.
     69  uint32_t finalPixel1 = aCol - aCol % aWeights.size();
     70  uint32_t finalPixel2 = finalPixel1 + aWeights.size();
     71 
     72  // If |finalPixel2| is past the end of the row, that means that there is no
     73  // final pixel after the pixel at |finalPixel1|. In that case, we just want to
     74  // duplicate |finalPixel1|'s color until the end of the row. We can do that by
     75  // setting |finalPixel2| equal to |finalPixel1| so that the interpolation has
     76  // no effect.
     77  if (finalPixel2 >= aWidth) {
     78    finalPixel2 = finalPixel1;
     79  }
     80 
     81  // We cycle through the vector of colors forever (subject to the above
     82  // constraint about the end of the row).
     83  BGRAColor color1 = aColors[finalPixel1 % aColors.size()];
     84  BGRAColor color2 = aColors[finalPixel2 % aColors.size()];
     85 
     86  // If we're not interpolating, we treat all pixels which aren't final as
     87  // transparent. Since the number of weights we have is equal to the stride
     88  // between final pixels, we can check if |aCol| is a final pixel by checking
     89  // whether |aCol| is a multiple of |aWeights.size()|.
     90  if (aShouldInterpolate == ShouldInterpolate::eNo) {
     91    return aCol % aWeights.size() == 0 ? color1 : BGRAColor::Transparent();
     92  }
     93 
     94  // Interpolate.
     95  return InterpolateColors(color1, color2, weight);
     96 }
     97 
     98 vector<float>& InterpolationWeights(int32_t aStride) {
     99  // Precalculated interpolation weights. These are used to interpolate
    100  // between final pixels or between important rows. Although no interpolation
    101  // is actually applied to the previous final pixel or important row value,
    102  // the arrays still start with 1.0f, which is always skipped, primarily
    103  // because otherwise |stride1Weights| would have zero elements.
    104  static vector<float> stride8Weights = {1.0f,     7 / 8.0f, 6 / 8.0f,
    105                                         5 / 8.0f, 4 / 8.0f, 3 / 8.0f,
    106                                         2 / 8.0f, 1 / 8.0f};
    107  static vector<float> stride4Weights = {1.0f, 3 / 4.0f, 2 / 4.0f, 1 / 4.0f};
    108  static vector<float> stride2Weights = {1.0f, 1 / 2.0f};
    109  static vector<float> stride1Weights = {1.0f};
    110 
    111  switch (aStride) {
    112    case 8:
    113      return stride8Weights;
    114    case 4:
    115      return stride4Weights;
    116    case 2:
    117      return stride2Weights;
    118    case 1:
    119      return stride1Weights;
    120    default:
    121      MOZ_CRASH();
    122  }
    123 }
    124 
    125 int32_t ImportantRowStride(uint8_t aPass) {
    126  // The stride between important rows for each pass, with a dummy value for
    127  // the nonexistent pass 0 and for pass 8, since the tests run an extra pass to
    128  // make sure nothing breaks.
    129  static int32_t strides[] = {1, 8, 8, 4, 4, 2, 2, 1, 1};
    130 
    131  return strides[aPass];
    132 }
    133 
    134 size_t FinalPixelStride(uint8_t aPass) {
    135  // The stride between the final pixels in important rows for each pass, with
    136  // a dummy value for the nonexistent pass 0 and for pass 8, since the tests
    137  // run an extra pass to make sure nothing breaks.
    138  static size_t strides[] = {1, 8, 4, 4, 2, 2, 1, 1, 1};
    139 
    140  return strides[aPass];
    141 }
    142 
    143 bool IsImportantRow(int32_t aRow, uint8_t aPass) {
    144  return aRow % ImportantRowStride(aPass) == 0;
    145 }
    146 
    147 /**
    148 * ADAM7 breaks up the image into 8x8 blocks. On each of the 7 passes, a new
    149 * set of pixels in each block receives their final values, according to the
    150 * following pattern:
    151 *
    152 *    1 6 4 6 2 6 4 6
    153 *    7 7 7 7 7 7 7 7
    154 *    5 6 5 6 5 6 5 6
    155 *    7 7 7 7 7 7 7 7
    156 *    3 6 4 6 3 6 4 6
    157 *    7 7 7 7 7 7 7 7
    158 *    5 6 5 6 5 6 5 6
    159 *    7 7 7 7 7 7 7 7
    160 *
    161 * This function produces a row of pixels @aWidth wide, suitable for testing
    162 * horizontal interpolation on pass @aPass. The pattern of pixels used is
    163 * determined by @aPass and @aRow, which determine which pixels are final
    164 * according to the table above, and @aColors, from which the pixel values
    165 * are selected.
    166 *
    167 * There are two different behaviors: if |eNo| is passed for
    168 * @aShouldInterpolate, non-final pixels are treated as transparent. If |eNo|
    169 * is passed, non-final pixels get interpolated in from the surrounding final
    170 * pixels. The intention is that |eNo| is passed to generate input which will
    171 * be run through ADAM7InterpolatingFilter, and |eYes| is passed to generate
    172 * reference data to check that the filter is performing horizontal
    173 * interpolation correctly.
    174 *
    175 * This function does not perform vertical interpolation. Rows which aren't on
    176 * the current pass are filled with transparent pixels.
    177 *
    178 * @return a vector<BGRAColor> representing a row of pixels.
    179 */
    180 vector<BGRAColor> ADAM7HorizontallyInterpolatedRow(
    181    uint8_t aPass, uint32_t aRow, uint32_t aWidth,
    182    ShouldInterpolate aShouldInterpolate, const vector<BGRAColor>& aColors) {
    183  EXPECT_GT(aPass, 0);
    184  EXPECT_LE(aPass, 8);
    185  EXPECT_GT(aColors.size(), 0u);
    186 
    187  vector<BGRAColor> result(aWidth);
    188 
    189  if (IsImportantRow(aRow, aPass)) {
    190    vector<float>& weights = InterpolationWeights(FinalPixelStride(aPass));
    191 
    192    // Compute the horizontally interpolated row.
    193    uint32_t col = 0;
    194    generate(result.begin(), result.end(), [&] {
    195      return HorizontallyInterpolatedPixel(col++, aWidth, weights,
    196                                           aShouldInterpolate, aColors);
    197    });
    198  } else {
    199    // This is an unimportant row; just make the entire thing transparent.
    200    generate(result.begin(), result.end(),
    201             [] { return BGRAColor::Transparent(); });
    202  }
    203 
    204  EXPECT_EQ(result.size(), size_t(aWidth));
    205 
    206  return result;
    207 }
    208 
    209 WriteState WriteUninterpolatedPixels(SurfaceFilter* aFilter,
    210                                     const IntSize& aSize, uint8_t aPass,
    211                                     const vector<BGRAColor>& aColors) {
    212  WriteState result = WriteState::NEED_MORE_DATA;
    213 
    214  for (int32_t row = 0; row < aSize.height; ++row) {
    215    // Compute uninterpolated pixels for this row.
    216    vector<BGRAColor> pixels = ADAM7HorizontallyInterpolatedRow(
    217        aPass, row, aSize.width, ShouldInterpolate::eNo, aColors);
    218 
    219    // Write them to the surface.
    220    auto pixelIterator = pixels.cbegin();
    221    result = aFilter->WritePixelsToRow<uint32_t>(
    222        [&] { return AsVariant((*pixelIterator++).AsPixel()); });
    223 
    224    if (result != WriteState::NEED_MORE_DATA) {
    225      break;
    226    }
    227  }
    228 
    229  return result;
    230 }
    231 
    232 bool CheckHorizontallyInterpolatedImage(image::Decoder* aDecoder,
    233                                        const IntSize& aSize, uint8_t aPass,
    234                                        const vector<BGRAColor>& aColors) {
    235  RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
    236  RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface();
    237 
    238  for (int32_t row = 0; row < aSize.height; ++row) {
    239    if (!IsImportantRow(row, aPass)) {
    240      continue;  // Don't check rows which aren't important on this pass.
    241    }
    242 
    243    // Compute the expected pixels, *with* interpolation to match what the
    244    // filter should have done.
    245    vector<BGRAColor> expectedPixels = ADAM7HorizontallyInterpolatedRow(
    246        aPass, row, aSize.width, ShouldInterpolate::eYes, aColors);
    247 
    248    if (!RowHasPixels(surface, row, expectedPixels)) {
    249      return false;
    250    }
    251  }
    252 
    253  return true;
    254 }
    255 
    256 void CheckHorizontalInterpolation(const IntSize& aSize,
    257                                  const vector<BGRAColor>& aColors) {
    258  const IntRect surfaceRect(IntPoint(0, 0), aSize);
    259 
    260  WithADAM7InterpolatingFilter(
    261      aSize, [&](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
    262        // We check horizontal interpolation behavior for each pass
    263        // individually. In addition to the normal 7 passes that ADAM7 includes,
    264        // we also check an eighth pass to verify that nothing breaks if extra
    265        // data is written.
    266        for (uint8_t pass = 1; pass <= 8; ++pass) {
    267          // Write our color pattern to the surface. We don't perform any
    268          // interpolation when writing to the filter so that we can check that
    269          // the filter itself *does*.
    270          WriteState result =
    271              WriteUninterpolatedPixels(aFilter, aSize, pass, aColors);
    272 
    273          EXPECT_EQ(WriteState::FINISHED, result);
    274          AssertCorrectPipelineFinalState(aFilter, surfaceRect, surfaceRect);
    275 
    276          // Check that the generated image matches the expected pattern, with
    277          // interpolation applied.
    278          EXPECT_TRUE(CheckHorizontallyInterpolatedImage(aDecoder, aSize, pass,
    279                                                         aColors));
    280 
    281          // Prepare for the next pass.
    282          aFilter->ResetToFirstRow();
    283        }
    284      });
    285 }
    286 
    287 BGRAColor ADAM7RowColor(int32_t aRow, uint8_t aPass,
    288                        const vector<BGRAColor>& aColors) {
    289  EXPECT_LT(0, aPass);
    290  EXPECT_GE(8, aPass);
    291  EXPECT_LT(0u, aColors.size());
    292 
    293  // If this is an important row, select the color from the provided vector of
    294  // colors, which we cycle through infinitely. If not, just fill the row with
    295  // transparent pixels.
    296  return IsImportantRow(aRow, aPass) ? aColors[aRow % aColors.size()]
    297                                     : BGRAColor::Transparent();
    298 }
    299 
    300 WriteState WriteRowColorPixels(SurfaceFilter* aFilter, const IntSize& aSize,
    301                               uint8_t aPass,
    302                               const vector<BGRAColor>& aColors) {
    303  WriteState result = WriteState::NEED_MORE_DATA;
    304 
    305  for (int32_t row = 0; row < aSize.height; ++row) {
    306    const uint32_t color = ADAM7RowColor(row, aPass, aColors).AsPixel();
    307 
    308    // Fill the surface with |color| pixels.
    309    result =
    310        aFilter->WritePixelsToRow<uint32_t>([&] { return AsVariant(color); });
    311 
    312    if (result != WriteState::NEED_MORE_DATA) {
    313      break;
    314    }
    315  }
    316 
    317  return result;
    318 }
    319 
    320 bool CheckVerticallyInterpolatedImage(image::Decoder* aDecoder,
    321                                      const IntSize& aSize, uint8_t aPass,
    322                                      const vector<BGRAColor>& aColors) {
    323  vector<float>& weights = InterpolationWeights(ImportantRowStride(aPass));
    324 
    325  for (int32_t row = 0; row < aSize.height; ++row) {
    326    // Vertically interpolation takes place between two important rows. The
    327    // separation between the important rows is determined by the stride of this
    328    // pass. When there is no "next" important row because we'd run off the
    329    // bottom of the image, we use the same row for both. This matches
    330    // ADAM7InterpolatingFilter's behavior of duplicating the last important row
    331    // since there isn't another important row to vertically interpolate it
    332    // with.
    333    const int32_t stride = ImportantRowStride(aPass);
    334    const int32_t prevImportantRow = row - row % stride;
    335    const int32_t maybeNextImportantRow = prevImportantRow + stride;
    336    const int32_t nextImportantRow = maybeNextImportantRow < aSize.height
    337                                         ? maybeNextImportantRow
    338                                         : prevImportantRow;
    339 
    340    // Retrieve the colors for the important rows we're going to interpolate.
    341    const BGRAColor prevImportantRowColor =
    342        ADAM7RowColor(prevImportantRow, aPass, aColors);
    343    const BGRAColor nextImportantRowColor =
    344        ADAM7RowColor(nextImportantRow, aPass, aColors);
    345 
    346    // The weight we'll use for interpolation is also determined by the stride.
    347    // A row halfway between two important rows should have pixels that have a
    348    // 50% contribution from each of the important rows, for example.
    349    const float weight = weights[row % stride];
    350    const BGRAColor interpolatedColor =
    351        InterpolateColors(prevImportantRowColor, nextImportantRowColor, weight);
    352 
    353    // Generate a row of expected pixels. Every pixel in the row is always the
    354    // same color since we're only testing vertical interpolation between
    355    // solid-colored rows.
    356    vector<BGRAColor> expectedPixels(aSize.width);
    357    generate(expectedPixels.begin(), expectedPixels.end(),
    358             [&] { return interpolatedColor; });
    359 
    360    // Check that the pixels match.
    361    RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
    362    RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface();
    363    if (!RowHasPixels(surface, row, expectedPixels)) {
    364      return false;
    365    }
    366  }
    367 
    368  return true;
    369 }
    370 
    371 void CheckVerticalInterpolation(const IntSize& aSize,
    372                                const vector<BGRAColor>& aColors) {
    373  const IntRect surfaceRect(IntPoint(0, 0), aSize);
    374 
    375  WithADAM7InterpolatingFilter(aSize, [&](image::Decoder* aDecoder,
    376                                          SurfaceFilter* aFilter) {
    377    for (uint8_t pass = 1; pass <= 8; ++pass) {
    378      // Write a pattern of rows to the surface. Important rows will receive a
    379      // color selected from |aColors|; unimportant rows will be transparent.
    380      WriteState result = WriteRowColorPixels(aFilter, aSize, pass, aColors);
    381 
    382      EXPECT_EQ(WriteState::FINISHED, result);
    383      AssertCorrectPipelineFinalState(aFilter, surfaceRect, surfaceRect);
    384 
    385      // Check that the generated image matches the expected pattern, with
    386      // interpolation applied.
    387      EXPECT_TRUE(
    388          CheckVerticallyInterpolatedImage(aDecoder, aSize, pass, aColors));
    389 
    390      // Prepare for the next pass.
    391      aFilter->ResetToFirstRow();
    392    }
    393  });
    394 }
    395 
    396 void CheckInterpolation(const IntSize& aSize,
    397                        const vector<BGRAColor>& aColors) {
    398  CheckHorizontalInterpolation(aSize, aColors);
    399  CheckVerticalInterpolation(aSize, aColors);
    400 }
    401 
    402 void CheckADAM7InterpolatingWritePixels(const IntSize& aSize) {
    403  // This test writes 8 passes of green pixels (the seven ADAM7 passes, plus one
    404  // extra to make sure nothing goes wrong if we write too much input) and
    405  // verifies that the output is a solid green surface each time. Because all
    406  // the pixels are the same color, interpolation doesn't matter; we test the
    407  // correctness of the interpolation algorithm itself separately.
    408  WithADAM7InterpolatingFilter(
    409      aSize, [&](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
    410        IntRect rect(IntPoint(0, 0), aSize);
    411 
    412        for (int32_t pass = 1; pass <= 8; ++pass) {
    413          // We only actually write up to the last important row for each pass,
    414          // because that row unambiguously determines the remaining rows.
    415          const int32_t lastRow = aSize.height - 1;
    416          const int32_t lastImportantRow =
    417              lastRow - (lastRow % ImportantRowStride(pass));
    418          const IntRect inputWriteRect(0, 0, aSize.width, lastImportantRow + 1);
    419 
    420          CheckWritePixels(aDecoder, aFilter,
    421                           /* aOutputRect = */ Some(rect),
    422                           /* aInputRect = */ Some(rect),
    423                           /* aInputWriteRect = */ Some(inputWriteRect));
    424 
    425          aFilter->ResetToFirstRow();
    426          EXPECT_FALSE(aFilter->IsSurfaceFinished());
    427          Maybe<SurfaceInvalidRect> invalidRect = aFilter->TakeInvalidRect();
    428          EXPECT_TRUE(invalidRect.isNothing());
    429        }
    430      });
    431 }
    432 
    433 TEST(ImageADAM7InterpolatingFilter, WritePixels100_100)
    434 {
    435  CheckADAM7InterpolatingWritePixels(IntSize(100, 100));
    436 }
    437 
    438 TEST(ImageADAM7InterpolatingFilter, WritePixels99_99)
    439 {
    440  CheckADAM7InterpolatingWritePixels(IntSize(99, 99));
    441 }
    442 
    443 TEST(ImageADAM7InterpolatingFilter, WritePixels66_33)
    444 {
    445  CheckADAM7InterpolatingWritePixels(IntSize(66, 33));
    446 }
    447 
    448 TEST(ImageADAM7InterpolatingFilter, WritePixels33_66)
    449 {
    450  CheckADAM7InterpolatingWritePixels(IntSize(33, 66));
    451 }
    452 
    453 TEST(ImageADAM7InterpolatingFilter, WritePixels15_15)
    454 {
    455  CheckADAM7InterpolatingWritePixels(IntSize(15, 15));
    456 }
    457 
    458 TEST(ImageADAM7InterpolatingFilter, WritePixels9_9)
    459 {
    460  CheckADAM7InterpolatingWritePixels(IntSize(9, 9));
    461 }
    462 
    463 TEST(ImageADAM7InterpolatingFilter, WritePixels8_8)
    464 {
    465  CheckADAM7InterpolatingWritePixels(IntSize(8, 8));
    466 }
    467 
    468 TEST(ImageADAM7InterpolatingFilter, WritePixels7_7)
    469 {
    470  CheckADAM7InterpolatingWritePixels(IntSize(7, 7));
    471 }
    472 
    473 TEST(ImageADAM7InterpolatingFilter, WritePixels3_3)
    474 {
    475  CheckADAM7InterpolatingWritePixels(IntSize(3, 3));
    476 }
    477 
    478 TEST(ImageADAM7InterpolatingFilter, WritePixels1_1)
    479 {
    480  CheckADAM7InterpolatingWritePixels(IntSize(1, 1));
    481 }
    482 
    483 TEST(ImageADAM7InterpolatingFilter, TrivialInterpolation48_48)
    484 {
    485  CheckInterpolation(IntSize(48, 48), {BGRAColor::Green()});
    486 }
    487 
    488 TEST(ImageADAM7InterpolatingFilter, InterpolationOutput33_17)
    489 {
    490  // We check interpolation using irregular patterns to make sure that the
    491  // interpolation will look different for different passes.
    492  CheckInterpolation(
    493      IntSize(33, 17),
    494      {BGRAColor::Green(), BGRAColor::Red(),   BGRAColor::Green(),
    495       BGRAColor::Blue(),  BGRAColor::Blue(),  BGRAColor::Blue(),
    496       BGRAColor::Red(),   BGRAColor::Green(), BGRAColor::Red(),
    497       BGRAColor::Red(),   BGRAColor::Blue(),  BGRAColor::Blue(),
    498       BGRAColor::Green(), BGRAColor::Blue(),  BGRAColor::Red(),
    499       BGRAColor::Blue(),  BGRAColor::Red(),   BGRAColor::Green(),
    500       BGRAColor::Blue(),  BGRAColor::Red(),   BGRAColor::Green(),
    501       BGRAColor::Red(),   BGRAColor::Red(),   BGRAColor::Blue(),
    502       BGRAColor::Blue(),  BGRAColor::Blue(),  BGRAColor::Red(),
    503       BGRAColor::Green(), BGRAColor::Green(), BGRAColor::Blue(),
    504       BGRAColor::Red(),   BGRAColor::Blue()});
    505 }
    506 
    507 TEST(ImageADAM7InterpolatingFilter, InterpolationOutput32_16)
    508 {
    509  CheckInterpolation(
    510      IntSize(32, 16),
    511      {BGRAColor::Green(), BGRAColor::Red(),   BGRAColor::Green(),
    512       BGRAColor::Blue(),  BGRAColor::Blue(),  BGRAColor::Blue(),
    513       BGRAColor::Red(),   BGRAColor::Green(), BGRAColor::Red(),
    514       BGRAColor::Red(),   BGRAColor::Blue(),  BGRAColor::Blue(),
    515       BGRAColor::Green(), BGRAColor::Blue(),  BGRAColor::Red(),
    516       BGRAColor::Blue(),  BGRAColor::Red(),   BGRAColor::Green(),
    517       BGRAColor::Blue(),  BGRAColor::Red(),   BGRAColor::Green(),
    518       BGRAColor::Red(),   BGRAColor::Red(),   BGRAColor::Blue(),
    519       BGRAColor::Blue(),  BGRAColor::Blue(),  BGRAColor::Red(),
    520       BGRAColor::Green(), BGRAColor::Green(), BGRAColor::Blue(),
    521       BGRAColor::Red(),   BGRAColor::Blue()});
    522 }
    523 
    524 TEST(ImageADAM7InterpolatingFilter, InterpolationOutput31_15)
    525 {
    526  CheckInterpolation(
    527      IntSize(31, 15),
    528      {BGRAColor::Green(), BGRAColor::Red(),   BGRAColor::Green(),
    529       BGRAColor::Blue(),  BGRAColor::Blue(),  BGRAColor::Blue(),
    530       BGRAColor::Red(),   BGRAColor::Green(), BGRAColor::Red(),
    531       BGRAColor::Red(),   BGRAColor::Blue(),  BGRAColor::Blue(),
    532       BGRAColor::Green(), BGRAColor::Blue(),  BGRAColor::Red(),
    533       BGRAColor::Blue(),  BGRAColor::Red(),   BGRAColor::Green(),
    534       BGRAColor::Blue(),  BGRAColor::Red(),   BGRAColor::Green(),
    535       BGRAColor::Red(),   BGRAColor::Red(),   BGRAColor::Blue(),
    536       BGRAColor::Blue(),  BGRAColor::Blue(),  BGRAColor::Red(),
    537       BGRAColor::Green(), BGRAColor::Green(), BGRAColor::Blue(),
    538       BGRAColor::Red(),   BGRAColor::Blue()});
    539 }
    540 
    541 TEST(ImageADAM7InterpolatingFilter, InterpolationOutput17_33)
    542 {
    543  CheckInterpolation(IntSize(17, 33),
    544                     {BGRAColor::Green(), BGRAColor::Red(), BGRAColor::Green(),
    545                      BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Green(),
    546                      BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Blue(),
    547                      BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Green(),
    548                      BGRAColor::Green(), BGRAColor::Red(), BGRAColor::Red(),
    549                      BGRAColor::Blue()});
    550 }
    551 
    552 TEST(ImageADAM7InterpolatingFilter, InterpolationOutput16_32)
    553 {
    554  CheckInterpolation(IntSize(16, 32),
    555                     {BGRAColor::Green(), BGRAColor::Red(), BGRAColor::Green(),
    556                      BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Green(),
    557                      BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Blue(),
    558                      BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Green(),
    559                      BGRAColor::Green(), BGRAColor::Red(), BGRAColor::Red(),
    560                      BGRAColor::Blue()});
    561 }
    562 
    563 TEST(ImageADAM7InterpolatingFilter, InterpolationOutput15_31)
    564 {
    565  CheckInterpolation(IntSize(15, 31),
    566                     {BGRAColor::Green(), BGRAColor::Red(), BGRAColor::Green(),
    567                      BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Green(),
    568                      BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Blue(),
    569                      BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Green(),
    570                      BGRAColor::Green(), BGRAColor::Red(), BGRAColor::Red(),
    571                      BGRAColor::Blue()});
    572 }
    573 
    574 TEST(ImageADAM7InterpolatingFilter, InterpolationOutput9_9)
    575 {
    576  CheckInterpolation(IntSize(9, 9),
    577                     {BGRAColor::Blue(), BGRAColor::Blue(), BGRAColor::Red(),
    578                      BGRAColor::Green(), BGRAColor::Green(), BGRAColor::Red(),
    579                      BGRAColor::Red(), BGRAColor::Blue()});
    580 }
    581 
    582 TEST(ImageADAM7InterpolatingFilter, InterpolationOutput8_8)
    583 {
    584  CheckInterpolation(IntSize(8, 8),
    585                     {BGRAColor::Blue(), BGRAColor::Blue(), BGRAColor::Red(),
    586                      BGRAColor::Green(), BGRAColor::Green(), BGRAColor::Red(),
    587                      BGRAColor::Red(), BGRAColor::Blue()});
    588 }
    589 
    590 TEST(ImageADAM7InterpolatingFilter, InterpolationOutput7_7)
    591 {
    592  CheckInterpolation(IntSize(7, 7),
    593                     {BGRAColor::Blue(), BGRAColor::Blue(), BGRAColor::Red(),
    594                      BGRAColor::Green(), BGRAColor::Green(), BGRAColor::Red(),
    595                      BGRAColor::Red(), BGRAColor::Blue()});
    596 }
    597 
    598 TEST(ImageADAM7InterpolatingFilter, InterpolationOutput3_3)
    599 {
    600  CheckInterpolation(IntSize(3, 3), {BGRAColor::Green(), BGRAColor::Red(),
    601                                     BGRAColor::Blue(), BGRAColor::Red()});
    602 }
    603 
    604 TEST(ImageADAM7InterpolatingFilter, InterpolationOutput1_1)
    605 {
    606  CheckInterpolation(IntSize(1, 1), {BGRAColor::Blue()});
    607 }
    608 
    609 TEST(ImageADAM7InterpolatingFilter, ADAM7InterpolationFailsFor0_0)
    610 {
    611  // A 0x0 input size is invalid, so configuration should fail.
    612  AssertConfiguringADAM7InterpolatingFilterFails(IntSize(0, 0));
    613 }
    614 
    615 TEST(ImageADAM7InterpolatingFilter, ADAM7InterpolationFailsForMinus1_Minus1)
    616 {
    617  // A negative input size is invalid, so configuration should fail.
    618  AssertConfiguringADAM7InterpolatingFilterFails(IntSize(-1, -1));
    619 }