blob: f1c87a43db2a47d8bd01bf3ed7f7661459d62dd2 [file] [log] [blame]
robertphillips@google.comf4c2c522012-04-27 12:08:47 +00001
2/*
3 * Copyright 2012 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9#include "GrSoftwarePathRenderer.h"
robertphillips@google.comed4155d2012-05-01 14:30:24 +000010#include "GrContext.h"
robertphillips@google.com58b20212012-06-27 20:44:52 +000011#include "GrSWMaskHelper.h"
robertphillips@google.comf4c2c522012-04-27 12:08:47 +000012
robertphillips@google.comed4155d2012-05-01 14:30:24 +000013////////////////////////////////////////////////////////////////////////////////
robertphillips@google.comf4c2c522012-04-27 12:08:47 +000014bool GrSoftwarePathRenderer::canDrawPath(const SkPath& path,
15 GrPathFill fill,
16 const GrDrawTarget* target,
17 bool antiAlias) const {
robertphillips@google.comed4155d2012-05-01 14:30:24 +000018 if (!antiAlias || NULL == fContext) {
19 // TODO: We could allow the SW path to also handle non-AA paths but
20 // this would mean that GrDefaultPathRenderer would never be called
21 // (since it appears after the SW renderer in the path renderer
22 // chain). Some testing would need to be done r.e. performance
23 // and consistency of the resulting images before removing
24 // the "!antiAlias" clause from the above test
robertphillips@google.comf4c2c522012-04-27 12:08:47 +000025 return false;
26 }
27
robertphillips@google.comed4155d2012-05-01 14:30:24 +000028 return true;
robertphillips@google.comf4c2c522012-04-27 12:08:47 +000029}
30
robertphillips@google.comed4155d2012-05-01 14:30:24 +000031namespace {
32
33////////////////////////////////////////////////////////////////////////////////
robertphillips@google.comed4155d2012-05-01 14:30:24 +000034// gets device coord bounds of path (not considering the fill) and clip. The
35// path bounds will be a subset of the clip bounds. returns false if
36// path bounds would be empty.
37bool get_path_and_clip_bounds(const GrDrawTarget* target,
38 const SkPath& path,
robertphillips@google.com366f1c62012-06-29 21:38:47 +000039 const GrMatrix& matrix,
robertphillips@google.comed4155d2012-05-01 14:30:24 +000040 GrIRect* pathBounds,
41 GrIRect* clipBounds) {
42 // compute bounds as intersection of rt size, clip, and path
43 const GrRenderTarget* rt = target->getDrawState().getRenderTarget();
44 if (NULL == rt) {
45 return false;
46 }
47 *pathBounds = GrIRect::MakeWH(rt->width(), rt->height());
48 const GrClip& clip = target->getClip();
49 if (clip.hasConservativeBounds()) {
50 clip.getConservativeBounds().roundOut(clipBounds);
51 if (!pathBounds->intersect(*clipBounds)) {
52 return false;
53 }
54 } else {
55 // pathBounds is currently the rt extent, set clip bounds to that rect.
56 *clipBounds = *pathBounds;
57 }
robertphillips@google.com366f1c62012-06-29 21:38:47 +000058 if (!path.getBounds().isEmpty()) {
59 GrRect pathSBounds;
60 matrix.mapRect(&pathSBounds, path.getBounds());
robertphillips@google.comed4155d2012-05-01 14:30:24 +000061 GrIRect pathIBounds;
62 pathSBounds.roundOut(&pathIBounds);
63 if (!pathBounds->intersect(pathIBounds)) {
bsalomon@google.com276c1fa2012-06-19 13:22:45 +000064 // set the correct path bounds, as this would be used later.
65 *pathBounds = pathIBounds;
robertphillips@google.comed4155d2012-05-01 14:30:24 +000066 return false;
67 }
68 } else {
bsalomon@google.com276c1fa2012-06-19 13:22:45 +000069 *pathBounds = GrIRect::EmptyIRect();
robertphillips@google.comed4155d2012-05-01 14:30:24 +000070 return false;
71 }
72 return true;
73}
74
75////////////////////////////////////////////////////////////////////////////////
robertphillips@google.comed4155d2012-05-01 14:30:24 +000076void draw_around_inv_path(GrDrawTarget* target,
77 GrDrawState::StageMask stageMask,
78 const GrIRect& clipBounds,
79 const GrIRect& pathBounds) {
80 GrDrawTarget::AutoDeviceCoordDraw adcd(target, stageMask);
81 GrRect rect;
82 if (clipBounds.fTop < pathBounds.fTop) {
83 rect.iset(clipBounds.fLeft, clipBounds.fTop,
84 clipBounds.fRight, pathBounds.fTop);
85 target->drawSimpleRect(rect, NULL, stageMask);
86 }
87 if (clipBounds.fLeft < pathBounds.fLeft) {
88 rect.iset(clipBounds.fLeft, pathBounds.fTop,
89 pathBounds.fLeft, pathBounds.fBottom);
90 target->drawSimpleRect(rect, NULL, stageMask);
91 }
92 if (clipBounds.fRight > pathBounds.fRight) {
93 rect.iset(pathBounds.fRight, pathBounds.fTop,
94 clipBounds.fRight, pathBounds.fBottom);
95 target->drawSimpleRect(rect, NULL, stageMask);
96 }
97 if (clipBounds.fBottom > pathBounds.fBottom) {
98 rect.iset(clipBounds.fLeft, pathBounds.fBottom,
99 clipBounds.fRight, clipBounds.fBottom);
100 target->drawSimpleRect(rect, NULL, stageMask);
101 }
102}
103
104}
105
106////////////////////////////////////////////////////////////////////////////////
107// return true on success; false on failure
robertphillips@google.comf4c2c522012-04-27 12:08:47 +0000108bool GrSoftwarePathRenderer::onDrawPath(const SkPath& path,
109 GrPathFill fill,
110 const GrVec* translate,
111 GrDrawTarget* target,
112 GrDrawState::StageMask stageMask,
113 bool antiAlias) {
114
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000115 if (NULL == fContext) {
116 return false;
117 }
118
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000119 GrDrawState* drawState = target->drawState();
120
121 GrMatrix vm = drawState->getViewMatrix();
122 if (NULL != translate) {
123 vm.postTranslate(translate->fX, translate->fY);
124 }
125
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000126 GrAutoScratchTexture ast;
127 GrIRect pathBounds, clipBounds;
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000128 if (!get_path_and_clip_bounds(target, path, vm,
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000129 &pathBounds, &clipBounds)) {
bsalomon@google.com276c1fa2012-06-19 13:22:45 +0000130 if (GrIsFillInverted(fill)) {
131 draw_around_inv_path(target, stageMask,
132 clipBounds, pathBounds);
133 }
134 return true;
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000135 }
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000136
137 if (GrSWMaskHelper::DrawToTexture(fContext, path, pathBounds, fill,
138 &ast, antiAlias, &vm)) {
robertphillips@google.com15c0fea2012-06-22 12:41:43 +0000139 SkAutoTUnref<GrTexture> texture(ast.detach());
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000140 GrAssert(NULL != texture);
141 GrDrawTarget::AutoDeviceCoordDraw adcd(target, stageMask);
142 enum {
robertphillips@google.com7ff2e372012-05-02 19:27:44 +0000143 // the SW path renderer shares this stage with glyph
144 // rendering (kGlyphMaskStage in GrBatchedTextContext)
145 kPathMaskStage = GrPaint::kTotalStages,
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000146 };
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000147 GrAssert(NULL == drawState->getTexture(kPathMaskStage));
148 drawState->setTexture(kPathMaskStage, texture);
149 drawState->sampler(kPathMaskStage)->reset();
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000150 GrScalar w = GrIntToScalar(pathBounds.width());
151 GrScalar h = GrIntToScalar(pathBounds.height());
152 GrRect maskRect = GrRect::MakeWH(w / texture->width(),
153 h / texture->height());
robertphillips@google.com15c0fea2012-06-22 12:41:43 +0000154
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000155 const GrRect* srcRects[GrDrawState::kNumStages] = {NULL};
156 srcRects[kPathMaskStage] = &maskRect;
157 stageMask |= 1 << kPathMaskStage;
158 GrRect dstRect = GrRect::MakeLTRB(
159 SK_Scalar1* pathBounds.fLeft,
160 SK_Scalar1* pathBounds.fTop,
161 SK_Scalar1* pathBounds.fRight,
162 SK_Scalar1* pathBounds.fBottom);
163 target->drawRect(dstRect, NULL, stageMask, srcRects, NULL);
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000164 drawState->setTexture(kPathMaskStage, NULL);
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000165 if (GrIsFillInverted(fill)) {
166 draw_around_inv_path(target, stageMask,
167 clipBounds, pathBounds);
168 }
169 return true;
170 }
171
robertphillips@google.comf4c2c522012-04-27 12:08:47 +0000172 return false;
173}