blob: 48015a390681ea3607180ccbc5acfbe03737a8db [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"
9
10#include "GrBlurUtils.h"
11#include "GrCaps.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"
bsalomonc55271f2015-11-09 11:55:57 -080015#include "SkDraw.h"
Brian Osman3b655982017-03-07 16:58:08 -050016#include "SkGr.h"
bsalomonc55271f2015-11-09 11:55:57 -080017#include "SkMaskFilter.h"
18#include "effects/GrBicubicEffect.h"
19#include "effects/GrSimpleTextureEffect.h"
20#include "effects/GrTextureDomain.h"
21
22static inline bool use_shader(bool textureIsAlphaOnly, const SkPaint& paint) {
23 return textureIsAlphaOnly && paint.getShader();
24}
25
bsalomonb1b01992015-11-18 10:56:08 -080026//////////////////////////////////////////////////////////////////////////////
27// Helper functions for dropping src rect constraint in bilerp mode.
28
29static const SkScalar kColorBleedTolerance = 0.001f;
30
31static bool has_aligned_samples(const SkRect& srcRect, const SkRect& transformedRect) {
32 // detect pixel disalignment
Brian Salomona911f8f2015-11-18 15:19:57 -050033 if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) - transformedRect.left()) < kColorBleedTolerance &&
34 SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) - transformedRect.top()) < kColorBleedTolerance &&
35 SkScalarAbs(transformedRect.width() - srcRect.width()) < kColorBleedTolerance &&
bsalomonb1b01992015-11-18 10:56:08 -080036 SkScalarAbs(transformedRect.height() - srcRect.height()) < kColorBleedTolerance) {
37 return true;
38 }
39 return false;
40}
41
42static bool may_color_bleed(const SkRect& srcRect,
43 const SkRect& transformedRect,
44 const SkMatrix& m,
Brian Salomon7c8460e2017-05-12 11:36:10 -040045 GrFSAAType fsaaType) {
bsalomonb1b01992015-11-18 10:56:08 -080046 // 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);
Brian Salomon7c8460e2017-05-12 11:36:10 -040050 if (GrFSAAType::kUnifiedMSAA == fsaaType) {
bsalomonb1b01992015-11-18 10:56:08 -080051 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
71static bool can_ignore_bilerp_constraint(const GrTextureProducer& producer,
72 const SkRect& srcRect,
73 const SkMatrix& srcRectToDeviceSpace,
Brian Salomon7c8460e2017-05-12 11:36:10 -040074 GrFSAAType fsaaType) {
bsalomonb1b01992015-11-18 10:56:08 -080075 if (srcRectToDeviceSpace.rectStaysRect()) {
76 // sampling is axis-aligned
77 SkRect transformedRect;
78 srcRectToDeviceSpace.mapRect(&transformedRect, srcRect);
79
80 if (has_aligned_samples(srcRect, transformedRect) ||
Brian Salomon7c8460e2017-05-12 11:36:10 -040081 !may_color_bleed(srcRect, transformedRect, srcRectToDeviceSpace, fsaaType)) {
bsalomonb1b01992015-11-18 10:56:08 -080082 return true;
83 }
84 }
85 return false;
86}
87
88//////////////////////////////////////////////////////////////////////////////
89
90void SkGpuDevice::drawTextureProducer(GrTextureProducer* producer,
bsalomonc55271f2015-11-09 11:55:57 -080091 const SkRect* srcRect,
92 const SkRect* dstRect,
93 SkCanvas::SrcRectConstraint constraint,
94 const SkMatrix& viewMatrix,
95 const GrClip& clip,
96 const SkPaint& paint) {
ericrk369e9372016-02-05 15:32:36 -080097 // This is the funnel for all non-tiled bitmap/image draw calls. Log a histogram entry.
98 SK_HISTOGRAM_BOOLEAN("DrawTiled", false);
99
bsalomonc55271f2015-11-09 11:55:57 -0800100 // 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;
bsalomonb1b01992015-11-18 10:56:08 -0800105 const SkRect srcBounds = SkRect::MakeIWH(producer->width(), producer->height());
bsalomonc55271f2015-11-09 11:55:57 -0800106 SkMatrix srcToDstMatrix;
107 if (srcRect) {
108 if (!dstRect) {
bsalomon3aa5fce2015-11-12 09:59:44 -0800109 dstRect = &srcBounds;
bsalomonc55271f2015-11-09 11:55:57 -0800110 }
bsalomon3aa5fce2015-11-12 09:59:44 -0800111 if (!srcBounds.contains(*srcRect)) {
bsalomonc55271f2015-11-09 11:55:57 -0800112 clippedSrcRect = *srcRect;
bsalomon3aa5fce2015-11-12 09:59:44 -0800113 if (!clippedSrcRect.intersect(srcBounds)) {
bsalomonc55271f2015-11-09 11:55:57 -0800114 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 {
bsalomon3aa5fce2015-11-12 09:59:44 -0800128 clippedSrcRect = srcBounds;
bsalomonc55271f2015-11-09 11:55:57 -0800129 if (dstRect) {
130 clippedDstRect = *dstRect;
bsalomon3aa5fce2015-11-12 09:59:44 -0800131 if (!srcToDstMatrix.setRectToRect(srcBounds, *dstRect, SkMatrix::kFill_ScaleToFit)) {
bsalomonc55271f2015-11-09 11:55:57 -0800132 return;
133 }
134 } else {
bsalomon3aa5fce2015-11-12 09:59:44 -0800135 clippedDstRect = srcBounds;
bsalomonc55271f2015-11-09 11:55:57 -0800136 srcToDstMatrix.reset();
137 }
138 }
139
ericrk983294f2016-04-18 09:14:00 -0700140 // Now that we have both the view and srcToDst matrices, log our scale factor.
141 LogDrawScaleFactor(SkMatrix::Concat(viewMatrix, srcToDstMatrix), paint.getFilterQuality());
142
bsalomonf1ecd212015-12-09 17:06:02 -0800143 this->drawTextureProducerImpl(producer, clippedSrcRect, clippedDstRect, constraint, viewMatrix,
144 srcToDstMatrix, clip, paint);
bsalomonc55271f2015-11-09 11:55:57 -0800145}
146
bsalomonb1b01992015-11-18 10:56:08 -0800147void SkGpuDevice::drawTextureProducerImpl(GrTextureProducer* producer,
bsalomonc55271f2015-11-09 11:55:57 -0800148 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) {
Brian Salomon09d994e2016-12-21 11:14:46 -0500155 // 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.
bsalomonc55271f2015-11-09 11:55:57 -0800159 const SkMaskFilter* mf = paint.getMaskFilter();
bsalomonc55271f2015-11-09 11:55:57 -0800160 // 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.
bsalomonf1ecd212015-12-09 17:06:02 -0800163 bool canUseTextureCoordsAsLocalCoords = !use_shader(producer->isAlphaOnly(), paint) && !mf;
bsalomonc55271f2015-11-09 11:55:57 -0800164
165 bool doBicubic;
Brian Salomon514baff2016-11-17 15:17:07 -0500166 GrSamplerParams::FilterMode fm =
bsalomonc55271f2015-11-09 11:55:57 -0800167 GrSkFilterQualityToGrFilterMode(paint.getFilterQuality(), viewMatrix, srcToDstMatrix,
168 &doBicubic);
Brian Salomon514baff2016-11-17 15:17:07 -0500169 const GrSamplerParams::FilterMode* filterMode = doBicubic ? nullptr : &fm;
bsalomonc55271f2015-11-09 11:55:57 -0800170
Brian Osmane8e54582016-11-28 10:06:27 -0500171 GrTextureProducer::FilterConstraint constraintMode;
bsalomonc55271f2015-11-09 11:55:57 -0800172 if (SkCanvas::kFast_SrcRectConstraint == constraint) {
173 constraintMode = GrTextureAdjuster::kNo_FilterConstraint;
174 } else {
175 constraintMode = GrTextureAdjuster::kYes_FilterConstraint;
176 }
halcanary9d524f22016-03-29 09:03:52 -0700177
bsalomonc55271f2015-11-09 11:55:57 -0800178 // 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 bool coordsAllInsideSrcRect = !paint.isAntiAlias() && !mf;
182
bsalomonb1b01992015-11-18 10:56:08 -0800183 // Check for optimization to drop the src rect constraint when on bilerp.
Brian Salomon514baff2016-11-17 15:17:07 -0500184 if (filterMode && GrSamplerParams::kBilerp_FilterMode == *filterMode &&
bsalomonb1b01992015-11-18 10:56:08 -0800185 GrTextureAdjuster::kYes_FilterConstraint == constraintMode && coordsAllInsideSrcRect) {
186 SkMatrix combinedMatrix;
187 combinedMatrix.setConcat(viewMatrix, srcToDstMatrix);
188 if (can_ignore_bilerp_constraint(*producer, clippedSrcRect, combinedMatrix,
Brian Salomon7c8460e2017-05-12 11:36:10 -0400189 fRenderTargetContext->fsaaType())) {
bsalomonb1b01992015-11-18 10:56:08 -0800190 constraintMode = GrTextureAdjuster::kNo_FilterConstraint;
191 }
192 }
193
bsalomon3aa5fce2015-11-12 09:59:44 -0800194 const SkMatrix* textureMatrix;
195 SkMatrix tempMatrix;
196 if (canUseTextureCoordsAsLocalCoords) {
197 textureMatrix = &SkMatrix::I();
198 } else {
199 if (!srcToDstMatrix.invert(&tempMatrix)) {
200 return;
201 }
202 textureMatrix = &tempMatrix;
203 }
bungeman06ca8ec2016-06-09 08:01:03 -0700204 sk_sp<GrFragmentProcessor> fp(producer->createFragmentProcessor(
brianosman982eb7f2016-06-06 13:10:58 -0700205 *textureMatrix, clippedSrcRect, constraintMode, coordsAllInsideSrcRect, filterMode,
Brian Osman61624f02016-12-09 14:51:59 -0500206 fRenderTargetContext->getColorSpace()));
bsalomonc55271f2015-11-09 11:55:57 -0800207 if (!fp) {
208 return;
209 }
joshualitt33a5fce2015-11-18 13:28:51 -0800210
bsalomonc55271f2015-11-09 11:55:57 -0800211 GrPaint grPaint;
Hal Canary144caf52016-11-07 17:57:18 -0500212 if (!SkPaintToGrPaintWithTexture(fContext.get(), fRenderTargetContext.get(), paint, viewMatrix,
213 fp, producer->isAlphaOnly(), &grPaint)) {
bsalomonc55271f2015-11-09 11:55:57 -0800214 return;
215 }
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500216 GrAA aa = GrBoolToAA(paint.isAntiAlias());
bsalomonc55271f2015-11-09 11:55:57 -0800217 if (canUseTextureCoordsAsLocalCoords) {
Brian Salomon82f44312017-01-11 13:42:54 -0500218 fRenderTargetContext->fillRectToRect(clip, std::move(grPaint), aa, viewMatrix,
219 clippedDstRect, clippedSrcRect);
bsalomonc55271f2015-11-09 11:55:57 -0800220 return;
221 }
222
223 if (!mf) {
Brian Salomon82f44312017-01-11 13:42:54 -0500224 fRenderTargetContext->drawRect(clip, std::move(grPaint), aa, viewMatrix, clippedDstRect);
bsalomonc55271f2015-11-09 11:55:57 -0800225 return;
226 }
227
228 // First see if we can do the draw + mask filter direct to the dst.
robertphillips6cfb1062016-08-22 16:13:48 -0700229 if (viewMatrix.isScaleTranslate()) {
230 SkRect devClippedDstRect;
231 viewMatrix.mapRectScaleTranslate(&devClippedDstRect, clippedDstRect);
232
233 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
Hal Canary144caf52016-11-07 17:57:18 -0500234 if (mf->directFilterRRectMaskGPU(fContext.get(),
235 fRenderTargetContext.get(),
Brian Salomon82f44312017-01-11 13:42:54 -0500236 std::move(grPaint),
Hal Canary144caf52016-11-07 17:57:18 -0500237 clip,
238 viewMatrix,
239 rec,
240 SkRRect::MakeRect(clippedDstRect),
241 SkRRect::MakeRect(devClippedDstRect))) {
robertphillips6cfb1062016-08-22 16:13:48 -0700242 return;
243 }
bsalomonc55271f2015-11-09 11:55:57 -0800244 }
robertphillips6cfb1062016-08-22 16:13:48 -0700245
bsalomonc55271f2015-11-09 11:55:57 -0800246 SkPath rectPath;
247 rectPath.addRect(clippedDstRect);
robertphillips0e7029e2015-11-30 05:45:06 -0800248 rectPath.setIsVolatile(true);
Brian Salomon0166cfd2017-03-13 17:58:25 -0400249 GrBlurUtils::drawPathWithMaskFilter(this->context(), fRenderTargetContext.get(), this->clip(),
Brian Salomon82f44312017-01-11 13:42:54 -0500250 rectPath, std::move(grPaint), aa, viewMatrix, mf,
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500251 GrStyle::SimpleFill(), true);
bsalomonc55271f2015-11-09 11:55:57 -0800252}