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
use_shader(bool textureIsAlphaOnly,const SkPaint & paint)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
has_aligned_samples(const SkRect & srcRect,const SkRect & transformedRect)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
may_color_bleed(const SkRect & srcRect,const SkRect & transformedRect,const SkMatrix & m,bool isMSAA)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
can_ignore_bilerp_constraint(const GrTextureProducer & producer,const SkRect & srcRect,const SkMatrix & srcRectToDeviceSpace,bool isMSAA)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
drawTextureProducer(GrTextureProducer * producer,const SkRect * srcRect,const SkRect * dstRect,SkCanvas::SrcRectConstraint constraint,const SkMatrix & viewMatrix,const GrClip & clip,const SkPaint & paint)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
drawTextureProducerImpl(GrTextureProducer * producer,const SkRect & clippedSrcRect,const SkRect & clippedDstRect,SkCanvas::SrcRectConstraint constraint,const SkMatrix & viewMatrix,const SkMatrix & srcToDstMatrix,const GrClip & clip,const SkPaint & paint)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