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