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 "GrStyle.h" 14 #include "GrTextureAdjuster.h" 15 #include "GrTextureMaker.h" 16 #include "SkDraw.h" 17 #include "SkGr.h" 18 #include "SkMaskFilterBase.h" 19 #include "effects/GrBicubicEffect.h" 20 #include "effects/GrSimpleTextureEffect.h" 21 #include "effects/GrTextureDomain.h" 22 23 static inline bool use_shader(bool textureIsAlphaOnly, const SkPaint& paint) { 24 return textureIsAlphaOnly && paint.getShader(); 25 } 26 27 ////////////////////////////////////////////////////////////////////////////// 28 // Helper functions for dropping src rect constraint in bilerp mode. 29 30 static const SkScalar kColorBleedTolerance = 0.001f; 31 32 static bool has_aligned_samples(const SkRect& srcRect, const SkRect& transformedRect) { 33 // detect pixel disalignment 34 if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) - transformedRect.left()) < kColorBleedTolerance && 35 SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) - transformedRect.top()) < kColorBleedTolerance && 36 SkScalarAbs(transformedRect.width() - srcRect.width()) < kColorBleedTolerance && 37 SkScalarAbs(transformedRect.height() - srcRect.height()) < kColorBleedTolerance) { 38 return true; 39 } 40 return false; 41 } 42 43 static bool may_color_bleed(const SkRect& srcRect, 44 const SkRect& transformedRect, 45 const SkMatrix& m, 46 GrFSAAType fsaaType) { 47 // 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); 51 if (GrFSAAType::kUnifiedMSAA == fsaaType) { 52 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 72 static bool can_ignore_bilerp_constraint(const GrTextureProducer& producer, 73 const SkRect& srcRect, 74 const SkMatrix& srcRectToDeviceSpace, 75 GrFSAAType fsaaType) { 76 if (srcRectToDeviceSpace.rectStaysRect()) { 77 // sampling is axis-aligned 78 SkRect transformedRect; 79 srcRectToDeviceSpace.mapRect(&transformedRect, srcRect); 80 81 if (has_aligned_samples(srcRect, transformedRect) || 82 !may_color_bleed(srcRect, transformedRect, srcRectToDeviceSpace, fsaaType)) { 83 return true; 84 } 85 } 86 return false; 87 } 88 89 /** 90 * Checks whether the paint, matrix, and constraint are compatible with using 91 * GrRenderTargetContext::drawTextureAffine. It is more effecient than the GrTextureProducer 92 * general case. 93 */ 94 static bool can_use_draw_texture_affine(const SkPaint& paint, GrAA aa, const SkMatrix& ctm, 95 SkCanvas::SrcRectConstraint constraint) { 96 // This is disabled in Chrome until crbug.com/802408 and crbug.com/801783 can be sorted out. 97 #ifdef SK_DISABLE_TEXTURE_OP_AA 98 if (GrAA::kYes == aa) { 99 return false; 100 } 101 #endif 102 return (!paint.getColorFilter() && !paint.getShader() && !paint.getMaskFilter() && 103 !paint.getImageFilter() && paint.getFilterQuality() < kMedium_SkFilterQuality && 104 paint.getBlendMode() == SkBlendMode::kSrcOver && !ctm.hasPerspective() && 105 SkCanvas::kFast_SrcRectConstraint == constraint); 106 } 107 108 static void draw_texture_affine(const SkPaint& paint, const SkMatrix& ctm, const SkRect* src, 109 const SkRect* dst, GrAA aa, sk_sp<GrTextureProxy> proxy, 110 SkColorSpace* colorSpace, const GrClip& clip, 111 GrRenderTargetContext* rtc) { 112 SkASSERT(!(SkToBool(src) && !SkToBool(dst))); 113 SkRect srcRect = src ? *src : SkRect::MakeWH(proxy->width(), proxy->height()); 114 SkRect dstRect = dst ? *dst : srcRect; 115 if (src && !SkRect::MakeIWH(proxy->width(), proxy->height()).contains(srcRect)) { 116 // Shrink the src rect to be within bounds and proportionately shrink the dst rect. 117 SkMatrix srcToDst; 118 srcToDst.setRectToRect(srcRect, dstRect, SkMatrix::kFill_ScaleToFit); 119 SkAssertResult(srcRect.intersect(SkRect::MakeIWH(proxy->width(), proxy->height()))); 120 srcToDst.mapRect(&dstRect, srcRect); 121 } 122 auto csxf = GrColorSpaceXform::Make(colorSpace, proxy->config(), 123 rtc->colorSpaceInfo().colorSpace()); 124 GrSamplerState::Filter filter; 125 switch (paint.getFilterQuality()) { 126 case kNone_SkFilterQuality: 127 filter = GrSamplerState::Filter::kNearest; 128 break; 129 case kLow_SkFilterQuality: 130 filter = GrSamplerState::Filter::kBilerp; 131 break; 132 case kMedium_SkFilterQuality: 133 case kHigh_SkFilterQuality: 134 SK_ABORT("Quality level not allowed."); 135 } 136 GrColor color = GrPixelConfigIsAlphaOnly(proxy->config()) 137 ? SkColorToPremulGrColor(paint.getColor()) 138 : SkColorAlphaToGrColor(paint.getColor()); 139 rtc->drawTextureAffine(clip, std::move(proxy), filter, color, srcRect, dstRect, aa, ctm, 140 std::move(csxf)); 141 } 142 143 ////////////////////////////////////////////////////////////////////////////// 144 145 void SkGpuDevice::drawPinnedTextureProxy(sk_sp<GrTextureProxy> proxy, uint32_t pinnedUniqueID, 146 SkColorSpace* colorSpace, SkAlphaType alphaType, 147 const SkRect* srcRect, const SkRect* dstRect, 148 SkCanvas::SrcRectConstraint constraint, 149 const SkMatrix& viewMatrix, const SkPaint& paint) { 150 GrAA aa = GrAA(paint.isAntiAlias()); 151 if (can_use_draw_texture_affine(paint, aa, this->ctm(), constraint)) { 152 draw_texture_affine(paint, viewMatrix, srcRect, dstRect, aa, std::move(proxy), colorSpace, 153 this->clip(), fRenderTargetContext.get()); 154 return; 155 } 156 GrTextureAdjuster adjuster(this->context(), std::move(proxy), alphaType, pinnedUniqueID, 157 colorSpace); 158 this->drawTextureProducer(&adjuster, srcRect, dstRect, constraint, viewMatrix, paint); 159 } 160 161 void SkGpuDevice::drawTextureMaker(GrTextureMaker* maker, int imageW, int imageH, 162 const SkRect* srcRect, const SkRect* dstRect, 163 SkCanvas::SrcRectConstraint constraint, 164 const SkMatrix& viewMatrix, const SkPaint& paint) { 165 GrAA aa = GrAA(paint.isAntiAlias()); 166 if (can_use_draw_texture_affine(paint, aa, viewMatrix, constraint)) { 167 sk_sp<SkColorSpace> cs; 168 // We've done enough checks above to allow us to pass ClampNearest() and not check for 169 // scaling adjustments. 170 auto proxy = maker->refTextureProxyForParams( 171 GrSamplerState::ClampNearest(), fRenderTargetContext->colorSpaceInfo().colorSpace(), 172 &cs, nullptr); 173 if (!proxy) { 174 return; 175 } 176 draw_texture_affine(paint, viewMatrix, srcRect, dstRect, aa, std::move(proxy), cs.get(), 177 this->clip(), fRenderTargetContext.get()); 178 return; 179 } 180 this->drawTextureProducer(maker, srcRect, dstRect, constraint, viewMatrix, paint); 181 } 182 183 void SkGpuDevice::drawTextureProducer(GrTextureProducer* producer, 184 const SkRect* srcRect, 185 const SkRect* dstRect, 186 SkCanvas::SrcRectConstraint constraint, 187 const SkMatrix& viewMatrix, 188 const SkPaint& paint) { 189 // This is the funnel for all non-tiled bitmap/image draw calls. Log a histogram entry. 190 SK_HISTOGRAM_BOOLEAN("DrawTiled", false); 191 192 // Figure out the actual dst and src rect by clipping the src rect to the bounds of the 193 // adjuster. If the src rect is clipped then the dst rect must be recomputed. Also determine 194 // the matrix that maps the src rect to the dst rect. 195 SkRect clippedSrcRect; 196 SkRect clippedDstRect; 197 const SkRect srcBounds = SkRect::MakeIWH(producer->width(), producer->height()); 198 SkMatrix srcToDstMatrix; 199 if (srcRect) { 200 if (!dstRect) { 201 dstRect = &srcBounds; 202 } 203 if (!srcBounds.contains(*srcRect)) { 204 clippedSrcRect = *srcRect; 205 if (!clippedSrcRect.intersect(srcBounds)) { 206 return; 207 } 208 if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) { 209 return; 210 } 211 srcToDstMatrix.mapRect(&clippedDstRect, clippedSrcRect); 212 } else { 213 clippedSrcRect = *srcRect; 214 clippedDstRect = *dstRect; 215 if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) { 216 return; 217 } 218 } 219 } else { 220 clippedSrcRect = srcBounds; 221 if (dstRect) { 222 clippedDstRect = *dstRect; 223 if (!srcToDstMatrix.setRectToRect(srcBounds, *dstRect, SkMatrix::kFill_ScaleToFit)) { 224 return; 225 } 226 } else { 227 clippedDstRect = srcBounds; 228 srcToDstMatrix.reset(); 229 } 230 } 231 232 // Now that we have both the view and srcToDst matrices, log our scale factor. 233 LogDrawScaleFactor(SkMatrix::Concat(viewMatrix, srcToDstMatrix), paint.getFilterQuality()); 234 235 this->drawTextureProducerImpl(producer, clippedSrcRect, clippedDstRect, constraint, viewMatrix, 236 srcToDstMatrix, paint); 237 } 238 239 void SkGpuDevice::drawTextureProducerImpl(GrTextureProducer* producer, 240 const SkRect& clippedSrcRect, 241 const SkRect& clippedDstRect, 242 SkCanvas::SrcRectConstraint constraint, 243 const SkMatrix& viewMatrix, 244 const SkMatrix& srcToDstMatrix, 245 const SkPaint& paint) { 246 // Specifying the texture coords as local coordinates is an attempt to enable more GrDrawOp 247 // combining by not baking anything about the srcRect, dstRect, or viewMatrix, into the texture 248 // FP. In the future this should be an opaque optimization enabled by the combination of 249 // GrDrawOp/GP and FP. 250 const SkMaskFilter* mf = paint.getMaskFilter(); 251 // The shader expects proper local coords, so we can't replace local coords with texture coords 252 // if the shader will be used. If we have a mask filter we will change the underlying geometry 253 // that is rendered. 254 bool canUseTextureCoordsAsLocalCoords = !use_shader(producer->isAlphaOnly(), paint) && !mf; 255 256 bool doBicubic; 257 GrSamplerState::Filter fm = GrSkFilterQualityToGrFilterMode( 258 paint.getFilterQuality(), viewMatrix, srcToDstMatrix, &doBicubic); 259 const GrSamplerState::Filter* filterMode = doBicubic ? nullptr : &fm; 260 261 GrTextureProducer::FilterConstraint constraintMode; 262 if (SkCanvas::kFast_SrcRectConstraint == constraint) { 263 constraintMode = GrTextureAdjuster::kNo_FilterConstraint; 264 } else { 265 constraintMode = GrTextureAdjuster::kYes_FilterConstraint; 266 } 267 268 // If we have to outset for AA then we will generate texture coords outside the src rect. The 269 // same happens for any mask filter that extends the bounds rendered in the dst. 270 // This is conservative as a mask filter does not have to expand the bounds rendered. 271 bool coordsAllInsideSrcRect = !paint.isAntiAlias() && !mf; 272 273 // Check for optimization to drop the src rect constraint when on bilerp. 274 if (filterMode && GrSamplerState::Filter::kBilerp == *filterMode && 275 GrTextureAdjuster::kYes_FilterConstraint == constraintMode && coordsAllInsideSrcRect) { 276 SkMatrix combinedMatrix; 277 combinedMatrix.setConcat(viewMatrix, srcToDstMatrix); 278 if (can_ignore_bilerp_constraint(*producer, clippedSrcRect, combinedMatrix, 279 fRenderTargetContext->fsaaType())) { 280 constraintMode = GrTextureAdjuster::kNo_FilterConstraint; 281 } 282 } 283 284 const SkMatrix* textureMatrix; 285 SkMatrix tempMatrix; 286 if (canUseTextureCoordsAsLocalCoords) { 287 textureMatrix = &SkMatrix::I(); 288 } else { 289 if (!srcToDstMatrix.invert(&tempMatrix)) { 290 return; 291 } 292 textureMatrix = &tempMatrix; 293 } 294 auto fp = producer->createFragmentProcessor( 295 *textureMatrix, clippedSrcRect, constraintMode, coordsAllInsideSrcRect, filterMode, 296 fRenderTargetContext->colorSpaceInfo().colorSpace()); 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 // First see if we can do the draw + mask filter direct to the dst. 321 if (viewMatrix.isScaleTranslate()) { 322 SkRect devClippedDstRect; 323 viewMatrix.mapRectScaleTranslate(&devClippedDstRect, clippedDstRect); 324 325 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle); 326 if (as_MFB(mf)->directFilterRRectMaskGPU(fContext.get(), 327 fRenderTargetContext.get(), 328 std::move(grPaint), 329 this->clip(), 330 viewMatrix, 331 rec, 332 SkRRect::MakeRect(clippedDstRect), 333 SkRRect::MakeRect(devClippedDstRect))) { 334 return; 335 } 336 } 337 338 SkPath rectPath; 339 rectPath.addRect(clippedDstRect); 340 rectPath.setIsVolatile(true); 341 GrBlurUtils::drawPathWithMaskFilter(this->context(), fRenderTargetContext.get(), this->clip(), 342 rectPath, std::move(grPaint), aa, viewMatrix, mf, 343 GrStyle::SimpleFill(), true); 344 } 345