OLD | NEW |
(Empty) | |
| 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 "GrContext.h" |
| 11 #include "effects/GrSimpleTextureEffect.h" |
| 12 #include "GrStrokeInfo.h" |
| 13 #include "GrTexture.h" |
| 14 #include "GrTextureProvider.h" |
| 15 #include "SkDraw.h" |
| 16 #include "SkGr.h" |
| 17 #include "SkMaskFilter.h" |
| 18 #include "SkPaint.h" |
| 19 |
| 20 static bool clip_bounds_quick_reject(const SkIRect& clipBounds, const SkIRect& r
ect) { |
| 21 return clipBounds.isEmpty() || rect.isEmpty() || !SkIRect::Intersects(clipBo
unds, rect); |
| 22 } |
| 23 |
| 24 // Draw a mask using the supplied paint. Since the coverage/geometry |
| 25 // is already burnt into the mask this boils down to a rect draw. |
| 26 // Return true if the mask was successfully drawn. |
| 27 static bool draw_mask(GrDrawContext* drawContext, |
| 28 GrRenderTarget* rt, |
| 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->addCoverageProcessor(GrSimpleTextureEffect::Create(mask, matrix, |
| 39 kDevice_GrCoordSet))
->unref(); |
| 40 |
| 41 SkMatrix inverse; |
| 42 if (!viewMatrix.invert(&inverse)) { |
| 43 return false; |
| 44 } |
| 45 drawContext->drawNonAARectWithLocalMatrix(rt, clip, *grp, SkMatrix::I(), mas
kRect, inverse); |
| 46 return true; |
| 47 } |
| 48 |
| 49 static bool draw_with_mask_filter(GrDrawContext* drawContext, |
| 50 GrTextureProvider* textureProvider, |
| 51 GrRenderTarget* rt, |
| 52 const GrClip& clipData, |
| 53 const SkMatrix& viewMatrix, |
| 54 const SkPath& devPath, |
| 55 SkMaskFilter* filter, |
| 56 const SkIRect& clipBounds, |
| 57 GrPaint* grp, |
| 58 SkPaint::Style style) { |
| 59 SkMask srcM, dstM; |
| 60 |
| 61 if (!SkDraw::DrawToMask(devPath, &clipBounds, filter, &viewMatrix, &srcM, |
| 62 SkMask::kComputeBoundsAndRenderImage_CreateMode, sty
le)) { |
| 63 return false; |
| 64 } |
| 65 SkAutoMaskFreeImage autoSrc(srcM.fImage); |
| 66 |
| 67 if (!filter->filterMask(&dstM, srcM, viewMatrix, NULL)) { |
| 68 return false; |
| 69 } |
| 70 // this will free-up dstM when we're done (allocated in filterMask()) |
| 71 SkAutoMaskFreeImage autoDst(dstM.fImage); |
| 72 |
| 73 if (clip_bounds_quick_reject(clipBounds, dstM.fBounds)) { |
| 74 return false; |
| 75 } |
| 76 |
| 77 // we now have a device-aligned 8bit mask in dstM, ready to be drawn using |
| 78 // the current clip (and identity matrix) and GrPaint settings |
| 79 GrSurfaceDesc desc; |
| 80 desc.fWidth = dstM.fBounds.width(); |
| 81 desc.fHeight = dstM.fBounds.height(); |
| 82 desc.fConfig = kAlpha_8_GrPixelConfig; |
| 83 |
| 84 SkAutoTUnref<GrTexture> texture(textureProvider->refScratchTexture( |
| 85 desc, GrTextureProvider::kApprox_ScratchTexMatch)); |
| 86 if (!texture) { |
| 87 return false; |
| 88 } |
| 89 texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig, |
| 90 dstM.fImage, dstM.fRowBytes); |
| 91 |
| 92 SkRect maskRect = SkRect::Make(dstM.fBounds); |
| 93 |
| 94 return draw_mask(drawContext, rt, clipData, viewMatrix, maskRect, grp, textu
re); |
| 95 } |
| 96 |
| 97 // Create a mask of 'devPath' and place the result in 'mask'. |
| 98 static GrTexture* create_mask_GPU(GrContext* context, |
| 99 const SkRect& maskRect, |
| 100 const SkPath& devPath, |
| 101 const GrStrokeInfo& strokeInfo, |
| 102 bool doAA, |
| 103 int sampleCnt) { |
| 104 GrSurfaceDesc desc; |
| 105 desc.fFlags = kRenderTarget_GrSurfaceFlag; |
| 106 desc.fWidth = SkScalarCeilToInt(maskRect.width()); |
| 107 desc.fHeight = SkScalarCeilToInt(maskRect.height()); |
| 108 desc.fSampleCnt = doAA ? sampleCnt : 0; |
| 109 // We actually only need A8, but it often isn't supported as a |
| 110 // render target so default to RGBA_8888 |
| 111 desc.fConfig = kRGBA_8888_GrPixelConfig; |
| 112 |
| 113 if (context->isConfigRenderable(kAlpha_8_GrPixelConfig, desc.fSampleCnt > 0)
) { |
| 114 desc.fConfig = kAlpha_8_GrPixelConfig; |
| 115 } |
| 116 |
| 117 GrTexture* mask = context->textureProvider()->refScratchTexture( |
| 118 desc, GrTextureProvider::kApprox_ScratchTexMatch); |
| 119 if (NULL == mask) { |
| 120 return NULL; |
| 121 } |
| 122 |
| 123 SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height()); |
| 124 |
| 125 GrDrawContext* drawContext = context->drawContext(); |
| 126 if (!drawContext) { |
| 127 return NULL; |
| 128 } |
| 129 |
| 130 drawContext->clear(mask->asRenderTarget(), NULL, 0x0, true); |
| 131 |
| 132 GrPaint tempPaint; |
| 133 tempPaint.setAntiAlias(doAA); |
| 134 tempPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op); |
| 135 |
| 136 // setup new clip |
| 137 GrClip clip(clipRect); |
| 138 |
| 139 // Draw the mask into maskTexture with the path's top-left at the origin usi
ng tempPaint. |
| 140 SkMatrix translate; |
| 141 translate.setTranslate(-maskRect.fLeft, -maskRect.fTop); |
| 142 drawContext->drawPath(mask->asRenderTarget(), clip, tempPaint, translate, de
vPath, strokeInfo); |
| 143 return mask; |
| 144 } |
| 145 |
| 146 void GrBlurUtils::drawPathWithMaskFilter(GrContext* context, |
| 147 GrDrawContext* drawContext, |
| 148 GrRenderTarget* renderTarget, |
| 149 const GrClip& clip, |
| 150 const SkPath& origSrcPath, |
| 151 const SkPaint& paint, |
| 152 const SkMatrix& origViewMatrix, |
| 153 const SkMatrix* prePathMatrix, |
| 154 const SkIRect& clipBounds, |
| 155 bool pathIsMutable) { |
| 156 SkASSERT(!pathIsMutable || origSrcPath.isVolatile()); |
| 157 |
| 158 GrStrokeInfo strokeInfo(paint); |
| 159 |
| 160 // If we have a prematrix, apply it to the path, optimizing for the case |
| 161 // where the original path can in fact be modified in place (even though |
| 162 // its parameter type is const). |
| 163 SkPath* pathPtr = const_cast<SkPath*>(&origSrcPath); |
| 164 SkTLazy<SkPath> tmpPath; |
| 165 SkTLazy<SkPath> effectPath; |
| 166 SkPathEffect* pathEffect = paint.getPathEffect(); |
| 167 |
| 168 SkMatrix viewMatrix = origViewMatrix; |
| 169 |
| 170 if (prePathMatrix) { |
| 171 // stroking, path effects, and blurs are supposed to be applied *after*
the prePathMatrix. |
| 172 // The pre-path-matrix also should not affect shading. |
| 173 if (NULL == paint.getMaskFilter() && NULL == pathEffect && NULL == paint
.getShader() && |
| 174 (strokeInfo.isFillStyle() || strokeInfo.isHairlineStyle())) { |
| 175 viewMatrix.preConcat(*prePathMatrix); |
| 176 } else { |
| 177 SkPath* result = pathPtr; |
| 178 |
| 179 if (!pathIsMutable) { |
| 180 result = tmpPath.init(); |
| 181 result->setIsVolatile(true); |
| 182 pathIsMutable = true; |
| 183 } |
| 184 // should I push prePathMatrix on our MV stack temporarily, instead |
| 185 // of applying it here? See SkDraw.cpp |
| 186 pathPtr->transform(*prePathMatrix, result); |
| 187 pathPtr = result; |
| 188 } |
| 189 } |
| 190 // at this point we're done with prePathMatrix |
| 191 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;) |
| 192 |
| 193 GrPaint grPaint; |
| 194 if (!SkPaint2GrPaint(context, renderTarget, paint, viewMatrix, true, &grPain
t)) { |
| 195 return; |
| 196 } |
| 197 |
| 198 const SkRect* cullRect = NULL; // TODO: what is our bounds? |
| 199 if (!strokeInfo.isDashed() && pathEffect && pathEffect->filterPath(effectPat
h.init(), *pathPtr, |
| 200 &strokeIn
fo, cullRect)) { |
| 201 pathPtr = effectPath.get(); |
| 202 pathIsMutable = true; |
| 203 } |
| 204 |
| 205 if (paint.getMaskFilter()) { |
| 206 if (!strokeInfo.isHairlineStyle()) { |
| 207 SkPath* strokedPath = pathIsMutable ? pathPtr : tmpPath.init(); |
| 208 if (strokeInfo.isDashed()) { |
| 209 if (pathEffect->filterPath(strokedPath, *pathPtr, &strokeInfo, c
ullRect)) { |
| 210 pathPtr = strokedPath; |
| 211 pathIsMutable = true; |
| 212 } |
| 213 strokeInfo.removeDash(); |
| 214 } |
| 215 if (strokeInfo.applyToPath(strokedPath, *pathPtr)) { |
| 216 pathPtr = strokedPath; |
| 217 pathIsMutable = true; |
| 218 strokeInfo.setFillStyle(); |
| 219 } |
| 220 } |
| 221 |
| 222 // avoid possibly allocating a new path in transform if we can |
| 223 SkPath* devPathPtr = pathIsMutable ? pathPtr : tmpPath.init(); |
| 224 if (!pathIsMutable) { |
| 225 devPathPtr->setIsVolatile(true); |
| 226 } |
| 227 |
| 228 // transform the path into device space |
| 229 pathPtr->transform(viewMatrix, devPathPtr); |
| 230 |
| 231 SkRect maskRect; |
| 232 if (paint.getMaskFilter()->canFilterMaskGPU(devPathPtr->getBounds(), |
| 233 clipBounds, |
| 234 viewMatrix, |
| 235 &maskRect)) { |
| 236 SkIRect finalIRect; |
| 237 maskRect.roundOut(&finalIRect); |
| 238 if (clip_bounds_quick_reject(clipBounds, finalIRect)) { |
| 239 // clipped out |
| 240 return; |
| 241 } |
| 242 |
| 243 if (paint.getMaskFilter()->directFilterMaskGPU(context, |
| 244 renderTarget, |
| 245 &grPaint, |
| 246 clip, |
| 247 viewMatrix, |
| 248 strokeInfo, |
| 249 *devPathPtr)) { |
| 250 // the mask filter was able to draw itself directly, so there's
nothing |
| 251 // left to do. |
| 252 return; |
| 253 } |
| 254 |
| 255 |
| 256 SkAutoTUnref<GrTexture> mask(create_mask_GPU(context, |
| 257 maskRect, |
| 258 *devPathPtr, |
| 259 strokeInfo, |
| 260 grPaint.isAntiAlias(), |
| 261 renderTarget->numSample
s())); |
| 262 if (mask) { |
| 263 GrTexture* filtered; |
| 264 |
| 265 if (paint.getMaskFilter()->filterMaskGPU(mask, viewMatrix, maskR
ect, |
| 266 &filtered, true)) { |
| 267 // filterMaskGPU gives us ownership of a ref to the result |
| 268 SkAutoTUnref<GrTexture> atu(filtered); |
| 269 if (draw_mask(drawContext, |
| 270 renderTarget, |
| 271 clip, |
| 272 viewMatrix, |
| 273 maskRect, |
| 274 &grPaint, |
| 275 filtered)) { |
| 276 // This path is completely drawn |
| 277 return; |
| 278 } |
| 279 } |
| 280 } |
| 281 } |
| 282 |
| 283 // draw the mask on the CPU - this is a fallthrough path in case the |
| 284 // GPU path fails |
| 285 SkPaint::Style style = strokeInfo.isHairlineStyle() ? SkPaint::kStroke_S
tyle : |
| 286 SkPaint::kFill_Sty
le; |
| 287 draw_with_mask_filter(drawContext, context->textureProvider(), renderTar
get, |
| 288 clip, viewMatrix, *devPathPtr, |
| 289 paint.getMaskFilter(), clipBounds, &grPaint, style
); |
| 290 return; |
| 291 } |
| 292 |
| 293 drawContext->drawPath(renderTarget, clip, grPaint, viewMatrix, *pathPtr, str
okeInfo); |
| 294 } |
| 295 |
OLD | NEW |