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