blob: d6db0dfdd29287bf6185dbc03db6e0b0525ac401 [file] [log] [blame]
robertphillips@google.comf4c2c522012-04-27 12:08:47 +00001/*
2 * Copyright 2012 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 "GrSoftwarePathRenderer.h"
robertphillips976f5f02016-06-03 10:59:20 -07009#include "GrAuditTrail.h"
10#include "GrClip.h"
bsalomonbb243832016-07-22 07:10:19 -070011#include "GrPipelineBuilder.h"
bsalomon39ef7fb2016-09-21 11:16:05 -070012#include "GrGpuResourcePriv.h"
robertphillips@google.com58b20212012-06-27 20:44:52 +000013#include "GrSWMaskHelper.h"
robertphillips0152d732016-05-20 06:38:43 -070014#include "GrTextureProvider.h"
joshualitt04194f32016-01-13 10:08:27 -080015#include "batches/GrRectBatchFactory.h"
robertphillips@google.comf4c2c522012-04-27 12:08:47 +000016
robertphillips@google.comed4155d2012-05-01 14:30:24 +000017////////////////////////////////////////////////////////////////////////////////
bsalomon0aff2fa2015-07-31 06:48:27 -070018bool GrSoftwarePathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
bsalomon8acedde2016-06-24 10:42:16 -070019 // Pass on any style that applies. The caller will apply the style if a suitable renderer is
20 // not found and try again with the new GrShape.
Brian Salomon0e8fc8b2016-12-09 15:10:07 -050021 return !args.fShape->style().applies() && SkToBool(fTexProvider) &&
22 (args.fAAType == GrAAType::kCoverage || args.fAAType == GrAAType::kNone);
robertphillips@google.comf4c2c522012-04-27 12:08:47 +000023}
24
robertphillips@google.comed4155d2012-05-01 14:30:24 +000025////////////////////////////////////////////////////////////////////////////////
bsalomon39ef7fb2016-09-21 11:16:05 -070026static bool get_unclipped_shape_dev_bounds(const GrShape& shape, const SkMatrix& matrix,
27 SkIRect* devBounds) {
28 SkRect shapeBounds = shape.styledBounds();
29 if (shapeBounds.isEmpty()) {
30 return false;
31 }
32 SkRect shapeDevBounds;
33 matrix.mapRect(&shapeDevBounds, shapeBounds);
34 shapeDevBounds.roundOut(devBounds);
35 return true;
36}
37
38// Gets the shape bounds, the clip bounds, and the intersection (if any). Returns false if there
39// is no intersection.
40static bool get_shape_and_clip_bounds(int width, int height,
41 const GrClip& clip,
42 const GrShape& shape,
43 const SkMatrix& matrix,
44 SkIRect* unclippedDevShapeBounds,
45 SkIRect* clippedDevShapeBounds,
46 SkIRect* devClipBounds) {
robertphillips@google.comed4155d2012-05-01 14:30:24 +000047 // compute bounds as intersection of rt size, clip, and path
robertphillips0152d732016-05-20 06:38:43 -070048 clip.getConservativeBounds(width, height, devClipBounds);
robertphillips@google.com7b112892012-07-31 15:18:21 +000049
bsalomon39ef7fb2016-09-21 11:16:05 -070050 if (!get_unclipped_shape_dev_bounds(shape, matrix, unclippedDevShapeBounds)) {
51 *unclippedDevShapeBounds = SkIRect::EmptyIRect();
52 *clippedDevShapeBounds = SkIRect::EmptyIRect();
robertphillips@google.com3e11c0b2012-07-11 18:20:35 +000053 return false;
robertphillips@google.comed4155d2012-05-01 14:30:24 +000054 }
bsalomon39ef7fb2016-09-21 11:16:05 -070055 if (!clippedDevShapeBounds->intersect(*devClipBounds, *unclippedDevShapeBounds)) {
56 *clippedDevShapeBounds = SkIRect::EmptyIRect();
robertphillips@google.comed4155d2012-05-01 14:30:24 +000057 return false;
58 }
59 return true;
60}
61
62////////////////////////////////////////////////////////////////////////////////
robertphillips976f5f02016-06-03 10:59:20 -070063
Brian Osman11052242016-10-27 14:47:55 -040064void GrSoftwarePathRenderer::DrawNonAARect(GrRenderTargetContext* renderTargetContext,
robertphillips3950f0d2016-07-07 07:33:13 -070065 const GrPaint& paint,
robertphillipsd2b6d642016-07-21 08:55:08 -070066 const GrUserStencilSettings& userStencilSettings,
robertphillips976f5f02016-06-03 10:59:20 -070067 const GrClip& clip,
robertphillips976f5f02016-06-03 10:59:20 -070068 const SkMatrix& viewMatrix,
69 const SkRect& rect,
70 const SkMatrix& localMatrix) {
Brian Salomon24f19782016-12-13 15:10:11 -050071 sk_sp<GrDrawOp> op(GrRectBatchFactory::CreateNonAAFill(paint.getColor(), viewMatrix, rect,
72 nullptr, &localMatrix));
robertphillips976f5f02016-06-03 10:59:20 -070073
Brian Salomon0e8fc8b2016-12-09 15:10:07 -050074 GrPipelineBuilder pipelineBuilder(paint, GrAAType::kNone);
bsalomonbb243832016-07-22 07:10:19 -070075 pipelineBuilder.setUserStencil(&userStencilSettings);
Brian Salomon24f19782016-12-13 15:10:11 -050076 renderTargetContext->addDrawOp(pipelineBuilder, clip, std::move(op));
robertphillips976f5f02016-06-03 10:59:20 -070077}
78
Brian Osman11052242016-10-27 14:47:55 -040079void GrSoftwarePathRenderer::DrawAroundInvPath(GrRenderTargetContext* renderTargetContext,
robertphillips3950f0d2016-07-07 07:33:13 -070080 const GrPaint& paint,
robertphillipsd2b6d642016-07-21 08:55:08 -070081 const GrUserStencilSettings& userStencilSettings,
robertphillips976f5f02016-06-03 10:59:20 -070082 const GrClip& clip,
robertphillips976f5f02016-06-03 10:59:20 -070083 const SkMatrix& viewMatrix,
84 const SkIRect& devClipBounds,
85 const SkIRect& devPathBounds) {
joshualittd27f73e2014-12-29 07:43:36 -080086 SkMatrix invert;
joshualitt8059eb92014-12-29 15:10:07 -080087 if (!viewMatrix.invert(&invert)) {
bsalomon@google.come3d32162012-07-20 13:37:06 +000088 return;
89 }
joshualittd27f73e2014-12-29 07:43:36 -080090
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +000091 SkRect rect;
robertphillips@google.com7b112892012-07-31 15:18:21 +000092 if (devClipBounds.fTop < devPathBounds.fTop) {
rmistry@google.comd6176b02012-08-23 18:14:13 +000093 rect.iset(devClipBounds.fLeft, devClipBounds.fTop,
robertphillips@google.com7b112892012-07-31 15:18:21 +000094 devClipBounds.fRight, devPathBounds.fTop);
Brian Osman11052242016-10-27 14:47:55 -040095 DrawNonAARect(renderTargetContext, paint, userStencilSettings, clip,
robertphillips976f5f02016-06-03 10:59:20 -070096 SkMatrix::I(), rect, invert);
robertphillips@google.comed4155d2012-05-01 14:30:24 +000097 }
robertphillips@google.com7b112892012-07-31 15:18:21 +000098 if (devClipBounds.fLeft < devPathBounds.fLeft) {
rmistry@google.comd6176b02012-08-23 18:14:13 +000099 rect.iset(devClipBounds.fLeft, devPathBounds.fTop,
robertphillips@google.com7b112892012-07-31 15:18:21 +0000100 devPathBounds.fLeft, devPathBounds.fBottom);
Brian Osman11052242016-10-27 14:47:55 -0400101 DrawNonAARect(renderTargetContext, paint, userStencilSettings, clip,
robertphillips976f5f02016-06-03 10:59:20 -0700102 SkMatrix::I(), rect, invert);
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000103 }
robertphillips@google.com7b112892012-07-31 15:18:21 +0000104 if (devClipBounds.fRight > devPathBounds.fRight) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000105 rect.iset(devPathBounds.fRight, devPathBounds.fTop,
robertphillips@google.com7b112892012-07-31 15:18:21 +0000106 devClipBounds.fRight, devPathBounds.fBottom);
Brian Osman11052242016-10-27 14:47:55 -0400107 DrawNonAARect(renderTargetContext, paint, userStencilSettings, clip,
robertphillips976f5f02016-06-03 10:59:20 -0700108 SkMatrix::I(), rect, invert);
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000109 }
robertphillips@google.com7b112892012-07-31 15:18:21 +0000110 if (devClipBounds.fBottom > devPathBounds.fBottom) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000111 rect.iset(devClipBounds.fLeft, devPathBounds.fBottom,
robertphillips@google.com7b112892012-07-31 15:18:21 +0000112 devClipBounds.fRight, devClipBounds.fBottom);
Brian Osman11052242016-10-27 14:47:55 -0400113 DrawNonAARect(renderTargetContext, paint, userStencilSettings, clip,
robertphillips976f5f02016-06-03 10:59:20 -0700114 SkMatrix::I(), rect, invert);
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000115 }
116}
117
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000118////////////////////////////////////////////////////////////////////////////////
119// return true on success; false on failure
bsalomon0aff2fa2015-07-31 06:48:27 -0700120bool GrSoftwarePathRenderer::onDrawPath(const DrawPathArgs& args) {
Brian Osman11052242016-10-27 14:47:55 -0400121 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
robertphillips976f5f02016-06-03 10:59:20 -0700122 "GrSoftwarePathRenderer::onDrawPath");
123 if (!fTexProvider) {
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000124 return false;
125 }
126
bsalomon8acedde2016-06-24 10:42:16 -0700127 // We really need to know if the shape will be inverse filled or not
128 bool inverseFilled = false;
129 SkTLazy<GrShape> tmpShape;
caryclarkd6562002016-07-27 12:02:07 -0700130 SkASSERT(!args.fShape->style().applies());
bsalomon8acedde2016-06-24 10:42:16 -0700131 inverseFilled = args.fShape->inverseFilled();
132
bsalomon39ef7fb2016-09-21 11:16:05 -0700133 SkIRect unclippedDevShapeBounds, clippedDevShapeBounds, devClipBounds;
134 // To prevent overloading the cache with entries during animations we limit the cache of masks
135 // to cases where the matrix preserves axis alignment.
136 bool useCache = fAllowCaching && !inverseFilled && args.fViewMatrix->preservesAxisAlignment() &&
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500137 args.fShape->hasUnstyledKey() && GrAAType::kCoverage == args.fAAType;
bsalomon39ef7fb2016-09-21 11:16:05 -0700138
Brian Osman11052242016-10-27 14:47:55 -0400139 if (!get_shape_and_clip_bounds(args.fRenderTargetContext->width(),
140 args.fRenderTargetContext->height(),
bsalomon8acedde2016-06-24 10:42:16 -0700141 *args.fClip, *args.fShape,
bsalomon39ef7fb2016-09-21 11:16:05 -0700142 *args.fViewMatrix, &unclippedDevShapeBounds,
143 &clippedDevShapeBounds,
144 &devClipBounds)) {
bsalomon8acedde2016-06-24 10:42:16 -0700145 if (inverseFilled) {
Brian Osman11052242016-10-27 14:47:55 -0400146 DrawAroundInvPath(args.fRenderTargetContext, *args.fPaint, *args.fUserStencilSettings,
robertphillips3950f0d2016-07-07 07:33:13 -0700147 *args.fClip,
bsalomon39ef7fb2016-09-21 11:16:05 -0700148 *args.fViewMatrix, devClipBounds, unclippedDevShapeBounds);
bsalomonbb243832016-07-22 07:10:19 -0700149
bsalomon@google.com276c1fa2012-06-19 13:22:45 +0000150 }
151 return true;
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000152 }
robertphillips@google.com366f1c62012-06-29 21:38:47 +0000153
bsalomon39ef7fb2016-09-21 11:16:05 -0700154 const SkIRect* boundsForMask = &clippedDevShapeBounds;
155 if (useCache) {
156 // Use the cache only if >50% of the path is visible.
157 int unclippedWidth = unclippedDevShapeBounds.width();
158 int unclippedHeight = unclippedDevShapeBounds.height();
159 int unclippedArea = unclippedWidth * unclippedHeight;
160 int clippedArea = clippedDevShapeBounds.width() * clippedDevShapeBounds.height();
Brian Osman11052242016-10-27 14:47:55 -0400161 int maxTextureSize = args.fRenderTargetContext->caps()->maxTextureSize();
bsalomon39ef7fb2016-09-21 11:16:05 -0700162 if (unclippedArea > 2 * clippedArea || unclippedWidth > maxTextureSize ||
163 unclippedHeight > maxTextureSize) {
164 useCache = false;
165 } else {
166 boundsForMask = &unclippedDevShapeBounds;
167 }
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000168 }
169
bsalomon39ef7fb2016-09-21 11:16:05 -0700170 GrUniqueKey maskKey;
171 struct KeyData {
172 SkScalar fFractionalTranslateX;
173 SkScalar fFractionalTranslateY;
174 };
175
176 if (useCache) {
177 // We require the upper left 2x2 of the matrix to match exactly for a cache hit.
178 SkScalar sx = args.fViewMatrix->get(SkMatrix::kMScaleX);
179 SkScalar sy = args.fViewMatrix->get(SkMatrix::kMScaleY);
180 SkScalar kx = args.fViewMatrix->get(SkMatrix::kMSkewX);
181 SkScalar ky = args.fViewMatrix->get(SkMatrix::kMSkewY);
182 SkScalar tx = args.fViewMatrix->get(SkMatrix::kMTransX);
183 SkScalar ty = args.fViewMatrix->get(SkMatrix::kMTransY);
184 // Allow 8 bits each in x and y of subpixel positioning.
185 SkFixed fracX = SkScalarToFixed(SkScalarFraction(tx)) & 0x0000FF00;
186 SkFixed fracY = SkScalarToFixed(SkScalarFraction(ty)) & 0x0000FF00;
187 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
188 GrUniqueKey::Builder builder(&maskKey, kDomain, 5 + args.fShape->unstyledKeySize());
189 builder[0] = SkFloat2Bits(sx);
190 builder[1] = SkFloat2Bits(sy);
191 builder[2] = SkFloat2Bits(kx);
192 builder[3] = SkFloat2Bits(ky);
193 builder[4] = fracX | (fracY >> 8);
194 args.fShape->writeUnstyledKey(&builder[5]);
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500195 // FIXME: Doesn't the key need to consider whether we're using AA or not? In practice that
196 // should always be true, though.
bsalomon39ef7fb2016-09-21 11:16:05 -0700197 }
198
199 sk_sp<GrTexture> texture;
200 if (useCache) {
201 texture.reset(args.fResourceProvider->findAndRefTextureByUniqueKey(maskKey));
202 }
203 if (!texture) {
Robert Phillips417b7f42016-12-14 09:12:13 -0500204 SkBackingFit fit = useCache ? SkBackingFit::kExact : SkBackingFit::kApprox;
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500205 GrAA aa = GrAAType::kCoverage == args.fAAType ? GrAA::kYes : GrAA::kNo;
206 texture.reset(GrSWMaskHelper::DrawShapeMaskToTexture(fTexProvider, *args.fShape,
207 *boundsForMask, aa,
Robert Phillips417b7f42016-12-14 09:12:13 -0500208 fit, args.fViewMatrix));
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500209 if (!texture) {
210 return false;
211 }
212 if (useCache) {
213 texture->resourcePriv().setUniqueKey(maskKey);
214 }
bsalomon39ef7fb2016-09-21 11:16:05 -0700215 }
Brian Osman11052242016-10-27 14:47:55 -0400216 GrSWMaskHelper::DrawToTargetWithShapeMask(texture.get(), args.fRenderTargetContext,
217 *args.fPaint, *args.fUserStencilSettings,
robertphillips3950f0d2016-07-07 07:33:13 -0700218 *args.fClip, *args.fViewMatrix,
bsalomon39ef7fb2016-09-21 11:16:05 -0700219 SkIPoint {boundsForMask->fLeft, boundsForMask->fTop},
220 *boundsForMask);
bsalomon8acedde2016-06-24 10:42:16 -0700221 if (inverseFilled) {
Brian Osman11052242016-10-27 14:47:55 -0400222 DrawAroundInvPath(args.fRenderTargetContext, *args.fPaint, *args.fUserStencilSettings,
robertphillips3950f0d2016-07-07 07:33:13 -0700223 *args.fClip,
bsalomon39ef7fb2016-09-21 11:16:05 -0700224 *args.fViewMatrix, devClipBounds, unclippedDevShapeBounds);
robertphillips@google.com5dfb6722012-07-09 16:32:28 +0000225 }
226
227 return true;
robertphillips@google.comf4c2c522012-04-27 12:08:47 +0000228}