SkGpuDevice_drawTexture.cpp revision 6663acff010ce752e4bf778da81fa97448c9db31
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 "GrStyle.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
22static 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
29static const SkScalar kColorBleedTolerance = 0.001f;
30
31static 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
42static 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
71static 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
90void 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
147void 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 batching
156    // by not baking anything about the srcRect, dstRect, or viewMatrix, into the texture FP. In
157    // the future this should be an opaque optimization enabled by the combination of batch/GP and
158    // 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    GrTextureParams::FilterMode fm =
167        GrSkFilterQualityToGrFilterMode(paint.getFilterQuality(), viewMatrix, srcToDstMatrix,
168                                        &doBicubic);
169    const GrTextureParams::FilterMode* filterMode = doBicubic ? nullptr : &fm;
170
171    GrTextureAdjuster::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 && GrTextureParams::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                                         fRenderTarget->isUnifiedMultisampled())) {
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    SkAutoTUnref<const GrFragmentProcessor> fp(producer->createFragmentProcessor(
205        *textureMatrix, clippedSrcRect, constraintMode, coordsAllInsideSrcRect, filterMode));
206    if (!fp) {
207        return;
208    }
209
210    GrPaint grPaint;
211    if (!SkPaintToGrPaintWithTexture(fContext, paint, viewMatrix, fp, producer->isAlphaOnly(),
212                                     this->surfaceProps().isGammaCorrect(), &grPaint)) {
213        return;
214    }
215
216    if (canUseTextureCoordsAsLocalCoords) {
217        fDrawContext->fillRectToRect(clip, grPaint, viewMatrix, clippedDstRect, clippedSrcRect);
218        return;
219    }
220
221    if (!mf) {
222        fDrawContext->drawRect(clip, grPaint, viewMatrix, clippedDstRect);
223        return;
224    }
225
226    // First see if we can do the draw + mask filter direct to the dst.
227    SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
228    SkRRect rrect;
229    rrect.setRect(clippedDstRect);
230    if (mf->directFilterRRectMaskGPU(fContext->textureProvider(),
231                                      fDrawContext.get(),
232                                      &grPaint,
233                                      clip,
234                                      viewMatrix,
235                                      rec,
236                                      rrect)) {
237        return;
238    }
239    SkPath rectPath;
240    rectPath.addRect(clippedDstRect);
241    rectPath.setIsVolatile(true);
242    GrBlurUtils::drawPathWithMaskFilter(this->context(), fDrawContext.get(), fClip,
243                                        rectPath, &grPaint, viewMatrix, mf, GrStyle::SimpleFill(),
244                                        true);
245}
246