blob: 9041bdfba25b1ec3859e3c0b0095a17139ef01d7 [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.org628ed0b2014-05-19 14:32:49 +000011#include "effects/GrDashingEffect.h"
commit-bot@chromium.org907fbd52013-12-09 17:03:02 +000012#include "effects/GrTextureDomain.h"
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +000013#include "effects/GrSimpleTextureEffect.h"
14
15#include "GrContext.h"
16#include "GrBitmapTextContext.h"
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +000017#include "GrDistanceFieldTextContext.h"
robertphillips@google.come930a072014-04-03 00:34:27 +000018#include "GrLayerCache.h"
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +000019#include "GrPictureUtils.h"
egdanield58a0ba2014-06-11 10:30:05 -070020#include "GrStrokeInfo.h"
egdanielbbcb38d2014-06-19 10:19:29 -070021#include "GrTracing.h"
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +000022
23#include "SkGrTexturePixelRef.h"
24
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +000025#include "SkDeviceImageFilterProxy.h"
26#include "SkDrawProcs.h"
27#include "SkGlyphCache.h"
28#include "SkImageFilter.h"
commit-bot@chromium.org82139702014-03-10 22:53:20 +000029#include "SkMaskFilter.h"
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +000030#include "SkPathEffect.h"
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +000031#include "SkPicture.h"
robertphillipsdb539902014-07-01 08:47:04 -070032#include "SkPictureData.h"
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +000033#include "SkRRect.h"
34#include "SkStroke.h"
reed@google.com76f10a32014-02-05 15:32:21 +000035#include "SkSurface.h"
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +000036#include "SkTLazy.h"
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +000037#include "SkUtils.h"
commit-bot@chromium.org559a8832014-05-30 10:08:22 +000038#include "SkVertState.h"
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +000039#include "SkErrorInternals.h"
40
41#define CACHE_COMPATIBLE_DEVICE_TEXTURES 1
42
43#if 0
44 extern bool (*gShouldDrawProc)();
45 #define CHECK_SHOULD_DRAW(draw, forceI) \
46 do { \
47 if (gShouldDrawProc && !gShouldDrawProc()) return; \
48 this->prepareDraw(draw, forceI); \
49 } while (0)
50#else
51 #define CHECK_SHOULD_DRAW(draw, forceI) this->prepareDraw(draw, forceI)
52#endif
53
54// This constant represents the screen alignment criterion in texels for
55// requiring texture domain clamping to prevent color bleeding when drawing
56// a sub region of a larger source image.
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +000057#define COLOR_BLEED_TOLERANCE 0.001f
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +000058
59#define DO_DEFERRED_CLEAR() \
60 do { \
61 if (fNeedClear) { \
62 this->clear(SK_ColorTRANSPARENT); \
63 } \
64 } while (false) \
65
66///////////////////////////////////////////////////////////////////////////////
67
68#define CHECK_FOR_ANNOTATION(paint) \
69 do { if (paint.getAnnotation()) { return; } } while (0)
70
71///////////////////////////////////////////////////////////////////////////////
72
73
74class SkGpuDevice::SkAutoCachedTexture : public ::SkNoncopyable {
75public:
76 SkAutoCachedTexture()
77 : fDevice(NULL)
78 , fTexture(NULL) {
79 }
80
81 SkAutoCachedTexture(SkGpuDevice* device,
82 const SkBitmap& bitmap,
83 const GrTextureParams* params,
84 GrTexture** texture)
85 : fDevice(NULL)
86 , fTexture(NULL) {
87 SkASSERT(NULL != texture);
88 *texture = this->set(device, bitmap, params);
89 }
90
91 ~SkAutoCachedTexture() {
92 if (NULL != fTexture) {
93 GrUnlockAndUnrefCachedBitmapTexture(fTexture);
94 }
95 }
96
97 GrTexture* set(SkGpuDevice* device,
98 const SkBitmap& bitmap,
99 const GrTextureParams* params) {
100 if (NULL != fTexture) {
101 GrUnlockAndUnrefCachedBitmapTexture(fTexture);
102 fTexture = NULL;
103 }
104 fDevice = device;
105 GrTexture* result = (GrTexture*)bitmap.getTexture();
106 if (NULL == result) {
107 // Cannot return the native texture so look it up in our cache
108 fTexture = GrLockAndRefCachedBitmapTexture(device->context(), bitmap, params);
109 result = fTexture;
110 }
111 return result;
112 }
113
114private:
115 SkGpuDevice* fDevice;
116 GrTexture* fTexture;
117};
118
119///////////////////////////////////////////////////////////////////////////////
120
121struct GrSkDrawProcs : public SkDrawProcs {
122public:
123 GrContext* fContext;
124 GrTextContext* fTextContext;
125 GrFontScaler* fFontScaler; // cached in the skia glyphcache
126};
127
128///////////////////////////////////////////////////////////////////////////////
129
commit-bot@chromium.orgd8a57af2014-03-19 21:19:16 +0000130SkGpuDevice* SkGpuDevice::Create(GrSurface* surface, unsigned flags) {
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000131 SkASSERT(NULL != surface);
132 if (NULL == surface->asRenderTarget() || NULL == surface->getContext()) {
133 return NULL;
134 }
135 if (surface->asTexture()) {
commit-bot@chromium.orgd8a57af2014-03-19 21:19:16 +0000136 return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asTexture(), flags));
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000137 } else {
commit-bot@chromium.orgd8a57af2014-03-19 21:19:16 +0000138 return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asRenderTarget(), flags));
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000139 }
140}
141
reed89443ab2014-06-27 11:34:19 -0700142SkGpuDevice::SkGpuDevice(GrContext* context, GrTexture* texture, unsigned flags) {
commit-bot@chromium.orgd8a57af2014-03-19 21:19:16 +0000143 this->initFromRenderTarget(context, texture->asRenderTarget(), flags);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000144}
145
reed89443ab2014-06-27 11:34:19 -0700146SkGpuDevice::SkGpuDevice(GrContext* context, GrRenderTarget* renderTarget, unsigned flags) {
commit-bot@chromium.orgd8a57af2014-03-19 21:19:16 +0000147 this->initFromRenderTarget(context, renderTarget, flags);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000148}
149
150void SkGpuDevice::initFromRenderTarget(GrContext* context,
151 GrRenderTarget* renderTarget,
commit-bot@chromium.orgd8a57af2014-03-19 21:19:16 +0000152 unsigned flags) {
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000153 fDrawProcs = NULL;
154
155 fContext = context;
156 fContext->ref();
157
158 fRenderTarget = NULL;
commit-bot@chromium.orgd8a57af2014-03-19 21:19:16 +0000159 fNeedClear = flags & kNeedClear_Flag;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000160
161 SkASSERT(NULL != renderTarget);
162 fRenderTarget = renderTarget;
163 fRenderTarget->ref();
164
165 // Hold onto to the texture in the pixel ref (if there is one) because the texture holds a ref
166 // on the RT but not vice-versa.
167 // TODO: Remove this trickery once we figure out how to make SkGrPixelRef do this without
168 // busting chrome (for a currently unknown reason).
169 GrSurface* surface = fRenderTarget->asTexture();
170 if (NULL == surface) {
171 surface = fRenderTarget;
172 }
reed@google.combf790232013-12-13 19:45:58 +0000173
reed6c225732014-06-09 19:52:07 -0700174 SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef,
175 (surface->info(), surface, SkToBool(flags & kCached_Flag)));
reed89443ab2014-06-27 11:34:19 -0700176 fLegacyBitmap.setInfo(surface->info());
177 fLegacyBitmap.setPixelRef(pr)->unref();
kkinnunenc6cb56f2014-06-24 00:12:27 -0700178
179 bool useDFFonts = !!(flags & kDFFonts_Flag);
180 fMainTextContext = fContext->createTextContext(fRenderTarget, fLeakyProperties, useDFFonts);
181 fFallbackTextContext = SkNEW_ARGS(GrBitmapTextContext, (fContext, fLeakyProperties));
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000182}
183
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000184SkGpuDevice* SkGpuDevice::Create(GrContext* context, const SkImageInfo& origInfo,
185 int sampleCount) {
186 if (kUnknown_SkColorType == origInfo.colorType() ||
187 origInfo.width() < 0 || origInfo.height() < 0) {
188 return NULL;
189 }
190
191 SkImageInfo info = origInfo;
192 // TODO: perhas we can loosen this check now that colortype is more detailed
193 // e.g. can we support both RGBA and BGRA here?
194 if (kRGB_565_SkColorType == info.colorType()) {
195 info.fAlphaType = kOpaque_SkAlphaType; // force this setting
196 } else {
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000197 info.fColorType = kN32_SkColorType;
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000198 if (kOpaque_SkAlphaType != info.alphaType()) {
199 info.fAlphaType = kPremul_SkAlphaType; // force this setting
200 }
201 }
202
203 GrTextureDesc desc;
204 desc.fFlags = kRenderTarget_GrTextureFlagBit;
205 desc.fWidth = info.width();
206 desc.fHeight = info.height();
commit-bot@chromium.org3adcc342014-04-23 19:18:09 +0000207 desc.fConfig = SkImageInfo2GrPixelConfig(info);
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000208 desc.fSampleCnt = sampleCount;
209
210 SkAutoTUnref<GrTexture> texture(context->createUncachedTexture(desc, NULL, 0));
211 if (!texture.get()) {
212 return NULL;
213 }
skia.committer@gmail.com969588f2014-02-16 03:01:56 +0000214
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000215 return SkNEW_ARGS(SkGpuDevice, (context, texture.get()));
216}
217
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000218SkGpuDevice::~SkGpuDevice() {
219 if (fDrawProcs) {
220 delete fDrawProcs;
221 }
skia.committer@gmail.comd2ac07b2014-01-25 07:01:49 +0000222
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000223 delete fMainTextContext;
224 delete fFallbackTextContext;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000225
226 // The GrContext takes a ref on the target. We don't want to cause the render
227 // target to be unnecessarily kept alive.
228 if (fContext->getRenderTarget() == fRenderTarget) {
229 fContext->setRenderTarget(NULL);
230 }
231
232 if (fContext->getClip() == &fClipData) {
233 fContext->setClip(NULL);
234 }
235
236 SkSafeUnref(fRenderTarget);
237 fContext->unref();
238}
239
240///////////////////////////////////////////////////////////////////////////////
241
242void SkGpuDevice::makeRenderTargetCurrent() {
243 DO_DEFERRED_CLEAR();
244 fContext->setRenderTarget(fRenderTarget);
245}
246
247///////////////////////////////////////////////////////////////////////////////
248
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000249bool SkGpuDevice::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
250 int x, int y) {
251 DO_DEFERRED_CLEAR();
252
253 // TODO: teach fRenderTarget to take ImageInfo directly to specify the src pixels
commit-bot@chromium.org3adcc342014-04-23 19:18:09 +0000254 GrPixelConfig config = SkImageInfo2GrPixelConfig(dstInfo);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000255 if (kUnknown_GrPixelConfig == config) {
256 return false;
257 }
258
259 uint32_t flags = 0;
260 if (kUnpremul_SkAlphaType == dstInfo.alphaType()) {
261 flags = GrContext::kUnpremul_PixelOpsFlag;
262 }
263 return fContext->readRenderTargetPixels(fRenderTarget, x, y, dstInfo.width(), dstInfo.height(),
264 config, dstPixels, dstRowBytes, flags);
265}
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000266
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000267bool SkGpuDevice::onWritePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes,
268 int x, int y) {
269 // TODO: teach fRenderTarget to take ImageInfo directly to specify the src pixels
commit-bot@chromium.org3adcc342014-04-23 19:18:09 +0000270 GrPixelConfig config = SkImageInfo2GrPixelConfig(info);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000271 if (kUnknown_GrPixelConfig == config) {
272 return false;
273 }
274 uint32_t flags = 0;
275 if (kUnpremul_SkAlphaType == info.alphaType()) {
276 flags = GrContext::kUnpremul_PixelOpsFlag;
277 }
278 fRenderTarget->writePixels(x, y, info.width(), info.height(), config, pixels, rowBytes, flags);
279
280 // need to bump our genID for compatibility with clients that "know" we have a bitmap
reed89443ab2014-06-27 11:34:19 -0700281 fLegacyBitmap.notifyPixelsChanged();
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000282
283 return true;
284}
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000285
senorblanco@chromium.orgb7b7eb32014-03-19 18:24:04 +0000286const SkBitmap& SkGpuDevice::onAccessBitmap() {
287 DO_DEFERRED_CLEAR();
reed89443ab2014-06-27 11:34:19 -0700288 return fLegacyBitmap;
senorblanco@chromium.orgb7b7eb32014-03-19 18:24:04 +0000289}
290
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000291void SkGpuDevice::onAttachToCanvas(SkCanvas* canvas) {
292 INHERITED::onAttachToCanvas(canvas);
293
294 // Canvas promises that this ptr is valid until onDetachFromCanvas is called
295 fClipData.fClipStack = canvas->getClipStack();
296}
297
298void SkGpuDevice::onDetachFromCanvas() {
299 INHERITED::onDetachFromCanvas();
300 fClipData.fClipStack = NULL;
301}
302
303// call this every draw call, to ensure that the context reflects our state,
304// and not the state from some other canvas/device
305void SkGpuDevice::prepareDraw(const SkDraw& draw, bool forceIdentity) {
306 SkASSERT(NULL != fClipData.fClipStack);
307
308 fContext->setRenderTarget(fRenderTarget);
309
310 SkASSERT(draw.fClipStack && draw.fClipStack == fClipData.fClipStack);
311
312 if (forceIdentity) {
313 fContext->setIdentityMatrix();
314 } else {
315 fContext->setMatrix(*draw.fMatrix);
316 }
317 fClipData.fOrigin = this->getOrigin();
318
319 fContext->setClip(&fClipData);
320
321 DO_DEFERRED_CLEAR();
322}
323
324GrRenderTarget* SkGpuDevice::accessRenderTarget() {
325 DO_DEFERRED_CLEAR();
326 return fRenderTarget;
327}
328
329///////////////////////////////////////////////////////////////////////////////
330
331SK_COMPILE_ASSERT(SkShader::kNone_BitmapType == 0, shader_type_mismatch);
332SK_COMPILE_ASSERT(SkShader::kDefault_BitmapType == 1, shader_type_mismatch);
333SK_COMPILE_ASSERT(SkShader::kRadial_BitmapType == 2, shader_type_mismatch);
334SK_COMPILE_ASSERT(SkShader::kSweep_BitmapType == 3, shader_type_mismatch);
335SK_COMPILE_ASSERT(SkShader::kTwoPointRadial_BitmapType == 4,
336 shader_type_mismatch);
337SK_COMPILE_ASSERT(SkShader::kTwoPointConical_BitmapType == 5,
338 shader_type_mismatch);
339SK_COMPILE_ASSERT(SkShader::kLinear_BitmapType == 6, shader_type_mismatch);
340SK_COMPILE_ASSERT(SkShader::kLast_BitmapType == 6, shader_type_mismatch);
341
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000342///////////////////////////////////////////////////////////////////////////////
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000343
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000344void SkGpuDevice::clear(SkColor color) {
345 SkIRect rect = SkIRect::MakeWH(this->width(), this->height());
346 fContext->clear(&rect, SkColor2GrColor(color), true, fRenderTarget);
347 fNeedClear = false;
348}
349
350void SkGpuDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
351 CHECK_SHOULD_DRAW(draw, false);
352
353 GrPaint grPaint;
commit-bot@chromium.org3595f882014-05-19 19:35:57 +0000354 SkPaint2GrPaintShader(this->context(), paint, true, &grPaint);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000355
356 fContext->drawPaint(grPaint);
357}
358
359// must be in SkCanvas::PointMode order
360static const GrPrimitiveType gPointMode2PrimtiveType[] = {
361 kPoints_GrPrimitiveType,
362 kLines_GrPrimitiveType,
363 kLineStrip_GrPrimitiveType
364};
365
366void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
367 size_t count, const SkPoint pts[], const SkPaint& paint) {
368 CHECK_FOR_ANNOTATION(paint);
369 CHECK_SHOULD_DRAW(draw, false);
370
371 SkScalar width = paint.getStrokeWidth();
372 if (width < 0) {
373 return;
374 }
375
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000376 if (paint.getPathEffect() && 2 == count && SkCanvas::kLines_PointMode == mode) {
egdaniele61c4112014-06-12 10:24:21 -0700377 GrStrokeInfo strokeInfo(paint, SkPaint::kStroke_Style);
378 GrPaint grPaint;
379 SkPaint2GrPaintShader(this->context(), paint, true, &grPaint);
380 SkPath path;
381 path.moveTo(pts[0]);
382 path.lineTo(pts[1]);
383 fContext->drawPath(grPaint, path, strokeInfo);
384 return;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000385 }
386
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000387 // we only handle hairlines and paints without path effects or mask filters,
388 // else we let the SkDraw call our drawPath()
389 if (width > 0 || paint.getPathEffect() || paint.getMaskFilter()) {
390 draw.drawPoints(mode, count, pts, paint, true);
391 return;
392 }
393
394 GrPaint grPaint;
commit-bot@chromium.org3595f882014-05-19 19:35:57 +0000395 SkPaint2GrPaintShader(this->context(), paint, true, &grPaint);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000396
397 fContext->drawVertices(grPaint,
398 gPointMode2PrimtiveType[mode],
robertphillips@google.coma4662862013-11-21 14:24:16 +0000399 SkToS32(count),
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000400 (SkPoint*)pts,
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000401 NULL,
402 NULL,
403 NULL,
404 0);
405}
406
407///////////////////////////////////////////////////////////////////////////////
408
409void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect,
410 const SkPaint& paint) {
egdanielbbcb38d2014-06-19 10:19:29 -0700411 GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice::drawRect", fContext);
412
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000413 CHECK_FOR_ANNOTATION(paint);
414 CHECK_SHOULD_DRAW(draw, false);
415
416 bool doStroke = paint.getStyle() != SkPaint::kFill_Style;
417 SkScalar width = paint.getStrokeWidth();
418
419 /*
420 We have special code for hairline strokes, miter-strokes, bevel-stroke
421 and fills. Anything else we just call our path code.
422 */
423 bool usePath = doStroke && width > 0 &&
424 (paint.getStrokeJoin() == SkPaint::kRound_Join ||
425 (paint.getStrokeJoin() == SkPaint::kBevel_Join && rect.isEmpty()));
426 // another two reasons we might need to call drawPath...
egdanield58a0ba2014-06-11 10:30:05 -0700427
428 if (paint.getMaskFilter()) {
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000429 usePath = true;
430 }
egdanield58a0ba2014-06-11 10:30:05 -0700431
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000432 if (!usePath && paint.isAntiAlias() && !fContext->getMatrix().rectStaysRect()) {
433#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)
434 if (doStroke) {
435#endif
436 usePath = true;
437#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)
438 } else {
439 usePath = !fContext->getMatrix().preservesRightAngles();
440 }
441#endif
442 }
443 // until we can both stroke and fill rectangles
444 if (paint.getStyle() == SkPaint::kStrokeAndFill_Style) {
445 usePath = true;
446 }
447
egdanield58a0ba2014-06-11 10:30:05 -0700448 GrStrokeInfo strokeInfo(paint);
449
450 const SkPathEffect* pe = paint.getPathEffect();
451 if (!usePath && NULL != pe && !strokeInfo.isDashed()) {
452 usePath = true;
453 }
454
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000455 if (usePath) {
456 SkPath path;
457 path.addRect(rect);
458 this->drawPath(draw, path, paint, NULL, true);
459 return;
460 }
461
462 GrPaint grPaint;
commit-bot@chromium.org3595f882014-05-19 19:35:57 +0000463 SkPaint2GrPaintShader(this->context(), paint, true, &grPaint);
Mike Klein744fb732014-06-23 15:13:26 -0400464
egdanield58a0ba2014-06-11 10:30:05 -0700465 fContext->drawRect(grPaint, rect, &strokeInfo);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000466}
467
468///////////////////////////////////////////////////////////////////////////////
469
470void SkGpuDevice::drawRRect(const SkDraw& draw, const SkRRect& rect,
471 const SkPaint& paint) {
472 CHECK_FOR_ANNOTATION(paint);
473 CHECK_SHOULD_DRAW(draw, false);
474
commit-bot@chromium.org82139702014-03-10 22:53:20 +0000475 GrPaint grPaint;
commit-bot@chromium.org3595f882014-05-19 19:35:57 +0000476 SkPaint2GrPaintShader(this->context(), paint, true, &grPaint);
Mike Klein744fb732014-06-23 15:13:26 -0400477
egdanield58a0ba2014-06-11 10:30:05 -0700478 GrStrokeInfo strokeInfo(paint);
commit-bot@chromium.org82139702014-03-10 22:53:20 +0000479 if (paint.getMaskFilter()) {
480 // try to hit the fast path for drawing filtered round rects
481
482 SkRRect devRRect;
483 if (rect.transform(fContext->getMatrix(), &devRRect)) {
484 if (devRRect.allCornersCircular()) {
485 SkRect maskRect;
486 if (paint.getMaskFilter()->canFilterMaskGPU(devRRect.rect(),
487 draw.fClip->getBounds(),
488 fContext->getMatrix(),
489 &maskRect)) {
490 SkIRect finalIRect;
491 maskRect.roundOut(&finalIRect);
492 if (draw.fClip->quickReject(finalIRect)) {
493 // clipped out
494 return;
495 }
commit-bot@chromium.org82139702014-03-10 22:53:20 +0000496 if (paint.getMaskFilter()->directFilterRRectMaskGPU(fContext, &grPaint,
egdanield58a0ba2014-06-11 10:30:05 -0700497 strokeInfo.getStrokeRec(),
498 devRRect)) {
commit-bot@chromium.org82139702014-03-10 22:53:20 +0000499 return;
500 }
501 }
502
503 }
504 }
505
506 }
507
egdanield58a0ba2014-06-11 10:30:05 -0700508 bool usePath = false;
509
510 if (paint.getMaskFilter()) {
511 usePath = true;
512 } else {
513 const SkPathEffect* pe = paint.getPathEffect();
514 if (NULL != pe && !strokeInfo.isDashed()) {
515 usePath = true;
516 }
517 }
518
519
520 if (usePath) {
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000521 SkPath path;
522 path.addRRect(rect);
523 this->drawPath(draw, path, paint, NULL, true);
524 return;
525 }
Mike Klein744fb732014-06-23 15:13:26 -0400526
egdanield58a0ba2014-06-11 10:30:05 -0700527 fContext->drawRRect(grPaint, rect, strokeInfo);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000528}
529
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000530void SkGpuDevice::drawDRRect(const SkDraw& draw, const SkRRect& outer,
531 const SkRRect& inner, const SkPaint& paint) {
532 SkStrokeRec stroke(paint);
533 if (stroke.isFillStyle()) {
534
535 CHECK_FOR_ANNOTATION(paint);
536 CHECK_SHOULD_DRAW(draw, false);
537
538 GrPaint grPaint;
commit-bot@chromium.org3595f882014-05-19 19:35:57 +0000539 SkPaint2GrPaintShader(this->context(), paint, true, &grPaint);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000540
541 if (NULL == paint.getMaskFilter() && NULL == paint.getPathEffect()) {
542 fContext->drawDRRect(grPaint, outer, inner);
543 return;
544 }
545 }
546
547 SkPath path;
548 path.addRRect(outer);
549 path.addRRect(inner);
550 path.setFillType(SkPath::kEvenOdd_FillType);
551
552 this->drawPath(draw, path, paint, NULL, true);
553}
554
555
commit-bot@chromium.org82139702014-03-10 22:53:20 +0000556/////////////////////////////////////////////////////////////////////////////
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000557
558void SkGpuDevice::drawOval(const SkDraw& draw, const SkRect& oval,
559 const SkPaint& paint) {
560 CHECK_FOR_ANNOTATION(paint);
561 CHECK_SHOULD_DRAW(draw, false);
562
egdanield58a0ba2014-06-11 10:30:05 -0700563 GrStrokeInfo strokeInfo(paint);
564
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000565 bool usePath = false;
566 // some basic reasons we might need to call drawPath...
egdanield58a0ba2014-06-11 10:30:05 -0700567 if (paint.getMaskFilter()) {
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000568 usePath = true;
egdanield58a0ba2014-06-11 10:30:05 -0700569 } else {
570 const SkPathEffect* pe = paint.getPathEffect();
571 if (NULL != pe && !strokeInfo.isDashed()) {
572 usePath = true;
573 }
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000574 }
575
576 if (usePath) {
577 SkPath path;
578 path.addOval(oval);
579 this->drawPath(draw, path, paint, NULL, true);
580 return;
581 }
582
583 GrPaint grPaint;
commit-bot@chromium.org3595f882014-05-19 19:35:57 +0000584 SkPaint2GrPaintShader(this->context(), paint, true, &grPaint);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000585
egdanield58a0ba2014-06-11 10:30:05 -0700586 fContext->drawOval(grPaint, oval, strokeInfo);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000587}
588
589#include "SkMaskFilter.h"
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000590
591///////////////////////////////////////////////////////////////////////////////
592
593// helpers for applying mask filters
594namespace {
595
596// Draw a mask using the supplied paint. Since the coverage/geometry
597// is already burnt into the mask this boils down to a rect draw.
598// Return true if the mask was successfully drawn.
599bool draw_mask(GrContext* context, const SkRect& maskRect,
600 GrPaint* grp, GrTexture* mask) {
601 GrContext::AutoMatrix am;
602 if (!am.setIdentity(context, grp)) {
603 return false;
604 }
605
606 SkMatrix matrix;
607 matrix.setTranslate(-maskRect.fLeft, -maskRect.fTop);
608 matrix.postIDiv(mask->width(), mask->height());
609
610 grp->addCoverageEffect(GrSimpleTextureEffect::Create(mask, matrix))->unref();
611 context->drawRect(*grp, maskRect);
612 return true;
613}
614
615bool draw_with_mask_filter(GrContext* context, const SkPath& devPath,
reed868074b2014-06-03 10:53:59 -0700616 SkMaskFilter* filter, const SkRegion& clip,
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000617 GrPaint* grp, SkPaint::Style style) {
618 SkMask srcM, dstM;
619
620 if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), filter, &context->getMatrix(), &srcM,
621 SkMask::kComputeBoundsAndRenderImage_CreateMode, style)) {
622 return false;
623 }
624 SkAutoMaskFreeImage autoSrc(srcM.fImage);
625
626 if (!filter->filterMask(&dstM, srcM, context->getMatrix(), NULL)) {
627 return false;
628 }
629 // this will free-up dstM when we're done (allocated in filterMask())
630 SkAutoMaskFreeImage autoDst(dstM.fImage);
631
632 if (clip.quickReject(dstM.fBounds)) {
633 return false;
634 }
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000635
636 // we now have a device-aligned 8bit mask in dstM, ready to be drawn using
637 // the current clip (and identity matrix) and GrPaint settings
638 GrTextureDesc desc;
639 desc.fWidth = dstM.fBounds.width();
640 desc.fHeight = dstM.fBounds.height();
641 desc.fConfig = kAlpha_8_GrPixelConfig;
642
643 GrAutoScratchTexture ast(context, desc);
644 GrTexture* texture = ast.texture();
645
646 if (NULL == texture) {
647 return false;
648 }
649 texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
650 dstM.fImage, dstM.fRowBytes);
651
652 SkRect maskRect = SkRect::Make(dstM.fBounds);
653
654 return draw_mask(context, maskRect, grp, texture);
655}
656
657// Create a mask of 'devPath' and place the result in 'mask'. Return true on
658// success; false otherwise.
659bool create_mask_GPU(GrContext* context,
660 const SkRect& maskRect,
661 const SkPath& devPath,
egdanield58a0ba2014-06-11 10:30:05 -0700662 const GrStrokeInfo& strokeInfo,
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000663 bool doAA,
664 GrAutoScratchTexture* mask) {
665 GrTextureDesc desc;
666 desc.fFlags = kRenderTarget_GrTextureFlagBit;
667 desc.fWidth = SkScalarCeilToInt(maskRect.width());
668 desc.fHeight = SkScalarCeilToInt(maskRect.height());
669 // We actually only need A8, but it often isn't supported as a
670 // render target so default to RGBA_8888
671 desc.fConfig = kRGBA_8888_GrPixelConfig;
672 if (context->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {
673 desc.fConfig = kAlpha_8_GrPixelConfig;
674 }
675
676 mask->set(context, desc);
677 if (NULL == mask->texture()) {
678 return false;
679 }
680
681 GrTexture* maskTexture = mask->texture();
682 SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height());
683
684 GrContext::AutoRenderTarget art(context, maskTexture->asRenderTarget());
685 GrContext::AutoClip ac(context, clipRect);
686
687 context->clear(NULL, 0x0, true);
688
689 GrPaint tempPaint;
690 if (doAA) {
691 tempPaint.setAntiAlias(true);
692 // AA uses the "coverage" stages on GrDrawTarget. Coverage with a dst
693 // blend coeff of zero requires dual source blending support in order
694 // to properly blend partially covered pixels. This means the AA
695 // code path may not be taken. So we use a dst blend coeff of ISA. We
696 // could special case AA draws to a dst surface with known alpha=0 to
697 // use a zero dst coeff when dual source blending isn't available.
698 tempPaint.setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff);
699 }
700
701 GrContext::AutoMatrix am;
702
703 // Draw the mask into maskTexture with the path's top-left at the origin using tempPaint.
704 SkMatrix translate;
705 translate.setTranslate(-maskRect.fLeft, -maskRect.fTop);
706 am.set(context, translate);
egdanield58a0ba2014-06-11 10:30:05 -0700707 context->drawPath(tempPaint, devPath, strokeInfo);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000708 return true;
709}
710
711SkBitmap wrap_texture(GrTexture* texture) {
712 SkBitmap result;
reed6c225732014-06-09 19:52:07 -0700713 result.setInfo(texture->info());
714 result.setPixelRef(SkNEW_ARGS(SkGrPixelRef, (result.info(), texture)))->unref();
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000715 return result;
716}
717
718};
719
720void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
721 const SkPaint& paint, const SkMatrix* prePathMatrix,
722 bool pathIsMutable) {
723 CHECK_FOR_ANNOTATION(paint);
724 CHECK_SHOULD_DRAW(draw, false);
725
726 GrPaint grPaint;
commit-bot@chromium.org3595f882014-05-19 19:35:57 +0000727 SkPaint2GrPaintShader(this->context(), paint, true, &grPaint);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000728
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000729 // If we have a prematrix, apply it to the path, optimizing for the case
730 // where the original path can in fact be modified in place (even though
731 // its parameter type is const).
732 SkPath* pathPtr = const_cast<SkPath*>(&origSrcPath);
commit-bot@chromium.orgf0c41e22014-01-14 18:42:34 +0000733 SkTLazy<SkPath> tmpPath;
734 SkTLazy<SkPath> effectPath;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000735
736 if (prePathMatrix) {
737 SkPath* result = pathPtr;
738
739 if (!pathIsMutable) {
commit-bot@chromium.orgf0c41e22014-01-14 18:42:34 +0000740 result = tmpPath.init();
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000741 pathIsMutable = true;
742 }
743 // should I push prePathMatrix on our MV stack temporarily, instead
744 // of applying it here? See SkDraw.cpp
745 pathPtr->transform(*prePathMatrix, result);
746 pathPtr = result;
747 }
748 // at this point we're done with prePathMatrix
749 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
750
egdanield58a0ba2014-06-11 10:30:05 -0700751 GrStrokeInfo strokeInfo(paint);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000752 SkPathEffect* pathEffect = paint.getPathEffect();
753 const SkRect* cullRect = NULL; // TODO: what is our bounds?
egdanield58a0ba2014-06-11 10:30:05 -0700754 SkStrokeRec* strokePtr = strokeInfo.getStrokeRecPtr();
755 if (pathEffect && pathEffect->filterPath(effectPath.init(), *pathPtr, strokePtr,
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000756 cullRect)) {
commit-bot@chromium.orgf0c41e22014-01-14 18:42:34 +0000757 pathPtr = effectPath.get();
758 pathIsMutable = true;
egdanield58a0ba2014-06-11 10:30:05 -0700759 strokeInfo.removeDash();
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000760 }
761
egdanield58a0ba2014-06-11 10:30:05 -0700762 const SkStrokeRec& stroke = strokeInfo.getStrokeRec();
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000763 if (paint.getMaskFilter()) {
764 if (!stroke.isHairlineStyle()) {
commit-bot@chromium.orgf0c41e22014-01-14 18:42:34 +0000765 SkPath* strokedPath = pathIsMutable ? pathPtr : tmpPath.init();
766 if (stroke.applyToPath(strokedPath, *pathPtr)) {
767 pathPtr = strokedPath;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000768 pathIsMutable = true;
egdanield58a0ba2014-06-11 10:30:05 -0700769 strokeInfo.setFillStyle();
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000770 }
771 }
772
773 // avoid possibly allocating a new path in transform if we can
commit-bot@chromium.orgf0c41e22014-01-14 18:42:34 +0000774 SkPath* devPathPtr = pathIsMutable ? pathPtr : tmpPath.init();
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000775
776 // transform the path into device space
777 pathPtr->transform(fContext->getMatrix(), devPathPtr);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000778
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000779 SkRect maskRect;
780 if (paint.getMaskFilter()->canFilterMaskGPU(devPathPtr->getBounds(),
781 draw.fClip->getBounds(),
782 fContext->getMatrix(),
783 &maskRect)) {
commit-bot@chromium.org439ff1b2014-01-13 16:39:39 +0000784 // The context's matrix may change while creating the mask, so save the CTM here to
785 // pass to filterMaskGPU.
786 const SkMatrix ctm = fContext->getMatrix();
787
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000788 SkIRect finalIRect;
789 maskRect.roundOut(&finalIRect);
790 if (draw.fClip->quickReject(finalIRect)) {
791 // clipped out
792 return;
793 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000794
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000795 if (paint.getMaskFilter()->directFilterMaskGPU(fContext, &grPaint,
commit-bot@chromium.org82139702014-03-10 22:53:20 +0000796 stroke, *devPathPtr)) {
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000797 // the mask filter was able to draw itself directly, so there's nothing
798 // left to do.
799 return;
800 }
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000801
802 GrAutoScratchTexture mask;
803
egdanield58a0ba2014-06-11 10:30:05 -0700804 if (create_mask_GPU(fContext, maskRect, *devPathPtr, strokeInfo,
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000805 grPaint.isAntiAlias(), &mask)) {
806 GrTexture* filtered;
807
commit-bot@chromium.org41bf9302014-01-08 22:25:53 +0000808 if (paint.getMaskFilter()->filterMaskGPU(mask.texture(),
commit-bot@chromium.org439ff1b2014-01-13 16:39:39 +0000809 ctm, maskRect, &filtered, true)) {
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000810 // filterMaskGPU gives us ownership of a ref to the result
811 SkAutoTUnref<GrTexture> atu(filtered);
812
813 // If the scratch texture that we used as the filter src also holds the filter
814 // result then we must detach so that this texture isn't recycled for a later
815 // draw.
816 if (filtered == mask.texture()) {
817 mask.detach();
818 filtered->unref(); // detach transfers GrAutoScratchTexture's ref to us.
819 }
820
821 if (draw_mask(fContext, maskRect, &grPaint, filtered)) {
822 // This path is completely drawn
823 return;
824 }
825 }
826 }
827 }
828
829 // draw the mask on the CPU - this is a fallthrough path in case the
830 // GPU path fails
831 SkPaint::Style style = stroke.isHairlineStyle() ? SkPaint::kStroke_Style :
832 SkPaint::kFill_Style;
egdanield58a0ba2014-06-11 10:30:05 -0700833 draw_with_mask_filter(fContext, *devPathPtr, paint.getMaskFilter(),
834 *draw.fClip, &grPaint, style);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000835 return;
836 }
837
egdanield58a0ba2014-06-11 10:30:05 -0700838 fContext->drawPath(grPaint, *pathPtr, strokeInfo);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000839}
840
841static const int kBmpSmallTileSize = 1 << 10;
842
843static inline int get_tile_count(const SkIRect& srcRect, int tileSize) {
844 int tilesX = (srcRect.fRight / tileSize) - (srcRect.fLeft / tileSize) + 1;
845 int tilesY = (srcRect.fBottom / tileSize) - (srcRect.fTop / tileSize) + 1;
846 return tilesX * tilesY;
847}
848
849static int determine_tile_size(const SkBitmap& bitmap, const SkIRect& src, int maxTileSize) {
850 if (maxTileSize <= kBmpSmallTileSize) {
851 return maxTileSize;
852 }
853
854 size_t maxTileTotalTileSize = get_tile_count(src, maxTileSize);
855 size_t smallTotalTileSize = get_tile_count(src, kBmpSmallTileSize);
856
857 maxTileTotalTileSize *= maxTileSize * maxTileSize;
858 smallTotalTileSize *= kBmpSmallTileSize * kBmpSmallTileSize;
859
860 if (maxTileTotalTileSize > 2 * smallTotalTileSize) {
861 return kBmpSmallTileSize;
862 } else {
863 return maxTileSize;
864 }
865}
866
867// Given a bitmap, an optional src rect, and a context with a clip and matrix determine what
868// pixels from the bitmap are necessary.
869static void determine_clipped_src_rect(const GrContext* context,
870 const SkBitmap& bitmap,
871 const SkRect* srcRectPtr,
872 SkIRect* clippedSrcIRect) {
873 const GrClipData* clip = context->getClip();
874 clip->getConservativeBounds(context->getRenderTarget(), clippedSrcIRect, NULL);
875 SkMatrix inv;
876 if (!context->getMatrix().invert(&inv)) {
877 clippedSrcIRect->setEmpty();
878 return;
879 }
880 SkRect clippedSrcRect = SkRect::Make(*clippedSrcIRect);
881 inv.mapRect(&clippedSrcRect);
882 if (NULL != srcRectPtr) {
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +0000883 // we've setup src space 0,0 to map to the top left of the src rect.
884 clippedSrcRect.offset(srcRectPtr->fLeft, srcRectPtr->fTop);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000885 if (!clippedSrcRect.intersect(*srcRectPtr)) {
886 clippedSrcIRect->setEmpty();
887 return;
888 }
889 }
890 clippedSrcRect.roundOut(clippedSrcIRect);
891 SkIRect bmpBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
892 if (!clippedSrcIRect->intersect(bmpBounds)) {
893 clippedSrcIRect->setEmpty();
894 }
895}
896
897bool SkGpuDevice::shouldTileBitmap(const SkBitmap& bitmap,
898 const GrTextureParams& params,
899 const SkRect* srcRectPtr,
900 int maxTileSize,
901 int* tileSize,
902 SkIRect* clippedSrcRect) const {
903 // if bitmap is explictly texture backed then just use the texture
904 if (NULL != bitmap.getTexture()) {
905 return false;
906 }
907
908 // if it's larger than the max tile size, then we have no choice but tiling.
909 if (bitmap.width() > maxTileSize || bitmap.height() > maxTileSize) {
910 determine_clipped_src_rect(fContext, bitmap, srcRectPtr, clippedSrcRect);
911 *tileSize = determine_tile_size(bitmap, *clippedSrcRect, maxTileSize);
912 return true;
913 }
914
915 if (bitmap.width() * bitmap.height() < 4 * kBmpSmallTileSize * kBmpSmallTileSize) {
916 return false;
917 }
918
919 // if the entire texture is already in our cache then no reason to tile it
920 if (GrIsBitmapInCache(fContext, bitmap, &params)) {
921 return false;
922 }
923
924 // At this point we know we could do the draw by uploading the entire bitmap
925 // as a texture. However, if the texture would be large compared to the
926 // cache size and we don't require most of it for this draw then tile to
927 // reduce the amount of upload and cache spill.
928
929 // assumption here is that sw bitmap size is a good proxy for its size as
930 // a texture
931 size_t bmpSize = bitmap.getSize();
932 size_t cacheSize;
commit-bot@chromium.org95c20032014-05-09 14:29:32 +0000933 fContext->getResourceCacheLimits(NULL, &cacheSize);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000934 if (bmpSize < cacheSize / 2) {
935 return false;
936 }
937
938 // Figure out how much of the src we will need based on the src rect and clipping.
939 determine_clipped_src_rect(fContext, bitmap, srcRectPtr, clippedSrcRect);
940 *tileSize = kBmpSmallTileSize; // already know whole bitmap fits in one max sized tile.
941 size_t usedTileBytes = get_tile_count(*clippedSrcRect, kBmpSmallTileSize) *
942 kBmpSmallTileSize * kBmpSmallTileSize;
943
944 return usedTileBytes < 2 * bmpSize;
945}
946
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +0000947void SkGpuDevice::drawBitmap(const SkDraw& origDraw,
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000948 const SkBitmap& bitmap,
949 const SkMatrix& m,
950 const SkPaint& paint) {
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +0000951 SkMatrix concat;
952 SkTCopyOnFirstWrite<SkDraw> draw(origDraw);
953 if (!m.isIdentity()) {
954 concat.setConcat(*draw->fMatrix, m);
955 draw.writable()->fMatrix = &concat;
956 }
957 this->drawBitmapCommon(*draw, bitmap, NULL, NULL, paint, SkCanvas::kNone_DrawBitmapRectFlag);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000958}
959
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +0000960// This method outsets 'iRect' by 'outset' all around and then clamps its extents to
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000961// 'clamp'. 'offset' is adjusted to remain positioned over the top-left corner
962// of 'iRect' for all possible outsets/clamps.
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +0000963static inline void clamped_outset_with_offset(SkIRect* iRect,
964 int outset,
965 SkPoint* offset,
966 const SkIRect& clamp) {
967 iRect->outset(outset, outset);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000968
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +0000969 int leftClampDelta = clamp.fLeft - iRect->fLeft;
970 if (leftClampDelta > 0) {
971 offset->fX -= outset - leftClampDelta;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000972 iRect->fLeft = clamp.fLeft;
973 } else {
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +0000974 offset->fX -= outset;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000975 }
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +0000976
977 int topClampDelta = clamp.fTop - iRect->fTop;
978 if (topClampDelta > 0) {
979 offset->fY -= outset - topClampDelta;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000980 iRect->fTop = clamp.fTop;
981 } else {
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +0000982 offset->fY -= outset;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000983 }
984
985 if (iRect->fRight > clamp.fRight) {
986 iRect->fRight = clamp.fRight;
987 }
988 if (iRect->fBottom > clamp.fBottom) {
989 iRect->fBottom = clamp.fBottom;
990 }
991}
992
commit-bot@chromium.orga17773f2014-05-09 13:53:38 +0000993static bool has_aligned_samples(const SkRect& srcRect,
994 const SkRect& transformedRect) {
995 // detect pixel disalignment
996 if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) -
997 transformedRect.left()) < COLOR_BLEED_TOLERANCE &&
998 SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) -
999 transformedRect.top()) < COLOR_BLEED_TOLERANCE &&
1000 SkScalarAbs(transformedRect.width() - srcRect.width()) <
1001 COLOR_BLEED_TOLERANCE &&
1002 SkScalarAbs(transformedRect.height() - srcRect.height()) <
1003 COLOR_BLEED_TOLERANCE) {
1004 return true;
1005 }
1006 return false;
1007}
1008
1009static bool may_color_bleed(const SkRect& srcRect,
1010 const SkRect& transformedRect,
1011 const SkMatrix& m) {
1012 // Only gets called if has_aligned_samples returned false.
1013 // So we can assume that sampling is axis aligned but not texel aligned.
1014 SkASSERT(!has_aligned_samples(srcRect, transformedRect));
1015 SkRect innerSrcRect(srcRect), innerTransformedRect,
1016 outerTransformedRect(transformedRect);
1017 innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf);
1018 m.mapRect(&innerTransformedRect, innerSrcRect);
1019
1020 // The gap between outerTransformedRect and innerTransformedRect
1021 // represents the projection of the source border area, which is
1022 // problematic for color bleeding. We must check whether any
1023 // destination pixels sample the border area.
1024 outerTransformedRect.inset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE);
1025 innerTransformedRect.outset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE);
1026 SkIRect outer, inner;
1027 outerTransformedRect.round(&outer);
1028 innerTransformedRect.round(&inner);
1029 // If the inner and outer rects round to the same result, it means the
1030 // border does not overlap any pixel centers. Yay!
1031 return inner != outer;
1032}
1033
1034static bool needs_texture_domain(const SkBitmap& bitmap,
1035 const SkRect& srcRect,
1036 GrTextureParams &params,
1037 const SkMatrix& contextMatrix,
1038 bool bicubic) {
1039 bool needsTextureDomain = false;
1040
1041 if (bicubic || params.filterMode() != GrTextureParams::kNone_FilterMode) {
1042 // Need texture domain if drawing a sub rect
1043 needsTextureDomain = srcRect.width() < bitmap.width() ||
1044 srcRect.height() < bitmap.height();
1045 if (!bicubic && needsTextureDomain && contextMatrix.rectStaysRect()) {
1046 // sampling is axis-aligned
1047 SkRect transformedRect;
1048 contextMatrix.mapRect(&transformedRect, srcRect);
1049
1050 if (has_aligned_samples(srcRect, transformedRect)) {
1051 params.setFilterMode(GrTextureParams::kNone_FilterMode);
1052 needsTextureDomain = false;
1053 } else {
1054 needsTextureDomain = may_color_bleed(srcRect, transformedRect, contextMatrix);
1055 }
1056 }
1057 }
1058 return needsTextureDomain;
1059}
1060
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001061void SkGpuDevice::drawBitmapCommon(const SkDraw& draw,
1062 const SkBitmap& bitmap,
1063 const SkRect* srcRectPtr,
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001064 const SkSize* dstSizePtr,
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001065 const SkPaint& paint,
1066 SkCanvas::DrawBitmapRectFlags flags) {
1067 CHECK_SHOULD_DRAW(draw, false);
1068
1069 SkRect srcRect;
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001070 SkSize dstSize;
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001071 // If there is no src rect, or the src rect contains the entire bitmap then we're effectively
1072 // in the (easier) bleed case, so update flags.
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001073 if (NULL == srcRectPtr) {
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001074 SkScalar w = SkIntToScalar(bitmap.width());
1075 SkScalar h = SkIntToScalar(bitmap.height());
1076 dstSize.fWidth = w;
1077 dstSize.fHeight = h;
1078 srcRect.set(0, 0, w, h);
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001079 flags = (SkCanvas::DrawBitmapRectFlags) (flags | SkCanvas::kBleed_DrawBitmapRectFlag);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001080 } else {
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001081 SkASSERT(NULL != dstSizePtr);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001082 srcRect = *srcRectPtr;
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001083 dstSize = *dstSizePtr;
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001084 if (srcRect.fLeft <= 0 && srcRect.fTop <= 0 &&
1085 srcRect.fRight >= bitmap.width() && srcRect.fBottom >= bitmap.height()) {
1086 flags = (SkCanvas::DrawBitmapRectFlags) (flags | SkCanvas::kBleed_DrawBitmapRectFlag);
1087 }
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001088 }
1089
1090 if (paint.getMaskFilter()){
1091 // Convert the bitmap to a shader so that the rect can be drawn
1092 // through drawRect, which supports mask filters.
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001093 SkBitmap tmp; // subset of bitmap, if necessary
1094 const SkBitmap* bitmapPtr = &bitmap;
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001095 SkMatrix localM;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001096 if (NULL != srcRectPtr) {
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001097 localM.setTranslate(-srcRectPtr->fLeft, -srcRectPtr->fTop);
1098 localM.postScale(dstSize.fWidth / srcRectPtr->width(),
1099 dstSize.fHeight / srcRectPtr->height());
commit-bot@chromium.orgd6ca4ac2013-11-22 20:34:59 +00001100 // In bleed mode we position and trim the bitmap based on the src rect which is
1101 // already accounted for in 'm' and 'srcRect'. In clamp mode we need to chop out
1102 // the desired portion of the bitmap and then update 'm' and 'srcRect' to
1103 // compensate.
1104 if (!(SkCanvas::kBleed_DrawBitmapRectFlag & flags)) {
1105 SkIRect iSrc;
1106 srcRect.roundOut(&iSrc);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001107
commit-bot@chromium.orgd6ca4ac2013-11-22 20:34:59 +00001108 SkPoint offset = SkPoint::Make(SkIntToScalar(iSrc.fLeft),
1109 SkIntToScalar(iSrc.fTop));
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001110
commit-bot@chromium.orgd6ca4ac2013-11-22 20:34:59 +00001111 if (!bitmap.extractSubset(&tmp, iSrc)) {
1112 return; // extraction failed
1113 }
1114 bitmapPtr = &tmp;
1115 srcRect.offset(-offset.fX, -offset.fY);
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001116
commit-bot@chromium.orgd6ca4ac2013-11-22 20:34:59 +00001117 // The source rect has changed so update the matrix
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001118 localM.preTranslate(offset.fX, offset.fY);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001119 }
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001120 } else {
1121 localM.reset();
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001122 }
1123
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001124 SkPaint paintWithShader(paint);
1125 paintWithShader.setShader(SkShader::CreateBitmapShader(*bitmapPtr,
commit-bot@chromium.org9c9005a2014-04-28 14:55:39 +00001126 SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &localM))->unref();
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001127 SkRect dstRect = {0, 0, dstSize.fWidth, dstSize.fHeight};
1128 this->drawRect(draw, dstRect, paintWithShader);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001129
1130 return;
1131 }
1132
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001133 // If there is no mask filter than it is OK to handle the src rect -> dst rect scaling using
1134 // the view matrix rather than a local matrix.
1135 SkMatrix m;
1136 m.setScale(dstSize.fWidth / srcRect.width(),
1137 dstSize.fHeight / srcRect.height());
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001138 fContext->concatMatrix(m);
1139
1140 GrTextureParams params;
1141 SkPaint::FilterLevel paintFilterLevel = paint.getFilterLevel();
1142 GrTextureParams::FilterMode textureFilterMode;
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001143
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001144 bool doBicubic = false;
1145
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001146 switch(paintFilterLevel) {
1147 case SkPaint::kNone_FilterLevel:
1148 textureFilterMode = GrTextureParams::kNone_FilterMode;
1149 break;
1150 case SkPaint::kLow_FilterLevel:
1151 textureFilterMode = GrTextureParams::kBilerp_FilterMode;
1152 break;
1153 case SkPaint::kMedium_FilterLevel:
commit-bot@chromium.org18786512014-05-20 14:53:45 +00001154 if (fContext->getMatrix().getMinScale() < SK_Scalar1) {
commit-bot@chromium.org79b7eee2013-12-16 21:02:29 +00001155 textureFilterMode = GrTextureParams::kMipMap_FilterMode;
1156 } else {
1157 // Don't trigger MIP level generation unnecessarily.
1158 textureFilterMode = GrTextureParams::kBilerp_FilterMode;
1159 }
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001160 break;
commit-bot@chromium.org79b7eee2013-12-16 21:02:29 +00001161 case SkPaint::kHigh_FilterLevel:
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001162 // Minification can look bad with the bicubic effect.
commit-bot@chromium.org9927bd32014-05-20 17:51:13 +00001163 doBicubic =
1164 GrBicubicEffect::ShouldUseBicubic(fContext->getMatrix(), &textureFilterMode);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001165 break;
1166 default:
1167 SkErrorInternals::SetError( kInvalidPaint_SkError,
1168 "Sorry, I don't understand the filtering "
1169 "mode you asked for. Falling back to "
1170 "MIPMaps.");
1171 textureFilterMode = GrTextureParams::kMipMap_FilterMode;
1172 break;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001173 }
1174
commit-bot@chromium.org9927bd32014-05-20 17:51:13 +00001175 int tileFilterPad;
1176 if (doBicubic) {
1177 tileFilterPad = GrBicubicEffect::kFilterTexelPad;
1178 } else if (GrTextureParams::kNone_FilterMode == textureFilterMode) {
1179 tileFilterPad = 0;
1180 } else {
1181 tileFilterPad = 1;
1182 }
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001183 params.setFilterMode(textureFilterMode);
1184
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001185 int maxTileSize = fContext->getMaxTextureSize() - 2 * tileFilterPad;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001186 int tileSize;
1187
1188 SkIRect clippedSrcRect;
1189 if (this->shouldTileBitmap(bitmap, params, srcRectPtr, maxTileSize, &tileSize,
1190 &clippedSrcRect)) {
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001191 this->drawTiledBitmap(bitmap, srcRect, clippedSrcRect, params, paint, flags, tileSize,
1192 doBicubic);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001193 } else {
1194 // take the simple case
commit-bot@chromium.orga17773f2014-05-09 13:53:38 +00001195 bool needsTextureDomain = needs_texture_domain(bitmap,
1196 srcRect,
1197 params,
1198 fContext->getMatrix(),
1199 doBicubic);
1200 this->internalDrawBitmap(bitmap,
1201 srcRect,
1202 params,
1203 paint,
1204 flags,
1205 doBicubic,
1206 needsTextureDomain);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001207 }
1208}
1209
1210// Break 'bitmap' into several tiles to draw it since it has already
1211// been determined to be too large to fit in VRAM
1212void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap,
1213 const SkRect& srcRect,
1214 const SkIRect& clippedSrcIRect,
1215 const GrTextureParams& params,
1216 const SkPaint& paint,
1217 SkCanvas::DrawBitmapRectFlags flags,
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001218 int tileSize,
1219 bool bicubic) {
commit-bot@chromium.org9d5e3f12014-05-01 21:23:19 +00001220 // The following pixel lock is technically redundant, but it is desirable
1221 // to lock outside of the tile loop to prevent redecoding the whole image
1222 // at each tile in cases where 'bitmap' holds an SkDiscardablePixelRef that
1223 // is larger than the limit of the discardable memory pool.
1224 SkAutoLockPixels alp(bitmap);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001225 SkRect clippedSrcRect = SkRect::Make(clippedSrcIRect);
1226
1227 int nx = bitmap.width() / tileSize;
1228 int ny = bitmap.height() / tileSize;
1229 for (int x = 0; x <= nx; x++) {
1230 for (int y = 0; y <= ny; y++) {
1231 SkRect tileR;
1232 tileR.set(SkIntToScalar(x * tileSize),
1233 SkIntToScalar(y * tileSize),
1234 SkIntToScalar((x + 1) * tileSize),
1235 SkIntToScalar((y + 1) * tileSize));
1236
1237 if (!SkRect::Intersects(tileR, clippedSrcRect)) {
1238 continue;
1239 }
1240
1241 if (!tileR.intersect(srcRect)) {
1242 continue;
1243 }
1244
1245 SkBitmap tmpB;
1246 SkIRect iTileR;
1247 tileR.roundOut(&iTileR);
1248 SkPoint offset = SkPoint::Make(SkIntToScalar(iTileR.fLeft),
1249 SkIntToScalar(iTileR.fTop));
1250
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001251 // Adjust the context matrix to draw at the right x,y in device space
1252 SkMatrix tmpM;
1253 GrContext::AutoMatrix am;
1254 tmpM.setTranslate(offset.fX - srcRect.fLeft, offset.fY - srcRect.fTop);
1255 am.setPreConcat(fContext, tmpM);
1256
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001257 if (SkPaint::kNone_FilterLevel != paint.getFilterLevel() || bicubic) {
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001258 SkIRect iClampRect;
1259
1260 if (SkCanvas::kBleed_DrawBitmapRectFlag & flags) {
1261 // In bleed mode we want to always expand the tile on all edges
1262 // but stay within the bitmap bounds
1263 iClampRect = SkIRect::MakeWH(bitmap.width(), bitmap.height());
1264 } else {
1265 // In texture-domain/clamp mode we only want to expand the
1266 // tile on edges interior to "srcRect" (i.e., we want to
1267 // not bleed across the original clamped edges)
1268 srcRect.roundOut(&iClampRect);
1269 }
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001270 int outset = bicubic ? GrBicubicEffect::kFilterTexelPad : 1;
1271 clamped_outset_with_offset(&iTileR, outset, &offset, iClampRect);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001272 }
1273
1274 if (bitmap.extractSubset(&tmpB, iTileR)) {
1275 // now offset it to make it "local" to our tmp bitmap
1276 tileR.offset(-offset.fX, -offset.fY);
commit-bot@chromium.orga17773f2014-05-09 13:53:38 +00001277 GrTextureParams paramsTemp = params;
1278 bool needsTextureDomain = needs_texture_domain(bitmap,
1279 srcRect,
1280 paramsTemp,
1281 fContext->getMatrix(),
1282 bicubic);
1283 this->internalDrawBitmap(tmpB,
1284 tileR,
1285 paramsTemp,
1286 paint,
1287 flags,
1288 bicubic,
1289 needsTextureDomain);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001290 }
1291 }
1292 }
1293}
1294
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001295
1296/*
1297 * This is called by drawBitmap(), which has to handle images that may be too
1298 * large to be represented by a single texture.
1299 *
1300 * internalDrawBitmap assumes that the specified bitmap will fit in a texture
1301 * and that non-texture portion of the GrPaint has already been setup.
1302 */
1303void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap,
1304 const SkRect& srcRect,
1305 const GrTextureParams& params,
1306 const SkPaint& paint,
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001307 SkCanvas::DrawBitmapRectFlags flags,
commit-bot@chromium.orga17773f2014-05-09 13:53:38 +00001308 bool bicubic,
1309 bool needsTextureDomain) {
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001310 SkASSERT(bitmap.width() <= fContext->getMaxTextureSize() &&
1311 bitmap.height() <= fContext->getMaxTextureSize());
1312
1313 GrTexture* texture;
1314 SkAutoCachedTexture act(this, bitmap, &params, &texture);
1315 if (NULL == texture) {
1316 return;
1317 }
1318
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001319 SkRect dstRect = {0, 0, srcRect.width(), srcRect.height() };
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001320 SkRect paintRect;
1321 SkScalar wInv = SkScalarInvert(SkIntToScalar(texture->width()));
1322 SkScalar hInv = SkScalarInvert(SkIntToScalar(texture->height()));
1323 paintRect.setLTRB(SkScalarMul(srcRect.fLeft, wInv),
1324 SkScalarMul(srcRect.fTop, hInv),
1325 SkScalarMul(srcRect.fRight, wInv),
1326 SkScalarMul(srcRect.fBottom, hInv));
1327
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001328 SkRect textureDomain = SkRect::MakeEmpty();
1329 SkAutoTUnref<GrEffectRef> effect;
commit-bot@chromium.orga17773f2014-05-09 13:53:38 +00001330 if (needsTextureDomain && !(flags & SkCanvas::kBleed_DrawBitmapRectFlag)) {
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001331 // Use a constrained texture domain to avoid color bleeding
1332 SkScalar left, top, right, bottom;
1333 if (srcRect.width() > SK_Scalar1) {
1334 SkScalar border = SK_ScalarHalf / texture->width();
1335 left = paintRect.left() + border;
1336 right = paintRect.right() - border;
1337 } else {
1338 left = right = SkScalarHalf(paintRect.left() + paintRect.right());
1339 }
1340 if (srcRect.height() > SK_Scalar1) {
1341 SkScalar border = SK_ScalarHalf / texture->height();
1342 top = paintRect.top() + border;
1343 bottom = paintRect.bottom() - border;
1344 } else {
1345 top = bottom = SkScalarHalf(paintRect.top() + paintRect.bottom());
1346 }
1347 textureDomain.setLTRB(left, top, right, bottom);
commit-bot@chromium.org7d7f3142013-12-16 15:18:11 +00001348 if (bicubic) {
1349 effect.reset(GrBicubicEffect::Create(texture, SkMatrix::I(), textureDomain));
1350 } else {
1351 effect.reset(GrTextureDomainEffect::Create(texture,
1352 SkMatrix::I(),
1353 textureDomain,
1354 GrTextureDomain::kClamp_Mode,
1355 params.filterMode()));
1356 }
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001357 } else if (bicubic) {
commit-bot@chromium.orgbc91fd72013-12-10 12:53:39 +00001358 SkASSERT(GrTextureParams::kNone_FilterMode == params.filterMode());
1359 SkShader::TileMode tileModes[2] = { params.getTileModeX(), params.getTileModeY() };
1360 effect.reset(GrBicubicEffect::Create(texture, SkMatrix::I(), tileModes));
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001361 } else {
1362 effect.reset(GrSimpleTextureEffect::Create(texture, SkMatrix::I(), params));
1363 }
1364
1365 // Construct a GrPaint by setting the bitmap texture as the first effect and then configuring
1366 // the rest from the SkPaint.
1367 GrPaint grPaint;
1368 grPaint.addColorEffect(effect);
reed0689d7b2014-06-14 05:30:20 -07001369 bool alphaOnly = !(kAlpha_8_SkColorType == bitmap.colorType());
dandov9de5b512014-06-10 14:38:28 -07001370 GrColor grColor = (alphaOnly) ? SkColor2GrColorJustAlpha(paint.getColor()) :
1371 SkColor2GrColor(paint.getColor());
1372 SkPaint2GrPaintNoShader(this->context(), paint, grColor, false, &grPaint);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001373
1374 fContext->drawRectToRect(grPaint, dstRect, paintRect, NULL);
1375}
1376
1377static bool filter_texture(SkBaseDevice* device, GrContext* context,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +00001378 GrTexture* texture, const SkImageFilter* filter,
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001379 int w, int h, const SkImageFilter::Context& ctx,
1380 SkBitmap* result, SkIPoint* offset) {
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001381 SkASSERT(filter);
1382 SkDeviceImageFilterProxy proxy(device);
1383
1384 if (filter->canFilterImageGPU()) {
1385 // Save the render target and set it to NULL, so we don't accidentally draw to it in the
1386 // filter. Also set the clip wide open and the matrix to identity.
1387 GrContext::AutoWideOpenIdentityDraw awo(context, NULL);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001388 return filter->filterImageGPU(&proxy, wrap_texture(texture), ctx, result, offset);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001389 } else {
1390 return false;
1391 }
1392}
1393
1394void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
1395 int left, int top, const SkPaint& paint) {
1396 // drawSprite is defined to be in device coords.
1397 CHECK_SHOULD_DRAW(draw, true);
1398
1399 SkAutoLockPixels alp(bitmap, !bitmap.getTexture());
1400 if (!bitmap.getTexture() && !bitmap.readyToDraw()) {
1401 return;
1402 }
1403
1404 int w = bitmap.width();
1405 int h = bitmap.height();
1406
1407 GrTexture* texture;
1408 // draw sprite uses the default texture params
1409 SkAutoCachedTexture act(this, bitmap, NULL, &texture);
1410
1411 SkImageFilter* filter = paint.getImageFilter();
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001412 // This bitmap will own the filtered result as a texture.
1413 SkBitmap filteredBitmap;
1414
1415 if (NULL != filter) {
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001416 SkIPoint offset = SkIPoint::Make(0, 0);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001417 SkMatrix matrix(*draw.fMatrix);
1418 matrix.postTranslate(SkIntToScalar(-left), SkIntToScalar(-top));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001419 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +00001420 SkImageFilter::Cache* cache = SkImageFilter::Cache::Create();
1421 SkAutoUnref aur(cache);
1422 SkImageFilter::Context ctx(matrix, clipBounds, cache);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001423 if (filter_texture(this, fContext, texture, filter, w, h, ctx, &filteredBitmap,
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001424 &offset)) {
1425 texture = (GrTexture*) filteredBitmap.getTexture();
1426 w = filteredBitmap.width();
1427 h = filteredBitmap.height();
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001428 left += offset.x();
1429 top += offset.y();
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001430 } else {
1431 return;
1432 }
1433 }
1434
1435 GrPaint grPaint;
1436 grPaint.addColorTextureEffect(texture, SkMatrix::I());
1437
dandov9de5b512014-06-10 14:38:28 -07001438 SkPaint2GrPaintNoShader(this->context(), paint, SkColor2GrColorJustAlpha(paint.getColor()),
1439 false, &grPaint);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001440
1441 fContext->drawRectToRect(grPaint,
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001442 SkRect::MakeXYWH(SkIntToScalar(left),
1443 SkIntToScalar(top),
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001444 SkIntToScalar(w),
1445 SkIntToScalar(h)),
1446 SkRect::MakeXYWH(0,
1447 0,
1448 SK_Scalar1 * w / texture->width(),
1449 SK_Scalar1 * h / texture->height()));
1450}
1451
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001452void SkGpuDevice::drawBitmapRect(const SkDraw& origDraw, const SkBitmap& bitmap,
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001453 const SkRect* src, const SkRect& dst,
1454 const SkPaint& paint,
1455 SkCanvas::DrawBitmapRectFlags flags) {
1456 SkMatrix matrix;
1457 SkRect bitmapBounds, tmpSrc;
1458
1459 bitmapBounds.set(0, 0,
1460 SkIntToScalar(bitmap.width()),
1461 SkIntToScalar(bitmap.height()));
1462
1463 // Compute matrix from the two rectangles
1464 if (NULL != src) {
1465 tmpSrc = *src;
1466 } else {
1467 tmpSrc = bitmapBounds;
1468 }
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001469
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001470 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
1471
1472 // clip the tmpSrc to the bounds of the bitmap. No check needed if src==null.
1473 if (NULL != src) {
1474 if (!bitmapBounds.contains(tmpSrc)) {
1475 if (!tmpSrc.intersect(bitmapBounds)) {
1476 return; // nothing to draw
1477 }
1478 }
1479 }
1480
commit-bot@chromium.orga7d89c82014-01-13 14:47:00 +00001481 SkRect tmpDst;
1482 matrix.mapRect(&tmpDst, tmpSrc);
1483
1484 SkTCopyOnFirstWrite<SkDraw> draw(origDraw);
1485 if (0 != tmpDst.fLeft || 0 != tmpDst.fTop) {
1486 // Translate so that tempDst's top left is at the origin.
1487 matrix = *origDraw.fMatrix;
1488 matrix.preTranslate(tmpDst.fLeft, tmpDst.fTop);
1489 draw.writable()->fMatrix = &matrix;
1490 }
1491 SkSize dstSize;
1492 dstSize.fWidth = tmpDst.width();
1493 dstSize.fHeight = tmpDst.height();
1494
1495 this->drawBitmapCommon(*draw, bitmap, &tmpSrc, &dstSize, paint, flags);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001496}
1497
1498void SkGpuDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device,
1499 int x, int y, const SkPaint& paint) {
1500 // clear of the source device must occur before CHECK_SHOULD_DRAW
1501 SkGpuDevice* dev = static_cast<SkGpuDevice*>(device);
1502 if (dev->fNeedClear) {
1503 // TODO: could check here whether we really need to draw at all
1504 dev->clear(0x0);
1505 }
1506
1507 // drawDevice is defined to be in device coords.
1508 CHECK_SHOULD_DRAW(draw, true);
1509
1510 GrRenderTarget* devRT = dev->accessRenderTarget();
1511 GrTexture* devTex;
1512 if (NULL == (devTex = devRT->asTexture())) {
1513 return;
1514 }
1515
1516 const SkBitmap& bm = dev->accessBitmap(false);
1517 int w = bm.width();
1518 int h = bm.height();
1519
1520 SkImageFilter* filter = paint.getImageFilter();
1521 // This bitmap will own the filtered result as a texture.
1522 SkBitmap filteredBitmap;
1523
1524 if (NULL != filter) {
1525 SkIPoint offset = SkIPoint::Make(0, 0);
1526 SkMatrix matrix(*draw.fMatrix);
1527 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001528 SkIRect clipBounds = SkIRect::MakeWH(devTex->width(), devTex->height());
commit-bot@chromium.orgf7efa502014-04-11 18:57:00 +00001529 SkImageFilter::Cache* cache = SkImageFilter::Cache::Create();
1530 SkAutoUnref aur(cache);
1531 SkImageFilter::Context ctx(matrix, clipBounds, cache);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001532 if (filter_texture(this, fContext, devTex, filter, w, h, ctx, &filteredBitmap,
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001533 &offset)) {
1534 devTex = filteredBitmap.getTexture();
1535 w = filteredBitmap.width();
1536 h = filteredBitmap.height();
1537 x += offset.fX;
1538 y += offset.fY;
1539 } else {
1540 return;
1541 }
1542 }
1543
1544 GrPaint grPaint;
1545 grPaint.addColorTextureEffect(devTex, SkMatrix::I());
1546
dandov9de5b512014-06-10 14:38:28 -07001547 SkPaint2GrPaintNoShader(this->context(), paint, SkColor2GrColorJustAlpha(paint.getColor()),
1548 false, &grPaint);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001549
1550 SkRect dstRect = SkRect::MakeXYWH(SkIntToScalar(x),
1551 SkIntToScalar(y),
1552 SkIntToScalar(w),
1553 SkIntToScalar(h));
1554
1555 // The device being drawn may not fill up its texture (e.g. saveLayer uses approximate
1556 // scratch texture).
1557 SkRect srcRect = SkRect::MakeWH(SK_Scalar1 * w / devTex->width(),
1558 SK_Scalar1 * h / devTex->height());
1559
1560 fContext->drawRectToRect(grPaint, dstRect, srcRect);
1561}
1562
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +00001563bool SkGpuDevice::canHandleImageFilter(const SkImageFilter* filter) {
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001564 return filter->canFilterImageGPU();
1565}
1566
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +00001567bool SkGpuDevice::filterImage(const SkImageFilter* filter, const SkBitmap& src,
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001568 const SkImageFilter::Context& ctx,
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001569 SkBitmap* result, SkIPoint* offset) {
1570 // want explicitly our impl, so guard against a subclass of us overriding it
1571 if (!this->SkGpuDevice::canHandleImageFilter(filter)) {
1572 return false;
1573 }
1574
1575 SkAutoLockPixels alp(src, !src.getTexture());
1576 if (!src.getTexture() && !src.readyToDraw()) {
1577 return false;
1578 }
1579
1580 GrTexture* texture;
1581 // We assume here that the filter will not attempt to tile the src. Otherwise, this cache lookup
1582 // must be pushed upstack.
1583 SkAutoCachedTexture act(this, src, NULL, &texture);
1584
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001585 return filter_texture(this, fContext, texture, filter, src.width(), src.height(), ctx,
1586 result, offset);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001587}
1588
1589///////////////////////////////////////////////////////////////////////////////
1590
1591// must be in SkCanvas::VertexMode order
1592static const GrPrimitiveType gVertexMode2PrimitiveType[] = {
1593 kTriangles_GrPrimitiveType,
1594 kTriangleStrip_GrPrimitiveType,
1595 kTriangleFan_GrPrimitiveType,
1596};
1597
1598void SkGpuDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
1599 int vertexCount, const SkPoint vertices[],
1600 const SkPoint texs[], const SkColor colors[],
1601 SkXfermode* xmode,
1602 const uint16_t indices[], int indexCount,
1603 const SkPaint& paint) {
1604 CHECK_SHOULD_DRAW(draw, false);
1605
commit-bot@chromium.org559a8832014-05-30 10:08:22 +00001606 // If both textures and vertex-colors are NULL, strokes hairlines with the paint's color.
1607 if ((NULL == texs || NULL == paint.getShader()) && NULL == colors) {
1608 texs = NULL;
1609 SkPaint copy(paint);
1610 copy.setStyle(SkPaint::kStroke_Style);
1611 copy.setStrokeWidth(0);
1612
1613 VertState state(vertexCount, indices, indexCount);
1614 VertState::Proc vertProc = state.chooseProc(vmode);
1615
1616 SkPoint* pts = new SkPoint[vertexCount * 6];
1617 int i = 0;
1618 while (vertProc(&state)) {
1619 pts[i] = vertices[state.f0];
1620 pts[i + 1] = vertices[state.f1];
1621 pts[i + 2] = vertices[state.f1];
1622 pts[i + 3] = vertices[state.f2];
1623 pts[i + 4] = vertices[state.f2];
1624 pts[i + 5] = vertices[state.f0];
1625 i += 6;
1626 }
1627 draw.drawPoints(SkCanvas::kLines_PointMode, i, pts, copy, true);
1628 return;
1629 }
1630
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001631 GrPaint grPaint;
1632 // we ignore the shader if texs is null.
1633 if (NULL == texs) {
dandov9de5b512014-06-10 14:38:28 -07001634 SkPaint2GrPaintNoShader(this->context(), paint, SkColor2GrColor(paint.getColor()),
1635 NULL == colors, &grPaint);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001636 } else {
commit-bot@chromium.org3595f882014-05-19 19:35:57 +00001637 SkPaint2GrPaintShader(this->context(), paint, NULL == colors, &grPaint);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001638 }
1639
mtklein2583b622014-06-04 08:20:41 -07001640#if 0
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001641 if (NULL != xmode && NULL != texs && NULL != colors) {
1642 if (!SkXfermode::IsMode(xmode, SkXfermode::kModulate_Mode)) {
1643 SkDebugf("Unsupported vertex-color/texture xfer mode.\n");
mtklein2583b622014-06-04 08:20:41 -07001644 return;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001645 }
1646 }
mtklein2583b622014-06-04 08:20:41 -07001647#endif
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001648
1649 SkAutoSTMalloc<128, GrColor> convertedColors(0);
1650 if (NULL != colors) {
1651 // need to convert byte order and from non-PM to PM
1652 convertedColors.reset(vertexCount);
commit-bot@chromium.orgc93e6812014-05-23 08:09:26 +00001653 SkColor color;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001654 for (int i = 0; i < vertexCount; ++i) {
commit-bot@chromium.orgc93e6812014-05-23 08:09:26 +00001655 color = colors[i];
1656 if (paint.getAlpha() != 255) {
1657 color = SkColorSetA(color, SkMulDiv255Round(SkColorGetA(color), paint.getAlpha()));
1658 }
1659 convertedColors[i] = SkColor2GrColor(color);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001660 }
1661 colors = convertedColors.get();
1662 }
1663 fContext->drawVertices(grPaint,
1664 gVertexMode2PrimitiveType[vmode],
1665 vertexCount,
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001666 vertices,
1667 texs,
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001668 colors,
1669 indices,
1670 indexCount);
1671}
1672
1673///////////////////////////////////////////////////////////////////////////////
1674
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001675void SkGpuDevice::drawText(const SkDraw& draw, const void* text,
1676 size_t byteLength, SkScalar x, SkScalar y,
1677 const SkPaint& paint) {
1678 CHECK_SHOULD_DRAW(draw, false);
1679
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +00001680 if (fMainTextContext->canDraw(paint)) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +00001681 GrPaint grPaint;
commit-bot@chromium.org3595f882014-05-19 19:35:57 +00001682 SkPaint2GrPaintShader(this->context(), paint, true, &grPaint);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +00001683
1684 SkDEBUGCODE(this->validate();)
1685
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +00001686 fMainTextContext->drawText(grPaint, paint, (const char *)text, byteLength, x, y);
1687 } else if (fFallbackTextContext && fFallbackTextContext->canDraw(paint)) {
commit-bot@chromium.org9f94b912014-01-30 15:22:54 +00001688 GrPaint grPaint;
commit-bot@chromium.org3595f882014-05-19 19:35:57 +00001689 SkPaint2GrPaintShader(this->context(), paint, true, &grPaint);
commit-bot@chromium.org9f94b912014-01-30 15:22:54 +00001690
1691 SkDEBUGCODE(this->validate();)
1692
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +00001693 fFallbackTextContext->drawText(grPaint, paint, (const char *)text, byteLength, x, y);
commit-bot@chromium.org9f94b912014-01-30 15:22:54 +00001694 } else {
1695 // this guy will just call our drawPath()
1696 draw.drawText_asPaths((const char*)text, byteLength, x, y, paint);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001697 }
1698}
1699
1700void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text,
1701 size_t byteLength, const SkScalar pos[],
1702 SkScalar constY, int scalarsPerPos,
1703 const SkPaint& paint) {
egdanielbbcb38d2014-06-19 10:19:29 -07001704 GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice::drawPosText", fContext);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001705 CHECK_SHOULD_DRAW(draw, false);
1706
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +00001707 if (fMainTextContext->canDraw(paint)) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +00001708 GrPaint grPaint;
commit-bot@chromium.org3595f882014-05-19 19:35:57 +00001709 SkPaint2GrPaintShader(this->context(), paint, true, &grPaint);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +00001710
1711 SkDEBUGCODE(this->validate();)
1712
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +00001713 fMainTextContext->drawPosText(grPaint, paint, (const char *)text, byteLength, pos,
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +00001714 constY, scalarsPerPos);
1715 } else if (fFallbackTextContext && fFallbackTextContext->canDraw(paint)) {
commit-bot@chromium.org9f94b912014-01-30 15:22:54 +00001716 GrPaint grPaint;
commit-bot@chromium.org3595f882014-05-19 19:35:57 +00001717 SkPaint2GrPaintShader(this->context(), paint, true, &grPaint);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +00001718
commit-bot@chromium.org9f94b912014-01-30 15:22:54 +00001719 SkDEBUGCODE(this->validate();)
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +00001720
1721 fFallbackTextContext->drawPosText(grPaint, paint, (const char *)text, byteLength, pos,
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +00001722 constY, scalarsPerPos);
commit-bot@chromium.org9f94b912014-01-30 15:22:54 +00001723 } else {
1724 draw.drawPosText_asPaths((const char*)text, byteLength, pos, constY,
1725 scalarsPerPos, paint);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001726 }
1727}
1728
1729void SkGpuDevice::drawTextOnPath(const SkDraw& draw, const void* text,
1730 size_t len, const SkPath& path,
1731 const SkMatrix* m, const SkPaint& paint) {
1732 CHECK_SHOULD_DRAW(draw, false);
1733
1734 SkASSERT(draw.fDevice == this);
1735 draw.drawTextOnPath((const char*)text, len, path, m, paint);
1736}
1737
1738///////////////////////////////////////////////////////////////////////////////
1739
1740bool SkGpuDevice::filterTextFlags(const SkPaint& paint, TextFlags* flags) {
1741 if (!paint.isLCDRenderText()) {
1742 // we're cool with the paint as is
1743 return false;
1744 }
1745
1746 if (paint.getShader() ||
1747 paint.getXfermode() || // unless its srcover
1748 paint.getMaskFilter() ||
1749 paint.getRasterizer() ||
1750 paint.getColorFilter() ||
1751 paint.getPathEffect() ||
1752 paint.isFakeBoldText() ||
1753 paint.getStyle() != SkPaint::kFill_Style) {
1754 // turn off lcd
1755 flags->fFlags = paint.getFlags() & ~SkPaint::kLCDRenderText_Flag;
1756 flags->fHinting = paint.getHinting();
1757 return true;
1758 }
1759 // we're cool with the paint as is
1760 return false;
1761}
1762
1763void SkGpuDevice::flush() {
1764 DO_DEFERRED_CLEAR();
1765 fContext->resolveRenderTarget(fRenderTarget);
1766}
1767
1768///////////////////////////////////////////////////////////////////////////////
1769
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001770SkBaseDevice* SkGpuDevice::onCreateDevice(const SkImageInfo& info, Usage usage) {
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001771 GrTextureDesc desc;
1772 desc.fConfig = fRenderTarget->config();
1773 desc.fFlags = kRenderTarget_GrTextureFlagBit;
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001774 desc.fWidth = info.width();
1775 desc.fHeight = info.height();
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001776 desc.fSampleCnt = fRenderTarget->numSamples();
1777
1778 SkAutoTUnref<GrTexture> texture;
1779 // Skia's convention is to only clear a device if it is non-opaque.
commit-bot@chromium.orgd8a57af2014-03-19 21:19:16 +00001780 unsigned flags = info.isOpaque() ? 0 : kNeedClear_Flag;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001781
1782#if CACHE_COMPATIBLE_DEVICE_TEXTURES
1783 // layers are never draw in repeat modes, so we can request an approx
1784 // match and ignore any padding.
commit-bot@chromium.orgd8a57af2014-03-19 21:19:16 +00001785 flags |= kCached_Flag;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001786 const GrContext::ScratchTexMatch match = (kSaveLayer_Usage == usage) ?
1787 GrContext::kApprox_ScratchTexMatch :
1788 GrContext::kExact_ScratchTexMatch;
1789 texture.reset(fContext->lockAndRefScratchTexture(desc, match));
1790#else
1791 texture.reset(fContext->createUncachedTexture(desc, NULL, 0));
1792#endif
1793 if (NULL != texture.get()) {
commit-bot@chromium.orgd8a57af2014-03-19 21:19:16 +00001794 return SkGpuDevice::Create(texture, flags);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001795 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001796 GrPrintf("---- failed to create compatible device texture [%d %d]\n",
1797 info.width(), info.height());
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001798 return NULL;
1799 }
1800}
1801
reed@google.com76f10a32014-02-05 15:32:21 +00001802SkSurface* SkGpuDevice::newSurface(const SkImageInfo& info) {
1803 return SkSurface::NewRenderTarget(fContext, info, fRenderTarget->numSamples());
1804}
1805
robertphillips9b14f262014-06-04 05:40:44 -07001806void SkGpuDevice::EXPERIMENTAL_optimize(const SkPicture* picture) {
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +00001807 SkPicture::AccelData::Key key = GPUAccelData::ComputeAccelDataKey();
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00001808
commit-bot@chromium.org8ec8bab2014-05-14 13:11:48 +00001809 const SkPicture::AccelData* existing = picture->EXPERIMENTAL_getAccelData(key);
1810 if (NULL != existing) {
1811 return;
1812 }
1813
commit-bot@chromium.org8fd93822014-05-06 13:43:22 +00001814 SkAutoTUnref<GPUAccelData> data(SkNEW_ARGS(GPUAccelData, (key)));
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00001815
1816 picture->EXPERIMENTAL_addAccelData(data);
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +00001817
1818 GatherGPUInfo(picture, data);
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00001819}
1820
robertphillips@google.combeb1af22014-05-07 21:31:09 +00001821static void wrap_texture(GrTexture* texture, int width, int height, SkBitmap* result) {
1822 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001823 result->setInfo(info);
robertphillips@google.combeb1af22014-05-07 21:31:09 +00001824 result->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (info, texture)))->unref();
1825}
1826
robertphillips9b14f262014-06-04 05:40:44 -07001827void SkGpuDevice::EXPERIMENTAL_purge(const SkPicture* picture) {
robertphillips952841b2014-06-30 08:26:50 -07001828 fContext->getLayerCache()->purge(picture);
commit-bot@chromium.orgc8733292014-04-11 15:54:14 +00001829}
1830
robertphillips9b14f262014-06-04 05:40:44 -07001831bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* canvas, const SkPicture* picture) {
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +00001832
robertphillipsdb539902014-07-01 08:47:04 -07001833 if (NULL == picture->fData.get()) {
robertphillips4ec84da2014-06-24 13:10:43 -07001834 return false;
1835 }
1836
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +00001837 SkPicture::AccelData::Key key = GPUAccelData::ComputeAccelDataKey();
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00001838
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +00001839 const SkPicture::AccelData* data = picture->EXPERIMENTAL_getAccelData(key);
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00001840 if (NULL == data) {
1841 return false;
1842 }
1843
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00001844 const GPUAccelData *gpuData = static_cast<const GPUAccelData*>(data);
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +00001845
commit-bot@chromium.org8ec8bab2014-05-14 13:11:48 +00001846 if (0 == gpuData->numSaveLayers()) {
1847 return false;
1848 }
1849
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +00001850 SkAutoTArray<bool> pullForward(gpuData->numSaveLayers());
1851 for (int i = 0; i < gpuData->numSaveLayers(); ++i) {
1852 pullForward[i] = false;
1853 }
1854
robertphillips@google.combeb1af22014-05-07 21:31:09 +00001855 SkRect clipBounds;
1856 if (!canvas->getClipBounds(&clipBounds)) {
1857 return true;
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +00001858 }
robertphillips@google.combeb1af22014-05-07 21:31:09 +00001859 SkIRect query;
1860 clipBounds.roundOut(&query);
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +00001861
robertphillips@google.combeb1af22014-05-07 21:31:09 +00001862 const SkPicture::OperationList& ops = picture->EXPERIMENTAL_getActiveOps(query);
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +00001863
robertphillips@google.combeb1af22014-05-07 21:31:09 +00001864 // This code pre-renders the entire layer since it will be cached and potentially
1865 // reused with different clips (e.g., in different tiles). Because of this the
1866 // clip will not be limiting the size of the pre-rendered layer. kSaveLayerMaxSize
1867 // is used to limit which clips are pre-rendered.
1868 static const int kSaveLayerMaxSize = 256;
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +00001869
robertphillips@google.combeb1af22014-05-07 21:31:09 +00001870 if (ops.valid()) {
1871 // In this case the picture has been generated with a BBH so we use
skia.committer@gmail.comb2c82c92014-05-08 03:05:29 +00001872 // the BBH to limit the pre-rendering to just the layers needed to cover
1873 // the region being drawn
robertphillips@google.combeb1af22014-05-07 21:31:09 +00001874 for (int i = 0; i < ops.numOps(); ++i) {
1875 uint32_t offset = ops.offset(i);
1876
1877 // For now we're saving all the layers in the GPUAccelData so they
skia.committer@gmail.comb2c82c92014-05-08 03:05:29 +00001878 // can be nested. Additionally, the nested layers appear before
robertphillips@google.combeb1af22014-05-07 21:31:09 +00001879 // their parent in the list.
1880 for (int j = 0 ; j < gpuData->numSaveLayers(); ++j) {
1881 const GPUAccelData::SaveLayerInfo& info = gpuData->saveLayerInfo(j);
1882
1883 if (pullForward[j]) {
1884 continue; // already pulling forward
1885 }
1886
1887 if (offset < info.fSaveLayerOpID || offset > info.fRestoreOpID) {
1888 continue; // the op isn't in this range
1889 }
1890
1891 // TODO: once this code is more stable unsuitable layers can
1892 // just be omitted during the optimization stage
1893 if (!info.fValid ||
1894 kSaveLayerMaxSize < info.fSize.fWidth ||
1895 kSaveLayerMaxSize < info.fSize.fHeight ||
1896 info.fIsNested) {
1897 continue; // this layer is unsuitable
1898 }
1899
1900 pullForward[j] = true;
1901 }
1902 }
1903 } else {
1904 // In this case there is no BBH associated with the picture. Pre-render
commit-bot@chromium.orgf97d65d2014-05-08 23:24:05 +00001905 // all the layers that intersect the drawn region
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +00001906 for (int j = 0; j < gpuData->numSaveLayers(); ++j) {
1907 const GPUAccelData::SaveLayerInfo& info = gpuData->saveLayerInfo(j);
1908
commit-bot@chromium.orgf97d65d2014-05-08 23:24:05 +00001909 SkIRect layerRect = SkIRect::MakeXYWH(info.fOffset.fX,
1910 info.fOffset.fY,
1911 info.fSize.fWidth,
1912 info.fSize.fHeight);
1913
1914 if (!SkIRect::Intersects(query, layerRect)) {
1915 continue;
1916 }
1917
robertphillips@google.combeb1af22014-05-07 21:31:09 +00001918 // TODO: once this code is more stable unsuitable layers can
1919 // just be omitted during the optimization stage
1920 if (!info.fValid ||
1921 kSaveLayerMaxSize < info.fSize.fWidth ||
1922 kSaveLayerMaxSize < info.fSize.fHeight ||
1923 info.fIsNested) {
1924 continue;
1925 }
1926
skia.committer@gmail.comb2c82c92014-05-08 03:05:29 +00001927 pullForward[j] = true;
robertphillips@google.combeb1af22014-05-07 21:31:09 +00001928 }
1929 }
1930
robertphillipsdb539902014-07-01 08:47:04 -07001931 SkPictureData::PlaybackReplacements replacements;
robertphillips@google.combeb1af22014-05-07 21:31:09 +00001932
robertphillips4ec84da2014-06-24 13:10:43 -07001933 // Generate the layer and/or ensure it is locked
robertphillips@google.combeb1af22014-05-07 21:31:09 +00001934 for (int i = 0; i < gpuData->numSaveLayers(); ++i) {
1935 if (pullForward[i]) {
1936 GrCachedLayer* layer = fContext->getLayerCache()->findLayerOrCreate(picture, i);
1937
1938 const GPUAccelData::SaveLayerInfo& info = gpuData->saveLayerInfo(i);
1939
robertphillipsdb539902014-07-01 08:47:04 -07001940 SkPictureData::PlaybackReplacements::ReplacementInfo* layerInfo =
robertphillips@google.combeb1af22014-05-07 21:31:09 +00001941 replacements.push();
robertphillips4ec84da2014-06-24 13:10:43 -07001942 layerInfo->fStart = info.fSaveLayerOpID;
1943 layerInfo->fStop = info.fRestoreOpID;
1944 layerInfo->fPos = info.fOffset;
robertphillips@google.combeb1af22014-05-07 21:31:09 +00001945
robertphillips4ec84da2014-06-24 13:10:43 -07001946 GrTextureDesc desc;
1947 desc.fFlags = kRenderTarget_GrTextureFlagBit;
1948 desc.fWidth = info.fSize.fWidth;
1949 desc.fHeight = info.fSize.fHeight;
1950 desc.fConfig = kSkia8888_GrPixelConfig;
1951 // TODO: need to deal with sample count
robertphillips@google.combeb1af22014-05-07 21:31:09 +00001952
robertphillips4ec84da2014-06-24 13:10:43 -07001953 bool needsRendering = !fContext->getLayerCache()->lock(layer, desc);
robertphillips952841b2014-06-30 08:26:50 -07001954 if (NULL == layer->texture()) {
robertphillips4ec84da2014-06-24 13:10:43 -07001955 continue;
1956 }
robertphillips@google.combeb1af22014-05-07 21:31:09 +00001957
robertphillips4ec84da2014-06-24 13:10:43 -07001958 layerInfo->fBM = SkNEW(SkBitmap); // fBM is allocated so ReplacementInfo can be POD
robertphillips952841b2014-06-30 08:26:50 -07001959 wrap_texture(layer->texture(),
1960 layer->rect().isEmpty() ? desc.fWidth : layer->texture()->width(),
1961 layer->rect().isEmpty() ? desc.fHeight : layer->texture()->height(),
1962 layerInfo->fBM);
robertphillips@google.combeb1af22014-05-07 21:31:09 +00001963
robertphillips4ec84da2014-06-24 13:10:43 -07001964 SkASSERT(info.fPaint);
1965 layerInfo->fPaint = info.fPaint;
robertphillips@google.combeb1af22014-05-07 21:31:09 +00001966
robertphillips952841b2014-06-30 08:26:50 -07001967 if (layer->rect().isEmpty()) {
1968 layerInfo->fSrcRect = SkIRect::MakeWH(desc.fWidth, desc.fHeight);
1969 } else {
1970 layerInfo->fSrcRect = SkIRect::MakeXYWH(layer->rect().fLeft,
1971 layer->rect().fTop,
1972 layer->rect().width(),
1973 layer->rect().height());
1974 }
1975
robertphillips4ec84da2014-06-24 13:10:43 -07001976 if (needsRendering) {
1977 SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTargetDirect(
robertphillips952841b2014-06-30 08:26:50 -07001978 layer->texture()->asRenderTarget(),
1979 SkSurface::kStandard_TextRenderMode,
1980 SkSurface::kDontClear_RenderTargetFlag));
robertphillips@google.combeb1af22014-05-07 21:31:09 +00001981
robertphillips4ec84da2014-06-24 13:10:43 -07001982 SkCanvas* canvas = surface->getCanvas();
robertphillips@google.combeb1af22014-05-07 21:31:09 +00001983
robertphillips952841b2014-06-30 08:26:50 -07001984 if (!layer->rect().isEmpty()) {
1985 // Add a rect clip to make sure the rendering doesn't
1986 // extend beyond the boundaries of the atlased sub-rect
1987 SkRect bound = SkRect::MakeXYWH(SkIntToScalar(layer->rect().fLeft),
1988 SkIntToScalar(layer->rect().fTop),
1989 SkIntToScalar(layer->rect().width()),
1990 SkIntToScalar(layer->rect().height()));
1991 canvas->clipRect(bound);
1992 // Since 'clear' doesn't respect the clip we need to draw a rect
1993 // TODO: ensure none of the atlased layers contain a clear call!
1994 SkPaint paint;
1995 paint.setColor(SK_ColorTRANSPARENT);
1996 canvas->drawRect(bound, paint);
1997 } else {
1998 canvas->clear(SK_ColorTRANSPARENT);
1999 }
2000
robertphillips4ec84da2014-06-24 13:10:43 -07002001 canvas->setMatrix(info.fCTM);
robertphillips952841b2014-06-30 08:26:50 -07002002
2003 if (!layer->rect().isEmpty()) {
2004 // info.fCTM maps the layer's top/left to the origin.
2005 // Since this layer is atlased the top/left corner needs
2006 // to be offset to some arbitrary location in the backing
2007 // texture.
2008 canvas->translate(SkIntToScalar(layer->rect().fLeft),
2009 SkIntToScalar(layer->rect().fTop));
2010 }
robertphillips@google.combeb1af22014-05-07 21:31:09 +00002011
robertphillipsdb539902014-07-01 08:47:04 -07002012 picture->fData->setDrawLimits(info.fSaveLayerOpID, info.fRestoreOpID);
2013 picture->fData->draw(*canvas, NULL);
2014 picture->fData->setDrawLimits(0, 0);
robertphillips952841b2014-06-30 08:26:50 -07002015
robertphillips4ec84da2014-06-24 13:10:43 -07002016 canvas->flush();
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +00002017 }
2018 }
2019 }
2020
robertphillips@google.combeb1af22014-05-07 21:31:09 +00002021 // Playback using new layers
robertphillipsdb539902014-07-01 08:47:04 -07002022 picture->fData->setReplacements(&replacements);
2023 picture->fData->draw(*canvas, NULL);
2024 picture->fData->setReplacements(NULL);
robertphillips@google.combeb1af22014-05-07 21:31:09 +00002025
robertphillips4ec84da2014-06-24 13:10:43 -07002026 // unlock the layers
robertphillips@google.combeb1af22014-05-07 21:31:09 +00002027 for (int i = 0; i < gpuData->numSaveLayers(); ++i) {
robertphillips4ec84da2014-06-24 13:10:43 -07002028 GrCachedLayer* layer = fContext->getLayerCache()->findLayer(picture, i);
2029 fContext->getLayerCache()->unlock(layer);
robertphillips@google.combeb1af22014-05-07 21:31:09 +00002030 }
2031
2032 return true;
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002033}