1 /*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "Test.h"
9
10 #include "SkBitmap.h"
11 #include "SkColorFilter.h"
12 #include "SkColorFilterImageFilter.h"
13 #include "SkImage.h"
14 #include "SkImageFilter.h"
15 #include "SkImageFilterCache.h"
16 #include "SkMatrix.h"
17 #include "SkSpecialImage.h"
18
19 static const int kSmallerSize = 10;
20 static const int kPad = 3;
21 static const int kFullSize = kSmallerSize + 2 * kPad;
22
create_bm()23 static SkBitmap create_bm() {
24 SkBitmap bm;
25 bm.allocN32Pixels(kFullSize, kFullSize, true);
26 bm.eraseColor(SK_ColorTRANSPARENT);
27 return bm;
28 }
29
make_filter()30 static sk_sp<SkImageFilter> make_filter() {
31 sk_sp<SkColorFilter> filter(SkColorFilter::MakeModeFilter(SK_ColorBLUE,
32 SkBlendMode::kSrcIn));
33 return SkColorFilterImageFilter::Make(std::move(filter), nullptr, nullptr);
34 }
35
36 // Ensure the cache can return a cached image
test_find_existing(skiatest::Reporter * reporter,const sk_sp<SkSpecialImage> & image,const sk_sp<SkSpecialImage> & subset)37 static void test_find_existing(skiatest::Reporter* reporter,
38 const sk_sp<SkSpecialImage>& image,
39 const sk_sp<SkSpecialImage>& subset) {
40 static const size_t kCacheSize = 1000000;
41 sk_sp<SkImageFilterCache> cache(SkImageFilterCache::Create(kCacheSize));
42
43 SkIRect clip = SkIRect::MakeWH(100, 100);
44 SkImageFilterCacheKey key1(0, SkMatrix::I(), clip, image->uniqueID(), image->subset());
45 SkImageFilterCacheKey key2(0, SkMatrix::I(), clip, subset->uniqueID(), subset->subset());
46
47 SkIPoint offset = SkIPoint::Make(3, 4);
48 auto filter = make_filter();
49 cache->set(key1, image.get(), offset, filter.get());
50
51 SkIPoint foundOffset;
52
53 sk_sp<SkSpecialImage> foundImage = cache->get(key1, &foundOffset);
54 REPORTER_ASSERT(reporter, foundImage);
55 REPORTER_ASSERT(reporter, offset == foundOffset);
56
57 REPORTER_ASSERT(reporter, !cache->get(key2, &foundOffset));
58 }
59
60 // If either id is different or the clip or the matrix are different the
61 // cached image won't be found. Even if it is caching the same bitmap.
test_dont_find_if_diff_key(skiatest::Reporter * reporter,const sk_sp<SkSpecialImage> & image,const sk_sp<SkSpecialImage> & subset)62 static void test_dont_find_if_diff_key(skiatest::Reporter* reporter,
63 const sk_sp<SkSpecialImage>& image,
64 const sk_sp<SkSpecialImage>& subset) {
65 static const size_t kCacheSize = 1000000;
66 sk_sp<SkImageFilterCache> cache(SkImageFilterCache::Create(kCacheSize));
67
68 SkIRect clip1 = SkIRect::MakeWH(100, 100);
69 SkIRect clip2 = SkIRect::MakeWH(200, 200);
70 SkImageFilterCacheKey key0(0, SkMatrix::I(), clip1, image->uniqueID(), image->subset());
71 SkImageFilterCacheKey key1(1, SkMatrix::I(), clip1, image->uniqueID(), image->subset());
72 SkImageFilterCacheKey key2(0, SkMatrix::MakeTrans(5, 5), clip1,
73 image->uniqueID(), image->subset());
74 SkImageFilterCacheKey key3(0, SkMatrix::I(), clip2, image->uniqueID(), image->subset());
75 SkImageFilterCacheKey key4(0, SkMatrix::I(), clip1, subset->uniqueID(), subset->subset());
76
77 SkIPoint offset = SkIPoint::Make(3, 4);
78 auto filter = make_filter();
79 cache->set(key0, image.get(), offset, filter.get());
80
81 SkIPoint foundOffset;
82 REPORTER_ASSERT(reporter, !cache->get(key1, &foundOffset));
83 REPORTER_ASSERT(reporter, !cache->get(key2, &foundOffset));
84 REPORTER_ASSERT(reporter, !cache->get(key3, &foundOffset));
85 REPORTER_ASSERT(reporter, !cache->get(key4, &foundOffset));
86 }
87
88 // Test purging when the max cache size is exceeded
test_internal_purge(skiatest::Reporter * reporter,const sk_sp<SkSpecialImage> & image)89 static void test_internal_purge(skiatest::Reporter* reporter, const sk_sp<SkSpecialImage>& image) {
90 SkASSERT(image->getSize());
91 const size_t kCacheSize = image->getSize() + 10;
92 sk_sp<SkImageFilterCache> cache(SkImageFilterCache::Create(kCacheSize));
93
94 SkIRect clip = SkIRect::MakeWH(100, 100);
95 SkImageFilterCacheKey key1(0, SkMatrix::I(), clip, image->uniqueID(), image->subset());
96 SkImageFilterCacheKey key2(1, SkMatrix::I(), clip, image->uniqueID(), image->subset());
97
98 SkIPoint offset = SkIPoint::Make(3, 4);
99 auto filter1 = make_filter();
100 cache->set(key1, image.get(), offset, filter1.get());
101
102 SkIPoint foundOffset;
103
104 REPORTER_ASSERT(reporter, cache->get(key1, &foundOffset));
105
106 // This should knock the first one out of the cache
107 auto filter2 = make_filter();
108 cache->set(key2, image.get(), offset, filter2.get());
109
110 REPORTER_ASSERT(reporter, cache->get(key2, &foundOffset));
111 REPORTER_ASSERT(reporter, !cache->get(key1, &foundOffset));
112 }
113
114 // Exercise the purgeByKey and purge methods
test_explicit_purging(skiatest::Reporter * reporter,const sk_sp<SkSpecialImage> & image,const sk_sp<SkSpecialImage> & subset)115 static void test_explicit_purging(skiatest::Reporter* reporter,
116 const sk_sp<SkSpecialImage>& image,
117 const sk_sp<SkSpecialImage>& subset) {
118 static const size_t kCacheSize = 1000000;
119 sk_sp<SkImageFilterCache> cache(SkImageFilterCache::Create(kCacheSize));
120
121 SkIRect clip = SkIRect::MakeWH(100, 100);
122 SkImageFilterCacheKey key1(0, SkMatrix::I(), clip, image->uniqueID(), image->subset());
123 SkImageFilterCacheKey key2(1, SkMatrix::I(), clip, subset->uniqueID(), image->subset());
124
125 SkIPoint offset = SkIPoint::Make(3, 4);
126 auto filter1 = make_filter();
127 auto filter2 = make_filter();
128 cache->set(key1, image.get(), offset, filter1.get());
129 cache->set(key2, image.get(), offset, filter2.get());
130 SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache->count());)
131
132 SkIPoint foundOffset;
133
134 REPORTER_ASSERT(reporter, cache->get(key1, &foundOffset));
135 REPORTER_ASSERT(reporter, cache->get(key2, &foundOffset));
136
137 cache->purgeByImageFilter(filter1.get());
138 SkDEBUGCODE(REPORTER_ASSERT(reporter, 1 == cache->count());)
139
140 REPORTER_ASSERT(reporter, !cache->get(key1, &foundOffset));
141 REPORTER_ASSERT(reporter, cache->get(key2, &foundOffset));
142
143 cache->purge();
144 SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->count());)
145
146 REPORTER_ASSERT(reporter, !cache->get(key1, &foundOffset));
147 REPORTER_ASSERT(reporter, !cache->get(key2, &foundOffset));
148 }
149
DEF_TEST(ImageFilterCache_RasterBacked,reporter)150 DEF_TEST(ImageFilterCache_RasterBacked, reporter) {
151 SkBitmap srcBM = create_bm();
152
153 const SkIRect& full = SkIRect::MakeWH(kFullSize, kFullSize);
154
155 sk_sp<SkSpecialImage> fullImg(SkSpecialImage::MakeFromRaster(full, srcBM));
156
157 const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
158
159 sk_sp<SkSpecialImage> subsetImg(SkSpecialImage::MakeFromRaster(subset, srcBM));
160
161 test_find_existing(reporter, fullImg, subsetImg);
162 test_dont_find_if_diff_key(reporter, fullImg, subsetImg);
163 test_internal_purge(reporter, fullImg);
164 test_explicit_purging(reporter, fullImg, subsetImg);
165 }
166
167
168 // Shared test code for both the raster and gpu-backed image cases
test_image_backed(skiatest::Reporter * reporter,GrContext * context,const sk_sp<SkImage> & srcImage)169 static void test_image_backed(skiatest::Reporter* reporter,
170 GrContext* context,
171 const sk_sp<SkImage>& srcImage) {
172 const SkIRect& full = SkIRect::MakeWH(kFullSize, kFullSize);
173
174 sk_sp<SkSpecialImage> fullImg(SkSpecialImage::MakeFromImage(context, full, srcImage));
175
176 const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
177
178 sk_sp<SkSpecialImage> subsetImg(SkSpecialImage::MakeFromImage(context, subset, srcImage));
179
180 test_find_existing(reporter, fullImg, subsetImg);
181 test_dont_find_if_diff_key(reporter, fullImg, subsetImg);
182 test_internal_purge(reporter, fullImg);
183 test_explicit_purging(reporter, fullImg, subsetImg);
184 }
185
DEF_TEST(ImageFilterCache_ImageBackedRaster,reporter)186 DEF_TEST(ImageFilterCache_ImageBackedRaster, reporter) {
187 SkBitmap srcBM = create_bm();
188
189 sk_sp<SkImage> srcImage(SkImage::MakeFromBitmap(srcBM));
190
191 test_image_backed(reporter, nullptr, srcImage);
192 }
193
194 #include "GrContext.h"
195 #include "GrContextPriv.h"
196 #include "GrProxyProvider.h"
197 #include "GrResourceProvider.h"
198 #include "GrSurfaceProxyPriv.h"
199 #include "GrTexture.h"
200 #include "GrTextureProxy.h"
201
create_proxy(GrProxyProvider * proxyProvider)202 static sk_sp<GrTextureProxy> create_proxy(GrProxyProvider* proxyProvider) {
203 SkBitmap srcBM = create_bm();
204 sk_sp<SkImage> srcImage(SkImage::MakeFromBitmap(srcBM));
205 return proxyProvider->createTextureProxy(srcImage, kNone_GrSurfaceFlags, 1,
206 SkBudgeted::kYes, SkBackingFit::kExact);
207 }
208
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_ImageBackedGPU,reporter,ctxInfo)209 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_ImageBackedGPU, reporter, ctxInfo) {
210 GrContext* context = ctxInfo.grContext();
211
212 sk_sp<GrTextureProxy> srcProxy(create_proxy(context->contextPriv().proxyProvider()));
213 if (!srcProxy) {
214 return;
215 }
216
217 if (!srcProxy->instantiate(context->contextPriv().resourceProvider())) {
218 return;
219 }
220 GrTexture* tex = srcProxy->peekTexture();
221
222 GrBackendTexture backendTex = tex->getBackendTexture();
223
224 GrSurfaceOrigin texOrigin = kTopLeft_GrSurfaceOrigin;
225 sk_sp<SkImage> srcImage(SkImage::MakeFromTexture(context,
226 backendTex,
227 texOrigin,
228 kRGBA_8888_SkColorType,
229 kPremul_SkAlphaType, nullptr,
230 nullptr, nullptr));
231 if (!srcImage) {
232 return;
233 }
234
235 GrSurfaceOrigin readBackOrigin;
236 GrBackendTexture readBackBackendTex = srcImage->getBackendTexture(false, &readBackOrigin);
237 if (!GrBackendTexture::TestingOnly_Equals(readBackBackendTex, backendTex)) {
238 ERRORF(reporter, "backend mismatch\n");
239 }
240 REPORTER_ASSERT(reporter, GrBackendTexture::TestingOnly_Equals(readBackBackendTex, backendTex));
241
242 if (readBackOrigin != texOrigin) {
243 ERRORF(reporter, "origin mismatch %d %d\n", readBackOrigin, texOrigin);
244 }
245 REPORTER_ASSERT(reporter, readBackOrigin == texOrigin);
246
247 test_image_backed(reporter, context, srcImage);
248 }
249
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_GPUBacked,reporter,ctxInfo)250 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_GPUBacked, reporter, ctxInfo) {
251 GrContext* context = ctxInfo.grContext();
252
253 sk_sp<GrTextureProxy> srcProxy(create_proxy(context->contextPriv().proxyProvider()));
254 if (!srcProxy) {
255 return;
256 }
257
258 const SkIRect& full = SkIRect::MakeWH(kFullSize, kFullSize);
259
260 sk_sp<SkSpecialImage> fullImg(SkSpecialImage::MakeDeferredFromGpu(
261 context, full,
262 kNeedNewImageUniqueID_SpecialImage,
263 srcProxy, nullptr));
264
265 const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
266
267 sk_sp<SkSpecialImage> subsetImg(SkSpecialImage::MakeDeferredFromGpu(
268 context, subset,
269 kNeedNewImageUniqueID_SpecialImage,
270 srcProxy, nullptr));
271
272 test_find_existing(reporter, fullImg, subsetImg);
273 test_dont_find_if_diff_key(reporter, fullImg, subsetImg);
274 test_internal_purge(reporter, fullImg);
275 test_explicit_purging(reporter, fullImg, subsetImg);
276 }
277