1 /* 2 * Copyright 2015 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 "GrBlurUtils.h" 9 #include "GrRenderTargetContext.h" 10 #include "GrCaps.h" 11 #include "GrContext.h" 12 #include "GrContextPriv.h" 13 #include "GrFixedClip.h" 14 #include "GrRenderTargetContextPriv.h" 15 #include "effects/GrSimpleTextureEffect.h" 16 #include "GrStyle.h" 17 #include "GrTextureProxy.h" 18 #include "SkDraw.h" 19 #include "SkGr.h" 20 #include "SkMaskFilter.h" 21 #include "SkPaint.h" 22 #include "SkTLazy.h" 23 24 static bool clip_bounds_quick_reject(const SkIRect& clipBounds, const SkIRect& rect) { 25 return clipBounds.isEmpty() || rect.isEmpty() || !SkIRect::Intersects(clipBounds, rect); 26 } 27 28 // Draw a mask using the supplied paint. Since the coverage/geometry 29 // is already burnt into the mask this boils down to a rect draw. 30 // Return true if the mask was successfully drawn. 31 static bool draw_mask(GrRenderTargetContext* renderTargetContext, 32 const GrClip& clip, 33 const SkMatrix& viewMatrix, 34 const SkIRect& maskRect, 35 GrPaint&& paint, 36 sk_sp<GrTextureProxy> mask) { 37 SkMatrix inverse; 38 if (!viewMatrix.invert(&inverse)) { 39 return false; 40 } 41 42 SkMatrix matrix = SkMatrix::MakeTrans(-SkIntToScalar(maskRect.fLeft), 43 -SkIntToScalar(maskRect.fTop)); 44 matrix.preConcat(viewMatrix); 45 paint.addCoverageFragmentProcessor(GrSimpleTextureEffect::Make(std::move(mask), 46 nullptr, matrix)); 47 48 renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(), 49 SkRect::Make(maskRect), inverse); 50 return true; 51 } 52 53 static bool sw_draw_with_mask_filter(GrContext* context, 54 GrRenderTargetContext* renderTargetContext, 55 const GrClip& clipData, 56 const SkMatrix& viewMatrix, 57 const SkPath& devPath, 58 const SkMaskFilter* filter, 59 const SkIRect& clipBounds, 60 GrPaint&& paint, 61 SkStrokeRec::InitStyle fillOrHairline) { 62 SkMask srcM, dstM; 63 if (!SkDraw::DrawToMask(devPath, &clipBounds, filter, &viewMatrix, &srcM, 64 SkMask::kComputeBoundsAndRenderImage_CreateMode, fillOrHairline)) { 65 return false; 66 } 67 SkAutoMaskFreeImage autoSrc(srcM.fImage); 68 69 if (!filter->filterMask(&dstM, srcM, viewMatrix, nullptr)) { 70 return false; 71 } 72 // this will free-up dstM when we're done (allocated in filterMask()) 73 SkAutoMaskFreeImage autoDst(dstM.fImage); 74 75 if (clip_bounds_quick_reject(clipBounds, dstM.fBounds)) { 76 return false; 77 } 78 79 // we now have a device-aligned 8bit mask in dstM, ready to be drawn using 80 // the current clip (and identity matrix) and GrPaint settings 81 GrSurfaceDesc desc; 82 desc.fOrigin = kTopLeft_GrSurfaceOrigin; 83 desc.fWidth = dstM.fBounds.width(); 84 desc.fHeight = dstM.fBounds.height(); 85 desc.fConfig = kAlpha_8_GrPixelConfig; 86 87 sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeDeferredSurfaceContext( 88 desc, 89 SkBackingFit::kApprox, 90 SkBudgeted::kYes); 91 if (!sContext) { 92 return false; 93 } 94 95 SkImageInfo ii = SkImageInfo::MakeA8(desc.fWidth, desc.fHeight); 96 if (!sContext->writePixels(ii, dstM.fImage, dstM.fRowBytes, 0, 0)) { 97 return false; 98 } 99 100 return draw_mask(renderTargetContext, clipData, viewMatrix, 101 dstM.fBounds, std::move(paint), sContext->asTextureProxyRef()); 102 } 103 104 // Create a mask of 'devPath' and place the result in 'mask'. 105 static sk_sp<GrTextureProxy> create_mask_GPU(GrContext* context, 106 const SkIRect& maskRect, 107 const SkPath& devPath, 108 SkStrokeRec::InitStyle fillOrHairline, 109 GrAA aa, 110 int sampleCnt) { 111 if (GrAA::kNo == aa) { 112 // Don't need MSAA if mask isn't AA 113 sampleCnt = 0; 114 } 115 116 sk_sp<GrRenderTargetContext> rtContext(context->makeDeferredRenderTargetContextWithFallback( 117 SkBackingFit::kApprox, maskRect.width(), maskRect.height(), kAlpha_8_GrPixelConfig, nullptr, 118 sampleCnt)); 119 if (!rtContext) { 120 return nullptr; 121 } 122 123 rtContext->priv().absClear(nullptr, 0x0); 124 125 GrPaint maskPaint; 126 maskPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op); 127 128 // setup new clip 129 const SkIRect clipRect = SkIRect::MakeWH(maskRect.width(), maskRect.height()); 130 GrFixedClip clip(clipRect); 131 132 // Draw the mask into maskTexture with the path's integerized top-left at 133 // the origin using maskPaint. 134 SkMatrix translate; 135 translate.setTranslate(-SkIntToScalar(maskRect.fLeft), -SkIntToScalar(maskRect.fTop)); 136 rtContext->drawPath(clip, std::move(maskPaint), aa, translate, devPath, 137 GrStyle(fillOrHairline)); 138 return rtContext->asTextureProxyRef(); 139 } 140 141 static void draw_path_with_mask_filter(GrContext* context, 142 GrRenderTargetContext* renderTargetContext, 143 const GrClip& clip, 144 GrPaint&& paint, 145 GrAA aa, 146 const SkMatrix& viewMatrix, 147 const SkMaskFilter* maskFilter, 148 const GrStyle& style, 149 const SkPath* path, 150 bool pathIsMutable) { 151 SkASSERT(maskFilter); 152 153 SkIRect clipBounds; 154 clip.getConservativeBounds(renderTargetContext->width(), 155 renderTargetContext->height(), 156 &clipBounds); 157 SkTLazy<SkPath> tmpPath; 158 SkStrokeRec::InitStyle fillOrHairline; 159 160 // We just fully apply the style here. 161 if (style.applies()) { 162 SkScalar scale = GrStyle::MatrixToScaleFactor(viewMatrix); 163 if (0 == scale || !style.applyToPath(tmpPath.init(), &fillOrHairline, *path, scale)) { 164 return; 165 } 166 pathIsMutable = true; 167 path = tmpPath.get(); 168 } else if (style.isSimpleHairline()) { 169 fillOrHairline = SkStrokeRec::kHairline_InitStyle; 170 } else { 171 SkASSERT(style.isSimpleFill()); 172 fillOrHairline = SkStrokeRec::kFill_InitStyle; 173 } 174 175 // transform the path into device space 176 if (!viewMatrix.isIdentity()) { 177 SkPath* result; 178 if (pathIsMutable) { 179 result = const_cast<SkPath*>(path); 180 } else { 181 if (!tmpPath.isValid()) { 182 tmpPath.init(); 183 } 184 result = tmpPath.get(); 185 } 186 path->transform(viewMatrix, result); 187 path = result; 188 result->setIsVolatile(true); 189 pathIsMutable = true; 190 } 191 192 SkRect maskRect; 193 if (maskFilter->canFilterMaskGPU(SkRRect::MakeRect(path->getBounds()), 194 clipBounds, 195 viewMatrix, 196 &maskRect)) { 197 // This mask will ultimately be drawn as a non-AA rect (see draw_mask). 198 // Non-AA rects have a bad habit of snapping arbitrarily. Integerize here 199 // so the mask draws in a reproducible manner. 200 SkIRect finalIRect; 201 maskRect.roundOut(&finalIRect); 202 if (clip_bounds_quick_reject(clipBounds, finalIRect)) { 203 // clipped out 204 return; 205 } 206 207 if (maskFilter->directFilterMaskGPU(context, 208 renderTargetContext, 209 std::move(paint), 210 clip, 211 viewMatrix, 212 SkStrokeRec(fillOrHairline), 213 *path)) { 214 // the mask filter was able to draw itself directly, so there's nothing 215 // left to do. 216 return; 217 } 218 219 sk_sp<GrTextureProxy> maskProxy(create_mask_GPU(context, 220 finalIRect, 221 *path, 222 fillOrHairline, 223 aa, 224 renderTargetContext->numColorSamples())); 225 if (maskProxy) { 226 sk_sp<GrTextureProxy> filtered = maskFilter->filterMaskGPU(context, 227 std::move(maskProxy), 228 viewMatrix, 229 finalIRect); 230 if (filtered) { 231 if (draw_mask(renderTargetContext, clip, viewMatrix, 232 finalIRect, std::move(paint), std::move(filtered))) { 233 // This path is completely drawn 234 return; 235 } 236 } 237 } 238 } 239 240 sw_draw_with_mask_filter(context, renderTargetContext, clip, viewMatrix, *path, maskFilter, 241 clipBounds, std::move(paint), fillOrHairline); 242 } 243 244 void GrBlurUtils::drawPathWithMaskFilter(GrContext* context, 245 GrRenderTargetContext* renderTargetContext, 246 const GrClip& clip, 247 const SkPath& path, 248 GrPaint&& paint, 249 GrAA aa, 250 const SkMatrix& viewMatrix, 251 const SkMaskFilter* mf, 252 const GrStyle& style, 253 bool pathIsMutable) { 254 draw_path_with_mask_filter(context, renderTargetContext, clip, std::move(paint), aa, viewMatrix, 255 mf, style, &path, pathIsMutable); 256 } 257 258 void GrBlurUtils::drawPathWithMaskFilter(GrContext* context, 259 GrRenderTargetContext* renderTargetContext, 260 const GrClip& clip, 261 const SkPath& origPath, 262 const SkPaint& paint, 263 const SkMatrix& origViewMatrix, 264 const SkMatrix* prePathMatrix, 265 const SkIRect& clipBounds, 266 bool pathIsMutable) { 267 SkASSERT(!pathIsMutable || origPath.isVolatile()); 268 269 GrStyle style(paint); 270 // If we have a prematrix, apply it to the path, optimizing for the case 271 // where the original path can in fact be modified in place (even though 272 // its parameter type is const). 273 274 const SkPath* path = &origPath; 275 SkTLazy<SkPath> tmpPath; 276 277 SkMatrix viewMatrix = origViewMatrix; 278 279 if (prePathMatrix) { 280 // Styling, blurs, and shading are supposed to be applied *after* the prePathMatrix. 281 if (!paint.getMaskFilter() && !paint.getShader() && !style.applies()) { 282 viewMatrix.preConcat(*prePathMatrix); 283 } else { 284 SkPath* result = pathIsMutable ? const_cast<SkPath*>(path) : tmpPath.init(); 285 pathIsMutable = true; 286 path->transform(*prePathMatrix, result); 287 path = result; 288 result->setIsVolatile(true); 289 } 290 } 291 // at this point we're done with prePathMatrix 292 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;) 293 294 GrPaint grPaint; 295 if (!SkPaintToGrPaint(context, renderTargetContext, paint, viewMatrix, &grPaint)) { 296 return; 297 } 298 GrAA aa = GrBoolToAA(paint.isAntiAlias()); 299 SkMaskFilter* mf = paint.getMaskFilter(); 300 if (mf && !mf->asFragmentProcessor(nullptr)) { 301 // The MaskFilter wasn't already handled in SkPaintToGrPaint 302 draw_path_with_mask_filter(context, renderTargetContext, clip, std::move(grPaint), aa, 303 viewMatrix, mf, style, path, pathIsMutable); 304 } else { 305 renderTargetContext->drawPath(clip, std::move(grPaint), aa, viewMatrix, *path, style); 306 } 307 } 308