SurfaceTest.cpp revision 5e0995e4b36178e1e4465a9f50114ed39f038c27
1/*
2 * Copyright 2013 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 "SkCanvas.h"
9#include "SkData.h"
10#include "SkImageEncoder.h"
11#include "SkRRect.h"
12#include "SkSurface.h"
13#include "SkUtils.h"
14#include "Test.h"
15
16#if SK_SUPPORT_GPU
17#include "GrContextFactory.h"
18#else
19class GrContextFactory;
20class GrContext;
21#endif
22
23enum SurfaceType {
24    kRaster_SurfaceType,
25    kGpu_SurfaceType,
26    kPicture_SurfaceType
27};
28
29static SkSurface* createSurface(SurfaceType surfaceType, GrContext* context) {
30    static const SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
31
32    switch (surfaceType) {
33    case kRaster_SurfaceType:
34        return SkSurface::NewRaster(info);
35    case kGpu_SurfaceType:
36#if SK_SUPPORT_GPU
37        SkASSERT(NULL != context);
38        return SkSurface::NewRenderTarget(context, info);
39#else
40        SkASSERT(0);
41#endif
42    case kPicture_SurfaceType:
43        return SkSurface::NewPicture(info.fWidth, info.fHeight);
44    }
45    SkASSERT(0);
46    return NULL;
47}
48
49enum ImageType {
50    kRasterCopy_ImageType,
51    kRasterData_ImageType,
52    kGpu_ImageType,
53    kPicture_ImageType,
54    kCodec_ImageType,
55};
56
57static void test_image(skiatest::Reporter* reporter) {
58    SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
59    size_t rowBytes = info.minRowBytes();
60    size_t size = info.getSafeSize(rowBytes);
61    void* addr = sk_malloc_throw(size);
62    SkData* data = SkData::NewFromMalloc(addr, size);
63
64    REPORTER_ASSERT(reporter, 1 == data->getRefCnt());
65    SkImage* image = SkImage::NewRasterData(info, data, rowBytes);
66    REPORTER_ASSERT(reporter, 2 == data->getRefCnt());
67    image->unref();
68    REPORTER_ASSERT(reporter, 1 == data->getRefCnt());
69    data->unref();
70}
71
72static SkImage* createImage(ImageType imageType, GrContext* context,
73                            SkColor color) {
74    const SkPMColor pmcolor = SkPreMultiplyColor(color);
75    const SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
76    const size_t rowBytes = info.minRowBytes();
77    const size_t size = rowBytes * info.fHeight;
78
79    void* addr = sk_malloc_throw(size);
80    sk_memset32((SkPMColor*)addr, pmcolor, SkToInt(size >> 2));
81    SkAutoTUnref<SkData> data(SkData::NewFromMalloc(addr, size));
82
83    switch (imageType) {
84        case kRasterCopy_ImageType:
85            return SkImage::NewRasterCopy(info, addr, rowBytes);
86        case kRasterData_ImageType:
87            return SkImage::NewRasterData(info, data, rowBytes);
88        case kGpu_ImageType:
89            return NULL;        // TODO
90        case kPicture_ImageType: {
91            SkAutoTUnref<SkSurface> surf(SkSurface::NewPicture(info.fWidth,
92                                                               info.fHeight));
93            surf->getCanvas()->drawColor(SK_ColorRED);
94            return surf->newImageSnapshot();
95        }
96        case kCodec_ImageType: {
97            SkBitmap bitmap;
98            bitmap.installPixels(info, addr, rowBytes, NULL, NULL);
99            SkAutoTUnref<SkData> src(
100                 SkImageEncoder::EncodeData(bitmap, SkImageEncoder::kPNG_Type,
101                                            100));
102            return SkImage::NewEncodedData(src);
103        }
104    }
105    SkASSERT(false);
106    return NULL;
107}
108
109static void test_imagepeek(skiatest::Reporter* reporter) {
110    static const struct {
111        ImageType   fType;
112        bool        fPeekShouldSucceed;
113    } gRec[] = {
114        { kRasterCopy_ImageType,    true    },
115        { kRasterData_ImageType,    true    },
116        { kGpu_ImageType,           false    },
117        { kPicture_ImageType,       false   },
118        { kCodec_ImageType,         false   },
119    };
120
121    const SkColor color = SK_ColorRED;
122    const SkPMColor pmcolor = SkPreMultiplyColor(color);
123
124    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
125        SkImageInfo info;
126        size_t rowBytes;
127
128        SkAutoTUnref<SkImage> image(createImage(gRec[i].fType, NULL, color));
129        if (!image.get()) {
130            continue;   // gpu may not be enabled
131        }
132        const void* addr = image->peekPixels(&info, &rowBytes);
133        bool success = (NULL != addr);
134        REPORTER_ASSERT(reporter, gRec[i].fPeekShouldSucceed == success);
135        if (success) {
136            REPORTER_ASSERT(reporter, 10 == info.fWidth);
137            REPORTER_ASSERT(reporter, 10 == info.fHeight);
138            REPORTER_ASSERT(reporter, kPMColor_SkColorType == info.fColorType);
139            REPORTER_ASSERT(reporter, kPremul_SkAlphaType == info.fAlphaType ||
140                                      kOpaque_SkAlphaType == info.fAlphaType);
141            REPORTER_ASSERT(reporter, info.minRowBytes() <= rowBytes);
142            REPORTER_ASSERT(reporter, pmcolor == *(const SkPMColor*)addr);
143        }
144    }
145}
146
147static void TestSurfaceCopyOnWrite(skiatest::Reporter* reporter, SurfaceType surfaceType,
148                                   GrContext* context) {
149    // Verify that the right canvas commands trigger a copy on write
150    SkSurface* surface = createSurface(surfaceType, context);
151    SkAutoTUnref<SkSurface> aur_surface(surface);
152    SkCanvas* canvas = surface->getCanvas();
153
154    const SkRect testRect =
155        SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0),
156                         SkIntToScalar(4), SkIntToScalar(5));
157    SkMatrix testMatrix;
158    testMatrix.reset();
159    testMatrix.setScale(SkIntToScalar(2), SkIntToScalar(3));
160
161    SkPath testPath;
162    testPath.addRect(SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0),
163                                      SkIntToScalar(2), SkIntToScalar(1)));
164
165    const SkIRect testIRect = SkIRect::MakeXYWH(0, 0, 2, 1);
166
167    SkRegion testRegion;
168    testRegion.setRect(testIRect);
169
170
171    const SkColor testColor = 0x01020304;
172    const SkPaint testPaint;
173    const SkPoint testPoints[3] = {
174        {SkIntToScalar(0), SkIntToScalar(0)},
175        {SkIntToScalar(2), SkIntToScalar(1)},
176        {SkIntToScalar(0), SkIntToScalar(2)}
177    };
178    const size_t testPointCount = 3;
179
180    SkBitmap testBitmap;
181    testBitmap.setConfig(SkBitmap::kARGB_8888_Config, 10, 10);
182    testBitmap.allocPixels();
183    testBitmap.eraseColor(0);
184
185    SkRRect testRRect;
186    testRRect.setRectXY(testRect, SK_Scalar1, SK_Scalar1);
187
188    SkString testText("Hello World");
189    const SkPoint testPoints2[] = {
190        { SkIntToScalar(0), SkIntToScalar(1) },
191        { SkIntToScalar(1), SkIntToScalar(1) },
192        { SkIntToScalar(2), SkIntToScalar(1) },
193        { SkIntToScalar(3), SkIntToScalar(1) },
194        { SkIntToScalar(4), SkIntToScalar(1) },
195        { SkIntToScalar(5), SkIntToScalar(1) },
196        { SkIntToScalar(6), SkIntToScalar(1) },
197        { SkIntToScalar(7), SkIntToScalar(1) },
198        { SkIntToScalar(8), SkIntToScalar(1) },
199        { SkIntToScalar(9), SkIntToScalar(1) },
200        { SkIntToScalar(10), SkIntToScalar(1) },
201    };
202
203#define EXPECT_COPY_ON_WRITE(command)                               \
204    {                                                               \
205        SkImage* imageBefore = surface->newImageSnapshot();         \
206        SkAutoTUnref<SkImage> aur_before(imageBefore);              \
207        canvas-> command ;                                          \
208        SkImage* imageAfter = surface->newImageSnapshot();          \
209        SkAutoTUnref<SkImage> aur_after(imageAfter);                \
210        REPORTER_ASSERT(reporter, imageBefore != imageAfter);       \
211    }
212
213    EXPECT_COPY_ON_WRITE(clear(testColor))
214    EXPECT_COPY_ON_WRITE(drawPaint(testPaint))
215    EXPECT_COPY_ON_WRITE(drawPoints(SkCanvas::kPoints_PointMode, testPointCount, testPoints, \
216        testPaint))
217    EXPECT_COPY_ON_WRITE(drawOval(testRect, testPaint))
218    EXPECT_COPY_ON_WRITE(drawRect(testRect, testPaint))
219    EXPECT_COPY_ON_WRITE(drawRRect(testRRect, testPaint))
220    EXPECT_COPY_ON_WRITE(drawPath(testPath, testPaint))
221    EXPECT_COPY_ON_WRITE(drawBitmap(testBitmap, 0, 0))
222    EXPECT_COPY_ON_WRITE(drawBitmapRect(testBitmap, NULL, testRect))
223    EXPECT_COPY_ON_WRITE(drawBitmapMatrix(testBitmap, testMatrix, NULL))
224    EXPECT_COPY_ON_WRITE(drawBitmapNine(testBitmap, testIRect, testRect, NULL))
225    EXPECT_COPY_ON_WRITE(drawSprite(testBitmap, 0, 0, NULL))
226    EXPECT_COPY_ON_WRITE(drawText(testText.c_str(), testText.size(), 0, 1, testPaint))
227    EXPECT_COPY_ON_WRITE(drawPosText(testText.c_str(), testText.size(), testPoints2, \
228        testPaint))
229    EXPECT_COPY_ON_WRITE(drawTextOnPath(testText.c_str(), testText.size(), testPath, NULL, \
230        testPaint))
231}
232
233static void TestSurfaceWritableAfterSnapshotRelease(skiatest::Reporter* reporter,
234                                                    SurfaceType surfaceType,
235                                                    GrContext* context) {
236    // This test succeeds by not triggering an assertion.
237    // The test verifies that the surface remains writable (usable) after
238    // acquiring and releasing a snapshot without triggering a copy on write.
239    SkAutoTUnref<SkSurface> surface(createSurface(surfaceType, context));
240    SkCanvas* canvas = surface->getCanvas();
241    canvas->clear(1);
242    surface->newImageSnapshot()->unref();  // Create and destroy SkImage
243    canvas->clear(2);  // Must not assert internally
244}
245
246#if SK_SUPPORT_GPU
247static void Test_crbug263329(skiatest::Reporter* reporter,
248                             GrContext* context) {
249    // This is a regression test for crbug.com/263329
250    // Bug was caused by onCopyOnWrite releasing the old surface texture
251    // back to the scratch texture pool even though the texture is used
252    // by and active SkImage_Gpu.
253    SkAutoTUnref<SkSurface> surface1(createSurface(kGpu_SurfaceType, context));
254    SkAutoTUnref<SkSurface> surface2(createSurface(kGpu_SurfaceType, context));
255    SkCanvas* canvas1 = surface1->getCanvas();
256    SkCanvas* canvas2 = surface2->getCanvas();
257    canvas1->clear(1);
258    SkAutoTUnref<SkImage> image1(surface1->newImageSnapshot());
259    // Trigger copy on write, new backing is a scratch texture
260    canvas1->clear(2);
261    SkAutoTUnref<SkImage> image2(surface1->newImageSnapshot());
262    // Trigger copy on write, old backing should not be returned to scratch
263    // pool because it is held by image2
264    canvas1->clear(3);
265
266    canvas2->clear(4);
267    SkAutoTUnref<SkImage> image3(surface2->newImageSnapshot());
268    // Trigger copy on write on surface2. The new backing store should not
269    // be recycling a texture that is held by an existing image.
270    canvas2->clear(5);
271    SkAutoTUnref<SkImage> image4(surface2->newImageSnapshot());
272    REPORTER_ASSERT(reporter, image4->getTexture() != image3->getTexture());
273    // The following assertion checks crbug.com/263329
274    REPORTER_ASSERT(reporter, image4->getTexture() != image2->getTexture());
275    REPORTER_ASSERT(reporter, image4->getTexture() != image1->getTexture());
276    REPORTER_ASSERT(reporter, image3->getTexture() != image2->getTexture());
277    REPORTER_ASSERT(reporter, image3->getTexture() != image1->getTexture());
278    REPORTER_ASSERT(reporter, image2->getTexture() != image1->getTexture());
279}
280
281static void TestGetTexture(skiatest::Reporter* reporter,
282                                 SurfaceType surfaceType,
283                                 GrContext* context) {
284    SkAutoTUnref<SkSurface> surface(createSurface(surfaceType, context));
285    SkAutoTUnref<SkImage> image(surface->newImageSnapshot());
286    GrTexture* texture = image->getTexture();
287    if (surfaceType == kGpu_SurfaceType) {
288        REPORTER_ASSERT(reporter, NULL != texture);
289        REPORTER_ASSERT(reporter, 0 != texture->getTextureHandle());
290    } else {
291        REPORTER_ASSERT(reporter, NULL == texture);
292    }
293    surface->notifyContentWillChange(SkSurface::kDiscard_ContentChangeMode);
294    REPORTER_ASSERT(reporter, image->getTexture() == texture);
295}
296#endif
297
298static void TestSurfaceNoCanvas(skiatest::Reporter* reporter,
299                                          SurfaceType surfaceType,
300                                          GrContext* context,
301                                          SkSurface::ContentChangeMode mode) {
302    // Verifies the robustness of SkSurface for handling use cases where calls
303    // are made before a canvas is created.
304    {
305        // Test passes by not asserting
306        SkSurface* surface = createSurface(surfaceType, context);
307        SkAutoTUnref<SkSurface> aur_surface(surface);
308        surface->notifyContentWillChange(mode);
309        SkDEBUGCODE(surface->validate();)
310    }
311    {
312        SkSurface* surface = createSurface(surfaceType, context);
313        SkAutoTUnref<SkSurface> aur_surface(surface);
314        SkImage* image1 = surface->newImageSnapshot();
315        SkAutoTUnref<SkImage> aur_image1(image1);
316        SkDEBUGCODE(image1->validate();)
317        SkDEBUGCODE(surface->validate();)
318        surface->notifyContentWillChange(mode);
319        SkDEBUGCODE(image1->validate();)
320        SkDEBUGCODE(surface->validate();)
321        SkImage* image2 = surface->newImageSnapshot();
322        SkAutoTUnref<SkImage> aur_image2(image2);
323        SkDEBUGCODE(image2->validate();)
324        SkDEBUGCODE(surface->validate();)
325        REPORTER_ASSERT(reporter, image1 != image2);
326    }
327
328}
329
330DEF_GPUTEST(Surface, reporter, factory) {
331    test_image(reporter);
332
333    TestSurfaceCopyOnWrite(reporter, kRaster_SurfaceType, NULL);
334    TestSurfaceCopyOnWrite(reporter, kPicture_SurfaceType, NULL);
335    TestSurfaceWritableAfterSnapshotRelease(reporter, kRaster_SurfaceType, NULL);
336    TestSurfaceWritableAfterSnapshotRelease(reporter, kPicture_SurfaceType, NULL);
337    TestSurfaceNoCanvas(reporter, kRaster_SurfaceType, NULL, SkSurface::kDiscard_ContentChangeMode);
338    TestSurfaceNoCanvas(reporter, kRaster_SurfaceType, NULL, SkSurface::kRetain_ContentChangeMode);
339    test_imagepeek(reporter);
340#if SK_SUPPORT_GPU
341    TestGetTexture(reporter, kRaster_SurfaceType, NULL);
342    TestGetTexture(reporter, kPicture_SurfaceType, NULL);
343    if (NULL != factory) {
344        GrContext* context = factory->get(GrContextFactory::kNative_GLContextType);
345        if (NULL != context) {
346            Test_crbug263329(reporter, context);
347            TestSurfaceCopyOnWrite(reporter, kGpu_SurfaceType, context);
348            TestSurfaceWritableAfterSnapshotRelease(reporter, kGpu_SurfaceType, context);
349            TestSurfaceNoCanvas(reporter, kGpu_SurfaceType, context, SkSurface::kDiscard_ContentChangeMode);
350            TestSurfaceNoCanvas(reporter, kGpu_SurfaceType, context, SkSurface::kRetain_ContentChangeMode);
351            TestGetTexture(reporter, kGpu_SurfaceType, context);
352        }
353    }
354#endif
355}
356