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 "SkGpuDevice.h"
      9 
     10 #include "GrBlurUtils.h"
     11 #include "GrCaps.h"
     12 #include "GrRenderTargetContext.h"
     13 #include "GrStyle.h"
     14 #include "GrTextureAdjuster.h"
     15 #include "SkDraw.h"
     16 #include "SkGr.h"
     17 #include "SkMaskFilter.h"
     18 #include "effects/GrBicubicEffect.h"
     19 #include "effects/GrSimpleTextureEffect.h"
     20 #include "effects/GrTextureDomain.h"
     21 
     22 static inline bool use_shader(bool textureIsAlphaOnly, const SkPaint& paint) {
     23     return textureIsAlphaOnly && paint.getShader();
     24 }
     25 
     26 //////////////////////////////////////////////////////////////////////////////
     27 //  Helper functions for dropping src rect constraint in bilerp mode.
     28 
     29 static const SkScalar kColorBleedTolerance = 0.001f;
     30 
     31 static bool has_aligned_samples(const SkRect& srcRect, const SkRect& transformedRect) {
     32     // detect pixel disalignment
     33     if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) - transformedRect.left()) < kColorBleedTolerance &&
     34         SkScalarAbs(SkScalarRoundToScalar(transformedRect.top())  - transformedRect.top())  < kColorBleedTolerance &&
     35         SkScalarAbs(transformedRect.width()  - srcRect.width())  < kColorBleedTolerance &&
     36         SkScalarAbs(transformedRect.height() - srcRect.height()) < kColorBleedTolerance) {
     37         return true;
     38     }
     39     return false;
     40 }
     41 
     42 static bool may_color_bleed(const SkRect& srcRect,
     43                             const SkRect& transformedRect,
     44                             const SkMatrix& m,
     45                             GrFSAAType fsaaType) {
     46     // Only gets called if has_aligned_samples returned false.
     47     // So we can assume that sampling is axis aligned but not texel aligned.
     48     SkASSERT(!has_aligned_samples(srcRect, transformedRect));
     49     SkRect innerSrcRect(srcRect), innerTransformedRect, outerTransformedRect(transformedRect);
     50     if (GrFSAAType::kUnifiedMSAA == fsaaType) {
     51         innerSrcRect.inset(SK_Scalar1, SK_Scalar1);
     52     } else {
     53         innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf);
     54     }
     55     m.mapRect(&innerTransformedRect, innerSrcRect);
     56 
     57     // The gap between outerTransformedRect and innerTransformedRect
     58     // represents the projection of the source border area, which is
     59     // problematic for color bleeding.  We must check whether any
     60     // destination pixels sample the border area.
     61     outerTransformedRect.inset(kColorBleedTolerance, kColorBleedTolerance);
     62     innerTransformedRect.outset(kColorBleedTolerance, kColorBleedTolerance);
     63     SkIRect outer, inner;
     64     outerTransformedRect.round(&outer);
     65     innerTransformedRect.round(&inner);
     66     // If the inner and outer rects round to the same result, it means the
     67     // border does not overlap any pixel centers. Yay!
     68     return inner != outer;
     69 }
     70 
     71 static bool can_ignore_bilerp_constraint(const GrTextureProducer& producer,
     72                                          const SkRect& srcRect,
     73                                          const SkMatrix& srcRectToDeviceSpace,
     74                                          GrFSAAType fsaaType) {
     75     if (srcRectToDeviceSpace.rectStaysRect()) {
     76         // sampling is axis-aligned
     77         SkRect transformedRect;
     78         srcRectToDeviceSpace.mapRect(&transformedRect, srcRect);
     79 
     80         if (has_aligned_samples(srcRect, transformedRect) ||
     81             !may_color_bleed(srcRect, transformedRect, srcRectToDeviceSpace, fsaaType)) {
     82             return true;
     83         }
     84     }
     85     return false;
     86 }
     87 
     88 //////////////////////////////////////////////////////////////////////////////
     89 
     90 void SkGpuDevice::drawTextureProducer(GrTextureProducer* producer,
     91                                       const SkRect* srcRect,
     92                                       const SkRect* dstRect,
     93                                       SkCanvas::SrcRectConstraint constraint,
     94                                       const SkMatrix& viewMatrix,
     95                                       const GrClip& clip,
     96                                       const SkPaint& paint) {
     97     // This is the funnel for all non-tiled bitmap/image draw calls. Log a histogram entry.
     98     SK_HISTOGRAM_BOOLEAN("DrawTiled", false);
     99 
    100     // Figure out the actual dst and src rect by clipping the src rect to the bounds of the
    101     // adjuster. If the src rect is clipped then the dst rect must be recomputed. Also determine
    102     // the matrix that maps the src rect to the dst rect.
    103     SkRect clippedSrcRect;
    104     SkRect clippedDstRect;
    105     const SkRect srcBounds = SkRect::MakeIWH(producer->width(), producer->height());
    106     SkMatrix srcToDstMatrix;
    107     if (srcRect) {
    108         if (!dstRect) {
    109             dstRect = &srcBounds;
    110         }
    111         if (!srcBounds.contains(*srcRect)) {
    112             clippedSrcRect = *srcRect;
    113             if (!clippedSrcRect.intersect(srcBounds)) {
    114                 return;
    115             }
    116             if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) {
    117                 return;
    118             }
    119             srcToDstMatrix.mapRect(&clippedDstRect, clippedSrcRect);
    120         } else {
    121             clippedSrcRect = *srcRect;
    122             clippedDstRect = *dstRect;
    123             if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) {
    124                 return;
    125             }
    126         }
    127     } else {
    128         clippedSrcRect = srcBounds;
    129         if (dstRect) {
    130             clippedDstRect = *dstRect;
    131             if (!srcToDstMatrix.setRectToRect(srcBounds, *dstRect, SkMatrix::kFill_ScaleToFit)) {
    132                 return;
    133             }
    134         } else {
    135             clippedDstRect = srcBounds;
    136             srcToDstMatrix.reset();
    137         }
    138     }
    139 
    140     // Now that we have both the view and srcToDst matrices, log our scale factor.
    141     LogDrawScaleFactor(SkMatrix::Concat(viewMatrix, srcToDstMatrix), paint.getFilterQuality());
    142 
    143     this->drawTextureProducerImpl(producer, clippedSrcRect, clippedDstRect, constraint, viewMatrix,
    144                                   srcToDstMatrix, clip, paint);
    145 }
    146 
    147 void SkGpuDevice::drawTextureProducerImpl(GrTextureProducer* producer,
    148                                           const SkRect& clippedSrcRect,
    149                                           const SkRect& clippedDstRect,
    150                                           SkCanvas::SrcRectConstraint constraint,
    151                                           const SkMatrix& viewMatrix,
    152                                           const SkMatrix& srcToDstMatrix,
    153                                           const GrClip& clip,
    154                                           const SkPaint& paint) {
    155     // Specifying the texture coords as local coordinates is an attempt to enable more GrDrawOp
    156     // combining by not baking anything about the srcRect, dstRect, or viewMatrix, into the texture
    157     // FP. In the future this should be an opaque optimization enabled by the combination of
    158     // GrDrawOp/GP and FP.
    159     const SkMaskFilter* mf = paint.getMaskFilter();
    160     // The shader expects proper local coords, so we can't replace local coords with texture coords
    161     // if the shader will be used. If we have a mask filter we will change the underlying geometry
    162     // that is rendered.
    163     bool canUseTextureCoordsAsLocalCoords = !use_shader(producer->isAlphaOnly(), paint) && !mf;
    164 
    165     bool doBicubic;
    166     GrSamplerParams::FilterMode fm =
    167         GrSkFilterQualityToGrFilterMode(paint.getFilterQuality(), viewMatrix, srcToDstMatrix,
    168                                         &doBicubic);
    169     const GrSamplerParams::FilterMode* filterMode = doBicubic ? nullptr : &fm;
    170 
    171     GrTextureProducer::FilterConstraint constraintMode;
    172     if (SkCanvas::kFast_SrcRectConstraint == constraint) {
    173         constraintMode = GrTextureAdjuster::kNo_FilterConstraint;
    174     } else {
    175         constraintMode = GrTextureAdjuster::kYes_FilterConstraint;
    176     }
    177 
    178     // If we have to outset for AA then we will generate texture coords outside the src rect. The
    179     // same happens for any mask filter that extends the bounds rendered in the dst.
    180     // This is conservative as a mask filter does not have to expand the bounds rendered.
    181     bool coordsAllInsideSrcRect = !paint.isAntiAlias() && !mf;
    182 
    183     // Check for optimization to drop the src rect constraint when on bilerp.
    184     if (filterMode && GrSamplerParams::kBilerp_FilterMode == *filterMode &&
    185         GrTextureAdjuster::kYes_FilterConstraint == constraintMode && coordsAllInsideSrcRect) {
    186         SkMatrix combinedMatrix;
    187         combinedMatrix.setConcat(viewMatrix, srcToDstMatrix);
    188         if (can_ignore_bilerp_constraint(*producer, clippedSrcRect, combinedMatrix,
    189                                          fRenderTargetContext->fsaaType())) {
    190             constraintMode = GrTextureAdjuster::kNo_FilterConstraint;
    191         }
    192     }
    193 
    194     const SkMatrix* textureMatrix;
    195     SkMatrix tempMatrix;
    196     if (canUseTextureCoordsAsLocalCoords) {
    197         textureMatrix = &SkMatrix::I();
    198     } else {
    199         if (!srcToDstMatrix.invert(&tempMatrix)) {
    200             return;
    201         }
    202         textureMatrix = &tempMatrix;
    203     }
    204     sk_sp<GrFragmentProcessor> fp(producer->createFragmentProcessor(
    205         *textureMatrix, clippedSrcRect, constraintMode, coordsAllInsideSrcRect, filterMode,
    206         fRenderTargetContext->getColorSpace()));
    207     if (!fp) {
    208         return;
    209     }
    210 
    211     GrPaint grPaint;
    212     if (!SkPaintToGrPaintWithTexture(fContext.get(), fRenderTargetContext.get(), paint, viewMatrix,
    213                                      fp, producer->isAlphaOnly(), &grPaint)) {
    214         return;
    215     }
    216     GrAA aa = GrBoolToAA(paint.isAntiAlias());
    217     if (canUseTextureCoordsAsLocalCoords) {
    218         fRenderTargetContext->fillRectToRect(clip, std::move(grPaint), aa, viewMatrix,
    219                                              clippedDstRect, clippedSrcRect);
    220         return;
    221     }
    222 
    223     if (!mf) {
    224         fRenderTargetContext->drawRect(clip, std::move(grPaint), aa, viewMatrix, clippedDstRect);
    225         return;
    226     }
    227 
    228     // First see if we can do the draw + mask filter direct to the dst.
    229     if (viewMatrix.isScaleTranslate()) {
    230         SkRect devClippedDstRect;
    231         viewMatrix.mapRectScaleTranslate(&devClippedDstRect, clippedDstRect);
    232 
    233         SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
    234         if (mf->directFilterRRectMaskGPU(fContext.get(),
    235                                          fRenderTargetContext.get(),
    236                                          std::move(grPaint),
    237                                          clip,
    238                                          viewMatrix,
    239                                          rec,
    240                                          SkRRect::MakeRect(clippedDstRect),
    241                                          SkRRect::MakeRect(devClippedDstRect))) {
    242             return;
    243         }
    244     }
    245 
    246     SkPath rectPath;
    247     rectPath.addRect(clippedDstRect);
    248     rectPath.setIsVolatile(true);
    249     GrBlurUtils::drawPathWithMaskFilter(this->context(), fRenderTargetContext.get(), this->clip(),
    250                                         rectPath, std::move(grPaint), aa, viewMatrix, mf,
    251                                         GrStyle::SimpleFill(), true);
    252 }
    253