1/* 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" 9#include "GrAuditTrail.h" 10#include "GrClip.h" 11#include "GrGpuResourcePriv.h" 12#include "GrPipelineBuilder.h" 13#include "GrResourceProvider.h" 14#include "GrSWMaskHelper.h" 15#include "ops/GrRectOpFactory.h" 16 17//////////////////////////////////////////////////////////////////////////////// 18bool GrSoftwarePathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const { 19 // 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. 21 return !args.fShape->style().applies() && SkToBool(fResourceProvider) && 22 (args.fAAType == GrAAType::kCoverage || args.fAAType == GrAAType::kNone); 23} 24 25//////////////////////////////////////////////////////////////////////////////// 26static 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 // Even though these are "unclipped" bounds we still clip to the int32_t range. 35 // This is the largest int32_t that is representable exactly as a float. The next 63 larger ints 36 // would round down to this value when cast to a float, but who really cares. 37 // INT32_MIN is exactly representable. 38 static constexpr int32_t kMaxInt = 2147483520; 39 if (!shapeDevBounds.intersect(SkRect::MakeLTRB(INT32_MIN, INT32_MIN, kMaxInt, kMaxInt))) { 40 return false; 41 } 42 shapeDevBounds.roundOut(devBounds); 43 return true; 44} 45 46// Gets the shape bounds, the clip bounds, and the intersection (if any). Returns false if there 47// is no intersection. 48static bool get_shape_and_clip_bounds(int width, int height, 49 const GrClip& clip, 50 const GrShape& shape, 51 const SkMatrix& matrix, 52 SkIRect* unclippedDevShapeBounds, 53 SkIRect* clippedDevShapeBounds, 54 SkIRect* devClipBounds) { 55 // compute bounds as intersection of rt size, clip, and path 56 clip.getConservativeBounds(width, height, devClipBounds); 57 58 if (!get_unclipped_shape_dev_bounds(shape, matrix, unclippedDevShapeBounds)) { 59 *unclippedDevShapeBounds = SkIRect::EmptyIRect(); 60 *clippedDevShapeBounds = SkIRect::EmptyIRect(); 61 return false; 62 } 63 if (!clippedDevShapeBounds->intersect(*devClipBounds, *unclippedDevShapeBounds)) { 64 *clippedDevShapeBounds = SkIRect::EmptyIRect(); 65 return false; 66 } 67 return true; 68} 69 70//////////////////////////////////////////////////////////////////////////////// 71 72void GrSoftwarePathRenderer::DrawNonAARect(GrRenderTargetContext* renderTargetContext, 73 GrPaint&& paint, 74 const GrUserStencilSettings& userStencilSettings, 75 const GrClip& clip, 76 const SkMatrix& viewMatrix, 77 const SkRect& rect, 78 const SkMatrix& localMatrix) { 79 std::unique_ptr<GrMeshDrawOp> op(GrRectOpFactory::MakeNonAAFill(paint.getColor(), viewMatrix, 80 rect, nullptr, &localMatrix)); 81 82 GrPipelineBuilder pipelineBuilder(std::move(paint), GrAAType::kNone); 83 pipelineBuilder.setUserStencil(&userStencilSettings); 84 renderTargetContext->addMeshDrawOp(pipelineBuilder, clip, std::move(op)); 85} 86 87void GrSoftwarePathRenderer::DrawAroundInvPath(GrRenderTargetContext* renderTargetContext, 88 GrPaint&& paint, 89 const GrUserStencilSettings& userStencilSettings, 90 const GrClip& clip, 91 const SkMatrix& viewMatrix, 92 const SkIRect& devClipBounds, 93 const SkIRect& devPathBounds) { 94 SkMatrix invert; 95 if (!viewMatrix.invert(&invert)) { 96 return; 97 } 98 99 SkRect rect; 100 if (devClipBounds.fTop < devPathBounds.fTop) { 101 rect.iset(devClipBounds.fLeft, devClipBounds.fTop, 102 devClipBounds.fRight, devPathBounds.fTop); 103 DrawNonAARect(renderTargetContext, GrPaint(paint), userStencilSettings, clip, SkMatrix::I(), 104 rect, invert); 105 } 106 if (devClipBounds.fLeft < devPathBounds.fLeft) { 107 rect.iset(devClipBounds.fLeft, devPathBounds.fTop, 108 devPathBounds.fLeft, devPathBounds.fBottom); 109 DrawNonAARect(renderTargetContext, GrPaint(paint), userStencilSettings, clip, SkMatrix::I(), 110 rect, invert); 111 } 112 if (devClipBounds.fRight > devPathBounds.fRight) { 113 rect.iset(devPathBounds.fRight, devPathBounds.fTop, 114 devClipBounds.fRight, devPathBounds.fBottom); 115 DrawNonAARect(renderTargetContext, GrPaint(paint), userStencilSettings, clip, SkMatrix::I(), 116 rect, invert); 117 } 118 if (devClipBounds.fBottom > devPathBounds.fBottom) { 119 rect.iset(devClipBounds.fLeft, devPathBounds.fBottom, 120 devClipBounds.fRight, devClipBounds.fBottom); 121 DrawNonAARect(renderTargetContext, std::move(paint), userStencilSettings, clip, 122 SkMatrix::I(), rect, invert); 123 } 124} 125 126//////////////////////////////////////////////////////////////////////////////// 127// return true on success; false on failure 128bool GrSoftwarePathRenderer::onDrawPath(const DrawPathArgs& args) { 129 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(), 130 "GrSoftwarePathRenderer::onDrawPath"); 131 if (!fResourceProvider) { 132 return false; 133 } 134 135 // We really need to know if the shape will be inverse filled or not 136 bool inverseFilled = false; 137 SkTLazy<GrShape> tmpShape; 138 SkASSERT(!args.fShape->style().applies()); 139 inverseFilled = args.fShape->inverseFilled(); 140 141 SkIRect unclippedDevShapeBounds, clippedDevShapeBounds, devClipBounds; 142 // To prevent overloading the cache with entries during animations we limit the cache of masks 143 // to cases where the matrix preserves axis alignment. 144 bool useCache = fAllowCaching && !inverseFilled && args.fViewMatrix->preservesAxisAlignment() && 145 args.fShape->hasUnstyledKey() && GrAAType::kCoverage == args.fAAType; 146 147 if (!get_shape_and_clip_bounds(args.fRenderTargetContext->width(), 148 args.fRenderTargetContext->height(), 149 *args.fClip, *args.fShape, 150 *args.fViewMatrix, &unclippedDevShapeBounds, 151 &clippedDevShapeBounds, 152 &devClipBounds)) { 153 if (inverseFilled) { 154 DrawAroundInvPath(args.fRenderTargetContext, std::move(args.fPaint), 155 *args.fUserStencilSettings, *args.fClip, *args.fViewMatrix, 156 devClipBounds, unclippedDevShapeBounds); 157 } 158 return true; 159 } 160 161 const SkIRect* boundsForMask = &clippedDevShapeBounds; 162 if (useCache) { 163 // Use the cache only if >50% of the path is visible. 164 int unclippedWidth = unclippedDevShapeBounds.width(); 165 int unclippedHeight = unclippedDevShapeBounds.height(); 166 int unclippedArea = unclippedWidth * unclippedHeight; 167 int clippedArea = clippedDevShapeBounds.width() * clippedDevShapeBounds.height(); 168 int maxTextureSize = args.fRenderTargetContext->caps()->maxTextureSize(); 169 if (unclippedArea > 2 * clippedArea || unclippedWidth > maxTextureSize || 170 unclippedHeight > maxTextureSize) { 171 useCache = false; 172 } else { 173 boundsForMask = &unclippedDevShapeBounds; 174 } 175 } 176 177 GrUniqueKey maskKey; 178 struct KeyData { 179 SkScalar fFractionalTranslateX; 180 SkScalar fFractionalTranslateY; 181 }; 182 183 if (useCache) { 184 // We require the upper left 2x2 of the matrix to match exactly for a cache hit. 185 SkScalar sx = args.fViewMatrix->get(SkMatrix::kMScaleX); 186 SkScalar sy = args.fViewMatrix->get(SkMatrix::kMScaleY); 187 SkScalar kx = args.fViewMatrix->get(SkMatrix::kMSkewX); 188 SkScalar ky = args.fViewMatrix->get(SkMatrix::kMSkewY); 189 SkScalar tx = args.fViewMatrix->get(SkMatrix::kMTransX); 190 SkScalar ty = args.fViewMatrix->get(SkMatrix::kMTransY); 191 // Allow 8 bits each in x and y of subpixel positioning. 192 SkFixed fracX = SkScalarToFixed(SkScalarFraction(tx)) & 0x0000FF00; 193 SkFixed fracY = SkScalarToFixed(SkScalarFraction(ty)) & 0x0000FF00; 194 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); 195 GrUniqueKey::Builder builder(&maskKey, kDomain, 5 + args.fShape->unstyledKeySize()); 196 builder[0] = SkFloat2Bits(sx); 197 builder[1] = SkFloat2Bits(sy); 198 builder[2] = SkFloat2Bits(kx); 199 builder[3] = SkFloat2Bits(ky); 200 builder[4] = fracX | (fracY >> 8); 201 args.fShape->writeUnstyledKey(&builder[5]); 202 // FIXME: Doesn't the key need to consider whether we're using AA or not? In practice that 203 // should always be true, though. 204 } 205 206 sk_sp<GrTextureProxy> proxy; 207 if (useCache) { 208 proxy = fResourceProvider->findProxyByUniqueKey(maskKey); 209 } 210 if (!proxy) { 211 SkBackingFit fit = useCache ? SkBackingFit::kExact : SkBackingFit::kApprox; 212 GrAA aa = GrAAType::kCoverage == args.fAAType ? GrAA::kYes : GrAA::kNo; 213 proxy = GrSWMaskHelper::DrawShapeMaskToTexture(args.fContext, *args.fShape, 214 *boundsForMask, aa, 215 fit, args.fViewMatrix); 216 if (!proxy) { 217 return false; 218 } 219 if (useCache) { 220 fResourceProvider->assignUniqueKeyToProxy(maskKey, proxy.get()); 221 } 222 } 223 if (inverseFilled) { 224 DrawAroundInvPath(args.fRenderTargetContext, GrPaint(args.fPaint), 225 *args.fUserStencilSettings, *args.fClip, *args.fViewMatrix, devClipBounds, 226 unclippedDevShapeBounds); 227 } 228 GrSWMaskHelper::DrawToTargetWithShapeMask( 229 std::move(proxy), args.fRenderTargetContext, std::move(args.fPaint), 230 *args.fUserStencilSettings, *args.fClip, *args.fViewMatrix, 231 SkIPoint{boundsForMask->fLeft, boundsForMask->fTop}, *boundsForMask); 232 233 return true; 234} 235