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 "SkMaskFilterBase.h" 21#include "SkPaint.h" 22#include "SkTLazy.h" 23 24static 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. 31static 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), matrix)); 46 47 renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(), 48 SkRect::Make(maskRect), inverse); 49 return true; 50} 51 52static bool sw_draw_with_mask_filter(GrContext* context, 53 GrRenderTargetContext* renderTargetContext, 54 const GrClip& clipData, 55 const SkMatrix& viewMatrix, 56 const SkPath& devPath, 57 const SkMaskFilter* filter, 58 const SkIRect& clipBounds, 59 GrPaint&& paint, 60 SkStrokeRec::InitStyle fillOrHairline) { 61 SkMask srcM, dstM; 62 if (!SkDraw::DrawToMask(devPath, &clipBounds, filter, &viewMatrix, &srcM, 63 SkMask::kComputeBoundsAndRenderImage_CreateMode, fillOrHairline)) { 64 return false; 65 } 66 SkAutoMaskFreeImage autoSrc(srcM.fImage); 67 68 if (!as_MFB(filter)->filterMask(&dstM, srcM, viewMatrix, nullptr)) { 69 return false; 70 } 71 // this will free-up dstM when we're done (allocated in filterMask()) 72 SkAutoMaskFreeImage autoDst(dstM.fImage); 73 74 if (clip_bounds_quick_reject(clipBounds, dstM.fBounds)) { 75 return false; 76 } 77 78 // we now have a device-aligned 8bit mask in dstM, ready to be drawn using 79 // the current clip (and identity matrix) and GrPaint settings 80 GrSurfaceDesc desc; 81 desc.fOrigin = kTopLeft_GrSurfaceOrigin; 82 desc.fWidth = dstM.fBounds.width(); 83 desc.fHeight = dstM.fBounds.height(); 84 desc.fConfig = kAlpha_8_GrPixelConfig; 85 86 sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeDeferredSurfaceContext( 87 desc, 88 GrMipMapped::kNo, 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'. 105static 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 = 1; 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 141static 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 SkMaskFilterBase* 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 244void 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 as_MFB(mf), style, &path, pathIsMutable); 256} 257 258void 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->colorSpaceInfo(), paint, viewMatrix, 296 &grPaint)) { 297 return; 298 } 299 GrAA aa = GrAA(paint.isAntiAlias()); 300 SkMaskFilterBase* mf = as_MFB(paint.getMaskFilter()); 301 if (mf && !mf->hasFragmentProcessor()) { 302 // The MaskFilter wasn't already handled in SkPaintToGrPaint 303 draw_path_with_mask_filter(context, renderTargetContext, clip, std::move(grPaint), aa, 304 viewMatrix, mf, style, path, pathIsMutable); 305 } else { 306 renderTargetContext->drawPath(clip, std::move(grPaint), aa, viewMatrix, *path, style); 307 } 308} 309