blob: efbc6c98d599587e08bc28578b91f903e853b443 [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"
12#include "GrDrawContext.h"
13#include "GrStrokeInfo.h"
14#include "GrTextureParamsAdjuster.h"
15#include "SkDraw.h"
16#include "SkGrPriv.h"
17#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,
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 SkASSERT(!has_aligned_samples(srcRect, transformedRect));
49 SkRect innerSrcRect(srcRect), innerTransformedRect, outerTransformedRect(transformedRect);
50 if (isMSAA) {
51 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,
74 bool isMSAA) {
75 if (srcRectToDeviceSpace.rectStaysRect()) {
76 // sampling is axis-aligned
77 SkRect transformedRect;
78 srcRectToDeviceSpace.mapRect(&transformedRect, srcRect);
79
80 if (has_aligned_samples(srcRect, transformedRect) ||
81 !may_color_bleed(srcRect, transformedRect, srcRectToDeviceSpace, isMSAA)) {
82 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) {
97 // Figure out the actual dst and src rect by clipping the src rect to the bounds of the
98 // adjuster. If the src rect is clipped then the dst rect must be recomputed. Also determine
99 // the matrix that maps the src rect to the dst rect.
100 SkRect clippedSrcRect;
101 SkRect clippedDstRect;
bsalomonb1b01992015-11-18 10:56:08 -0800102 const SkRect srcBounds = SkRect::MakeIWH(producer->width(), producer->height());
bsalomonc55271f2015-11-09 11:55:57 -0800103 SkMatrix srcToDstMatrix;
104 if (srcRect) {
105 if (!dstRect) {
bsalomon3aa5fce2015-11-12 09:59:44 -0800106 dstRect = &srcBounds;
bsalomonc55271f2015-11-09 11:55:57 -0800107 }
bsalomon3aa5fce2015-11-12 09:59:44 -0800108 if (!srcBounds.contains(*srcRect)) {
bsalomonc55271f2015-11-09 11:55:57 -0800109 clippedSrcRect = *srcRect;
bsalomon3aa5fce2015-11-12 09:59:44 -0800110 if (!clippedSrcRect.intersect(srcBounds)) {
bsalomonc55271f2015-11-09 11:55:57 -0800111 return;
112 }
113 if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) {
114 return;
115 }
116 srcToDstMatrix.mapRect(&clippedDstRect, clippedSrcRect);
117 } else {
118 clippedSrcRect = *srcRect;
119 clippedDstRect = *dstRect;
120 if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) {
121 return;
122 }
123 }
124 } else {
bsalomon3aa5fce2015-11-12 09:59:44 -0800125 clippedSrcRect = srcBounds;
bsalomonc55271f2015-11-09 11:55:57 -0800126 if (dstRect) {
127 clippedDstRect = *dstRect;
bsalomon3aa5fce2015-11-12 09:59:44 -0800128 if (!srcToDstMatrix.setRectToRect(srcBounds, *dstRect, SkMatrix::kFill_ScaleToFit)) {
bsalomonc55271f2015-11-09 11:55:57 -0800129 return;
130 }
131 } else {
bsalomon3aa5fce2015-11-12 09:59:44 -0800132 clippedDstRect = srcBounds;
bsalomonc55271f2015-11-09 11:55:57 -0800133 srcToDstMatrix.reset();
134 }
135 }
136
bsalomonf1ecd212015-12-09 17:06:02 -0800137 this->drawTextureProducerImpl(producer, clippedSrcRect, clippedDstRect, constraint, viewMatrix,
138 srcToDstMatrix, clip, paint);
bsalomonc55271f2015-11-09 11:55:57 -0800139}
140
bsalomonb1b01992015-11-18 10:56:08 -0800141void SkGpuDevice::drawTextureProducerImpl(GrTextureProducer* producer,
bsalomonc55271f2015-11-09 11:55:57 -0800142 const SkRect& clippedSrcRect,
143 const SkRect& clippedDstRect,
144 SkCanvas::SrcRectConstraint constraint,
145 const SkMatrix& viewMatrix,
146 const SkMatrix& srcToDstMatrix,
147 const GrClip& clip,
148 const SkPaint& paint) {
149 // Specifying the texture coords as local coordinates is an attempt to enable more batching
150 // by not baking anything about the srcRect, dstRect, or viewMatrix, into the texture FP. In
151 // the future this should be an opaque optimization enabled by the combination of batch/GP and
152 // FP.
bsalomonc55271f2015-11-09 11:55:57 -0800153 const SkMaskFilter* mf = paint.getMaskFilter();
bsalomonc55271f2015-11-09 11:55:57 -0800154 // The shader expects proper local coords, so we can't replace local coords with texture coords
155 // if the shader will be used. If we have a mask filter we will change the underlying geometry
156 // that is rendered.
bsalomonf1ecd212015-12-09 17:06:02 -0800157 bool canUseTextureCoordsAsLocalCoords = !use_shader(producer->isAlphaOnly(), paint) && !mf;
bsalomonc55271f2015-11-09 11:55:57 -0800158
159 bool doBicubic;
160 GrTextureParams::FilterMode fm =
161 GrSkFilterQualityToGrFilterMode(paint.getFilterQuality(), viewMatrix, srcToDstMatrix,
162 &doBicubic);
163 const GrTextureParams::FilterMode* filterMode = doBicubic ? nullptr : &fm;
164
165 GrTextureAdjuster::FilterConstraint constraintMode;
166 if (SkCanvas::kFast_SrcRectConstraint == constraint) {
167 constraintMode = GrTextureAdjuster::kNo_FilterConstraint;
168 } else {
169 constraintMode = GrTextureAdjuster::kYes_FilterConstraint;
170 }
171
172 // If we have to outset for AA then we will generate texture coords outside the src rect. The
173 // same happens for any mask filter that extends the bounds rendered in the dst.
174 // This is conservative as a mask filter does not have to expand the bounds rendered.
175 bool coordsAllInsideSrcRect = !paint.isAntiAlias() && !mf;
176
bsalomonb1b01992015-11-18 10:56:08 -0800177 // Check for optimization to drop the src rect constraint when on bilerp.
178 if (filterMode && GrTextureParams::kBilerp_FilterMode == *filterMode &&
179 GrTextureAdjuster::kYes_FilterConstraint == constraintMode && coordsAllInsideSrcRect) {
180 SkMatrix combinedMatrix;
181 combinedMatrix.setConcat(viewMatrix, srcToDstMatrix);
182 if (can_ignore_bilerp_constraint(*producer, clippedSrcRect, combinedMatrix,
183 fRenderTarget->isUnifiedMultisampled())) {
184 constraintMode = GrTextureAdjuster::kNo_FilterConstraint;
185 }
186 }
187
bsalomon3aa5fce2015-11-12 09:59:44 -0800188 const SkMatrix* textureMatrix;
189 SkMatrix tempMatrix;
190 if (canUseTextureCoordsAsLocalCoords) {
191 textureMatrix = &SkMatrix::I();
192 } else {
193 if (!srcToDstMatrix.invert(&tempMatrix)) {
194 return;
195 }
196 textureMatrix = &tempMatrix;
197 }
bsalomonb1b01992015-11-18 10:56:08 -0800198 SkAutoTUnref<const GrFragmentProcessor> fp(producer->createFragmentProcessor(
bsalomon3aa5fce2015-11-12 09:59:44 -0800199 *textureMatrix, clippedSrcRect, constraintMode, coordsAllInsideSrcRect, filterMode));
bsalomonc55271f2015-11-09 11:55:57 -0800200 if (!fp) {
201 return;
202 }
joshualitt33a5fce2015-11-18 13:28:51 -0800203
bsalomonc55271f2015-11-09 11:55:57 -0800204 GrPaint grPaint;
bsalomonf1ecd212015-12-09 17:06:02 -0800205 if (!SkPaintToGrPaintWithTexture(fContext, paint, viewMatrix, fp, producer->isAlphaOnly(),
206 &grPaint)) {
bsalomonc55271f2015-11-09 11:55:57 -0800207 return;
208 }
209
210 if (canUseTextureCoordsAsLocalCoords) {
bsalomon3877ab82015-11-11 16:52:03 -0800211 fDrawContext->fillRectToRect(clip, grPaint, viewMatrix, clippedDstRect, clippedSrcRect);
bsalomonc55271f2015-11-09 11:55:57 -0800212 return;
213 }
214
215 if (!mf) {
216 fDrawContext->drawRect(clip, grPaint, viewMatrix, clippedDstRect);
217 return;
218 }
219
220 // First see if we can do the draw + mask filter direct to the dst.
221 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
222 SkRRect rrect;
223 rrect.setRect(clippedDstRect);
224 if (mf->directFilterRRectMaskGPU(fContext->textureProvider(),
225 fDrawContext,
226 &grPaint,
227 clip,
228 viewMatrix,
229 rec,
230 rrect)) {
231 return;
232 }
233 SkPath rectPath;
234 rectPath.addRect(clippedDstRect);
robertphillips0e7029e2015-11-30 05:45:06 -0800235 rectPath.setIsVolatile(true);
robertphillips7bceedc2015-12-01 12:51:26 -0800236 GrBlurUtils::drawPathWithMaskFilter(this->context(), fDrawContext, fClip,
bsalomonc55271f2015-11-09 11:55:57 -0800237 rectPath, &grPaint, viewMatrix, mf, paint.getPathEffect(),
robertphillips0e7029e2015-11-30 05:45:06 -0800238 GrStrokeInfo::FillInfo(), true);
bsalomonc55271f2015-11-09 11:55:57 -0800239}