blob: 4803b68221f708cb9e9ad64a835bcfe9855317e2 [file] [log] [blame]
junov@chromium.org995beb62013-03-28 13:49:22 +00001/*
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 */
tfarina@chromium.org4ee16bf2014-01-10 22:08:27 +00007
junov@chromium.org995beb62013-03-28 13:49:22 +00008#include "SkCanvas.h"
reed@google.com4f7c6152014-02-06 14:11:56 +00009#include "SkData.h"
reed41e010c2015-06-09 12:16:53 -070010#include "SkDevice.h"
reed@google.com4f7c6152014-02-06 14:11:56 +000011#include "SkImageEncoder.h"
junov@chromium.org995beb62013-03-28 13:49:22 +000012#include "SkRRect.h"
13#include "SkSurface.h"
reed@google.com4f7c6152014-02-06 14:11:56 +000014#include "SkUtils.h"
junov@chromium.org995beb62013-03-28 13:49:22 +000015#include "Test.h"
16
17#if SK_SUPPORT_GPU
18#include "GrContextFactory.h"
19#else
20class GrContextFactory;
21class GrContext;
22#endif
23
24enum SurfaceType {
25 kRaster_SurfaceType,
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +000026 kRasterDirect_SurfaceType,
junov@chromium.org995beb62013-03-28 13:49:22 +000027 kGpu_SurfaceType,
commit-bot@chromium.orgd8a57af2014-03-19 21:19:16 +000028 kGpuScratch_SurfaceType,
junov@chromium.org995beb62013-03-28 13:49:22 +000029};
30
reed982542d2014-06-27 06:48:14 -070031static void release_storage(void* pixels, void* context) {
32 SkASSERT(pixels == context);
33 sk_free(pixels);
34}
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +000035
36static SkSurface* createSurface(SurfaceType surfaceType, GrContext* context,
37 SkImageInfo* requestedInfo = NULL) {
reed982542d2014-06-27 06:48:14 -070038 static const SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +000039
40 if (requestedInfo) {
41 *requestedInfo = info;
42 }
junov@chromium.org995beb62013-03-28 13:49:22 +000043
44 switch (surfaceType) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +000045 case kRaster_SurfaceType:
46 return SkSurface::NewRaster(info);
reed982542d2014-06-27 06:48:14 -070047 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 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +000053 case kGpu_SurfaceType:
bsalomonafe30052015-01-16 07:32:33 -080054 return SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, info, 0, NULL);
commit-bot@chromium.orgd8a57af2014-03-19 21:19:16 +000055 case kGpuScratch_SurfaceType:
bsalomonafe30052015-01-16 07:32:33 -080056 return SkSurface::NewRenderTarget(context, SkSurface::kYes_Budgeted, info, 0, NULL);
junov@chromium.org995beb62013-03-28 13:49:22 +000057 }
junov@chromium.org995beb62013-03-28 13:49:22 +000058 return NULL;
59}
60
reed@google.com4f7c6152014-02-06 14:11:56 +000061enum ImageType {
62 kRasterCopy_ImageType,
63 kRasterData_ImageType,
64 kGpu_ImageType,
reed@google.com4f7c6152014-02-06 14:11:56 +000065 kCodec_ImageType,
66};
reed@google.com999da9c2014-02-06 13:43:07 +000067
reedb2497c22014-12-31 12:31:43 -080068#include "SkImageGenerator.h"
69
70class EmptyGenerator : public SkImageGenerator {
reed3ef71e32015-03-19 08:31:14 -070071public:
72 EmptyGenerator() : SkImageGenerator(SkImageInfo::MakeN32Premul(0, 0)) {}
reedb2497c22014-12-31 12:31:43 -080073};
74
75static void test_empty_image(skiatest::Reporter* reporter) {
76 const SkImageInfo info = SkImageInfo::Make(0, 0, kN32_SkColorType, kPremul_SkAlphaType);
77
78 REPORTER_ASSERT(reporter, NULL == SkImage::NewRasterCopy(info, NULL, 0));
79 REPORTER_ASSERT(reporter, NULL == SkImage::NewRasterData(info, NULL, 0));
80 REPORTER_ASSERT(reporter, NULL == SkImage::NewFromGenerator(SkNEW(EmptyGenerator)));
81}
82
83static void test_empty_surface(skiatest::Reporter* reporter, GrContext* ctx) {
84 const SkImageInfo info = SkImageInfo::Make(0, 0, kN32_SkColorType, kPremul_SkAlphaType);
85
86 REPORTER_ASSERT(reporter, NULL == SkSurface::NewRaster(info));
87 REPORTER_ASSERT(reporter, NULL == SkSurface::NewRasterDirect(info, NULL, 0));
88 if (ctx) {
bsalomonafe30052015-01-16 07:32:33 -080089 REPORTER_ASSERT(reporter, NULL ==
90 SkSurface::NewRenderTarget(ctx, SkSurface::kNo_Budgeted, info, 0, NULL));
reedb2497c22014-12-31 12:31:43 -080091 }
92}
93
bsalomone4579ad2015-04-08 08:38:40 -070094#if SK_SUPPORT_GPU
95static void test_wrapped_texture_surface(skiatest::Reporter* reporter, GrContext* ctx) {
96 if (NULL == ctx) {
97 return;
98 }
99 // Test the wrapped factory for SkSurface by creating a texture using ctx and then treat it as
100 // an external texture and wrap it in a SkSurface.
101
102 GrSurfaceDesc texDesc;
103 texDesc.fConfig = kRGBA_8888_GrPixelConfig;
104 texDesc.fFlags = kRenderTarget_GrSurfaceFlag;
105 texDesc.fWidth = texDesc.fHeight = 100;
106 texDesc.fSampleCnt = 0;
107 texDesc.fOrigin = kTopLeft_GrSurfaceOrigin;
bsalomond309e7a2015-04-30 14:18:54 -0700108 SkAutoTUnref<GrSurface> dummySurface(ctx->textureProvider()->createTexture(texDesc, false));
bsalomone4579ad2015-04-08 08:38:40 -0700109
110 REPORTER_ASSERT(reporter, dummySurface && dummySurface->asTexture() &&
111 dummySurface->asRenderTarget());
112 if (!dummySurface || !dummySurface->asTexture() || !dummySurface->asRenderTarget()) {
113 return;
114 }
115
116 GrBackendObject textureHandle = dummySurface->asTexture()->getTextureHandle();
117
118 GrBackendTextureDesc wrappedDesc;
119 wrappedDesc.fConfig = dummySurface->config();
120 wrappedDesc.fWidth = dummySurface->width();
121 wrappedDesc.fHeight = dummySurface->height();
122 wrappedDesc.fOrigin = dummySurface->origin();
123 wrappedDesc.fSampleCnt = dummySurface->asRenderTarget()->numSamples();
124 wrappedDesc.fFlags = kRenderTarget_GrBackendTextureFlag;
125 wrappedDesc.fTextureHandle = textureHandle;
126
127 SkAutoTUnref<SkSurface> surface(SkSurface::NewWrappedRenderTarget(ctx, wrappedDesc, NULL));
128 REPORTER_ASSERT(reporter, surface);
129}
130#endif
131
132
reed@google.com999da9c2014-02-06 13:43:07 +0000133static void test_image(skiatest::Reporter* reporter) {
134 SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
135 size_t rowBytes = info.minRowBytes();
136 size_t size = info.getSafeSize(rowBytes);
reed9594da12014-09-12 12:12:27 -0700137 SkData* data = SkData::NewUninitialized(size);
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000138
mtkleinbbb61d72014-11-24 13:09:39 -0800139 REPORTER_ASSERT(reporter, data->unique());
reed@google.com999da9c2014-02-06 13:43:07 +0000140 SkImage* image = SkImage::NewRasterData(info, data, rowBytes);
mtkleinbbb61d72014-11-24 13:09:39 -0800141 REPORTER_ASSERT(reporter, !data->unique());
reed@google.com999da9c2014-02-06 13:43:07 +0000142 image->unref();
mtkleinbbb61d72014-11-24 13:09:39 -0800143 REPORTER_ASSERT(reporter, data->unique());
reed@google.com999da9c2014-02-06 13:43:07 +0000144 data->unref();
145}
146
reed67f2eb42014-12-10 06:54:06 -0800147static SkImage* createImage(ImageType imageType, GrContext* context, SkColor color) {
reed@google.com4f7c6152014-02-06 14:11:56 +0000148 const SkPMColor pmcolor = SkPreMultiplyColor(color);
149 const SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
150 const size_t rowBytes = info.minRowBytes();
reede5ea5002014-09-03 11:54:58 -0700151 const size_t size = rowBytes * info.height();
reed@google.com4f7c6152014-02-06 14:11:56 +0000152
reed9594da12014-09-12 12:12:27 -0700153 SkAutoTUnref<SkData> data(SkData::NewUninitialized(size));
154 void* addr = data->writable_data();
reed@google.com4f7c6152014-02-06 14:11:56 +0000155 sk_memset32((SkPMColor*)addr, pmcolor, SkToInt(size >> 2));
reed@google.com4f7c6152014-02-06 14:11:56 +0000156
157 switch (imageType) {
158 case kRasterCopy_ImageType:
159 return SkImage::NewRasterCopy(info, addr, rowBytes);
160 case kRasterData_ImageType:
161 return SkImage::NewRasterData(info, data, rowBytes);
reed67f2eb42014-12-10 06:54:06 -0800162 case kGpu_ImageType: {
bsalomonafe30052015-01-16 07:32:33 -0800163 SkAutoTUnref<SkSurface> surf(
164 SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, info, 0));
reed67f2eb42014-12-10 06:54:06 -0800165 surf->getCanvas()->clear(color);
166 return surf->newImageSnapshot();
167 }
reed@google.com4f7c6152014-02-06 14:11:56 +0000168 case kCodec_ImageType: {
169 SkBitmap bitmap;
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +0000170 bitmap.installPixels(info, addr, rowBytes);
reed@google.com4f7c6152014-02-06 14:11:56 +0000171 SkAutoTUnref<SkData> src(
reed67f2eb42014-12-10 06:54:06 -0800172 SkImageEncoder::EncodeData(bitmap, SkImageEncoder::kPNG_Type, 100));
reed5965c8a2015-01-07 18:04:45 -0800173 return SkImage::NewFromData(src);
reed@google.com4f7c6152014-02-06 14:11:56 +0000174 }
175 }
176 SkASSERT(false);
177 return NULL;
178}
179
reed96472de2014-12-10 09:53:42 -0800180static void set_pixels(SkPMColor pixels[], int count, SkPMColor color) {
181 sk_memset32(pixels, color, count);
182}
183static bool has_pixels(const SkPMColor pixels[], int count, SkPMColor expected) {
184 for (int i = 0; i < count; ++i) {
185 if (pixels[i] != expected) {
186 return false;
187 }
188 }
189 return true;
190}
191
192static void test_image_readpixels(skiatest::Reporter* reporter, SkImage* image,
193 SkPMColor expected) {
194 const SkPMColor notExpected = ~expected;
195
196 const int w = 2, h = 2;
197 const size_t rowBytes = w * sizeof(SkPMColor);
198 SkPMColor pixels[w*h];
199
200 SkImageInfo info;
201
202 info = SkImageInfo::MakeUnknown(w, h);
203 REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, 0, 0));
204
205 // out-of-bounds should fail
206 info = SkImageInfo::MakeN32Premul(w, h);
207 REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, -w, 0));
208 REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, 0, -h));
209 REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, image->width(), 0));
210 REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, 0, image->height()));
211
212 // top-left should succeed
213 set_pixels(pixels, w*h, notExpected);
214 REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes, 0, 0));
215 REPORTER_ASSERT(reporter, has_pixels(pixels, w*h, expected));
216
217 // bottom-right should succeed
218 set_pixels(pixels, w*h, notExpected);
219 REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes,
220 image->width() - w, image->height() - h));
221 REPORTER_ASSERT(reporter, has_pixels(pixels, w*h, expected));
222
223 // partial top-left should succeed
224 set_pixels(pixels, w*h, notExpected);
225 REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes, -1, -1));
226 REPORTER_ASSERT(reporter, pixels[3] == expected);
227 REPORTER_ASSERT(reporter, has_pixels(pixels, w*h - 1, notExpected));
228
229 // partial bottom-right should succeed
230 set_pixels(pixels, w*h, notExpected);
231 REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes,
232 image->width() - 1, image->height() - 1));
233 REPORTER_ASSERT(reporter, pixels[0] == expected);
234 REPORTER_ASSERT(reporter, has_pixels(&pixels[1], w*h - 1, notExpected));
235}
236
reed67f2eb42014-12-10 06:54:06 -0800237static void test_imagepeek(skiatest::Reporter* reporter, GrContextFactory* factory) {
reed@google.com4f7c6152014-02-06 14:11:56 +0000238 static const struct {
239 ImageType fType;
240 bool fPeekShouldSucceed;
reed67f2eb42014-12-10 06:54:06 -0800241 const char* fName;
reed@google.com4f7c6152014-02-06 14:11:56 +0000242 } gRec[] = {
reed67f2eb42014-12-10 06:54:06 -0800243 { kRasterCopy_ImageType, true, "RasterCopy" },
244 { kRasterData_ImageType, true, "RasterData" },
245 { kGpu_ImageType, false, "Gpu" },
246 { kCodec_ImageType, false, "Codec" },
reed@google.com4f7c6152014-02-06 14:11:56 +0000247 };
skia.committer@gmail.com02d6f542014-02-14 03:02:05 +0000248
reed@google.com4f7c6152014-02-06 14:11:56 +0000249 const SkColor color = SK_ColorRED;
250 const SkPMColor pmcolor = SkPreMultiplyColor(color);
skia.committer@gmail.com02d6f542014-02-14 03:02:05 +0000251
reed67f2eb42014-12-10 06:54:06 -0800252 GrContext* ctx = NULL;
253#if SK_SUPPORT_GPU
254 ctx = factory->get(GrContextFactory::kNative_GLContextType);
senorblancoc8e93402015-04-21 07:20:36 -0700255 if (NULL == ctx) {
senorblanco84bfd392015-04-21 06:59:17 -0700256 return;
257 }
reed67f2eb42014-12-10 06:54:06 -0800258#endif
259
reed@google.com4f7c6152014-02-06 14:11:56 +0000260 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
261 SkImageInfo info;
262 size_t rowBytes;
skia.committer@gmail.com02d6f542014-02-14 03:02:05 +0000263
reed67f2eb42014-12-10 06:54:06 -0800264 SkAutoTUnref<SkImage> image(createImage(gRec[i].fType, ctx, color));
reed@google.com4f7c6152014-02-06 14:11:56 +0000265 if (!image.get()) {
reed67f2eb42014-12-10 06:54:06 -0800266 SkDebugf("failed to createImage[%d] %s\n", i, gRec[i].fName);
reed@google.com4f7c6152014-02-06 14:11:56 +0000267 continue; // gpu may not be enabled
268 }
269 const void* addr = image->peekPixels(&info, &rowBytes);
bsalomon49f085d2014-09-05 13:34:00 -0700270 bool success = SkToBool(addr);
reed@google.com4f7c6152014-02-06 14:11:56 +0000271 REPORTER_ASSERT(reporter, gRec[i].fPeekShouldSucceed == success);
272 if (success) {
reede5ea5002014-09-03 11:54:58 -0700273 REPORTER_ASSERT(reporter, 10 == info.width());
274 REPORTER_ASSERT(reporter, 10 == info.height());
275 REPORTER_ASSERT(reporter, kN32_SkColorType == info.colorType());
276 REPORTER_ASSERT(reporter, kPremul_SkAlphaType == info.alphaType() ||
277 kOpaque_SkAlphaType == info.alphaType());
reed@google.com4f7c6152014-02-06 14:11:56 +0000278 REPORTER_ASSERT(reporter, info.minRowBytes() <= rowBytes);
279 REPORTER_ASSERT(reporter, pmcolor == *(const SkPMColor*)addr);
280 }
reed96472de2014-12-10 09:53:42 -0800281
282 test_image_readpixels(reporter, image, pmcolor);
reed@google.com4f7c6152014-02-06 14:11:56 +0000283 }
284}
285
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000286static void test_canvaspeek(skiatest::Reporter* reporter,
287 GrContextFactory* factory) {
288 static const struct {
289 SurfaceType fType;
290 bool fPeekShouldSucceed;
291 } gRec[] = {
292 { kRaster_SurfaceType, true },
293 { kRasterDirect_SurfaceType, true },
294#if SK_SUPPORT_GPU
295 { kGpu_SurfaceType, false },
commit-bot@chromium.orgd8a57af2014-03-19 21:19:16 +0000296 { kGpuScratch_SurfaceType, false },
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000297#endif
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000298 };
299
300 const SkColor color = SK_ColorRED;
301 const SkPMColor pmcolor = SkPreMultiplyColor(color);
302
bsalomone904c092014-07-17 10:50:59 -0700303 int cnt;
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000304#if SK_SUPPORT_GPU
bsalomone904c092014-07-17 10:50:59 -0700305 cnt = GrContextFactory::kGLContextTypeCnt;
306#else
307 cnt = 1;
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000308#endif
309
bsalomone904c092014-07-17 10:50:59 -0700310 for (int i= 0; i < cnt; ++i) {
311 GrContext* context = NULL;
312#if SK_SUPPORT_GPU
313 GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i;
314 if (!GrContextFactory::IsRenderingGLContext(glCtxType)) {
315 continue;
316 }
317 context = factory->get(glCtxType);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000318
bsalomone904c092014-07-17 10:50:59 -0700319 if (NULL == context) {
320 continue;
321 }
322#endif
323 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
324 SkImageInfo info, requestInfo;
325 size_t rowBytes;
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000326
bsalomone904c092014-07-17 10:50:59 -0700327 SkAutoTUnref<SkSurface> surface(createSurface(gRec[i].fType, context,
328 &requestInfo));
329 surface->getCanvas()->clear(color);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000330
bsalomone904c092014-07-17 10:50:59 -0700331 const void* addr = surface->getCanvas()->peekPixels(&info, &rowBytes);
bsalomon49f085d2014-09-05 13:34:00 -0700332 bool success = SkToBool(addr);
bsalomone904c092014-07-17 10:50:59 -0700333 REPORTER_ASSERT(reporter, gRec[i].fPeekShouldSucceed == success);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000334
bsalomone904c092014-07-17 10:50:59 -0700335 SkImageInfo info2;
336 size_t rb2;
337 const void* addr2 = surface->peekPixels(&info2, &rb2);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000338
bsalomone904c092014-07-17 10:50:59 -0700339 if (success) {
340 REPORTER_ASSERT(reporter, requestInfo == info);
341 REPORTER_ASSERT(reporter, requestInfo.minRowBytes() <= rowBytes);
342 REPORTER_ASSERT(reporter, pmcolor == *(const SkPMColor*)addr);
343
344 REPORTER_ASSERT(reporter, addr2 == addr);
345 REPORTER_ASSERT(reporter, info2 == info);
346 REPORTER_ASSERT(reporter, rb2 == rowBytes);
347 } else {
348 REPORTER_ASSERT(reporter, NULL == addr2);
349 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000350 }
351 }
352}
353
reed41e010c2015-06-09 12:16:53 -0700354// For compatibility with clients that still call accessBitmap(), we need to ensure that we bump
355// the bitmap's genID when we draw to it, else they won't know it has new values. When they are
356// exclusively using surface/image, and we can hide accessBitmap from device, we can remove this
357// test.
358static void test_accessPixels(skiatest::Reporter* reporter, GrContextFactory* factory) {
359 static const struct {
360 SurfaceType fType;
361 bool fPeekShouldSucceed;
362 } gRec[] = {
363 { kRaster_SurfaceType, true },
364 { kRasterDirect_SurfaceType, true },
365#if SK_SUPPORT_GPU
366 { kGpu_SurfaceType, false },
367 { kGpuScratch_SurfaceType, false },
368#endif
369 };
370
371 int cnt;
372#if SK_SUPPORT_GPU
373 cnt = GrContextFactory::kGLContextTypeCnt;
374#else
375 cnt = 1;
376#endif
377
378 for (int i= 0; i < cnt; ++i) {
379 GrContext* context = NULL;
380#if SK_SUPPORT_GPU
381 GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i;
382 if (!GrContextFactory::IsRenderingGLContext(glCtxType)) {
383 continue;
384 }
385 context = factory->get(glCtxType);
386
387 if (NULL == context) {
388 continue;
389 }
390#endif
391 for (size_t j = 0; j < SK_ARRAY_COUNT(gRec); ++j) {
392 SkImageInfo info, requestInfo;
393
394 SkAutoTUnref<SkSurface> surface(createSurface(gRec[j].fType, context,
395 &requestInfo));
396 SkCanvas* canvas = surface->getCanvas();
397 canvas->clear(0);
398
399 SkBaseDevice* device = canvas->getDevice_just_for_deprecated_compatibility_testing();
400 SkBitmap bm = device->accessBitmap(false);
401 uint32_t genID0 = bm.getGenerationID();
402 // Now we draw something, which needs to "dirty" the genID (sorta like copy-on-write)
403 canvas->drawColor(SK_ColorBLUE);
404 // Now check that we get a different genID
405 uint32_t genID1 = bm.getGenerationID();
406 REPORTER_ASSERT(reporter, genID0 != genID1);
407 }
408 }
409}
410
junov@chromium.org995beb62013-03-28 13:49:22 +0000411static void TestSurfaceCopyOnWrite(skiatest::Reporter* reporter, SurfaceType surfaceType,
412 GrContext* context) {
413 // Verify that the right canvas commands trigger a copy on write
414 SkSurface* surface = createSurface(surfaceType, context);
415 SkAutoTUnref<SkSurface> aur_surface(surface);
416 SkCanvas* canvas = surface->getCanvas();
417
418 const SkRect testRect =
419 SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0),
420 SkIntToScalar(4), SkIntToScalar(5));
junov@chromium.org995beb62013-03-28 13:49:22 +0000421 SkPath testPath;
422 testPath.addRect(SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0),
423 SkIntToScalar(2), SkIntToScalar(1)));
424
425 const SkIRect testIRect = SkIRect::MakeXYWH(0, 0, 2, 1);
426
427 SkRegion testRegion;
428 testRegion.setRect(testIRect);
429
430
431 const SkColor testColor = 0x01020304;
432 const SkPaint testPaint;
433 const SkPoint testPoints[3] = {
434 {SkIntToScalar(0), SkIntToScalar(0)},
435 {SkIntToScalar(2), SkIntToScalar(1)},
436 {SkIntToScalar(0), SkIntToScalar(2)}
437 };
438 const size_t testPointCount = 3;
439
440 SkBitmap testBitmap;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000441 testBitmap.allocN32Pixels(10, 10);
robertphillips@google.comd1ce77d2013-10-09 12:51:09 +0000442 testBitmap.eraseColor(0);
junov@chromium.org995beb62013-03-28 13:49:22 +0000443
444 SkRRect testRRect;
445 testRRect.setRectXY(testRect, SK_Scalar1, SK_Scalar1);
446
447 SkString testText("Hello World");
448 const SkPoint testPoints2[] = {
449 { SkIntToScalar(0), SkIntToScalar(1) },
450 { SkIntToScalar(1), SkIntToScalar(1) },
451 { SkIntToScalar(2), SkIntToScalar(1) },
452 { SkIntToScalar(3), SkIntToScalar(1) },
453 { SkIntToScalar(4), SkIntToScalar(1) },
454 { SkIntToScalar(5), SkIntToScalar(1) },
455 { SkIntToScalar(6), SkIntToScalar(1) },
456 { SkIntToScalar(7), SkIntToScalar(1) },
457 { SkIntToScalar(8), SkIntToScalar(1) },
458 { SkIntToScalar(9), SkIntToScalar(1) },
459 { SkIntToScalar(10), SkIntToScalar(1) },
460 };
461
462#define EXPECT_COPY_ON_WRITE(command) \
463 { \
junov@chromium.org5ee449a2013-04-12 20:20:50 +0000464 SkImage* imageBefore = surface->newImageSnapshot(); \
junov@chromium.org995beb62013-03-28 13:49:22 +0000465 SkAutoTUnref<SkImage> aur_before(imageBefore); \
466 canvas-> command ; \
junov@chromium.org5ee449a2013-04-12 20:20:50 +0000467 SkImage* imageAfter = surface->newImageSnapshot(); \
junov@chromium.org995beb62013-03-28 13:49:22 +0000468 SkAutoTUnref<SkImage> aur_after(imageAfter); \
469 REPORTER_ASSERT(reporter, imageBefore != imageAfter); \
470 }
471
472 EXPECT_COPY_ON_WRITE(clear(testColor))
473 EXPECT_COPY_ON_WRITE(drawPaint(testPaint))
474 EXPECT_COPY_ON_WRITE(drawPoints(SkCanvas::kPoints_PointMode, testPointCount, testPoints, \
475 testPaint))
476 EXPECT_COPY_ON_WRITE(drawOval(testRect, testPaint))
477 EXPECT_COPY_ON_WRITE(drawRect(testRect, testPaint))
478 EXPECT_COPY_ON_WRITE(drawRRect(testRRect, testPaint))
479 EXPECT_COPY_ON_WRITE(drawPath(testPath, testPaint))
480 EXPECT_COPY_ON_WRITE(drawBitmap(testBitmap, 0, 0))
481 EXPECT_COPY_ON_WRITE(drawBitmapRect(testBitmap, NULL, testRect))
junov@chromium.org995beb62013-03-28 13:49:22 +0000482 EXPECT_COPY_ON_WRITE(drawBitmapNine(testBitmap, testIRect, testRect, NULL))
483 EXPECT_COPY_ON_WRITE(drawSprite(testBitmap, 0, 0, NULL))
484 EXPECT_COPY_ON_WRITE(drawText(testText.c_str(), testText.size(), 0, 1, testPaint))
485 EXPECT_COPY_ON_WRITE(drawPosText(testText.c_str(), testText.size(), testPoints2, \
486 testPaint))
487 EXPECT_COPY_ON_WRITE(drawTextOnPath(testText.c_str(), testText.size(), testPath, NULL, \
488 testPaint))
489}
490
junov@chromium.orgaf058352013-04-03 15:03:26 +0000491static void TestSurfaceWritableAfterSnapshotRelease(skiatest::Reporter* reporter,
492 SurfaceType surfaceType,
493 GrContext* context) {
494 // This test succeeds by not triggering an assertion.
495 // The test verifies that the surface remains writable (usable) after
496 // acquiring and releasing a snapshot without triggering a copy on write.
commit-bot@chromium.org4d24b742013-07-25 23:29:40 +0000497 SkAutoTUnref<SkSurface> surface(createSurface(surfaceType, context));
junov@chromium.orgaf058352013-04-03 15:03:26 +0000498 SkCanvas* canvas = surface->getCanvas();
499 canvas->clear(1);
junov@chromium.org5ee449a2013-04-12 20:20:50 +0000500 surface->newImageSnapshot()->unref(); // Create and destroy SkImage
commit-bot@chromium.org4d24b742013-07-25 23:29:40 +0000501 canvas->clear(2); // Must not assert internally
junov@chromium.org995beb62013-03-28 13:49:22 +0000502}
junov@chromium.orgda904742013-05-01 22:38:16 +0000503
junov@chromium.orgb516a412013-05-01 22:49:59 +0000504#if SK_SUPPORT_GPU
commit-bot@chromium.org4d24b742013-07-25 23:29:40 +0000505static void Test_crbug263329(skiatest::Reporter* reporter,
commit-bot@chromium.orgd8a57af2014-03-19 21:19:16 +0000506 SurfaceType surfaceType,
commit-bot@chromium.org4d24b742013-07-25 23:29:40 +0000507 GrContext* context) {
508 // This is a regression test for crbug.com/263329
509 // Bug was caused by onCopyOnWrite releasing the old surface texture
510 // back to the scratch texture pool even though the texture is used
511 // by and active SkImage_Gpu.
commit-bot@chromium.orgd8a57af2014-03-19 21:19:16 +0000512 SkAutoTUnref<SkSurface> surface1(createSurface(surfaceType, context));
513 SkAutoTUnref<SkSurface> surface2(createSurface(surfaceType, context));
commit-bot@chromium.org4d24b742013-07-25 23:29:40 +0000514 SkCanvas* canvas1 = surface1->getCanvas();
515 SkCanvas* canvas2 = surface2->getCanvas();
516 canvas1->clear(1);
517 SkAutoTUnref<SkImage> image1(surface1->newImageSnapshot());
518 // Trigger copy on write, new backing is a scratch texture
519 canvas1->clear(2);
520 SkAutoTUnref<SkImage> image2(surface1->newImageSnapshot());
521 // Trigger copy on write, old backing should not be returned to scratch
522 // pool because it is held by image2
523 canvas1->clear(3);
524
525 canvas2->clear(4);
526 SkAutoTUnref<SkImage> image3(surface2->newImageSnapshot());
527 // Trigger copy on write on surface2. The new backing store should not
528 // be recycling a texture that is held by an existing image.
529 canvas2->clear(5);
530 SkAutoTUnref<SkImage> image4(surface2->newImageSnapshot());
531 REPORTER_ASSERT(reporter, image4->getTexture() != image3->getTexture());
532 // The following assertion checks crbug.com/263329
533 REPORTER_ASSERT(reporter, image4->getTexture() != image2->getTexture());
534 REPORTER_ASSERT(reporter, image4->getTexture() != image1->getTexture());
535 REPORTER_ASSERT(reporter, image3->getTexture() != image2->getTexture());
536 REPORTER_ASSERT(reporter, image3->getTexture() != image1->getTexture());
537 REPORTER_ASSERT(reporter, image2->getTexture() != image1->getTexture());
538}
539
junov@chromium.orgda904742013-05-01 22:38:16 +0000540static void TestGetTexture(skiatest::Reporter* reporter,
541 SurfaceType surfaceType,
542 GrContext* context) {
543 SkAutoTUnref<SkSurface> surface(createSurface(surfaceType, context));
544 SkAutoTUnref<SkImage> image(surface->newImageSnapshot());
545 GrTexture* texture = image->getTexture();
commit-bot@chromium.orgd8a57af2014-03-19 21:19:16 +0000546 if (surfaceType == kGpu_SurfaceType || surfaceType == kGpuScratch_SurfaceType) {
bsalomon49f085d2014-09-05 13:34:00 -0700547 REPORTER_ASSERT(reporter, texture);
junov@chromium.orgda904742013-05-01 22:38:16 +0000548 REPORTER_ASSERT(reporter, 0 != texture->getTextureHandle());
549 } else {
550 REPORTER_ASSERT(reporter, NULL == texture);
551 }
552 surface->notifyContentWillChange(SkSurface::kDiscard_ContentChangeMode);
553 REPORTER_ASSERT(reporter, image->getTexture() == texture);
554}
bsalomoneaaaf0b2015-01-23 08:08:04 -0800555
bsalomon3582d3e2015-02-13 14:20:05 -0800556#include "GrGpuResourcePriv.h"
bsalomoneaaaf0b2015-01-23 08:08:04 -0800557#include "SkGpuDevice.h"
558#include "SkImage_Gpu.h"
559#include "SkSurface_Gpu.h"
560
561SkSurface::Budgeted is_budgeted(SkSurface* surf) {
bsalomon3582d3e2015-02-13 14:20:05 -0800562 return ((SkSurface_Gpu*)surf)->getDevice()->accessRenderTarget()->resourcePriv().isBudgeted() ?
bsalomoneaaaf0b2015-01-23 08:08:04 -0800563 SkSurface::kYes_Budgeted : SkSurface::kNo_Budgeted;
564}
565
566SkSurface::Budgeted is_budgeted(SkImage* image) {
bsalomon3582d3e2015-02-13 14:20:05 -0800567 return ((SkImage_Gpu*)image)->getTexture()->resourcePriv().isBudgeted() ?
bsalomoneaaaf0b2015-01-23 08:08:04 -0800568 SkSurface::kYes_Budgeted : SkSurface::kNo_Budgeted;
569}
570
571static void test_surface_budget(skiatest::Reporter* reporter, GrContext* context) {
572 SkImageInfo info = SkImageInfo::MakeN32Premul(8,8);
573 for (int i = 0; i < 2; ++i) {
574 SkSurface::Budgeted sbudgeted = i ? SkSurface::kYes_Budgeted : SkSurface::kNo_Budgeted;
575 for (int j = 0; j < 2; ++j) {
576 SkSurface::Budgeted ibudgeted = j ? SkSurface::kYes_Budgeted : SkSurface::kNo_Budgeted;
577 SkAutoTUnref<SkSurface>
578 surface(SkSurface::NewRenderTarget(context, sbudgeted, info, 0));
579 SkASSERT(surface);
580 REPORTER_ASSERT(reporter, sbudgeted == is_budgeted(surface));
581
mtklein31ff2982015-01-24 11:27:27 -0800582 SkAutoTUnref<SkImage> image(surface->newImageSnapshot(ibudgeted));
bsalomoneaaaf0b2015-01-23 08:08:04 -0800583
584 // Initially the image shares a texture with the surface, and the surface decides
585 // whether it is budgeted or not.
586 REPORTER_ASSERT(reporter, sbudgeted == is_budgeted(surface));
587 REPORTER_ASSERT(reporter, sbudgeted == is_budgeted(image));
588
589 // Now trigger copy-on-write
590 surface->getCanvas()->clear(SK_ColorBLUE);
591
592 // They don't share a texture anymore. They should each have made their own budget
593 // decision.
594 REPORTER_ASSERT(reporter, sbudgeted == is_budgeted(surface));
595 REPORTER_ASSERT(reporter, ibudgeted == is_budgeted(image));
596 }
597 }
598}
599
junov@chromium.orgb516a412013-05-01 22:49:59 +0000600#endif
junov@chromium.orgda904742013-05-01 22:38:16 +0000601
junov@chromium.orgacea3ef2013-04-16 19:41:09 +0000602static void TestSurfaceNoCanvas(skiatest::Reporter* reporter,
603 SurfaceType surfaceType,
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +0000604 GrContext* context,
605 SkSurface::ContentChangeMode mode) {
junov@chromium.orgacea3ef2013-04-16 19:41:09 +0000606 // Verifies the robustness of SkSurface for handling use cases where calls
607 // are made before a canvas is created.
608 {
609 // Test passes by not asserting
610 SkSurface* surface = createSurface(surfaceType, context);
611 SkAutoTUnref<SkSurface> aur_surface(surface);
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +0000612 surface->notifyContentWillChange(mode);
robertphillips@google.com03087072013-10-02 16:42:21 +0000613 SkDEBUGCODE(surface->validate();)
junov@chromium.orgacea3ef2013-04-16 19:41:09 +0000614 }
615 {
616 SkSurface* surface = createSurface(surfaceType, context);
617 SkAutoTUnref<SkSurface> aur_surface(surface);
618 SkImage* image1 = surface->newImageSnapshot();
619 SkAutoTUnref<SkImage> aur_image1(image1);
robertphillips@google.com03087072013-10-02 16:42:21 +0000620 SkDEBUGCODE(image1->validate();)
621 SkDEBUGCODE(surface->validate();)
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +0000622 surface->notifyContentWillChange(mode);
robertphillips@google.com03087072013-10-02 16:42:21 +0000623 SkDEBUGCODE(image1->validate();)
624 SkDEBUGCODE(surface->validate();)
junov@chromium.orgacea3ef2013-04-16 19:41:09 +0000625 SkImage* image2 = surface->newImageSnapshot();
626 SkAutoTUnref<SkImage> aur_image2(image2);
robertphillips@google.com03087072013-10-02 16:42:21 +0000627 SkDEBUGCODE(image2->validate();)
628 SkDEBUGCODE(surface->validate();)
junov@chromium.orgacea3ef2013-04-16 19:41:09 +0000629 REPORTER_ASSERT(reporter, image1 != image2);
630 }
skia.committer@gmail.com45fb8b62013-04-17 07:00:56 +0000631
junov@chromium.orgacea3ef2013-04-16 19:41:09 +0000632}
junov@chromium.org995beb62013-03-28 13:49:22 +0000633
tfarina@chromium.org4ee16bf2014-01-10 22:08:27 +0000634DEF_GPUTEST(Surface, reporter, factory) {
reed@google.com999da9c2014-02-06 13:43:07 +0000635 test_image(reporter);
636
junov@chromium.orgaf058352013-04-03 15:03:26 +0000637 TestSurfaceCopyOnWrite(reporter, kRaster_SurfaceType, NULL);
junov@chromium.orgaf058352013-04-03 15:03:26 +0000638 TestSurfaceWritableAfterSnapshotRelease(reporter, kRaster_SurfaceType, NULL);
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +0000639 TestSurfaceNoCanvas(reporter, kRaster_SurfaceType, NULL, SkSurface::kDiscard_ContentChangeMode);
640 TestSurfaceNoCanvas(reporter, kRaster_SurfaceType, NULL, SkSurface::kRetain_ContentChangeMode);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000641
reedb2497c22014-12-31 12:31:43 -0800642 test_empty_image(reporter);
643 test_empty_surface(reporter, NULL);
644
reed67f2eb42014-12-10 06:54:06 -0800645 test_imagepeek(reporter, factory);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000646 test_canvaspeek(reporter, factory);
647
reed41e010c2015-06-09 12:16:53 -0700648 test_accessPixels(reporter, factory);
649
junov@chromium.orgb516a412013-05-01 22:49:59 +0000650#if SK_SUPPORT_GPU
junov@chromium.orgda904742013-05-01 22:38:16 +0000651 TestGetTexture(reporter, kRaster_SurfaceType, NULL);
bsalomon49f085d2014-09-05 13:34:00 -0700652 if (factory) {
bsalomone904c092014-07-17 10:50:59 -0700653 for (int i= 0; i < GrContextFactory::kGLContextTypeCnt; ++i) {
654 GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i;
655 if (!GrContextFactory::IsRenderingGLContext(glCtxType)) {
656 continue;
657 }
658 GrContext* context = factory->get(glCtxType);
bsalomon49f085d2014-09-05 13:34:00 -0700659 if (context) {
bsalomone904c092014-07-17 10:50:59 -0700660 Test_crbug263329(reporter, kGpu_SurfaceType, context);
661 Test_crbug263329(reporter, kGpuScratch_SurfaceType, context);
662 TestSurfaceCopyOnWrite(reporter, kGpu_SurfaceType, context);
663 TestSurfaceCopyOnWrite(reporter, kGpuScratch_SurfaceType, context);
664 TestSurfaceWritableAfterSnapshotRelease(reporter, kGpu_SurfaceType, context);
665 TestSurfaceWritableAfterSnapshotRelease(reporter, kGpuScratch_SurfaceType, context);
666 TestSurfaceNoCanvas(reporter, kGpu_SurfaceType, context, SkSurface::kDiscard_ContentChangeMode);
667 TestSurfaceNoCanvas(reporter, kGpuScratch_SurfaceType, context, SkSurface::kDiscard_ContentChangeMode);
668 TestSurfaceNoCanvas(reporter, kGpu_SurfaceType, context, SkSurface::kRetain_ContentChangeMode);
669 TestSurfaceNoCanvas(reporter, kGpuScratch_SurfaceType, context, SkSurface::kRetain_ContentChangeMode);
670 TestGetTexture(reporter, kGpu_SurfaceType, context);
671 TestGetTexture(reporter, kGpuScratch_SurfaceType, context);
reedb2497c22014-12-31 12:31:43 -0800672 test_empty_surface(reporter, context);
bsalomoneaaaf0b2015-01-23 08:08:04 -0800673 test_surface_budget(reporter, context);
bsalomone4579ad2015-04-08 08:38:40 -0700674 test_wrapped_texture_surface(reporter, context);
bsalomone904c092014-07-17 10:50:59 -0700675 }
robertphillips@google.com3bddb382013-11-12 13:51:03 +0000676 }
junov@chromium.orgaf058352013-04-03 15:03:26 +0000677 }
junov@chromium.org995beb62013-03-28 13:49:22 +0000678#endif
679}
reed8b26b992015-05-07 15:36:17 -0700680
681#if SK_SUPPORT_GPU
682static SkImage* make_desc_image(GrContext* ctx, int w, int h, GrBackendObject texID, bool doCopy) {
683 GrBackendTextureDesc desc;
684 desc.fConfig = kSkia8888_GrPixelConfig;
685 // need to be a rendertarget for now...
686 desc.fFlags = kRenderTarget_GrBackendTextureFlag;
687 desc.fWidth = w;
688 desc.fHeight = h;
689 desc.fSampleCnt = 0;
690 desc.fTextureHandle = texID;
691 return doCopy ? SkImage::NewFromTextureCopy(ctx, desc) : SkImage::NewFromTexture(ctx, desc);
692}
693
694static void test_image_color(skiatest::Reporter* reporter, SkImage* image, SkPMColor expected) {
695 const SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
696 SkPMColor pixel;
697 REPORTER_ASSERT(reporter, image->readPixels(info, &pixel, sizeof(pixel), 0, 0));
698 REPORTER_ASSERT(reporter, pixel == expected);
699}
700
701DEF_GPUTEST(SkImage_NewFromTexture, reporter, factory) {
702 GrContext* ctx = factory->get(GrContextFactory::kNative_GLContextType);
703 if (!ctx) {
704 REPORTER_ASSERT(reporter, false);
705 return;
706 }
707 GrTextureProvider* provider = ctx->textureProvider();
708
709 const int w = 10;
710 const int h = 10;
711 SkPMColor storage[w * h];
712 const SkPMColor expected0 = SkPreMultiplyColor(SK_ColorRED);
713 sk_memset32(storage, expected0, w * h);
714
715 GrSurfaceDesc desc;
716 desc.fFlags = kRenderTarget_GrSurfaceFlag; // needs to be a rendertarget for readpixels();
717 desc.fOrigin = kDefault_GrSurfaceOrigin;
718 desc.fWidth = w;
719 desc.fHeight = h;
720 desc.fConfig = kSkia8888_GrPixelConfig;
721 desc.fSampleCnt = 0;
722
723 SkAutoTUnref<GrTexture> tex(provider->createTexture(desc, false, storage, w * 4));
724 if (!tex) {
725 REPORTER_ASSERT(reporter, false);
726 return;
727 }
728
729 GrBackendObject srcTex = tex->getTextureHandle();
730 SkAutoTUnref<SkImage> refImg(make_desc_image(ctx, w, h, srcTex, false));
731 SkAutoTUnref<SkImage> cpyImg(make_desc_image(ctx, w, h, srcTex, true));
732
733 test_image_color(reporter, refImg, expected0);
734 test_image_color(reporter, cpyImg, expected0);
735
736 // Now lets jam new colors into our "external" texture, and see if the images notice
737 const SkPMColor expected1 = SkPreMultiplyColor(SK_ColorBLUE);
738 sk_memset32(storage, expected1, w * h);
739 tex->writePixels(0, 0, w, h, kSkia8888_GrPixelConfig, storage, GrContext::kFlushWrites_PixelOp);
740
741 // We expect the ref'd image to see the new color, but cpy'd one should still see the old color
742 test_image_color(reporter, refImg, expected1);
743 test_image_color(reporter, cpyImg, expected0);
744}
745#endif