/src/mozilla-central/image/test/gtest/TestSurfaceCache.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
2 | | * This Source Code Form is subject to the terms of the Mozilla Public |
3 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
5 | | |
6 | | #include "gtest/gtest.h" |
7 | | |
8 | | #include "Common.h" |
9 | | #include "imgIContainer.h" |
10 | | #include "imgITools.h" |
11 | | #include "ImageFactory.h" |
12 | | #include "mozilla/gfx/2D.h" |
13 | | #include "mozilla/RefPtr.h" |
14 | | #include "nsIInputStream.h" |
15 | | #include "nsString.h" |
16 | | #include "ProgressTracker.h" |
17 | | |
18 | | using namespace mozilla; |
19 | | using namespace mozilla::gfx; |
20 | | using namespace mozilla::image; |
21 | | |
22 | | class ImageSurfaceCache : public ::testing::Test |
23 | | { |
24 | | protected: |
25 | | AutoInitializeImageLib mInit; |
26 | | }; |
27 | | |
28 | | TEST_F(ImageSurfaceCache, Factor2) |
29 | 0 | { |
30 | 0 | ImageTestCase testCase = GreenPNGTestCase(); |
31 | 0 |
|
32 | 0 | // Create an image. |
33 | 0 | RefPtr<Image> image = |
34 | 0 | ImageFactory::CreateAnonymousImage(nsDependentCString(testCase.mMimeType)); |
35 | 0 | ASSERT_TRUE(!image->HasError()); |
36 | 0 |
|
37 | 0 | nsCOMPtr<nsIInputStream> inputStream = LoadFile(testCase.mPath); |
38 | 0 | ASSERT_TRUE(inputStream); |
39 | 0 |
|
40 | 0 | // Figure out how much data we have. |
41 | 0 | uint64_t length; |
42 | 0 | nsresult rv = inputStream->Available(&length); |
43 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
44 | 0 |
|
45 | 0 | // Ensures we meet the threshold for FLAG_SYNC_DECODE_IF_FAST to do sync |
46 | 0 | // decoding without the implications of FLAG_SYNC_DECODE. |
47 | 0 | ASSERT_LT(length, static_cast<uint64_t>(gfxPrefs::ImageMemDecodeBytesAtATime())); |
48 | 0 |
|
49 | 0 | // Write the data into the image. |
50 | 0 | rv = image->OnImageDataAvailable(nullptr, nullptr, inputStream, 0, |
51 | 0 | static_cast<uint32_t>(length)); |
52 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
53 | 0 |
|
54 | 0 | // Let the image know we've sent all the data. |
55 | 0 | rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true); |
56 | 0 | ASSERT_TRUE(NS_SUCCEEDED(rv)); |
57 | 0 |
|
58 | 0 | RefPtr<ProgressTracker> tracker = image->GetProgressTracker(); |
59 | 0 | tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE); |
60 | 0 |
|
61 | 0 | const uint32_t whichFrame = imgIContainer::FRAME_CURRENT; |
62 | 0 |
|
63 | 0 | // FLAG_SYNC_DECODE will make RasterImage::LookupFrame use |
64 | 0 | // SurfaceCache::Lookup to force an exact match lookup (and potential decode). |
65 | 0 | const uint32_t exactFlags = imgIContainer::FLAG_HIGH_QUALITY_SCALING | |
66 | 0 | imgIContainer::FLAG_SYNC_DECODE; |
67 | 0 |
|
68 | 0 | // If the data stream is small enough, as we assert above, |
69 | 0 | // FLAG_SYNC_DECODE_IF_FAST will allow us to decode sync, but avoid forcing |
70 | 0 | // SurfaceCache::Lookup. Instead it will use SurfaceCache::LookupBestMatch. |
71 | 0 | const uint32_t bestMatchFlags = imgIContainer::FLAG_HIGH_QUALITY_SCALING | |
72 | 0 | imgIContainer::FLAG_SYNC_DECODE_IF_FAST; |
73 | 0 |
|
74 | 0 | // We need the default threshold to be enabled (otherwise we should disable |
75 | 0 | // this test). |
76 | 0 | int32_t threshold = gfxPrefs::ImageCacheFactor2ThresholdSurfaces(); |
77 | 0 | ASSERT_TRUE(threshold >= 0); |
78 | 0 |
|
79 | 0 | // We need to know what the native sizes are, otherwise factor of 2 mode will |
80 | 0 | // be disabled. |
81 | 0 | size_t nativeSizes = image->GetNativeSizesLength(); |
82 | 0 | ASSERT_EQ(nativeSizes, 1u); |
83 | 0 |
|
84 | 0 | // Threshold is the native size count and the pref threshold added together. |
85 | 0 | // Make sure the image is big enough that we can simply decrement and divide |
86 | 0 | // off the size as we please and not hit unexpected duplicates. |
87 | 0 | int32_t totalThreshold = static_cast<int32_t>(nativeSizes) + threshold; |
88 | 0 | ASSERT_TRUE(testCase.mSize.width > totalThreshold * 4); |
89 | 0 |
|
90 | 0 | // Request a bunch of slightly different sizes. We won't trip factor of 2 mode |
91 | 0 | // in this loop. |
92 | 0 | IntSize size = testCase.mSize; |
93 | 0 | for (int32_t i = 0; i <= totalThreshold; ++i) { |
94 | 0 | RefPtr<SourceSurface> surf = |
95 | 0 | image->GetFrameAtSize(size, whichFrame, bestMatchFlags); |
96 | 0 | ASSERT_TRUE(surf); |
97 | 0 | EXPECT_EQ(surf->GetSize(), size); |
98 | 0 |
|
99 | 0 | size.width -= 1; |
100 | 0 | size.height -= 1; |
101 | 0 | } |
102 | 0 |
|
103 | 0 | // Now let's ask for a new size. Despite this being sync, it will return |
104 | 0 | // the closest factor of 2 size we have and not the requested size. |
105 | 0 | RefPtr<SourceSurface> surf = |
106 | 0 | image->GetFrameAtSize(size, whichFrame, bestMatchFlags); |
107 | 0 | ASSERT_TRUE(surf); |
108 | 0 |
|
109 | 0 | EXPECT_EQ(surf->GetSize(), testCase.mSize); |
110 | 0 |
|
111 | 0 | // Now we should be in factor of 2 mode but unless we trigger a decode no |
112 | 0 | // pruning of the old sized surfaces should happen. |
113 | 0 | size = testCase.mSize; |
114 | 0 | for (int32_t i = 0; i < totalThreshold; ++i) { |
115 | 0 | RefPtr<SourceSurface> surf = |
116 | 0 | image->GetFrameAtSize(size, whichFrame, bestMatchFlags); |
117 | 0 | ASSERT_TRUE(surf); |
118 | 0 | EXPECT_EQ(surf->GetSize(), size); |
119 | 0 |
|
120 | 0 | size.width -= 1; |
121 | 0 | size.height -= 1; |
122 | 0 | } |
123 | 0 |
|
124 | 0 | // Now force an existing surface to be marked as explicit so that it |
125 | 0 | // won't get freed upon pruning (gets marked in the Lookup). |
126 | 0 | size.width += 1; |
127 | 0 | size.height += 1; |
128 | 0 | surf = image->GetFrameAtSize(size, whichFrame, exactFlags); |
129 | 0 | ASSERT_TRUE(surf); |
130 | 0 | EXPECT_EQ(surf->GetSize(), size); |
131 | 0 |
|
132 | 0 | // Now force a new decode to happen by getting a new factor of 2 size. |
133 | 0 | size.width = testCase.mSize.width / 2 - 1; |
134 | 0 | size.height = testCase.mSize.height / 2 - 1; |
135 | 0 | surf = image->GetFrameAtSize(size, whichFrame, bestMatchFlags); |
136 | 0 | ASSERT_TRUE(surf); |
137 | 0 | EXPECT_EQ(surf->GetSize().width, testCase.mSize.width / 2); |
138 | 0 | EXPECT_EQ(surf->GetSize().height, testCase.mSize.height / 2); |
139 | 0 |
|
140 | 0 | // The decode above would have forced a pruning to happen, so now if |
141 | 0 | // we request all of the sizes we used to have decoded, only the explicit |
142 | 0 | // size should have been kept. |
143 | 0 | size = testCase.mSize; |
144 | 0 | for (int32_t i = 0; i < totalThreshold - 1; ++i) { |
145 | 0 | RefPtr<SourceSurface> surf = |
146 | 0 | image->GetFrameAtSize(size, whichFrame, bestMatchFlags); |
147 | 0 | ASSERT_TRUE(surf); |
148 | 0 | EXPECT_EQ(surf->GetSize(), testCase.mSize); |
149 | 0 |
|
150 | 0 | size.width -= 1; |
151 | 0 | size.height -= 1; |
152 | 0 | } |
153 | 0 |
|
154 | 0 | // This lookup finds the surface that already existed that we later marked |
155 | 0 | // as explicit. It should still exist after pruning. |
156 | 0 | surf = image->GetFrameAtSize(size, whichFrame, bestMatchFlags); |
157 | 0 | ASSERT_TRUE(surf); |
158 | 0 | EXPECT_EQ(surf->GetSize(), size); |
159 | 0 | } |