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