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 10 #include "GrBlurUtils.h" 11 #include "GrCaps.h" 12 #include "GrRenderTargetContext.h" 13 #include "GrStyle.h" 14 #include "GrTextureAdjuster.h" 15 #include "SkDraw.h" 16 #include "SkGr.h" 17 #include "SkMaskFilter.h" 18 #include "effects/GrBicubicEffect.h" 19 #include "effects/GrSimpleTextureEffect.h" 20 #include "effects/GrTextureDomain.h" 21 22 static inline bool use_shader(bool textureIsAlphaOnly, const SkPaint& paint) { 23 return textureIsAlphaOnly && paint.getShader(); 24 } 25 26 ////////////////////////////////////////////////////////////////////////////// 27 // Helper functions for dropping src rect constraint in bilerp mode. 28 29 static const SkScalar kColorBleedTolerance = 0.001f; 30 31 static bool has_aligned_samples(const SkRect& srcRect, const SkRect& transformedRect) { 32 // detect pixel disalignment 33 if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) - transformedRect.left()) < kColorBleedTolerance && 34 SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) - transformedRect.top()) < kColorBleedTolerance && 35 SkScalarAbs(transformedRect.width() - srcRect.width()) < kColorBleedTolerance && 36 SkScalarAbs(transformedRect.height() - srcRect.height()) < kColorBleedTolerance) { 37 return true; 38 } 39 return false; 40 } 41 42 static bool may_color_bleed(const SkRect& srcRect, 43 const SkRect& transformedRect, 44 const SkMatrix& m, 45 GrFSAAType fsaaType) { 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 (GrFSAAType::kUnifiedMSAA == fsaaType) { 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 71 static bool can_ignore_bilerp_constraint(const GrTextureProducer& producer, 72 const SkRect& srcRect, 73 const SkMatrix& srcRectToDeviceSpace, 74 GrFSAAType fsaaType) { 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, fsaaType)) { 82 return true; 83 } 84 } 85 return false; 86 } 87 88 ////////////////////////////////////////////////////////////////////////////// 89 90 void SkGpuDevice::drawTextureProducer(GrTextureProducer* producer, 91 const SkRect* srcRect, 92 const SkRect* dstRect, 93 SkCanvas::SrcRectConstraint constraint, 94 const SkMatrix& viewMatrix, 95 const GrClip& clip, 96 const SkPaint& paint) { 97 // This is the funnel for all non-tiled bitmap/image draw calls. Log a histogram entry. 98 SK_HISTOGRAM_BOOLEAN("DrawTiled", false); 99 100 // 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; 105 const SkRect srcBounds = SkRect::MakeIWH(producer->width(), producer->height()); 106 SkMatrix srcToDstMatrix; 107 if (srcRect) { 108 if (!dstRect) { 109 dstRect = &srcBounds; 110 } 111 if (!srcBounds.contains(*srcRect)) { 112 clippedSrcRect = *srcRect; 113 if (!clippedSrcRect.intersect(srcBounds)) { 114 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 { 128 clippedSrcRect = srcBounds; 129 if (dstRect) { 130 clippedDstRect = *dstRect; 131 if (!srcToDstMatrix.setRectToRect(srcBounds, *dstRect, SkMatrix::kFill_ScaleToFit)) { 132 return; 133 } 134 } else { 135 clippedDstRect = srcBounds; 136 srcToDstMatrix.reset(); 137 } 138 } 139 140 // Now that we have both the view and srcToDst matrices, log our scale factor. 141 LogDrawScaleFactor(SkMatrix::Concat(viewMatrix, srcToDstMatrix), paint.getFilterQuality()); 142 143 this->drawTextureProducerImpl(producer, clippedSrcRect, clippedDstRect, constraint, viewMatrix, 144 srcToDstMatrix, clip, paint); 145 } 146 147 void SkGpuDevice::drawTextureProducerImpl(GrTextureProducer* producer, 148 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) { 155 // 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. 159 const SkMaskFilter* mf = paint.getMaskFilter(); 160 // 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. 163 bool canUseTextureCoordsAsLocalCoords = !use_shader(producer->isAlphaOnly(), paint) && !mf; 164 165 bool doBicubic; 166 GrSamplerParams::FilterMode fm = 167 GrSkFilterQualityToGrFilterMode(paint.getFilterQuality(), viewMatrix, srcToDstMatrix, 168 &doBicubic); 169 const GrSamplerParams::FilterMode* filterMode = doBicubic ? nullptr : &fm; 170 171 GrTextureProducer::FilterConstraint constraintMode; 172 if (SkCanvas::kFast_SrcRectConstraint == constraint) { 173 constraintMode = GrTextureAdjuster::kNo_FilterConstraint; 174 } else { 175 constraintMode = GrTextureAdjuster::kYes_FilterConstraint; 176 } 177 178 // 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 183 // Check for optimization to drop the src rect constraint when on bilerp. 184 if (filterMode && GrSamplerParams::kBilerp_FilterMode == *filterMode && 185 GrTextureAdjuster::kYes_FilterConstraint == constraintMode && coordsAllInsideSrcRect) { 186 SkMatrix combinedMatrix; 187 combinedMatrix.setConcat(viewMatrix, srcToDstMatrix); 188 if (can_ignore_bilerp_constraint(*producer, clippedSrcRect, combinedMatrix, 189 fRenderTargetContext->fsaaType())) { 190 constraintMode = GrTextureAdjuster::kNo_FilterConstraint; 191 } 192 } 193 194 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 } 204 sk_sp<GrFragmentProcessor> fp(producer->createFragmentProcessor( 205 *textureMatrix, clippedSrcRect, constraintMode, coordsAllInsideSrcRect, filterMode, 206 fRenderTargetContext->getColorSpace())); 207 if (!fp) { 208 return; 209 } 210 211 GrPaint grPaint; 212 if (!SkPaintToGrPaintWithTexture(fContext.get(), fRenderTargetContext.get(), paint, viewMatrix, 213 fp, producer->isAlphaOnly(), &grPaint)) { 214 return; 215 } 216 GrAA aa = GrBoolToAA(paint.isAntiAlias()); 217 if (canUseTextureCoordsAsLocalCoords) { 218 fRenderTargetContext->fillRectToRect(clip, std::move(grPaint), aa, viewMatrix, 219 clippedDstRect, clippedSrcRect); 220 return; 221 } 222 223 if (!mf) { 224 fRenderTargetContext->drawRect(clip, std::move(grPaint), aa, viewMatrix, clippedDstRect); 225 return; 226 } 227 228 // First see if we can do the draw + mask filter direct to the dst. 229 if (viewMatrix.isScaleTranslate()) { 230 SkRect devClippedDstRect; 231 viewMatrix.mapRectScaleTranslate(&devClippedDstRect, clippedDstRect); 232 233 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle); 234 if (mf->directFilterRRectMaskGPU(fContext.get(), 235 fRenderTargetContext.get(), 236 std::move(grPaint), 237 clip, 238 viewMatrix, 239 rec, 240 SkRRect::MakeRect(clippedDstRect), 241 SkRRect::MakeRect(devClippedDstRect))) { 242 return; 243 } 244 } 245 246 SkPath rectPath; 247 rectPath.addRect(clippedDstRect); 248 rectPath.setIsVolatile(true); 249 GrBlurUtils::drawPathWithMaskFilter(this->context(), fRenderTargetContext.get(), this->clip(), 250 rectPath, std::move(grPaint), aa, viewMatrix, mf, 251 GrStyle::SimpleFill(), true); 252 } 253