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 }