blob: 21815f7c02fa89dbac48771cd183a4afa28e52bf [file] [log] [blame]
bsalomonc55271f2015-11-09 11:55:57 -08001/*
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"
bsalomonc55271f2015-11-09 11:55:57 -08009#include "GrBlurUtils.h"
10#include "GrCaps.h"
Brian Osman1cb41712017-10-19 12:54:52 -040011#include "GrColorSpaceXform.h"
Brian Osman11052242016-10-27 14:47:55 -040012#include "GrRenderTargetContext.h"
Robert Phillips27927a52018-08-20 13:18:12 -040013#include "GrShape.h"
bsalomon6663acf2016-05-10 09:14:17 -070014#include "GrStyle.h"
Brian Osmane8e54582016-11-28 10:06:27 -050015#include "GrTextureAdjuster.h"
Brian Salomon34169692017-08-28 15:32:01 -040016#include "GrTextureMaker.h"
bsalomonc55271f2015-11-09 11:55:57 -080017#include "SkDraw.h"
Brian Osman3b655982017-03-07 16:58:08 -050018#include "SkGr.h"
Mike Reed80747ef2018-01-23 15:29:32 -050019#include "SkMaskFilterBase.h"
bsalomonc55271f2015-11-09 11:55:57 -080020#include "effects/GrBicubicEffect.h"
21#include "effects/GrSimpleTextureEffect.h"
22#include "effects/GrTextureDomain.h"
23
24static inline bool use_shader(bool textureIsAlphaOnly, const SkPaint& paint) {
25 return textureIsAlphaOnly && paint.getShader();
26}
27
bsalomonb1b01992015-11-18 10:56:08 -080028//////////////////////////////////////////////////////////////////////////////
29// Helper functions for dropping src rect constraint in bilerp mode.
30
31static const SkScalar kColorBleedTolerance = 0.001f;
32
33static bool has_aligned_samples(const SkRect& srcRect, const SkRect& transformedRect) {
34 // detect pixel disalignment
Brian Salomona911f8f2015-11-18 15:19:57 -050035 if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) - transformedRect.left()) < kColorBleedTolerance &&
36 SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) - transformedRect.top()) < kColorBleedTolerance &&
37 SkScalarAbs(transformedRect.width() - srcRect.width()) < kColorBleedTolerance &&
bsalomonb1b01992015-11-18 10:56:08 -080038 SkScalarAbs(transformedRect.height() - srcRect.height()) < kColorBleedTolerance) {
39 return true;
40 }
41 return false;
42}
43
44static bool may_color_bleed(const SkRect& srcRect,
45 const SkRect& transformedRect,
46 const SkMatrix& m,
Brian Salomon7c8460e2017-05-12 11:36:10 -040047 GrFSAAType fsaaType) {
bsalomonb1b01992015-11-18 10:56:08 -080048 // 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);
Brian Salomon7c8460e2017-05-12 11:36:10 -040052 if (GrFSAAType::kUnifiedMSAA == fsaaType) {
bsalomonb1b01992015-11-18 10:56:08 -080053 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
73static bool can_ignore_bilerp_constraint(const GrTextureProducer& producer,
74 const SkRect& srcRect,
75 const SkMatrix& srcRectToDeviceSpace,
Brian Salomon7c8460e2017-05-12 11:36:10 -040076 GrFSAAType fsaaType) {
bsalomonb1b01992015-11-18 10:56:08 -080077 if (srcRectToDeviceSpace.rectStaysRect()) {
78 // sampling is axis-aligned
79 SkRect transformedRect;
80 srcRectToDeviceSpace.mapRect(&transformedRect, srcRect);
81
82 if (has_aligned_samples(srcRect, transformedRect) ||
Brian Salomon7c8460e2017-05-12 11:36:10 -040083 !may_color_bleed(srcRect, transformedRect, srcRectToDeviceSpace, fsaaType)) {
bsalomonb1b01992015-11-18 10:56:08 -080084 return true;
85 }
86 }
87 return false;
88}
89
Brian Salomon34169692017-08-28 15:32:01 -040090/**
Brian Salomonb80ffee2018-05-23 16:39:39 -040091 * Checks whether the paint is compatible with using GrRenderTargetContext::drawTexture. It is more
92 * efficient than the GrTextureProducer general case.
Brian Salomon34169692017-08-28 15:32:01 -040093 */
Brian Salomonb80ffee2018-05-23 16:39:39 -040094static bool can_use_draw_texture(const SkPaint& paint) {
Brian Salomon34169692017-08-28 15:32:01 -040095 return (!paint.getColorFilter() && !paint.getShader() && !paint.getMaskFilter() &&
Michael Ludwigd54ca8f2019-02-13 13:25:21 -050096 !paint.getImageFilter() && paint.getFilterQuality() < kMedium_SkFilterQuality);
Brian Salomon34169692017-08-28 15:32:01 -040097}
98
Michael Ludwig24adb3a2019-02-27 19:42:20 +000099static void draw_texture(const SkPaint& paint, const SkMatrix& ctm, const SkRect* src,
100 const SkRect* dst, GrAA aa, SkCanvas::SrcRectConstraint constraint,
101 sk_sp<GrTextureProxy> proxy, SkAlphaType alphaType,
102 SkColorSpace* colorSpace, const GrClip& clip, GrRenderTargetContext* rtc) {
103 SkASSERT(!(SkToBool(src) && !SkToBool(dst)));
104 SkRect srcRect = src ? *src : SkRect::MakeWH(proxy->width(), proxy->height());
105 SkRect dstRect = dst ? *dst : srcRect;
106 if (src && !SkRect::MakeIWH(proxy->width(), proxy->height()).contains(srcRect)) {
107 // Shrink the src rect to be within bounds and proportionately shrink the dst rect.
108 SkMatrix srcToDst;
109 srcToDst.setRectToRect(srcRect, dstRect, SkMatrix::kFill_ScaleToFit);
110 SkAssertResult(srcRect.intersect(SkRect::MakeIWH(proxy->width(), proxy->height())));
111 srcToDst.mapRect(&dstRect, srcRect);
112 }
Brian Osman3d139a42018-11-19 10:42:10 -0500113 const GrColorSpaceInfo& dstInfo(rtc->colorSpaceInfo());
Mike Kleine03a1762018-08-22 11:52:16 -0400114 auto textureXform =
Brian Osman3d139a42018-11-19 10:42:10 -0500115 GrColorSpaceXform::Make(colorSpace , alphaType,
116 dstInfo.colorSpace(), kPremul_SkAlphaType);
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400117 GrSamplerState::Filter filter;
Brian Salomon34169692017-08-28 15:32:01 -0400118 switch (paint.getFilterQuality()) {
119 case kNone_SkFilterQuality:
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400120 filter = GrSamplerState::Filter::kNearest;
Brian Salomon34169692017-08-28 15:32:01 -0400121 break;
122 case kLow_SkFilterQuality:
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400123 filter = GrSamplerState::Filter::kBilerp;
Brian Salomon34169692017-08-28 15:32:01 -0400124 break;
125 case kMedium_SkFilterQuality:
126 case kHigh_SkFilterQuality:
127 SK_ABORT("Quality level not allowed.");
128 }
Michael Ludwigd54ca8f2019-02-13 13:25:21 -0500129
130 // Must specify the strict constraint when the proxy is not functionally exact and the src
131 // rect would access pixels outside the proxy's content area without the constraint.
132 if (constraint != SkCanvas::kStrict_SrcRectConstraint &&
133 !GrProxyProvider::IsFunctionallyExact(proxy.get())) {
134 // Conservative estimate of how much a coord could be outset from src rect:
135 // 1/2 pixel for AA and 1/2 pixel for bilerp
136 float buffer = 0.5f * (aa == GrAA::kYes) +
137 0.5f * (filter == GrSamplerState::Filter::kBilerp);
138 SkRect safeBounds = SkRect::MakeWH(proxy->width(), proxy->height());
139 safeBounds.inset(buffer, buffer);
140 if (!safeBounds.contains(srcRect)) {
141 constraint = SkCanvas::kStrict_SrcRectConstraint;
142 }
143 }
Brian Osman3d139a42018-11-19 10:42:10 -0500144 SkPMColor4f color;
Brian Osman3ebd3542018-07-30 14:36:53 -0400145 if (GrPixelConfigIsAlphaOnly(proxy->config())) {
Brian Osman3d139a42018-11-19 10:42:10 -0500146 color = SkColor4fPrepForDst(paint.getColor4f(), dstInfo, *rtc->caps()).premul();
Brian Osman3ebd3542018-07-30 14:36:53 -0400147 } else {
Brian Osman3d139a42018-11-19 10:42:10 -0500148 float paintAlpha = paint.getColor4f().fA;
149 color = { paintAlpha, paintAlpha, paintAlpha, paintAlpha };
Brian Osman3ebd3542018-07-30 14:36:53 -0400150 }
Michael Ludwig24adb3a2019-02-27 19:42:20 +0000151 GrQuadAAFlags aaFlags = aa == GrAA::kYes ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone;
152 rtc->drawTexture(clip, std::move(proxy), filter, paint.getBlendMode(), color, srcRect, dstRect,
153 aa, aaFlags, constraint, ctm, std::move(textureXform));
Brian Salomon34169692017-08-28 15:32:01 -0400154}
155
Michael Ludwig24adb3a2019-02-27 19:42:20 +0000156//////////////////////////////////////////////////////////////////////////////
157
158void SkGpuDevice::drawPinnedTextureProxy(sk_sp<GrTextureProxy> proxy, uint32_t pinnedUniqueID,
159 SkColorSpace* colorSpace, SkAlphaType alphaType,
160 const SkRect* srcRect, const SkRect* dstRect,
161 SkCanvas::SrcRectConstraint constraint,
162 const SkMatrix& viewMatrix, const SkPaint& paint) {
163 GrAA aa = GrAA(paint.isAntiAlias());
164 if (can_use_draw_texture(paint)) {
165 draw_texture(paint, viewMatrix, srcRect, dstRect, aa, constraint, std::move(proxy),
166 alphaType, colorSpace, this->clip(), fRenderTargetContext.get());
167 return;
168 }
169 GrTextureAdjuster adjuster(this->context(), std::move(proxy), alphaType, pinnedUniqueID,
170 colorSpace);
171 this->drawTextureProducer(&adjuster, srcRect, dstRect, constraint, viewMatrix, paint, false);
172}
173
174void SkGpuDevice::drawTextureProducer(GrTextureProducer* producer,
175 const SkRect* srcRect,
176 const SkRect* dstRect,
177 SkCanvas::SrcRectConstraint constraint,
178 const SkMatrix& viewMatrix,
179 const SkPaint& paint,
180 bool attemptDrawTexture) {
Jim Van Verth30e0d7f2018-11-02 13:36:42 -0400181 if (attemptDrawTexture && can_use_draw_texture(paint)) {
Michael Ludwig24adb3a2019-02-27 19:42:20 +0000182 GrAA aa = GrAA(paint.isAntiAlias());
Jim Van Verth30e0d7f2018-11-02 13:36:42 -0400183 // We've done enough checks above to allow us to pass ClampNearest() and not check for
184 // scaling adjustments.
185 auto proxy = producer->refTextureProxyForParams(GrSamplerState::ClampNearest(), nullptr);
186 if (!proxy) {
187 return;
188 }
Michael Ludwig24adb3a2019-02-27 19:42:20 +0000189 draw_texture(paint, viewMatrix, srcRect, dstRect, aa, constraint, std::move(proxy),
190 producer->alphaType(), producer->colorSpace(), this->clip(),
191 fRenderTargetContext.get());
Jim Van Verth30e0d7f2018-11-02 13:36:42 -0400192 return;
193 }
194
Michael Ludwig24adb3a2019-02-27 19:42:20 +0000195 // This is the funnel for all non-tiled bitmap/image draw calls. Log a histogram entry.
196 SK_HISTOGRAM_BOOLEAN("DrawTiled", false);
197
198 // Figure out the actual dst and src rect by clipping the src rect to the bounds of the
199 // adjuster. If the src rect is clipped then the dst rect must be recomputed. Also determine
200 // the matrix that maps the src rect to the dst rect.
201 SkRect clippedSrcRect;
202 SkRect clippedDstRect;
203 const SkRect srcBounds = SkRect::MakeIWH(producer->width(), producer->height());
204 SkMatrix srcToDstMatrix;
205 if (srcRect) {
206 if (!dstRect) {
207 dstRect = &srcBounds;
208 }
209 if (!srcBounds.contains(*srcRect)) {
210 clippedSrcRect = *srcRect;
211 if (!clippedSrcRect.intersect(srcBounds)) {
212 return;
213 }
214 if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) {
215 return;
216 }
217 srcToDstMatrix.mapRect(&clippedDstRect, clippedSrcRect);
218 } else {
219 clippedSrcRect = *srcRect;
220 clippedDstRect = *dstRect;
221 if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) {
222 return;
223 }
224 }
225 } else {
226 clippedSrcRect = srcBounds;
227 if (dstRect) {
228 clippedDstRect = *dstRect;
229 if (!srcToDstMatrix.setRectToRect(srcBounds, *dstRect, SkMatrix::kFill_ScaleToFit)) {
230 return;
231 }
232 } else {
233 clippedDstRect = srcBounds;
234 srcToDstMatrix.reset();
235 }
236 }
237
238 // Now that we have both the view and srcToDst matrices, log our scale factor.
239 LogDrawScaleFactor(viewMatrix, srcToDstMatrix, paint.getFilterQuality());
240
241 this->drawTextureProducerImpl(producer, clippedSrcRect, clippedDstRect, constraint, viewMatrix,
242 srcToDstMatrix, paint);
243}
244
245void SkGpuDevice::drawTextureProducerImpl(GrTextureProducer* producer,
246 const SkRect& clippedSrcRect,
247 const SkRect& clippedDstRect,
248 SkCanvas::SrcRectConstraint constraint,
249 const SkMatrix& viewMatrix,
250 const SkMatrix& srcToDstMatrix,
251 const SkPaint& paint) {
bsalomonc55271f2015-11-09 11:55:57 -0800252 const SkMaskFilter* mf = paint.getMaskFilter();
Michael Ludwigb7d64b92019-02-11 11:09:15 -0500253
bsalomonc55271f2015-11-09 11:55:57 -0800254 // The shader expects proper local coords, so we can't replace local coords with texture coords
255 // if the shader will be used. If we have a mask filter we will change the underlying geometry
256 // that is rendered.
bsalomonf1ecd212015-12-09 17:06:02 -0800257 bool canUseTextureCoordsAsLocalCoords = !use_shader(producer->isAlphaOnly(), paint) && !mf;
bsalomonc55271f2015-11-09 11:55:57 -0800258
Michael Ludwigb7d64b92019-02-11 11:09:15 -0500259 // Specifying the texture coords as local coordinates is an attempt to enable more GrDrawOp
Michael Ludwig24adb3a2019-02-27 19:42:20 +0000260 // combining by not baking anything about the srcRect, dstRect, or viewMatrix, into the texture
Michael Ludwigb7d64b92019-02-11 11:09:15 -0500261 // FP. In the future this should be an opaque optimization enabled by the combination of
262 // GrDrawOp/GP and FP.
263 if (mf && as_MFB(mf)->hasFragmentProcessor()) {
264 mf = nullptr;
265 }
Michael Ludwig24adb3a2019-02-27 19:42:20 +0000266
bsalomonc55271f2015-11-09 11:55:57 -0800267 bool doBicubic;
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400268 GrSamplerState::Filter fm = GrSkFilterQualityToGrFilterMode(
Michael Ludwig24adb3a2019-02-27 19:42:20 +0000269 paint.getFilterQuality(), viewMatrix, srcToDstMatrix,
270 fContext->priv().options().fSharpenMipmappedTextures, &doBicubic);
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400271 const GrSamplerState::Filter* filterMode = doBicubic ? nullptr : &fm;
bsalomonc55271f2015-11-09 11:55:57 -0800272
Brian Osmane8e54582016-11-28 10:06:27 -0500273 GrTextureProducer::FilterConstraint constraintMode;
bsalomonc55271f2015-11-09 11:55:57 -0800274 if (SkCanvas::kFast_SrcRectConstraint == constraint) {
275 constraintMode = GrTextureAdjuster::kNo_FilterConstraint;
276 } else {
277 constraintMode = GrTextureAdjuster::kYes_FilterConstraint;
278 }
halcanary9d524f22016-03-29 09:03:52 -0700279
bsalomonc55271f2015-11-09 11:55:57 -0800280 // If we have to outset for AA then we will generate texture coords outside the src rect. The
281 // same happens for any mask filter that extends the bounds rendered in the dst.
282 // This is conservative as a mask filter does not have to expand the bounds rendered.
Michael Ludwig24adb3a2019-02-27 19:42:20 +0000283 bool coordsAllInsideSrcRect = !paint.isAntiAlias() && !mf;
bsalomonc55271f2015-11-09 11:55:57 -0800284
bsalomonb1b01992015-11-18 10:56:08 -0800285 // Check for optimization to drop the src rect constraint when on bilerp.
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400286 if (filterMode && GrSamplerState::Filter::kBilerp == *filterMode &&
bsalomonb1b01992015-11-18 10:56:08 -0800287 GrTextureAdjuster::kYes_FilterConstraint == constraintMode && coordsAllInsideSrcRect) {
288 SkMatrix combinedMatrix;
Michael Ludwig24adb3a2019-02-27 19:42:20 +0000289 combinedMatrix.setConcat(viewMatrix, srcToDstMatrix);
290 if (can_ignore_bilerp_constraint(*producer, clippedSrcRect, combinedMatrix,
291 fRenderTargetContext->fsaaType())) {
bsalomonb1b01992015-11-18 10:56:08 -0800292 constraintMode = GrTextureAdjuster::kNo_FilterConstraint;
293 }
294 }
295
Michael Ludwig24adb3a2019-02-27 19:42:20 +0000296 const SkMatrix* textureMatrix;
297 SkMatrix tempMatrix;
bsalomon3aa5fce2015-11-12 09:59:44 -0800298 if (canUseTextureCoordsAsLocalCoords) {
Michael Ludwig24adb3a2019-02-27 19:42:20 +0000299 textureMatrix = &SkMatrix::I();
bsalomon3aa5fce2015-11-12 09:59:44 -0800300 } else {
Michael Ludwig24adb3a2019-02-27 19:42:20 +0000301 if (!srcToDstMatrix.invert(&tempMatrix)) {
bsalomon3aa5fce2015-11-12 09:59:44 -0800302 return;
303 }
Michael Ludwig24adb3a2019-02-27 19:42:20 +0000304 textureMatrix = &tempMatrix;
bsalomon3aa5fce2015-11-12 09:59:44 -0800305 }
Michael Ludwig24adb3a2019-02-27 19:42:20 +0000306 auto fp = producer->createFragmentProcessor(*textureMatrix, clippedSrcRect, constraintMode,
Brian Osman05c8f462018-10-22 17:13:36 -0400307 coordsAllInsideSrcRect, filterMode);
308 fp = GrColorSpaceXformEffect::Make(std::move(fp), producer->colorSpace(), producer->alphaType(),
Michael Ludwig24adb3a2019-02-27 19:42:20 +0000309 fRenderTargetContext->colorSpaceInfo().colorSpace());
bsalomonc55271f2015-11-09 11:55:57 -0800310 if (!fp) {
311 return;
312 }
joshualitt33a5fce2015-11-18 13:28:51 -0800313
bsalomonc55271f2015-11-09 11:55:57 -0800314 GrPaint grPaint;
Michael Ludwig24adb3a2019-02-27 19:42:20 +0000315 if (!SkPaintToGrPaintWithTexture(fContext.get(), fRenderTargetContext->colorSpaceInfo(), paint,
316 viewMatrix, std::move(fp), producer->isAlphaOnly(),
317 &grPaint)) {
318 return;
319 }
320 GrAA aa = GrAA(paint.isAntiAlias());
321 if (canUseTextureCoordsAsLocalCoords) {
322 fRenderTargetContext->fillRectToRect(this->clip(), std::move(grPaint), aa, viewMatrix,
323 clippedDstRect, clippedSrcRect);
bsalomonc55271f2015-11-09 11:55:57 -0800324 return;
325 }
326
327 if (!mf) {
Michael Ludwig24adb3a2019-02-27 19:42:20 +0000328 fRenderTargetContext->drawRect(this->clip(), std::move(grPaint), aa, viewMatrix,
329 clippedDstRect);
bsalomonc55271f2015-11-09 11:55:57 -0800330 return;
331 }
332
Michael Ludwig24adb3a2019-02-27 19:42:20 +0000333 GrShape shape(clippedDstRect, GrStyle::SimpleFill());
Robert Phillips27927a52018-08-20 13:18:12 -0400334
Michael Ludwig24adb3a2019-02-27 19:42:20 +0000335 GrBlurUtils::drawShapeWithMaskFilter(this->context(), fRenderTargetContext.get(), this->clip(),
336 shape, std::move(grPaint), viewMatrix, mf);
bsalomonc55271f2015-11-09 11:55:57 -0800337}