tor-browser

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

TestSurfacePipeIntegration.cpp (13438B)


      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 "gtest/gtest.h"
      8 
      9 #include "mozilla/gfx/2D.h"
     10 #include "Common.h"
     11 #include "Decoder.h"
     12 #include "DecoderFactory.h"
     13 #include "SourceBuffer.h"
     14 #include "SurfacePipe.h"
     15 
     16 using namespace mozilla;
     17 using namespace mozilla::gfx;
     18 using namespace mozilla::image;
     19 
     20 namespace mozilla {
     21 namespace image {
     22 
     23 class TestSurfacePipeFactory {
     24 public:
     25  static SurfacePipe SimpleSurfacePipe() {
     26    SurfacePipe pipe;
     27    return pipe;
     28  }
     29 
     30  template <typename T>
     31  static SurfacePipe SurfacePipeFromPipeline(T&& aPipeline) {
     32    return SurfacePipe{std::move(aPipeline)};
     33  }
     34 
     35 private:
     36  TestSurfacePipeFactory() {}
     37 };
     38 
     39 }  // namespace image
     40 }  // namespace mozilla
     41 
     42 void CheckSurfacePipeMethodResults(SurfacePipe* aPipe, image::Decoder* aDecoder,
     43                                   const IntRect& aRect = IntRect(0, 0, 100,
     44                                                                  100)) {
     45  // Check that the pipeline ended up in the state we expect.  Note that we're
     46  // explicitly testing the SurfacePipe versions of these methods, so we don't
     47  // want to use AssertCorrectPipelineFinalState() here.
     48  EXPECT_TRUE(aPipe->IsSurfaceFinished());
     49  Maybe<SurfaceInvalidRect> invalidRect = aPipe->TakeInvalidRect();
     50  EXPECT_TRUE(invalidRect.isSome());
     51  EXPECT_EQ(OrientedIntRect(0, 0, 100, 100), invalidRect->mInputSpaceRect);
     52  EXPECT_EQ(OrientedIntRect(0, 0, 100, 100), invalidRect->mOutputSpaceRect);
     53 
     54  // Check the generated image.
     55  CheckGeneratedImage(aDecoder, aRect);
     56 
     57  // Reset and clear the image before the next test.
     58  aPipe->ResetToFirstRow();
     59  EXPECT_FALSE(aPipe->IsSurfaceFinished());
     60  invalidRect = aPipe->TakeInvalidRect();
     61  EXPECT_TRUE(invalidRect.isNothing());
     62 
     63  uint32_t count = 0;
     64  auto result = aPipe->WritePixels<uint32_t>([&]() {
     65    ++count;
     66    return AsVariant(BGRAColor::Transparent().AsPixel());
     67  });
     68  EXPECT_EQ(WriteState::FINISHED, result);
     69  EXPECT_EQ(100u * 100u, count);
     70 
     71  EXPECT_TRUE(aPipe->IsSurfaceFinished());
     72  invalidRect = aPipe->TakeInvalidRect();
     73  EXPECT_TRUE(invalidRect.isSome());
     74  EXPECT_EQ(OrientedIntRect(0, 0, 100, 100), invalidRect->mInputSpaceRect);
     75  EXPECT_EQ(OrientedIntRect(0, 0, 100, 100), invalidRect->mOutputSpaceRect);
     76 
     77  aPipe->ResetToFirstRow();
     78  EXPECT_FALSE(aPipe->IsSurfaceFinished());
     79  invalidRect = aPipe->TakeInvalidRect();
     80  EXPECT_TRUE(invalidRect.isNothing());
     81 }
     82 
     83 class ImageSurfacePipeIntegration : public ::testing::Test {
     84 protected:
     85  AutoInitializeImageLib mInit;
     86 };
     87 
     88 TEST_F(ImageSurfacePipeIntegration, SurfacePipe) {
     89  // Test that SurfacePipe objects can be initialized and move constructed.
     90  SurfacePipe pipe = TestSurfacePipeFactory::SimpleSurfacePipe();
     91 
     92  // Test that SurfacePipe objects can be move assigned.
     93  pipe = TestSurfacePipeFactory::SimpleSurfacePipe();
     94 
     95  // Test that SurfacePipe objects can be initialized with a pipeline.
     96  RefPtr<image::Decoder> decoder = CreateTrivialDecoder();
     97  ASSERT_TRUE(decoder != nullptr);
     98 
     99  auto sink = MakeUnique<SurfaceSink>();
    100  nsresult rv = sink->Configure(
    101      SurfaceConfig{decoder, IntSize(100, 100), SurfaceFormat::OS_RGBA, false});
    102  ASSERT_NS_SUCCEEDED(rv);
    103 
    104  pipe = TestSurfacePipeFactory::SurfacePipeFromPipeline(sink);
    105 
    106  // Test that WritePixels() gets passed through to the underlying pipeline.
    107  {
    108    uint32_t count = 0;
    109    auto result = pipe.WritePixels<uint32_t>([&]() {
    110      ++count;
    111      return AsVariant(BGRAColor::Green().AsPixel());
    112    });
    113    EXPECT_EQ(WriteState::FINISHED, result);
    114    EXPECT_EQ(100u * 100u, count);
    115    CheckSurfacePipeMethodResults(&pipe, decoder);
    116  }
    117 
    118  // Create a buffer the same size as one row of the surface, containing all
    119  // green pixels. We'll use this for the WriteBuffer() tests.
    120  uint32_t buffer[100];
    121  for (int i = 0; i < 100; ++i) {
    122    buffer[i] = BGRAColor::Green().AsPixel();
    123  }
    124 
    125  // Test that WriteBuffer() gets passed through to the underlying pipeline.
    126  {
    127    uint32_t count = 0;
    128    WriteState result = WriteState::NEED_MORE_DATA;
    129    while (result == WriteState::NEED_MORE_DATA) {
    130      result = pipe.WriteBuffer(buffer);
    131      ++count;
    132    }
    133    EXPECT_EQ(WriteState::FINISHED, result);
    134    EXPECT_EQ(100u, count);
    135    CheckSurfacePipeMethodResults(&pipe, decoder);
    136  }
    137 
    138  // Test that the 3 argument version of WriteBuffer() gets passed through to
    139  // the underlying pipeline.
    140  {
    141    uint32_t count = 0;
    142    WriteState result = WriteState::NEED_MORE_DATA;
    143    while (result == WriteState::NEED_MORE_DATA) {
    144      result = pipe.WriteBuffer(buffer, 0, 100);
    145      ++count;
    146    }
    147    EXPECT_EQ(WriteState::FINISHED, result);
    148    EXPECT_EQ(100u, count);
    149    CheckSurfacePipeMethodResults(&pipe, decoder);
    150  }
    151 
    152  // Test that WritePixelBlocks() gets passed through to the underlying
    153  // pipeline.
    154  {
    155    uint32_t count = 0;
    156    WriteState result = pipe.WritePixelBlocks<uint32_t>(
    157        [&](uint32_t* aBlockStart, int32_t aLength) {
    158          ++count;
    159          EXPECT_EQ(int32_t(100), aLength);
    160          memcpy(aBlockStart, buffer, 100 * sizeof(uint32_t));
    161          return std::make_tuple(int32_t(100), Maybe<WriteState>());
    162        });
    163 
    164    EXPECT_EQ(WriteState::FINISHED, result);
    165    EXPECT_EQ(100u, count);
    166    CheckSurfacePipeMethodResults(&pipe, decoder);
    167  }
    168 
    169  // Test that WriteEmptyRow() gets passed through to the underlying pipeline.
    170  {
    171    uint32_t count = 0;
    172    WriteState result = WriteState::NEED_MORE_DATA;
    173    while (result == WriteState::NEED_MORE_DATA) {
    174      result = pipe.WriteEmptyRow();
    175      ++count;
    176    }
    177    EXPECT_EQ(WriteState::FINISHED, result);
    178    EXPECT_EQ(100u, count);
    179    CheckSurfacePipeMethodResults(&pipe, decoder, IntRect(0, 0, 0, 0));
    180  }
    181 
    182  // Mark the frame as finished so we don't get an assertion.
    183  RawAccessFrameRef currentFrame = decoder->GetCurrentFrameRef();
    184  currentFrame->Finish();
    185 }
    186 
    187 TEST_F(ImageSurfacePipeIntegration, DeinterlaceDownscaleWritePixels) {
    188  RefPtr<image::Decoder> decoder = CreateTrivialDecoder();
    189  ASSERT_TRUE(decoder != nullptr);
    190 
    191  auto test = [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
    192    CheckWritePixels(aDecoder, aFilter,
    193                     /* aOutputRect = */ Some(IntRect(0, 0, 25, 25)));
    194  };
    195 
    196  WithFilterPipeline(
    197      decoder, test,
    198      DeinterlacingConfig<uint32_t>{/* mProgressiveDisplay = */ true},
    199      DownscalingConfig{IntSize(100, 100), SurfaceFormat::OS_RGBA},
    200      SurfaceConfig{decoder, IntSize(25, 25), SurfaceFormat::OS_RGBA, false});
    201 }
    202 
    203 TEST_F(ImageSurfacePipeIntegration,
    204       RemoveFrameRectBottomRightDownscaleWritePixels) {
    205  // This test case uses a frame rect that extends beyond the borders of the
    206  // image to the bottom and to the right. It looks roughly like this (with the
    207  // box made of '#'s representing the frame rect):
    208  //
    209  // +------------+
    210  // +            +
    211  // +      +------------+
    212  // +      +############+
    213  // +------+############+
    214  //        +############+
    215  //        +------------+
    216 
    217  RefPtr<image::Decoder> decoder = CreateTrivialDecoder();
    218  ASSERT_TRUE(decoder != nullptr);
    219 
    220  // Note that aInputWriteRect is 100x50 because RemoveFrameRectFilter ignores
    221  // trailing rows that don't show up in the output. (Leading rows unfortunately
    222  // can't be ignored.) So the action of the pipeline is as follows:
    223  //
    224  // (1) RemoveFrameRectFilter reads a 100x50 region of the input.
    225  //     (aInputWriteRect captures this fact.) The remaining 50 rows are ignored
    226  //     because they extend off the bottom of the image due to the frame rect's
    227  //     (50, 50) offset. The 50 columns on the right also don't end up in the
    228  //     output, so ultimately only a 50x50 region in the output contains data
    229  //     from the input. The filter's output is not 50x50, though, but 100x100,
    230  //     because what RemoveFrameRectFilter does is introduce blank rows or
    231  //     columns as necessary to transform an image that needs a frame rect into
    232  //     an image that doesn't.
    233  //
    234  // (2) DownscalingFilter reads the output of RemoveFrameRectFilter (100x100)
    235  //     and downscales it to 20x20.
    236  //
    237  // (3) The surface owned by SurfaceSink logically has only a 10x10 region
    238  //     region in it that's non-blank; this is the downscaled version of the
    239  //     50x50 region discussed in (1). (aOutputWriteRect captures this fact.)
    240  //     Some fuzz, as usual, is necessary when dealing with Lanczos
    241  //     downscaling.
    242 
    243  auto test = [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
    244    CheckWritePixels(aDecoder, aFilter,
    245                     /* aOutputRect = */ Some(IntRect(0, 0, 20, 20)),
    246                     /* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
    247                     /* aInputWriteRect = */ Some(IntRect(50, 50, 100, 50)),
    248                     /* aOutputWriteRect = */ Some(IntRect(10, 10, 10, 10)),
    249                     /* aFuzz = */ 0x33);
    250  };
    251 
    252  WithFilterPipeline(
    253      decoder, test, RemoveFrameRectConfig{IntRect(50, 50, 100, 100)},
    254      DownscalingConfig{IntSize(100, 100), SurfaceFormat::OS_RGBA},
    255      SurfaceConfig{decoder, IntSize(20, 20), SurfaceFormat::OS_RGBA, false});
    256 }
    257 
    258 TEST_F(ImageSurfacePipeIntegration,
    259       RemoveFrameRectTopLeftDownscaleWritePixels) {
    260  // This test case uses a frame rect that extends beyond the borders of the
    261  // image to the top and to the left. It looks roughly like this (with the
    262  // box made of '#'s representing the frame rect):
    263  //
    264  // +------------+
    265  // +############+
    266  // +############+------+
    267  // +############+      +
    268  // +------------+      +
    269  //        +            +
    270  //        +------------+
    271 
    272  RefPtr<image::Decoder> decoder = CreateTrivialDecoder();
    273  ASSERT_TRUE(decoder != nullptr);
    274 
    275  auto test = [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
    276    CheckWritePixels(aDecoder, aFilter,
    277                     /* aOutputRect = */ Some(IntRect(0, 0, 20, 20)),
    278                     /* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
    279                     /* aInputWriteRect = */ Some(IntRect(0, 0, 100, 100)),
    280                     /* aOutputWriteRect = */ Some(IntRect(0, 0, 10, 10)),
    281                     /* aFuzz = */ 0x21);
    282  };
    283 
    284  WithFilterPipeline(
    285      decoder, test, RemoveFrameRectConfig{IntRect(-50, -50, 100, 100)},
    286      DownscalingConfig{IntSize(100, 100), SurfaceFormat::OS_RGBA},
    287      SurfaceConfig{decoder, IntSize(20, 20), SurfaceFormat::OS_RGBA, false});
    288 }
    289 
    290 TEST_F(ImageSurfacePipeIntegration, DeinterlaceRemoveFrameRectWritePixels) {
    291  RefPtr<image::Decoder> decoder = CreateTrivialDecoder();
    292  ASSERT_TRUE(decoder != nullptr);
    293 
    294  // Note that aInputRect is the full 100x100 size even though
    295  // RemoveFrameRectFilter is part of this pipeline, because deinterlacing
    296  // requires reading every row.
    297 
    298  auto test = [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
    299    CheckWritePixels(aDecoder, aFilter,
    300                     /* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
    301                     /* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
    302                     /* aInputWriteRect = */ Some(IntRect(50, 50, 100, 100)),
    303                     /* aOutputWriteRect = */ Some(IntRect(50, 50, 50, 50)));
    304  };
    305 
    306  WithFilterPipeline(
    307      decoder, test,
    308      DeinterlacingConfig<uint32_t>{/* mProgressiveDisplay = */ true},
    309      RemoveFrameRectConfig{IntRect(50, 50, 100, 100)},
    310      SurfaceConfig{decoder, IntSize(100, 100), SurfaceFormat::OS_RGBA, false});
    311 }
    312 
    313 TEST_F(ImageSurfacePipeIntegration,
    314       DeinterlaceRemoveFrameRectDownscaleWritePixels) {
    315  RefPtr<image::Decoder> decoder = CreateTrivialDecoder();
    316  ASSERT_TRUE(decoder != nullptr);
    317 
    318  auto test = [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
    319    CheckWritePixels(aDecoder, aFilter,
    320                     /* aOutputRect = */ Some(IntRect(0, 0, 20, 20)),
    321                     /* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
    322                     /* aInputWriteRect = */ Some(IntRect(50, 50, 100, 100)),
    323                     /* aOutputWriteRect = */ Some(IntRect(10, 10, 10, 10)),
    324                     /* aFuzz = */ 33);
    325  };
    326 
    327  WithFilterPipeline(
    328      decoder, test,
    329      DeinterlacingConfig<uint32_t>{/* mProgressiveDisplay = */ true},
    330      RemoveFrameRectConfig{IntRect(50, 50, 100, 100)},
    331      DownscalingConfig{IntSize(100, 100), SurfaceFormat::OS_RGBA},
    332      SurfaceConfig{decoder, IntSize(20, 20), SurfaceFormat::OS_RGBA, false});
    333 }
    334 
    335 TEST_F(ImageSurfacePipeIntegration, ConfiguringHugeDeinterlacingBufferFails) {
    336  RefPtr<image::Decoder> decoder = CreateTrivialDecoder();
    337  ASSERT_TRUE(decoder != nullptr);
    338 
    339  // When DownscalingFilter is used, we may succeed in allocating an output
    340  // surface for huge images, because we only need to store the scaled-down
    341  // version of the image. However, regardless of downscaling,
    342  // DeinterlacingFilter needs to allocate a buffer as large as the size of the
    343  // input. This can cause OOMs on operating systems that allow overcommit. This
    344  // test makes sure that we reject such allocations.
    345  AssertConfiguringPipelineFails(
    346      decoder, DeinterlacingConfig<uint32_t>{/* mProgressiveDisplay = */ true},
    347      DownscalingConfig{IntSize(60000, 60000), SurfaceFormat::OS_RGBA},
    348      SurfaceConfig{decoder, IntSize(600, 600), SurfaceFormat::OS_RGBA, false});
    349 }