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 "GrDrawContext.h" 10 #include "GrCaps.h" 11 #include "GrContext.h" 12 #include "effects/GrSimpleTextureEffect.h" 13 #include "GrStrokeInfo.h" 14 #include "GrTexture.h" 15 #include "GrTextureProvider.h" 16 #include "SkDraw.h" 17 #include "SkGrPriv.h" 18 #include "SkMaskFilter.h" 19 #include "SkPaint.h" 20 21 static bool clip_bounds_quick_reject(const SkIRect& clipBounds, const SkIRect& rect) { 22 return clipBounds.isEmpty() || rect.isEmpty() || !SkIRect::Intersects(clipBounds, rect); 23 } 24 25 // Draw a mask using the supplied paint. Since the coverage/geometry 26 // is already burnt into the mask this boils down to a rect draw. 27 // Return true if the mask was successfully drawn. 28 static bool draw_mask(GrDrawContext* drawContext, 29 const GrClip& clip, 30 const SkMatrix& viewMatrix, 31 const SkRect& maskRect, 32 GrPaint* grp, 33 GrTexture* mask) { 34 SkMatrix matrix; 35 matrix.setTranslate(-maskRect.fLeft, -maskRect.fTop); 36 matrix.postIDiv(mask->width(), mask->height()); 37 38 grp->addCoverageFragmentProcessor(GrSimpleTextureEffect::Create(mask, matrix, 39 kDevice_GrCoordSet))->unref(); 40 41 SkMatrix inverse; 42 if (!viewMatrix.invert(&inverse)) { 43 return false; 44 } 45 drawContext->fillRectWithLocalMatrix(clip, *grp, SkMatrix::I(), maskRect, inverse); 46 return true; 47 } 48 49 static bool sw_draw_with_mask_filter(GrDrawContext* drawContext, 50 GrTextureProvider* textureProvider, 51 const GrClip& clipData, 52 const SkMatrix& viewMatrix, 53 const SkPath& devPath, 54 const SkMaskFilter* filter, 55 const SkIRect& clipBounds, 56 GrPaint* grp, 57 SkPaint::Style style) { 58 SkMask srcM, dstM; 59 60 if (!SkDraw::DrawToMask(devPath, &clipBounds, filter, &viewMatrix, &srcM, 61 SkMask::kComputeBoundsAndRenderImage_CreateMode, style)) { 62 return false; 63 } 64 SkAutoMaskFreeImage autoSrc(srcM.fImage); 65 66 if (!filter->filterMask(&dstM, srcM, viewMatrix, nullptr)) { 67 return false; 68 } 69 // this will free-up dstM when we're done (allocated in filterMask()) 70 SkAutoMaskFreeImage autoDst(dstM.fImage); 71 72 if (clip_bounds_quick_reject(clipBounds, dstM.fBounds)) { 73 return false; 74 } 75 76 // we now have a device-aligned 8bit mask in dstM, ready to be drawn using 77 // the current clip (and identity matrix) and GrPaint settings 78 GrSurfaceDesc desc; 79 desc.fWidth = dstM.fBounds.width(); 80 desc.fHeight = dstM.fBounds.height(); 81 desc.fConfig = kAlpha_8_GrPixelConfig; 82 83 SkAutoTUnref<GrTexture> texture(textureProvider->createApproxTexture(desc)); 84 if (!texture) { 85 return false; 86 } 87 texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig, 88 dstM.fImage, dstM.fRowBytes); 89 90 SkRect maskRect = SkRect::Make(dstM.fBounds); 91 92 return draw_mask(drawContext, clipData, viewMatrix, maskRect, grp, texture); 93 } 94 95 // Create a mask of 'devPath' and place the result in 'mask'. 96 static GrTexture* create_mask_GPU(GrContext* context, 97 SkRect* maskRect, 98 const SkPath& devPath, 99 const GrStrokeInfo& strokeInfo, 100 bool doAA, 101 int sampleCnt) { 102 // This mask will ultimately be drawn as a non-AA rect (see draw_mask). 103 // Non-AA rects have a bad habit of snapping arbitrarily. Integerize here 104 // so the mask draws in a reproducible manner. 105 *maskRect = SkRect::Make(maskRect->roundOut()); 106 107 GrSurfaceDesc desc; 108 desc.fFlags = kRenderTarget_GrSurfaceFlag; 109 desc.fWidth = SkScalarCeilToInt(maskRect->width()); 110 desc.fHeight = SkScalarCeilToInt(maskRect->height()); 111 desc.fSampleCnt = doAA ? sampleCnt : 0; 112 // We actually only need A8, but it often isn't supported as a 113 // render target so default to RGBA_8888 114 desc.fConfig = kRGBA_8888_GrPixelConfig; 115 116 if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, desc.fSampleCnt > 0)) { 117 desc.fConfig = kAlpha_8_GrPixelConfig; 118 } 119 120 GrTexture* mask = context->textureProvider()->createApproxTexture(desc); 121 if (nullptr == mask) { 122 return nullptr; 123 } 124 125 SkRect clipRect = SkRect::MakeWH(maskRect->width(), maskRect->height()); 126 127 SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(mask->asRenderTarget())); 128 if (!drawContext) { 129 return nullptr; 130 } 131 132 drawContext->clear(nullptr, 0x0, true); 133 134 GrPaint tempPaint; 135 tempPaint.setAntiAlias(doAA); 136 tempPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op); 137 138 // setup new clip 139 GrClip clip(clipRect); 140 141 // Draw the mask into maskTexture with the path's integerized top-left at 142 // the origin using tempPaint. 143 SkMatrix translate; 144 translate.setTranslate(-maskRect->fLeft, -maskRect->fTop); 145 drawContext->drawPath(clip, tempPaint, translate, devPath, strokeInfo); 146 return mask; 147 } 148 149 static void draw_path_with_mask_filter(GrContext* context, 150 GrDrawContext* drawContext, 151 const GrClip& clip, 152 GrPaint* paint, 153 const SkMatrix& viewMatrix, 154 const SkMaskFilter* maskFilter, 155 const SkPathEffect* pathEffect, 156 const GrStrokeInfo& origStrokeInfo, 157 SkPath* pathPtr, 158 bool pathIsMutable) { 159 SkASSERT(maskFilter); 160 161 SkIRect clipBounds; 162 clip.getConservativeBounds(drawContext->width(), drawContext->height(), &clipBounds); 163 SkTLazy<SkPath> tmpPath; 164 GrStrokeInfo strokeInfo(origStrokeInfo); 165 166 static const SkRect* cullRect = nullptr; // TODO: what is our bounds? 167 168 SkASSERT(strokeInfo.isDashed() || !pathEffect); 169 170 if (!strokeInfo.isHairlineStyle()) { 171 SkPath* strokedPath = pathIsMutable ? pathPtr : tmpPath.init(); 172 if (strokeInfo.isDashed()) { 173 if (pathEffect->filterPath(strokedPath, *pathPtr, &strokeInfo, cullRect)) { 174 pathPtr = strokedPath; 175 pathPtr->setIsVolatile(true); 176 pathIsMutable = true; 177 } 178 strokeInfo.removeDash(); 179 } 180 if (strokeInfo.applyToPath(strokedPath, *pathPtr)) { 181 // Apply the stroke to the path if there is one 182 pathPtr = strokedPath; 183 pathPtr->setIsVolatile(true); 184 pathIsMutable = true; 185 strokeInfo.setFillStyle(); 186 } 187 } 188 189 // avoid possibly allocating a new path in transform if we can 190 SkPath* devPathPtr = pathIsMutable ? pathPtr : tmpPath.init(); 191 if (!pathIsMutable) { 192 devPathPtr->setIsVolatile(true); 193 } 194 195 // transform the path into device space 196 pathPtr->transform(viewMatrix, devPathPtr); 197 198 SkRect maskRect; 199 if (maskFilter->canFilterMaskGPU(SkRRect::MakeRect(devPathPtr->getBounds()), 200 clipBounds, 201 viewMatrix, 202 &maskRect)) { 203 SkIRect finalIRect; 204 maskRect.roundOut(&finalIRect); 205 if (clip_bounds_quick_reject(clipBounds, finalIRect)) { 206 // clipped out 207 return; 208 } 209 210 if (maskFilter->directFilterMaskGPU(context->textureProvider(), 211 drawContext, 212 paint, 213 clip, 214 viewMatrix, 215 strokeInfo, 216 *devPathPtr)) { 217 // the mask filter was able to draw itself directly, so there's nothing 218 // left to do. 219 return; 220 } 221 222 SkAutoTUnref<GrTexture> mask(create_mask_GPU(context, 223 &maskRect, 224 *devPathPtr, 225 strokeInfo, 226 paint->isAntiAlias(), 227 drawContext->numColorSamples())); 228 if (mask) { 229 GrTexture* filtered; 230 231 if (maskFilter->filterMaskGPU(mask, viewMatrix, maskRect, &filtered, true)) { 232 // filterMaskGPU gives us ownership of a ref to the result 233 SkAutoTUnref<GrTexture> atu(filtered); 234 if (draw_mask(drawContext, clip, viewMatrix, maskRect, paint, filtered)) { 235 // This path is completely drawn 236 return; 237 } 238 } 239 } 240 } 241 242 // draw the mask on the CPU - this is a fallthrough path in case the 243 // GPU path fails 244 SkPaint::Style style = strokeInfo.isHairlineStyle() ? SkPaint::kStroke_Style : 245 SkPaint::kFill_Style; 246 sw_draw_with_mask_filter(drawContext, context->textureProvider(), 247 clip, viewMatrix, *devPathPtr, 248 maskFilter, clipBounds, paint, style); 249 } 250 251 void GrBlurUtils::drawPathWithMaskFilter(GrContext* context, 252 GrDrawContext* drawContext, 253 const GrClip& clip, 254 const SkPath& origPath, 255 GrPaint* paint, 256 const SkMatrix& viewMatrix, 257 const SkMaskFilter* mf, 258 const SkPathEffect* pathEffect, 259 const GrStrokeInfo& origStrokeInfo, 260 bool pathIsMutable) { 261 SkPath* pathPtr = const_cast<SkPath*>(&origPath); 262 263 SkTLazy<SkPath> tmpPath; 264 GrStrokeInfo strokeInfo(origStrokeInfo); 265 266 if (!strokeInfo.isDashed() && pathEffect && pathEffect->filterPath(tmpPath.init(), *pathPtr, 267 &strokeInfo, nullptr)) { 268 pathPtr = tmpPath.get(); 269 pathPtr->setIsVolatile(true); 270 pathIsMutable = true; 271 pathEffect = nullptr; 272 } 273 274 draw_path_with_mask_filter(context, drawContext, clip, paint, viewMatrix, mf, pathEffect, 275 strokeInfo, pathPtr, pathIsMutable); 276 } 277 278 void GrBlurUtils::drawPathWithMaskFilter(GrContext* context, 279 GrDrawContext* drawContext, 280 const GrClip& clip, 281 const SkPath& origSrcPath, 282 const SkPaint& paint, 283 const SkMatrix& origViewMatrix, 284 const SkMatrix* prePathMatrix, 285 const SkIRect& clipBounds, 286 bool pathIsMutable) { 287 SkASSERT(!pathIsMutable || origSrcPath.isVolatile()); 288 289 GrStrokeInfo strokeInfo(paint); 290 // comment out the line below to determine if it is the reason that the chrome mac perf bot 291 // has begun crashing 292 // strokeInfo.setResScale(SkDraw::ComputeResScaleForStroking(origViewMatrix)); 293 294 // If we have a prematrix, apply it to the path, optimizing for the case 295 // where the original path can in fact be modified in place (even though 296 // its parameter type is const). 297 SkPath* pathPtr = const_cast<SkPath*>(&origSrcPath); 298 SkTLazy<SkPath> tmpPath; 299 SkTLazy<SkPath> effectPath; 300 SkPathEffect* pathEffect = paint.getPathEffect(); 301 302 SkMatrix viewMatrix = origViewMatrix; 303 304 if (prePathMatrix) { 305 // stroking, path effects, and blurs are supposed to be applied *after* the prePathMatrix. 306 // The pre-path-matrix also should not affect shading. 307 if (!paint.getMaskFilter() && !pathEffect && !paint.getShader() && 308 (strokeInfo.isFillStyle() || strokeInfo.isHairlineStyle())) { 309 viewMatrix.preConcat(*prePathMatrix); 310 } else { 311 SkPath* result = pathPtr; 312 313 if (!pathIsMutable) { 314 result = tmpPath.init(); 315 result->setIsVolatile(true); 316 pathIsMutable = true; 317 } 318 // should I push prePathMatrix on our MV stack temporarily, instead 319 // of applying it here? See SkDraw.cpp 320 pathPtr->transform(*prePathMatrix, result); 321 pathPtr = result; 322 } 323 } 324 // at this point we're done with prePathMatrix 325 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;) 326 327 SkTLazy<SkPath> tmpPath2; 328 329 if (!strokeInfo.isDashed() && pathEffect && 330 pathEffect->filterPath(tmpPath2.init(), *pathPtr, &strokeInfo, nullptr)) { 331 pathPtr = tmpPath2.get(); 332 pathPtr->setIsVolatile(true); 333 pathIsMutable = true; 334 pathEffect = nullptr; 335 } 336 337 GrPaint grPaint; 338 if (!SkPaintToGrPaint(context, paint, viewMatrix, &grPaint)) { 339 return; 340 } 341 342 if (paint.getMaskFilter()) { 343 draw_path_with_mask_filter(context, drawContext, clip, &grPaint, viewMatrix, 344 paint.getMaskFilter(), pathEffect, strokeInfo, 345 pathPtr, pathIsMutable); 346 } else { 347 drawContext->drawPath(clip, grPaint, viewMatrix, *pathPtr, strokeInfo); 348 } 349 } 350 351