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 "include/core/SkBitmap.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkImage.h"
11 #include "include/core/SkPixmap.h"
12 #include "include/core/SkSurface.h"
13 #include "src/core/SkAutoPixmapStorage.h"
14 #include "src/core/SkSpecialImage.h"
15 #include "src/core/SkSpecialSurface.h"
16 #include "tests/Test.h"
17
18 #include "include/gpu/GrBackendSurface.h"
19 #include "include/gpu/GrContext.h"
20 #include "src/gpu/GrContextPriv.h"
21 #include "src/gpu/GrProxyProvider.h"
22 #include "src/gpu/GrSurfaceProxy.h"
23 #include "src/gpu/GrTextureProxy.h"
24 #include "src/gpu/SkGr.h"
25
26
27 // This test creates backing resources exactly sized to [kFullSize x kFullSize].
28 // It then wraps them in an SkSpecialImage with only the center (red) region being active.
29 // It then draws the SkSpecialImage to a full sized (all blue) canvas and checks that none
30 // of the inactive (green) region leaked out.
31
32 static const int kSmallerSize = 10;
33 static const int kPad = 3;
34 static const int kFullSize = kSmallerSize + 2 * kPad;
35
36 // Create a bitmap with red in the center and green around it
create_bm()37 static SkBitmap create_bm() {
38 SkBitmap bm;
39 bm.allocN32Pixels(kFullSize, kFullSize, true);
40
41 SkCanvas temp(bm);
42
43 temp.clear(SK_ColorGREEN);
44 SkPaint p;
45 p.setColor(SK_ColorRED);
46 p.setAntiAlias(false);
47
48 temp.drawRect(SkRect::MakeXYWH(SkIntToScalar(kPad), SkIntToScalar(kPad),
49 SkIntToScalar(kSmallerSize), SkIntToScalar(kSmallerSize)),
50 p);
51
52 return bm;
53 }
54
55 // Basic test of the SkSpecialImage public API (e.g., peekTexture, peekPixels & draw)
test_image(const sk_sp<SkSpecialImage> & img,skiatest::Reporter * reporter,GrContext * context,bool isGPUBacked)56 static void test_image(const sk_sp<SkSpecialImage>& img, skiatest::Reporter* reporter,
57 GrContext* context, bool isGPUBacked) {
58 const SkIRect subset = img->subset();
59 REPORTER_ASSERT(reporter, kPad == subset.left());
60 REPORTER_ASSERT(reporter, kPad == subset.top());
61 REPORTER_ASSERT(reporter, kSmallerSize == subset.width());
62 REPORTER_ASSERT(reporter, kSmallerSize == subset.height());
63
64 //--------------
65 // Test that isTextureBacked reports the correct backing type
66 REPORTER_ASSERT(reporter, isGPUBacked == img->isTextureBacked());
67
68 //--------------
69 // Test asTextureProxyRef - as long as there is a context this should succeed
70 if (context) {
71 sk_sp<GrTextureProxy> proxy(img->asTextureProxyRef(context));
72 REPORTER_ASSERT(reporter, proxy);
73 }
74
75 //--------------
76 // Test getROPixels - this should always succeed regardless of backing store
77 SkBitmap bitmap;
78 REPORTER_ASSERT(reporter, img->getROPixels(&bitmap));
79 REPORTER_ASSERT(reporter, kSmallerSize == bitmap.width());
80 REPORTER_ASSERT(reporter, kSmallerSize == bitmap.height());
81
82 //--------------
83 // Test that draw restricts itself to the subset
84 sk_sp<SkSpecialSurface> surf(img->makeSurface(kN32_SkColorType, img->getColorSpace(),
85 SkISize::Make(kFullSize, kFullSize),
86 kPremul_SkAlphaType));
87
88 SkCanvas* canvas = surf->getCanvas();
89
90 canvas->clear(SK_ColorBLUE);
91 img->draw(canvas, SkIntToScalar(kPad), SkIntToScalar(kPad), nullptr);
92
93 SkBitmap bm;
94 bm.allocN32Pixels(kFullSize, kFullSize, false);
95
96 bool result = canvas->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), 0, 0);
97 SkASSERT_RELEASE(result);
98
99 // Only the center (red) portion should've been drawn into the canvas
100 REPORTER_ASSERT(reporter, SK_ColorBLUE == bm.getColor(kPad-1, kPad-1));
101 REPORTER_ASSERT(reporter, SK_ColorRED == bm.getColor(kPad, kPad));
102 REPORTER_ASSERT(reporter, SK_ColorRED == bm.getColor(kSmallerSize+kPad-1,
103 kSmallerSize+kPad-1));
104 REPORTER_ASSERT(reporter, SK_ColorBLUE == bm.getColor(kSmallerSize+kPad,
105 kSmallerSize+kPad));
106
107 //--------------
108 // Test that asImage & makeTightSurface return appropriately sized objects
109 // of the correct backing type
110 SkIRect newSubset = SkIRect::MakeWH(subset.width(), subset.height());
111 {
112 sk_sp<SkImage> tightImg(img->asImage(&newSubset));
113
114 REPORTER_ASSERT(reporter, tightImg->width() == subset.width());
115 REPORTER_ASSERT(reporter, tightImg->height() == subset.height());
116 REPORTER_ASSERT(reporter, isGPUBacked == tightImg->isTextureBacked());
117 SkPixmap tmpPixmap;
118 REPORTER_ASSERT(reporter, isGPUBacked != !!tightImg->peekPixels(&tmpPixmap));
119 }
120 {
121 sk_sp<SkSurface> tightSurf(img->makeTightSurface(kN32_SkColorType, img->getColorSpace(),
122 subset.size()));
123
124 REPORTER_ASSERT(reporter, tightSurf->width() == subset.width());
125 REPORTER_ASSERT(reporter, tightSurf->height() == subset.height());
126 GrBackendTexture backendTex = tightSurf->getBackendTexture(
127 SkSurface::kDiscardWrite_BackendHandleAccess);
128 REPORTER_ASSERT(reporter, isGPUBacked == backendTex.isValid());
129 SkPixmap tmpPixmap;
130 REPORTER_ASSERT(reporter, isGPUBacked != !!tightSurf->peekPixels(&tmpPixmap));
131 }
132 }
133
DEF_TEST(SpecialImage_Raster,reporter)134 DEF_TEST(SpecialImage_Raster, reporter) {
135 SkBitmap bm = create_bm();
136
137 sk_sp<SkSpecialImage> fullSImage(SkSpecialImage::MakeFromRaster(
138 SkIRect::MakeWH(kFullSize, kFullSize),
139 bm));
140
141 const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
142
143 {
144 sk_sp<SkSpecialImage> subSImg1(SkSpecialImage::MakeFromRaster(subset, bm));
145 test_image(subSImg1, reporter, nullptr, false);
146 }
147
148 {
149 sk_sp<SkSpecialImage> subSImg2(fullSImage->makeSubset(subset));
150 test_image(subSImg2, reporter, nullptr, false);
151 }
152 }
153
test_specialimage_image(skiatest::Reporter * reporter)154 static void test_specialimage_image(skiatest::Reporter* reporter) {
155 SkBitmap bm = create_bm();
156
157 sk_sp<SkImage> fullImage(SkImage::MakeFromBitmap(bm));
158
159 sk_sp<SkSpecialImage> fullSImage(SkSpecialImage::MakeFromImage(
160 nullptr,
161 SkIRect::MakeWH(kFullSize, kFullSize),
162 fullImage));
163
164 const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
165
166 {
167 sk_sp<SkSpecialImage> subSImg1(SkSpecialImage::MakeFromImage(nullptr, subset, fullImage));
168 test_image(subSImg1, reporter, nullptr, false);
169 }
170
171 {
172 sk_sp<SkSpecialImage> subSImg2(fullSImage->makeSubset(subset));
173 test_image(subSImg2, reporter, nullptr, false);
174 }
175 }
176
DEF_TEST(SpecialImage_Image_Legacy,reporter)177 DEF_TEST(SpecialImage_Image_Legacy, reporter) {
178 test_specialimage_image(reporter);
179 }
180
test_texture_backed(skiatest::Reporter * reporter,const sk_sp<SkSpecialImage> & orig,const sk_sp<SkSpecialImage> & gpuBacked)181 static void test_texture_backed(skiatest::Reporter* reporter,
182 const sk_sp<SkSpecialImage>& orig,
183 const sk_sp<SkSpecialImage>& gpuBacked) {
184 REPORTER_ASSERT(reporter, gpuBacked);
185 REPORTER_ASSERT(reporter, gpuBacked->isTextureBacked());
186 REPORTER_ASSERT(reporter, gpuBacked->uniqueID() == orig->uniqueID());
187 REPORTER_ASSERT(reporter, gpuBacked->subset().width() == orig->subset().width() &&
188 gpuBacked->subset().height() == orig->subset().height());
189 REPORTER_ASSERT(reporter, gpuBacked->getColorSpace() == orig->getColorSpace());
190 }
191
192 // Test out the SkSpecialImage::makeTextureImage entry point
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_MakeTexture,reporter,ctxInfo)193 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_MakeTexture, reporter, ctxInfo) {
194 GrContext* context = ctxInfo.grContext();
195 GrProxyProvider* proxyProvider = context->priv().proxyProvider();
196 SkBitmap bm = create_bm();
197
198 const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
199
200 {
201 // raster
202 sk_sp<SkSpecialImage> rasterImage(SkSpecialImage::MakeFromRaster(
203 SkIRect::MakeWH(kFullSize,
204 kFullSize),
205 bm));
206
207 {
208 sk_sp<SkSpecialImage> fromRaster(rasterImage->makeTextureImage(context));
209 test_texture_backed(reporter, rasterImage, fromRaster);
210 }
211
212 {
213 sk_sp<SkSpecialImage> subRasterImage(rasterImage->makeSubset(subset));
214
215 sk_sp<SkSpecialImage> fromSubRaster(subRasterImage->makeTextureImage(context));
216 test_texture_backed(reporter, subRasterImage, fromSubRaster);
217 }
218 }
219
220 {
221 // gpu
222 sk_sp<SkImage> rasterImage = SkImage::MakeFromBitmap(bm);
223 sk_sp<GrTextureProxy> proxy = proxyProvider->createTextureProxy(
224 rasterImage, 1, SkBudgeted::kNo, SkBackingFit::kExact);
225 if (!proxy) {
226 return;
227 }
228
229 sk_sp<SkSpecialImage> gpuImage(SkSpecialImage::MakeDeferredFromGpu(
230 context,
231 SkIRect::MakeWH(kFullSize, kFullSize),
232 kNeedNewImageUniqueID_SpecialImage,
233 std::move(proxy), nullptr));
234
235 {
236 sk_sp<SkSpecialImage> fromGPU(gpuImage->makeTextureImage(context));
237 test_texture_backed(reporter, gpuImage, fromGPU);
238 }
239
240 {
241 sk_sp<SkSpecialImage> subGPUImage(gpuImage->makeSubset(subset));
242
243 sk_sp<SkSpecialImage> fromSubGPU(subGPUImage->makeTextureImage(context));
244 test_texture_backed(reporter, subGPUImage, fromSubGPU);
245 }
246 }
247 }
248
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_Gpu,reporter,ctxInfo)249 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_Gpu, reporter, ctxInfo) {
250 GrContext* context = ctxInfo.grContext();
251 GrProxyProvider* proxyProvider = context->priv().proxyProvider();
252 SkBitmap bm = create_bm();
253 sk_sp<SkImage> rasterImage = SkImage::MakeFromBitmap(bm);
254
255 sk_sp<GrTextureProxy> proxy = proxyProvider->createTextureProxy(rasterImage, 1, SkBudgeted::kNo,
256 SkBackingFit::kExact);
257 if (!proxy) {
258 return;
259 }
260
261 sk_sp<SkSpecialImage> fullSImg(SkSpecialImage::MakeDeferredFromGpu(
262 context,
263 SkIRect::MakeWH(kFullSize, kFullSize),
264 kNeedNewImageUniqueID_SpecialImage,
265 proxy, nullptr));
266
267 const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
268
269 {
270 sk_sp<SkSpecialImage> subSImg1(SkSpecialImage::MakeDeferredFromGpu(
271 context, subset,
272 kNeedNewImageUniqueID_SpecialImage,
273 std::move(proxy), nullptr));
274 test_image(subSImg1, reporter, context, true);
275 }
276
277 {
278 sk_sp<SkSpecialImage> subSImg2(fullSImg->makeSubset(subset));
279 test_image(subSImg2, reporter, context, true);
280 }
281 }
282
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_ReadbackAndCachingSubsets_Gpu,reporter,ctxInfo)283 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_ReadbackAndCachingSubsets_Gpu, reporter, ctxInfo) {
284 GrContext* context = ctxInfo.grContext();
285 SkImageInfo ii = SkImageInfo::Make(50, 50, kN32_SkColorType, kPremul_SkAlphaType);
286 auto surface = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, ii);
287
288 // Fill out our surface:
289 // Green | Blue
290 // Red | Green
291 {
292 surface->getCanvas()->clear(SK_ColorGREEN);
293 SkPaint p;
294 p.setColor(SK_ColorRED);
295 surface->getCanvas()->drawRect(SkRect::MakeXYWH(0, 25, 25, 25), p);
296 p.setColor(SK_ColorBLUE);
297 surface->getCanvas()->drawRect(SkRect::MakeXYWH(25, 0, 25, 25), p);
298 }
299
300 auto image = surface->makeImageSnapshot();
301 auto redImg = SkSpecialImage::MakeFromImage(context, SkIRect::MakeXYWH(10, 30, 10, 10), image);
302 auto blueImg = SkSpecialImage::MakeFromImage(context, SkIRect::MakeXYWH(30, 10, 10, 10), image);
303
304 // This isn't necessary, but if it ever becomes false, then the cache collision bug that we're
305 // checking below is irrelevant.
306 REPORTER_ASSERT(reporter, redImg->uniqueID() == blueImg->uniqueID());
307
308 SkBitmap redBM, blueBM;
309 SkAssertResult(redImg->getROPixels(&redBM));
310 SkAssertResult(blueImg->getROPixels(&blueBM));
311
312 // Each image should read from the correct sub-rect. Past bugs (skbug.com/8448) have included:
313 // - Always reading back from (0, 0), producing green
314 // - Incorrectly hitting the cache on the 2nd read-back, causing blueBM to be red
315 REPORTER_ASSERT(reporter, redBM.getColor(0, 0) == SK_ColorRED,
316 "0x%08x != 0x%08x", redBM.getColor(0, 0), SK_ColorRED);
317 REPORTER_ASSERT(reporter, blueBM.getColor(0, 0) == SK_ColorBLUE,
318 "0x%08x != 0x%08x", blueBM.getColor(0, 0), SK_ColorBLUE);
319 }
320