Home | History | Annotate | Download | only in gpu
      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