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 #include "GrBlurUtils.h"
     10 #include "GrCaps.h"
     11 #include "GrColorSpaceXform.h"
     12 #include "GrRenderTargetContext.h"
     13 #include "GrStyle.h"
     14 #include "GrTextureAdjuster.h"
     15 #include "GrTextureMaker.h"
     16 #include "SkDraw.h"
     17 #include "SkGr.h"
     18 #include "SkMaskFilterBase.h"
     19 #include "effects/GrBicubicEffect.h"
     20 #include "effects/GrSimpleTextureEffect.h"
     21 #include "effects/GrTextureDomain.h"
     22 
     23 static inline bool use_shader(bool textureIsAlphaOnly, const SkPaint& paint) {
     24     return textureIsAlphaOnly && paint.getShader();
     25 }
     26 
     27 //////////////////////////////////////////////////////////////////////////////
     28 //  Helper functions for dropping src rect constraint in bilerp mode.
     29 
     30 static const SkScalar kColorBleedTolerance = 0.001f;
     31 
     32 static bool has_aligned_samples(const SkRect& srcRect, const SkRect& transformedRect) {
     33     // detect pixel disalignment
     34     if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) - transformedRect.left()) < kColorBleedTolerance &&
     35         SkScalarAbs(SkScalarRoundToScalar(transformedRect.top())  - transformedRect.top())  < kColorBleedTolerance &&
     36         SkScalarAbs(transformedRect.width()  - srcRect.width())  < kColorBleedTolerance &&
     37         SkScalarAbs(transformedRect.height() - srcRect.height()) < kColorBleedTolerance) {
     38         return true;
     39     }
     40     return false;
     41 }
     42 
     43 static bool may_color_bleed(const SkRect& srcRect,
     44                             const SkRect& transformedRect,
     45                             const SkMatrix& m,
     46                             GrFSAAType fsaaType) {
     47     // Only gets called if has_aligned_samples returned false.
     48     // So we can assume that sampling is axis aligned but not texel aligned.
     49     SkASSERT(!has_aligned_samples(srcRect, transformedRect));
     50     SkRect innerSrcRect(srcRect), innerTransformedRect, outerTransformedRect(transformedRect);
     51     if (GrFSAAType::kUnifiedMSAA == fsaaType) {
     52         innerSrcRect.inset(SK_Scalar1, SK_Scalar1);
     53     } else {
     54         innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf);
     55     }
     56     m.mapRect(&innerTransformedRect, innerSrcRect);
     57 
     58     // The gap between outerTransformedRect and innerTransformedRect
     59     // represents the projection of the source border area, which is
     60     // problematic for color bleeding.  We must check whether any
     61     // destination pixels sample the border area.
     62     outerTransformedRect.inset(kColorBleedTolerance, kColorBleedTolerance);
     63     innerTransformedRect.outset(kColorBleedTolerance, kColorBleedTolerance);
     64     SkIRect outer, inner;
     65     outerTransformedRect.round(&outer);
     66     innerTransformedRect.round(&inner);
     67     // If the inner and outer rects round to the same result, it means the
     68     // border does not overlap any pixel centers. Yay!
     69     return inner != outer;
     70 }
     71 
     72 static bool can_ignore_bilerp_constraint(const GrTextureProducer& producer,
     73                                          const SkRect& srcRect,
     74                                          const SkMatrix& srcRectToDeviceSpace,
     75                                          GrFSAAType fsaaType) {
     76     if (srcRectToDeviceSpace.rectStaysRect()) {
     77         // sampling is axis-aligned
     78         SkRect transformedRect;
     79         srcRectToDeviceSpace.mapRect(&transformedRect, srcRect);
     80 
     81         if (has_aligned_samples(srcRect, transformedRect) ||
     82             !may_color_bleed(srcRect, transformedRect, srcRectToDeviceSpace, fsaaType)) {
     83             return true;
     84         }
     85     }
     86     return false;
     87 }
     88 
     89 /**
     90  * Checks whether the paint, matrix, and constraint are compatible with using
     91  * GrRenderTargetContext::drawTextureAffine. It is more effecient than the GrTextureProducer
     92  * general case.
     93  */
     94 static bool can_use_draw_texture_affine(const SkPaint& paint, GrAA aa, const SkMatrix& ctm,
     95                                         SkCanvas::SrcRectConstraint constraint) {
     96 // This is disabled in Chrome until crbug.com/802408 and crbug.com/801783 can be sorted out.
     97 #ifdef SK_DISABLE_TEXTURE_OP_AA
     98     if (GrAA::kYes == aa) {
     99         return false;
    100     }
    101 #endif
    102     return (!paint.getColorFilter() && !paint.getShader() && !paint.getMaskFilter() &&
    103             !paint.getImageFilter() && paint.getFilterQuality() < kMedium_SkFilterQuality &&
    104             paint.getBlendMode() == SkBlendMode::kSrcOver && !ctm.hasPerspective() &&
    105             SkCanvas::kFast_SrcRectConstraint == constraint);
    106 }
    107 
    108 static void draw_texture_affine(const SkPaint& paint, const SkMatrix& ctm, const SkRect* src,
    109                                 const SkRect* dst, GrAA aa, sk_sp<GrTextureProxy> proxy,
    110                                 SkColorSpace* colorSpace, const GrClip& clip,
    111                                 GrRenderTargetContext* rtc) {
    112     SkASSERT(!(SkToBool(src) && !SkToBool(dst)));
    113     SkRect srcRect = src ? *src : SkRect::MakeWH(proxy->width(), proxy->height());
    114     SkRect dstRect = dst ? *dst : srcRect;
    115     if (src && !SkRect::MakeIWH(proxy->width(), proxy->height()).contains(srcRect)) {
    116         // Shrink the src rect to be within bounds and proportionately shrink the dst rect.
    117         SkMatrix srcToDst;
    118         srcToDst.setRectToRect(srcRect, dstRect, SkMatrix::kFill_ScaleToFit);
    119         SkAssertResult(srcRect.intersect(SkRect::MakeIWH(proxy->width(), proxy->height())));
    120         srcToDst.mapRect(&dstRect, srcRect);
    121     }
    122     auto csxf = GrColorSpaceXform::Make(colorSpace, proxy->config(),
    123                                         rtc->colorSpaceInfo().colorSpace());
    124     GrSamplerState::Filter filter;
    125     switch (paint.getFilterQuality()) {
    126         case kNone_SkFilterQuality:
    127             filter = GrSamplerState::Filter::kNearest;
    128             break;
    129         case kLow_SkFilterQuality:
    130             filter = GrSamplerState::Filter::kBilerp;
    131             break;
    132         case kMedium_SkFilterQuality:
    133         case kHigh_SkFilterQuality:
    134             SK_ABORT("Quality level not allowed.");
    135     }
    136     GrColor color = GrPixelConfigIsAlphaOnly(proxy->config())
    137                             ? SkColorToPremulGrColor(paint.getColor())
    138                             : SkColorAlphaToGrColor(paint.getColor());
    139     rtc->drawTextureAffine(clip, std::move(proxy), filter, color, srcRect, dstRect, aa, ctm,
    140                            std::move(csxf));
    141 }
    142 
    143 //////////////////////////////////////////////////////////////////////////////
    144 
    145 void SkGpuDevice::drawPinnedTextureProxy(sk_sp<GrTextureProxy> proxy, uint32_t pinnedUniqueID,
    146                                          SkColorSpace* colorSpace, SkAlphaType alphaType,
    147                                          const SkRect* srcRect, const SkRect* dstRect,
    148                                          SkCanvas::SrcRectConstraint constraint,
    149                                          const SkMatrix& viewMatrix, const SkPaint& paint) {
    150     GrAA aa = GrAA(paint.isAntiAlias());
    151     if (can_use_draw_texture_affine(paint, aa, this->ctm(), constraint)) {
    152         draw_texture_affine(paint, viewMatrix, srcRect, dstRect, aa, std::move(proxy), colorSpace,
    153                             this->clip(), fRenderTargetContext.get());
    154         return;
    155     }
    156     GrTextureAdjuster adjuster(this->context(), std::move(proxy), alphaType, pinnedUniqueID,
    157                                colorSpace);
    158     this->drawTextureProducer(&adjuster, srcRect, dstRect, constraint, viewMatrix, paint);
    159 }
    160 
    161 void SkGpuDevice::drawTextureMaker(GrTextureMaker* maker, int imageW, int imageH,
    162                                    const SkRect* srcRect, const SkRect* dstRect,
    163                                    SkCanvas::SrcRectConstraint constraint,
    164                                    const SkMatrix& viewMatrix, const SkPaint& paint) {
    165     GrAA aa = GrAA(paint.isAntiAlias());
    166     if (can_use_draw_texture_affine(paint, aa, viewMatrix, constraint)) {
    167         sk_sp<SkColorSpace> cs;
    168         // We've done enough checks above to allow us to pass ClampNearest() and not check for
    169         // scaling adjustments.
    170         auto proxy = maker->refTextureProxyForParams(
    171                 GrSamplerState::ClampNearest(), fRenderTargetContext->colorSpaceInfo().colorSpace(),
    172                 &cs, nullptr);
    173         if (!proxy) {
    174             return;
    175         }
    176         draw_texture_affine(paint, viewMatrix, srcRect, dstRect, aa, std::move(proxy), cs.get(),
    177                             this->clip(), fRenderTargetContext.get());
    178         return;
    179     }
    180     this->drawTextureProducer(maker, srcRect, dstRect, constraint, viewMatrix, paint);
    181 }
    182 
    183 void SkGpuDevice::drawTextureProducer(GrTextureProducer* producer,
    184                                       const SkRect* srcRect,
    185                                       const SkRect* dstRect,
    186                                       SkCanvas::SrcRectConstraint constraint,
    187                                       const SkMatrix& viewMatrix,
    188                                       const SkPaint& paint) {
    189     // This is the funnel for all non-tiled bitmap/image draw calls. Log a histogram entry.
    190     SK_HISTOGRAM_BOOLEAN("DrawTiled", false);
    191 
    192     // Figure out the actual dst and src rect by clipping the src rect to the bounds of the
    193     // adjuster. If the src rect is clipped then the dst rect must be recomputed. Also determine
    194     // the matrix that maps the src rect to the dst rect.
    195     SkRect clippedSrcRect;
    196     SkRect clippedDstRect;
    197     const SkRect srcBounds = SkRect::MakeIWH(producer->width(), producer->height());
    198     SkMatrix srcToDstMatrix;
    199     if (srcRect) {
    200         if (!dstRect) {
    201             dstRect = &srcBounds;
    202         }
    203         if (!srcBounds.contains(*srcRect)) {
    204             clippedSrcRect = *srcRect;
    205             if (!clippedSrcRect.intersect(srcBounds)) {
    206                 return;
    207             }
    208             if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) {
    209                 return;
    210             }
    211             srcToDstMatrix.mapRect(&clippedDstRect, clippedSrcRect);
    212         } else {
    213             clippedSrcRect = *srcRect;
    214             clippedDstRect = *dstRect;
    215             if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) {
    216                 return;
    217             }
    218         }
    219     } else {
    220         clippedSrcRect = srcBounds;
    221         if (dstRect) {
    222             clippedDstRect = *dstRect;
    223             if (!srcToDstMatrix.setRectToRect(srcBounds, *dstRect, SkMatrix::kFill_ScaleToFit)) {
    224                 return;
    225             }
    226         } else {
    227             clippedDstRect = srcBounds;
    228             srcToDstMatrix.reset();
    229         }
    230     }
    231 
    232     // Now that we have both the view and srcToDst matrices, log our scale factor.
    233     LogDrawScaleFactor(SkMatrix::Concat(viewMatrix, srcToDstMatrix), paint.getFilterQuality());
    234 
    235     this->drawTextureProducerImpl(producer, clippedSrcRect, clippedDstRect, constraint, viewMatrix,
    236                                   srcToDstMatrix, paint);
    237 }
    238 
    239 void SkGpuDevice::drawTextureProducerImpl(GrTextureProducer* producer,
    240                                           const SkRect& clippedSrcRect,
    241                                           const SkRect& clippedDstRect,
    242                                           SkCanvas::SrcRectConstraint constraint,
    243                                           const SkMatrix& viewMatrix,
    244                                           const SkMatrix& srcToDstMatrix,
    245                                           const SkPaint& paint) {
    246     // Specifying the texture coords as local coordinates is an attempt to enable more GrDrawOp
    247     // combining by not baking anything about the srcRect, dstRect, or viewMatrix, into the texture
    248     // FP. In the future this should be an opaque optimization enabled by the combination of
    249     // GrDrawOp/GP and FP.
    250     const SkMaskFilter* mf = paint.getMaskFilter();
    251     // The shader expects proper local coords, so we can't replace local coords with texture coords
    252     // if the shader will be used. If we have a mask filter we will change the underlying geometry
    253     // that is rendered.
    254     bool canUseTextureCoordsAsLocalCoords = !use_shader(producer->isAlphaOnly(), paint) && !mf;
    255 
    256     bool doBicubic;
    257     GrSamplerState::Filter fm = GrSkFilterQualityToGrFilterMode(
    258             paint.getFilterQuality(), viewMatrix, srcToDstMatrix, &doBicubic);
    259     const GrSamplerState::Filter* filterMode = doBicubic ? nullptr : &fm;
    260 
    261     GrTextureProducer::FilterConstraint constraintMode;
    262     if (SkCanvas::kFast_SrcRectConstraint == constraint) {
    263         constraintMode = GrTextureAdjuster::kNo_FilterConstraint;
    264     } else {
    265         constraintMode = GrTextureAdjuster::kYes_FilterConstraint;
    266     }
    267 
    268     // If we have to outset for AA then we will generate texture coords outside the src rect. The
    269     // same happens for any mask filter that extends the bounds rendered in the dst.
    270     // This is conservative as a mask filter does not have to expand the bounds rendered.
    271     bool coordsAllInsideSrcRect = !paint.isAntiAlias() && !mf;
    272 
    273     // Check for optimization to drop the src rect constraint when on bilerp.
    274     if (filterMode && GrSamplerState::Filter::kBilerp == *filterMode &&
    275         GrTextureAdjuster::kYes_FilterConstraint == constraintMode && coordsAllInsideSrcRect) {
    276         SkMatrix combinedMatrix;
    277         combinedMatrix.setConcat(viewMatrix, srcToDstMatrix);
    278         if (can_ignore_bilerp_constraint(*producer, clippedSrcRect, combinedMatrix,
    279                                          fRenderTargetContext->fsaaType())) {
    280             constraintMode = GrTextureAdjuster::kNo_FilterConstraint;
    281         }
    282     }
    283 
    284     const SkMatrix* textureMatrix;
    285     SkMatrix tempMatrix;
    286     if (canUseTextureCoordsAsLocalCoords) {
    287         textureMatrix = &SkMatrix::I();
    288     } else {
    289         if (!srcToDstMatrix.invert(&tempMatrix)) {
    290             return;
    291         }
    292         textureMatrix = &tempMatrix;
    293     }
    294     auto fp = producer->createFragmentProcessor(
    295             *textureMatrix, clippedSrcRect, constraintMode, coordsAllInsideSrcRect, filterMode,
    296             fRenderTargetContext->colorSpaceInfo().colorSpace());
    297     if (!fp) {
    298         return;
    299     }
    300 
    301     GrPaint grPaint;
    302     if (!SkPaintToGrPaintWithTexture(fContext.get(), fRenderTargetContext->colorSpaceInfo(), paint,
    303                                      viewMatrix, std::move(fp), producer->isAlphaOnly(),
    304                                      &grPaint)) {
    305         return;
    306     }
    307     GrAA aa = GrAA(paint.isAntiAlias());
    308     if (canUseTextureCoordsAsLocalCoords) {
    309         fRenderTargetContext->fillRectToRect(this->clip(), std::move(grPaint), aa, viewMatrix,
    310                                              clippedDstRect, clippedSrcRect);
    311         return;
    312     }
    313 
    314     if (!mf) {
    315         fRenderTargetContext->drawRect(this->clip(), std::move(grPaint), aa, viewMatrix,
    316                                        clippedDstRect);
    317         return;
    318     }
    319 
    320     // First see if we can do the draw + mask filter direct to the dst.
    321     if (viewMatrix.isScaleTranslate()) {
    322         SkRect devClippedDstRect;
    323         viewMatrix.mapRectScaleTranslate(&devClippedDstRect, clippedDstRect);
    324 
    325         SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
    326         if (as_MFB(mf)->directFilterRRectMaskGPU(fContext.get(),
    327                                                  fRenderTargetContext.get(),
    328                                                  std::move(grPaint),
    329                                                  this->clip(),
    330                                                  viewMatrix,
    331                                                  rec,
    332                                                  SkRRect::MakeRect(clippedDstRect),
    333                                                  SkRRect::MakeRect(devClippedDstRect))) {
    334             return;
    335         }
    336     }
    337 
    338     SkPath rectPath;
    339     rectPath.addRect(clippedDstRect);
    340     rectPath.setIsVolatile(true);
    341     GrBlurUtils::drawPathWithMaskFilter(this->context(), fRenderTargetContext.get(), this->clip(),
    342                                         rectPath, std::move(grPaint), aa, viewMatrix, mf,
    343                                         GrStyle::SimpleFill(), true);
    344 }
    345