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 #include "GrBlurUtils.h" 10 #include "GrCaps.h" 11 #include "GrColorSpaceXform.h" 12 #include "GrRenderTargetContext.h" 13 #include "GrShape.h" 14 #include "GrStyle.h" 15 #include "GrTextureAdjuster.h" 16 #include "GrTextureMaker.h" 17 #include "SkDraw.h" 18 #include "SkGr.h" 19 #include "SkMaskFilterBase.h" 20 #include "effects/GrBicubicEffect.h" 21 #include "effects/GrSimpleTextureEffect.h" 22 #include "effects/GrTextureDomain.h" 23 24 static inline bool use_shader(bool textureIsAlphaOnly, const SkPaint& paint) { 25 return textureIsAlphaOnly && paint.getShader(); 26 } 27 28 ////////////////////////////////////////////////////////////////////////////// 29 // Helper functions for dropping src rect constraint in bilerp mode. 30 31 static const SkScalar kColorBleedTolerance = 0.001f; 32 33 static bool has_aligned_samples(const SkRect& srcRect, const SkRect& transformedRect) { 34 // detect pixel disalignment 35 if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) - transformedRect.left()) < kColorBleedTolerance && 36 SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) - transformedRect.top()) < kColorBleedTolerance && 37 SkScalarAbs(transformedRect.width() - srcRect.width()) < kColorBleedTolerance && 38 SkScalarAbs(transformedRect.height() - srcRect.height()) < kColorBleedTolerance) { 39 return true; 40 } 41 return false; 42 } 43 44 static bool may_color_bleed(const SkRect& srcRect, 45 const SkRect& transformedRect, 46 const SkMatrix& m, 47 GrFSAAType fsaaType) { 48 // 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); 52 if (GrFSAAType::kUnifiedMSAA == fsaaType) { 53 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 73 static bool can_ignore_bilerp_constraint(const GrTextureProducer& producer, 74 const SkRect& srcRect, 75 const SkMatrix& srcRectToDeviceSpace, 76 GrFSAAType fsaaType) { 77 if (srcRectToDeviceSpace.rectStaysRect()) { 78 // sampling is axis-aligned 79 SkRect transformedRect; 80 srcRectToDeviceSpace.mapRect(&transformedRect, srcRect); 81 82 if (has_aligned_samples(srcRect, transformedRect) || 83 !may_color_bleed(srcRect, transformedRect, srcRectToDeviceSpace, fsaaType)) { 84 return true; 85 } 86 } 87 return false; 88 } 89 90 /** 91 * Checks whether the paint is compatible with using GrRenderTargetContext::drawTexture. It is more 92 * efficient than the GrTextureProducer general case. 93 */ 94 static bool can_use_draw_texture(const SkPaint& paint) { 95 return (!paint.getColorFilter() && !paint.getShader() && !paint.getMaskFilter() && 96 !paint.getImageFilter() && paint.getFilterQuality() < kMedium_SkFilterQuality && 97 paint.getBlendMode() == SkBlendMode::kSrcOver); 98 } 99 100 static void draw_texture(const SkPaint& paint, const SkMatrix& ctm, const SkRect* src, 101 const SkRect* dst, GrAA aa, SkCanvas::SrcRectConstraint constraint, 102 sk_sp<GrTextureProxy> proxy, SkAlphaType alphaType, 103 SkColorSpace* colorSpace, const GrClip& clip, GrRenderTargetContext* rtc) { 104 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 } 114 const GrColorSpaceInfo& dstInfo(rtc->colorSpaceInfo()); 115 auto textureXform = 116 GrColorSpaceXform::Make(colorSpace , alphaType, 117 dstInfo.colorSpace(), kPremul_SkAlphaType); 118 GrSamplerState::Filter filter; 119 switch (paint.getFilterQuality()) { 120 case kNone_SkFilterQuality: 121 filter = GrSamplerState::Filter::kNearest; 122 break; 123 case kLow_SkFilterQuality: 124 filter = GrSamplerState::Filter::kBilerp; 125 break; 126 case kMedium_SkFilterQuality: 127 case kHigh_SkFilterQuality: 128 SK_ABORT("Quality level not allowed."); 129 } 130 SkPMColor4f color; 131 if (GrPixelConfigIsAlphaOnly(proxy->config())) { 132 color = SkColor4fPrepForDst(paint.getColor4f(), dstInfo, *rtc->caps()).premul(); 133 } else { 134 float paintAlpha = paint.getColor4f().fA; 135 color = { paintAlpha, paintAlpha, paintAlpha, paintAlpha }; 136 } 137 GrQuadAAFlags aaFlags = aa == GrAA::kYes ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone; 138 rtc->drawTexture(clip, std::move(proxy), filter, color, srcRect, dstRect, aaFlags, constraint, 139 ctm, std::move(textureXform)); 140 } 141 142 ////////////////////////////////////////////////////////////////////////////// 143 144 void SkGpuDevice::drawPinnedTextureProxy(sk_sp<GrTextureProxy> proxy, uint32_t pinnedUniqueID, 145 SkColorSpace* colorSpace, SkAlphaType alphaType, 146 const SkRect* srcRect, const SkRect* dstRect, 147 SkCanvas::SrcRectConstraint constraint, 148 const SkMatrix& viewMatrix, const SkPaint& paint) { 149 GrAA aa = GrAA(paint.isAntiAlias()); 150 if (can_use_draw_texture(paint)) { 151 draw_texture(paint, viewMatrix, srcRect, dstRect, aa, constraint, std::move(proxy), 152 alphaType, colorSpace, this->clip(), fRenderTargetContext.get()); 153 return; 154 } 155 GrTextureAdjuster adjuster(this->context(), std::move(proxy), alphaType, pinnedUniqueID, 156 colorSpace); 157 this->drawTextureProducer(&adjuster, srcRect, dstRect, constraint, viewMatrix, paint, false); 158 } 159 160 void SkGpuDevice::drawTextureProducer(GrTextureProducer* producer, 161 const SkRect* srcRect, 162 const SkRect* dstRect, 163 SkCanvas::SrcRectConstraint constraint, 164 const SkMatrix& viewMatrix, 165 const SkPaint& paint, 166 bool attemptDrawTexture) { 167 if (attemptDrawTexture && can_use_draw_texture(paint)) { 168 GrAA aa = GrAA(paint.isAntiAlias()); 169 // We've done enough checks above to allow us to pass ClampNearest() and not check for 170 // scaling adjustments. 171 auto proxy = producer->refTextureProxyForParams(GrSamplerState::ClampNearest(), nullptr); 172 if (!proxy) { 173 return; 174 } 175 draw_texture(paint, viewMatrix, srcRect, dstRect, aa, constraint, std::move(proxy), 176 producer->alphaType(), producer->colorSpace(), this->clip(), 177 fRenderTargetContext.get()); 178 return; 179 } 180 181 // This is the funnel for all non-tiled bitmap/image draw calls. Log a histogram entry. 182 SK_HISTOGRAM_BOOLEAN("DrawTiled", false); 183 184 // Figure out the actual dst and src rect by clipping the src rect to the bounds of the 185 // adjuster. If the src rect is clipped then the dst rect must be recomputed. Also determine 186 // the matrix that maps the src rect to the dst rect. 187 SkRect clippedSrcRect; 188 SkRect clippedDstRect; 189 const SkRect srcBounds = SkRect::MakeIWH(producer->width(), producer->height()); 190 SkMatrix srcToDstMatrix; 191 if (srcRect) { 192 if (!dstRect) { 193 dstRect = &srcBounds; 194 } 195 if (!srcBounds.contains(*srcRect)) { 196 clippedSrcRect = *srcRect; 197 if (!clippedSrcRect.intersect(srcBounds)) { 198 return; 199 } 200 if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) { 201 return; 202 } 203 srcToDstMatrix.mapRect(&clippedDstRect, clippedSrcRect); 204 } else { 205 clippedSrcRect = *srcRect; 206 clippedDstRect = *dstRect; 207 if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) { 208 return; 209 } 210 } 211 } else { 212 clippedSrcRect = srcBounds; 213 if (dstRect) { 214 clippedDstRect = *dstRect; 215 if (!srcToDstMatrix.setRectToRect(srcBounds, *dstRect, SkMatrix::kFill_ScaleToFit)) { 216 return; 217 } 218 } else { 219 clippedDstRect = srcBounds; 220 srcToDstMatrix.reset(); 221 } 222 } 223 224 // Now that we have both the view and srcToDst matrices, log our scale factor. 225 LogDrawScaleFactor(SkMatrix::Concat(viewMatrix, srcToDstMatrix), paint.getFilterQuality()); 226 227 this->drawTextureProducerImpl(producer, clippedSrcRect, clippedDstRect, constraint, viewMatrix, 228 srcToDstMatrix, paint); 229 } 230 231 void SkGpuDevice::drawTextureProducerImpl(GrTextureProducer* producer, 232 const SkRect& clippedSrcRect, 233 const SkRect& clippedDstRect, 234 SkCanvas::SrcRectConstraint constraint, 235 const SkMatrix& viewMatrix, 236 const SkMatrix& srcToDstMatrix, 237 const SkPaint& paint) { 238 // Specifying the texture coords as local coordinates is an attempt to enable more GrDrawOp 239 // combining by not baking anything about the srcRect, dstRect, or viewMatrix, into the texture 240 // FP. In the future this should be an opaque optimization enabled by the combination of 241 // GrDrawOp/GP and FP. 242 const SkMaskFilter* mf = paint.getMaskFilter(); 243 if (mf && as_MFB(mf)->hasFragmentProcessor()) { 244 mf = nullptr; 245 } 246 // The shader expects proper local coords, so we can't replace local coords with texture coords 247 // if the shader will be used. If we have a mask filter we will change the underlying geometry 248 // that is rendered. 249 bool canUseTextureCoordsAsLocalCoords = !use_shader(producer->isAlphaOnly(), paint) && !mf; 250 251 bool doBicubic; 252 GrSamplerState::Filter fm = GrSkFilterQualityToGrFilterMode( 253 paint.getFilterQuality(), viewMatrix, srcToDstMatrix, 254 fContext->contextPriv().sharpenMipmappedTextures(), &doBicubic); 255 const GrSamplerState::Filter* filterMode = doBicubic ? nullptr : &fm; 256 257 GrTextureProducer::FilterConstraint constraintMode; 258 if (SkCanvas::kFast_SrcRectConstraint == constraint) { 259 constraintMode = GrTextureAdjuster::kNo_FilterConstraint; 260 } else { 261 constraintMode = GrTextureAdjuster::kYes_FilterConstraint; 262 } 263 264 // If we have to outset for AA then we will generate texture coords outside the src rect. The 265 // same happens for any mask filter that extends the bounds rendered in the dst. 266 // This is conservative as a mask filter does not have to expand the bounds rendered. 267 bool coordsAllInsideSrcRect = !paint.isAntiAlias() && !mf; 268 269 // Check for optimization to drop the src rect constraint when on bilerp. 270 if (filterMode && GrSamplerState::Filter::kBilerp == *filterMode && 271 GrTextureAdjuster::kYes_FilterConstraint == constraintMode && coordsAllInsideSrcRect) { 272 SkMatrix combinedMatrix; 273 combinedMatrix.setConcat(viewMatrix, srcToDstMatrix); 274 if (can_ignore_bilerp_constraint(*producer, clippedSrcRect, combinedMatrix, 275 fRenderTargetContext->fsaaType())) { 276 constraintMode = GrTextureAdjuster::kNo_FilterConstraint; 277 } 278 } 279 280 const SkMatrix* textureMatrix; 281 SkMatrix tempMatrix; 282 if (canUseTextureCoordsAsLocalCoords) { 283 textureMatrix = &SkMatrix::I(); 284 } else { 285 if (!srcToDstMatrix.invert(&tempMatrix)) { 286 return; 287 } 288 textureMatrix = &tempMatrix; 289 } 290 auto fp = producer->createFragmentProcessor(*textureMatrix, clippedSrcRect, constraintMode, 291 coordsAllInsideSrcRect, filterMode); 292 SkColorSpace* rtColorSpace = fRenderTargetContext->colorSpaceInfo().colorSpace(); 293 SkColorSpace* targetColorSpace = producer->targetColorSpace(); 294 SkColorSpace* dstColorSpace = SkToBool(rtColorSpace) ? rtColorSpace : targetColorSpace; 295 fp = GrColorSpaceXformEffect::Make(std::move(fp), producer->colorSpace(), producer->alphaType(), 296 dstColorSpace); 297 if (!fp) { 298 return; 299 } 300 301 GrPaint grPaint; 302 if (!SkPaintToGrPaintWithTexture(fContext.get(), fRenderTargetContext->colorSpaceInfo(), paint, 303 viewMatrix, std::move(fp), producer->isAlphaOnly(), 304 &grPaint)) { 305 return; 306 } 307 GrAA aa = GrAA(paint.isAntiAlias()); 308 if (canUseTextureCoordsAsLocalCoords) { 309 fRenderTargetContext->fillRectToRect(this->clip(), std::move(grPaint), aa, viewMatrix, 310 clippedDstRect, clippedSrcRect); 311 return; 312 } 313 314 if (!mf) { 315 fRenderTargetContext->drawRect(this->clip(), std::move(grPaint), aa, viewMatrix, 316 clippedDstRect); 317 return; 318 } 319 320 GrShape shape(clippedDstRect, GrStyle::SimpleFill()); 321 322 GrBlurUtils::drawShapeWithMaskFilter(this->context(), fRenderTargetContext.get(), this->clip(), 323 shape, std::move(grPaint), viewMatrix, mf); 324 } 325