blob: af397cd431d6e31f3f0a64a833d57e6ba4584b17 [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"
bsalomon6663acf2016-05-10 09:14:17 -070013#include "GrStyle.h"
Brian Osmane8e54582016-11-28 10:06:27 -050014#include "GrTextureAdjuster.h"
Brian Salomon34169692017-08-28 15:32:01 -040015#include "GrTextureMaker.h"
bsalomonc55271f2015-11-09 11:55:57 -080016#include "SkDraw.h"
Brian Osman3b655982017-03-07 16:58:08 -050017#include "SkGr.h"
Mike Reed80747ef2018-01-23 15:29:32 -050018#include "SkMaskFilterBase.h"
bsalomonc55271f2015-11-09 11:55:57 -080019#include "effects/GrBicubicEffect.h"
20#include "effects/GrSimpleTextureEffect.h"
21#include "effects/GrTextureDomain.h"
22
23static inline bool use_shader(bool textureIsAlphaOnly, const SkPaint& paint) {
24 return textureIsAlphaOnly && paint.getShader();
25}
26
bsalomonb1b01992015-11-18 10:56:08 -080027//////////////////////////////////////////////////////////////////////////////
28// Helper functions for dropping src rect constraint in bilerp mode.
29
30static const SkScalar kColorBleedTolerance = 0.001f;
31
32static bool has_aligned_samples(const SkRect& srcRect, const SkRect& transformedRect) {
33 // detect pixel disalignment
Brian Salomona911f8f2015-11-18 15:19:57 -050034 if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) - transformedRect.left()) < kColorBleedTolerance &&
35 SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) - transformedRect.top()) < kColorBleedTolerance &&
36 SkScalarAbs(transformedRect.width() - srcRect.width()) < kColorBleedTolerance &&
bsalomonb1b01992015-11-18 10:56:08 -080037 SkScalarAbs(transformedRect.height() - srcRect.height()) < kColorBleedTolerance) {
38 return true;
39 }
40 return false;
41}
42
43static bool may_color_bleed(const SkRect& srcRect,
44 const SkRect& transformedRect,
45 const SkMatrix& m,
Brian Salomon7c8460e2017-05-12 11:36:10 -040046 GrFSAAType fsaaType) {
bsalomonb1b01992015-11-18 10:56:08 -080047 // Only gets called if has_aligned_samples returned false.
48 // So we can assume that sampling is axis aligned but not texel aligned.
49 SkASSERT(!has_aligned_samples(srcRect, transformedRect));
50 SkRect innerSrcRect(srcRect), innerTransformedRect, outerTransformedRect(transformedRect);
Brian Salomon7c8460e2017-05-12 11:36:10 -040051 if (GrFSAAType::kUnifiedMSAA == fsaaType) {
bsalomonb1b01992015-11-18 10:56:08 -080052 innerSrcRect.inset(SK_Scalar1, SK_Scalar1);
53 } else {
54 innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf);
55 }
56 m.mapRect(&innerTransformedRect, innerSrcRect);
57
58 // The gap between outerTransformedRect and innerTransformedRect
59 // represents the projection of the source border area, which is
60 // problematic for color bleeding. We must check whether any
61 // destination pixels sample the border area.
62 outerTransformedRect.inset(kColorBleedTolerance, kColorBleedTolerance);
63 innerTransformedRect.outset(kColorBleedTolerance, kColorBleedTolerance);
64 SkIRect outer, inner;
65 outerTransformedRect.round(&outer);
66 innerTransformedRect.round(&inner);
67 // If the inner and outer rects round to the same result, it means the
68 // border does not overlap any pixel centers. Yay!
69 return inner != outer;
70}
71
72static bool can_ignore_bilerp_constraint(const GrTextureProducer& producer,
73 const SkRect& srcRect,
74 const SkMatrix& srcRectToDeviceSpace,
Brian Salomon7c8460e2017-05-12 11:36:10 -040075 GrFSAAType fsaaType) {
bsalomonb1b01992015-11-18 10:56:08 -080076 if (srcRectToDeviceSpace.rectStaysRect()) {
77 // sampling is axis-aligned
78 SkRect transformedRect;
79 srcRectToDeviceSpace.mapRect(&transformedRect, srcRect);
80
81 if (has_aligned_samples(srcRect, transformedRect) ||
Brian Salomon7c8460e2017-05-12 11:36:10 -040082 !may_color_bleed(srcRect, transformedRect, srcRectToDeviceSpace, fsaaType)) {
bsalomonb1b01992015-11-18 10:56:08 -080083 return true;
84 }
85 }
86 return false;
87}
88
Brian Salomon34169692017-08-28 15:32:01 -040089/**
Brian Salomonb80ffee2018-05-23 16:39:39 -040090 * Checks whether the paint is compatible with using GrRenderTargetContext::drawTexture. It is more
91 * efficient than the GrTextureProducer general case.
Brian Salomon34169692017-08-28 15:32:01 -040092 */
Brian Salomonb80ffee2018-05-23 16:39:39 -040093static bool can_use_draw_texture(const SkPaint& paint) {
Brian Salomon34169692017-08-28 15:32:01 -040094 return (!paint.getColorFilter() && !paint.getShader() && !paint.getMaskFilter() &&
Brian Salomonb5ef1f92018-01-11 11:46:21 -050095 !paint.getImageFilter() && paint.getFilterQuality() < kMedium_SkFilterQuality &&
Brian Salomonb80ffee2018-05-23 16:39:39 -040096 paint.getBlendMode() == SkBlendMode::kSrcOver);
Brian Salomon34169692017-08-28 15:32:01 -040097}
98
Brian Salomonbe3c1d22018-05-21 12:54:39 -040099static void draw_texture(const SkPaint& paint, const SkMatrix& ctm, const SkRect* src,
Brian Salomonb80ffee2018-05-23 16:39:39 -0400100 const SkRect* dst, GrAA aa, SkCanvas::SrcRectConstraint constraint,
101 sk_sp<GrTextureProxy> proxy,
102
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400103 SkColorSpace* colorSpace, const GrClip& clip, GrRenderTargetContext* rtc) {
Brian Salomon34169692017-08-28 15:32:01 -0400104 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 }
Brian Osman653f34d2018-06-14 11:44:02 -0400114 auto csxf = GrColorSpaceXform::Make(colorSpace, rtc->colorSpaceInfo().colorSpace());
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400115 GrSamplerState::Filter filter;
Brian Salomon34169692017-08-28 15:32:01 -0400116 switch (paint.getFilterQuality()) {
117 case kNone_SkFilterQuality:
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400118 filter = GrSamplerState::Filter::kNearest;
Brian Salomon34169692017-08-28 15:32:01 -0400119 break;
120 case kLow_SkFilterQuality:
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400121 filter = GrSamplerState::Filter::kBilerp;
Brian Salomon34169692017-08-28 15:32:01 -0400122 break;
123 case kMedium_SkFilterQuality:
124 case kHigh_SkFilterQuality:
125 SK_ABORT("Quality level not allowed.");
126 }
127 GrColor color = GrPixelConfigIsAlphaOnly(proxy->config())
128 ? SkColorToPremulGrColor(paint.getColor())
129 : SkColorAlphaToGrColor(paint.getColor());
Brian Salomonb80ffee2018-05-23 16:39:39 -0400130 rtc->drawTexture(clip, std::move(proxy), filter, color, srcRect, dstRect, aa, constraint, ctm,
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400131 std::move(csxf));
Brian Salomon34169692017-08-28 15:32:01 -0400132}
133
bsalomonb1b01992015-11-18 10:56:08 -0800134//////////////////////////////////////////////////////////////////////////////
135
Brian Salomon34169692017-08-28 15:32:01 -0400136void SkGpuDevice::drawPinnedTextureProxy(sk_sp<GrTextureProxy> proxy, uint32_t pinnedUniqueID,
137 SkColorSpace* colorSpace, SkAlphaType alphaType,
138 const SkRect* srcRect, const SkRect* dstRect,
139 SkCanvas::SrcRectConstraint constraint,
140 const SkMatrix& viewMatrix, const SkPaint& paint) {
Brian Salomon4e6cf912018-01-17 09:27:35 -0500141 GrAA aa = GrAA(paint.isAntiAlias());
Brian Salomonb80ffee2018-05-23 16:39:39 -0400142 if (can_use_draw_texture(paint)) {
143 draw_texture(paint, viewMatrix, srcRect, dstRect, aa, constraint, std::move(proxy),
144 colorSpace, this->clip(), fRenderTargetContext.get());
Brian Salomon34169692017-08-28 15:32:01 -0400145 return;
146 }
Greg Danielc77085d2017-11-01 16:38:48 -0400147 GrTextureAdjuster adjuster(this->context(), std::move(proxy), alphaType, pinnedUniqueID,
148 colorSpace);
Brian Salomon34169692017-08-28 15:32:01 -0400149 this->drawTextureProducer(&adjuster, srcRect, dstRect, constraint, viewMatrix, paint);
150}
151
152void SkGpuDevice::drawTextureMaker(GrTextureMaker* maker, int imageW, int imageH,
153 const SkRect* srcRect, const SkRect* dstRect,
154 SkCanvas::SrcRectConstraint constraint,
155 const SkMatrix& viewMatrix, const SkPaint& paint) {
Brian Salomon4e6cf912018-01-17 09:27:35 -0500156 GrAA aa = GrAA(paint.isAntiAlias());
Brian Salomonb80ffee2018-05-23 16:39:39 -0400157 if (can_use_draw_texture(paint)) {
Brian Salomon34169692017-08-28 15:32:01 -0400158 sk_sp<SkColorSpace> cs;
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400159 // We've done enough checks above to allow us to pass ClampNearest() and not check for
Brian Salomon34169692017-08-28 15:32:01 -0400160 // scaling adjustments.
Brian Salomonf3569f02017-10-24 12:52:33 -0400161 auto proxy = maker->refTextureProxyForParams(
162 GrSamplerState::ClampNearest(), fRenderTargetContext->colorSpaceInfo().colorSpace(),
163 &cs, nullptr);
Brian Salomon34169692017-08-28 15:32:01 -0400164 if (!proxy) {
165 return;
166 }
Brian Salomonb80ffee2018-05-23 16:39:39 -0400167 draw_texture(paint, viewMatrix, srcRect, dstRect, aa, constraint, std::move(proxy),
168 cs.get(), this->clip(), fRenderTargetContext.get());
Brian Salomon34169692017-08-28 15:32:01 -0400169 return;
170 }
171 this->drawTextureProducer(maker, srcRect, dstRect, constraint, viewMatrix, paint);
172}
173
bsalomonb1b01992015-11-18 10:56:08 -0800174void SkGpuDevice::drawTextureProducer(GrTextureProducer* producer,
bsalomonc55271f2015-11-09 11:55:57 -0800175 const SkRect* srcRect,
176 const SkRect* dstRect,
177 SkCanvas::SrcRectConstraint constraint,
178 const SkMatrix& viewMatrix,
bsalomonc55271f2015-11-09 11:55:57 -0800179 const SkPaint& paint) {
ericrk369e9372016-02-05 15:32:36 -0800180 // This is the funnel for all non-tiled bitmap/image draw calls. Log a histogram entry.
181 SK_HISTOGRAM_BOOLEAN("DrawTiled", false);
182
bsalomonc55271f2015-11-09 11:55:57 -0800183 // Figure out the actual dst and src rect by clipping the src rect to the bounds of the
184 // adjuster. If the src rect is clipped then the dst rect must be recomputed. Also determine
185 // the matrix that maps the src rect to the dst rect.
186 SkRect clippedSrcRect;
187 SkRect clippedDstRect;
bsalomonb1b01992015-11-18 10:56:08 -0800188 const SkRect srcBounds = SkRect::MakeIWH(producer->width(), producer->height());
bsalomonc55271f2015-11-09 11:55:57 -0800189 SkMatrix srcToDstMatrix;
190 if (srcRect) {
191 if (!dstRect) {
bsalomon3aa5fce2015-11-12 09:59:44 -0800192 dstRect = &srcBounds;
bsalomonc55271f2015-11-09 11:55:57 -0800193 }
bsalomon3aa5fce2015-11-12 09:59:44 -0800194 if (!srcBounds.contains(*srcRect)) {
bsalomonc55271f2015-11-09 11:55:57 -0800195 clippedSrcRect = *srcRect;
bsalomon3aa5fce2015-11-12 09:59:44 -0800196 if (!clippedSrcRect.intersect(srcBounds)) {
bsalomonc55271f2015-11-09 11:55:57 -0800197 return;
198 }
199 if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) {
200 return;
201 }
202 srcToDstMatrix.mapRect(&clippedDstRect, clippedSrcRect);
203 } else {
204 clippedSrcRect = *srcRect;
205 clippedDstRect = *dstRect;
206 if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) {
207 return;
208 }
209 }
210 } else {
bsalomon3aa5fce2015-11-12 09:59:44 -0800211 clippedSrcRect = srcBounds;
bsalomonc55271f2015-11-09 11:55:57 -0800212 if (dstRect) {
213 clippedDstRect = *dstRect;
bsalomon3aa5fce2015-11-12 09:59:44 -0800214 if (!srcToDstMatrix.setRectToRect(srcBounds, *dstRect, SkMatrix::kFill_ScaleToFit)) {
bsalomonc55271f2015-11-09 11:55:57 -0800215 return;
216 }
217 } else {
bsalomon3aa5fce2015-11-12 09:59:44 -0800218 clippedDstRect = srcBounds;
bsalomonc55271f2015-11-09 11:55:57 -0800219 srcToDstMatrix.reset();
220 }
221 }
222
ericrk983294f2016-04-18 09:14:00 -0700223 // Now that we have both the view and srcToDst matrices, log our scale factor.
224 LogDrawScaleFactor(SkMatrix::Concat(viewMatrix, srcToDstMatrix), paint.getFilterQuality());
225
bsalomonf1ecd212015-12-09 17:06:02 -0800226 this->drawTextureProducerImpl(producer, clippedSrcRect, clippedDstRect, constraint, viewMatrix,
Brian Salomon34169692017-08-28 15:32:01 -0400227 srcToDstMatrix, paint);
bsalomonc55271f2015-11-09 11:55:57 -0800228}
229
bsalomonb1b01992015-11-18 10:56:08 -0800230void SkGpuDevice::drawTextureProducerImpl(GrTextureProducer* producer,
bsalomonc55271f2015-11-09 11:55:57 -0800231 const SkRect& clippedSrcRect,
232 const SkRect& clippedDstRect,
233 SkCanvas::SrcRectConstraint constraint,
234 const SkMatrix& viewMatrix,
235 const SkMatrix& srcToDstMatrix,
bsalomonc55271f2015-11-09 11:55:57 -0800236 const SkPaint& paint) {
Brian Salomon09d994e2016-12-21 11:14:46 -0500237 // Specifying the texture coords as local coordinates is an attempt to enable more GrDrawOp
238 // combining by not baking anything about the srcRect, dstRect, or viewMatrix, into the texture
239 // FP. In the future this should be an opaque optimization enabled by the combination of
240 // GrDrawOp/GP and FP.
bsalomonc55271f2015-11-09 11:55:57 -0800241 const SkMaskFilter* mf = paint.getMaskFilter();
Mike Reed547c8592018-02-05 15:59:23 -0500242 if (mf && as_MFB(mf)->hasFragmentProcessor()) {
243 mf = nullptr;
244 }
bsalomonc55271f2015-11-09 11:55:57 -0800245 // The shader expects proper local coords, so we can't replace local coords with texture coords
246 // if the shader will be used. If we have a mask filter we will change the underlying geometry
247 // that is rendered.
bsalomonf1ecd212015-12-09 17:06:02 -0800248 bool canUseTextureCoordsAsLocalCoords = !use_shader(producer->isAlphaOnly(), paint) && !mf;
bsalomonc55271f2015-11-09 11:55:57 -0800249
250 bool doBicubic;
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400251 GrSamplerState::Filter fm = GrSkFilterQualityToGrFilterMode(
Brian Osmandb78cba2018-02-15 10:09:48 -0500252 paint.getFilterQuality(), viewMatrix, srcToDstMatrix,
253 fContext->contextPriv().sharpenMipmappedTextures(), &doBicubic);
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400254 const GrSamplerState::Filter* filterMode = doBicubic ? nullptr : &fm;
bsalomonc55271f2015-11-09 11:55:57 -0800255
Brian Osmane8e54582016-11-28 10:06:27 -0500256 GrTextureProducer::FilterConstraint constraintMode;
bsalomonc55271f2015-11-09 11:55:57 -0800257 if (SkCanvas::kFast_SrcRectConstraint == constraint) {
258 constraintMode = GrTextureAdjuster::kNo_FilterConstraint;
259 } else {
260 constraintMode = GrTextureAdjuster::kYes_FilterConstraint;
261 }
halcanary9d524f22016-03-29 09:03:52 -0700262
bsalomonc55271f2015-11-09 11:55:57 -0800263 // If we have to outset for AA then we will generate texture coords outside the src rect. The
264 // same happens for any mask filter that extends the bounds rendered in the dst.
265 // This is conservative as a mask filter does not have to expand the bounds rendered.
266 bool coordsAllInsideSrcRect = !paint.isAntiAlias() && !mf;
267
bsalomonb1b01992015-11-18 10:56:08 -0800268 // Check for optimization to drop the src rect constraint when on bilerp.
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400269 if (filterMode && GrSamplerState::Filter::kBilerp == *filterMode &&
bsalomonb1b01992015-11-18 10:56:08 -0800270 GrTextureAdjuster::kYes_FilterConstraint == constraintMode && coordsAllInsideSrcRect) {
271 SkMatrix combinedMatrix;
272 combinedMatrix.setConcat(viewMatrix, srcToDstMatrix);
273 if (can_ignore_bilerp_constraint(*producer, clippedSrcRect, combinedMatrix,
Brian Salomon7c8460e2017-05-12 11:36:10 -0400274 fRenderTargetContext->fsaaType())) {
bsalomonb1b01992015-11-18 10:56:08 -0800275 constraintMode = GrTextureAdjuster::kNo_FilterConstraint;
276 }
277 }
278
bsalomon3aa5fce2015-11-12 09:59:44 -0800279 const SkMatrix* textureMatrix;
280 SkMatrix tempMatrix;
281 if (canUseTextureCoordsAsLocalCoords) {
282 textureMatrix = &SkMatrix::I();
283 } else {
284 if (!srcToDstMatrix.invert(&tempMatrix)) {
285 return;
286 }
287 textureMatrix = &tempMatrix;
288 }
Brian Salomonf3569f02017-10-24 12:52:33 -0400289 auto fp = producer->createFragmentProcessor(
290 *textureMatrix, clippedSrcRect, constraintMode, coordsAllInsideSrcRect, filterMode,
291 fRenderTargetContext->colorSpaceInfo().colorSpace());
bsalomonc55271f2015-11-09 11:55:57 -0800292 if (!fp) {
293 return;
294 }
joshualitt33a5fce2015-11-18 13:28:51 -0800295
bsalomonc55271f2015-11-09 11:55:57 -0800296 GrPaint grPaint;
Brian Salomonf3569f02017-10-24 12:52:33 -0400297 if (!SkPaintToGrPaintWithTexture(fContext.get(), fRenderTargetContext->colorSpaceInfo(), paint,
298 viewMatrix, std::move(fp), producer->isAlphaOnly(),
299 &grPaint)) {
bsalomonc55271f2015-11-09 11:55:57 -0800300 return;
301 }
Chris Dalton3b51df12017-11-27 14:33:06 -0700302 GrAA aa = GrAA(paint.isAntiAlias());
bsalomonc55271f2015-11-09 11:55:57 -0800303 if (canUseTextureCoordsAsLocalCoords) {
Brian Salomon34169692017-08-28 15:32:01 -0400304 fRenderTargetContext->fillRectToRect(this->clip(), std::move(grPaint), aa, viewMatrix,
Brian Salomon82f44312017-01-11 13:42:54 -0500305 clippedDstRect, clippedSrcRect);
bsalomonc55271f2015-11-09 11:55:57 -0800306 return;
307 }
308
309 if (!mf) {
Brian Salomon34169692017-08-28 15:32:01 -0400310 fRenderTargetContext->drawRect(this->clip(), std::move(grPaint), aa, viewMatrix,
311 clippedDstRect);
bsalomonc55271f2015-11-09 11:55:57 -0800312 return;
313 }
314
315 // First see if we can do the draw + mask filter direct to the dst.
robertphillips6cfb1062016-08-22 16:13:48 -0700316 if (viewMatrix.isScaleTranslate()) {
317 SkRect devClippedDstRect;
318 viewMatrix.mapRectScaleTranslate(&devClippedDstRect, clippedDstRect);
319
320 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
Mike Reed80747ef2018-01-23 15:29:32 -0500321 if (as_MFB(mf)->directFilterRRectMaskGPU(fContext.get(),
322 fRenderTargetContext.get(),
323 std::move(grPaint),
324 this->clip(),
325 viewMatrix,
326 rec,
327 SkRRect::MakeRect(clippedDstRect),
328 SkRRect::MakeRect(devClippedDstRect))) {
robertphillips6cfb1062016-08-22 16:13:48 -0700329 return;
330 }
bsalomonc55271f2015-11-09 11:55:57 -0800331 }
robertphillips6cfb1062016-08-22 16:13:48 -0700332
bsalomonc55271f2015-11-09 11:55:57 -0800333 SkPath rectPath;
334 rectPath.addRect(clippedDstRect);
robertphillips0e7029e2015-11-30 05:45:06 -0800335 rectPath.setIsVolatile(true);
Brian Salomon0166cfd2017-03-13 17:58:25 -0400336 GrBlurUtils::drawPathWithMaskFilter(this->context(), fRenderTargetContext.get(), this->clip(),
Brian Salomon82f44312017-01-11 13:42:54 -0500337 rectPath, std::move(grPaint), aa, viewMatrix, mf,
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500338 GrStyle::SimpleFill(), true);
bsalomonc55271f2015-11-09 11:55:57 -0800339}