Add general quad API to SkGpuDevice
Heavily refactors SkGpuDevice's internal texturing code in an attempt
to consolidate entry points for drawing an image. Helps lay the ground
work for eventually implementing bitmap tiling with per-edge AA.
Bug: skia:
Change-Id: I9feb86d5315d73119deb21e954c45e45513a63f6
Reviewed-on: https://skia-review.googlesource.com/c/191571
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/gm/compositor_quads.cpp b/gm/compositor_quads.cpp
index 02fe08d..fd2487f 100644
--- a/gm/compositor_quads.cpp
+++ b/gm/compositor_quads.cpp
@@ -9,16 +9,16 @@
#if SK_SUPPORT_GPU
-#include "GrClip.h"
-#include "GrRect.h"
-#include "GrRenderTargetContextPriv.h"
-
#include "Resources.h"
+#include "SkColorMatrixFilter.h"
#include "SkFont.h"
-#include "SkGr.h"
+#include "SkGpuDevice.h"
#include "SkGradientShader.h"
#include "SkImage_Base.h"
#include "SkLineClipper.h"
+#include "SkMorphologyImageFilter.h"
+#include "SkPaintFilterCanvas.h"
+#include "SkShaderMaskFilter.h"
#include <array>
@@ -172,9 +172,9 @@
// the Chromium quad types, and a generic GM template to arrange renderers x transforms in a grid
/////////////////////////////////////////////////////////////////////////////////////////////////
-class TileRenderer : public SkRefCntBase {
+class ClipTileRenderer : public SkRefCntBase {
public:
- virtual ~TileRenderer() {}
+ virtual ~ClipTileRenderer() {}
// Draw the base rect, possibly clipped by 'clip' if that is not null. The edges to antialias
// are specified in 'edgeAA' (to make manipulation easier than an unsigned bitfield). 'tileID'
@@ -191,7 +191,18 @@
// TODO (michaelludwig) - once the quad APIs are in SkCanvas, drop these
// cached fields, which drawTile() needs
fContext = context;
- fRTC = rtc;
+
+ SkBaseDevice* device = canvas->getDevice();
+ if (device->context()) {
+ // Pretty sure it's a SkGpuDevice since this is a run as a GPU GM, unfortunately we
+ // don't have RTTI for dynamic_cast
+ fDevice = static_cast<SkGpuDevice*>(device);
+ } else {
+ // Assume that we're in viewer and not dm, so the canvas is an SkPaintFilterCanvas
+ // and we need the device from its proxy (it always returns an SkNoPixelDevice).
+ SkPaintFilterCanvas* filteredCanvas = static_cast<SkPaintFilterCanvas*>(canvas);
+ fDevice = static_cast<SkGpuDevice*>(filteredCanvas->proxy()->getDevice());
+ }
// All three lines in a list
SkPoint lines[6];
@@ -224,15 +235,14 @@
protected:
// Remembered for convenience in drawTile, set by drawTiles()
GrContext* fContext;
- GrRenderTargetContext* fRTC;
+ SkGpuDevice* fDevice;
- GrQuadAAFlags maskToFlags(const bool edgeAA[4]) const {
- GrQuadAAFlags flags = GrQuadAAFlags::kNone;
- flags |= edgeAA[0] ? GrQuadAAFlags::kTop : GrQuadAAFlags::kNone;
- flags |= edgeAA[1] ? GrQuadAAFlags::kRight : GrQuadAAFlags::kNone;
- flags |= edgeAA[2] ? GrQuadAAFlags::kBottom : GrQuadAAFlags::kNone;
- flags |= edgeAA[3] ? GrQuadAAFlags::kLeft : GrQuadAAFlags::kNone;
- return flags;
+ SkCanvas::QuadAAFlags maskToFlags(const bool edgeAA[4]) const {
+ unsigned flags = (edgeAA[0] * SkCanvas::kTop_QuadAAFlag) |
+ (edgeAA[1] * SkCanvas::kRight_QuadAAFlag) |
+ (edgeAA[2] * SkCanvas::kBottom_QuadAAFlag) |
+ (edgeAA[3] * SkCanvas::kLeft_QuadAAFlag);
+ return static_cast<SkCanvas::QuadAAFlags>(flags);
}
// Recursively splits the quadrilateral against the segments stored in 'lines', which must be
@@ -392,23 +402,13 @@
class CompositorGM : public skiagm::GpuGM {
public:
- CompositorGM(const char* name, sk_sp<TileRenderer> renderer)
+ CompositorGM(const char* name, sk_sp<ClipTileRenderer> renderer)
: fName(name) {
fRenderers.push_back(std::move(renderer));
}
- CompositorGM(const char* name, sk_sp<TileRenderer> r1, sk_sp<TileRenderer> r2)
- : fName(name) {
- fRenderers.push_back(std::move(r1));
- fRenderers.push_back(std::move(r2));
- }
- CompositorGM(const char* name, sk_sp<TileRenderer> r1, sk_sp<TileRenderer> r2,
- sk_sp<TileRenderer> r3)
- : fName(name) {
- fRenderers.push_back(std::move(r1));
- fRenderers.push_back(std::move(r2));
- fRenderers.push_back(std::move(r3));
- }
- // 3 renderer modes is the max any GM needs right now
+ CompositorGM(const char* name, const SkTArray<sk_sp<ClipTileRenderer>> renderers)
+ : fRenderers(renderers)
+ , fName(name) {}
protected:
SkISize onISize() override {
@@ -475,7 +475,7 @@
private:
static constexpr int kMatrixCount = 5;
- SkTArray<sk_sp<TileRenderer>> fRenderers;
+ SkTArray<sk_sp<ClipTileRenderer>> fRenderers;
SkTArray<SkMatrix> fMatrices;
SkTArray<SkString> fMatrixNames;
@@ -526,20 +526,20 @@
// Implementations of TileRenderer that color the clipped tiles in various ways
////////////////////////////////////////////////////////////////////////////////////////////////
-class DebugTileRenderer : public TileRenderer {
+class DebugTileRenderer : public ClipTileRenderer {
public:
- static sk_sp<TileRenderer> Make() {
+ static sk_sp<ClipTileRenderer> Make() {
// Since aa override is disabled, the quad flags arg doesn't matter.
- return sk_sp<TileRenderer>(new DebugTileRenderer(GrQuadAAFlags::kAll, false));
+ return sk_sp<ClipTileRenderer>(new DebugTileRenderer(SkCanvas::kAll_QuadAAFlags, false));
}
- static sk_sp<TileRenderer> MakeAA() {
- return sk_sp<TileRenderer>(new DebugTileRenderer(GrQuadAAFlags::kAll, true));
+ static sk_sp<ClipTileRenderer> MakeAA() {
+ return sk_sp<ClipTileRenderer>(new DebugTileRenderer(SkCanvas::kAll_QuadAAFlags, true));
}
- static sk_sp<TileRenderer> MakeNonAA() {
- return sk_sp<TileRenderer>(new DebugTileRenderer(GrQuadAAFlags::kNone, true));
+ static sk_sp<ClipTileRenderer> MakeNonAA() {
+ return sk_sp<ClipTileRenderer>(new DebugTileRenderer(SkCanvas::kNone_QuadAAFlags, true));
}
void drawTile(SkCanvas* canvas, const SkRect& rect, const SkPoint clip[4], const bool edgeAA[4],
@@ -548,24 +548,16 @@
int i = tileID / kColCount;
int j = tileID % kColCount;
- SkPMColor4f c = {(i + 1.f) / kRowCount, (j + 1.f) / kColCount, .4f, 1.f};
+ SkColor4f c = {(i + 1.f) / kRowCount, (j + 1.f) / kColCount, .4f, 1.f};
float alpha = quadID / 10.f;
c.fR = c.fR * (1 - alpha) + alpha;
c.fG = c.fG * (1 - alpha) + alpha;
c.fB = c.fB * (1 - alpha) + alpha;
c.fA = c.fA * (1 - alpha) + alpha;
- GrPaint paint;
- paint.setColor4f(c);
-
- GrQuadAAFlags aaFlags = fEnableAAOverride ? fAAOverride : this->maskToFlags(edgeAA);
- if (clip) {
- fRTC->fillQuadWithEdgeAA(GrNoClip(), std::move(paint), GrAA::kYes, aaFlags,
- canvas->getTotalMatrix(), clip, nullptr);
- } else {
- fRTC->fillRectWithEdgeAA(GrNoClip(), std::move(paint), GrAA::kYes, aaFlags,
- canvas->getTotalMatrix(), rect);
- }
+ SkCanvas::QuadAAFlags aaFlags = fEnableAAOverride ? fAAOverride : this->maskToFlags(edgeAA);
+ fDevice->tmp_drawEdgeAAQuad(
+ rect, clip, clip ? 4 : 0, aaFlags, c.toSkColor(), SkBlendMode::kSrcOver);
}
void drawBanner(SkCanvas* canvas) override {
@@ -576,8 +568,9 @@
SkString config;
static const char* kFormat = "Ext(%s) - Int(%s)";
if (fEnableAAOverride) {
- SkASSERT(fAAOverride == GrQuadAAFlags::kAll || fAAOverride == GrQuadAAFlags::kNone);
- if (fAAOverride == GrQuadAAFlags::kAll) {
+ SkASSERT(fAAOverride == SkCanvas::kAll_QuadAAFlags ||
+ fAAOverride == SkCanvas::kNone_QuadAAFlags);
+ if (fAAOverride == SkCanvas::kAll_QuadAAFlags) {
config.appendf(kFormat, "yes", "yes");
} else {
config.appendf(kFormat, "no", "no");
@@ -591,35 +584,28 @@
}
private:
- GrQuadAAFlags fAAOverride;
+ SkCanvas::QuadAAFlags fAAOverride;
bool fEnableAAOverride;
- DebugTileRenderer(GrQuadAAFlags aa, bool enableAAOverrde)
+ DebugTileRenderer(SkCanvas::QuadAAFlags aa, bool enableAAOverrde)
: fAAOverride(aa)
, fEnableAAOverride(enableAAOverrde) {}
- typedef TileRenderer INHERITED;
+ typedef ClipTileRenderer INHERITED;
};
-class SolidColorRenderer : public TileRenderer {
+// Tests tmp_drawEdgeAAQuad
+class SolidColorRenderer : public ClipTileRenderer {
public:
- static sk_sp<TileRenderer> Make(const SkPMColor4f& color) {
- return sk_sp<TileRenderer>(new SolidColorRenderer(color));
+ static sk_sp<ClipTileRenderer> Make(const SkColor4f& color) {
+ return sk_sp<ClipTileRenderer>(new SolidColorRenderer(color));
}
void drawTile(SkCanvas* canvas, const SkRect& rect, const SkPoint clip[4], const bool edgeAA[4],
int tileID, int quadID) override {
- GrPaint paint;
- paint.setColor4f(fColor);
-
- if (clip) {
- fRTC->fillQuadWithEdgeAA(GrNoClip(), std::move(paint), GrAA::kYes,
- this->maskToFlags(edgeAA), canvas->getTotalMatrix(), clip, nullptr);
- } else {
- fRTC->fillRectWithEdgeAA(GrNoClip(), std::move(paint), GrAA::kYes,
- this->maskToFlags(edgeAA), canvas->getTotalMatrix(), rect);
- }
+ fDevice->tmp_drawEdgeAAQuad(rect, clip, clip ? 4 : 0, this->maskToFlags(edgeAA),
+ fColor.toSkColor(), SkBlendMode::kSrcOver);
}
void drawBanner(SkCanvas* canvas) override {
@@ -627,136 +613,69 @@
}
private:
- SkPMColor4f fColor;
+ SkColor4f fColor;
- SolidColorRenderer(const SkPMColor4f& color) : fColor(color) {}
+ SolidColorRenderer(const SkColor4f& color) : fColor(color) {}
- typedef TileRenderer INHERITED;
+ typedef ClipTileRenderer INHERITED;
};
-class GradientRenderer : public TileRenderer {
+// Tests tmp_drawImageSet(), but can batch the entries together in different ways
+// TODO(michaelludwig) - add transform batching
+class TextureSetRenderer : public ClipTileRenderer {
public:
- static sk_sp<TileRenderer> MakeSeamless() {
- return sk_sp<TileRenderer>(new GradientRenderer(false));
+ static sk_sp<ClipTileRenderer> MakeUnbatched(sk_sp<SkImage> image) {
+ return Make("Texture", "", std::move(image), nullptr, nullptr, nullptr, nullptr, 1.f, true);
}
- static sk_sp<TileRenderer> MakeLocal() {
- return sk_sp<TileRenderer>(new GradientRenderer(true));
+ static sk_sp<ClipTileRenderer> MakeBatched(sk_sp<SkImage> image) {
+ return Make("Texture Set", "", std::move(image), nullptr, nullptr, nullptr, nullptr, 1.f,
+ false);
}
- void drawTile(SkCanvas* canvas, const SkRect& rect, const SkPoint clip[4], const bool edgeAA[4],
- int tileID, int quadID) override {
- GrPaint paint;
- SkPaintToGrPaint(fContext, fRTC->colorSpaceInfo(), fGradient, canvas->getTotalMatrix(),
- &paint);
-
- SkRect localRect = SkRect::MakeWH(kTileWidth, kTileHeight);
- SkPoint localQuad[4];
- if (fLocal && clip) {
- GrMapRectPoints(rect, localRect, clip, localQuad, 4);
- }
-
- if (clip) {
- fRTC->fillQuadWithEdgeAA(GrNoClip(), std::move(paint), GrAA::kYes,
- this->maskToFlags(edgeAA), canvas->getTotalMatrix(), clip,
- fLocal ? localQuad : nullptr);
- } else {
- fRTC->fillRectWithEdgeAA(GrNoClip(), std::move(paint), GrAA::kYes,
- this->maskToFlags(edgeAA), canvas->getTotalMatrix(), rect,
- fLocal ? &localRect : nullptr);
- }
+ static sk_sp<ClipTileRenderer> MakeShader(const char* name, sk_sp<SkImage> image,
+ sk_sp<SkShader> shader, bool local) {
+ return Make("Shader", name, std::move(image), std::move(shader),
+ nullptr, nullptr, nullptr, 1.f, local);
}
- void drawBanner(SkCanvas* canvas) override {
- canvas->save();
- draw_text(canvas, "Gradient");
- canvas->translate(0.f, 15.f);
- if (fLocal) {
- draw_text(canvas, "Local");
- } else {
- draw_text(canvas, "Seamless");
- }
- canvas->restore();
+ static sk_sp<ClipTileRenderer> MakeColorFilter(const char* name, sk_sp<SkImage> image,
+ sk_sp<SkColorFilter> filter) {
+ return Make("Color Filter", name, std::move(image), nullptr, std::move(filter), nullptr,
+ nullptr, 1.f, false);
}
-private:
- SkPaint fGradient;
- bool fLocal;
-
- GradientRenderer(bool local) : fLocal(local) {
- static constexpr SkPoint pts[] = { {0.f, 0.f}, {0.25f * kTileWidth, 0.25f * kTileHeight} };
- static constexpr SkColor colors[] = { SK_ColorBLUE, SK_ColorWHITE };
- auto gradient = SkGradientShader::MakeLinear(pts, colors, nullptr, 2,
- SkShader::kMirror_TileMode);
- fGradient.setShader(gradient);
+ static sk_sp<ClipTileRenderer> MakeImageFilter(const char* name, sk_sp<SkImage> image,
+ sk_sp<SkImageFilter> filter) {
+ return Make("Image Filter", name, std::move(image), nullptr, nullptr, std::move(filter),
+ nullptr, 1.f, false);
}
- typedef TileRenderer INHERITED;
-};
-
-static SkRect get_image_local_rect(const sk_sp<SkImage> image, const SkRect& rect) {
- // This acts like the whole image is rendered over the entire tile grid, so derive local
- // coordinates from 'rect', based on the grid to image transform.
- SkMatrix gridToImage = SkMatrix::MakeRectToRect(SkRect::MakeWH(kColCount * kTileWidth,
- kRowCount * kTileHeight),
- SkRect::MakeWH(image->width(),
- image->height()),
- SkMatrix::kFill_ScaleToFit);
- return gridToImage.mapRect(rect);
-}
-
-class TextureRenderer : public TileRenderer {
-public:
-
- static sk_sp<TileRenderer> Make(sk_sp<SkImage> image) {
- return sk_sp<TileRenderer>(new TextureRenderer(image));
+ static sk_sp<ClipTileRenderer> MakeMaskFilter(const char* name, sk_sp<SkImage> image,
+ sk_sp<SkMaskFilter> filter) {
+ return Make("Mask Filter", name, std::move(image), nullptr, nullptr, nullptr,
+ std::move(filter), 1.f, false);
}
- void drawTile(SkCanvas* canvas, const SkRect& rect, const SkPoint clip[4], const bool edgeAA[4],
- int tileID, int quadID) override {
- SkPMColor4f color = {1.f, 1.f, 1.f, 1.f};
- SkRect localRect = get_image_local_rect(fImage, rect);
-
- fImage = fImage->makeTextureImage(fContext, nullptr);
- sk_sp<GrTextureProxy> proxy = as_IB(fImage)->asTextureProxyRef();
- SkASSERT(proxy);
- if (clip) {
- SkPoint localQuad[4];
- GrMapRectPoints(rect, localRect, clip, localQuad, 4);
- fRTC->drawTextureQuad(GrNoClip(), std::move(proxy), GrSamplerState::Filter::kBilerp,
- SkBlendMode::kSrcOver, color, localQuad, clip, GrAA::kYes,
- this->maskToFlags(edgeAA), nullptr, canvas->getTotalMatrix(), nullptr);
- } else {
- fRTC->drawTexture(GrNoClip(), std::move(proxy), GrSamplerState::Filter::kBilerp,
- SkBlendMode::kSrcOver, color, localRect, rect, GrAA::kYes,
- this->maskToFlags(edgeAA), SkCanvas::kFast_SrcRectConstraint,
- canvas->getTotalMatrix(), nullptr);
- }
+ static sk_sp<ClipTileRenderer> MakeAlpha(sk_sp<SkImage> image, SkScalar alpha) {
+ return Make("Alpha", SkStringPrintf("a = %.2f", alpha).c_str(), std::move(image), nullptr,
+ nullptr, nullptr, nullptr, alpha, false);
}
- void drawBanner(SkCanvas* canvas) override {
- draw_text(canvas, "Texture");
- }
-
-private:
- sk_sp<SkImage> fImage;
-
- TextureRenderer(sk_sp<SkImage> image)
- : fImage(image) {}
-
- typedef TileRenderer INHERITED;
-};
-
-// Looks like TextureRenderer, but bundles tiles into drawTextureSet calls
-class TextureSetRenderer : public TileRenderer {
-public:
-
- static sk_sp<TileRenderer> Make(sk_sp<SkImage> image) {
- return sk_sp<TileRenderer>(new TextureSetRenderer(image));
+ static sk_sp<ClipTileRenderer> Make(const char* topBanner, const char* bottomBanner,
+ sk_sp<SkImage> image, sk_sp<SkShader> shader,
+ sk_sp<SkColorFilter> colorFilter,
+ sk_sp<SkImageFilter> imageFilter,
+ sk_sp<SkMaskFilter> maskFilter, SkScalar paintAlpha,
+ bool resetAfterEachQuad) {
+ return sk_sp<ClipTileRenderer>(new TextureSetRenderer(topBanner, bottomBanner,
+ std::move(image), std::move(shader), std::move(colorFilter), std::move(imageFilter),
+ std::move(maskFilter), paintAlpha, resetAfterEachQuad));
}
void drawTiles(SkCanvas* canvas, GrContext* ctx, GrRenderTargetContext* rtc) override {
+ SkASSERT(fImage); // initImage should be called before any drawing
this->INHERITED::drawTiles(canvas, ctx, rtc);
// Push the last tile set
this->drawAndReset(canvas);
@@ -768,89 +687,219 @@
if (tileID != fCurrentTileID) {
this->drawAndReset(canvas);
}
- SkASSERT((fCurrentTileID < 0 && fDstClips.count() == 0 && fDstClipIndices.count() == 0 &&
+ SkASSERT((fCurrentTileID < 0 && fDstClips.count() == 0 && fDstClipCounts.count() == 0 &&
fSetEntries.count() == 0) ||
(fCurrentTileID == tileID && fSetEntries.count() > 0));
// Now don't actually draw the tile, accumulate it in the growing entry set
fCurrentTileID = tileID;
- int clipIndex = -1;
+ int clipCount = 0;
if (clip) {
- // Record the four points into fDstClips and get the pointer to the first in the array
- clipIndex = fDstClips.count();
+ // Record the four points into fDstClips
+ clipCount = 4;
fDstClips.push_back_n(4, clip);
}
- SkRect localRect = get_image_local_rect(fImage, rect);
+ // This acts like the whole image is rendered over the entire tile grid, so derive local
+ // coordinates from 'rect', based on the grid to image transform.
+ SkMatrix gridToImage = SkMatrix::MakeRectToRect(SkRect::MakeWH(kColCount * kTileWidth,
+ kRowCount * kTileHeight),
+ SkRect::MakeWH(fImage->width(),
+ fImage->height()),
+ SkMatrix::kFill_ScaleToFit);
+ SkRect localRect = gridToImage.mapRect(rect);
- fImage = fImage->makeTextureImage(fContext, nullptr);
- sk_sp<GrTextureProxy> proxy = as_IB(fImage)->asTextureProxyRef();
// drawTextureSet automatically derives appropriate local quad from localRect if clipPtr
// is not null.
- fSetEntries.push_back({proxy, localRect, rect, nullptr, 1.f, this->maskToFlags(edgeAA)});
- fDstClipIndices.push_back(clipIndex);
+ fSetEntries.push_back({fImage, localRect, rect, 1.f, this->maskToFlags(edgeAA)});
+ fDstClipCounts.push_back(clipCount);
+
+ if (fResetEachQuad) {
+ // Only ever draw one entry at a time
+ this->drawAndReset(canvas);
+ }
}
void drawBanner(SkCanvas* canvas) override {
- draw_text(canvas, "Texture Set");
+ canvas->save();
+ if (fTopBanner.size() > 0) {
+ draw_text(canvas, fTopBanner.c_str());
+ }
+ canvas->translate(0.f, 15.f);
+ if (fBottomBanner.size() > 0) {
+ draw_text(canvas, fBottomBanner.c_str());
+ }
+ canvas->restore();
}
private:
+ SkString fTopBanner;
+ SkString fBottomBanner;
+
sk_sp<SkImage> fImage;
+ sk_sp<SkShader> fShader;
+ sk_sp<SkColorFilter> fColorFilter;
+ sk_sp<SkImageFilter> fImageFilter;
+ sk_sp<SkMaskFilter> fMaskFilter;
+ SkScalar fPaintAlpha;
+ bool fResetEachQuad;
SkTArray<SkPoint> fDstClips;
- // Since fDstClips will reallocate as needed, can't get the final pointer for the entries'
- // fDstClip values until submitting the entire set
- SkTArray<int> fDstClipIndices;
- SkTArray<GrRenderTargetContext::TextureSetEntry> fSetEntries;
+ // ImageSetEntry does not yet have a fDstClipCount field
+ SkTArray<int> fDstClipCounts;
+ SkTArray<SkCanvas::ImageSetEntry> fSetEntries;
int fCurrentTileID;
- TextureSetRenderer(sk_sp<SkImage> image)
- : fImage(image)
- , fCurrentTileID(-1) {}
+ TextureSetRenderer(const char* topBanner,
+ const char* bottomBanner,
+ sk_sp<SkImage> image,
+ sk_sp<SkShader> shader,
+ sk_sp<SkColorFilter> colorFilter,
+ sk_sp<SkImageFilter> imageFilter,
+ sk_sp<SkMaskFilter> maskFilter,
+ SkScalar paintAlpha,
+ bool resetEachQuad)
+ : fTopBanner(topBanner)
+ , fBottomBanner(bottomBanner)
+ , fImage(std::move(image))
+ , fShader(std::move(shader))
+ , fColorFilter(std::move(colorFilter))
+ , fImageFilter(std::move(imageFilter))
+ , fMaskFilter(std::move(maskFilter))
+ , fPaintAlpha(paintAlpha)
+ , fResetEachQuad(resetEachQuad)
+ , fCurrentTileID(-1) {}
+
+ void configureTilePaint(const SkRect& rect, int tileID, SkPaint* paint) const {
+ paint->setAntiAlias(true);
+ paint->setFilterQuality(kLow_SkFilterQuality);
+ paint->setBlendMode(SkBlendMode::kSrcOver);
+
+ // Send non-white RGB, that should be ignored
+ paint->setColor4f({1.f, 0.4f, 0.25f, fPaintAlpha}, nullptr);
+
+
+ if (fShader) {
+ if (fResetEachQuad) {
+ // Apply a local transform in the shader to map from the tile rectangle to (0,0,w,h)
+ static const SkRect kTarget = SkRect::MakeWH(kTileWidth, kTileHeight);
+ SkMatrix local = SkMatrix::MakeRectToRect(kTarget, rect,
+ SkMatrix::kFill_ScaleToFit);
+ paint->setShader(fShader->makeWithLocalMatrix(local));
+ } else {
+ paint->setShader(fShader);
+ }
+ }
+
+ paint->setColorFilter(fColorFilter);
+ paint->setImageFilter(fImageFilter);
+ paint->setMaskFilter(fMaskFilter);
+ }
void drawAndReset(SkCanvas* canvas) {
// Early out if there's nothing to draw
if (fSetEntries.count() == 0) {
- SkASSERT(fCurrentTileID < 0 && fDstClips.count() == 0 && fDstClipIndices.count() == 0);
+ SkASSERT(fCurrentTileID < 0 && fDstClips.count() == 0 && fDstClipCounts.count() == 0);
return;
}
- // Fill in fDstClip in the entries now that fDstClips' storage won't change until after the
- // draw is finished.
- // NOTE: The eventual API in SkGpuDevice will make easier to collect
- // SkCanvas::ImageSetEntries and dst clips without this extra work, but also internally maps
- // very cleanly on to the TextureSetEntry fDstClip approach.
- SkASSERT(fDstClipIndices.count() == fSetEntries.count());
- for (int i = 0; i < fSetEntries.count(); ++i) {
- if (fDstClipIndices[i] >= 0) {
- fSetEntries[i].fDstClip = &fDstClips[fDstClipIndices[i]];
- }
- }
+ // NOTE: Eventually fDstClipCounts will just be stored as a field on each entry
+ SkASSERT(fDstClipCounts.count() == fSetEntries.count());
- // Send to GPU
- fRTC->drawTextureSet(GrNoClip(), fSetEntries.begin(), fSetEntries.count(),
- GrSamplerState::Filter::kBilerp, SkBlendMode::kSrcOver, GrAA::kYes,
- canvas->getTotalMatrix(), nullptr);
+#ifdef SK_DEBUG
+ int expectedDstClipCount = 0;
+ for (int i = 0; i < fDstClipCounts.count(); ++i) {
+ expectedDstClipCount += fDstClipCounts[i];
+ }
+ SkASSERT(expectedDstClipCount == fDstClips.count());
+#endif
+
+ SkPaint paint;
+ SkRect lastTileRect = fSetEntries[fSetEntries.count() - 1].fDstRect;
+ this->configureTilePaint(lastTileRect, fCurrentTileID, &paint);
+
+ fDevice->tmp_drawImageSetV2(fSetEntries.begin(), fDstClipCounts.begin(),
+ fSetEntries.count(), fDstClips.begin(), paint,
+ SkCanvas::kFast_SrcRectConstraint);
+
// Reset for next tile
fCurrentTileID = -1;
fDstClips.reset();
- fDstClipIndices.reset();
+ fDstClipCounts.reset();
fSetEntries.reset();
}
- typedef TileRenderer INHERITED;
+ typedef ClipTileRenderer INHERITED;
};
-DEF_GM(return new CompositorGM("debug",
- DebugTileRenderer::Make(), DebugTileRenderer::MakeAA(),
- DebugTileRenderer::MakeNonAA());)
-DEF_GM(return new CompositorGM("color", SolidColorRenderer::Make({.2f, .8f, .3f, 1.f})));
-DEF_GM(return new CompositorGM("shader",
- GradientRenderer::MakeSeamless(), GradientRenderer::MakeLocal()));
-DEF_GM(return new CompositorGM("image",
- TextureRenderer::Make(GetResourceAsImage("images/mandrill_512.png")),
- TextureSetRenderer::Make(GetResourceAsImage("images/mandrill_512.png"))));
+static SkTArray<sk_sp<ClipTileRenderer>> make_debug_renderers() {
+ SkTArray<sk_sp<ClipTileRenderer>> renderers;
+ renderers.push_back(DebugTileRenderer::Make());
+ renderers.push_back(DebugTileRenderer::MakeAA());
+ renderers.push_back(DebugTileRenderer::MakeNonAA());
+ return renderers;
+}
+
+static SkTArray<sk_sp<ClipTileRenderer>> make_shader_renderers() {
+ static constexpr SkPoint kPts[] = { {0.f, 0.f}, {0.25f * kTileWidth, 0.25f * kTileHeight} };
+ static constexpr SkColor kColors[] = { SK_ColorBLUE, SK_ColorWHITE };
+ auto gradient = SkGradientShader::MakeLinear(kPts, kColors, nullptr, 2,
+ SkShader::kMirror_TileMode);
+
+ auto info = SkImageInfo::Make(1, 1, kAlpha_8_SkColorType, kOpaque_SkAlphaType);
+ SkBitmap bm;
+ bm.allocPixels(info);
+ bm.eraseColor(SK_ColorWHITE);
+ sk_sp<SkImage> image = SkImage::MakeFromBitmap(bm);
+
+ SkTArray<sk_sp<ClipTileRenderer>> renderers;
+ renderers.push_back(TextureSetRenderer::MakeShader("Gradient", image, gradient, false));
+ renderers.push_back(TextureSetRenderer::MakeShader("Local Gradient", image, gradient, true));
+ return renderers;
+}
+
+static SkTArray<sk_sp<ClipTileRenderer>> make_image_renderers() {
+ sk_sp<SkImage> mandrill = GetResourceAsImage("images/mandrill_512.png");
+ SkTArray<sk_sp<ClipTileRenderer>> renderers;
+ renderers.push_back(TextureSetRenderer::MakeUnbatched(mandrill));
+ renderers.push_back(TextureSetRenderer::MakeBatched(mandrill));
+ return renderers;
+}
+
+static SkTArray<sk_sp<ClipTileRenderer>> make_filtered_renderers() {
+ sk_sp<SkImage> mandrill = GetResourceAsImage("images/mandrill_512.png");
+
+ SkColorMatrix cm;
+ cm.setSaturation(10);
+ sk_sp<SkColorFilter> colorFilter = SkColorFilter::MakeMatrixFilterRowMajor255(cm.fMat);
+ sk_sp<SkImageFilter> imageFilter = SkDilateImageFilter::Make(8, 8, nullptr);
+
+ static constexpr SkColor kAlphas[] = { SK_ColorTRANSPARENT, SK_ColorBLACK };
+ auto alphaGradient = SkGradientShader::MakeRadial(
+ {0.5f * kTileWidth * kColCount, 0.5f * kTileHeight * kRowCount},
+ 0.25f * kTileWidth * kColCount, kAlphas, nullptr, 2, SkShader::kClamp_TileMode);
+ sk_sp<SkMaskFilter> maskFilter = SkShaderMaskFilter::Make(std::move(alphaGradient));
+
+ SkTArray<sk_sp<ClipTileRenderer>> renderers;
+ renderers.push_back(TextureSetRenderer::MakeAlpha(mandrill, 0.5f));
+ renderers.push_back(TextureSetRenderer::MakeColorFilter("Saturation", mandrill,
+ std::move(colorFilter)));
+ // NOTE: won't draw correctly until SkCanvas' AutoLoopers are used to handle image filters
+ renderers.push_back(TextureSetRenderer::MakeImageFilter("Dilate", mandrill,
+ std::move(imageFilter)));
+
+ renderers.push_back(TextureSetRenderer::MakeMaskFilter("Shader", mandrill,
+ std::move(maskFilter)));
+ // NOTE: blur mask filters do work (tested locally), but visually they don't make much
+ // sense, since each quad is blurred independently
+ return renderers;
+}
+
+DEF_GM(return new CompositorGM("debug", make_debug_renderers());)
+DEF_GM(return new CompositorGM("color", SolidColorRenderer::Make({.2f, .8f, .3f, 1.f}));)
+DEF_GM(return new CompositorGM("shader", make_shader_renderers());)
+DEF_GM(return new CompositorGM("image", make_image_renderers());)
+DEF_GM(return new CompositorGM("filter", make_filtered_renderers());)
#endif // SK_SUPPORT_GPU
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index 4786775..42c81a9 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -2563,7 +2563,7 @@
friend class SkPictureRecord; // predrawNotify (why does it need it? <reed>)
friend class SkOverdrawCanvas;
friend class SkRasterHandleAllocator;
-
+ friend class ClipTileRenderer; // GM needs getTopDevice() until API is in SkCanvas
protected:
// For use by SkNoDrawCanvas (via SkCanvasVirtualEnforcer, which can't be a friend)
SkCanvas(const SkIRect& bounds);
diff --git a/include/utils/SkPaintFilterCanvas.h b/include/utils/SkPaintFilterCanvas.h
index 2781a20..8f6aedb 100644
--- a/include/utils/SkPaintFilterCanvas.h
+++ b/include/utils/SkPaintFilterCanvas.h
@@ -114,6 +114,8 @@
bool onGetProps(SkSurfaceProps* props) const override;
private:
+ friend class ClipTileRenderer; // GM needs proxy() until API is in SkCanvas
+
class AutoPaintFilter;
SkCanvas* proxy() const { SkASSERT(fList.count() == 1); return fList[0]; }
diff --git a/src/core/SkDevice.h b/src/core/SkDevice.h
index 2fd3c15..c0796cd 100644
--- a/src/core/SkDevice.h
+++ b/src/core/SkDevice.h
@@ -341,6 +341,8 @@
friend class SkGlyphRunList;
friend class SkGlyphRunBuilder;
+ friend class ClipTileRenderer; // GM needs context() until API is in SkCanvas
+
// used to change the backend's pixels (and possibly config/rowbytes)
// but cannot change the width/height, so there should be no change to
// any clip information.
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index d488403..84aa9b4 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -1074,7 +1074,7 @@
// Draw one at a time with GrFillRectOp and a GrPaint that emulates what GrTextureOp does
for (int i = 0; i < cnt; ++i) {
float alpha = set[i].fAlpha;
- if (set[i].fDstClip == nullptr) {
+ if (set[i].fDstClipQuad == nullptr) {
// Stick with original rectangles, which allows the ops to know more about what's
// being drawn.
this->drawTexture(clip, set[i].fProxy, filter, mode, {alpha, alpha, alpha, alpha},
@@ -1083,12 +1083,12 @@
} else {
// Generate interpolated texture coordinates to match the dst clip
SkPoint srcQuad[4];
- GrMapRectPoints(set[i].fDstRect, set[i].fSrcRect, set[i].fDstClip, srcQuad, 4);
+ GrMapRectPoints(set[i].fDstRect, set[i].fSrcRect, set[i].fDstClipQuad, srcQuad, 4);
// Don't send srcRect as the domain, since the normal case doesn't use a constraint
// with the entire srcRect, so sampling into dstRect outside of dstClip will just
// keep seams look more correct.
this->drawTextureQuad(clip, set[i].fProxy, filter, mode,
- {alpha, alpha, alpha, alpha}, srcQuad, set[i].fDstClip,
+ {alpha, alpha, alpha, alpha}, srcQuad, set[i].fDstClipQuad,
aa, set[i].fAAFlags, nullptr, viewMatrix, texXform);
}
}
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index 8f344cd..8b14e87 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -195,7 +195,7 @@
sk_sp<GrTextureProxy> fProxy;
SkRect fSrcRect;
SkRect fDstRect;
- SkPoint* fDstClip; // Must be null, or point to an array of 4 points
+ const SkPoint* fDstClipQuad; // Must be null, or point to an array of 4 points
float fAlpha;
GrQuadAAFlags fAAFlags;
};
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 0b790ca..d56b2d3 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -10,7 +10,6 @@
#include "../private/SkShadowFlags.h"
#include "GrBitmapTextureMaker.h"
#include "GrBlurUtils.h"
-#include "GrColorSpaceXform.h"
#include "GrContext.h"
#include "GrContextPriv.h"
#include "GrGpu.h"
@@ -20,7 +19,6 @@
#include "GrStyle.h"
#include "GrSurfaceProxyPriv.h"
#include "GrTextureAdjuster.h"
-#include "GrTextureProxy.h"
#include "GrTracing.h"
#include "SkCanvasPriv.h"
#include "SkDraw.h"
@@ -31,7 +29,6 @@
#include "SkImage_Base.h"
#include "SkLatticeIter.h"
#include "SkMakeUnique.h"
-#include "SkMaskFilterBase.h"
#include "SkPathEffect.h"
#include "SkPicture.h"
#include "SkPictureData.h"
@@ -49,9 +46,7 @@
#include "SkVertState.h"
#include "SkVertices.h"
#include "SkWritePixelsRec.h"
-#include "SkYUVAIndex.h"
#include "effects/GrBicubicEffect.h"
-#include "effects/GrSimpleTextureEffect.h"
#include "effects/GrTextureDomain.h"
#include "text/GrTextTarget.h"
@@ -404,8 +399,18 @@
void SkGpuDevice::drawEdgeAARect(const SkRect& r, SkCanvas::QuadAAFlags aa, SkColor color,
SkBlendMode mode) {
+ this->tmp_drawEdgeAAQuad(r, nullptr, 0, aa, color, mode);
+}
+
+void SkGpuDevice::tmp_drawEdgeAAQuad(const SkRect& rect, const SkPoint clip[], int clipCount,
+ SkCanvas::QuadAAFlags aaFlags, SkColor color,
+ SkBlendMode mode) {
ASSERT_SINGLE_OWNER
- GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawEdgeAARect", fContext.get());
+ GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "tmp_drawEdgeAAQuad", fContext.get());
+
+ // Only no clip or a quad clip is currently supported
+ SkASSERT(clipCount == 0 || clipCount == 4);
+ SkASSERT(clipCount == 0 || clip);
SkPMColor4f dstColor = SkColor4fPrepForDst(SkColor4f::FromColor(color),
fRenderTargetContext->colorSpaceInfo(),
@@ -419,8 +424,16 @@
}
// This is exclusively meant for tiling operations, so keep AA enabled to handle MSAA seaming
- fRenderTargetContext->fillRectWithEdgeAA(this->clip(), std::move(grPaint),
- GrAA::kYes, SkToGrQuadAAFlags(aa), this->ctm(), r);
+ GrQuadAAFlags grAA = SkToGrQuadAAFlags(aaFlags);
+ if (clipCount > 0) {
+ // Use fillQuadWithEdgeAA
+ fRenderTargetContext->fillQuadWithEdgeAA(this->clip(), std::move(grPaint), GrAA::kYes, grAA,
+ this->ctm(), clip, nullptr);
+ } else {
+ // Use fillRectWithEdgeAA to preserve mathematical properties of dst being rectangular
+ fRenderTargetContext->fillRectWithEdgeAA(this->clip(), std::move(grPaint), GrAA::kYes, grAA,
+ this->ctm(), rect);
+ }
}
///////////////////////////////////////////////////////////////////////////////
@@ -1280,42 +1293,9 @@
void SkGpuDevice::drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) {
ASSERT_SINGLE_OWNER
- if (!src || src->contains(image->bounds())) {
- constraint = SkCanvas::kFast_SrcRectConstraint;
- }
- if (as_IB(image)->isYUVA()) {
- GrYUVAImageTextureMaker maker(fContext.get(), image);
- this->drawTextureProducer(&maker, src, &dst, constraint, this->ctm(), paint, false);
- return;
- }
- uint32_t pinnedUniqueID;
- if (sk_sp<GrTextureProxy> proxy = as_IB(image)->refPinnedTextureProxy(&pinnedUniqueID)) {
- this->drawPinnedTextureProxy(std::move(proxy), pinnedUniqueID, as_IB(image)->colorSpace(),
- image->alphaType(), src, &dst, constraint, this->ctm(), paint);
- return;
- }
- SkBitmap bm;
- SkMatrix srcToDstRect;
- srcToDstRect.setRectToRect((src ? *src : SkRect::MakeIWH(image->width(), image->height())),
- dst, SkMatrix::kFill_ScaleToFit);
- if (this->shouldTileImage(image, src, constraint, paint.getFilterQuality(), this->ctm(),
- srcToDstRect)) {
- // only support tiling as bitmap at the moment, so force raster-version
- if (!as_IB(image)->getROPixels(&bm)) {
- return;
- }
- this->drawBitmapRect(bm, src, dst, paint, constraint);
- return;
- }
- if (image->isLazyGenerated()) {
- GrImageTextureMaker maker(fContext.get(), image, SkImage::kAllow_CachingHint);
- this->drawTextureProducer(&maker, src, &dst, constraint, this->ctm(), paint, true);
- return;
- }
- if (as_IB(image)->getROPixels(&bm)) {
- GrBitmapTextureMaker maker(fContext.get(), bm);
- this->drawTextureProducer(&maker, src, &dst, constraint, this->ctm(), paint, true);
- }
+ GrQuadAAFlags aaFlags = paint.isAntiAlias() ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone;
+ this->drawImageQuad(
+ image, src, &dst, nullptr, GrAA(paint.isAntiAlias()), aaFlags, paint, constraint);
}
// When drawing nine-patches or n-patches, cap the filter quality at kBilerp.
@@ -1420,70 +1400,12 @@
void SkGpuDevice::drawImageSet(const SkCanvas::ImageSetEntry set[], int count,
SkFilterQuality filterQuality, SkBlendMode mode) {
- SkASSERT(count > 0);
-
- GrSamplerState sampler;
- sampler.setFilterMode(kNone_SkFilterQuality == filterQuality ? GrSamplerState::Filter::kNearest
- : GrSamplerState::Filter::kBilerp);
- SkAutoTArray<GrRenderTargetContext::TextureSetEntry> textures(count);
- // We accumulate compatible proxies until we find an an incompatible one or reach the end and
- // issue the accumulated 'n' draws starting at 'base'.
- int base = 0, n = 0;
- auto draw = [&] {
- if (n > 0) {
- auto textureXform = GrColorSpaceXform::Make(
- set[base].fImage->colorSpace(), set[base].fImage->alphaType(),
- fRenderTargetContext->colorSpaceInfo().colorSpace(), kPremul_SkAlphaType);
- // Currently, drawImageSet is only used for tiled images so keep AA to handle MSAA
- // seams (this will not remain true in the future)
- fRenderTargetContext->drawTextureSet(this->clip(), textures.get() + base, n,
- sampler.filter(), mode, GrAA::kYes, this->ctm(),
- std::move(textureXform));
- }
- };
- for (int i = 0; i < count; ++i) {
- // The default SkBaseDevice implementation is based on drawImageRect which does not allow
- // non-sorted src rects. TODO: Decide this is OK or make sure we handle it.
- if (!set[i].fSrcRect.isSorted()) {
- draw();
- base = i + 1;
- n = 0;
- continue;
- }
- uint32_t uniqueID;
- textures[i].fProxy = as_IB(set[i].fImage.get())->refPinnedTextureProxy(&uniqueID);
- if (!textures[i].fProxy) {
- textures[i].fProxy =
- as_IB(set[i].fImage.get())
- ->asTextureProxyRef(fContext.get(), GrSamplerState::ClampBilerp(),
- nullptr);
- // If we failed to make a proxy then flush the accumulated set and reset for the next
- // image.
- if (!textures[i].fProxy) {
- draw();
- base = i + 1;
- n = 0;
- continue;
- }
- }
- textures[i].fSrcRect = set[i].fSrcRect;
- textures[i].fDstRect = set[i].fDstRect;
- textures[i].fDstClip = nullptr; // TODO(michaelludwig) Not exposed in SkGpuDevice API yet
- textures[i].fAlpha = set[i].fAlpha;
- textures[i].fAAFlags = SkToGrQuadAAFlags(set[i].fAAFlags);
- if (n > 0 &&
- (!GrTextureProxy::ProxiesAreCompatibleAsDynamicState(textures[i].fProxy.get(),
- textures[base].fProxy.get()) ||
- set[i].fImage->alphaType() != set[base].fImage->alphaType() ||
- !SkColorSpace::Equals(set[i].fImage->colorSpace(), set[base].fImage->colorSpace()))) {
- draw();
- base = i;
- n = 1;
- } else {
- ++n;
- }
- }
- draw();
+ SkPaint paint;
+ paint.setBlendMode(mode);
+ paint.setFilterQuality(filterQuality);
+ paint.setAntiAlias(true);
+ this->tmp_drawImageSetV2(set, nullptr, count, nullptr, paint,
+ SkCanvas::kFast_SrcRectConstraint);
}
static bool init_vertices_paint(GrContext* context, const GrColorSpaceInfo& colorSpaceInfo,
diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h
index 16c0010..434c766 100644
--- a/src/gpu/SkGpuDevice.h
+++ b/src/gpu/SkGpuDevice.h
@@ -124,6 +124,17 @@
bool onAccessPixels(SkPixmap*) override;
+ // Temporary interface until it gets lifted up to SkDevice and exposed in SkCanvas
+
+ /*
+ * dstClipCounts[] is a parallel array to the image entries, acting like the intended
+ * dstClipCount field in ImageSetEntry.
+ */
+ void tmp_drawImageSetV2(const SkCanvas::ImageSetEntry[], int dstClipCounts[], int count,
+ const SkPoint dstClips[], const SkPaint& paint,
+ SkCanvas::SrcRectConstraint constraint = SkCanvas::kStrict_SrcRectConstraint);
+ void tmp_drawEdgeAAQuad(const SkRect& rect, const SkPoint clip[], int clipCount,
+ SkCanvas::QuadAAFlags aaFlags, SkColor color, SkBlendMode mode);
protected:
bool onReadPixels(const SkPixmap&, int, int) override;
bool onWritePixels(const SkPixmap&, int, int) override;
@@ -206,16 +217,13 @@
bool bicubic,
bool needsTextureDomain);
- void drawPinnedTextureProxy(sk_sp<GrTextureProxy>,
- uint32_t pinnedUniqueID,
- SkColorSpace*,
- SkAlphaType alphaType,
- const SkRect* srcRect,
- const SkRect* dstRect,
- SkCanvas::SrcRectConstraint,
- const SkMatrix& viewMatrix,
- const SkPaint&);
+ // If not null, dstClip must be contained inside dst and will also respect the edge AA flags
+ void drawImageQuad(const SkImage*, const SkRect* src, const SkRect* dst,
+ const SkPoint dstClip[4], GrAA aa, GrQuadAAFlags edgeAA,
+ 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,
@@ -224,14 +232,6 @@
const SkPaint&,
bool attemptDrawTexture);
- void drawTextureProducerImpl(GrTextureProducer*,
- const SkRect& clippedSrcRect,
- const SkRect& clippedDstRect,
- SkCanvas::SrcRectConstraint,
- const SkMatrix& viewMatrix,
- const SkMatrix& srcToDstMatrix,
- const SkPaint&);
-
void drawProducerLattice(GrTextureProducer*, std::unique_ptr<SkLatticeIter>, const SkRect& dst,
const SkPaint&);
diff --git a/src/gpu/SkGpuDevice_drawTexture.cpp b/src/gpu/SkGpuDevice_drawTexture.cpp
index 21815f7..da2c887 100644
--- a/src/gpu/SkGpuDevice_drawTexture.cpp
+++ b/src/gpu/SkGpuDevice_drawTexture.cpp
@@ -6,9 +6,12 @@
*/
#include "SkGpuDevice.h"
+
+#include "GrBitmapTextureMaker.h"
#include "GrBlurUtils.h"
#include "GrCaps.h"
#include "GrColorSpaceXform.h"
+#include "GrImageTextureMaker.h"
#include "GrRenderTargetContext.h"
#include "GrShape.h"
#include "GrStyle.h"
@@ -16,11 +19,15 @@
#include "GrTextureMaker.h"
#include "SkDraw.h"
#include "SkGr.h"
+#include "SkImage_Base.h"
#include "SkMaskFilterBase.h"
+#include "SkYUVAIndex.h"
#include "effects/GrBicubicEffect.h"
#include "effects/GrSimpleTextureEffect.h"
#include "effects/GrTextureDomain.h"
+namespace {
+
static inline bool use_shader(bool textureIsAlphaOnly, const SkPaint& paint) {
return textureIsAlphaOnly && paint.getShader();
}
@@ -87,6 +94,73 @@
return false;
}
+enum class ImageDrawMode {
+ // Src and dst have been restricted to the image content. May need to clamp, no need to decal.
+ kOptimized,
+ // Src and dst are their original sizes, requires use of a decal instead of plain clamping.
+ // This is used when a dst clip is provided and extends outside of the optimized dst rect.
+ kDecal,
+ // Src or dst are empty, or do not intersect the image content so don't draw anything.
+ kSkip
+};
+
+/**
+ * Optimize the src rect sampling area within an image (sized 'width' x 'height') such that
+ * 'outSrcRect' will be completely contained in the image's bounds. The corresponding rect
+ * to draw will be output to 'outDstRect'. The mapping between src and dst will be cached in
+ * 'srcToDst'. Outputs are not always updated when kSkip is returned.
+ *
+ * If 'origSrcRect' is null, implicitly use the image bounds. If 'origDstRect' is null, use the
+ * original src rect. 'dstClip' should be null when there is no additional clipping.
+ */
+static ImageDrawMode optimize_sample_area(const SkISize& image, const SkRect* origSrcRect,
+ const SkRect* origDstRect, const SkPoint dstClip[4],
+ SkRect* outSrcRect, SkRect* outDstRect,
+ SkMatrix* srcToDst) {
+ SkRect srcBounds = SkRect::MakeIWH(image.fWidth, image.fHeight);
+
+ SkRect src = origSrcRect ? *origSrcRect : srcBounds;
+ SkRect dst = origDstRect ? *origDstRect : src;
+
+ if (src.isEmpty() || dst.isEmpty()) {
+ return ImageDrawMode::kSkip;
+ }
+
+ if (outDstRect) {
+ srcToDst->setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
+ } else {
+ srcToDst->setIdentity();
+ }
+
+ if (origSrcRect && !srcBounds.contains(src)) {
+ if (!src.intersect(srcBounds)) {
+ return ImageDrawMode::kSkip;
+ }
+ srcToDst->mapRect(&dst, src);
+
+ // Both src and dst have gotten smaller. If dstClip is provided, confirm it is still
+ // contained in dst, otherwise cannot optimize the sample area and must use a decal instead
+ if (dstClip) {
+ for (int i = 0; i < 4; ++i) {
+ if (!dst.contains(dstClip[i].fX, dstClip[i].fY)) {
+ // Must resort to using a decal mode restricted to the clipped 'src', and
+ // use the original dst rect (filling in src bounds as needed)
+ *outSrcRect = src;
+ *outDstRect = (origDstRect ? *origDstRect
+ : (origSrcRect ? *origSrcRect : srcBounds));
+ return ImageDrawMode::kDecal;
+ }
+ }
+ }
+ }
+
+ // The original src and dst were fully contained in the image, or there was no dst clip to
+ // worry about, or the clip was still contained in the restricted dst rect.
+ *outSrcRect = src;
+ *outDstRect = dst;
+ return ImageDrawMode::kOptimized;
+}
+
/**
* Checks whether the paint is compatible with using GrRenderTargetContext::drawTexture. It is more
* efficient than the GrTextureProducer general case.
@@ -96,20 +170,12 @@
!paint.getImageFilter() && paint.getFilterQuality() < kMedium_SkFilterQuality);
}
-static void draw_texture(const SkPaint& paint, const SkMatrix& ctm, const SkRect* src,
- const SkRect* dst, GrAA aa, SkCanvas::SrcRectConstraint constraint,
- sk_sp<GrTextureProxy> proxy, SkAlphaType alphaType,
- SkColorSpace* colorSpace, const GrClip& clip, GrRenderTargetContext* rtc) {
- SkASSERT(!(SkToBool(src) && !SkToBool(dst)));
- SkRect srcRect = src ? *src : SkRect::MakeWH(proxy->width(), proxy->height());
- SkRect dstRect = dst ? *dst : srcRect;
- if (src && !SkRect::MakeIWH(proxy->width(), proxy->height()).contains(srcRect)) {
- // Shrink the src rect to be within bounds and proportionately shrink the dst rect.
- SkMatrix srcToDst;
- srcToDst.setRectToRect(srcRect, dstRect, SkMatrix::kFill_ScaleToFit);
- SkAssertResult(srcRect.intersect(SkRect::MakeIWH(proxy->width(), proxy->height())));
- srcToDst.mapRect(&dstRect, srcRect);
- }
+// Assumes srcRect and dstRect have already been optimized to fit the proxy
+static void draw_texture(GrRenderTargetContext* rtc, const GrClip& clip, const SkMatrix& ctm,
+ const SkPaint& paint, const SkRect& srcRect, const SkRect& dstRect,
+ const SkPoint dstClip[4], GrAA aa, GrQuadAAFlags aaFlags,
+ SkCanvas::SrcRectConstraint constraint, sk_sp<GrTextureProxy> proxy,
+ SkAlphaType alphaType, SkColorSpace* colorSpace) {
const GrColorSpaceInfo& dstInfo(rtc->colorSpaceInfo());
auto textureXform =
GrColorSpaceXform::Make(colorSpace , alphaType,
@@ -148,107 +214,42 @@
float paintAlpha = paint.getColor4f().fA;
color = { paintAlpha, paintAlpha, paintAlpha, paintAlpha };
}
- GrQuadAAFlags aaFlags = aa == GrAA::kYes ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone;
- rtc->drawTexture(clip, std::move(proxy), filter, paint.getBlendMode(), color, srcRect, dstRect,
- aa, aaFlags, constraint, ctm, std::move(textureXform));
-}
-//////////////////////////////////////////////////////////////////////////////
+ if (dstClip) {
+ // Get source coords corresponding to dstClip
+ SkPoint srcQuad[4];
+ GrMapRectPoints(dstRect, srcRect, dstClip, srcQuad, 4);
-void SkGpuDevice::drawPinnedTextureProxy(sk_sp<GrTextureProxy> proxy, uint32_t pinnedUniqueID,
- SkColorSpace* colorSpace, SkAlphaType alphaType,
- const SkRect* srcRect, const SkRect* dstRect,
- SkCanvas::SrcRectConstraint constraint,
- const SkMatrix& viewMatrix, const SkPaint& paint) {
- GrAA aa = GrAA(paint.isAntiAlias());
- if (can_use_draw_texture(paint)) {
- draw_texture(paint, viewMatrix, srcRect, dstRect, aa, constraint, std::move(proxy),
- alphaType, colorSpace, this->clip(), fRenderTargetContext.get());
- return;
+ rtc->drawTextureQuad(clip, std::move(proxy), filter, paint.getBlendMode(), color,
+ srcQuad, dstClip, aa, aaFlags,
+ constraint == SkCanvas::kStrict_SrcRectConstraint ? &srcRect : nullptr,
+ ctm, std::move(textureXform));
+ } else {
+ rtc->drawTexture(clip, std::move(proxy), filter, paint.getBlendMode(), color, srcRect,
+ dstRect, aa, aaFlags, constraint, ctm, std::move(textureXform));
}
- GrTextureAdjuster adjuster(this->context(), std::move(proxy), alphaType, pinnedUniqueID,
- colorSpace);
- this->drawTextureProducer(&adjuster, srcRect, dstRect, constraint, viewMatrix, paint, false);
}
-void SkGpuDevice::drawTextureProducer(GrTextureProducer* producer,
- const SkRect* srcRect,
- const SkRect* dstRect,
- SkCanvas::SrcRectConstraint constraint,
- const SkMatrix& viewMatrix,
- const SkPaint& paint,
- bool attemptDrawTexture) {
+// Assumes srcRect and dstRect have already been optimized to fit the proxy.
+static void draw_texture_producer(GrContext* context, GrRenderTargetContext* rtc,
+ const GrClip& clip, const SkMatrix& ctm,
+ const SkPaint& paint, GrTextureProducer* producer,
+ const SkRect& src, const SkRect& dst, const SkPoint dstClip[4],
+ const SkMatrix& srcToDst, GrAA aa, GrQuadAAFlags aaFlags,
+ SkCanvas::SrcRectConstraint constraint, bool attemptDrawTexture) {
if (attemptDrawTexture && can_use_draw_texture(paint)) {
- GrAA aa = GrAA(paint.isAntiAlias());
// We've done enough checks above to allow us to pass ClampNearest() and not check for
// scaling adjustments.
auto proxy = producer->refTextureProxyForParams(GrSamplerState::ClampNearest(), nullptr);
if (!proxy) {
return;
}
- draw_texture(paint, viewMatrix, srcRect, dstRect, aa, constraint, std::move(proxy),
- producer->alphaType(), producer->colorSpace(), this->clip(),
- fRenderTargetContext.get());
+
+ draw_texture(rtc, clip, ctm, paint, src, dst, dstClip, aa, aaFlags, constraint,
+ std::move(proxy), producer->alphaType(), producer->colorSpace());
return;
}
- // This is the funnel for all non-tiled bitmap/image draw calls. Log a histogram entry.
- SK_HISTOGRAM_BOOLEAN("DrawTiled", false);
-
- // Figure out the actual dst and src rect by clipping the src rect to the bounds of the
- // adjuster. If the src rect is clipped then the dst rect must be recomputed. Also determine
- // the matrix that maps the src rect to the dst rect.
- SkRect clippedSrcRect;
- SkRect clippedDstRect;
- const SkRect srcBounds = SkRect::MakeIWH(producer->width(), producer->height());
- SkMatrix srcToDstMatrix;
- if (srcRect) {
- if (!dstRect) {
- dstRect = &srcBounds;
- }
- if (!srcBounds.contains(*srcRect)) {
- clippedSrcRect = *srcRect;
- if (!clippedSrcRect.intersect(srcBounds)) {
- return;
- }
- if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) {
- return;
- }
- srcToDstMatrix.mapRect(&clippedDstRect, clippedSrcRect);
- } else {
- clippedSrcRect = *srcRect;
- clippedDstRect = *dstRect;
- if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) {
- return;
- }
- }
- } else {
- clippedSrcRect = srcBounds;
- if (dstRect) {
- clippedDstRect = *dstRect;
- if (!srcToDstMatrix.setRectToRect(srcBounds, *dstRect, SkMatrix::kFill_ScaleToFit)) {
- return;
- }
- } else {
- clippedDstRect = srcBounds;
- srcToDstMatrix.reset();
- }
- }
-
- // Now that we have both the view and srcToDst matrices, log our scale factor.
- LogDrawScaleFactor(viewMatrix, srcToDstMatrix, paint.getFilterQuality());
-
- this->drawTextureProducerImpl(producer, clippedSrcRect, clippedDstRect, constraint, viewMatrix,
- srcToDstMatrix, paint);
-}
-
-void SkGpuDevice::drawTextureProducerImpl(GrTextureProducer* producer,
- const SkRect& clippedSrcRect,
- const SkRect& clippedDstRect,
- SkCanvas::SrcRectConstraint constraint,
- const SkMatrix& viewMatrix,
- const SkMatrix& srcToDstMatrix,
- const SkPaint& paint) {
const SkMaskFilter* mf = paint.getMaskFilter();
// The shader expects proper local coords, so we can't replace local coords with texture coords
@@ -257,17 +258,16 @@
bool canUseTextureCoordsAsLocalCoords = !use_shader(producer->isAlphaOnly(), paint) && !mf;
// Specifying the texture coords as local coordinates is an attempt to enable more GrDrawOp
- // combining by not baking anything about the srcRect, dstRect, or viewMatrix, into the texture
+ // combining by not baking anything about the srcRect, dstRect, or ctm, into the texture
// FP. In the future this should be an opaque optimization enabled by the combination of
// GrDrawOp/GP and FP.
if (mf && as_MFB(mf)->hasFragmentProcessor()) {
mf = nullptr;
}
-
bool doBicubic;
GrSamplerState::Filter fm = GrSkFilterQualityToGrFilterMode(
- paint.getFilterQuality(), viewMatrix, srcToDstMatrix,
- fContext->priv().options().fSharpenMipmappedTextures, &doBicubic);
+ paint.getFilterQuality(), ctm, srcToDst,
+ context->priv().options().fSharpenMipmappedTextures, &doBicubic);
const GrSamplerState::Filter* filterMode = doBicubic ? nullptr : &fm;
GrTextureProducer::FilterConstraint constraintMode;
@@ -280,58 +280,302 @@
// If we have to outset for AA then we will generate texture coords outside the src rect. The
// same happens for any mask filter that extends the bounds rendered in the dst.
// This is conservative as a mask filter does not have to expand the bounds rendered.
- bool coordsAllInsideSrcRect = !paint.isAntiAlias() && !mf;
+ bool coordsAllInsideSrcRect = aaFlags == GrQuadAAFlags::kNone && !mf;
// Check for optimization to drop the src rect constraint when on bilerp.
if (filterMode && GrSamplerState::Filter::kBilerp == *filterMode &&
GrTextureAdjuster::kYes_FilterConstraint == constraintMode && coordsAllInsideSrcRect) {
SkMatrix combinedMatrix;
- combinedMatrix.setConcat(viewMatrix, srcToDstMatrix);
- if (can_ignore_bilerp_constraint(*producer, clippedSrcRect, combinedMatrix,
- fRenderTargetContext->fsaaType())) {
+ combinedMatrix.setConcat(ctm, srcToDst);
+ if (can_ignore_bilerp_constraint(*producer, src, combinedMatrix, rtc->fsaaType())) {
constraintMode = GrTextureAdjuster::kNo_FilterConstraint;
}
}
- const SkMatrix* textureMatrix;
- SkMatrix tempMatrix;
+ SkMatrix textureMatrix;
if (canUseTextureCoordsAsLocalCoords) {
- textureMatrix = &SkMatrix::I();
+ textureMatrix = SkMatrix::I();
} else {
- if (!srcToDstMatrix.invert(&tempMatrix)) {
+ if (!srcToDst.invert(&textureMatrix)) {
return;
}
- textureMatrix = &tempMatrix;
}
- auto fp = producer->createFragmentProcessor(*textureMatrix, clippedSrcRect, constraintMode,
+ auto fp = producer->createFragmentProcessor(textureMatrix, src, constraintMode,
coordsAllInsideSrcRect, filterMode);
fp = GrColorSpaceXformEffect::Make(std::move(fp), producer->colorSpace(), producer->alphaType(),
- fRenderTargetContext->colorSpaceInfo().colorSpace());
+ rtc->colorSpaceInfo().colorSpace());
if (!fp) {
return;
}
GrPaint grPaint;
- if (!SkPaintToGrPaintWithTexture(fContext.get(), fRenderTargetContext->colorSpaceInfo(), paint,
- viewMatrix, std::move(fp), producer->isAlphaOnly(),
- &grPaint)) {
- return;
- }
- GrAA aa = GrAA(paint.isAntiAlias());
- if (canUseTextureCoordsAsLocalCoords) {
- fRenderTargetContext->fillRectToRect(this->clip(), std::move(grPaint), aa, viewMatrix,
- clippedDstRect, clippedSrcRect);
+ if (!SkPaintToGrPaintWithTexture(context, rtc->colorSpaceInfo(), paint, ctm,
+ std::move(fp), producer->isAlphaOnly(), &grPaint)) {
return;
}
if (!mf) {
- fRenderTargetContext->drawRect(this->clip(), std::move(grPaint), aa, viewMatrix,
- clippedDstRect);
+ // Can draw the image directly (any mask filter on the paint was converted to an FP already)
+ if (dstClip) {
+ SkPoint srcClipPoints[4];
+ SkPoint* srcClip = nullptr;
+ if (canUseTextureCoordsAsLocalCoords) {
+ // Calculate texture coordinates that match the dst clip
+ GrMapRectPoints(dst, src, dstClip, srcClipPoints, 4);
+ srcClip = srcClipPoints;
+ }
+ rtc->fillQuadWithEdgeAA(clip, std::move(grPaint), aa, aaFlags, ctm, dstClip, srcClip);
+ } else {
+ // Provide explicit texture coords when possible, otherwise rely on texture matrix
+ rtc->fillRectWithEdgeAA(clip, std::move(grPaint), aa, aaFlags, ctm, dst,
+ canUseTextureCoordsAsLocalCoords ? &src : nullptr);
+ }
+ } else {
+ // Must draw the mask filter as a GrShape. For now, this loses the per-edge AA information
+ // since it always draws with AA, but that is should not be noticeable since the mask filter
+ // is probably a blur.
+ GrShape shape;
+ if (dstClip) {
+ // Represent it as an SkPath formed from the dstClip
+ SkPath path;
+ path.addPoly(dstClip, 4, true);
+ shape = GrShape(path);
+ } else {
+ shape = GrShape(dst);
+ }
+
+ GrBlurUtils::drawShapeWithMaskFilter(
+ context, rtc, clip, shape, std::move(grPaint), ctm, mf);
+ }
+}
+
+} // anonymous namespace
+
+//////////////////////////////////////////////////////////////////////////////
+
+void SkGpuDevice::drawImageQuad(const SkImage* image, const SkRect* srcRect, const SkRect* dstRect,
+ const SkPoint dstClip[4], GrAA aa, GrQuadAAFlags aaFlags,
+ const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) {
+ SkRect src;
+ SkRect dst;
+ SkMatrix srcToDst;
+ ImageDrawMode mode = optimize_sample_area(SkISize::Make(image->width(), image->height()),
+ srcRect, dstRect, dstClip, &src, &dst, &srcToDst);
+ if (mode == ImageDrawMode::kSkip) {
return;
}
- GrShape shape(clippedDstRect, GrStyle::SimpleFill());
+ if (src.contains(image->bounds())) {
+ constraint = SkCanvas::kFast_SrcRectConstraint;
+ }
+ // Depending on the nature of image, it can flow through more or less optimal pipelines
+ bool useDecal = mode == ImageDrawMode::kDecal;
+ bool attemptDrawTexture = !useDecal; // rtc->drawTexture() only clamps
- GrBlurUtils::drawShapeWithMaskFilter(this->context(), fRenderTargetContext.get(), this->clip(),
- shape, std::move(grPaint), viewMatrix, mf);
+ // 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.
+ if (as_IB(image)->isYUVA()) {
+ SK_HISTOGRAM_BOOLEAN("DrawTiled", false);
+ LogDrawScaleFactor(this->ctm(), srcToDst, paint.getFilterQuality());
+
+ GrYUVAImageTextureMaker maker(fContext.get(), image, useDecal);
+ draw_texture_producer(fContext.get(), fRenderTargetContext.get(), this->clip(), this->ctm(),
+ paint, &maker, src, dst, dstClip, srcToDst, aa, aaFlags, constraint,
+ /* attempt draw texture */ false);
+ return;
+ }
+
+ // Pinned texture proxies can be rendered directly as textures, or with relatively simple
+ // adjustments applied to the image content (scaling, mipmaps, color space, etc.)
+ uint32_t pinnedUniqueID;
+ if (sk_sp<GrTextureProxy> proxy = as_IB(image)->refPinnedTextureProxy(&pinnedUniqueID)) {
+ SK_HISTOGRAM_BOOLEAN("DrawTiled", false);
+ LogDrawScaleFactor(this->ctm(), srcToDst, paint.getFilterQuality());
+
+ SkAlphaType alphaType = image->alphaType();
+ SkColorSpace* colorSpace = as_IB(image)->colorSpace();
+
+ if (attemptDrawTexture && can_use_draw_texture(paint)) {
+ draw_texture(fRenderTargetContext.get(), this->clip(), this->ctm(), paint, src, dst,
+ dstClip, aa, aaFlags, constraint, std::move(proxy), alphaType, colorSpace);
+ return;
+ }
+
+ GrTextureAdjuster adjuster(fContext.get(), std::move(proxy), alphaType, pinnedUniqueID,
+ colorSpace, useDecal);
+ draw_texture_producer(fContext.get(), fRenderTargetContext.get(), this->clip(), this->ctm(),
+ paint, &adjuster, src, dst, dstClip, srcToDst, aa, aaFlags,
+ constraint, /* attempt draw_texture */ false);
+ 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(), this->ctm(),
+ srcToDst)) {
+ // only support tiling as bitmap at the moment, so force raster-version
+ if (!as_IB(image)->getROPixels(&bm)) {
+ return;
+ }
+ this->drawBitmapRect(bm, &src, dst, paint, constraint);
+ return;
+ }
+
+ // This is the funnel for all non-tiled bitmap/image draw calls. Log a histogram entry.
+ SK_HISTOGRAM_BOOLEAN("DrawTiled", false);
+ LogDrawScaleFactor(this->ctm(), srcToDst, paint.getFilterQuality());
+
+ // Lazily generated images must get drawn as a texture producer that handles the final
+ // texture creation.
+ if (image->isLazyGenerated()) {
+ GrImageTextureMaker maker(fContext.get(), image, SkImage::kAllow_CachingHint, useDecal);
+ draw_texture_producer(fContext.get(), fRenderTargetContext.get(), this->clip(), this->ctm(),
+ paint, &maker, src, dst, dstClip, srcToDst, aa, aaFlags, constraint,
+ attemptDrawTexture);
+ return;
+ }
+ if (as_IB(image)->getROPixels(&bm)) {
+ GrBitmapTextureMaker maker(fContext.get(), bm, useDecal);
+ draw_texture_producer(fContext.get(), fRenderTargetContext.get(), this->clip(), this->ctm(),
+ paint, &maker, src, dst, dstClip, srcToDst, aa, aaFlags, constraint,
+ attemptDrawTexture);
+ }
+
+ // Otherwise don't know how to draw it
+}
+
+// For ease-of-use, the temporary API treats null dstClipCounts as if it were the proper sized
+// array, filled with all 0s (so dstClips can be null too)
+void SkGpuDevice::tmp_drawImageSetV2(const SkCanvas::ImageSetEntry set[], int dstClipCounts[],
+ int count, const SkPoint dstClips[], const SkPaint& paint,
+ SkCanvas::SrcRectConstraint constraint) {
+ SkASSERT(count > 0);
+
+ if (!can_use_draw_texture(paint)) {
+ // Send every entry through drawImageQuad() to handle the more complicated paint
+ int dstClipIndex = 0;
+ for (int i = 0; i < count; ++i) {
+ // Only no clip or quad clip are supported
+ SkASSERT(!dstClipCounts || dstClipCounts[i] == 0 || dstClipCounts[i] == 4);
+ // Always send GrAA::kYes to preserve seaming across tiling in MSAA
+ this->drawImageQuad(set[i].fImage.get(), &set[i].fSrcRect, &set[i].fDstRect,
+ (dstClipCounts && dstClipCounts[i] > 0) ? dstClips + dstClipIndex : nullptr,
+ GrAA::kYes, SkToGrQuadAAFlags(set[i].fAAFlags), paint, constraint);
+ if (dstClipCounts) {
+ dstClipIndex += dstClipCounts[i];
+ }
+ }
+ return;
+ }
+
+ GrSamplerState::Filter filter = kNone_SkFilterQuality == paint.getFilterQuality() ?
+ GrSamplerState::Filter::kNearest : GrSamplerState::Filter::kBilerp;
+ SkBlendMode mode = paint.getBlendMode();
+
+ SkAutoTArray<GrRenderTargetContext::TextureSetEntry> textures(count);
+ // We accumulate compatible proxies until we find an an incompatible one or reach the end and
+ // issue the accumulated 'n' draws starting at 'base'.
+ int base = 0, n = 0;
+ auto draw = [&] {
+ if (n > 0) {
+ auto textureXform = GrColorSpaceXform::Make(
+ set[base].fImage->colorSpace(), set[base].fImage->alphaType(),
+ fRenderTargetContext->colorSpaceInfo().colorSpace(), kPremul_SkAlphaType);
+ fRenderTargetContext->drawTextureSet(this->clip(), textures.get() + base, n,
+ filter, mode, GrAA::kYes, this->ctm(),
+ std::move(textureXform));
+ }
+ };
+ int dstClipIndex = 0;
+ for (int i = 0; i < count; ++i) {
+ // Manage the dst clip pointer tracking before any continues are used so we don't lose
+ // our place in the dstClips array.
+ int clipCount = (dstClipCounts ? dstClipCounts[i] : 0);
+ SkASSERT(clipCount == 0 || (dstClipCounts[i] == 4 && dstClips));
+ const SkPoint* clip = clipCount > 0 ? dstClips + dstClipIndex : nullptr;
+ if (dstClipCounts) {
+ dstClipIndex += dstClipCounts[i];
+ }
+ // The default SkBaseDevice implementation is based on drawImageRect which does not allow
+ // non-sorted src rects. TODO: Decide this is OK or make sure we handle it.
+ if (!set[i].fSrcRect.isSorted()) {
+ draw();
+ base = i + 1;
+ n = 0;
+ continue;
+ }
+
+ uint32_t uniqueID;
+ textures[i].fProxy = as_IB(set[i].fImage.get())->refPinnedTextureProxy(&uniqueID);
+ if (!textures[i].fProxy) {
+ // FIXME(michaelludwig) - If asTextureProxyRef fails, does going through drawImageQuad
+ // make sense? Does that catch the lazy-image cases then?
+ // FIXME(michaelludwig) - Both refPinnedTextureProxy and asTextureProxyRef for YUVA
+ // images force flatten the planes. It would be nice to detect a YUVA image entry and
+ // send it to drawImageQuad (which uses a special effect for YUV)
+ textures[i].fProxy =
+ as_IB(set[i].fImage.get())
+ ->asTextureProxyRef(fContext.get(), GrSamplerState::ClampBilerp(),
+ nullptr);
+ // If we failed to make a proxy then flush the accumulated set and reset for the next
+ // image.
+ if (!textures[i].fProxy) {
+ draw();
+ base = i + 1;
+ n = 0;
+ continue;
+ }
+ }
+ textures[i].fSrcRect = set[i].fSrcRect;
+ textures[i].fDstRect = set[i].fDstRect;
+ textures[i].fDstClipQuad = clip;
+ textures[i].fAlpha = set[i].fAlpha * paint.getAlphaf();
+ textures[i].fAAFlags = SkToGrQuadAAFlags(set[i].fAAFlags);
+
+ if (n > 0 &&
+ (!GrTextureProxy::ProxiesAreCompatibleAsDynamicState(textures[i].fProxy.get(),
+ textures[base].fProxy.get()) ||
+ set[i].fImage->alphaType() != set[base].fImage->alphaType() ||
+ !SkColorSpace::Equals(set[i].fImage->colorSpace(), set[base].fImage->colorSpace()))) {
+ draw();
+ base = i;
+ n = 1;
+ } else {
+ ++n;
+ }
+ }
+ draw();
+}
+
+// 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,
+ bool attemptDrawTexture) {
+ // 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(SkISize::Make(producer->width(), producer->height()),
+ 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, attemptDrawTexture);
}
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index eb9ccee..5fe5d41 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -322,11 +322,11 @@
}
// Use dstRect unless dstClip is provided, which is assumed to be a quad
- auto quad = set[p].fDstClip == nullptr ?
+ auto quad = set[p].fDstClipQuad == nullptr ?
GrPerspQuad::MakeFromRect(set[p].fDstRect, viewMatrix) :
- GrPerspQuad::MakeFromSkQuad(set[p].fDstClip, viewMatrix);
+ GrPerspQuad::MakeFromSkQuad(set[p].fDstClipQuad, viewMatrix);
GrQuadType quadType = baseQuadType;
- if (set[p].fDstClip && baseQuadType != GrQuadType::kPerspective) {
+ if (set[p].fDstClipQuad && baseQuadType != GrQuadType::kPerspective) {
quadType = GrQuadType::kStandard;
}
@@ -348,11 +348,11 @@
float alpha = SkTPin(set[p].fAlpha, 0.f, 1.f);
SkPMColor4f color{alpha, alpha, alpha, alpha};
int srcQuadIndex = -1;
- if (set[p].fDstClip) {
+ if (set[p].fDstClipQuad) {
// Derive new source coordinates that match dstClip's relative locations in dstRect,
// but with respect to srcRect
SkPoint srcQuad[4];
- GrMapRectPoints(set[p].fDstRect, set[p].fSrcRect, set[p].fDstClip, srcQuad, 4);
+ GrMapRectPoints(set[p].fDstRect, set[p].fSrcRect, set[p].fDstClipQuad, srcQuad, 4);
fSrcQuads.push_back(GrPerspQuad::MakeFromSkQuad(srcQuad, SkMatrix::I()),
GrQuadType::kStandard);
srcQuadIndex = fSrcQuads.count() - 1;