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 "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