Home | History | Annotate | Download | only in tests
      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 "SkDecodingImageGenerator.h"
     11 #include "SkImageEncoder.h"
     12 #include "SkRRect.h"
     13 #include "SkSurface.h"
     14 #include "SkUtils.h"
     15 #include "Test.h"
     16 
     17 #if SK_SUPPORT_GPU
     18 #include "GrContextFactory.h"
     19 #else
     20 class GrContextFactory;
     21 class GrContext;
     22 #endif
     23 
     24 enum SurfaceType {
     25     kRaster_SurfaceType,
     26     kRasterDirect_SurfaceType,
     27     kGpu_SurfaceType,
     28     kGpuScratch_SurfaceType,
     29 };
     30 
     31 static void release_storage(void* pixels, void* context) {
     32     SkASSERT(pixels == context);
     33     sk_free(pixels);
     34 }
     35 
     36 static SkSurface* createSurface(SurfaceType surfaceType, GrContext* context,
     37                                 SkImageInfo* requestedInfo = NULL) {
     38     static const SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
     39 
     40     if (requestedInfo) {
     41         *requestedInfo = info;
     42     }
     43 
     44     switch (surfaceType) {
     45         case kRaster_SurfaceType:
     46             return SkSurface::NewRaster(info);
     47         case kRasterDirect_SurfaceType: {
     48             const size_t rowBytes = info.minRowBytes();
     49             void* storage = sk_malloc_throw(info.getSafeSize(rowBytes));
     50             return SkSurface::NewRasterDirectReleaseProc(info, storage, rowBytes,
     51                                                          release_storage, storage);
     52         }
     53         case kGpu_SurfaceType:
     54 #if SK_SUPPORT_GPU
     55             return context ? SkSurface::NewRenderTarget(context, info, 0, NULL) : NULL;
     56 #endif
     57             break;
     58         case kGpuScratch_SurfaceType:
     59 #if SK_SUPPORT_GPU
     60             return context ? SkSurface::NewScratchRenderTarget(context, info) : NULL;
     61 #endif
     62             break;
     63     }
     64     return NULL;
     65 }
     66 
     67 enum ImageType {
     68     kRasterCopy_ImageType,
     69     kRasterData_ImageType,
     70     kGpu_ImageType,
     71     kCodec_ImageType,
     72 };
     73 
     74 static void test_image(skiatest::Reporter* reporter) {
     75     SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
     76     size_t rowBytes = info.minRowBytes();
     77     size_t size = info.getSafeSize(rowBytes);
     78     SkData* data = SkData::NewUninitialized(size);
     79 
     80     REPORTER_ASSERT(reporter, 1 == data->getRefCnt());
     81     SkImage* image = SkImage::NewRasterData(info, data, rowBytes);
     82     REPORTER_ASSERT(reporter, 2 == data->getRefCnt());
     83     image->unref();
     84     REPORTER_ASSERT(reporter, 1 == data->getRefCnt());
     85     data->unref();
     86 }
     87 
     88 static SkImage* createImage(ImageType imageType, GrContext* context,
     89                             SkColor color) {
     90     const SkPMColor pmcolor = SkPreMultiplyColor(color);
     91     const SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
     92     const size_t rowBytes = info.minRowBytes();
     93     const size_t size = rowBytes * info.height();
     94 
     95     SkAutoTUnref<SkData> data(SkData::NewUninitialized(size));
     96     void* addr = data->writable_data();
     97     sk_memset32((SkPMColor*)addr, pmcolor, SkToInt(size >> 2));
     98 
     99     switch (imageType) {
    100         case kRasterCopy_ImageType:
    101             return SkImage::NewRasterCopy(info, addr, rowBytes);
    102         case kRasterData_ImageType:
    103             return SkImage::NewRasterData(info, data, rowBytes);
    104         case kGpu_ImageType:
    105             return NULL;        // TODO
    106         case kCodec_ImageType: {
    107             SkBitmap bitmap;
    108             bitmap.installPixels(info, addr, rowBytes);
    109             SkAutoTUnref<SkData> src(
    110                  SkImageEncoder::EncodeData(bitmap, SkImageEncoder::kPNG_Type,
    111                                             100));
    112             return SkImage::NewFromGenerator(
    113                 SkDecodingImageGenerator::Create(data, SkDecodingImageGenerator::Options()));
    114         }
    115     }
    116     SkASSERT(false);
    117     return NULL;
    118 }
    119 
    120 static void test_imagepeek(skiatest::Reporter* reporter) {
    121     static const struct {
    122         ImageType   fType;
    123         bool        fPeekShouldSucceed;
    124     } gRec[] = {
    125         { kRasterCopy_ImageType,    true    },
    126         { kRasterData_ImageType,    true    },
    127         { kGpu_ImageType,           false   },
    128         { kCodec_ImageType,         false   },
    129     };
    130 
    131     const SkColor color = SK_ColorRED;
    132     const SkPMColor pmcolor = SkPreMultiplyColor(color);
    133 
    134     for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
    135         SkImageInfo info;
    136         size_t rowBytes;
    137 
    138         SkAutoTUnref<SkImage> image(createImage(gRec[i].fType, NULL, color));
    139         if (!image.get()) {
    140             continue;   // gpu may not be enabled
    141         }
    142         const void* addr = image->peekPixels(&info, &rowBytes);
    143         bool success = SkToBool(addr);
    144         REPORTER_ASSERT(reporter, gRec[i].fPeekShouldSucceed == success);
    145         if (success) {
    146             REPORTER_ASSERT(reporter, 10 == info.width());
    147             REPORTER_ASSERT(reporter, 10 == info.height());
    148             REPORTER_ASSERT(reporter, kN32_SkColorType == info.colorType());
    149             REPORTER_ASSERT(reporter, kPremul_SkAlphaType == info.alphaType() ||
    150                             kOpaque_SkAlphaType == info.alphaType());
    151             REPORTER_ASSERT(reporter, info.minRowBytes() <= rowBytes);
    152             REPORTER_ASSERT(reporter, pmcolor == *(const SkPMColor*)addr);
    153         }
    154     }
    155 }
    156 
    157 static void test_canvaspeek(skiatest::Reporter* reporter,
    158                             GrContextFactory* factory) {
    159     static const struct {
    160         SurfaceType fType;
    161         bool        fPeekShouldSucceed;
    162     } gRec[] = {
    163         { kRaster_SurfaceType,          true    },
    164         { kRasterDirect_SurfaceType,    true    },
    165 #if SK_SUPPORT_GPU
    166         { kGpu_SurfaceType,             false   },
    167         { kGpuScratch_SurfaceType,      false   },
    168 #endif
    169     };
    170 
    171     const SkColor color = SK_ColorRED;
    172     const SkPMColor pmcolor = SkPreMultiplyColor(color);
    173 
    174     int cnt;
    175 #if SK_SUPPORT_GPU
    176     cnt = GrContextFactory::kGLContextTypeCnt;
    177 #else
    178     cnt = 1;
    179 #endif
    180 
    181     for (int i= 0; i < cnt; ++i) {
    182         GrContext* context = NULL;
    183 #if SK_SUPPORT_GPU
    184         GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i;
    185         if (!GrContextFactory::IsRenderingGLContext(glCtxType)) {
    186             continue;
    187         }
    188         context = factory->get(glCtxType);
    189 
    190         if (NULL == context) {
    191             continue;
    192         }
    193 #endif
    194         for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
    195             SkImageInfo info, requestInfo;
    196             size_t rowBytes;
    197 
    198             SkAutoTUnref<SkSurface> surface(createSurface(gRec[i].fType, context,
    199                                                           &requestInfo));
    200             surface->getCanvas()->clear(color);
    201 
    202             const void* addr = surface->getCanvas()->peekPixels(&info, &rowBytes);
    203             bool success = SkToBool(addr);
    204             REPORTER_ASSERT(reporter, gRec[i].fPeekShouldSucceed == success);
    205 
    206             SkImageInfo info2;
    207             size_t rb2;
    208             const void* addr2 = surface->peekPixels(&info2, &rb2);
    209 
    210             if (success) {
    211                 REPORTER_ASSERT(reporter, requestInfo == info);
    212                 REPORTER_ASSERT(reporter, requestInfo.minRowBytes() <= rowBytes);
    213                 REPORTER_ASSERT(reporter, pmcolor == *(const SkPMColor*)addr);
    214 
    215                 REPORTER_ASSERT(reporter, addr2 == addr);
    216                 REPORTER_ASSERT(reporter, info2 == info);
    217                 REPORTER_ASSERT(reporter, rb2 == rowBytes);
    218             } else {
    219                 REPORTER_ASSERT(reporter, NULL == addr2);
    220             }
    221         }
    222     }
    223 }
    224 
    225 static void TestSurfaceCopyOnWrite(skiatest::Reporter* reporter, SurfaceType surfaceType,
    226                                    GrContext* context) {
    227     // Verify that the right canvas commands trigger a copy on write
    228     SkSurface* surface = createSurface(surfaceType, context);
    229     SkAutoTUnref<SkSurface> aur_surface(surface);
    230     SkCanvas* canvas = surface->getCanvas();
    231 
    232     const SkRect testRect =
    233         SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0),
    234                          SkIntToScalar(4), SkIntToScalar(5));
    235     SkMatrix testMatrix;
    236     testMatrix.reset();
    237     testMatrix.setScale(SkIntToScalar(2), SkIntToScalar(3));
    238 
    239     SkPath testPath;
    240     testPath.addRect(SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0),
    241                                       SkIntToScalar(2), SkIntToScalar(1)));
    242 
    243     const SkIRect testIRect = SkIRect::MakeXYWH(0, 0, 2, 1);
    244 
    245     SkRegion testRegion;
    246     testRegion.setRect(testIRect);
    247 
    248 
    249     const SkColor testColor = 0x01020304;
    250     const SkPaint testPaint;
    251     const SkPoint testPoints[3] = {
    252         {SkIntToScalar(0), SkIntToScalar(0)},
    253         {SkIntToScalar(2), SkIntToScalar(1)},
    254         {SkIntToScalar(0), SkIntToScalar(2)}
    255     };
    256     const size_t testPointCount = 3;
    257 
    258     SkBitmap testBitmap;
    259     testBitmap.allocN32Pixels(10, 10);
    260     testBitmap.eraseColor(0);
    261 
    262     SkRRect testRRect;
    263     testRRect.setRectXY(testRect, SK_Scalar1, SK_Scalar1);
    264 
    265     SkString testText("Hello World");
    266     const SkPoint testPoints2[] = {
    267         { SkIntToScalar(0), SkIntToScalar(1) },
    268         { SkIntToScalar(1), SkIntToScalar(1) },
    269         { SkIntToScalar(2), SkIntToScalar(1) },
    270         { SkIntToScalar(3), SkIntToScalar(1) },
    271         { SkIntToScalar(4), SkIntToScalar(1) },
    272         { SkIntToScalar(5), SkIntToScalar(1) },
    273         { SkIntToScalar(6), SkIntToScalar(1) },
    274         { SkIntToScalar(7), SkIntToScalar(1) },
    275         { SkIntToScalar(8), SkIntToScalar(1) },
    276         { SkIntToScalar(9), SkIntToScalar(1) },
    277         { SkIntToScalar(10), SkIntToScalar(1) },
    278     };
    279 
    280 #define EXPECT_COPY_ON_WRITE(command)                               \
    281     {                                                               \
    282         SkImage* imageBefore = surface->newImageSnapshot();         \
    283         SkAutoTUnref<SkImage> aur_before(imageBefore);              \
    284         canvas-> command ;                                          \
    285         SkImage* imageAfter = surface->newImageSnapshot();          \
    286         SkAutoTUnref<SkImage> aur_after(imageAfter);                \
    287         REPORTER_ASSERT(reporter, imageBefore != imageAfter);       \
    288     }
    289 
    290     EXPECT_COPY_ON_WRITE(clear(testColor))
    291     EXPECT_COPY_ON_WRITE(drawPaint(testPaint))
    292     EXPECT_COPY_ON_WRITE(drawPoints(SkCanvas::kPoints_PointMode, testPointCount, testPoints, \
    293         testPaint))
    294     EXPECT_COPY_ON_WRITE(drawOval(testRect, testPaint))
    295     EXPECT_COPY_ON_WRITE(drawRect(testRect, testPaint))
    296     EXPECT_COPY_ON_WRITE(drawRRect(testRRect, testPaint))
    297     EXPECT_COPY_ON_WRITE(drawPath(testPath, testPaint))
    298     EXPECT_COPY_ON_WRITE(drawBitmap(testBitmap, 0, 0))
    299     EXPECT_COPY_ON_WRITE(drawBitmapRect(testBitmap, NULL, testRect))
    300     EXPECT_COPY_ON_WRITE(drawBitmapMatrix(testBitmap, testMatrix, NULL))
    301     EXPECT_COPY_ON_WRITE(drawBitmapNine(testBitmap, testIRect, testRect, NULL))
    302     EXPECT_COPY_ON_WRITE(drawSprite(testBitmap, 0, 0, NULL))
    303     EXPECT_COPY_ON_WRITE(drawText(testText.c_str(), testText.size(), 0, 1, testPaint))
    304     EXPECT_COPY_ON_WRITE(drawPosText(testText.c_str(), testText.size(), testPoints2, \
    305         testPaint))
    306     EXPECT_COPY_ON_WRITE(drawTextOnPath(testText.c_str(), testText.size(), testPath, NULL, \
    307         testPaint))
    308 }
    309 
    310 static void TestSurfaceWritableAfterSnapshotRelease(skiatest::Reporter* reporter,
    311                                                     SurfaceType surfaceType,
    312                                                     GrContext* context) {
    313     // This test succeeds by not triggering an assertion.
    314     // The test verifies that the surface remains writable (usable) after
    315     // acquiring and releasing a snapshot without triggering a copy on write.
    316     SkAutoTUnref<SkSurface> surface(createSurface(surfaceType, context));
    317     SkCanvas* canvas = surface->getCanvas();
    318     canvas->clear(1);
    319     surface->newImageSnapshot()->unref();  // Create and destroy SkImage
    320     canvas->clear(2);  // Must not assert internally
    321 }
    322 
    323 #if SK_SUPPORT_GPU
    324 static void TestSurfaceInCache(skiatest::Reporter* reporter,
    325                                SurfaceType surfaceType,
    326                                GrContext* context) {
    327     context->freeGpuResources();
    328     int resourceCount;
    329 
    330     context->getResourceCacheUsage(&resourceCount, NULL);
    331     REPORTER_ASSERT(reporter, 0 == resourceCount);
    332     SkAutoTUnref<SkSurface> surface(createSurface(surfaceType, context));
    333     // Note: the stencil buffer is always cached, so kGpu_SurfaceType uses
    334     // one cached resource, and kGpuScratch_SurfaceType uses two.
    335     int expectedCachedResources = surfaceType == kGpuScratch_SurfaceType ? 2 : 1;
    336     context->getResourceCacheUsage(&resourceCount, NULL);
    337     REPORTER_ASSERT(reporter, expectedCachedResources == resourceCount);
    338 
    339     // Verify that all the cached resources are locked in cache.
    340     context->freeGpuResources();
    341     context->getResourceCacheUsage(&resourceCount, NULL);
    342     REPORTER_ASSERT(reporter, expectedCachedResources == resourceCount);
    343 
    344     // Verify that all the cached resources are unlocked upon surface release
    345     surface.reset(0);
    346     context->freeGpuResources();
    347     context->getResourceCacheUsage(&resourceCount, NULL);
    348     REPORTER_ASSERT(reporter, 0 == resourceCount);
    349 }
    350 
    351 static void Test_crbug263329(skiatest::Reporter* reporter,
    352                              SurfaceType surfaceType,
    353                              GrContext* context) {
    354     // This is a regression test for crbug.com/263329
    355     // Bug was caused by onCopyOnWrite releasing the old surface texture
    356     // back to the scratch texture pool even though the texture is used
    357     // by and active SkImage_Gpu.
    358     SkAutoTUnref<SkSurface> surface1(createSurface(surfaceType, context));
    359     SkAutoTUnref<SkSurface> surface2(createSurface(surfaceType, context));
    360     SkCanvas* canvas1 = surface1->getCanvas();
    361     SkCanvas* canvas2 = surface2->getCanvas();
    362     canvas1->clear(1);
    363     SkAutoTUnref<SkImage> image1(surface1->newImageSnapshot());
    364     // Trigger copy on write, new backing is a scratch texture
    365     canvas1->clear(2);
    366     SkAutoTUnref<SkImage> image2(surface1->newImageSnapshot());
    367     // Trigger copy on write, old backing should not be returned to scratch
    368     // pool because it is held by image2
    369     canvas1->clear(3);
    370 
    371     canvas2->clear(4);
    372     SkAutoTUnref<SkImage> image3(surface2->newImageSnapshot());
    373     // Trigger copy on write on surface2. The new backing store should not
    374     // be recycling a texture that is held by an existing image.
    375     canvas2->clear(5);
    376     SkAutoTUnref<SkImage> image4(surface2->newImageSnapshot());
    377     REPORTER_ASSERT(reporter, image4->getTexture() != image3->getTexture());
    378     // The following assertion checks crbug.com/263329
    379     REPORTER_ASSERT(reporter, image4->getTexture() != image2->getTexture());
    380     REPORTER_ASSERT(reporter, image4->getTexture() != image1->getTexture());
    381     REPORTER_ASSERT(reporter, image3->getTexture() != image2->getTexture());
    382     REPORTER_ASSERT(reporter, image3->getTexture() != image1->getTexture());
    383     REPORTER_ASSERT(reporter, image2->getTexture() != image1->getTexture());
    384 }
    385 
    386 static void TestGetTexture(skiatest::Reporter* reporter,
    387                                  SurfaceType surfaceType,
    388                                  GrContext* context) {
    389     SkAutoTUnref<SkSurface> surface(createSurface(surfaceType, context));
    390     SkAutoTUnref<SkImage> image(surface->newImageSnapshot());
    391     GrTexture* texture = image->getTexture();
    392     if (surfaceType == kGpu_SurfaceType || surfaceType == kGpuScratch_SurfaceType) {
    393         REPORTER_ASSERT(reporter, texture);
    394         REPORTER_ASSERT(reporter, 0 != texture->getTextureHandle());
    395     } else {
    396         REPORTER_ASSERT(reporter, NULL == texture);
    397     }
    398     surface->notifyContentWillChange(SkSurface::kDiscard_ContentChangeMode);
    399     REPORTER_ASSERT(reporter, image->getTexture() == texture);
    400 }
    401 #endif
    402 
    403 static void TestSurfaceNoCanvas(skiatest::Reporter* reporter,
    404                                           SurfaceType surfaceType,
    405                                           GrContext* context,
    406                                           SkSurface::ContentChangeMode mode) {
    407     // Verifies the robustness of SkSurface for handling use cases where calls
    408     // are made before a canvas is created.
    409     {
    410         // Test passes by not asserting
    411         SkSurface* surface = createSurface(surfaceType, context);
    412         SkAutoTUnref<SkSurface> aur_surface(surface);
    413         surface->notifyContentWillChange(mode);
    414         SkDEBUGCODE(surface->validate();)
    415     }
    416     {
    417         SkSurface* surface = createSurface(surfaceType, context);
    418         SkAutoTUnref<SkSurface> aur_surface(surface);
    419         SkImage* image1 = surface->newImageSnapshot();
    420         SkAutoTUnref<SkImage> aur_image1(image1);
    421         SkDEBUGCODE(image1->validate();)
    422         SkDEBUGCODE(surface->validate();)
    423         surface->notifyContentWillChange(mode);
    424         SkDEBUGCODE(image1->validate();)
    425         SkDEBUGCODE(surface->validate();)
    426         SkImage* image2 = surface->newImageSnapshot();
    427         SkAutoTUnref<SkImage> aur_image2(image2);
    428         SkDEBUGCODE(image2->validate();)
    429         SkDEBUGCODE(surface->validate();)
    430         REPORTER_ASSERT(reporter, image1 != image2);
    431     }
    432 
    433 }
    434 
    435 DEF_GPUTEST(Surface, reporter, factory) {
    436     test_image(reporter);
    437 
    438     TestSurfaceCopyOnWrite(reporter, kRaster_SurfaceType, NULL);
    439     TestSurfaceWritableAfterSnapshotRelease(reporter, kRaster_SurfaceType, NULL);
    440     TestSurfaceNoCanvas(reporter, kRaster_SurfaceType, NULL, SkSurface::kDiscard_ContentChangeMode);
    441     TestSurfaceNoCanvas(reporter, kRaster_SurfaceType, NULL, SkSurface::kRetain_ContentChangeMode);
    442 
    443     test_imagepeek(reporter);
    444     test_canvaspeek(reporter, factory);
    445 
    446 #if SK_SUPPORT_GPU
    447     TestGetTexture(reporter, kRaster_SurfaceType, NULL);
    448     if (factory) {
    449         for (int i= 0; i < GrContextFactory::kGLContextTypeCnt; ++i) {
    450             GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i;
    451             if (!GrContextFactory::IsRenderingGLContext(glCtxType)) {
    452                 continue;
    453             }
    454             GrContext* context = factory->get(glCtxType);
    455             if (context) {
    456                 TestSurfaceInCache(reporter, kGpu_SurfaceType, context);
    457                 TestSurfaceInCache(reporter, kGpuScratch_SurfaceType, context);
    458                 Test_crbug263329(reporter, kGpu_SurfaceType, context);
    459                 Test_crbug263329(reporter, kGpuScratch_SurfaceType, context);
    460                 TestSurfaceCopyOnWrite(reporter, kGpu_SurfaceType, context);
    461                 TestSurfaceCopyOnWrite(reporter, kGpuScratch_SurfaceType, context);
    462                 TestSurfaceWritableAfterSnapshotRelease(reporter, kGpu_SurfaceType, context);
    463                 TestSurfaceWritableAfterSnapshotRelease(reporter, kGpuScratch_SurfaceType, context);
    464                 TestSurfaceNoCanvas(reporter, kGpu_SurfaceType, context, SkSurface::kDiscard_ContentChangeMode);
    465                 TestSurfaceNoCanvas(reporter, kGpuScratch_SurfaceType, context, SkSurface::kDiscard_ContentChangeMode);
    466                 TestSurfaceNoCanvas(reporter, kGpu_SurfaceType, context, SkSurface::kRetain_ContentChangeMode);
    467                 TestSurfaceNoCanvas(reporter, kGpuScratch_SurfaceType, context, SkSurface::kRetain_ContentChangeMode);
    468                 TestGetTexture(reporter, kGpu_SurfaceType, context);
    469                 TestGetTexture(reporter, kGpuScratch_SurfaceType, context);
    470             }
    471         }
    472     }
    473 #endif
    474 }
    475