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 "GrDrawContext.h"
     13 #include "GrStrokeInfo.h"
     14 #include "GrTextureParamsAdjuster.h"
     15 #include "SkDraw.h"
     16 #include "SkGrPriv.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                             bool isMSAA) {
     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 (isMSAA) {
     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                                          bool isMSAA) {
     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, isMSAA)) {
     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     this->drawTextureProducerImpl(producer, clippedSrcRect, clippedDstRect, constraint, viewMatrix,
    141                                   srcToDstMatrix, clip, paint);
    142 }
    143 
    144 void SkGpuDevice::drawTextureProducerImpl(GrTextureProducer* producer,
    145                                           const SkRect& clippedSrcRect,
    146                                           const SkRect& clippedDstRect,
    147                                           SkCanvas::SrcRectConstraint constraint,
    148                                           const SkMatrix& viewMatrix,
    149                                           const SkMatrix& srcToDstMatrix,
    150                                           const GrClip& clip,
    151                                           const SkPaint& paint) {
    152     // Specifying the texture coords as local coordinates is an attempt to enable more batching
    153     // by not baking anything about the srcRect, dstRect, or viewMatrix, into the texture FP. In
    154     // the future this should be an opaque optimization enabled by the combination of batch/GP and
    155     // FP.
    156     const SkMaskFilter* mf = paint.getMaskFilter();
    157     // The shader expects proper local coords, so we can't replace local coords with texture coords
    158     // if the shader will be used. If we have a mask filter we will change the underlying geometry
    159     // that is rendered.
    160     bool canUseTextureCoordsAsLocalCoords = !use_shader(producer->isAlphaOnly(), paint) && !mf;
    161 
    162     bool doBicubic;
    163     GrTextureParams::FilterMode fm =
    164         GrSkFilterQualityToGrFilterMode(paint.getFilterQuality(), viewMatrix, srcToDstMatrix,
    165                                         &doBicubic);
    166     const GrTextureParams::FilterMode* filterMode = doBicubic ? nullptr : &fm;
    167 
    168     GrTextureAdjuster::FilterConstraint constraintMode;
    169     if (SkCanvas::kFast_SrcRectConstraint == constraint) {
    170         constraintMode = GrTextureAdjuster::kNo_FilterConstraint;
    171     } else {
    172         constraintMode = GrTextureAdjuster::kYes_FilterConstraint;
    173     }
    174 
    175     // If we have to outset for AA then we will generate texture coords outside the src rect. The
    176     // same happens for any mask filter that extends the bounds rendered in the dst.
    177     // This is conservative as a mask filter does not have to expand the bounds rendered.
    178     bool coordsAllInsideSrcRect = !paint.isAntiAlias() && !mf;
    179 
    180     // Check for optimization to drop the src rect constraint when on bilerp.
    181     if (filterMode && GrTextureParams::kBilerp_FilterMode == *filterMode &&
    182         GrTextureAdjuster::kYes_FilterConstraint == constraintMode && coordsAllInsideSrcRect) {
    183         SkMatrix combinedMatrix;
    184         combinedMatrix.setConcat(viewMatrix, srcToDstMatrix);
    185         if (can_ignore_bilerp_constraint(*producer, clippedSrcRect, combinedMatrix,
    186                                          fRenderTarget->isUnifiedMultisampled())) {
    187             constraintMode = GrTextureAdjuster::kNo_FilterConstraint;
    188         }
    189     }
    190 
    191     const SkMatrix* textureMatrix;
    192     SkMatrix tempMatrix;
    193     if (canUseTextureCoordsAsLocalCoords) {
    194         textureMatrix = &SkMatrix::I();
    195     } else {
    196         if (!srcToDstMatrix.invert(&tempMatrix)) {
    197             return;
    198         }
    199         textureMatrix = &tempMatrix;
    200     }
    201     SkAutoTUnref<const GrFragmentProcessor> fp(producer->createFragmentProcessor(
    202         *textureMatrix, clippedSrcRect, constraintMode, coordsAllInsideSrcRect, filterMode));
    203     if (!fp) {
    204         return;
    205     }
    206 
    207     GrPaint grPaint;
    208     if (!SkPaintToGrPaintWithTexture(fContext, paint, viewMatrix, fp, producer->isAlphaOnly(),
    209                                      &grPaint)) {
    210         return;
    211     }
    212 
    213     if (canUseTextureCoordsAsLocalCoords) {
    214         fDrawContext->fillRectToRect(clip, grPaint, viewMatrix, clippedDstRect, clippedSrcRect);
    215         return;
    216     }
    217 
    218     if (!mf) {
    219         fDrawContext->drawRect(clip, grPaint, viewMatrix, clippedDstRect);
    220         return;
    221     }
    222 
    223     // First see if we can do the draw + mask filter direct to the dst.
    224     SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
    225     SkRRect rrect;
    226     rrect.setRect(clippedDstRect);
    227     if (mf->directFilterRRectMaskGPU(fContext->textureProvider(),
    228                                       fDrawContext,
    229                                       &grPaint,
    230                                       clip,
    231                                       viewMatrix,
    232                                       rec,
    233                                       rrect)) {
    234         return;
    235     }
    236     SkPath rectPath;
    237     rectPath.addRect(clippedDstRect);
    238     rectPath.setIsVolatile(true);
    239     GrBlurUtils::drawPathWithMaskFilter(this->context(), fDrawContext, fClip,
    240                                         rectPath, &grPaint, viewMatrix, mf, paint.getPathEffect(),
    241                                         GrStrokeInfo::FillInfo(), true);
    242 }
    243