From 4723724bac707d8e2f869638a217980374dc855c Mon Sep 17 00:00:00 2001 From: Michael Ludwig Date: Tue, 10 Mar 2020 16:16:17 -0400 Subject: [PATCH] Impl. tiled bitmaps in drawImageRect with per-edge AA flags Bug: skia:3803 Change-Id: If2b529bf9167e2a94784d8797ce28a9618e86d11 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/276203 Reviewed-by: Greg Daniel Commit-Queue: Michael Ludwig --- src/gpu/SkGpuDevice.cpp | 79 +------ src/gpu/SkGpuDevice.h | 56 +---- src/gpu/SkGpuDevice_drawTexture.cpp | 333 +++++++++++----------------- 3 files changed, 128 insertions(+), 340 deletions(-) diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 264032f392..1b3c8d0b1e 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -13,7 +13,6 @@ #include "include/core/SkSurface.h" #include "include/core/SkVertices.h" #include "include/gpu/GrContext.h" -#include "include/private/SkImageInfoPriv.h" #include "include/private/SkShadowFlags.h" #include "include/private/SkTo.h" #include "src/core/SkCanvasPriv.h" @@ -34,7 +33,6 @@ #include "src/gpu/GrBlurUtils.h" #include "src/gpu/GrContextPriv.h" #include "src/gpu/GrGpu.h" -#include "src/gpu/GrImageInfo.h" #include "src/gpu/GrImageTextureMaker.h" #include "src/gpu/GrRenderTargetContextPriv.h" #include "src/gpu/GrStyle.h" @@ -42,7 +40,6 @@ #include "src/gpu/GrTextureAdjuster.h" #include "src/gpu/GrTracing.h" #include "src/gpu/SkGr.h" -#include "src/gpu/effects/GrBicubicEffect.h" #include "src/gpu/geometry/GrShape.h" #include "src/gpu/text/GrTextTarget.h" #include "src/image/SkImage_Base.h" @@ -635,10 +632,6 @@ void SkGpuDevice::drawPath(const SkPath& origSrcPath, const SkPaint& paint, bool paint, this->localToDevice(), shape); } -const GrCaps* SkGpuDevice::caps() const { - return fContext->priv().caps(); -} - void SkGpuDevice::drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint& paint) { ASSERT_SINGLE_OWNER @@ -769,76 +762,8 @@ void SkGpuDevice::drawSpecial(SkSpecialImage* special, int left, int top, const void SkGpuDevice::drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& origDst, const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) { - ASSERT_SINGLE_OWNER - // The src rect is inferred to be the bmp bounds if not provided. Otherwise, the src rect must - // be clipped to the bmp bounds. To determine tiling parameters we need the filter mode which - // in turn requires knowing the src-to-dst mapping. If the src was clipped to the bmp bounds - // then we use the src-to-dst mapping to compute a new clipped dst rect. - const SkRect* dst = &origDst; - const SkRect bmpBounds = SkRect::MakeIWH(bitmap.width(), bitmap.height()); - // Compute matrix from the two rectangles - if (!src) { - src = &bmpBounds; - } - - SkMatrix srcToDstMatrix; - if (!srcToDstMatrix.setRectToRect(*src, *dst, SkMatrix::kFill_ScaleToFit)) { - return; - } - SkRect tmpSrc, tmpDst; - if (src != &bmpBounds) { - if (!bmpBounds.contains(*src)) { - tmpSrc = *src; - if (!tmpSrc.intersect(bmpBounds)) { - return; // nothing to draw - } - src = &tmpSrc; - srcToDstMatrix.mapRect(&tmpDst, *src); - dst = &tmpDst; - } - } - - int maxTileSize = this->caps()->maxTileSize(); - - // The tile code path doesn't currently support AA, so if the paint asked for aa and we could - // draw untiled, then we bypass checking for tiling purely for optimization reasons. - bool useCoverageAA = fRenderTargetContext->numSamples() <= 1 && - paint.isAntiAlias() && bitmap.width() <= maxTileSize && - bitmap.height() <= maxTileSize; - - bool skipTileCheck = useCoverageAA || paint.getMaskFilter(); - - if (!skipTileCheck) { - int tileSize; - SkIRect clippedSrcRect; - - bool doBicubic; - GrSamplerState::Filter textureFilterMode = GrSkFilterQualityToGrFilterMode( - bitmap.width(), bitmap.height(), paint.getFilterQuality(), this->localToDevice(), - srcToDstMatrix, fContext->priv().options().fSharpenMipmappedTextures, &doBicubic); - - int tileFilterPad; - - if (doBicubic) { - tileFilterPad = GrBicubicEffect::kFilterTexelPad; - } else if (GrSamplerState::Filter::kNearest == textureFilterMode) { - tileFilterPad = 0; - } else { - tileFilterPad = 1; - } - - int maxTileSizeForFilter = this->caps()->maxTileSize() - 2 * tileFilterPad; - if (this->shouldTileImageID(bitmap.getGenerationID(), bitmap.getSubset(), - this->localToDevice(), srcToDstMatrix, src, - maxTileSizeForFilter, &tileSize, &clippedSrcRect)) { - this->drawTiledBitmap(bitmap, this->localToDevice(), srcToDstMatrix, *src, - clippedSrcRect, textureFilterMode, paint, constraint, tileSize, - doBicubic); - return; - } - } - GrBitmapTextureMaker maker(fContext.get(), bitmap, GrBitmapTextureMaker::Cached::kYes); - this->drawTextureProducer(&maker, src, dst, constraint, this->localToDevice(), paint); + sk_sp asImage = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode); + this->drawImageRect(asImage.get(), src, origDst, paint, constraint); } sk_sp SkGpuDevice::makeSpecial(const SkBitmap& bitmap) { diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h index c51d5122df..4a2be47cea 100644 --- a/src/gpu/SkGpuDevice.h +++ b/src/gpu/SkGpuDevice.h @@ -157,71 +157,19 @@ private: GrClipStackClip clip() const { return GrClipStackClip(&this->cs()); } - const GrCaps* caps() const; - - /** - * Helper functions called by drawBitmapCommon. By the time these are called the SkDraw's - * matrix, clip, and the device's render target has already been set on GrContext. - */ - - // The tileSize and clippedSrcRect will be valid only if true is returned. - bool shouldTileImageID(uint32_t imageID, - const SkIRect& imageRect, - const SkMatrix& viewMatrix, - const SkMatrix& srcToDstRectMatrix, - const SkRect* srcRectPtr, - int maxTileSize, - int* tileSize, - SkIRect* clippedSubset) const; - // Just returns the predicate, not the out-tileSize or out-clippedSubset, as they are not - // needed at the moment. - bool shouldTileImage(const SkImage* image, const SkRect* srcRectPtr, - SkCanvas::SrcRectConstraint constraint, SkFilterQuality quality, - const SkMatrix& viewMatrix, const SkMatrix& srcToDstRect) const; - sk_sp filterTexture(SkSpecialImage*, int left, int top, SkIPoint* offset, const SkImageFilter* filter); - // Splits bitmap into tiles of tileSize and draws them using separate textures for each tile. - void drawTiledBitmap(const SkBitmap& bitmap, - const SkMatrix& viewMatrix, - const SkMatrix& srcToDstMatrix, - const SkRect& srcRect, - const SkIRect& clippedSrcRect, - GrSamplerState::Filter, - const SkPaint& paint, - SkCanvas::SrcRectConstraint, - int tileSize, - bool bicubic); - - // Used by drawTiledBitmap to draw each tile. - void drawBitmapTile(const SkBitmap&, - const SkMatrix& viewMatrix, - const SkRect& dstRect, - const SkRect& srcRect, - GrSamplerState::Filter, - const SkPaint& paint, - SkCanvas::SrcRectConstraint, - bool bicubic, - bool needsTextureDomain); - // If not null, dstClip must be contained inside dst and will also respect the edge AA flags. // If 'preViewMatrix' is not null, final CTM will be this->ctm() * preViewMatrix. void drawImageQuad(const SkImage*, const SkRect* src, const SkRect* dst, const SkPoint dstClip[4], GrAA aa, GrQuadAAFlags aaFlags, const SkMatrix* preViewMatrix, const SkPaint&, SkCanvas::SrcRectConstraint); - // TODO(michaelludwig): This can be removed once drawBitmapRect is removed from SkDevice - // so that drawImageQuad is the sole entry point into the draw-single-image op - void drawTextureProducer(GrTextureProducer*, - const SkRect* srcRect, - const SkRect* dstRect, - SkCanvas::SrcRectConstraint, - const SkMatrix& viewMatrix, - const SkPaint&); - + // FIXME(michaelludwig) - Should be removed in favor of using drawImageQuad with edge flags to + // for every element in the SkLatticeIter. void drawProducerLattice(GrTextureProducer*, std::unique_ptr, const SkRect& dst, const SkPaint&); diff --git a/src/gpu/SkGpuDevice_drawTexture.cpp b/src/gpu/SkGpuDevice_drawTexture.cpp index 88b88a481c..4f86c1947e 100644 --- a/src/gpu/SkGpuDevice_drawTexture.cpp +++ b/src/gpu/SkGpuDevice_drawTexture.cpp @@ -153,27 +153,28 @@ static void determine_clipped_src_rect(int width, int height, } } -} // temporary anonymous namespace boundary before shouldTileImageID is removed from SkGPUDevice - -bool SkGpuDevice::shouldTileImageID(uint32_t imageID, - const SkIRect& imageRect, - const SkMatrix& viewMatrix, - const SkMatrix& srcToDstRect, - const SkRect* srcRectPtr, - int maxTileSize, - int* tileSize, - SkIRect* clippedSubset) const { +// tileSize and clippedSubset are valid if true is returned +static bool should_tile_image_id(GrContext* context, + SkISize rtSize, + const GrClip& clip, + uint32_t imageID, + const SkISize& imageSize, + const SkMatrix& ctm, + const SkMatrix& srcToDst, + const SkRect* src, + int maxTileSize, + int* tileSize, + SkIRect* clippedSubset) { // if it's larger than the max tile size, then we have no choice but tiling. - if (imageRect.width() > maxTileSize || imageRect.height() > maxTileSize) { - determine_clipped_src_rect(fRenderTargetContext->width(), fRenderTargetContext->height(), - this->clip(), viewMatrix, srcToDstRect, imageRect.size(), - srcRectPtr, clippedSubset); + if (imageSize.width() > maxTileSize || imageSize.height() > maxTileSize) { + determine_clipped_src_rect(rtSize.width(), rtSize.height(), clip, ctm, srcToDst, + imageSize, src, clippedSubset); *tileSize = determine_tile_size(*clippedSubset, maxTileSize); return true; } // If the image would only produce 4 tiles of the smaller size, don't bother tiling it. - const size_t area = imageRect.width() * imageRect.height(); + const size_t area = imageSize.width() * imageSize.height(); if (area < 4 * kBmpSmallTileSize * kBmpSmallTileSize) { return false; } @@ -186,15 +187,14 @@ bool SkGpuDevice::shouldTileImageID(uint32_t imageID, // assumption here is that sw bitmap size is a good proxy for its size as // a texture size_t bmpSize = area * sizeof(SkPMColor); // assume 32bit pixels - size_t cacheSize = fContext->getResourceCacheLimit(); + size_t cacheSize = context->getResourceCacheLimit(); if (bmpSize < cacheSize / 2) { return false; } // Figure out how much of the src we will need based on the src rect and clipping. Reject if // tiling memory savings would be < 50%. - determine_clipped_src_rect(fRenderTargetContext->width(), fRenderTargetContext->height(), - this->clip(), viewMatrix, srcToDstRect, imageRect.size(), srcRectPtr, + determine_clipped_src_rect(rtSize.width(), rtSize.height(), clip, ctm, srcToDst, imageSize, src, clippedSubset); *tileSize = kBmpSmallTileSize; // already know whole bitmap fits in one max sized tile. size_t usedTileBytes = get_tile_count(*clippedSubset, kBmpSmallTileSize) * @@ -204,45 +204,10 @@ bool SkGpuDevice::shouldTileImageID(uint32_t imageID, return usedTileBytes * 2 < bmpSize; } -bool SkGpuDevice::shouldTileImage(const SkImage* image, const SkRect* srcRectPtr, - SkCanvas::SrcRectConstraint constraint, SkFilterQuality quality, - const SkMatrix& viewMatrix, - const SkMatrix& srcToDstRect) const { - // If image is explicitly texture backed then we shouldn't get here. - SkASSERT(!image->isTextureBacked()); - - bool doBicubic; - GrSamplerState::Filter textureFilterMode = GrSkFilterQualityToGrFilterMode( - image->width(), image->height(), quality, viewMatrix, srcToDstRect, - fContext->priv().options().fSharpenMipmappedTextures, &doBicubic); - - int tileFilterPad; - if (doBicubic) { - tileFilterPad = GrBicubicEffect::kFilterTexelPad; - } else if (GrSamplerState::Filter::kNearest == textureFilterMode) { - tileFilterPad = 0; - } else { - tileFilterPad = 1; - } - - int maxTileSize = this->caps()->maxTileSize() - 2 * tileFilterPad; - - // these are output, which we safely ignore, as we just want to know the predicate - int outTileSize; - SkIRect outClippedSrcRect; - - return this->shouldTileImageID(image->unique(), image->bounds(), viewMatrix, srcToDstRect, - srcRectPtr, maxTileSize, &outTileSize, &outClippedSrcRect); -} - -namespace { - // This method outsets 'iRect' by 'outset' all around and then clamps its extents to // 'clamp'. 'offset' is adjusted to remain positioned over the top-left corner // of 'iRect' for all possible outsets/clamps. -static inline void clamped_outset_with_offset(SkIRect* iRect, - int outset, - SkPoint* offset, +static inline void clamped_outset_with_offset(SkIRect* iRect, int outset, SkPoint* offset, const SkIRect& clamp) { iRect->outset(outset, outset); @@ -424,7 +389,9 @@ static void draw_texture_producer(GrContext* context, GrAA aa, GrQuadAAFlags aaFlags, SkCanvas::SrcRectConstraint constraint, - GrSamplerState::WrapMode wm) { + GrSamplerState::WrapMode wm, + GrSamplerState::Filter fm, + bool doBicubic) { if (wm == GrSamplerState::WrapMode::kClamp && !producer->isPlanar() && can_use_draw_texture(paint)) { // We've done enough checks above to allow us to pass ClampNearest() and not check for @@ -454,10 +421,6 @@ static void draw_texture_producer(GrContext* context, if (mf && as_MFB(mf)->hasFragmentProcessor()) { mf = nullptr; } - bool doBicubic; - GrSamplerState::Filter fm = GrSkFilterQualityToGrFilterMode( - producer->width(), producer->height(), paint.getFilterQuality(), ctm, srcToDst, - context->priv().options().fSharpenMipmappedTextures, &doBicubic); const GrSamplerState::Filter* filterMode = doBicubic ? nullptr : &fm; GrTextureProducer::FilterConstraint constraintMode; @@ -540,36 +503,26 @@ static void draw_texture_producer(GrContext* context, } } -} // anonymous namespace - -// Break 'bitmap' into several tiles to draw it since it has already -// been determined to be too large to fit in VRAM -void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap, - const SkMatrix& viewMatrix, - const SkMatrix& dstMatrix, - const SkRect& srcRect, - const SkIRect& clippedSrcIRect, - GrSamplerState::Filter filter, - const SkPaint& origPaint, - SkCanvas::SrcRectConstraint constraint, - int tileSize, - bool bicubic) { - // This is the funnel for all paths that draw tiled bitmaps/images. Log histogram entries. - SK_HISTOGRAM_BOOLEAN("DrawTiled", true); - LogDrawScaleFactor(viewMatrix, SkMatrix::I(), origPaint.getFilterQuality()); - - const SkPaint* paint = &origPaint; - SkPaint tempPaint; - if (origPaint.isAntiAlias() && fRenderTargetContext->numSamples() <= 1) { - // Drop antialiasing to avoid seams at tile boundaries. - tempPaint = origPaint; - tempPaint.setAntiAlias(false); - paint = &tempPaint; - } +void draw_tiled_bitmap(GrContext* context, + GrRenderTargetContext* rtc, + const GrClip& clip, + const SkBitmap& bitmap, + int tileSize, + const SkMatrix& ctm, + const SkMatrix& srcToDst, + const SkRect& srcRect, + const SkIRect& clippedSrcIRect, + const SkPaint& paint, + GrAA aa, + SkCanvas::SrcRectConstraint constraint, + GrSamplerState::WrapMode wm, + GrSamplerState::Filter fm, + bool doBicubic) { SkRect clippedSrcRect = SkRect::Make(clippedSrcIRect); int nx = bitmap.width() / tileSize; int ny = bitmap.height() / tileSize; + for (int x = 0; x <= nx; x++) { for (int y = 0; y <= ny; y++) { SkRect tileR; @@ -589,8 +542,8 @@ void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap, SkVector offset = SkPoint::Make(SkIntToScalar(iTileR.fLeft), SkIntToScalar(iTileR.fTop)); SkRect rectToDraw = tileR; - dstMatrix.mapRect(&rectToDraw); - if (filter != GrSamplerState::Filter::kNearest || bicubic) { + srcToDst.mapRect(&rectToDraw); + if (fm != GrSamplerState::Filter::kNearest || doBicubic) { SkIRect iClampRect; if (SkCanvas::kFast_SrcRectConstraint == constraint) { @@ -603,94 +556,53 @@ void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap, // not bleed across the original clamped edges) srcRect.roundOut(&iClampRect); } - int outset = bicubic ? GrBicubicEffect::kFilterTexelPad : 1; + int outset = doBicubic ? GrBicubicEffect::kFilterTexelPad : 1; clamped_outset_with_offset(&iTileR, outset, &offset, iClampRect); } SkBitmap tmpB; if (bitmap.extractSubset(&tmpB, iTileR)) { + // We should have already handled bitmaps larger than the max texture size. + SkASSERT(tmpB.width() <= context->priv().caps()->maxTextureSize() && + tmpB.height() <= context->priv().caps()->maxTextureSize()); + // We should be respecting the max tile size by the time we get here. + SkASSERT(tmpB.width() <= context->priv().caps()->maxTileSize() && + tmpB.height() <= context->priv().caps()->maxTileSize()); + + GrBitmapTextureMaker tileProducer(context, tmpB, GrBitmapTextureMaker::Cached::kYes, + SkBackingFit::kExact); + + GrQuadAAFlags aaFlags = GrQuadAAFlags::kNone; + if (aa == GrAA::kYes) { + // If the entire bitmap was anti-aliased, turn on AA for the outside tile edges. + if (tileR.fLeft <= srcRect.fLeft) { + aaFlags |= GrQuadAAFlags::kLeft; + } + if (tileR.fRight >= srcRect.fRight) { + aaFlags |= GrQuadAAFlags::kRight; + } + if (tileR.fTop <= srcRect.fTop) { + aaFlags |= GrQuadAAFlags::kTop; + } + if (tileR.fBottom >= srcRect.fBottom) { + aaFlags |= GrQuadAAFlags::kBottom; + } + } + // now offset it to make it "local" to our tmp bitmap tileR.offset(-offset.fX, -offset.fY); - // de-optimized this determination - bool needsTextureDomain = true; - this->drawBitmapTile(tmpB, - viewMatrix, - rectToDraw, - tileR, - filter, - *paint, - constraint, - bicubic, - needsTextureDomain); + SkMatrix offsetSrcToDst = srcToDst; + offsetSrcToDst.preTranslate(offset.fX, offset.fY); + + draw_texture_producer(context, rtc, clip, ctm, paint, &tileProducer, tileR, + rectToDraw, nullptr, offsetSrcToDst, aa, aaFlags, constraint, + wm, fm, doBicubic); } } } } -void SkGpuDevice::drawBitmapTile(const SkBitmap& bitmap, - const SkMatrix& viewMatrix, - const SkRect& dstRect, - const SkRect& srcRect, - GrSamplerState::Filter filter, - const SkPaint& paint, - SkCanvas::SrcRectConstraint constraint, - bool bicubic, - bool needsTextureDomain) { - // We should have already handled bitmaps larger than the max texture size. - SkASSERT(bitmap.width() <= this->caps()->maxTextureSize() && - bitmap.height() <= this->caps()->maxTextureSize()); - // We should be respecting the max tile size by the time we get here. - SkASSERT(bitmap.width() <= this->caps()->maxTileSize() && - bitmap.height() <= this->caps()->maxTileSize()); - - GrMipMapped mipMapped = filter == GrSamplerState::Filter::kMipMap ? GrMipMapped::kYes - : GrMipMapped::kNo; - GrSurfaceProxyView view = GrRefCachedBitmapView(fContext.get(), bitmap, mipMapped); - if (!view) { - return; - } - - // Compute a matrix that maps the rect we will draw to the src rect. - SkMatrix texMatrix = SkMatrix::MakeRectToRect(dstRect, srcRect, SkMatrix::kFill_ScaleToFit); - - SkAlphaType srcAlphaType = bitmap.alphaType(); - - // Construct a GrPaint by setting the bitmap texture as the first effect and then configuring - // the rest from the SkPaint. - std::unique_ptr fp; - - const auto& caps = *this->caps(); - if (needsTextureDomain && (SkCanvas::kStrict_SrcRectConstraint == constraint)) { - if (bicubic) { - static constexpr auto kDir = GrBicubicEffect::Direction::kXY; - fp = GrBicubicEffect::MakeSubset(std::move(view), srcAlphaType, texMatrix, - GrSamplerState::WrapMode::kClamp, - GrSamplerState::WrapMode::kClamp, srcRect, kDir, caps); - } else { - fp = GrTextureEffect::MakeSubset(std::move(view), srcAlphaType, texMatrix, filter, - srcRect, caps); - } - } else if (bicubic) { - SkASSERT(GrSamplerState::Filter::kNearest == filter); - static constexpr auto kDir = GrBicubicEffect::Direction::kXY; - fp = GrBicubicEffect::Make(std::move(view), srcAlphaType, texMatrix, kDir); - } else { - fp = GrTextureEffect::Make(std::move(view), srcAlphaType, texMatrix, filter); - } - - fp = GrColorSpaceXformEffect::Make(std::move(fp), bitmap.colorSpace(), bitmap.alphaType(), - fRenderTargetContext->colorInfo().colorSpace()); - GrPaint grPaint; - if (!SkPaintToGrPaintWithTexture(this->context(), fRenderTargetContext->colorInfo(), paint, - viewMatrix, std::move(fp), - kAlpha_8_SkColorType == bitmap.colorType(), &grPaint)) { - return; - } - - // Coverage-based AA would cause seams between tiles. - GrAA aa = GrAA(paint.isAntiAlias() && fRenderTargetContext->numSamples() > 1); - fRenderTargetContext->drawRect(this->clip(), std::move(grPaint), aa, viewMatrix, dstRect); -} +} // anonymous namespace ////////////////////////////////////////////////////////////////////////////// @@ -721,6 +633,13 @@ void SkGpuDevice::drawImageQuad(const SkImage* image, const SkRect* srcRect, con ctm.preConcat(*preViewMatrix); } + bool doBicubic; + GrSamplerState::Filter fm = GrSkFilterQualityToGrFilterMode( + image->width(), image->height(), paint.getFilterQuality(), ctm, srcToDst, + fContext->priv().options().fSharpenMipmappedTextures, &doBicubic); + + auto clip = this->clip(); + // YUVA images can be stored in multiple images with different plane resolutions, so this // uses an effect to combine them dynamically on the GPU. This is done before requesting a // pinned texture proxy because YUV images force-flatten to RGBA in that scenario. @@ -729,9 +648,9 @@ void SkGpuDevice::drawImageQuad(const SkImage* image, const SkRect* srcRect, con LogDrawScaleFactor(ctm, srcToDst, paint.getFilterQuality()); GrYUVAImageTextureMaker maker(fContext.get(), image); - draw_texture_producer(fContext.get(), fRenderTargetContext.get(), this->clip(), ctm, paint, + draw_texture_producer(fContext.get(), fRenderTargetContext.get(), clip, ctm, paint, &maker, src, dst, dstClip, srcToDst, aa, aaFlags, constraint, - wrapMode); + wrapMode, fm, doBicubic); return; } @@ -752,21 +671,44 @@ void SkGpuDevice::drawImageQuad(const SkImage* image, const SkRect* srcRect, con } GrTextureAdjuster adjuster(fContext.get(), std::move(view), colorInfo, pinnedUniqueID); - draw_texture_producer(fContext.get(), fRenderTargetContext.get(), this->clip(), ctm, paint, + draw_texture_producer(fContext.get(), fRenderTargetContext.get(), clip, ctm, paint, &adjuster, src, dst, dstClip, srcToDst, aa, aaFlags, constraint, - wrapMode); + wrapMode, fm, doBicubic); return; } - // Next up, try tiling the image - // TODO (michaelludwig): Implement this with per-edge AA flags to handle seaming properly - // instead of going through drawBitmapRect (which will be removed from SkDevice in the future) - SkBitmap bm; - if (this->shouldTileImage(image, &src, constraint, paint.getFilterQuality(), ctm, srcToDst)) { - // only support tiling as bitmap at the moment, so force raster-version - if (as_IB(image)->getROPixels(&bm)) { - this->drawBitmapRect(bm, &src, dst, paint, constraint); - return; + // Next up, determine if the image must be tiled + { + // If image is explicitly already texture backed then we shouldn't get here. + SkASSERT(!image->isTextureBacked()); + + int tileFilterPad; + if (doBicubic) { + tileFilterPad = GrBicubicEffect::kFilterTexelPad; + } else if (GrSamplerState::Filter::kNearest == fm) { + tileFilterPad = 0; + } else { + tileFilterPad = 1; + } + int maxTileSize = fContext->priv().caps()->maxTileSize() - 2 * tileFilterPad; + int tileSize; + SkIRect clippedSubset; + if (should_tile_image_id(fContext.get(), SkISize::Make(fRenderTargetContext->width(), + fRenderTargetContext->height()), + clip, image->unique(), image->dimensions(), ctm, srcToDst, &src, + maxTileSize, &tileSize, &clippedSubset)) { + // Extract pixels on the CPU, since we have to split into separate textures before + // sending to the GPU. + SkBitmap bm; + if (as_IB(image)->getROPixels(&bm)) { + // This is the funnel for all paths that draw tiled bitmaps/images. Log histogram + SK_HISTOGRAM_BOOLEAN("DrawTiled", true); + LogDrawScaleFactor(ctm, srcToDst, paint.getFilterQuality()); + draw_tiled_bitmap(fContext.get(), fRenderTargetContext.get(), clip, bm, tileSize, + ctm, srcToDst, src, clippedSubset, paint, aa, constraint, + wrapMode, fm, doBicubic); + return; + } } } @@ -778,17 +720,19 @@ void SkGpuDevice::drawImageQuad(const SkImage* image, const SkRect* srcRect, con // texture creation. if (image->isLazyGenerated()) { GrImageTextureMaker maker(fContext.get(), image, SkImage::kAllow_CachingHint); - draw_texture_producer(fContext.get(), fRenderTargetContext.get(), this->clip(), ctm, paint, + draw_texture_producer(fContext.get(), fRenderTargetContext.get(), clip, ctm, paint, &maker, src, dst, dstClip, srcToDst, aa, aaFlags, constraint, - wrapMode); + wrapMode, fm, doBicubic); return; } + + SkBitmap bm; if (as_IB(image)->getROPixels(&bm)) { GrBitmapTextureMaker maker(fContext.get(), bm, GrBitmapTextureMaker::Cached::kYes, SkBackingFit::kExact); - draw_texture_producer(fContext.get(), fRenderTargetContext.get(), this->clip(), ctm, paint, + draw_texture_producer(fContext.get(), fRenderTargetContext.get(), clip, ctm, paint, &maker, src, dst, dstClip, srcToDst, aa, aaFlags, constraint, - wrapMode); + wrapMode, fm, doBicubic); } // Otherwise don't know how to draw it @@ -921,32 +865,3 @@ void SkGpuDevice::drawEdgeAAImageSet(const SkCanvas::ImageSetEntry set[], int co } draw(count); } - -// TODO (michaelludwig) - to be removed when drawBitmapRect doesn't need it anymore -void SkGpuDevice::drawTextureProducer(GrTextureProducer* producer, - const SkRect* srcRect, - const SkRect* dstRect, - SkCanvas::SrcRectConstraint constraint, - const SkMatrix& viewMatrix, - const SkPaint& paint) { - // The texture refactor split the old logic of drawTextureProducer into the beginning of - // drawImageQuad() and into the static draw_texture_producer. Replicate necessary logic that - // drawImageQuad() handles. - SkRect src; - SkRect dst; - SkMatrix srcToDst; - ImageDrawMode mode = optimize_sample_area(producer->dimensions(), srcRect, dstRect, nullptr, - &src, &dst, &srcToDst); - if (mode == ImageDrawMode::kSkip) { - return; - } - // There's no dstClip to worry about and the producer is already made so we wouldn't be able - // to tell it to use decals if we had to - SkASSERT(mode != ImageDrawMode::kDecal); - - draw_texture_producer(fContext.get(), fRenderTargetContext.get(), this->clip(), viewMatrix, - paint, producer, src, dst, /* clip */ nullptr, srcToDst, - GrAA(paint.isAntiAlias()), - paint.isAntiAlias() ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone, - constraint, GrSamplerState::WrapMode::kClamp); -}