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/SkAlphaType.h"
9 #include "include/core/SkBitmap.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColor.h"
12 #include "include/core/SkColorSpace.h"
13 #include "include/core/SkColorType.h"
14 #include "include/core/SkImage.h"
15 #include "include/core/SkImageInfo.h"
16 #include "include/core/SkPaint.h"
17 #include "include/core/SkPixmap.h"
18 #include "include/core/SkRect.h"
19 #include "include/core/SkRefCnt.h"
20 #include "include/core/SkScalar.h"
21 #include "include/core/SkSize.h"
22 #include "include/core/SkSurface.h"
23 #include "include/core/SkSurfaceProps.h"
24 #include "include/core/SkTypes.h"
25 #include "include/gpu/GrBackendSurface.h"
26 #include "include/gpu/GrDirectContext.h"
27 #include "src/core/SkSpecialImage.h"
28 #include "src/core/SkSpecialSurface.h"
29 #include "src/gpu/ganesh/GrColorInfo.h" // IWYU pragma: keep
30 #include "src/gpu/ganesh/GrSurfaceProxyView.h"
31 #include "src/gpu/ganesh/SkGr.h"
32 #include "tests/CtsEnforcement.h"
33 #include "tests/Test.h"
34 
35 #include <utility>
36 
37 class GrRecordingContext;
38 struct GrContextOptions;
39 
40 // This test creates backing resources exactly sized to [kFullSize x kFullSize].
41 // It then wraps them in an SkSpecialImage with only the center (red) region being active.
42 // It then draws the SkSpecialImage to a full sized (all blue) canvas and checks that none
43 // of the inactive (green) region leaked out.
44 
45 static const int kSmallerSize = 10;
46 static const int kPad = 3;
47 static const int kFullSize = kSmallerSize + 2 * kPad;
48 
49 // Create a bitmap with red in the center and green around it
create_bm()50 static SkBitmap create_bm() {
51     SkImageInfo ii = SkImageInfo::Make(kFullSize, kFullSize, kRGBA_8888_SkColorType,
52                                        kPremul_SkAlphaType);
53 
54     SkBitmap bm;
55     bm.allocPixels(ii);
56 
57     SkCanvas temp(bm);
58 
59     temp.clear(SK_ColorGREEN);
60     SkPaint p;
61     p.setColor(SK_ColorRED);
62     p.setAntiAlias(false);
63 
64     temp.drawRect(SkRect::MakeXYWH(SkIntToScalar(kPad), SkIntToScalar(kPad),
65                                    SkIntToScalar(kSmallerSize), SkIntToScalar(kSmallerSize)),
66                   p);
67 
68     bm.setImmutable();
69     return bm;
70 }
71 
72 // Basic test of the SkSpecialImage public API (e.g., peekTexture, peekPixels & draw)
test_image(const sk_sp<SkSpecialImage> & img,skiatest::Reporter * reporter,GrRecordingContext * rContext,bool isGPUBacked)73 static void test_image(const sk_sp<SkSpecialImage>& img, skiatest::Reporter* reporter,
74                        GrRecordingContext* rContext, bool isGPUBacked) {
75     const SkIRect subset = img->subset();
76     REPORTER_ASSERT(reporter, kPad == subset.left());
77     REPORTER_ASSERT(reporter, kPad == subset.top());
78     REPORTER_ASSERT(reporter, kSmallerSize == subset.width());
79     REPORTER_ASSERT(reporter, kSmallerSize == subset.height());
80 
81     //--------------
82     // Test that isTextureBacked reports the correct backing type
83     REPORTER_ASSERT(reporter, isGPUBacked == img->isTextureBacked());
84 
85     //--------------
86     // Test view - as long as there is a context this should succeed
87     if (rContext) {
88         GrSurfaceProxyView view = img->view(rContext);
89         REPORTER_ASSERT(reporter, view.asTextureProxy());
90     }
91 
92     //--------------
93     // Test getROPixels - this only works for raster-backed special images
94     if (!img->isTextureBacked()) {
95         SkBitmap bitmap;
96         REPORTER_ASSERT(reporter, img->getROPixels(&bitmap));
97         REPORTER_ASSERT(reporter, kSmallerSize == bitmap.width());
98         REPORTER_ASSERT(reporter, kSmallerSize == bitmap.height());
99     }
100 
101     //--------------
102     // Test that draw restricts itself to the subset
103     sk_sp<SkSpecialSurface> surf(img->makeSurface(kN32_SkColorType, img->getColorSpace(),
104                                                   SkISize::Make(kFullSize, kFullSize),
105                                                   kPremul_SkAlphaType, SkSurfaceProps()));
106 
107     SkCanvas* canvas = surf->getCanvas();
108 
109     canvas->clear(SK_ColorBLUE);
110     img->draw(canvas, SkIntToScalar(kPad), SkIntToScalar(kPad));
111 
112     SkBitmap bm;
113     bm.allocN32Pixels(kFullSize, kFullSize, false);
114 
115     bool result = canvas->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), 0, 0);
116     SkASSERT_RELEASE(result);
117 
118     // Only the center (red) portion should've been drawn into the canvas
119     REPORTER_ASSERT(reporter, SK_ColorBLUE == bm.getColor(kPad-1, kPad-1));
120     REPORTER_ASSERT(reporter, SK_ColorRED  == bm.getColor(kPad, kPad));
121     REPORTER_ASSERT(reporter, SK_ColorRED  == bm.getColor(kSmallerSize+kPad-1,
122                                                           kSmallerSize+kPad-1));
123     REPORTER_ASSERT(reporter, SK_ColorBLUE == bm.getColor(kSmallerSize+kPad,
124                                                           kSmallerSize+kPad));
125 
126     //--------------
127     // Test that asImage & makeTightSurface return appropriately sized objects
128     // of the correct backing type
129     SkIRect newSubset = SkIRect::MakeWH(subset.width(), subset.height());
130     {
131         sk_sp<SkImage> tightImg(img->asImage(&newSubset));
132 
133         REPORTER_ASSERT(reporter, tightImg->width() == subset.width());
134         REPORTER_ASSERT(reporter, tightImg->height() == subset.height());
135         REPORTER_ASSERT(reporter, isGPUBacked == tightImg->isTextureBacked());
136         SkPixmap tmpPixmap;
137         REPORTER_ASSERT(reporter, isGPUBacked != !!tightImg->peekPixels(&tmpPixmap));
138     }
139     {
140         sk_sp<SkSurface> tightSurf(img->makeTightSurface(kN32_SkColorType, img->getColorSpace(),
141                                                          subset.size()));
142 
143         REPORTER_ASSERT(reporter, tightSurf->width() == subset.width());
144         REPORTER_ASSERT(reporter, tightSurf->height() == subset.height());
145         GrBackendTexture backendTex = tightSurf->getBackendTexture(
146                                                     SkSurface::kDiscardWrite_BackendHandleAccess);
147         REPORTER_ASSERT(reporter, isGPUBacked == backendTex.isValid());
148         SkPixmap tmpPixmap;
149         REPORTER_ASSERT(reporter, isGPUBacked != !!tightSurf->peekPixels(&tmpPixmap));
150     }
151 }
152 
DEF_TEST(SpecialImage_Raster,reporter)153 DEF_TEST(SpecialImage_Raster, reporter) {
154     SkBitmap bm = create_bm();
155 
156     sk_sp<SkSpecialImage> fullSImage(SkSpecialImage::MakeFromRaster(
157                                                             SkIRect::MakeWH(kFullSize, kFullSize),
158                                                             bm, SkSurfaceProps()));
159 
160     const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
161 
162     {
163         sk_sp<SkSpecialImage> subSImg1(SkSpecialImage::MakeFromRaster(subset, bm,
164                                                                       SkSurfaceProps()));
165         test_image(subSImg1, reporter, nullptr, false);
166     }
167 
168     {
169         sk_sp<SkSpecialImage> subSImg2(fullSImage->makeSubset(subset));
170         test_image(subSImg2, reporter, nullptr, false);
171     }
172 }
173 
test_specialimage_image(skiatest::Reporter * reporter)174 static void test_specialimage_image(skiatest::Reporter* reporter) {
175     SkBitmap bm = create_bm();
176 
177     sk_sp<SkImage> fullImage(bm.asImage());
178 
179     sk_sp<SkSpecialImage> fullSImage(SkSpecialImage::MakeFromImage(
180                                                             nullptr,
181                                                             SkIRect::MakeWH(kFullSize, kFullSize),
182                                                             fullImage,
183                                                             SkSurfaceProps()));
184 
185     const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
186 
187     {
188         sk_sp<SkSpecialImage> subSImg1(SkSpecialImage::MakeFromImage(nullptr, subset, fullImage,
189                                                                      SkSurfaceProps()));
190         test_image(subSImg1, reporter, nullptr, false);
191     }
192 
193     {
194         sk_sp<SkSpecialImage> subSImg2(fullSImage->makeSubset(subset));
195         test_image(subSImg2, reporter, nullptr, false);
196     }
197 }
198 
DEF_TEST(SpecialImage_Image_Legacy,reporter)199 DEF_TEST(SpecialImage_Image_Legacy, reporter) {
200     test_specialimage_image(reporter);
201 }
202 
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SpecialImage_Gpu,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)203 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SpecialImage_Gpu,
204                                        reporter,
205                                        ctxInfo,
206                                        CtsEnforcement::kApiLevel_T) {
207     auto context = ctxInfo.directContext();
208     SkBitmap bm = create_bm();
209     auto [view, ct] = GrMakeUncachedBitmapProxyView(context, bm);
210     if (!view) {
211         return;
212     }
213 
214     sk_sp<SkSpecialImage> fullSImg =
215             SkSpecialImage::MakeDeferredFromGpu(context,
216                                                 SkIRect::MakeWH(kFullSize, kFullSize),
217                                                 kNeedNewImageUniqueID_SpecialImage,
218                                                 view,
219                                                 { ct, kPremul_SkAlphaType, nullptr },
220                                                 SkSurfaceProps());
221 
222     const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
223 
224     {
225         sk_sp<SkSpecialImage> subSImg1 = SkSpecialImage::MakeDeferredFromGpu(
226                 context,
227                 subset,
228                 kNeedNewImageUniqueID_SpecialImage,
229                 std::move(view),
230                 { ct, kPremul_SkAlphaType, nullptr },
231                 SkSurfaceProps());
232         test_image(subSImg1, reporter, context, true);
233     }
234 
235     {
236         sk_sp<SkSpecialImage> subSImg2 = fullSImg->makeSubset(subset);
237         test_image(subSImg2, reporter, context, true);
238     }
239 }
240