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