Line data Source code
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 0 : static inline bool use_shader(bool textureIsAlphaOnly, const SkPaint& paint) {
23 0 : 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 0 : static bool has_aligned_samples(const SkRect& srcRect, const SkRect& transformedRect) {
32 : // detect pixel disalignment
33 0 : if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) - transformedRect.left()) < kColorBleedTolerance &&
34 0 : SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) - transformedRect.top()) < kColorBleedTolerance &&
35 0 : SkScalarAbs(transformedRect.width() - srcRect.width()) < kColorBleedTolerance &&
36 0 : SkScalarAbs(transformedRect.height() - srcRect.height()) < kColorBleedTolerance) {
37 0 : return true;
38 : }
39 0 : return false;
40 : }
41 :
42 0 : 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 0 : SkASSERT(!has_aligned_samples(srcRect, transformedRect));
49 0 : SkRect innerSrcRect(srcRect), innerTransformedRect, outerTransformedRect(transformedRect);
50 0 : if (isMSAA) {
51 0 : innerSrcRect.inset(SK_Scalar1, SK_Scalar1);
52 : } else {
53 0 : innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf);
54 : }
55 0 : 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 0 : outerTransformedRect.inset(kColorBleedTolerance, kColorBleedTolerance);
62 0 : innerTransformedRect.outset(kColorBleedTolerance, kColorBleedTolerance);
63 : SkIRect outer, inner;
64 0 : outerTransformedRect.round(&outer);
65 0 : 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 0 : return inner != outer;
69 : }
70 :
71 0 : static bool can_ignore_bilerp_constraint(const GrTextureProducer& producer,
72 : const SkRect& srcRect,
73 : const SkMatrix& srcRectToDeviceSpace,
74 : bool isMSAA) {
75 0 : if (srcRectToDeviceSpace.rectStaysRect()) {
76 : // sampling is axis-aligned
77 : SkRect transformedRect;
78 0 : srcRectToDeviceSpace.mapRect(&transformedRect, srcRect);
79 :
80 0 : if (has_aligned_samples(srcRect, transformedRect) ||
81 0 : !may_color_bleed(srcRect, transformedRect, srcRectToDeviceSpace, isMSAA)) {
82 0 : return true;
83 : }
84 : }
85 0 : return false;
86 : }
87 :
88 : //////////////////////////////////////////////////////////////////////////////
89 :
90 0 : 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 0 : const SkRect srcBounds = SkRect::MakeIWH(producer->width(), producer->height());
106 : SkMatrix srcToDstMatrix;
107 0 : if (srcRect) {
108 0 : if (!dstRect) {
109 0 : dstRect = &srcBounds;
110 : }
111 0 : if (!srcBounds.contains(*srcRect)) {
112 0 : clippedSrcRect = *srcRect;
113 0 : if (!clippedSrcRect.intersect(srcBounds)) {
114 0 : return;
115 : }
116 0 : if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) {
117 0 : return;
118 : }
119 0 : srcToDstMatrix.mapRect(&clippedDstRect, clippedSrcRect);
120 : } else {
121 0 : clippedSrcRect = *srcRect;
122 0 : clippedDstRect = *dstRect;
123 0 : if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) {
124 0 : return;
125 : }
126 : }
127 : } else {
128 0 : clippedSrcRect = srcBounds;
129 0 : if (dstRect) {
130 0 : clippedDstRect = *dstRect;
131 0 : if (!srcToDstMatrix.setRectToRect(srcBounds, *dstRect, SkMatrix::kFill_ScaleToFit)) {
132 0 : return;
133 : }
134 : } else {
135 0 : clippedDstRect = srcBounds;
136 0 : srcToDstMatrix.reset();
137 : }
138 : }
139 :
140 : // Now that we have both the view and srcToDst matrices, log our scale factor.
141 0 : LogDrawScaleFactor(SkMatrix::Concat(viewMatrix, srcToDstMatrix), paint.getFilterQuality());
142 :
143 : this->drawTextureProducerImpl(producer, clippedSrcRect, clippedDstRect, constraint, viewMatrix,
144 0 : srcToDstMatrix, clip, paint);
145 : }
146 :
147 0 : 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 0 : 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 0 : bool canUseTextureCoordsAsLocalCoords = !use_shader(producer->isAlphaOnly(), paint) && !mf;
164 :
165 : bool doBicubic;
166 : GrSamplerParams::FilterMode fm =
167 0 : GrSkFilterQualityToGrFilterMode(paint.getFilterQuality(), viewMatrix, srcToDstMatrix,
168 0 : &doBicubic);
169 0 : const GrSamplerParams::FilterMode* filterMode = doBicubic ? nullptr : &fm;
170 :
171 : GrTextureProducer::FilterConstraint constraintMode;
172 0 : if (SkCanvas::kFast_SrcRectConstraint == constraint) {
173 0 : constraintMode = GrTextureAdjuster::kNo_FilterConstraint;
174 : } else {
175 0 : 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 0 : bool coordsAllInsideSrcRect = !paint.isAntiAlias() && !mf;
182 :
183 : // Check for optimization to drop the src rect constraint when on bilerp.
184 0 : if (filterMode && GrSamplerParams::kBilerp_FilterMode == *filterMode &&
185 0 : GrTextureAdjuster::kYes_FilterConstraint == constraintMode && coordsAllInsideSrcRect) {
186 : SkMatrix combinedMatrix;
187 0 : combinedMatrix.setConcat(viewMatrix, srcToDstMatrix);
188 0 : if (can_ignore_bilerp_constraint(*producer, clippedSrcRect, combinedMatrix,
189 0 : fRenderTargetContext->isUnifiedMultisampled())) {
190 0 : constraintMode = GrTextureAdjuster::kNo_FilterConstraint;
191 : }
192 : }
193 :
194 : const SkMatrix* textureMatrix;
195 : SkMatrix tempMatrix;
196 0 : if (canUseTextureCoordsAsLocalCoords) {
197 0 : textureMatrix = &SkMatrix::I();
198 : } else {
199 0 : if (!srcToDstMatrix.invert(&tempMatrix)) {
200 0 : return;
201 : }
202 0 : textureMatrix = &tempMatrix;
203 : }
204 : sk_sp<GrFragmentProcessor> fp(producer->createFragmentProcessor(
205 : *textureMatrix, clippedSrcRect, constraintMode, coordsAllInsideSrcRect, filterMode,
206 0 : fRenderTargetContext->getColorSpace()));
207 0 : if (!fp) {
208 0 : return;
209 : }
210 :
211 0 : GrPaint grPaint;
212 0 : if (!SkPaintToGrPaintWithTexture(fContext.get(), fRenderTargetContext.get(), paint, viewMatrix,
213 0 : fp, producer->isAlphaOnly(), &grPaint)) {
214 0 : return;
215 : }
216 0 : GrAA aa = GrBoolToAA(paint.isAntiAlias());
217 0 : if (canUseTextureCoordsAsLocalCoords) {
218 0 : fRenderTargetContext->fillRectToRect(clip, std::move(grPaint), aa, viewMatrix,
219 0 : clippedDstRect, clippedSrcRect);
220 0 : return;
221 : }
222 :
223 0 : if (!mf) {
224 0 : fRenderTargetContext->drawRect(clip, std::move(grPaint), aa, viewMatrix, clippedDstRect);
225 0 : return;
226 : }
227 :
228 : // First see if we can do the draw + mask filter direct to the dst.
229 0 : if (viewMatrix.isScaleTranslate()) {
230 : SkRect devClippedDstRect;
231 0 : viewMatrix.mapRectScaleTranslate(&devClippedDstRect, clippedDstRect);
232 :
233 0 : SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
234 0 : if (mf->directFilterRRectMaskGPU(fContext.get(),
235 : fRenderTargetContext.get(),
236 0 : std::move(grPaint),
237 : clip,
238 : viewMatrix,
239 : rec,
240 0 : SkRRect::MakeRect(clippedDstRect),
241 0 : SkRRect::MakeRect(devClippedDstRect))) {
242 0 : return;
243 : }
244 : }
245 :
246 0 : SkPath rectPath;
247 0 : rectPath.addRect(clippedDstRect);
248 0 : rectPath.setIsVolatile(true);
249 0 : GrBlurUtils::drawPathWithMaskFilter(this->context(), fRenderTargetContext.get(), this->clip(),
250 0 : rectPath, std::move(grPaint), aa, viewMatrix, mf,
251 0 : GrStyle::SimpleFill(), true);
252 : }
|