blob: a86170b75e1aa88bba07f670fa4ba87b31a4048a [file] [log] [blame]
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001/*
2 * Copyright 2011 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 "SkGpuDevice.h"
9
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +000010#include "effects/GrBicubicEffect.h"
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +000011#include "effects/GrTextureDomain.h"
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +000012#include "effects/GrSimpleTextureEffect.h"
13
14#include "GrContext.h"
15#include "GrBitmapTextContext.h"
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +000016#include "GrDistanceFieldTextContext.h"
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +000017
18#include "SkGrTexturePixelRef.h"
19
commit-bot@chromium.org82139702014-03-10 22:53:20 +000020#include "SkBounder.h"
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +000021#include "SkColorFilter.h"
22#include "SkDeviceImageFilterProxy.h"
23#include "SkDrawProcs.h"
24#include "SkGlyphCache.h"
25#include "SkImageFilter.h"
commit-bot@chromium.org82139702014-03-10 22:53:20 +000026#include "SkMaskFilter.h"
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +000027#include "SkPathEffect.h"
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +000028#include "SkPicture.h"
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +000029#include "SkRRect.h"
30#include "SkStroke.h"
reed@google.com76f10a32014-02-05 15:32:21 +000031#include "SkSurface.h"
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +000032#include "SkTLazy.h"
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +000033#include "SkUtils.h"
34#include "SkErrorInternals.h"
35
36#define CACHE_COMPATIBLE_DEVICE_TEXTURES 1
37
38#if 0
39 extern bool (*gShouldDrawProc)();
40 #define CHECK_SHOULD_DRAW(draw, forceI) \
41 do { \
42 if (gShouldDrawProc && !gShouldDrawProc()) return; \
43 this->prepareDraw(draw, forceI); \
44 } while (0)
45#else
46 #define CHECK_SHOULD_DRAW(draw, forceI) this->prepareDraw(draw, forceI)
47#endif
48
49// This constant represents the screen alignment criterion in texels for
50// requiring texture domain clamping to prevent color bleeding when drawing
51// a sub region of a larger source image.
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +000052#define COLOR_BLEED_TOLERANCE 0.001f
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +000053
54#define DO_DEFERRED_CLEAR() \
55 do { \
56 if (fNeedClear) { \
57 this->clear(SK_ColorTRANSPARENT); \
58 } \
59 } while (false) \
60
61///////////////////////////////////////////////////////////////////////////////
62
63#define CHECK_FOR_ANNOTATION(paint) \
64 do { if (paint.getAnnotation()) { return; } } while (0)
65
66///////////////////////////////////////////////////////////////////////////////
67
68
69class SkGpuDevice::SkAutoCachedTexture : public ::SkNoncopyable {
70public:
71 SkAutoCachedTexture()
72 : fDevice(NULL)
73 , fTexture(NULL) {
74 }
75
76 SkAutoCachedTexture(SkGpuDevice* device,
77 const SkBitmap& bitmap,
78 const GrTextureParams* params,
79 GrTexture** texture)
80 : fDevice(NULL)
81 , fTexture(NULL) {
82 SkASSERT(NULL != texture);
83 *texture = this->set(device, bitmap, params);
84 }
85
86 ~SkAutoCachedTexture() {
87 if (NULL != fTexture) {
88 GrUnlockAndUnrefCachedBitmapTexture(fTexture);
89 }
90 }
91
92 GrTexture* set(SkGpuDevice* device,
93 const SkBitmap& bitmap,
94 const GrTextureParams* params) {
95 if (NULL != fTexture) {
96 GrUnlockAndUnrefCachedBitmapTexture(fTexture);
97 fTexture = NULL;
98 }
99 fDevice = device;
100 GrTexture* result = (GrTexture*)bitmap.getTexture();
101 if (NULL == result) {
102 // Cannot return the native texture so look it up in our cache
103 fTexture = GrLockAndRefCachedBitmapTexture(device->context(), bitmap, params);
104 result = fTexture;
105 }
106 return result;
107 }
108
109private:
110 SkGpuDevice* fDevice;
111 GrTexture* fTexture;
112};
113
114///////////////////////////////////////////////////////////////////////////////
115
116struct GrSkDrawProcs : public SkDrawProcs {
117public:
118 GrContext* fContext;
119 GrTextContext* fTextContext;
120 GrFontScaler* fFontScaler; // cached in the skia glyphcache
121};
122
123///////////////////////////////////////////////////////////////////////////////
124
125static SkBitmap::Config grConfig2skConfig(GrPixelConfig config, bool* isOpaque) {
126 switch (config) {
127 case kAlpha_8_GrPixelConfig:
128 *isOpaque = false;
129 return SkBitmap::kA8_Config;
130 case kRGB_565_GrPixelConfig:
131 *isOpaque = true;
132 return SkBitmap::kRGB_565_Config;
133 case kRGBA_4444_GrPixelConfig:
134 *isOpaque = false;
135 return SkBitmap::kARGB_4444_Config;
136 case kSkia8888_GrPixelConfig:
137 // we don't currently have a way of knowing whether
138 // a 8888 is opaque based on the config.
139 *isOpaque = false;
140 return SkBitmap::kARGB_8888_Config;
141 default:
142 *isOpaque = false;
143 return SkBitmap::kNo_Config;
144 }
145}
146
147/*
148 * GrRenderTarget does not know its opaqueness, only its config, so we have
149 * to make conservative guesses when we return an "equivalent" bitmap.
150 */
151static SkBitmap make_bitmap(GrContext* context, GrRenderTarget* renderTarget) {
152 bool isOpaque;
153 SkBitmap::Config config = grConfig2skConfig(renderTarget->config(), &isOpaque);
154
155 SkBitmap bitmap;
156 bitmap.setConfig(config, renderTarget->width(), renderTarget->height(), 0,
157 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
158 return bitmap;
159}
160
161SkGpuDevice* SkGpuDevice::Create(GrSurface* surface) {
162 SkASSERT(NULL != surface);
163 if (NULL == surface->asRenderTarget() || NULL == surface->getContext()) {
164 return NULL;
165 }
166 if (surface->asTexture()) {
167 return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asTexture()));
168 } else {
169 return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asRenderTarget()));
170 }
171}
172
173SkGpuDevice::SkGpuDevice(GrContext* context, GrTexture* texture)
174 : SkBitmapDevice(make_bitmap(context, texture->asRenderTarget())) {
175 this->initFromRenderTarget(context, texture->asRenderTarget(), false);
176}
177
178SkGpuDevice::SkGpuDevice(GrContext* context, GrRenderTarget* renderTarget)
179 : SkBitmapDevice(make_bitmap(context, renderTarget)) {
180 this->initFromRenderTarget(context, renderTarget, false);
181}
182
183void SkGpuDevice::initFromRenderTarget(GrContext* context,
184 GrRenderTarget* renderTarget,
185 bool cached) {
186 fDrawProcs = NULL;
187
188 fContext = context;
189 fContext->ref();
190
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000191 fMainTextContext = SkNEW_ARGS(GrDistanceFieldTextContext, (fContext, fLeakyProperties));
192 fFallbackTextContext = SkNEW_ARGS(GrBitmapTextContext, (fContext, fLeakyProperties));
commit-bot@chromium.orgcc40f062014-01-24 14:38:27 +0000193
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000194 fRenderTarget = NULL;
195 fNeedClear = false;
196
197 SkASSERT(NULL != renderTarget);
198 fRenderTarget = renderTarget;
199 fRenderTarget->ref();
200
201 // Hold onto to the texture in the pixel ref (if there is one) because the texture holds a ref
202 // on the RT but not vice-versa.
203 // TODO: Remove this trickery once we figure out how to make SkGrPixelRef do this without
204 // busting chrome (for a currently unknown reason).
205 GrSurface* surface = fRenderTarget->asTexture();
206 if (NULL == surface) {
207 surface = fRenderTarget;
208 }
reed@google.combf790232013-12-13 19:45:58 +0000209
210 SkImageInfo info;
211 surface->asImageInfo(&info);
212 SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (info, surface, cached));
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000213
reed@google.com672588b2014-01-08 15:42:01 +0000214 this->setPixelRef(pr)->unref();
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000215}
216
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000217SkGpuDevice* SkGpuDevice::Create(GrContext* context, const SkImageInfo& origInfo,
218 int sampleCount) {
219 if (kUnknown_SkColorType == origInfo.colorType() ||
220 origInfo.width() < 0 || origInfo.height() < 0) {
221 return NULL;
222 }
223
224 SkImageInfo info = origInfo;
225 // TODO: perhas we can loosen this check now that colortype is more detailed
226 // e.g. can we support both RGBA and BGRA here?
227 if (kRGB_565_SkColorType == info.colorType()) {
228 info.fAlphaType = kOpaque_SkAlphaType; // force this setting
229 } else {
230 info.fColorType = kPMColor_SkColorType;
231 if (kOpaque_SkAlphaType != info.alphaType()) {
232 info.fAlphaType = kPremul_SkAlphaType; // force this setting
233 }
234 }
235
236 GrTextureDesc desc;
237 desc.fFlags = kRenderTarget_GrTextureFlagBit;
238 desc.fWidth = info.width();
239 desc.fHeight = info.height();
240 desc.fConfig = SkImageInfo2GrPixelConfig(info.colorType(), info.alphaType());
241 desc.fSampleCnt = sampleCount;
242
243 SkAutoTUnref<GrTexture> texture(context->createUncachedTexture(desc, NULL, 0));
244 if (!texture.get()) {
245 return NULL;
246 }
skia.committer@gmail.com969588f2014-02-16 03:01:56 +0000247
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000248 return SkNEW_ARGS(SkGpuDevice, (context, texture.get()));
249}
250
251#ifdef SK_SUPPORT_LEGACY_COMPATIBLEDEVICE_CONFIG
252static SkBitmap make_bitmap(SkBitmap::Config config, int width, int height) {
253 SkBitmap bm;
254 bm.setConfig(SkImageInfo::Make(width, height,
255 SkBitmapConfigToColorType(config),
256 kPremul_SkAlphaType));
257 return bm;
258}
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000259SkGpuDevice::SkGpuDevice(GrContext* context,
260 SkBitmap::Config config,
261 int width,
262 int height,
263 int sampleCount)
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000264 : SkBitmapDevice(make_bitmap(config, width, height))
reed@google.combf790232013-12-13 19:45:58 +0000265{
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000266 fDrawProcs = NULL;
skia.committer@gmail.com969588f2014-02-16 03:01:56 +0000267
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000268 fContext = context;
269 fContext->ref();
skia.committer@gmail.com969588f2014-02-16 03:01:56 +0000270
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000271 fMainTextContext = SkNEW_ARGS(GrDistanceFieldTextContext, (fContext, fLeakyProperties));
272 fFallbackTextContext = SkNEW_ARGS(GrBitmapTextContext, (fContext, fLeakyProperties));
skia.committer@gmail.com969588f2014-02-16 03:01:56 +0000273
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000274 fRenderTarget = NULL;
275 fNeedClear = false;
skia.committer@gmail.com969588f2014-02-16 03:01:56 +0000276
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000277 if (config != SkBitmap::kRGB_565_Config) {
278 config = SkBitmap::kARGB_8888_Config;
279 }
skia.committer@gmail.com969588f2014-02-16 03:01:56 +0000280
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000281 GrTextureDesc desc;
282 desc.fFlags = kRenderTarget_GrTextureFlagBit;
283 desc.fWidth = width;
284 desc.fHeight = height;
285 desc.fConfig = SkBitmapConfig2GrPixelConfig(config);
286 desc.fSampleCnt = sampleCount;
skia.committer@gmail.com969588f2014-02-16 03:01:56 +0000287
reed@google.combf790232013-12-13 19:45:58 +0000288 SkImageInfo info;
289 if (!GrPixelConfig2ColorType(desc.fConfig, &info.fColorType)) {
290 sk_throw();
291 }
292 info.fWidth = width;
293 info.fHeight = height;
294 info.fAlphaType = kPremul_SkAlphaType;
skia.committer@gmail.com969588f2014-02-16 03:01:56 +0000295
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000296 SkAutoTUnref<GrTexture> texture(fContext->createUncachedTexture(desc, NULL, 0));
skia.committer@gmail.com969588f2014-02-16 03:01:56 +0000297
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000298 if (NULL != texture) {
299 fRenderTarget = texture->asRenderTarget();
300 fRenderTarget->ref();
skia.committer@gmail.com969588f2014-02-16 03:01:56 +0000301
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000302 SkASSERT(NULL != fRenderTarget);
skia.committer@gmail.com969588f2014-02-16 03:01:56 +0000303
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000304 // wrap the bitmap with a pixelref to expose our texture
reed@google.combf790232013-12-13 19:45:58 +0000305 SkGrPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (info, texture));
reed@google.com672588b2014-01-08 15:42:01 +0000306 this->setPixelRef(pr)->unref();
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000307 } else {
308 GrPrintf("--- failed to create gpu-offscreen [%d %d]\n",
309 width, height);
310 SkASSERT(false);
311 }
312}
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000313#endif
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000314
315SkGpuDevice::~SkGpuDevice() {
316 if (fDrawProcs) {
317 delete fDrawProcs;
318 }
skia.committer@gmail.comd2ac07b2014-01-25 07:01:49 +0000319
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000320 delete fMainTextContext;
321 delete fFallbackTextContext;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000322
323 // The GrContext takes a ref on the target. We don't want to cause the render
324 // target to be unnecessarily kept alive.
325 if (fContext->getRenderTarget() == fRenderTarget) {
326 fContext->setRenderTarget(NULL);
327 }
328
329 if (fContext->getClip() == &fClipData) {
330 fContext->setClip(NULL);
331 }
332
333 SkSafeUnref(fRenderTarget);
334 fContext->unref();
335}
336
337///////////////////////////////////////////////////////////////////////////////
338
339void SkGpuDevice::makeRenderTargetCurrent() {
340 DO_DEFERRED_CLEAR();
341 fContext->setRenderTarget(fRenderTarget);
342}
343
344///////////////////////////////////////////////////////////////////////////////
345
346namespace {
347GrPixelConfig config8888_to_grconfig_and_flags(SkCanvas::Config8888 config8888, uint32_t* flags) {
348 switch (config8888) {
349 case SkCanvas::kNative_Premul_Config8888:
350 *flags = 0;
351 return kSkia8888_GrPixelConfig;
352 case SkCanvas::kNative_Unpremul_Config8888:
353 *flags = GrContext::kUnpremul_PixelOpsFlag;
354 return kSkia8888_GrPixelConfig;
355 case SkCanvas::kBGRA_Premul_Config8888:
356 *flags = 0;
357 return kBGRA_8888_GrPixelConfig;
358 case SkCanvas::kBGRA_Unpremul_Config8888:
359 *flags = GrContext::kUnpremul_PixelOpsFlag;
360 return kBGRA_8888_GrPixelConfig;
361 case SkCanvas::kRGBA_Premul_Config8888:
362 *flags = 0;
363 return kRGBA_8888_GrPixelConfig;
364 case SkCanvas::kRGBA_Unpremul_Config8888:
365 *flags = GrContext::kUnpremul_PixelOpsFlag;
366 return kRGBA_8888_GrPixelConfig;
367 default:
368 GrCrash("Unexpected Config8888.");
369 *flags = 0; // suppress warning
370 return kSkia8888_GrPixelConfig;
371 }
372}
373}
374
375bool SkGpuDevice::onReadPixels(const SkBitmap& bitmap,
376 int x, int y,
377 SkCanvas::Config8888 config8888) {
378 DO_DEFERRED_CLEAR();
379 SkASSERT(SkBitmap::kARGB_8888_Config == bitmap.config());
380 SkASSERT(!bitmap.isNull());
381 SkASSERT(SkIRect::MakeWH(this->width(), this->height()).contains(SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height())));
382
383 SkAutoLockPixels alp(bitmap);
384 GrPixelConfig config;
385 uint32_t flags;
386 config = config8888_to_grconfig_and_flags(config8888, &flags);
387 return fContext->readRenderTargetPixels(fRenderTarget,
388 x, y,
389 bitmap.width(),
390 bitmap.height(),
391 config,
392 bitmap.getPixels(),
393 bitmap.rowBytes(),
394 flags);
395}
396
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000397#ifdef SK_SUPPORT_LEGACY_WRITEPIXELSCONFIG
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000398void SkGpuDevice::writePixels(const SkBitmap& bitmap, int x, int y,
399 SkCanvas::Config8888 config8888) {
400 SkAutoLockPixels alp(bitmap);
401 if (!bitmap.readyToDraw()) {
402 return;
403 }
404
405 GrPixelConfig config;
406 uint32_t flags;
407 if (SkBitmap::kARGB_8888_Config == bitmap.config()) {
408 config = config8888_to_grconfig_and_flags(config8888, &flags);
409 } else {
410 flags = 0;
411 config= SkBitmapConfig2GrPixelConfig(bitmap.config());
412 }
413
414 fRenderTarget->writePixels(x, y, bitmap.width(), bitmap.height(),
415 config, bitmap.getPixels(), bitmap.rowBytes(), flags);
416}
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000417#endif
418
419bool SkGpuDevice::onWritePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes,
420 int x, int y) {
421 // TODO: teach fRenderTarget to take ImageInfo directly to specify the src pixels
422 GrPixelConfig config = SkImageInfo2GrPixelConfig(info.colorType(), info.alphaType());
423 if (kUnknown_GrPixelConfig == config) {
424 return false;
425 }
426 uint32_t flags = 0;
427 if (kUnpremul_SkAlphaType == info.alphaType()) {
428 flags = GrContext::kUnpremul_PixelOpsFlag;
429 }
430 fRenderTarget->writePixels(x, y, info.width(), info.height(), config, pixels, rowBytes, flags);
431
432 // need to bump our genID for compatibility with clients that "know" we have a bitmap
433 this->onAccessBitmap().notifyPixelsChanged();
434
435 return true;
436}
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000437
438void SkGpuDevice::onAttachToCanvas(SkCanvas* canvas) {
439 INHERITED::onAttachToCanvas(canvas);
440
441 // Canvas promises that this ptr is valid until onDetachFromCanvas is called
442 fClipData.fClipStack = canvas->getClipStack();
443}
444
445void SkGpuDevice::onDetachFromCanvas() {
446 INHERITED::onDetachFromCanvas();
447 fClipData.fClipStack = NULL;
448}
449
450// call this every draw call, to ensure that the context reflects our state,
451// and not the state from some other canvas/device
452void SkGpuDevice::prepareDraw(const SkDraw& draw, bool forceIdentity) {
453 SkASSERT(NULL != fClipData.fClipStack);
454
455 fContext->setRenderTarget(fRenderTarget);
456
457 SkASSERT(draw.fClipStack && draw.fClipStack == fClipData.fClipStack);
458
459 if (forceIdentity) {
460 fContext->setIdentityMatrix();
461 } else {
462 fContext->setMatrix(*draw.fMatrix);
463 }
464 fClipData.fOrigin = this->getOrigin();
465
466 fContext->setClip(&fClipData);
467
468 DO_DEFERRED_CLEAR();
469}
470
471GrRenderTarget* SkGpuDevice::accessRenderTarget() {
472 DO_DEFERRED_CLEAR();
473 return fRenderTarget;
474}
475
476///////////////////////////////////////////////////////////////////////////////
477
478SK_COMPILE_ASSERT(SkShader::kNone_BitmapType == 0, shader_type_mismatch);
479SK_COMPILE_ASSERT(SkShader::kDefault_BitmapType == 1, shader_type_mismatch);
480SK_COMPILE_ASSERT(SkShader::kRadial_BitmapType == 2, shader_type_mismatch);
481SK_COMPILE_ASSERT(SkShader::kSweep_BitmapType == 3, shader_type_mismatch);
482SK_COMPILE_ASSERT(SkShader::kTwoPointRadial_BitmapType == 4,
483 shader_type_mismatch);
484SK_COMPILE_ASSERT(SkShader::kTwoPointConical_BitmapType == 5,
485 shader_type_mismatch);
486SK_COMPILE_ASSERT(SkShader::kLinear_BitmapType == 6, shader_type_mismatch);
487SK_COMPILE_ASSERT(SkShader::kLast_BitmapType == 6, shader_type_mismatch);
488
489namespace {
490
491// converts a SkPaint to a GrPaint, ignoring the skPaint's shader
492// justAlpha indicates that skPaint's alpha should be used rather than the color
493// Callers may subsequently modify the GrPaint. Setting constantColor indicates
494// that the final paint will draw the same color at every pixel. This allows
495// an optimization where the the color filter can be applied to the skPaint's
496// color once while converting to GrPaint and then ignored.
497inline bool skPaint2GrPaintNoShader(SkGpuDevice* dev,
498 const SkPaint& skPaint,
499 bool justAlpha,
500 bool constantColor,
501 GrPaint* grPaint) {
502
503 grPaint->setDither(skPaint.isDither());
504 grPaint->setAntiAlias(skPaint.isAntiAlias());
505
506 SkXfermode::Coeff sm;
507 SkXfermode::Coeff dm;
508
509 SkXfermode* mode = skPaint.getXfermode();
510 GrEffectRef* xferEffect = NULL;
511 if (SkXfermode::AsNewEffectOrCoeff(mode, &xferEffect, &sm, &dm)) {
512 if (NULL != xferEffect) {
513 grPaint->addColorEffect(xferEffect)->unref();
514 sm = SkXfermode::kOne_Coeff;
515 dm = SkXfermode::kZero_Coeff;
516 }
517 } else {
518 //SkDEBUGCODE(SkDebugf("Unsupported xfer mode.\n");)
519#if 0
520 return false;
521#else
522 // Fall back to src-over
523 sm = SkXfermode::kOne_Coeff;
524 dm = SkXfermode::kISA_Coeff;
525#endif
526 }
527 grPaint->setBlendFunc(sk_blend_to_grblend(sm), sk_blend_to_grblend(dm));
528
529 if (justAlpha) {
530 uint8_t alpha = skPaint.getAlpha();
531 grPaint->setColor(GrColorPackRGBA(alpha, alpha, alpha, alpha));
532 // justAlpha is currently set to true only if there is a texture,
533 // so constantColor should not also be true.
534 SkASSERT(!constantColor);
535 } else {
536 grPaint->setColor(SkColor2GrColor(skPaint.getColor()));
537 }
538
539 SkColorFilter* colorFilter = skPaint.getColorFilter();
540 if (NULL != colorFilter) {
541 // if the source color is a constant then apply the filter here once rather than per pixel
542 // in a shader.
543 if (constantColor) {
544 SkColor filtered = colorFilter->filterColor(skPaint.getColor());
545 grPaint->setColor(SkColor2GrColor(filtered));
546 } else {
547 SkAutoTUnref<GrEffectRef> effect(colorFilter->asNewEffect(dev->context()));
548 if (NULL != effect.get()) {
549 grPaint->addColorEffect(effect);
550 }
551 }
552 }
553
554 return true;
555}
556
557// This function is similar to skPaint2GrPaintNoShader but also converts
558// skPaint's shader to a GrTexture/GrEffectStage if possible. The texture to
559// be used is set on grPaint and returned in param act. constantColor has the
560// same meaning as in skPaint2GrPaintNoShader.
561inline bool skPaint2GrPaintShader(SkGpuDevice* dev,
562 const SkPaint& skPaint,
563 bool constantColor,
564 GrPaint* grPaint) {
565 SkShader* shader = skPaint.getShader();
566 if (NULL == shader) {
567 return skPaint2GrPaintNoShader(dev, skPaint, false, constantColor, grPaint);
568 }
569
commit-bot@chromium.org60770572014-01-13 15:57:05 +0000570 // SkShader::asNewEffect() may do offscreen rendering. Setup default drawing state and require
571 // the shader to set a render target .
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000572 GrContext::AutoWideOpenIdentityDraw awo(dev->context(), NULL);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000573
574 // setup the shader as the first color effect on the paint
575 SkAutoTUnref<GrEffectRef> effect(shader->asNewEffect(dev->context(), skPaint));
576 if (NULL != effect.get()) {
577 grPaint->addColorEffect(effect);
578 // Now setup the rest of the paint.
579 return skPaint2GrPaintNoShader(dev, skPaint, true, false, grPaint);
580 } else {
581 // We still don't have SkColorShader::asNewEffect() implemented.
582 SkShader::GradientInfo info;
583 SkColor color;
584
585 info.fColors = &color;
586 info.fColorOffsets = NULL;
587 info.fColorCount = 1;
588 if (SkShader::kColor_GradientType == shader->asAGradient(&info)) {
589 SkPaint copy(skPaint);
590 copy.setShader(NULL);
591 // modulate the paint alpha by the shader's solid color alpha
592 U8CPU newA = SkMulDiv255Round(SkColorGetA(color), copy.getAlpha());
593 copy.setColor(SkColorSetA(color, newA));
594 return skPaint2GrPaintNoShader(dev, copy, false, constantColor, grPaint);
595 } else {
596 return false;
597 }
598 }
599}
600}
601
602///////////////////////////////////////////////////////////////////////////////
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000603
604SkBitmap::Config SkGpuDevice::config() const {
605 if (NULL == fRenderTarget) {
606 return SkBitmap::kNo_Config;
607 }
608
609 bool isOpaque;
610 return grConfig2skConfig(fRenderTarget->config(), &isOpaque);
611}
612
613void SkGpuDevice::clear(SkColor color) {
614 SkIRect rect = SkIRect::MakeWH(this->width(), this->height());
615 fContext->clear(&rect, SkColor2GrColor(color), true, fRenderTarget);
616 fNeedClear = false;
617}
618
619void SkGpuDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
620 CHECK_SHOULD_DRAW(draw, false);
621
622 GrPaint grPaint;
623 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
624 return;
625 }
626
627 fContext->drawPaint(grPaint);
628}
629
630// must be in SkCanvas::PointMode order
631static const GrPrimitiveType gPointMode2PrimtiveType[] = {
632 kPoints_GrPrimitiveType,
633 kLines_GrPrimitiveType,
634 kLineStrip_GrPrimitiveType
635};
636
637void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
638 size_t count, const SkPoint pts[], const SkPaint& paint) {
639 CHECK_FOR_ANNOTATION(paint);
640 CHECK_SHOULD_DRAW(draw, false);
641
642 SkScalar width = paint.getStrokeWidth();
643 if (width < 0) {
644 return;
645 }
646
647 // we only handle hairlines and paints without path effects or mask filters,
648 // else we let the SkDraw call our drawPath()
649 if (width > 0 || paint.getPathEffect() || paint.getMaskFilter()) {
650 draw.drawPoints(mode, count, pts, paint, true);
651 return;
652 }
653
654 GrPaint grPaint;
655 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
656 return;
657 }
658
659 fContext->drawVertices(grPaint,
660 gPointMode2PrimtiveType[mode],
robertphillips@google.coma4662862013-11-21 14:24:16 +0000661 SkToS32(count),
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000662 (GrPoint*)pts,
663 NULL,
664 NULL,
665 NULL,
666 0);
667}
668
669///////////////////////////////////////////////////////////////////////////////
670
671void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect,
672 const SkPaint& paint) {
673 CHECK_FOR_ANNOTATION(paint);
674 CHECK_SHOULD_DRAW(draw, false);
675
676 bool doStroke = paint.getStyle() != SkPaint::kFill_Style;
677 SkScalar width = paint.getStrokeWidth();
678
679 /*
680 We have special code for hairline strokes, miter-strokes, bevel-stroke
681 and fills. Anything else we just call our path code.
682 */
683 bool usePath = doStroke && width > 0 &&
684 (paint.getStrokeJoin() == SkPaint::kRound_Join ||
685 (paint.getStrokeJoin() == SkPaint::kBevel_Join && rect.isEmpty()));
686 // another two reasons we might need to call drawPath...
687 if (paint.getMaskFilter() || paint.getPathEffect()) {
688 usePath = true;
689 }
690 if (!usePath && paint.isAntiAlias() && !fContext->getMatrix().rectStaysRect()) {
691#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)
692 if (doStroke) {
693#endif
694 usePath = true;
695#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)
696 } else {
697 usePath = !fContext->getMatrix().preservesRightAngles();
698 }
699#endif
700 }
701 // until we can both stroke and fill rectangles
702 if (paint.getStyle() == SkPaint::kStrokeAndFill_Style) {
703 usePath = true;
704 }
705
706 if (usePath) {
707 SkPath path;
708 path.addRect(rect);
709 this->drawPath(draw, path, paint, NULL, true);
710 return;
711 }
712
713 GrPaint grPaint;
714 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
715 return;
716 }
717
718 if (!doStroke) {
719 fContext->drawRect(grPaint, rect);
720 } else {
721 SkStrokeRec stroke(paint);
722 fContext->drawRect(grPaint, rect, &stroke);
723 }
724}
725
726///////////////////////////////////////////////////////////////////////////////
727
728void SkGpuDevice::drawRRect(const SkDraw& draw, const SkRRect& rect,
729 const SkPaint& paint) {
730 CHECK_FOR_ANNOTATION(paint);
731 CHECK_SHOULD_DRAW(draw, false);
732
commit-bot@chromium.org82139702014-03-10 22:53:20 +0000733 GrPaint grPaint;
734 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
735 return;
736 }
737
738 SkStrokeRec stroke(paint);
739 if (paint.getMaskFilter()) {
740 // try to hit the fast path for drawing filtered round rects
741
742 SkRRect devRRect;
743 if (rect.transform(fContext->getMatrix(), &devRRect)) {
744 if (devRRect.allCornersCircular()) {
745 SkRect maskRect;
746 if (paint.getMaskFilter()->canFilterMaskGPU(devRRect.rect(),
747 draw.fClip->getBounds(),
748 fContext->getMatrix(),
749 &maskRect)) {
750 SkIRect finalIRect;
751 maskRect.roundOut(&finalIRect);
752 if (draw.fClip->quickReject(finalIRect)) {
753 // clipped out
754 return;
755 }
756 if (NULL != draw.fBounder && !draw.fBounder->doIRect(finalIRect)) {
757 // nothing to draw
758 return;
759 }
760 if (paint.getMaskFilter()->directFilterRRectMaskGPU(fContext, &grPaint,
761 stroke, devRRect)) {
762 return;
763 }
764 }
765
766 }
767 }
768
769 }
770
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000771 bool usePath = !rect.isSimple();
772 // another two reasons we might need to call drawPath...
773 if (paint.getMaskFilter() || paint.getPathEffect()) {
774 usePath = true;
775 }
776 // until we can rotate rrects...
777 if (!usePath && !fContext->getMatrix().rectStaysRect()) {
778 usePath = true;
779 }
780
781 if (usePath) {
782 SkPath path;
783 path.addRRect(rect);
784 this->drawPath(draw, path, paint, NULL, true);
785 return;
786 }
787
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000788 fContext->drawRRect(grPaint, rect, stroke);
789}
790
commit-bot@chromium.org82139702014-03-10 22:53:20 +0000791/////////////////////////////////////////////////////////////////////////////
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000792
793void SkGpuDevice::drawOval(const SkDraw& draw, const SkRect& oval,
794 const SkPaint& paint) {
795 CHECK_FOR_ANNOTATION(paint);
796 CHECK_SHOULD_DRAW(draw, false);
797
798 bool usePath = false;
799 // some basic reasons we might need to call drawPath...
800 if (paint.getMaskFilter() || paint.getPathEffect()) {
801 usePath = true;
802 }
803
804 if (usePath) {
805 SkPath path;
806 path.addOval(oval);
807 this->drawPath(draw, path, paint, NULL, true);
808 return;
809 }
810
811 GrPaint grPaint;
812 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
813 return;
814 }
815 SkStrokeRec stroke(paint);
816
817 fContext->drawOval(grPaint, oval, stroke);
818}
819
820#include "SkMaskFilter.h"
821#include "SkBounder.h"
822
823///////////////////////////////////////////////////////////////////////////////
824
825// helpers for applying mask filters
826namespace {
827
828// Draw a mask using the supplied paint. Since the coverage/geometry
829// is already burnt into the mask this boils down to a rect draw.
830// Return true if the mask was successfully drawn.
831bool draw_mask(GrContext* context, const SkRect& maskRect,
832 GrPaint* grp, GrTexture* mask) {
833 GrContext::AutoMatrix am;
834 if (!am.setIdentity(context, grp)) {
835 return false;
836 }
837
838 SkMatrix matrix;
839 matrix.setTranslate(-maskRect.fLeft, -maskRect.fTop);
840 matrix.postIDiv(mask->width(), mask->height());
841
842 grp->addCoverageEffect(GrSimpleTextureEffect::Create(mask, matrix))->unref();
843 context->drawRect(*grp, maskRect);
844 return true;
845}
846
847bool draw_with_mask_filter(GrContext* context, const SkPath& devPath,
848 SkMaskFilter* filter, const SkRegion& clip, SkBounder* bounder,
849 GrPaint* grp, SkPaint::Style style) {
850 SkMask srcM, dstM;
851
852 if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), filter, &context->getMatrix(), &srcM,
853 SkMask::kComputeBoundsAndRenderImage_CreateMode, style)) {
854 return false;
855 }
856 SkAutoMaskFreeImage autoSrc(srcM.fImage);
857
858 if (!filter->filterMask(&dstM, srcM, context->getMatrix(), NULL)) {
859 return false;
860 }
861 // this will free-up dstM when we're done (allocated in filterMask())
862 SkAutoMaskFreeImage autoDst(dstM.fImage);
863
864 if (clip.quickReject(dstM.fBounds)) {
865 return false;
866 }
867 if (bounder && !bounder->doIRect(dstM.fBounds)) {
868 return false;
869 }
870
871 // we now have a device-aligned 8bit mask in dstM, ready to be drawn using
872 // the current clip (and identity matrix) and GrPaint settings
873 GrTextureDesc desc;
874 desc.fWidth = dstM.fBounds.width();
875 desc.fHeight = dstM.fBounds.height();
876 desc.fConfig = kAlpha_8_GrPixelConfig;
877
878 GrAutoScratchTexture ast(context, desc);
879 GrTexture* texture = ast.texture();
880
881 if (NULL == texture) {
882 return false;
883 }
884 texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
885 dstM.fImage, dstM.fRowBytes);
886
887 SkRect maskRect = SkRect::Make(dstM.fBounds);
888
889 return draw_mask(context, maskRect, grp, texture);
890}
891
892// Create a mask of 'devPath' and place the result in 'mask'. Return true on
893// success; false otherwise.
894bool create_mask_GPU(GrContext* context,
895 const SkRect& maskRect,
896 const SkPath& devPath,
897 const SkStrokeRec& stroke,
898 bool doAA,
899 GrAutoScratchTexture* mask) {
900 GrTextureDesc desc;
901 desc.fFlags = kRenderTarget_GrTextureFlagBit;
902 desc.fWidth = SkScalarCeilToInt(maskRect.width());
903 desc.fHeight = SkScalarCeilToInt(maskRect.height());
904 // We actually only need A8, but it often isn't supported as a
905 // render target so default to RGBA_8888
906 desc.fConfig = kRGBA_8888_GrPixelConfig;
907 if (context->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {
908 desc.fConfig = kAlpha_8_GrPixelConfig;
909 }
910
911 mask->set(context, desc);
912 if (NULL == mask->texture()) {
913 return false;
914 }
915
916 GrTexture* maskTexture = mask->texture();
917 SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height());
918
919 GrContext::AutoRenderTarget art(context, maskTexture->asRenderTarget());
920 GrContext::AutoClip ac(context, clipRect);
921
922 context->clear(NULL, 0x0, true);
923
924 GrPaint tempPaint;
925 if (doAA) {
926 tempPaint.setAntiAlias(true);
927 // AA uses the "coverage" stages on GrDrawTarget. Coverage with a dst
928 // blend coeff of zero requires dual source blending support in order
929 // to properly blend partially covered pixels. This means the AA
930 // code path may not be taken. So we use a dst blend coeff of ISA. We
931 // could special case AA draws to a dst surface with known alpha=0 to
932 // use a zero dst coeff when dual source blending isn't available.
933 tempPaint.setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff);
934 }
935
936 GrContext::AutoMatrix am;
937
938 // Draw the mask into maskTexture with the path's top-left at the origin using tempPaint.
939 SkMatrix translate;
940 translate.setTranslate(-maskRect.fLeft, -maskRect.fTop);
941 am.set(context, translate);
942 context->drawPath(tempPaint, devPath, stroke);
943 return true;
944}
945
946SkBitmap wrap_texture(GrTexture* texture) {
reed@google.combf790232013-12-13 19:45:58 +0000947 SkImageInfo info;
948 texture->asImageInfo(&info);
949
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000950 SkBitmap result;
reed@google.combf790232013-12-13 19:45:58 +0000951 result.setConfig(info);
952 result.setPixelRef(SkNEW_ARGS(SkGrPixelRef, (info, texture)))->unref();
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000953 return result;
954}
955
956};
957
958void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
959 const SkPaint& paint, const SkMatrix* prePathMatrix,
960 bool pathIsMutable) {
961 CHECK_FOR_ANNOTATION(paint);
962 CHECK_SHOULD_DRAW(draw, false);
963
964 GrPaint grPaint;
965 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
966 return;
967 }
968
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000969 // If we have a prematrix, apply it to the path, optimizing for the case
970 // where the original path can in fact be modified in place (even though
971 // its parameter type is const).
972 SkPath* pathPtr = const_cast<SkPath*>(&origSrcPath);
commit-bot@chromium.orgf0c41e22014-01-14 18:42:34 +0000973 SkTLazy<SkPath> tmpPath;
974 SkTLazy<SkPath> effectPath;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000975
976 if (prePathMatrix) {
977 SkPath* result = pathPtr;
978
979 if (!pathIsMutable) {
commit-bot@chromium.orgf0c41e22014-01-14 18:42:34 +0000980 result = tmpPath.init();
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000981 pathIsMutable = true;
982 }
983 // should I push prePathMatrix on our MV stack temporarily, instead
984 // of applying it here? See SkDraw.cpp
985 pathPtr->transform(*prePathMatrix, result);
986 pathPtr = result;
987 }
988 // at this point we're done with prePathMatrix
989 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
990
991 SkStrokeRec stroke(paint);
992 SkPathEffect* pathEffect = paint.getPathEffect();
993 const SkRect* cullRect = NULL; // TODO: what is our bounds?
commit-bot@chromium.orgf0c41e22014-01-14 18:42:34 +0000994 if (pathEffect && pathEffect->filterPath(effectPath.init(), *pathPtr, &stroke,
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000995 cullRect)) {
commit-bot@chromium.orgf0c41e22014-01-14 18:42:34 +0000996 pathPtr = effectPath.get();
997 pathIsMutable = true;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000998 }
999
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001000 if (paint.getMaskFilter()) {
1001 if (!stroke.isHairlineStyle()) {
commit-bot@chromium.orgf0c41e22014-01-14 18:42:34 +00001002 SkPath* strokedPath = pathIsMutable ? pathPtr : tmpPath.init();
1003 if (stroke.applyToPath(strokedPath, *pathPtr)) {
1004 pathPtr = strokedPath;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001005 pathIsMutable = true;
1006 stroke.setFillStyle();
1007 }
1008 }
1009
1010 // avoid possibly allocating a new path in transform if we can
commit-bot@chromium.orgf0c41e22014-01-14 18:42:34 +00001011 SkPath* devPathPtr = pathIsMutable ? pathPtr : tmpPath.init();
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001012
1013 // transform the path into device space
1014 pathPtr->transform(fContext->getMatrix(), devPathPtr);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +00001015
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001016 SkRect maskRect;
1017 if (paint.getMaskFilter()->canFilterMaskGPU(devPathPtr->getBounds(),
1018 draw.fClip->getBounds(),
1019 fContext->getMatrix(),
1020 &maskRect)) {
commit-bot@chromium.org439ff1b2014-01-13 16:39:39 +00001021 // The context's matrix may change while creating the mask, so save the CTM here to
1022 // pass to filterMaskGPU.
1023 const SkMatrix ctm = fContext->getMatrix();
1024
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001025 SkIRect finalIRect;
1026 maskRect.roundOut(&finalIRect);
1027 if (draw.fClip->quickReject(finalIRect)) {
1028 // clipped out
1029 return;
1030 }
1031 if (NULL != draw.fBounder && !draw.fBounder->doIRect(finalIRect)) {
1032 // nothing to draw
1033 return;
1034 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +00001035
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +00001036 if (paint.getMaskFilter()->directFilterMaskGPU(fContext, &grPaint,
commit-bot@chromium.org82139702014-03-10 22:53:20 +00001037 stroke, *devPathPtr)) {
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +00001038 // the mask filter was able to draw itself directly, so there's nothing
1039 // left to do.
1040 return;
1041 }
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001042
1043 GrAutoScratchTexture mask;
1044
1045 if (create_mask_GPU(fContext, maskRect, *devPathPtr, stroke,
1046 grPaint.isAntiAlias(), &mask)) {
1047 GrTexture* filtered;
1048
commit-bot@chromium.org41bf9302014-01-08 22:25:53 +00001049 if (paint.getMaskFilter()->filterMaskGPU(mask.texture(),
commit-bot@chromium.org439ff1b2014-01-13 16:39:39 +00001050 ctm, maskRect, &filtered, true)) {
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001051 // filterMaskGPU gives us ownership of a ref to the result
1052 SkAutoTUnref<GrTexture> atu(filtered);
1053
1054 // If the scratch texture that we used as the filter src also holds the filter
1055 // result then we must detach so that this texture isn't recycled for a later
1056 // draw.
1057 if (filtered == mask.texture()) {
1058 mask.detach();
1059 filtered->unref(); // detach transfers GrAutoScratchTexture's ref to us.
1060 }
1061
1062 if (draw_mask(fContext, maskRect, &grPaint, filtered)) {
1063 // This path is completely drawn
1064 return;
1065 }
1066 }
1067 }
1068 }
1069
1070 // draw the mask on the CPU - this is a fallthrough path in case the
1071 // GPU path fails
1072 SkPaint::Style style = stroke.isHairlineStyle() ? SkPaint::kStroke_Style :
1073 SkPaint::kFill_Style;
1074 draw_with_mask_filter(fContext, *devPathPtr, paint.getMaskFilter(),
1075 *draw.fClip, draw.fBounder, &grPaint, style);
1076 return;
1077 }
1078
1079 fContext->drawPath(grPaint, *pathPtr, stroke);
1080}
1081
1082static const int kBmpSmallTileSize = 1 << 10;
1083
1084static inline int get_tile_count(const SkIRect& srcRect, int tileSize) {
1085 int tilesX = (srcRect.fRight / tileSize) - (srcRect.fLeft / tileSize) + 1;
1086 int tilesY = (srcRect.fBottom / tileSize) - (srcRect.fTop / tileSize) + 1;
1087 return tilesX * tilesY;
1088}
1089
1090static int determine_tile_size(const SkBitmap& bitmap, const SkIRect& src, int maxTileSize) {
1091 if (maxTileSize <= kBmpSmallTileSize) {
1092 return maxTileSize;
1093 }
1094
1095 size_t maxTileTotalTileSize = get_tile_count(src, maxTileSize);
1096 size_t smallTotalTileSize = get_tile_count(src, kBmpSmallTileSize);
1097
1098 maxTileTotalTileSize *= maxTileSize * maxTileSize;
1099 smallTotalTileSize *= kBmpSmallTileSize * kBmpSmallTileSize;
1100
1101 if (maxTileTotalTileSize > 2 * smallTotalTileSize) {
1102 return kBmpSmallTileSize;
1103 } else {
1104 return maxTileSize;
1105 }
1106}
1107
1108// Given a bitmap, an optional src rect, and a context with a clip and matrix determine what
1109// pixels from the bitmap are necessary.
1110static void determine_clipped_src_rect(const GrContext* context,
1111 const SkBitmap& bitmap,
1112 const SkRect* srcRectPtr,
1113 SkIRect* clippedSrcIRect) {
1114 const GrClipData* clip = context->getClip();
1115 clip->getConservativeBounds(context->getRenderTarget(), clippedSrcIRect, NULL);
1116 SkMatrix inv;
1117 if (!context->getMatrix().invert(&inv)) {
1118 clippedSrcIRect->setEmpty();
1119 return;
1120 }
1121 SkRect clippedSrcRect = SkRect::Make(*clippedSrcIRect);
1122 inv.mapRect(&clippedSrcRect);
1123 if (NULL != srcRectPtr) {
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001124 // we've setup src space 0,0 to map to the top left of the src rect.
1125 clippedSrcRect.offset(srcRectPtr->fLeft, srcRectPtr->fTop);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001126 if (!clippedSrcRect.intersect(*srcRectPtr)) {
1127 clippedSrcIRect->setEmpty();
1128 return;
1129 }
1130 }
1131 clippedSrcRect.roundOut(clippedSrcIRect);
1132 SkIRect bmpBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
1133 if (!clippedSrcIRect->intersect(bmpBounds)) {
1134 clippedSrcIRect->setEmpty();
1135 }
1136}
1137
1138bool SkGpuDevice::shouldTileBitmap(const SkBitmap& bitmap,
1139 const GrTextureParams& params,
1140 const SkRect* srcRectPtr,
1141 int maxTileSize,
1142 int* tileSize,
1143 SkIRect* clippedSrcRect) const {
1144 // if bitmap is explictly texture backed then just use the texture
1145 if (NULL != bitmap.getTexture()) {
1146 return false;
1147 }
1148
1149 // if it's larger than the max tile size, then we have no choice but tiling.
1150 if (bitmap.width() > maxTileSize || bitmap.height() > maxTileSize) {
1151 determine_clipped_src_rect(fContext, bitmap, srcRectPtr, clippedSrcRect);
1152 *tileSize = determine_tile_size(bitmap, *clippedSrcRect, maxTileSize);
1153 return true;
1154 }
1155
1156 if (bitmap.width() * bitmap.height() < 4 * kBmpSmallTileSize * kBmpSmallTileSize) {
1157 return false;
1158 }
1159
1160 // if the entire texture is already in our cache then no reason to tile it
1161 if (GrIsBitmapInCache(fContext, bitmap, &params)) {
1162 return false;
1163 }
1164
1165 // At this point we know we could do the draw by uploading the entire bitmap
1166 // as a texture. However, if the texture would be large compared to the
1167 // cache size and we don't require most of it for this draw then tile to
1168 // reduce the amount of upload and cache spill.
1169
1170 // assumption here is that sw bitmap size is a good proxy for its size as
1171 // a texture
1172 size_t bmpSize = bitmap.getSize();
1173 size_t cacheSize;
1174 fContext->getTextureCacheLimits(NULL, &cacheSize);
1175 if (bmpSize < cacheSize / 2) {
1176 return false;
1177 }
1178
1179 // Figure out how much of the src we will need based on the src rect and clipping.
1180 determine_clipped_src_rect(fContext, bitmap, srcRectPtr, clippedSrcRect);
1181 *tileSize = kBmpSmallTileSize; // already know whole bitmap fits in one max sized tile.
1182 size_t usedTileBytes = get_tile_count(*clippedSrcRect, kBmpSmallTileSize) *
1183 kBmpSmallTileSize * kBmpSmallTileSize;
1184
1185 return usedTileBytes < 2 * bmpSize;
1186}
1187
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001188void SkGpuDevice::drawBitmap(const SkDraw& origDraw,
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001189 const SkBitmap& bitmap,
1190 const SkMatrix& m,
1191 const SkPaint& paint) {
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001192 SkMatrix concat;
1193 SkTCopyOnFirstWrite<SkDraw> draw(origDraw);
1194 if (!m.isIdentity()) {
1195 concat.setConcat(*draw->fMatrix, m);
1196 draw.writable()->fMatrix = &concat;
1197 }
1198 this->drawBitmapCommon(*draw, bitmap, NULL, NULL, paint, SkCanvas::kNone_DrawBitmapRectFlag);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001199}
1200
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001201// This method outsets 'iRect' by 'outset' all around and then clamps its extents to
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001202// 'clamp'. 'offset' is adjusted to remain positioned over the top-left corner
1203// of 'iRect' for all possible outsets/clamps.
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001204static inline void clamped_outset_with_offset(SkIRect* iRect,
1205 int outset,
1206 SkPoint* offset,
1207 const SkIRect& clamp) {
1208 iRect->outset(outset, outset);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001209
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001210 int leftClampDelta = clamp.fLeft - iRect->fLeft;
1211 if (leftClampDelta > 0) {
1212 offset->fX -= outset - leftClampDelta;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001213 iRect->fLeft = clamp.fLeft;
1214 } else {
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001215 offset->fX -= outset;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001216 }
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001217
1218 int topClampDelta = clamp.fTop - iRect->fTop;
1219 if (topClampDelta > 0) {
1220 offset->fY -= outset - topClampDelta;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001221 iRect->fTop = clamp.fTop;
1222 } else {
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001223 offset->fY -= outset;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001224 }
1225
1226 if (iRect->fRight > clamp.fRight) {
1227 iRect->fRight = clamp.fRight;
1228 }
1229 if (iRect->fBottom > clamp.fBottom) {
1230 iRect->fBottom = clamp.fBottom;
1231 }
1232}
1233
1234void SkGpuDevice::drawBitmapCommon(const SkDraw& draw,
1235 const SkBitmap& bitmap,
1236 const SkRect* srcRectPtr,
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001237 const SkSize* dstSizePtr,
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001238 const SkPaint& paint,
1239 SkCanvas::DrawBitmapRectFlags flags) {
1240 CHECK_SHOULD_DRAW(draw, false);
1241
1242 SkRect srcRect;
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001243 SkSize dstSize;
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001244 // If there is no src rect, or the src rect contains the entire bitmap then we're effectively
1245 // in the (easier) bleed case, so update flags.
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001246 if (NULL == srcRectPtr) {
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001247 SkScalar w = SkIntToScalar(bitmap.width());
1248 SkScalar h = SkIntToScalar(bitmap.height());
1249 dstSize.fWidth = w;
1250 dstSize.fHeight = h;
1251 srcRect.set(0, 0, w, h);
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001252 flags = (SkCanvas::DrawBitmapRectFlags) (flags | SkCanvas::kBleed_DrawBitmapRectFlag);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001253 } else {
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001254 SkASSERT(NULL != dstSizePtr);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001255 srcRect = *srcRectPtr;
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001256 dstSize = *dstSizePtr;
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001257 if (srcRect.fLeft <= 0 && srcRect.fTop <= 0 &&
1258 srcRect.fRight >= bitmap.width() && srcRect.fBottom >= bitmap.height()) {
1259 flags = (SkCanvas::DrawBitmapRectFlags) (flags | SkCanvas::kBleed_DrawBitmapRectFlag);
1260 }
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001261 }
1262
1263 if (paint.getMaskFilter()){
1264 // Convert the bitmap to a shader so that the rect can be drawn
1265 // through drawRect, which supports mask filters.
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001266 SkBitmap tmp; // subset of bitmap, if necessary
1267 const SkBitmap* bitmapPtr = &bitmap;
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001268 SkMatrix localM;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001269 if (NULL != srcRectPtr) {
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001270 localM.setTranslate(-srcRectPtr->fLeft, -srcRectPtr->fTop);
1271 localM.postScale(dstSize.fWidth / srcRectPtr->width(),
1272 dstSize.fHeight / srcRectPtr->height());
commit-bot@chromium.orgd6ca4ac2013-11-22 20:34:59 +00001273 // In bleed mode we position and trim the bitmap based on the src rect which is
1274 // already accounted for in 'm' and 'srcRect'. In clamp mode we need to chop out
1275 // the desired portion of the bitmap and then update 'm' and 'srcRect' to
1276 // compensate.
1277 if (!(SkCanvas::kBleed_DrawBitmapRectFlag & flags)) {
1278 SkIRect iSrc;
1279 srcRect.roundOut(&iSrc);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001280
commit-bot@chromium.orgd6ca4ac2013-11-22 20:34:59 +00001281 SkPoint offset = SkPoint::Make(SkIntToScalar(iSrc.fLeft),
1282 SkIntToScalar(iSrc.fTop));
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001283
commit-bot@chromium.orgd6ca4ac2013-11-22 20:34:59 +00001284 if (!bitmap.extractSubset(&tmp, iSrc)) {
1285 return; // extraction failed
1286 }
1287 bitmapPtr = &tmp;
1288 srcRect.offset(-offset.fX, -offset.fY);
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001289
commit-bot@chromium.orgd6ca4ac2013-11-22 20:34:59 +00001290 // The source rect has changed so update the matrix
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001291 localM.preTranslate(offset.fX, offset.fY);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001292 }
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001293 } else {
1294 localM.reset();
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001295 }
1296
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001297 SkPaint paintWithShader(paint);
1298 paintWithShader.setShader(SkShader::CreateBitmapShader(*bitmapPtr,
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001299 SkShader::kClamp_TileMode, SkShader::kClamp_TileMode))->unref();
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001300 paintWithShader.getShader()->setLocalMatrix(localM);
1301 SkRect dstRect = {0, 0, dstSize.fWidth, dstSize.fHeight};
1302 this->drawRect(draw, dstRect, paintWithShader);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001303
1304 return;
1305 }
1306
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001307 // If there is no mask filter than it is OK to handle the src rect -> dst rect scaling using
1308 // the view matrix rather than a local matrix.
1309 SkMatrix m;
1310 m.setScale(dstSize.fWidth / srcRect.width(),
1311 dstSize.fHeight / srcRect.height());
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001312 fContext->concatMatrix(m);
1313
1314 GrTextureParams params;
1315 SkPaint::FilterLevel paintFilterLevel = paint.getFilterLevel();
1316 GrTextureParams::FilterMode textureFilterMode;
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001317
1318 int tileFilterPad;
1319 bool doBicubic = false;
1320
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001321 switch(paintFilterLevel) {
1322 case SkPaint::kNone_FilterLevel:
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001323 tileFilterPad = 0;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001324 textureFilterMode = GrTextureParams::kNone_FilterMode;
1325 break;
1326 case SkPaint::kLow_FilterLevel:
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001327 tileFilterPad = 1;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001328 textureFilterMode = GrTextureParams::kBilerp_FilterMode;
1329 break;
1330 case SkPaint::kMedium_FilterLevel:
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001331 tileFilterPad = 1;
commit-bot@chromium.org79b7eee2013-12-16 21:02:29 +00001332 if (fContext->getMatrix().getMinStretch() < SK_Scalar1) {
1333 textureFilterMode = GrTextureParams::kMipMap_FilterMode;
1334 } else {
1335 // Don't trigger MIP level generation unnecessarily.
1336 textureFilterMode = GrTextureParams::kBilerp_FilterMode;
1337 }
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001338 break;
commit-bot@chromium.org79b7eee2013-12-16 21:02:29 +00001339 case SkPaint::kHigh_FilterLevel:
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001340 // Minification can look bad with the bicubic effect.
commit-bot@chromium.org7d7f3142013-12-16 15:18:11 +00001341 if (fContext->getMatrix().getMinStretch() >= SK_Scalar1) {
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001342 // We will install an effect that does the filtering in the shader.
1343 textureFilterMode = GrTextureParams::kNone_FilterMode;
1344 tileFilterPad = GrBicubicEffect::kFilterTexelPad;
1345 doBicubic = true;
1346 } else {
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001347 textureFilterMode = GrTextureParams::kMipMap_FilterMode;
1348 tileFilterPad = 1;
1349 }
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001350 break;
1351 default:
1352 SkErrorInternals::SetError( kInvalidPaint_SkError,
1353 "Sorry, I don't understand the filtering "
1354 "mode you asked for. Falling back to "
1355 "MIPMaps.");
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001356 tileFilterPad = 1;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001357 textureFilterMode = GrTextureParams::kMipMap_FilterMode;
1358 break;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001359 }
1360
1361 params.setFilterMode(textureFilterMode);
1362
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001363 int maxTileSize = fContext->getMaxTextureSize() - 2 * tileFilterPad;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001364 int tileSize;
1365
1366 SkIRect clippedSrcRect;
1367 if (this->shouldTileBitmap(bitmap, params, srcRectPtr, maxTileSize, &tileSize,
1368 &clippedSrcRect)) {
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001369 this->drawTiledBitmap(bitmap, srcRect, clippedSrcRect, params, paint, flags, tileSize,
1370 doBicubic);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001371 } else {
1372 // take the simple case
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001373 this->internalDrawBitmap(bitmap, srcRect, params, paint, flags, doBicubic);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001374 }
1375}
1376
1377// Break 'bitmap' into several tiles to draw it since it has already
1378// been determined to be too large to fit in VRAM
1379void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap,
1380 const SkRect& srcRect,
1381 const SkIRect& clippedSrcIRect,
1382 const GrTextureParams& params,
1383 const SkPaint& paint,
1384 SkCanvas::DrawBitmapRectFlags flags,
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001385 int tileSize,
1386 bool bicubic) {
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001387 SkRect clippedSrcRect = SkRect::Make(clippedSrcIRect);
1388
1389 int nx = bitmap.width() / tileSize;
1390 int ny = bitmap.height() / tileSize;
1391 for (int x = 0; x <= nx; x++) {
1392 for (int y = 0; y <= ny; y++) {
1393 SkRect tileR;
1394 tileR.set(SkIntToScalar(x * tileSize),
1395 SkIntToScalar(y * tileSize),
1396 SkIntToScalar((x + 1) * tileSize),
1397 SkIntToScalar((y + 1) * tileSize));
1398
1399 if (!SkRect::Intersects(tileR, clippedSrcRect)) {
1400 continue;
1401 }
1402
1403 if (!tileR.intersect(srcRect)) {
1404 continue;
1405 }
1406
1407 SkBitmap tmpB;
1408 SkIRect iTileR;
1409 tileR.roundOut(&iTileR);
1410 SkPoint offset = SkPoint::Make(SkIntToScalar(iTileR.fLeft),
1411 SkIntToScalar(iTileR.fTop));
1412
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001413 // Adjust the context matrix to draw at the right x,y in device space
1414 SkMatrix tmpM;
1415 GrContext::AutoMatrix am;
1416 tmpM.setTranslate(offset.fX - srcRect.fLeft, offset.fY - srcRect.fTop);
1417 am.setPreConcat(fContext, tmpM);
1418
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001419 if (SkPaint::kNone_FilterLevel != paint.getFilterLevel() || bicubic) {
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001420 SkIRect iClampRect;
1421
1422 if (SkCanvas::kBleed_DrawBitmapRectFlag & flags) {
1423 // In bleed mode we want to always expand the tile on all edges
1424 // but stay within the bitmap bounds
1425 iClampRect = SkIRect::MakeWH(bitmap.width(), bitmap.height());
1426 } else {
1427 // In texture-domain/clamp mode we only want to expand the
1428 // tile on edges interior to "srcRect" (i.e., we want to
1429 // not bleed across the original clamped edges)
1430 srcRect.roundOut(&iClampRect);
1431 }
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001432 int outset = bicubic ? GrBicubicEffect::kFilterTexelPad : 1;
1433 clamped_outset_with_offset(&iTileR, outset, &offset, iClampRect);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001434 }
1435
1436 if (bitmap.extractSubset(&tmpB, iTileR)) {
1437 // now offset it to make it "local" to our tmp bitmap
1438 tileR.offset(-offset.fX, -offset.fY);
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001439
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001440 this->internalDrawBitmap(tmpB, tileR, params, paint, flags, bicubic);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001441 }
1442 }
1443 }
1444}
1445
1446static bool has_aligned_samples(const SkRect& srcRect,
1447 const SkRect& transformedRect) {
1448 // detect pixel disalignment
1449 if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) -
1450 transformedRect.left()) < COLOR_BLEED_TOLERANCE &&
1451 SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) -
1452 transformedRect.top()) < COLOR_BLEED_TOLERANCE &&
1453 SkScalarAbs(transformedRect.width() - srcRect.width()) <
1454 COLOR_BLEED_TOLERANCE &&
1455 SkScalarAbs(transformedRect.height() - srcRect.height()) <
1456 COLOR_BLEED_TOLERANCE) {
1457 return true;
1458 }
1459 return false;
1460}
1461
1462static bool may_color_bleed(const SkRect& srcRect,
1463 const SkRect& transformedRect,
1464 const SkMatrix& m) {
1465 // Only gets called if has_aligned_samples returned false.
1466 // So we can assume that sampling is axis aligned but not texel aligned.
1467 SkASSERT(!has_aligned_samples(srcRect, transformedRect));
1468 SkRect innerSrcRect(srcRect), innerTransformedRect,
1469 outerTransformedRect(transformedRect);
1470 innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf);
1471 m.mapRect(&innerTransformedRect, innerSrcRect);
1472
1473 // The gap between outerTransformedRect and innerTransformedRect
1474 // represents the projection of the source border area, which is
1475 // problematic for color bleeding. We must check whether any
1476 // destination pixels sample the border area.
1477 outerTransformedRect.inset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE);
1478 innerTransformedRect.outset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE);
1479 SkIRect outer, inner;
1480 outerTransformedRect.round(&outer);
1481 innerTransformedRect.round(&inner);
1482 // If the inner and outer rects round to the same result, it means the
1483 // border does not overlap any pixel centers. Yay!
1484 return inner != outer;
1485}
1486
1487
1488/*
1489 * This is called by drawBitmap(), which has to handle images that may be too
1490 * large to be represented by a single texture.
1491 *
1492 * internalDrawBitmap assumes that the specified bitmap will fit in a texture
1493 * and that non-texture portion of the GrPaint has already been setup.
1494 */
1495void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap,
1496 const SkRect& srcRect,
1497 const GrTextureParams& params,
1498 const SkPaint& paint,
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001499 SkCanvas::DrawBitmapRectFlags flags,
1500 bool bicubic) {
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001501 SkASSERT(bitmap.width() <= fContext->getMaxTextureSize() &&
1502 bitmap.height() <= fContext->getMaxTextureSize());
1503
1504 GrTexture* texture;
1505 SkAutoCachedTexture act(this, bitmap, &params, &texture);
1506 if (NULL == texture) {
1507 return;
1508 }
1509
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001510 SkRect dstRect = {0, 0, srcRect.width(), srcRect.height() };
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001511 SkRect paintRect;
1512 SkScalar wInv = SkScalarInvert(SkIntToScalar(texture->width()));
1513 SkScalar hInv = SkScalarInvert(SkIntToScalar(texture->height()));
1514 paintRect.setLTRB(SkScalarMul(srcRect.fLeft, wInv),
1515 SkScalarMul(srcRect.fTop, hInv),
1516 SkScalarMul(srcRect.fRight, wInv),
1517 SkScalarMul(srcRect.fBottom, hInv));
1518
1519 bool needsTextureDomain = false;
1520 if (!(flags & SkCanvas::kBleed_DrawBitmapRectFlag) &&
commit-bot@chromium.org7d7f3142013-12-16 15:18:11 +00001521 (bicubic || params.filterMode() != GrTextureParams::kNone_FilterMode)) {
1522 // Need texture domain if drawing a sub rect
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001523 needsTextureDomain = srcRect.width() < bitmap.width() ||
1524 srcRect.height() < bitmap.height();
commit-bot@chromium.org7d7f3142013-12-16 15:18:11 +00001525 if (!bicubic && needsTextureDomain && fContext->getMatrix().rectStaysRect()) {
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001526 const SkMatrix& matrix = fContext->getMatrix();
1527 // sampling is axis-aligned
1528 SkRect transformedRect;
1529 matrix.mapRect(&transformedRect, srcRect);
1530
1531 if (has_aligned_samples(srcRect, transformedRect)) {
1532 // We could also turn off filtering here (but we already did a cache lookup with
1533 // params).
1534 needsTextureDomain = false;
1535 } else {
1536 needsTextureDomain = may_color_bleed(srcRect, transformedRect, matrix);
1537 }
1538 }
1539 }
1540
1541 SkRect textureDomain = SkRect::MakeEmpty();
1542 SkAutoTUnref<GrEffectRef> effect;
1543 if (needsTextureDomain) {
1544 // Use a constrained texture domain to avoid color bleeding
1545 SkScalar left, top, right, bottom;
1546 if (srcRect.width() > SK_Scalar1) {
1547 SkScalar border = SK_ScalarHalf / texture->width();
1548 left = paintRect.left() + border;
1549 right = paintRect.right() - border;
1550 } else {
1551 left = right = SkScalarHalf(paintRect.left() + paintRect.right());
1552 }
1553 if (srcRect.height() > SK_Scalar1) {
1554 SkScalar border = SK_ScalarHalf / texture->height();
1555 top = paintRect.top() + border;
1556 bottom = paintRect.bottom() - border;
1557 } else {
1558 top = bottom = SkScalarHalf(paintRect.top() + paintRect.bottom());
1559 }
1560 textureDomain.setLTRB(left, top, right, bottom);
commit-bot@chromium.org7d7f3142013-12-16 15:18:11 +00001561 if (bicubic) {
1562 effect.reset(GrBicubicEffect::Create(texture, SkMatrix::I(), textureDomain));
1563 } else {
1564 effect.reset(GrTextureDomainEffect::Create(texture,
1565 SkMatrix::I(),
1566 textureDomain,
1567 GrTextureDomain::kClamp_Mode,
1568 params.filterMode()));
1569 }
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001570 } else if (bicubic) {
commit-bot@chromium.orgbc91fd72013-12-10 12:53:39 +00001571 SkASSERT(GrTextureParams::kNone_FilterMode == params.filterMode());
1572 SkShader::TileMode tileModes[2] = { params.getTileModeX(), params.getTileModeY() };
1573 effect.reset(GrBicubicEffect::Create(texture, SkMatrix::I(), tileModes));
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001574 } else {
1575 effect.reset(GrSimpleTextureEffect::Create(texture, SkMatrix::I(), params));
1576 }
1577
1578 // Construct a GrPaint by setting the bitmap texture as the first effect and then configuring
1579 // the rest from the SkPaint.
1580 GrPaint grPaint;
1581 grPaint.addColorEffect(effect);
1582 bool alphaOnly = !(SkBitmap::kA8_Config == bitmap.config());
1583 if (!skPaint2GrPaintNoShader(this, paint, alphaOnly, false, &grPaint)) {
1584 return;
1585 }
1586
1587 fContext->drawRectToRect(grPaint, dstRect, paintRect, NULL);
1588}
1589
1590static bool filter_texture(SkBaseDevice* device, GrContext* context,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +00001591 GrTexture* texture, const SkImageFilter* filter,
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001592 int w, int h, const SkImageFilter::Context& ctx,
1593 SkBitmap* result, SkIPoint* offset) {
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001594 SkASSERT(filter);
1595 SkDeviceImageFilterProxy proxy(device);
1596
1597 if (filter->canFilterImageGPU()) {
1598 // Save the render target and set it to NULL, so we don't accidentally draw to it in the
1599 // filter. Also set the clip wide open and the matrix to identity.
1600 GrContext::AutoWideOpenIdentityDraw awo(context, NULL);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001601 return filter->filterImageGPU(&proxy, wrap_texture(texture), ctx, result, offset);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001602 } else {
1603 return false;
1604 }
1605}
1606
1607void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
1608 int left, int top, const SkPaint& paint) {
1609 // drawSprite is defined to be in device coords.
1610 CHECK_SHOULD_DRAW(draw, true);
1611
1612 SkAutoLockPixels alp(bitmap, !bitmap.getTexture());
1613 if (!bitmap.getTexture() && !bitmap.readyToDraw()) {
1614 return;
1615 }
1616
1617 int w = bitmap.width();
1618 int h = bitmap.height();
1619
1620 GrTexture* texture;
1621 // draw sprite uses the default texture params
1622 SkAutoCachedTexture act(this, bitmap, NULL, &texture);
1623
1624 SkImageFilter* filter = paint.getImageFilter();
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001625 // This bitmap will own the filtered result as a texture.
1626 SkBitmap filteredBitmap;
1627
1628 if (NULL != filter) {
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001629 SkIPoint offset = SkIPoint::Make(0, 0);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001630 SkMatrix matrix(*draw.fMatrix);
1631 matrix.postTranslate(SkIntToScalar(-left), SkIntToScalar(-top));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001632 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
1633 SkImageFilter::Context ctx(matrix, clipBounds);
1634 if (filter_texture(this, fContext, texture, filter, w, h, ctx, &filteredBitmap,
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001635 &offset)) {
1636 texture = (GrTexture*) filteredBitmap.getTexture();
1637 w = filteredBitmap.width();
1638 h = filteredBitmap.height();
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001639 left += offset.x();
1640 top += offset.y();
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001641 } else {
1642 return;
1643 }
1644 }
1645
1646 GrPaint grPaint;
1647 grPaint.addColorTextureEffect(texture, SkMatrix::I());
1648
1649 if(!skPaint2GrPaintNoShader(this, paint, true, false, &grPaint)) {
1650 return;
1651 }
1652
1653 fContext->drawRectToRect(grPaint,
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001654 SkRect::MakeXYWH(SkIntToScalar(left),
1655 SkIntToScalar(top),
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001656 SkIntToScalar(w),
1657 SkIntToScalar(h)),
1658 SkRect::MakeXYWH(0,
1659 0,
1660 SK_Scalar1 * w / texture->width(),
1661 SK_Scalar1 * h / texture->height()));
1662}
1663
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001664void SkGpuDevice::drawBitmapRect(const SkDraw& origDraw, const SkBitmap& bitmap,
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001665 const SkRect* src, const SkRect& dst,
1666 const SkPaint& paint,
1667 SkCanvas::DrawBitmapRectFlags flags) {
1668 SkMatrix matrix;
1669 SkRect bitmapBounds, tmpSrc;
1670
1671 bitmapBounds.set(0, 0,
1672 SkIntToScalar(bitmap.width()),
1673 SkIntToScalar(bitmap.height()));
1674
1675 // Compute matrix from the two rectangles
1676 if (NULL != src) {
1677 tmpSrc = *src;
1678 } else {
1679 tmpSrc = bitmapBounds;
1680 }
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001681
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001682 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
1683
1684 // clip the tmpSrc to the bounds of the bitmap. No check needed if src==null.
1685 if (NULL != src) {
1686 if (!bitmapBounds.contains(tmpSrc)) {
1687 if (!tmpSrc.intersect(bitmapBounds)) {
1688 return; // nothing to draw
1689 }
1690 }
1691 }
1692
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001693 SkRect tmpDst;
1694 matrix.mapRect(&tmpDst, tmpSrc);
1695
1696 SkTCopyOnFirstWrite<SkDraw> draw(origDraw);
1697 if (0 != tmpDst.fLeft || 0 != tmpDst.fTop) {
1698 // Translate so that tempDst's top left is at the origin.
1699 matrix = *origDraw.fMatrix;
1700 matrix.preTranslate(tmpDst.fLeft, tmpDst.fTop);
1701 draw.writable()->fMatrix = &matrix;
1702 }
1703 SkSize dstSize;
1704 dstSize.fWidth = tmpDst.width();
1705 dstSize.fHeight = tmpDst.height();
1706
1707 this->drawBitmapCommon(*draw, bitmap, &tmpSrc, &dstSize, paint, flags);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001708}
1709
1710void SkGpuDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device,
1711 int x, int y, const SkPaint& paint) {
1712 // clear of the source device must occur before CHECK_SHOULD_DRAW
1713 SkGpuDevice* dev = static_cast<SkGpuDevice*>(device);
1714 if (dev->fNeedClear) {
1715 // TODO: could check here whether we really need to draw at all
1716 dev->clear(0x0);
1717 }
1718
1719 // drawDevice is defined to be in device coords.
1720 CHECK_SHOULD_DRAW(draw, true);
1721
1722 GrRenderTarget* devRT = dev->accessRenderTarget();
1723 GrTexture* devTex;
1724 if (NULL == (devTex = devRT->asTexture())) {
1725 return;
1726 }
1727
1728 const SkBitmap& bm = dev->accessBitmap(false);
1729 int w = bm.width();
1730 int h = bm.height();
1731
1732 SkImageFilter* filter = paint.getImageFilter();
1733 // This bitmap will own the filtered result as a texture.
1734 SkBitmap filteredBitmap;
1735
1736 if (NULL != filter) {
1737 SkIPoint offset = SkIPoint::Make(0, 0);
1738 SkMatrix matrix(*draw.fMatrix);
1739 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001740 SkIRect clipBounds = SkIRect::MakeWH(devTex->width(), devTex->height());
1741 SkImageFilter::Context ctx(matrix, clipBounds);
1742 if (filter_texture(this, fContext, devTex, filter, w, h, ctx, &filteredBitmap,
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001743 &offset)) {
1744 devTex = filteredBitmap.getTexture();
1745 w = filteredBitmap.width();
1746 h = filteredBitmap.height();
1747 x += offset.fX;
1748 y += offset.fY;
1749 } else {
1750 return;
1751 }
1752 }
1753
1754 GrPaint grPaint;
1755 grPaint.addColorTextureEffect(devTex, SkMatrix::I());
1756
1757 if (!skPaint2GrPaintNoShader(this, paint, true, false, &grPaint)) {
1758 return;
1759 }
1760
1761 SkRect dstRect = SkRect::MakeXYWH(SkIntToScalar(x),
1762 SkIntToScalar(y),
1763 SkIntToScalar(w),
1764 SkIntToScalar(h));
1765
1766 // The device being drawn may not fill up its texture (e.g. saveLayer uses approximate
1767 // scratch texture).
1768 SkRect srcRect = SkRect::MakeWH(SK_Scalar1 * w / devTex->width(),
1769 SK_Scalar1 * h / devTex->height());
1770
1771 fContext->drawRectToRect(grPaint, dstRect, srcRect);
1772}
1773
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +00001774bool SkGpuDevice::canHandleImageFilter(const SkImageFilter* filter) {
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001775 return filter->canFilterImageGPU();
1776}
1777
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +00001778bool SkGpuDevice::filterImage(const SkImageFilter* filter, const SkBitmap& src,
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001779 const SkImageFilter::Context& ctx,
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001780 SkBitmap* result, SkIPoint* offset) {
1781 // want explicitly our impl, so guard against a subclass of us overriding it
1782 if (!this->SkGpuDevice::canHandleImageFilter(filter)) {
1783 return false;
1784 }
1785
1786 SkAutoLockPixels alp(src, !src.getTexture());
1787 if (!src.getTexture() && !src.readyToDraw()) {
1788 return false;
1789 }
1790
1791 GrTexture* texture;
1792 // We assume here that the filter will not attempt to tile the src. Otherwise, this cache lookup
1793 // must be pushed upstack.
1794 SkAutoCachedTexture act(this, src, NULL, &texture);
1795
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001796 return filter_texture(this, fContext, texture, filter, src.width(), src.height(), ctx,
1797 result, offset);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001798}
1799
1800///////////////////////////////////////////////////////////////////////////////
1801
1802// must be in SkCanvas::VertexMode order
1803static const GrPrimitiveType gVertexMode2PrimitiveType[] = {
1804 kTriangles_GrPrimitiveType,
1805 kTriangleStrip_GrPrimitiveType,
1806 kTriangleFan_GrPrimitiveType,
1807};
1808
1809void SkGpuDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
1810 int vertexCount, const SkPoint vertices[],
1811 const SkPoint texs[], const SkColor colors[],
1812 SkXfermode* xmode,
1813 const uint16_t indices[], int indexCount,
1814 const SkPaint& paint) {
1815 CHECK_SHOULD_DRAW(draw, false);
1816
1817 GrPaint grPaint;
1818 // we ignore the shader if texs is null.
1819 if (NULL == texs) {
1820 if (!skPaint2GrPaintNoShader(this, paint, false, NULL == colors, &grPaint)) {
1821 return;
1822 }
1823 } else {
1824 if (!skPaint2GrPaintShader(this, paint, NULL == colors, &grPaint)) {
1825 return;
1826 }
1827 }
1828
1829 if (NULL != xmode && NULL != texs && NULL != colors) {
1830 if (!SkXfermode::IsMode(xmode, SkXfermode::kModulate_Mode)) {
1831 SkDebugf("Unsupported vertex-color/texture xfer mode.\n");
1832#if 0
1833 return
1834#endif
1835 }
1836 }
1837
1838 SkAutoSTMalloc<128, GrColor> convertedColors(0);
1839 if (NULL != colors) {
1840 // need to convert byte order and from non-PM to PM
1841 convertedColors.reset(vertexCount);
1842 for (int i = 0; i < vertexCount; ++i) {
1843 convertedColors[i] = SkColor2GrColor(colors[i]);
1844 }
1845 colors = convertedColors.get();
1846 }
1847 fContext->drawVertices(grPaint,
1848 gVertexMode2PrimitiveType[vmode],
1849 vertexCount,
1850 (GrPoint*) vertices,
1851 (GrPoint*) texs,
1852 colors,
1853 indices,
1854 indexCount);
1855}
1856
1857///////////////////////////////////////////////////////////////////////////////
1858
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001859void SkGpuDevice::drawText(const SkDraw& draw, const void* text,
1860 size_t byteLength, SkScalar x, SkScalar y,
1861 const SkPaint& paint) {
1862 CHECK_SHOULD_DRAW(draw, false);
1863
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +00001864 if (fMainTextContext->canDraw(paint)) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +00001865 GrPaint grPaint;
1866 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
1867 return;
1868 }
1869
1870 SkDEBUGCODE(this->validate();)
1871
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +00001872 fMainTextContext->drawText(grPaint, paint, (const char *)text, byteLength, x, y);
1873 } else if (fFallbackTextContext && fFallbackTextContext->canDraw(paint)) {
commit-bot@chromium.org9f94b912014-01-30 15:22:54 +00001874 GrPaint grPaint;
1875 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
1876 return;
1877 }
1878
1879 SkDEBUGCODE(this->validate();)
1880
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +00001881 fFallbackTextContext->drawText(grPaint, paint, (const char *)text, byteLength, x, y);
commit-bot@chromium.org9f94b912014-01-30 15:22:54 +00001882 } else {
1883 // this guy will just call our drawPath()
1884 draw.drawText_asPaths((const char*)text, byteLength, x, y, paint);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001885 }
1886}
1887
1888void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text,
1889 size_t byteLength, const SkScalar pos[],
1890 SkScalar constY, int scalarsPerPos,
1891 const SkPaint& paint) {
1892 CHECK_SHOULD_DRAW(draw, false);
1893
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +00001894 if (fMainTextContext->canDraw(paint)) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +00001895 GrPaint grPaint;
1896 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
1897 return;
1898 }
1899
1900 SkDEBUGCODE(this->validate();)
1901
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +00001902 fMainTextContext->drawPosText(grPaint, paint, (const char *)text, byteLength, pos,
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +00001903 constY, scalarsPerPos);
1904 } else if (fFallbackTextContext && fFallbackTextContext->canDraw(paint)) {
commit-bot@chromium.org9f94b912014-01-30 15:22:54 +00001905 GrPaint grPaint;
1906 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
1907 return;
1908 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +00001909
commit-bot@chromium.org9f94b912014-01-30 15:22:54 +00001910 SkDEBUGCODE(this->validate();)
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +00001911
1912 fFallbackTextContext->drawPosText(grPaint, paint, (const char *)text, byteLength, pos,
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +00001913 constY, scalarsPerPos);
commit-bot@chromium.org9f94b912014-01-30 15:22:54 +00001914 } else {
1915 draw.drawPosText_asPaths((const char*)text, byteLength, pos, constY,
1916 scalarsPerPos, paint);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001917 }
1918}
1919
1920void SkGpuDevice::drawTextOnPath(const SkDraw& draw, const void* text,
1921 size_t len, const SkPath& path,
1922 const SkMatrix* m, const SkPaint& paint) {
1923 CHECK_SHOULD_DRAW(draw, false);
1924
1925 SkASSERT(draw.fDevice == this);
1926 draw.drawTextOnPath((const char*)text, len, path, m, paint);
1927}
1928
1929///////////////////////////////////////////////////////////////////////////////
1930
1931bool SkGpuDevice::filterTextFlags(const SkPaint& paint, TextFlags* flags) {
1932 if (!paint.isLCDRenderText()) {
1933 // we're cool with the paint as is
1934 return false;
1935 }
1936
1937 if (paint.getShader() ||
1938 paint.getXfermode() || // unless its srcover
1939 paint.getMaskFilter() ||
1940 paint.getRasterizer() ||
1941 paint.getColorFilter() ||
1942 paint.getPathEffect() ||
1943 paint.isFakeBoldText() ||
1944 paint.getStyle() != SkPaint::kFill_Style) {
1945 // turn off lcd
1946 flags->fFlags = paint.getFlags() & ~SkPaint::kLCDRenderText_Flag;
1947 flags->fHinting = paint.getHinting();
1948 return true;
1949 }
1950 // we're cool with the paint as is
1951 return false;
1952}
1953
1954void SkGpuDevice::flush() {
1955 DO_DEFERRED_CLEAR();
1956 fContext->resolveRenderTarget(fRenderTarget);
1957}
1958
1959///////////////////////////////////////////////////////////////////////////////
1960
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001961SkBaseDevice* SkGpuDevice::onCreateDevice(const SkImageInfo& info, Usage usage) {
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001962 GrTextureDesc desc;
1963 desc.fConfig = fRenderTarget->config();
1964 desc.fFlags = kRenderTarget_GrTextureFlagBit;
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001965 desc.fWidth = info.width();
1966 desc.fHeight = info.height();
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001967 desc.fSampleCnt = fRenderTarget->numSamples();
1968
1969 SkAutoTUnref<GrTexture> texture;
1970 // Skia's convention is to only clear a device if it is non-opaque.
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001971 bool needClear = !info.isOpaque();
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001972
1973#if CACHE_COMPATIBLE_DEVICE_TEXTURES
1974 // layers are never draw in repeat modes, so we can request an approx
1975 // match and ignore any padding.
1976 const GrContext::ScratchTexMatch match = (kSaveLayer_Usage == usage) ?
1977 GrContext::kApprox_ScratchTexMatch :
1978 GrContext::kExact_ScratchTexMatch;
1979 texture.reset(fContext->lockAndRefScratchTexture(desc, match));
1980#else
1981 texture.reset(fContext->createUncachedTexture(desc, NULL, 0));
1982#endif
1983 if (NULL != texture.get()) {
1984 return SkNEW_ARGS(SkGpuDevice,(fContext, texture, needClear));
1985 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001986 GrPrintf("---- failed to create compatible device texture [%d %d]\n",
1987 info.width(), info.height());
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001988 return NULL;
1989 }
1990}
1991
reed@google.com76f10a32014-02-05 15:32:21 +00001992SkSurface* SkGpuDevice::newSurface(const SkImageInfo& info) {
1993 return SkSurface::NewRenderTarget(fContext, info, fRenderTarget->numSamples());
1994}
1995
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001996SkGpuDevice::SkGpuDevice(GrContext* context,
1997 GrTexture* texture,
1998 bool needClear)
1999 : SkBitmapDevice(make_bitmap(context, texture->asRenderTarget())) {
2000
2001 SkASSERT(texture && texture->asRenderTarget());
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00002002 // This constructor is called from onCreateDevice. It has locked the RT in the texture
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00002003 // cache. We pass true for the third argument so that it will get unlocked.
2004 this->initFromRenderTarget(context, texture->asRenderTarget(), true);
2005 fNeedClear = needClear;
2006}
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002007
2008class GPUAccelData : public SkPicture::AccelData {
2009public:
2010 GPUAccelData(Key key) : INHERITED(key) { }
2011
2012protected:
2013
2014private:
2015 typedef SkPicture::AccelData INHERITED;
2016};
2017
2018// In the future this may not be a static method if we need to incorporate the
2019// clip and matrix state into the key
2020SkPicture::AccelData::Key SkGpuDevice::ComputeAccelDataKey() {
2021 static const SkPicture::AccelData::Key gGPUID = SkPicture::AccelData::GenerateDomain();
2022
2023 return gGPUID;
2024}
2025
2026void SkGpuDevice::EXPERIMENTAL_optimize(SkPicture* picture) {
2027 SkPicture::AccelData::Key key = ComputeAccelDataKey();
2028
2029 GPUAccelData* data = SkNEW_ARGS(GPUAccelData, (key));
2030
2031 picture->EXPERIMENTAL_addAccelData(data);
2032}
2033
2034bool SkGpuDevice::EXPERIMENTAL_drawPicture(const SkPicture& picture) {
2035 SkPicture::AccelData::Key key = ComputeAccelDataKey();
2036
2037 const SkPicture::AccelData* data = picture.EXPERIMENTAL_getAccelData(key);
2038 if (NULL == data) {
2039 return false;
2040 }
2041
2042#if 0
2043 const GPUAccelData *gpuData = static_cast<const GPUAccelData*>(data);
2044#endif
2045
2046 return false;
2047}