blob: 07a946ef94ab1820ad71a2d48e69d97a98b23a4d [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"
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +000011#include "effects/GrTextureDomainEffect.h"
12#include "effects/GrSimpleTextureEffect.h"
13
14#include "GrContext.h"
15#include "GrBitmapTextContext.h"
16#if SK_DISTANCEFIELD_FONTS
17#include "GrDistanceFieldTextContext.h"
18#endif
19
20#include "SkGrTexturePixelRef.h"
21
22#include "SkColorFilter.h"
23#include "SkDeviceImageFilterProxy.h"
24#include "SkDrawProcs.h"
25#include "SkGlyphCache.h"
26#include "SkImageFilter.h"
27#include "SkPathEffect.h"
28#include "SkRRect.h"
29#include "SkStroke.h"
30#include "SkUtils.h"
31#include "SkErrorInternals.h"
32
33#define CACHE_COMPATIBLE_DEVICE_TEXTURES 1
34
35#if 0
36 extern bool (*gShouldDrawProc)();
37 #define CHECK_SHOULD_DRAW(draw, forceI) \
38 do { \
39 if (gShouldDrawProc && !gShouldDrawProc()) return; \
40 this->prepareDraw(draw, forceI); \
41 } while (0)
42#else
43 #define CHECK_SHOULD_DRAW(draw, forceI) this->prepareDraw(draw, forceI)
44#endif
45
46// This constant represents the screen alignment criterion in texels for
47// requiring texture domain clamping to prevent color bleeding when drawing
48// a sub region of a larger source image.
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +000049#define COLOR_BLEED_TOLERANCE 0.001f
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +000050
51#define DO_DEFERRED_CLEAR() \
52 do { \
53 if (fNeedClear) { \
54 this->clear(SK_ColorTRANSPARENT); \
55 } \
56 } while (false) \
57
58///////////////////////////////////////////////////////////////////////////////
59
60#define CHECK_FOR_ANNOTATION(paint) \
61 do { if (paint.getAnnotation()) { return; } } while (0)
62
63///////////////////////////////////////////////////////////////////////////////
64
65
66class SkGpuDevice::SkAutoCachedTexture : public ::SkNoncopyable {
67public:
68 SkAutoCachedTexture()
69 : fDevice(NULL)
70 , fTexture(NULL) {
71 }
72
73 SkAutoCachedTexture(SkGpuDevice* device,
74 const SkBitmap& bitmap,
75 const GrTextureParams* params,
76 GrTexture** texture)
77 : fDevice(NULL)
78 , fTexture(NULL) {
79 SkASSERT(NULL != texture);
80 *texture = this->set(device, bitmap, params);
81 }
82
83 ~SkAutoCachedTexture() {
84 if (NULL != fTexture) {
85 GrUnlockAndUnrefCachedBitmapTexture(fTexture);
86 }
87 }
88
89 GrTexture* set(SkGpuDevice* device,
90 const SkBitmap& bitmap,
91 const GrTextureParams* params) {
92 if (NULL != fTexture) {
93 GrUnlockAndUnrefCachedBitmapTexture(fTexture);
94 fTexture = NULL;
95 }
96 fDevice = device;
97 GrTexture* result = (GrTexture*)bitmap.getTexture();
98 if (NULL == result) {
99 // Cannot return the native texture so look it up in our cache
100 fTexture = GrLockAndRefCachedBitmapTexture(device->context(), bitmap, params);
101 result = fTexture;
102 }
103 return result;
104 }
105
106private:
107 SkGpuDevice* fDevice;
108 GrTexture* fTexture;
109};
110
111///////////////////////////////////////////////////////////////////////////////
112
113struct GrSkDrawProcs : public SkDrawProcs {
114public:
115 GrContext* fContext;
116 GrTextContext* fTextContext;
117 GrFontScaler* fFontScaler; // cached in the skia glyphcache
118};
119
120///////////////////////////////////////////////////////////////////////////////
121
122static SkBitmap::Config grConfig2skConfig(GrPixelConfig config, bool* isOpaque) {
123 switch (config) {
124 case kAlpha_8_GrPixelConfig:
125 *isOpaque = false;
126 return SkBitmap::kA8_Config;
127 case kRGB_565_GrPixelConfig:
128 *isOpaque = true;
129 return SkBitmap::kRGB_565_Config;
130 case kRGBA_4444_GrPixelConfig:
131 *isOpaque = false;
132 return SkBitmap::kARGB_4444_Config;
133 case kSkia8888_GrPixelConfig:
134 // we don't currently have a way of knowing whether
135 // a 8888 is opaque based on the config.
136 *isOpaque = false;
137 return SkBitmap::kARGB_8888_Config;
138 default:
139 *isOpaque = false;
140 return SkBitmap::kNo_Config;
141 }
142}
143
144/*
145 * GrRenderTarget does not know its opaqueness, only its config, so we have
146 * to make conservative guesses when we return an "equivalent" bitmap.
147 */
148static SkBitmap make_bitmap(GrContext* context, GrRenderTarget* renderTarget) {
149 bool isOpaque;
150 SkBitmap::Config config = grConfig2skConfig(renderTarget->config(), &isOpaque);
151
152 SkBitmap bitmap;
153 bitmap.setConfig(config, renderTarget->width(), renderTarget->height(), 0,
154 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
155 return bitmap;
156}
157
158SkGpuDevice* SkGpuDevice::Create(GrSurface* surface) {
159 SkASSERT(NULL != surface);
160 if (NULL == surface->asRenderTarget() || NULL == surface->getContext()) {
161 return NULL;
162 }
163 if (surface->asTexture()) {
164 return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asTexture()));
165 } else {
166 return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asRenderTarget()));
167 }
168}
169
170SkGpuDevice::SkGpuDevice(GrContext* context, GrTexture* texture)
171 : SkBitmapDevice(make_bitmap(context, texture->asRenderTarget())) {
172 this->initFromRenderTarget(context, texture->asRenderTarget(), false);
173}
174
175SkGpuDevice::SkGpuDevice(GrContext* context, GrRenderTarget* renderTarget)
176 : SkBitmapDevice(make_bitmap(context, renderTarget)) {
177 this->initFromRenderTarget(context, renderTarget, false);
178}
179
180void SkGpuDevice::initFromRenderTarget(GrContext* context,
181 GrRenderTarget* renderTarget,
182 bool cached) {
183 fDrawProcs = NULL;
184
185 fContext = context;
186 fContext->ref();
187
188 fRenderTarget = NULL;
189 fNeedClear = false;
190
191 SkASSERT(NULL != renderTarget);
192 fRenderTarget = renderTarget;
193 fRenderTarget->ref();
194
195 // Hold onto to the texture in the pixel ref (if there is one) because the texture holds a ref
196 // on the RT but not vice-versa.
197 // TODO: Remove this trickery once we figure out how to make SkGrPixelRef do this without
198 // busting chrome (for a currently unknown reason).
199 GrSurface* surface = fRenderTarget->asTexture();
200 if (NULL == surface) {
201 surface = fRenderTarget;
202 }
203 SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (surface, cached));
204
205 this->setPixelRef(pr, 0)->unref();
206}
207
208SkGpuDevice::SkGpuDevice(GrContext* context,
209 SkBitmap::Config config,
210 int width,
211 int height,
212 int sampleCount)
213 : SkBitmapDevice(config, width, height, false /*isOpaque*/) {
214
215 fDrawProcs = NULL;
216
217 fContext = context;
218 fContext->ref();
219
220 fRenderTarget = NULL;
221 fNeedClear = false;
222
223 if (config != SkBitmap::kRGB_565_Config) {
224 config = SkBitmap::kARGB_8888_Config;
225 }
226
227 GrTextureDesc desc;
228 desc.fFlags = kRenderTarget_GrTextureFlagBit;
229 desc.fWidth = width;
230 desc.fHeight = height;
231 desc.fConfig = SkBitmapConfig2GrPixelConfig(config);
232 desc.fSampleCnt = sampleCount;
233
234 SkAutoTUnref<GrTexture> texture(fContext->createUncachedTexture(desc, NULL, 0));
235
236 if (NULL != texture) {
237 fRenderTarget = texture->asRenderTarget();
238 fRenderTarget->ref();
239
240 SkASSERT(NULL != fRenderTarget);
241
242 // wrap the bitmap with a pixelref to expose our texture
243 SkGrPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (texture));
244 this->setPixelRef(pr, 0)->unref();
245 } else {
246 GrPrintf("--- failed to create gpu-offscreen [%d %d]\n",
247 width, height);
248 SkASSERT(false);
249 }
250}
251
252SkGpuDevice::~SkGpuDevice() {
253 if (fDrawProcs) {
254 delete fDrawProcs;
255 }
256
257 // The GrContext takes a ref on the target. We don't want to cause the render
258 // target to be unnecessarily kept alive.
259 if (fContext->getRenderTarget() == fRenderTarget) {
260 fContext->setRenderTarget(NULL);
261 }
262
263 if (fContext->getClip() == &fClipData) {
264 fContext->setClip(NULL);
265 }
266
267 SkSafeUnref(fRenderTarget);
268 fContext->unref();
269}
270
271///////////////////////////////////////////////////////////////////////////////
272
273void SkGpuDevice::makeRenderTargetCurrent() {
274 DO_DEFERRED_CLEAR();
275 fContext->setRenderTarget(fRenderTarget);
276}
277
278///////////////////////////////////////////////////////////////////////////////
279
280namespace {
281GrPixelConfig config8888_to_grconfig_and_flags(SkCanvas::Config8888 config8888, uint32_t* flags) {
282 switch (config8888) {
283 case SkCanvas::kNative_Premul_Config8888:
284 *flags = 0;
285 return kSkia8888_GrPixelConfig;
286 case SkCanvas::kNative_Unpremul_Config8888:
287 *flags = GrContext::kUnpremul_PixelOpsFlag;
288 return kSkia8888_GrPixelConfig;
289 case SkCanvas::kBGRA_Premul_Config8888:
290 *flags = 0;
291 return kBGRA_8888_GrPixelConfig;
292 case SkCanvas::kBGRA_Unpremul_Config8888:
293 *flags = GrContext::kUnpremul_PixelOpsFlag;
294 return kBGRA_8888_GrPixelConfig;
295 case SkCanvas::kRGBA_Premul_Config8888:
296 *flags = 0;
297 return kRGBA_8888_GrPixelConfig;
298 case SkCanvas::kRGBA_Unpremul_Config8888:
299 *flags = GrContext::kUnpremul_PixelOpsFlag;
300 return kRGBA_8888_GrPixelConfig;
301 default:
302 GrCrash("Unexpected Config8888.");
303 *flags = 0; // suppress warning
304 return kSkia8888_GrPixelConfig;
305 }
306}
307}
308
309bool SkGpuDevice::onReadPixels(const SkBitmap& bitmap,
310 int x, int y,
311 SkCanvas::Config8888 config8888) {
312 DO_DEFERRED_CLEAR();
313 SkASSERT(SkBitmap::kARGB_8888_Config == bitmap.config());
314 SkASSERT(!bitmap.isNull());
315 SkASSERT(SkIRect::MakeWH(this->width(), this->height()).contains(SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height())));
316
317 SkAutoLockPixels alp(bitmap);
318 GrPixelConfig config;
319 uint32_t flags;
320 config = config8888_to_grconfig_and_flags(config8888, &flags);
321 return fContext->readRenderTargetPixels(fRenderTarget,
322 x, y,
323 bitmap.width(),
324 bitmap.height(),
325 config,
326 bitmap.getPixels(),
327 bitmap.rowBytes(),
328 flags);
329}
330
331void SkGpuDevice::writePixels(const SkBitmap& bitmap, int x, int y,
332 SkCanvas::Config8888 config8888) {
333 SkAutoLockPixels alp(bitmap);
334 if (!bitmap.readyToDraw()) {
335 return;
336 }
337
338 GrPixelConfig config;
339 uint32_t flags;
340 if (SkBitmap::kARGB_8888_Config == bitmap.config()) {
341 config = config8888_to_grconfig_and_flags(config8888, &flags);
342 } else {
343 flags = 0;
344 config= SkBitmapConfig2GrPixelConfig(bitmap.config());
345 }
346
347 fRenderTarget->writePixels(x, y, bitmap.width(), bitmap.height(),
348 config, bitmap.getPixels(), bitmap.rowBytes(), flags);
349}
350
351void SkGpuDevice::onAttachToCanvas(SkCanvas* canvas) {
352 INHERITED::onAttachToCanvas(canvas);
353
354 // Canvas promises that this ptr is valid until onDetachFromCanvas is called
355 fClipData.fClipStack = canvas->getClipStack();
356}
357
358void SkGpuDevice::onDetachFromCanvas() {
359 INHERITED::onDetachFromCanvas();
360 fClipData.fClipStack = NULL;
361}
362
363// call this every draw call, to ensure that the context reflects our state,
364// and not the state from some other canvas/device
365void SkGpuDevice::prepareDraw(const SkDraw& draw, bool forceIdentity) {
366 SkASSERT(NULL != fClipData.fClipStack);
367
368 fContext->setRenderTarget(fRenderTarget);
369
370 SkASSERT(draw.fClipStack && draw.fClipStack == fClipData.fClipStack);
371
372 if (forceIdentity) {
373 fContext->setIdentityMatrix();
374 } else {
375 fContext->setMatrix(*draw.fMatrix);
376 }
377 fClipData.fOrigin = this->getOrigin();
378
379 fContext->setClip(&fClipData);
380
381 DO_DEFERRED_CLEAR();
382}
383
384GrRenderTarget* SkGpuDevice::accessRenderTarget() {
385 DO_DEFERRED_CLEAR();
386 return fRenderTarget;
387}
388
389///////////////////////////////////////////////////////////////////////////////
390
391SK_COMPILE_ASSERT(SkShader::kNone_BitmapType == 0, shader_type_mismatch);
392SK_COMPILE_ASSERT(SkShader::kDefault_BitmapType == 1, shader_type_mismatch);
393SK_COMPILE_ASSERT(SkShader::kRadial_BitmapType == 2, shader_type_mismatch);
394SK_COMPILE_ASSERT(SkShader::kSweep_BitmapType == 3, shader_type_mismatch);
395SK_COMPILE_ASSERT(SkShader::kTwoPointRadial_BitmapType == 4,
396 shader_type_mismatch);
397SK_COMPILE_ASSERT(SkShader::kTwoPointConical_BitmapType == 5,
398 shader_type_mismatch);
399SK_COMPILE_ASSERT(SkShader::kLinear_BitmapType == 6, shader_type_mismatch);
400SK_COMPILE_ASSERT(SkShader::kLast_BitmapType == 6, shader_type_mismatch);
401
402namespace {
403
404// converts a SkPaint to a GrPaint, ignoring the skPaint's shader
405// justAlpha indicates that skPaint's alpha should be used rather than the color
406// Callers may subsequently modify the GrPaint. Setting constantColor indicates
407// that the final paint will draw the same color at every pixel. This allows
408// an optimization where the the color filter can be applied to the skPaint's
409// color once while converting to GrPaint and then ignored.
410inline bool skPaint2GrPaintNoShader(SkGpuDevice* dev,
411 const SkPaint& skPaint,
412 bool justAlpha,
413 bool constantColor,
414 GrPaint* grPaint) {
415
416 grPaint->setDither(skPaint.isDither());
417 grPaint->setAntiAlias(skPaint.isAntiAlias());
418
419 SkXfermode::Coeff sm;
420 SkXfermode::Coeff dm;
421
422 SkXfermode* mode = skPaint.getXfermode();
423 GrEffectRef* xferEffect = NULL;
424 if (SkXfermode::AsNewEffectOrCoeff(mode, &xferEffect, &sm, &dm)) {
425 if (NULL != xferEffect) {
426 grPaint->addColorEffect(xferEffect)->unref();
427 sm = SkXfermode::kOne_Coeff;
428 dm = SkXfermode::kZero_Coeff;
429 }
430 } else {
431 //SkDEBUGCODE(SkDebugf("Unsupported xfer mode.\n");)
432#if 0
433 return false;
434#else
435 // Fall back to src-over
436 sm = SkXfermode::kOne_Coeff;
437 dm = SkXfermode::kISA_Coeff;
438#endif
439 }
440 grPaint->setBlendFunc(sk_blend_to_grblend(sm), sk_blend_to_grblend(dm));
441
442 if (justAlpha) {
443 uint8_t alpha = skPaint.getAlpha();
444 grPaint->setColor(GrColorPackRGBA(alpha, alpha, alpha, alpha));
445 // justAlpha is currently set to true only if there is a texture,
446 // so constantColor should not also be true.
447 SkASSERT(!constantColor);
448 } else {
449 grPaint->setColor(SkColor2GrColor(skPaint.getColor()));
450 }
451
452 SkColorFilter* colorFilter = skPaint.getColorFilter();
453 if (NULL != colorFilter) {
454 // if the source color is a constant then apply the filter here once rather than per pixel
455 // in a shader.
456 if (constantColor) {
457 SkColor filtered = colorFilter->filterColor(skPaint.getColor());
458 grPaint->setColor(SkColor2GrColor(filtered));
459 } else {
460 SkAutoTUnref<GrEffectRef> effect(colorFilter->asNewEffect(dev->context()));
461 if (NULL != effect.get()) {
462 grPaint->addColorEffect(effect);
463 }
464 }
465 }
466
467 return true;
468}
469
470// This function is similar to skPaint2GrPaintNoShader but also converts
471// skPaint's shader to a GrTexture/GrEffectStage if possible. The texture to
472// be used is set on grPaint and returned in param act. constantColor has the
473// same meaning as in skPaint2GrPaintNoShader.
474inline bool skPaint2GrPaintShader(SkGpuDevice* dev,
475 const SkPaint& skPaint,
476 bool constantColor,
477 GrPaint* grPaint) {
478 SkShader* shader = skPaint.getShader();
479 if (NULL == shader) {
480 return skPaint2GrPaintNoShader(dev, skPaint, false, constantColor, grPaint);
481 }
482
483 // SkShader::asNewEffect() may do offscreen rendering. Setup default drawing state
484 // Also require shader to set the render target .
485 GrContext::AutoWideOpenIdentityDraw awo(dev->context(), NULL);
486 GrContext::AutoRenderTarget(dev->context(), NULL);
487
488 // setup the shader as the first color effect on the paint
489 SkAutoTUnref<GrEffectRef> effect(shader->asNewEffect(dev->context(), skPaint));
490 if (NULL != effect.get()) {
491 grPaint->addColorEffect(effect);
492 // Now setup the rest of the paint.
493 return skPaint2GrPaintNoShader(dev, skPaint, true, false, grPaint);
494 } else {
495 // We still don't have SkColorShader::asNewEffect() implemented.
496 SkShader::GradientInfo info;
497 SkColor color;
498
499 info.fColors = &color;
500 info.fColorOffsets = NULL;
501 info.fColorCount = 1;
502 if (SkShader::kColor_GradientType == shader->asAGradient(&info)) {
503 SkPaint copy(skPaint);
504 copy.setShader(NULL);
505 // modulate the paint alpha by the shader's solid color alpha
506 U8CPU newA = SkMulDiv255Round(SkColorGetA(color), copy.getAlpha());
507 copy.setColor(SkColorSetA(color, newA));
508 return skPaint2GrPaintNoShader(dev, copy, false, constantColor, grPaint);
509 } else {
510 return false;
511 }
512 }
513}
514}
515
516///////////////////////////////////////////////////////////////////////////////
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000517
518SkBitmap::Config SkGpuDevice::config() const {
519 if (NULL == fRenderTarget) {
520 return SkBitmap::kNo_Config;
521 }
522
523 bool isOpaque;
524 return grConfig2skConfig(fRenderTarget->config(), &isOpaque);
525}
526
527void SkGpuDevice::clear(SkColor color) {
528 SkIRect rect = SkIRect::MakeWH(this->width(), this->height());
529 fContext->clear(&rect, SkColor2GrColor(color), true, fRenderTarget);
530 fNeedClear = false;
531}
532
533void SkGpuDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
534 CHECK_SHOULD_DRAW(draw, false);
535
536 GrPaint grPaint;
537 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
538 return;
539 }
540
541 fContext->drawPaint(grPaint);
542}
543
544// must be in SkCanvas::PointMode order
545static const GrPrimitiveType gPointMode2PrimtiveType[] = {
546 kPoints_GrPrimitiveType,
547 kLines_GrPrimitiveType,
548 kLineStrip_GrPrimitiveType
549};
550
551void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
552 size_t count, const SkPoint pts[], const SkPaint& paint) {
553 CHECK_FOR_ANNOTATION(paint);
554 CHECK_SHOULD_DRAW(draw, false);
555
556 SkScalar width = paint.getStrokeWidth();
557 if (width < 0) {
558 return;
559 }
560
561 // we only handle hairlines and paints without path effects or mask filters,
562 // else we let the SkDraw call our drawPath()
563 if (width > 0 || paint.getPathEffect() || paint.getMaskFilter()) {
564 draw.drawPoints(mode, count, pts, paint, true);
565 return;
566 }
567
568 GrPaint grPaint;
569 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
570 return;
571 }
572
573 fContext->drawVertices(grPaint,
574 gPointMode2PrimtiveType[mode],
robertphillips@google.coma4662862013-11-21 14:24:16 +0000575 SkToS32(count),
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000576 (GrPoint*)pts,
577 NULL,
578 NULL,
579 NULL,
580 0);
581}
582
583///////////////////////////////////////////////////////////////////////////////
584
585void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect,
586 const SkPaint& paint) {
587 CHECK_FOR_ANNOTATION(paint);
588 CHECK_SHOULD_DRAW(draw, false);
589
590 bool doStroke = paint.getStyle() != SkPaint::kFill_Style;
591 SkScalar width = paint.getStrokeWidth();
592
593 /*
594 We have special code for hairline strokes, miter-strokes, bevel-stroke
595 and fills. Anything else we just call our path code.
596 */
597 bool usePath = doStroke && width > 0 &&
598 (paint.getStrokeJoin() == SkPaint::kRound_Join ||
599 (paint.getStrokeJoin() == SkPaint::kBevel_Join && rect.isEmpty()));
600 // another two reasons we might need to call drawPath...
601 if (paint.getMaskFilter() || paint.getPathEffect()) {
602 usePath = true;
603 }
604 if (!usePath && paint.isAntiAlias() && !fContext->getMatrix().rectStaysRect()) {
605#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)
606 if (doStroke) {
607#endif
608 usePath = true;
609#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)
610 } else {
611 usePath = !fContext->getMatrix().preservesRightAngles();
612 }
613#endif
614 }
615 // until we can both stroke and fill rectangles
616 if (paint.getStyle() == SkPaint::kStrokeAndFill_Style) {
617 usePath = true;
618 }
619
620 if (usePath) {
621 SkPath path;
622 path.addRect(rect);
623 this->drawPath(draw, path, paint, NULL, true);
624 return;
625 }
626
627 GrPaint grPaint;
628 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
629 return;
630 }
631
632 if (!doStroke) {
633 fContext->drawRect(grPaint, rect);
634 } else {
635 SkStrokeRec stroke(paint);
636 fContext->drawRect(grPaint, rect, &stroke);
637 }
638}
639
640///////////////////////////////////////////////////////////////////////////////
641
642void SkGpuDevice::drawRRect(const SkDraw& draw, const SkRRect& rect,
643 const SkPaint& paint) {
644 CHECK_FOR_ANNOTATION(paint);
645 CHECK_SHOULD_DRAW(draw, false);
646
647 bool usePath = !rect.isSimple();
648 // another two reasons we might need to call drawPath...
649 if (paint.getMaskFilter() || paint.getPathEffect()) {
650 usePath = true;
651 }
652 // until we can rotate rrects...
653 if (!usePath && !fContext->getMatrix().rectStaysRect()) {
654 usePath = true;
655 }
656
657 if (usePath) {
658 SkPath path;
659 path.addRRect(rect);
660 this->drawPath(draw, path, paint, NULL, true);
661 return;
662 }
663
664 GrPaint grPaint;
665 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
666 return;
667 }
668
669 SkStrokeRec stroke(paint);
670 fContext->drawRRect(grPaint, rect, stroke);
671}
672
673///////////////////////////////////////////////////////////////////////////////
674
675void SkGpuDevice::drawOval(const SkDraw& draw, const SkRect& oval,
676 const SkPaint& paint) {
677 CHECK_FOR_ANNOTATION(paint);
678 CHECK_SHOULD_DRAW(draw, false);
679
680 bool usePath = false;
681 // some basic reasons we might need to call drawPath...
682 if (paint.getMaskFilter() || paint.getPathEffect()) {
683 usePath = true;
684 }
685
686 if (usePath) {
687 SkPath path;
688 path.addOval(oval);
689 this->drawPath(draw, path, paint, NULL, true);
690 return;
691 }
692
693 GrPaint grPaint;
694 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
695 return;
696 }
697 SkStrokeRec stroke(paint);
698
699 fContext->drawOval(grPaint, oval, stroke);
700}
701
702#include "SkMaskFilter.h"
703#include "SkBounder.h"
704
705///////////////////////////////////////////////////////////////////////////////
706
707// helpers for applying mask filters
708namespace {
709
710// Draw a mask using the supplied paint. Since the coverage/geometry
711// is already burnt into the mask this boils down to a rect draw.
712// Return true if the mask was successfully drawn.
713bool draw_mask(GrContext* context, const SkRect& maskRect,
714 GrPaint* grp, GrTexture* mask) {
715 GrContext::AutoMatrix am;
716 if (!am.setIdentity(context, grp)) {
717 return false;
718 }
719
720 SkMatrix matrix;
721 matrix.setTranslate(-maskRect.fLeft, -maskRect.fTop);
722 matrix.postIDiv(mask->width(), mask->height());
723
724 grp->addCoverageEffect(GrSimpleTextureEffect::Create(mask, matrix))->unref();
725 context->drawRect(*grp, maskRect);
726 return true;
727}
728
729bool draw_with_mask_filter(GrContext* context, const SkPath& devPath,
730 SkMaskFilter* filter, const SkRegion& clip, SkBounder* bounder,
731 GrPaint* grp, SkPaint::Style style) {
732 SkMask srcM, dstM;
733
734 if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), filter, &context->getMatrix(), &srcM,
735 SkMask::kComputeBoundsAndRenderImage_CreateMode, style)) {
736 return false;
737 }
738 SkAutoMaskFreeImage autoSrc(srcM.fImage);
739
740 if (!filter->filterMask(&dstM, srcM, context->getMatrix(), NULL)) {
741 return false;
742 }
743 // this will free-up dstM when we're done (allocated in filterMask())
744 SkAutoMaskFreeImage autoDst(dstM.fImage);
745
746 if (clip.quickReject(dstM.fBounds)) {
747 return false;
748 }
749 if (bounder && !bounder->doIRect(dstM.fBounds)) {
750 return false;
751 }
752
753 // we now have a device-aligned 8bit mask in dstM, ready to be drawn using
754 // the current clip (and identity matrix) and GrPaint settings
755 GrTextureDesc desc;
756 desc.fWidth = dstM.fBounds.width();
757 desc.fHeight = dstM.fBounds.height();
758 desc.fConfig = kAlpha_8_GrPixelConfig;
759
760 GrAutoScratchTexture ast(context, desc);
761 GrTexture* texture = ast.texture();
762
763 if (NULL == texture) {
764 return false;
765 }
766 texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
767 dstM.fImage, dstM.fRowBytes);
768
769 SkRect maskRect = SkRect::Make(dstM.fBounds);
770
771 return draw_mask(context, maskRect, grp, texture);
772}
773
774// Create a mask of 'devPath' and place the result in 'mask'. Return true on
775// success; false otherwise.
776bool create_mask_GPU(GrContext* context,
777 const SkRect& maskRect,
778 const SkPath& devPath,
779 const SkStrokeRec& stroke,
780 bool doAA,
781 GrAutoScratchTexture* mask) {
782 GrTextureDesc desc;
783 desc.fFlags = kRenderTarget_GrTextureFlagBit;
784 desc.fWidth = SkScalarCeilToInt(maskRect.width());
785 desc.fHeight = SkScalarCeilToInt(maskRect.height());
786 // We actually only need A8, but it often isn't supported as a
787 // render target so default to RGBA_8888
788 desc.fConfig = kRGBA_8888_GrPixelConfig;
789 if (context->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {
790 desc.fConfig = kAlpha_8_GrPixelConfig;
791 }
792
793 mask->set(context, desc);
794 if (NULL == mask->texture()) {
795 return false;
796 }
797
798 GrTexture* maskTexture = mask->texture();
799 SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height());
800
801 GrContext::AutoRenderTarget art(context, maskTexture->asRenderTarget());
802 GrContext::AutoClip ac(context, clipRect);
803
804 context->clear(NULL, 0x0, true);
805
806 GrPaint tempPaint;
807 if (doAA) {
808 tempPaint.setAntiAlias(true);
809 // AA uses the "coverage" stages on GrDrawTarget. Coverage with a dst
810 // blend coeff of zero requires dual source blending support in order
811 // to properly blend partially covered pixels. This means the AA
812 // code path may not be taken. So we use a dst blend coeff of ISA. We
813 // could special case AA draws to a dst surface with known alpha=0 to
814 // use a zero dst coeff when dual source blending isn't available.
815 tempPaint.setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff);
816 }
817
818 GrContext::AutoMatrix am;
819
820 // Draw the mask into maskTexture with the path's top-left at the origin using tempPaint.
821 SkMatrix translate;
822 translate.setTranslate(-maskRect.fLeft, -maskRect.fTop);
823 am.set(context, translate);
824 context->drawPath(tempPaint, devPath, stroke);
825 return true;
826}
827
828SkBitmap wrap_texture(GrTexture* texture) {
829 SkBitmap result;
830 bool dummy;
831 SkBitmap::Config config = grConfig2skConfig(texture->config(), &dummy);
832 result.setConfig(config, texture->width(), texture->height());
833 result.setPixelRef(SkNEW_ARGS(SkGrPixelRef, (texture)))->unref();
834 return result;
835}
836
837};
838
839void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
840 const SkPaint& paint, const SkMatrix* prePathMatrix,
841 bool pathIsMutable) {
842 CHECK_FOR_ANNOTATION(paint);
843 CHECK_SHOULD_DRAW(draw, false);
844
845 GrPaint grPaint;
846 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
847 return;
848 }
849
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000850 // If we have a prematrix, apply it to the path, optimizing for the case
851 // where the original path can in fact be modified in place (even though
852 // its parameter type is const).
853 SkPath* pathPtr = const_cast<SkPath*>(&origSrcPath);
854 SkPath tmpPath, effectPath;
855
856 if (prePathMatrix) {
857 SkPath* result = pathPtr;
858
859 if (!pathIsMutable) {
860 result = &tmpPath;
861 pathIsMutable = true;
862 }
863 // should I push prePathMatrix on our MV stack temporarily, instead
864 // of applying it here? See SkDraw.cpp
865 pathPtr->transform(*prePathMatrix, result);
866 pathPtr = result;
867 }
868 // at this point we're done with prePathMatrix
869 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
870
871 SkStrokeRec stroke(paint);
872 SkPathEffect* pathEffect = paint.getPathEffect();
873 const SkRect* cullRect = NULL; // TODO: what is our bounds?
874 if (pathEffect && pathEffect->filterPath(&effectPath, *pathPtr, &stroke,
875 cullRect)) {
876 pathPtr = &effectPath;
877 }
878
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000879 if (paint.getMaskFilter()) {
880 if (!stroke.isHairlineStyle()) {
881 if (stroke.applyToPath(&tmpPath, *pathPtr)) {
882 pathPtr = &tmpPath;
883 pathIsMutable = true;
884 stroke.setFillStyle();
885 }
886 }
887
888 // avoid possibly allocating a new path in transform if we can
889 SkPath* devPathPtr = pathIsMutable ? pathPtr : &tmpPath;
890
891 // transform the path into device space
892 pathPtr->transform(fContext->getMatrix(), devPathPtr);
893
894 SkRect maskRect;
895 if (paint.getMaskFilter()->canFilterMaskGPU(devPathPtr->getBounds(),
896 draw.fClip->getBounds(),
897 fContext->getMatrix(),
898 &maskRect)) {
899 SkIRect finalIRect;
900 maskRect.roundOut(&finalIRect);
901 if (draw.fClip->quickReject(finalIRect)) {
902 // clipped out
903 return;
904 }
905 if (NULL != draw.fBounder && !draw.fBounder->doIRect(finalIRect)) {
906 // nothing to draw
907 return;
908 }
909
910 GrAutoScratchTexture mask;
911
912 if (create_mask_GPU(fContext, maskRect, *devPathPtr, stroke,
913 grPaint.isAntiAlias(), &mask)) {
914 GrTexture* filtered;
915
916 if (paint.getMaskFilter()->filterMaskGPU(mask.texture(), maskRect, &filtered, true)) {
917 // filterMaskGPU gives us ownership of a ref to the result
918 SkAutoTUnref<GrTexture> atu(filtered);
919
920 // If the scratch texture that we used as the filter src also holds the filter
921 // result then we must detach so that this texture isn't recycled for a later
922 // draw.
923 if (filtered == mask.texture()) {
924 mask.detach();
925 filtered->unref(); // detach transfers GrAutoScratchTexture's ref to us.
926 }
927
928 if (draw_mask(fContext, maskRect, &grPaint, filtered)) {
929 // This path is completely drawn
930 return;
931 }
932 }
933 }
934 }
935
936 // draw the mask on the CPU - this is a fallthrough path in case the
937 // GPU path fails
938 SkPaint::Style style = stroke.isHairlineStyle() ? SkPaint::kStroke_Style :
939 SkPaint::kFill_Style;
940 draw_with_mask_filter(fContext, *devPathPtr, paint.getMaskFilter(),
941 *draw.fClip, draw.fBounder, &grPaint, style);
942 return;
943 }
944
945 fContext->drawPath(grPaint, *pathPtr, stroke);
946}
947
948static const int kBmpSmallTileSize = 1 << 10;
949
950static inline int get_tile_count(const SkIRect& srcRect, int tileSize) {
951 int tilesX = (srcRect.fRight / tileSize) - (srcRect.fLeft / tileSize) + 1;
952 int tilesY = (srcRect.fBottom / tileSize) - (srcRect.fTop / tileSize) + 1;
953 return tilesX * tilesY;
954}
955
956static int determine_tile_size(const SkBitmap& bitmap, const SkIRect& src, int maxTileSize) {
957 if (maxTileSize <= kBmpSmallTileSize) {
958 return maxTileSize;
959 }
960
961 size_t maxTileTotalTileSize = get_tile_count(src, maxTileSize);
962 size_t smallTotalTileSize = get_tile_count(src, kBmpSmallTileSize);
963
964 maxTileTotalTileSize *= maxTileSize * maxTileSize;
965 smallTotalTileSize *= kBmpSmallTileSize * kBmpSmallTileSize;
966
967 if (maxTileTotalTileSize > 2 * smallTotalTileSize) {
968 return kBmpSmallTileSize;
969 } else {
970 return maxTileSize;
971 }
972}
973
974// Given a bitmap, an optional src rect, and a context with a clip and matrix determine what
975// pixels from the bitmap are necessary.
976static void determine_clipped_src_rect(const GrContext* context,
977 const SkBitmap& bitmap,
978 const SkRect* srcRectPtr,
979 SkIRect* clippedSrcIRect) {
980 const GrClipData* clip = context->getClip();
981 clip->getConservativeBounds(context->getRenderTarget(), clippedSrcIRect, NULL);
982 SkMatrix inv;
983 if (!context->getMatrix().invert(&inv)) {
984 clippedSrcIRect->setEmpty();
985 return;
986 }
987 SkRect clippedSrcRect = SkRect::Make(*clippedSrcIRect);
988 inv.mapRect(&clippedSrcRect);
989 if (NULL != srcRectPtr) {
990 if (!clippedSrcRect.intersect(*srcRectPtr)) {
991 clippedSrcIRect->setEmpty();
992 return;
993 }
994 }
995 clippedSrcRect.roundOut(clippedSrcIRect);
996 SkIRect bmpBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
997 if (!clippedSrcIRect->intersect(bmpBounds)) {
998 clippedSrcIRect->setEmpty();
999 }
1000}
1001
1002bool SkGpuDevice::shouldTileBitmap(const SkBitmap& bitmap,
1003 const GrTextureParams& params,
1004 const SkRect* srcRectPtr,
1005 int maxTileSize,
1006 int* tileSize,
1007 SkIRect* clippedSrcRect) const {
1008 // if bitmap is explictly texture backed then just use the texture
1009 if (NULL != bitmap.getTexture()) {
1010 return false;
1011 }
1012
1013 // if it's larger than the max tile size, then we have no choice but tiling.
1014 if (bitmap.width() > maxTileSize || bitmap.height() > maxTileSize) {
1015 determine_clipped_src_rect(fContext, bitmap, srcRectPtr, clippedSrcRect);
1016 *tileSize = determine_tile_size(bitmap, *clippedSrcRect, maxTileSize);
1017 return true;
1018 }
1019
1020 if (bitmap.width() * bitmap.height() < 4 * kBmpSmallTileSize * kBmpSmallTileSize) {
1021 return false;
1022 }
1023
1024 // if the entire texture is already in our cache then no reason to tile it
1025 if (GrIsBitmapInCache(fContext, bitmap, &params)) {
1026 return false;
1027 }
1028
1029 // At this point we know we could do the draw by uploading the entire bitmap
1030 // as a texture. However, if the texture would be large compared to the
1031 // cache size and we don't require most of it for this draw then tile to
1032 // reduce the amount of upload and cache spill.
1033
1034 // assumption here is that sw bitmap size is a good proxy for its size as
1035 // a texture
1036 size_t bmpSize = bitmap.getSize();
1037 size_t cacheSize;
1038 fContext->getTextureCacheLimits(NULL, &cacheSize);
1039 if (bmpSize < cacheSize / 2) {
1040 return false;
1041 }
1042
1043 // Figure out how much of the src we will need based on the src rect and clipping.
1044 determine_clipped_src_rect(fContext, bitmap, srcRectPtr, clippedSrcRect);
1045 *tileSize = kBmpSmallTileSize; // already know whole bitmap fits in one max sized tile.
1046 size_t usedTileBytes = get_tile_count(*clippedSrcRect, kBmpSmallTileSize) *
1047 kBmpSmallTileSize * kBmpSmallTileSize;
1048
1049 return usedTileBytes < 2 * bmpSize;
1050}
1051
1052void SkGpuDevice::drawBitmap(const SkDraw& draw,
1053 const SkBitmap& bitmap,
1054 const SkMatrix& m,
1055 const SkPaint& paint) {
1056 // We cannot call drawBitmapRect here since 'm' could be anything
1057 this->drawBitmapCommon(draw, bitmap, NULL, m, paint,
1058 SkCanvas::kNone_DrawBitmapRectFlag);
1059}
1060
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001061// This method outsets 'iRect' by 'outset' all around and then clamps its extents to
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001062// 'clamp'. 'offset' is adjusted to remain positioned over the top-left corner
1063// of 'iRect' for all possible outsets/clamps.
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001064static inline void clamped_outset_with_offset(SkIRect* iRect,
1065 int outset,
1066 SkPoint* offset,
1067 const SkIRect& clamp) {
1068 iRect->outset(outset, outset);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001069
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001070 int leftClampDelta = clamp.fLeft - iRect->fLeft;
1071 if (leftClampDelta > 0) {
1072 offset->fX -= outset - leftClampDelta;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001073 iRect->fLeft = clamp.fLeft;
1074 } else {
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001075 offset->fX -= outset;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001076 }
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001077
1078 int topClampDelta = clamp.fTop - iRect->fTop;
1079 if (topClampDelta > 0) {
1080 offset->fY -= outset - topClampDelta;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001081 iRect->fTop = clamp.fTop;
1082 } else {
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001083 offset->fY -= outset;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001084 }
1085
1086 if (iRect->fRight > clamp.fRight) {
1087 iRect->fRight = clamp.fRight;
1088 }
1089 if (iRect->fBottom > clamp.fBottom) {
1090 iRect->fBottom = clamp.fBottom;
1091 }
1092}
1093
1094void SkGpuDevice::drawBitmapCommon(const SkDraw& draw,
1095 const SkBitmap& bitmap,
1096 const SkRect* srcRectPtr,
1097 const SkMatrix& m,
1098 const SkPaint& paint,
1099 SkCanvas::DrawBitmapRectFlags flags) {
1100 CHECK_SHOULD_DRAW(draw, false);
1101
1102 SkRect srcRect;
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001103 // If there is no src rect, or the src rect contains the entire bitmap then we're effectively
1104 // in the (easier) bleed case, so update flags.
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001105 if (NULL == srcRectPtr) {
1106 srcRect.set(0, 0, SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()));
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001107 flags = (SkCanvas::DrawBitmapRectFlags) (flags | SkCanvas::kBleed_DrawBitmapRectFlag);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001108 } else {
1109 srcRect = *srcRectPtr;
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001110 if (srcRect.fLeft <= 0 && srcRect.fTop <= 0 &&
1111 srcRect.fRight >= bitmap.width() && srcRect.fBottom >= bitmap.height()) {
1112 flags = (SkCanvas::DrawBitmapRectFlags) (flags | SkCanvas::kBleed_DrawBitmapRectFlag);
1113 }
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001114 }
1115
1116 if (paint.getMaskFilter()){
1117 // Convert the bitmap to a shader so that the rect can be drawn
1118 // through drawRect, which supports mask filters.
1119 SkMatrix newM(m);
1120 SkBitmap tmp; // subset of bitmap, if necessary
1121 const SkBitmap* bitmapPtr = &bitmap;
1122 if (NULL != srcRectPtr) {
commit-bot@chromium.orgd6ca4ac2013-11-22 20:34:59 +00001123 // In bleed mode we position and trim the bitmap based on the src rect which is
1124 // already accounted for in 'm' and 'srcRect'. In clamp mode we need to chop out
1125 // the desired portion of the bitmap and then update 'm' and 'srcRect' to
1126 // compensate.
1127 if (!(SkCanvas::kBleed_DrawBitmapRectFlag & flags)) {
1128 SkIRect iSrc;
1129 srcRect.roundOut(&iSrc);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001130
commit-bot@chromium.orgd6ca4ac2013-11-22 20:34:59 +00001131 SkPoint offset = SkPoint::Make(SkIntToScalar(iSrc.fLeft),
1132 SkIntToScalar(iSrc.fTop));
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001133
commit-bot@chromium.orgd6ca4ac2013-11-22 20:34:59 +00001134 if (!bitmap.extractSubset(&tmp, iSrc)) {
1135 return; // extraction failed
1136 }
1137 bitmapPtr = &tmp;
1138 srcRect.offset(-offset.fX, -offset.fY);
1139 // The source rect has changed so update the matrix
1140 newM.preTranslate(offset.fX, offset.fY);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001141 }
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001142 }
1143
1144 SkPaint paintWithTexture(paint);
1145 paintWithTexture.setShader(SkShader::CreateBitmapShader(*bitmapPtr,
1146 SkShader::kClamp_TileMode, SkShader::kClamp_TileMode))->unref();
1147
1148 // Transform 'newM' needs to be concatenated to the current matrix,
1149 // rather than transforming the primitive directly, so that 'newM' will
1150 // also affect the behavior of the mask filter.
1151 SkMatrix drawMatrix;
1152 drawMatrix.setConcat(fContext->getMatrix(), newM);
1153 SkDraw transformedDraw(draw);
1154 transformedDraw.fMatrix = &drawMatrix;
1155
1156 this->drawRect(transformedDraw, srcRect, paintWithTexture);
1157
1158 return;
1159 }
1160
1161 fContext->concatMatrix(m);
1162
1163 GrTextureParams params;
1164 SkPaint::FilterLevel paintFilterLevel = paint.getFilterLevel();
1165 GrTextureParams::FilterMode textureFilterMode;
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001166
1167 int tileFilterPad;
1168 bool doBicubic = false;
1169
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001170 switch(paintFilterLevel) {
1171 case SkPaint::kNone_FilterLevel:
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001172 tileFilterPad = 0;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001173 textureFilterMode = GrTextureParams::kNone_FilterMode;
1174 break;
1175 case SkPaint::kLow_FilterLevel:
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001176 tileFilterPad = 1;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001177 textureFilterMode = GrTextureParams::kBilerp_FilterMode;
1178 break;
1179 case SkPaint::kMedium_FilterLevel:
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001180 tileFilterPad = 1;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001181 textureFilterMode = GrTextureParams::kMipMap_FilterMode;
1182 break;
1183 case SkPaint::kHigh_FilterLevel:
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001184 if (flags & SkCanvas::kBleed_DrawBitmapRectFlag) {
1185 // We will install an effect that does the filtering in the shader.
1186 textureFilterMode = GrTextureParams::kNone_FilterMode;
1187 tileFilterPad = GrBicubicEffect::kFilterTexelPad;
1188 doBicubic = true;
1189 } else {
1190 // We don't yet support doing bicubic filtering with an interior clamp. Fall back
1191 // to MIPs
1192 textureFilterMode = GrTextureParams::kMipMap_FilterMode;
1193 tileFilterPad = 1;
1194 }
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001195 break;
1196 default:
1197 SkErrorInternals::SetError( kInvalidPaint_SkError,
1198 "Sorry, I don't understand the filtering "
1199 "mode you asked for. Falling back to "
1200 "MIPMaps.");
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001201 tileFilterPad = 1;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001202 textureFilterMode = GrTextureParams::kMipMap_FilterMode;
1203 break;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001204 }
1205
1206 params.setFilterMode(textureFilterMode);
1207
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001208 int maxTileSize = fContext->getMaxTextureSize() - 2 * tileFilterPad;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001209 int tileSize;
1210
1211 SkIRect clippedSrcRect;
1212 if (this->shouldTileBitmap(bitmap, params, srcRectPtr, maxTileSize, &tileSize,
1213 &clippedSrcRect)) {
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001214 this->drawTiledBitmap(bitmap, srcRect, clippedSrcRect, params, paint, flags, tileSize,
1215 doBicubic);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001216 } else {
1217 // take the simple case
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001218 this->internalDrawBitmap(bitmap, srcRect, params, paint, flags, doBicubic);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001219 }
1220}
1221
1222// Break 'bitmap' into several tiles to draw it since it has already
1223// been determined to be too large to fit in VRAM
1224void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap,
1225 const SkRect& srcRect,
1226 const SkIRect& clippedSrcIRect,
1227 const GrTextureParams& params,
1228 const SkPaint& paint,
1229 SkCanvas::DrawBitmapRectFlags flags,
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001230 int tileSize,
1231 bool bicubic) {
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001232 SkRect clippedSrcRect = SkRect::Make(clippedSrcIRect);
1233
1234 int nx = bitmap.width() / tileSize;
1235 int ny = bitmap.height() / tileSize;
1236 for (int x = 0; x <= nx; x++) {
1237 for (int y = 0; y <= ny; y++) {
1238 SkRect tileR;
1239 tileR.set(SkIntToScalar(x * tileSize),
1240 SkIntToScalar(y * tileSize),
1241 SkIntToScalar((x + 1) * tileSize),
1242 SkIntToScalar((y + 1) * tileSize));
1243
1244 if (!SkRect::Intersects(tileR, clippedSrcRect)) {
1245 continue;
1246 }
1247
1248 if (!tileR.intersect(srcRect)) {
1249 continue;
1250 }
1251
1252 SkBitmap tmpB;
1253 SkIRect iTileR;
1254 tileR.roundOut(&iTileR);
1255 SkPoint offset = SkPoint::Make(SkIntToScalar(iTileR.fLeft),
1256 SkIntToScalar(iTileR.fTop));
1257
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001258 if (SkPaint::kNone_FilterLevel != paint.getFilterLevel() || bicubic) {
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001259 SkIRect iClampRect;
1260
1261 if (SkCanvas::kBleed_DrawBitmapRectFlag & flags) {
1262 // In bleed mode we want to always expand the tile on all edges
1263 // but stay within the bitmap bounds
1264 iClampRect = SkIRect::MakeWH(bitmap.width(), bitmap.height());
1265 } else {
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001266 SkASSERT(!bicubic); // Bicubic is not supported with non-bleed yet.
1267
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001268 // In texture-domain/clamp mode we only want to expand the
1269 // tile on edges interior to "srcRect" (i.e., we want to
1270 // not bleed across the original clamped edges)
1271 srcRect.roundOut(&iClampRect);
1272 }
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001273 int outset = bicubic ? GrBicubicEffect::kFilterTexelPad : 1;
1274 clamped_outset_with_offset(&iTileR, outset, &offset, iClampRect);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001275 }
1276
1277 if (bitmap.extractSubset(&tmpB, iTileR)) {
1278 // now offset it to make it "local" to our tmp bitmap
1279 tileR.offset(-offset.fX, -offset.fY);
1280 SkMatrix tmpM;
1281 tmpM.setTranslate(offset.fX, offset.fY);
1282 GrContext::AutoMatrix am;
1283 am.setPreConcat(fContext, tmpM);
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001284 this->internalDrawBitmap(tmpB, tileR, params, paint, flags, bicubic);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001285 }
1286 }
1287 }
1288}
1289
1290static bool has_aligned_samples(const SkRect& srcRect,
1291 const SkRect& transformedRect) {
1292 // detect pixel disalignment
1293 if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) -
1294 transformedRect.left()) < COLOR_BLEED_TOLERANCE &&
1295 SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) -
1296 transformedRect.top()) < COLOR_BLEED_TOLERANCE &&
1297 SkScalarAbs(transformedRect.width() - srcRect.width()) <
1298 COLOR_BLEED_TOLERANCE &&
1299 SkScalarAbs(transformedRect.height() - srcRect.height()) <
1300 COLOR_BLEED_TOLERANCE) {
1301 return true;
1302 }
1303 return false;
1304}
1305
1306static bool may_color_bleed(const SkRect& srcRect,
1307 const SkRect& transformedRect,
1308 const SkMatrix& m) {
1309 // Only gets called if has_aligned_samples returned false.
1310 // So we can assume that sampling is axis aligned but not texel aligned.
1311 SkASSERT(!has_aligned_samples(srcRect, transformedRect));
1312 SkRect innerSrcRect(srcRect), innerTransformedRect,
1313 outerTransformedRect(transformedRect);
1314 innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf);
1315 m.mapRect(&innerTransformedRect, innerSrcRect);
1316
1317 // The gap between outerTransformedRect and innerTransformedRect
1318 // represents the projection of the source border area, which is
1319 // problematic for color bleeding. We must check whether any
1320 // destination pixels sample the border area.
1321 outerTransformedRect.inset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE);
1322 innerTransformedRect.outset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE);
1323 SkIRect outer, inner;
1324 outerTransformedRect.round(&outer);
1325 innerTransformedRect.round(&inner);
1326 // If the inner and outer rects round to the same result, it means the
1327 // border does not overlap any pixel centers. Yay!
1328 return inner != outer;
1329}
1330
1331
1332/*
1333 * This is called by drawBitmap(), which has to handle images that may be too
1334 * large to be represented by a single texture.
1335 *
1336 * internalDrawBitmap assumes that the specified bitmap will fit in a texture
1337 * and that non-texture portion of the GrPaint has already been setup.
1338 */
1339void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap,
1340 const SkRect& srcRect,
1341 const GrTextureParams& params,
1342 const SkPaint& paint,
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001343 SkCanvas::DrawBitmapRectFlags flags,
1344 bool bicubic) {
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001345 SkASSERT(bitmap.width() <= fContext->getMaxTextureSize() &&
1346 bitmap.height() <= fContext->getMaxTextureSize());
1347
1348 GrTexture* texture;
1349 SkAutoCachedTexture act(this, bitmap, &params, &texture);
1350 if (NULL == texture) {
1351 return;
1352 }
1353
1354 SkRect dstRect(srcRect);
1355 SkRect paintRect;
1356 SkScalar wInv = SkScalarInvert(SkIntToScalar(texture->width()));
1357 SkScalar hInv = SkScalarInvert(SkIntToScalar(texture->height()));
1358 paintRect.setLTRB(SkScalarMul(srcRect.fLeft, wInv),
1359 SkScalarMul(srcRect.fTop, hInv),
1360 SkScalarMul(srcRect.fRight, wInv),
1361 SkScalarMul(srcRect.fBottom, hInv));
1362
1363 bool needsTextureDomain = false;
1364 if (!(flags & SkCanvas::kBleed_DrawBitmapRectFlag) &&
1365 params.filterMode() != GrTextureParams::kNone_FilterMode) {
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001366 SkASSERT(!bicubic);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001367 // Need texture domain if drawing a sub rect.
1368 needsTextureDomain = srcRect.width() < bitmap.width() ||
1369 srcRect.height() < bitmap.height();
1370 if (needsTextureDomain && fContext->getMatrix().rectStaysRect()) {
1371 const SkMatrix& matrix = fContext->getMatrix();
1372 // sampling is axis-aligned
1373 SkRect transformedRect;
1374 matrix.mapRect(&transformedRect, srcRect);
1375
1376 if (has_aligned_samples(srcRect, transformedRect)) {
1377 // We could also turn off filtering here (but we already did a cache lookup with
1378 // params).
1379 needsTextureDomain = false;
1380 } else {
1381 needsTextureDomain = may_color_bleed(srcRect, transformedRect, matrix);
1382 }
1383 }
1384 }
1385
1386 SkRect textureDomain = SkRect::MakeEmpty();
1387 SkAutoTUnref<GrEffectRef> effect;
1388 if (needsTextureDomain) {
1389 // Use a constrained texture domain to avoid color bleeding
1390 SkScalar left, top, right, bottom;
1391 if (srcRect.width() > SK_Scalar1) {
1392 SkScalar border = SK_ScalarHalf / texture->width();
1393 left = paintRect.left() + border;
1394 right = paintRect.right() - border;
1395 } else {
1396 left = right = SkScalarHalf(paintRect.left() + paintRect.right());
1397 }
1398 if (srcRect.height() > SK_Scalar1) {
1399 SkScalar border = SK_ScalarHalf / texture->height();
1400 top = paintRect.top() + border;
1401 bottom = paintRect.bottom() - border;
1402 } else {
1403 top = bottom = SkScalarHalf(paintRect.top() + paintRect.bottom());
1404 }
1405 textureDomain.setLTRB(left, top, right, bottom);
1406 effect.reset(GrTextureDomainEffect::Create(texture,
1407 SkMatrix::I(),
1408 textureDomain,
1409 GrTextureDomainEffect::kClamp_WrapMode,
1410 params.filterMode()));
commit-bot@chromium.orgdec61502013-12-02 22:22:35 +00001411 } else if (bicubic) {
1412 effect.reset(GrBicubicEffect::Create(texture, SkMatrix::I(), params));
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001413 } else {
1414 effect.reset(GrSimpleTextureEffect::Create(texture, SkMatrix::I(), params));
1415 }
1416
1417 // Construct a GrPaint by setting the bitmap texture as the first effect and then configuring
1418 // the rest from the SkPaint.
1419 GrPaint grPaint;
1420 grPaint.addColorEffect(effect);
1421 bool alphaOnly = !(SkBitmap::kA8_Config == bitmap.config());
1422 if (!skPaint2GrPaintNoShader(this, paint, alphaOnly, false, &grPaint)) {
1423 return;
1424 }
1425
1426 fContext->drawRectToRect(grPaint, dstRect, paintRect, NULL);
1427}
1428
1429static bool filter_texture(SkBaseDevice* device, GrContext* context,
1430 GrTexture* texture, SkImageFilter* filter,
1431 int w, int h, const SkMatrix& ctm, SkBitmap* result,
1432 SkIPoint* offset) {
1433 SkASSERT(filter);
1434 SkDeviceImageFilterProxy proxy(device);
1435
1436 if (filter->canFilterImageGPU()) {
1437 // Save the render target and set it to NULL, so we don't accidentally draw to it in the
1438 // filter. Also set the clip wide open and the matrix to identity.
1439 GrContext::AutoWideOpenIdentityDraw awo(context, NULL);
1440 return filter->filterImageGPU(&proxy, wrap_texture(texture), ctm, result, offset);
1441 } else {
1442 return false;
1443 }
1444}
1445
1446void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
1447 int left, int top, const SkPaint& paint) {
1448 // drawSprite is defined to be in device coords.
1449 CHECK_SHOULD_DRAW(draw, true);
1450
1451 SkAutoLockPixels alp(bitmap, !bitmap.getTexture());
1452 if (!bitmap.getTexture() && !bitmap.readyToDraw()) {
1453 return;
1454 }
1455
1456 int w = bitmap.width();
1457 int h = bitmap.height();
1458
1459 GrTexture* texture;
1460 // draw sprite uses the default texture params
1461 SkAutoCachedTexture act(this, bitmap, NULL, &texture);
1462
1463 SkImageFilter* filter = paint.getImageFilter();
1464 SkIPoint offset = SkIPoint::Make(left, top);
1465 // This bitmap will own the filtered result as a texture.
1466 SkBitmap filteredBitmap;
1467
1468 if (NULL != filter) {
1469 SkMatrix matrix(*draw.fMatrix);
1470 matrix.postTranslate(SkIntToScalar(-left), SkIntToScalar(-top));
1471 if (filter_texture(this, fContext, texture, filter, w, h, matrix, &filteredBitmap,
1472 &offset)) {
1473 texture = (GrTexture*) filteredBitmap.getTexture();
1474 w = filteredBitmap.width();
1475 h = filteredBitmap.height();
1476 } else {
1477 return;
1478 }
1479 }
1480
1481 GrPaint grPaint;
1482 grPaint.addColorTextureEffect(texture, SkMatrix::I());
1483
1484 if(!skPaint2GrPaintNoShader(this, paint, true, false, &grPaint)) {
1485 return;
1486 }
1487
1488 fContext->drawRectToRect(grPaint,
1489 SkRect::MakeXYWH(SkIntToScalar(offset.fX),
1490 SkIntToScalar(offset.fY),
1491 SkIntToScalar(w),
1492 SkIntToScalar(h)),
1493 SkRect::MakeXYWH(0,
1494 0,
1495 SK_Scalar1 * w / texture->width(),
1496 SK_Scalar1 * h / texture->height()));
1497}
1498
1499void SkGpuDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
1500 const SkRect* src, const SkRect& dst,
1501 const SkPaint& paint,
1502 SkCanvas::DrawBitmapRectFlags flags) {
1503 SkMatrix matrix;
1504 SkRect bitmapBounds, tmpSrc;
1505
1506 bitmapBounds.set(0, 0,
1507 SkIntToScalar(bitmap.width()),
1508 SkIntToScalar(bitmap.height()));
1509
1510 // Compute matrix from the two rectangles
1511 if (NULL != src) {
1512 tmpSrc = *src;
1513 } else {
1514 tmpSrc = bitmapBounds;
1515 }
1516 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
1517
1518 // clip the tmpSrc to the bounds of the bitmap. No check needed if src==null.
1519 if (NULL != src) {
1520 if (!bitmapBounds.contains(tmpSrc)) {
1521 if (!tmpSrc.intersect(bitmapBounds)) {
1522 return; // nothing to draw
1523 }
1524 }
1525 }
1526
1527 this->drawBitmapCommon(draw, bitmap, &tmpSrc, matrix, paint, flags);
1528}
1529
1530void SkGpuDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device,
1531 int x, int y, const SkPaint& paint) {
1532 // clear of the source device must occur before CHECK_SHOULD_DRAW
1533 SkGpuDevice* dev = static_cast<SkGpuDevice*>(device);
1534 if (dev->fNeedClear) {
1535 // TODO: could check here whether we really need to draw at all
1536 dev->clear(0x0);
1537 }
1538
1539 // drawDevice is defined to be in device coords.
1540 CHECK_SHOULD_DRAW(draw, true);
1541
1542 GrRenderTarget* devRT = dev->accessRenderTarget();
1543 GrTexture* devTex;
1544 if (NULL == (devTex = devRT->asTexture())) {
1545 return;
1546 }
1547
1548 const SkBitmap& bm = dev->accessBitmap(false);
1549 int w = bm.width();
1550 int h = bm.height();
1551
1552 SkImageFilter* filter = paint.getImageFilter();
1553 // This bitmap will own the filtered result as a texture.
1554 SkBitmap filteredBitmap;
1555
1556 if (NULL != filter) {
1557 SkIPoint offset = SkIPoint::Make(0, 0);
1558 SkMatrix matrix(*draw.fMatrix);
1559 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
1560 if (filter_texture(this, fContext, devTex, filter, w, h, matrix, &filteredBitmap,
1561 &offset)) {
1562 devTex = filteredBitmap.getTexture();
1563 w = filteredBitmap.width();
1564 h = filteredBitmap.height();
1565 x += offset.fX;
1566 y += offset.fY;
1567 } else {
1568 return;
1569 }
1570 }
1571
1572 GrPaint grPaint;
1573 grPaint.addColorTextureEffect(devTex, SkMatrix::I());
1574
1575 if (!skPaint2GrPaintNoShader(this, paint, true, false, &grPaint)) {
1576 return;
1577 }
1578
1579 SkRect dstRect = SkRect::MakeXYWH(SkIntToScalar(x),
1580 SkIntToScalar(y),
1581 SkIntToScalar(w),
1582 SkIntToScalar(h));
1583
1584 // The device being drawn may not fill up its texture (e.g. saveLayer uses approximate
1585 // scratch texture).
1586 SkRect srcRect = SkRect::MakeWH(SK_Scalar1 * w / devTex->width(),
1587 SK_Scalar1 * h / devTex->height());
1588
1589 fContext->drawRectToRect(grPaint, dstRect, srcRect);
1590}
1591
1592bool SkGpuDevice::canHandleImageFilter(SkImageFilter* filter) {
1593 return filter->canFilterImageGPU();
1594}
1595
1596bool SkGpuDevice::filterImage(SkImageFilter* filter, const SkBitmap& src,
1597 const SkMatrix& ctm,
1598 SkBitmap* result, SkIPoint* offset) {
1599 // want explicitly our impl, so guard against a subclass of us overriding it
1600 if (!this->SkGpuDevice::canHandleImageFilter(filter)) {
1601 return false;
1602 }
1603
1604 SkAutoLockPixels alp(src, !src.getTexture());
1605 if (!src.getTexture() && !src.readyToDraw()) {
1606 return false;
1607 }
1608
1609 GrTexture* texture;
1610 // We assume here that the filter will not attempt to tile the src. Otherwise, this cache lookup
1611 // must be pushed upstack.
1612 SkAutoCachedTexture act(this, src, NULL, &texture);
1613
1614 return filter_texture(this, fContext, texture, filter, src.width(), src.height(), ctm, result,
1615 offset);
1616}
1617
1618///////////////////////////////////////////////////////////////////////////////
1619
1620// must be in SkCanvas::VertexMode order
1621static const GrPrimitiveType gVertexMode2PrimitiveType[] = {
1622 kTriangles_GrPrimitiveType,
1623 kTriangleStrip_GrPrimitiveType,
1624 kTriangleFan_GrPrimitiveType,
1625};
1626
1627void SkGpuDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
1628 int vertexCount, const SkPoint vertices[],
1629 const SkPoint texs[], const SkColor colors[],
1630 SkXfermode* xmode,
1631 const uint16_t indices[], int indexCount,
1632 const SkPaint& paint) {
1633 CHECK_SHOULD_DRAW(draw, false);
1634
1635 GrPaint grPaint;
1636 // we ignore the shader if texs is null.
1637 if (NULL == texs) {
1638 if (!skPaint2GrPaintNoShader(this, paint, false, NULL == colors, &grPaint)) {
1639 return;
1640 }
1641 } else {
1642 if (!skPaint2GrPaintShader(this, paint, NULL == colors, &grPaint)) {
1643 return;
1644 }
1645 }
1646
1647 if (NULL != xmode && NULL != texs && NULL != colors) {
1648 if (!SkXfermode::IsMode(xmode, SkXfermode::kModulate_Mode)) {
1649 SkDebugf("Unsupported vertex-color/texture xfer mode.\n");
1650#if 0
1651 return
1652#endif
1653 }
1654 }
1655
1656 SkAutoSTMalloc<128, GrColor> convertedColors(0);
1657 if (NULL != colors) {
1658 // need to convert byte order and from non-PM to PM
1659 convertedColors.reset(vertexCount);
1660 for (int i = 0; i < vertexCount; ++i) {
1661 convertedColors[i] = SkColor2GrColor(colors[i]);
1662 }
1663 colors = convertedColors.get();
1664 }
1665 fContext->drawVertices(grPaint,
1666 gVertexMode2PrimitiveType[vmode],
1667 vertexCount,
1668 (GrPoint*) vertices,
1669 (GrPoint*) texs,
1670 colors,
1671 indices,
1672 indexCount);
1673}
1674
1675///////////////////////////////////////////////////////////////////////////////
1676
1677static void GlyphCacheAuxProc(void* data) {
1678 GrFontScaler* scaler = (GrFontScaler*)data;
1679 SkSafeUnref(scaler);
1680}
1681
1682static GrFontScaler* get_gr_font_scaler(SkGlyphCache* cache) {
1683 void* auxData;
1684 GrFontScaler* scaler = NULL;
1685 if (cache->getAuxProcData(GlyphCacheAuxProc, &auxData)) {
1686 scaler = (GrFontScaler*)auxData;
1687 }
1688 if (NULL == scaler) {
1689 scaler = SkNEW_ARGS(SkGrFontScaler, (cache));
1690 cache->setAuxProc(GlyphCacheAuxProc, scaler);
1691 }
1692 return scaler;
1693}
1694
1695static void SkGPU_Draw1Glyph(const SkDraw1Glyph& state,
1696 SkFixed fx, SkFixed fy,
1697 const SkGlyph& glyph) {
1698 SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
1699
1700 GrSkDrawProcs* procs = static_cast<GrSkDrawProcs*>(state.fDraw->fProcs);
1701
1702 if (NULL == procs->fFontScaler) {
1703 procs->fFontScaler = get_gr_font_scaler(state.fCache);
1704 }
1705
1706 procs->fTextContext->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
1707 glyph.getSubXFixed(),
1708 glyph.getSubYFixed()),
1709 SkFixedFloorToFixed(fx),
1710 SkFixedFloorToFixed(fy),
1711 procs->fFontScaler);
1712}
1713
1714SkDrawProcs* SkGpuDevice::initDrawForText(GrTextContext* context) {
1715
1716 // deferred allocation
1717 if (NULL == fDrawProcs) {
1718 fDrawProcs = SkNEW(GrSkDrawProcs);
1719 fDrawProcs->fD1GProc = SkGPU_Draw1Glyph;
1720 fDrawProcs->fContext = fContext;
1721#if SK_DISTANCEFIELD_FONTS
1722 fDrawProcs->fFlags = 0;
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001723#endif
1724 }
1725
1726 // init our (and GL's) state
1727 fDrawProcs->fTextContext = context;
1728 fDrawProcs->fFontScaler = NULL;
1729 return fDrawProcs;
1730}
1731
1732void SkGpuDevice::drawText(const SkDraw& draw, const void* text,
1733 size_t byteLength, SkScalar x, SkScalar y,
1734 const SkPaint& paint) {
1735 CHECK_SHOULD_DRAW(draw, false);
1736
1737 if (fContext->getMatrix().hasPerspective()) {
1738 // this guy will just call our drawPath()
1739 draw.drawText((const char*)text, byteLength, x, y, paint);
1740 } else {
1741 SkDraw myDraw(draw);
1742
1743 GrPaint grPaint;
1744 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
1745 return;
1746 }
1747#if SK_DISTANCEFIELD_FONTS
commit-bot@chromium.org75a22952013-11-21 15:09:33 +00001748 if (paint.getRasterizer()) {
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001749#endif
commit-bot@chromium.org75a22952013-11-21 15:09:33 +00001750 GrBitmapTextContext context(fContext, grPaint, paint.getColor());
1751 myDraw.fProcs = this->initDrawForText(&context);
1752 this->INHERITED::drawText(myDraw, text, byteLength, x, y, paint);
1753#if SK_DISTANCEFIELD_FONTS
1754 } else {
1755 GrDistanceFieldTextContext context(fContext, grPaint, paint.getColor(),
1756 paint.getTextSize()/SkDrawProcs::kBaseDFFontSize);
1757 myDraw.fProcs = this->initDrawForText(&context);
1758 fDrawProcs->fFlags |= SkDrawProcs::kSkipBakedGlyphTransform_Flag;
1759 fDrawProcs->fFlags |= SkDrawProcs::kUseScaledGlyphs_Flag;
1760 this->INHERITED::drawText(myDraw, text, byteLength, x, y, paint);
1761 fDrawProcs->fFlags = 0;
1762 }
1763#endif
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001764 }
1765}
1766
1767void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text,
1768 size_t byteLength, const SkScalar pos[],
1769 SkScalar constY, int scalarsPerPos,
1770 const SkPaint& paint) {
1771 CHECK_SHOULD_DRAW(draw, false);
1772
1773 if (fContext->getMatrix().hasPerspective()) {
1774 // this guy will just call our drawPath()
1775 draw.drawPosText((const char*)text, byteLength, pos, constY,
1776 scalarsPerPos, paint);
1777 } else {
1778 SkDraw myDraw(draw);
1779
1780 GrPaint grPaint;
1781 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
1782 return;
1783 }
1784#if SK_DISTANCEFIELD_FONTS
commit-bot@chromium.org75a22952013-11-21 15:09:33 +00001785 if (paint.getRasterizer()) {
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001786#endif
commit-bot@chromium.org75a22952013-11-21 15:09:33 +00001787 GrBitmapTextContext context(fContext, grPaint, paint.getColor());
1788 myDraw.fProcs = this->initDrawForText(&context);
1789 this->INHERITED::drawPosText(myDraw, text, byteLength, pos, constY,
1790 scalarsPerPos, paint);
1791#if SK_DISTANCEFIELD_FONTS
1792 } else {
1793 GrDistanceFieldTextContext context(fContext, grPaint, paint.getColor(),
1794 paint.getTextSize()/SkDrawProcs::kBaseDFFontSize);
1795 myDraw.fProcs = this->initDrawForText(&context);
1796 fDrawProcs->fFlags |= SkDrawProcs::kSkipBakedGlyphTransform_Flag;
1797 fDrawProcs->fFlags |= SkDrawProcs::kUseScaledGlyphs_Flag;
1798 this->INHERITED::drawPosText(myDraw, text, byteLength, pos, constY,
1799 scalarsPerPos, paint);
1800 fDrawProcs->fFlags = 0;
1801 }
1802#endif
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +00001803 }
1804}
1805
1806void SkGpuDevice::drawTextOnPath(const SkDraw& draw, const void* text,
1807 size_t len, const SkPath& path,
1808 const SkMatrix* m, const SkPaint& paint) {
1809 CHECK_SHOULD_DRAW(draw, false);
1810
1811 SkASSERT(draw.fDevice == this);
1812 draw.drawTextOnPath((const char*)text, len, path, m, paint);
1813}
1814
1815///////////////////////////////////////////////////////////////////////////////
1816
1817bool SkGpuDevice::filterTextFlags(const SkPaint& paint, TextFlags* flags) {
1818 if (!paint.isLCDRenderText()) {
1819 // we're cool with the paint as is
1820 return false;
1821 }
1822
1823 if (paint.getShader() ||
1824 paint.getXfermode() || // unless its srcover
1825 paint.getMaskFilter() ||
1826 paint.getRasterizer() ||
1827 paint.getColorFilter() ||
1828 paint.getPathEffect() ||
1829 paint.isFakeBoldText() ||
1830 paint.getStyle() != SkPaint::kFill_Style) {
1831 // turn off lcd
1832 flags->fFlags = paint.getFlags() & ~SkPaint::kLCDRenderText_Flag;
1833 flags->fHinting = paint.getHinting();
1834 return true;
1835 }
1836 // we're cool with the paint as is
1837 return false;
1838}
1839
1840void SkGpuDevice::flush() {
1841 DO_DEFERRED_CLEAR();
1842 fContext->resolveRenderTarget(fRenderTarget);
1843}
1844
1845///////////////////////////////////////////////////////////////////////////////
1846
1847SkBaseDevice* SkGpuDevice::onCreateCompatibleDevice(SkBitmap::Config config,
1848 int width, int height,
1849 bool isOpaque,
1850 Usage usage) {
1851 GrTextureDesc desc;
1852 desc.fConfig = fRenderTarget->config();
1853 desc.fFlags = kRenderTarget_GrTextureFlagBit;
1854 desc.fWidth = width;
1855 desc.fHeight = height;
1856 desc.fSampleCnt = fRenderTarget->numSamples();
1857
1858 SkAutoTUnref<GrTexture> texture;
1859 // Skia's convention is to only clear a device if it is non-opaque.
1860 bool needClear = !isOpaque;
1861
1862#if CACHE_COMPATIBLE_DEVICE_TEXTURES
1863 // layers are never draw in repeat modes, so we can request an approx
1864 // match and ignore any padding.
1865 const GrContext::ScratchTexMatch match = (kSaveLayer_Usage == usage) ?
1866 GrContext::kApprox_ScratchTexMatch :
1867 GrContext::kExact_ScratchTexMatch;
1868 texture.reset(fContext->lockAndRefScratchTexture(desc, match));
1869#else
1870 texture.reset(fContext->createUncachedTexture(desc, NULL, 0));
1871#endif
1872 if (NULL != texture.get()) {
1873 return SkNEW_ARGS(SkGpuDevice,(fContext, texture, needClear));
1874 } else {
1875 GrPrintf("---- failed to create compatible device texture [%d %d]\n", width, height);
1876 return NULL;
1877 }
1878}
1879
1880SkGpuDevice::SkGpuDevice(GrContext* context,
1881 GrTexture* texture,
1882 bool needClear)
1883 : SkBitmapDevice(make_bitmap(context, texture->asRenderTarget())) {
1884
1885 SkASSERT(texture && texture->asRenderTarget());
1886 // This constructor is called from onCreateCompatibleDevice. It has locked the RT in the texture
1887 // cache. We pass true for the third argument so that it will get unlocked.
1888 this->initFromRenderTarget(context, texture->asRenderTarget(), true);
1889 fNeedClear = needClear;
1890}