Support per-entry transforms in image-set API
Bug: skia:
Change-Id: I508ec8cb1df1c407853b401c73c66a575fb9c661
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/196642
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 3c426c4..a148cef 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -1068,14 +1068,20 @@
if (mode != SkBlendMode::kSrcOver ||
!fContext->priv().caps()->dynamicStateArrayGeometryProcessorTextureSupport()) {
// Draw one at a time with GrFillRectOp and a GrPaint that emulates what GrTextureOp does
+ SkMatrix ctm;
for (int i = 0; i < cnt; ++i) {
float alpha = set[i].fAlpha;
+ ctm = viewMatrix;
+ if (set[i].fPreViewMatrix) {
+ ctm.preConcat(*set[i].fPreViewMatrix);
+ }
+
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},
set[i].fSrcRect, set[i].fDstRect, aa, set[i].fAAFlags,
- SkCanvas::kFast_SrcRectConstraint, viewMatrix, texXform);
+ SkCanvas::kFast_SrcRectConstraint, ctm, texXform);
} else {
// Generate interpolated texture coordinates to match the dst clip
SkPoint srcQuad[4];
@@ -1085,7 +1091,7 @@
// keep seams look more correct.
this->drawTextureQuad(clip, set[i].fProxy, filter, mode,
{alpha, alpha, alpha, alpha}, srcQuad, set[i].fDstClipQuad,
- aa, set[i].fAAFlags, nullptr, viewMatrix, texXform);
+ aa, set[i].fAAFlags, nullptr, ctm, texXform);
}
}
} else {
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index 1d2f7a5..4691e94 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -197,6 +197,7 @@
SkRect fSrcRect;
SkRect fDstRect;
const SkPoint* fDstClipQuad; // Must be null, or point to an array of 4 points
+ const SkMatrix* fPreViewMatrix; // If not null, entry's CTM is 'viewMatrix' * fPreViewMatrix
float fAlpha;
GrQuadAAFlags fAAFlags;
};
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 1dc594b..eb8b84a 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1298,8 +1298,8 @@
const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) {
ASSERT_SINGLE_OWNER
GrQuadAAFlags aaFlags = paint.isAntiAlias() ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone;
- this->drawImageQuad(
- image, src, &dst, nullptr, GrAA(paint.isAntiAlias()), aaFlags, paint, constraint);
+ this->drawImageQuad(image, src, &dst, nullptr, GrAA(paint.isAntiAlias()), aaFlags, nullptr,
+ paint, constraint);
}
// When drawing nine-patches or n-patches, cap the filter quality at kBilerp.
@@ -1410,7 +1410,7 @@
paint.setBlendMode(mode);
paint.setFilterQuality(filterQuality);
paint.setAntiAlias(true);
- this->tmp_drawImageSetV2(set, nullptr, count, nullptr, paint,
+ this->tmp_drawImageSetV3(set, nullptr, nullptr, count, nullptr, nullptr, paint,
SkCanvas::kFast_SrcRectConstraint);
}
diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h
index 9d60096..b41b8c8 100644
--- a/src/gpu/SkGpuDevice.h
+++ b/src/gpu/SkGpuDevice.h
@@ -130,10 +130,12 @@
/*
* dstClipCounts[] is a parallel array to the image entries, acting like the intended
- * dstClipCount field in ImageSetEntry.
+ * dstClipCount field in ImageSetEntry. Similarly, preViewMatrixIdx is parallel and will
+ * become an index field in ImageSetEntry that specifies an entry in the matrix array.
*/
- void tmp_drawImageSetV2(const SkCanvas::ImageSetEntry[], int dstClipCounts[], int count,
- const SkPoint dstClips[], const SkPaint& paint,
+ void tmp_drawImageSetV3(const SkCanvas::ImageSetEntry[],
+ int dstClipCounts[], int preViewMatrixIdx[], int count,
+ const SkPoint dstClips[], const SkMatrix preViewMatrices[], 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);
@@ -219,10 +221,11 @@
bool bicubic,
bool needsTextureDomain);
- // If not null, dstClip must be contained inside dst and will also respect the edge AA flags
+ // 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 SkPaint&, SkCanvas::SrcRectConstraint);
+ 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
diff --git a/src/gpu/SkGpuDevice_drawTexture.cpp b/src/gpu/SkGpuDevice_drawTexture.cpp
index 5b50506..8c280d5 100644
--- a/src/gpu/SkGpuDevice_drawTexture.cpp
+++ b/src/gpu/SkGpuDevice_drawTexture.cpp
@@ -355,7 +355,8 @@
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) {
+ const SkMatrix* preViewMatrix, const SkPaint& paint,
+ SkCanvas::SrcRectConstraint constraint) {
SkRect src;
SkRect dst;
SkMatrix srcToDst;
@@ -372,15 +373,21 @@
bool useDecal = mode == ImageDrawMode::kDecal;
bool attemptDrawTexture = !useDecal; // rtc->drawTexture() only clamps
+ // Get final CTM matrix
+ SkMatrix ctm = this->ctm();
+ if (preViewMatrix) {
+ ctm.preConcat(*preViewMatrix);
+ }
+
// 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());
+ LogDrawScaleFactor(ctm, srcToDst, paint.getFilterQuality());
GrYUVAImageTextureMaker maker(fContext.get(), image, useDecal);
- draw_texture_producer(fContext.get(), fRenderTargetContext.get(), this->clip(), this->ctm(),
+ draw_texture_producer(fContext.get(), fRenderTargetContext.get(), this->clip(), ctm,
paint, &maker, src, dst, dstClip, srcToDst, aa, aaFlags, constraint,
/* attempt draw texture */ false);
return;
@@ -392,20 +399,20 @@
if (sk_sp<GrTextureProxy> proxy = as_IB(image)->refPinnedTextureProxy(this->context(),
&pinnedUniqueID)) {
SK_HISTOGRAM_BOOLEAN("DrawTiled", false);
- LogDrawScaleFactor(this->ctm(), srcToDst, paint.getFilterQuality());
+ LogDrawScaleFactor(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,
+ draw_texture(fRenderTargetContext.get(), this->clip(), 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(),
+ draw_texture_producer(fContext.get(), fRenderTargetContext.get(), this->clip(), ctm,
paint, &adjuster, src, dst, dstClip, srcToDst, aa, aaFlags,
constraint, /* attempt draw_texture */ false);
return;
@@ -415,8 +422,7 @@
// 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)) {
+ 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)) {
return;
@@ -427,20 +433,20 @@
// 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());
+ LogDrawScaleFactor(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(),
+ draw_texture_producer(fContext.get(), fRenderTargetContext.get(), this->clip(), 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(),
+ draw_texture_producer(fContext.get(), fRenderTargetContext.get(), this->clip(), ctm,
paint, &maker, src, dst, dstClip, srcToDst, aa, aaFlags, constraint,
attemptDrawTexture);
}
@@ -450,8 +456,9 @@
// 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,
+void SkGpuDevice::tmp_drawImageSetV3(const SkCanvas::ImageSetEntry set[], int dstClipCounts[],
+ int preViewMatrixIdx[], int count, const SkPoint dstClips[],
+ const SkMatrix preViewMatrices[], const SkPaint& paint,
SkCanvas::SrcRectConstraint constraint) {
SkASSERT(count > 0);
@@ -461,10 +468,15 @@
for (int i = 0; i < count; ++i) {
// Only no clip or quad clip are supported
SkASSERT(!dstClipCounts || dstClipCounts[i] == 0 || dstClipCounts[i] == 4);
+
+ int xform = preViewMatrixIdx ? preViewMatrixIdx[i] : -1;
+ SkASSERT(xform < 0 || preViewMatrices);
+
// 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);
+ GrAA::kYes, SkToGrQuadAAFlags(set[i].fAAFlags),
+ xform < 0 ? nullptr : preViewMatrices + xform, paint, constraint);
if (dstClipCounts) {
dstClipIndex += dstClipCounts[i];
}
@@ -531,9 +543,14 @@
continue;
}
}
+
+ int xform = preViewMatrixIdx ? preViewMatrixIdx[i] : -1;
+ SkASSERT(xform < 0 || preViewMatrices);
+
textures[i].fSrcRect = set[i].fSrcRect;
textures[i].fDstRect = set[i].fDstRect;
textures[i].fDstClipQuad = clip;
+ textures[i].fPreViewMatrix = xform < 0 ? nullptr : preViewMatrices + xform;
textures[i].fAlpha = set[i].fAlpha * paint.getAlphaf();
textures[i].fAAFlags = SkToGrQuadAAFlags(set[i].fAAFlags);
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index 210dfee..9db287a 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -307,10 +307,10 @@
GrAAType overallAAType = GrAAType::kNone; // aa type maximally compatible with all dst rects
bool mustFilter = false;
fCanSkipAllocatorGather = static_cast<unsigned>(true);
- // All dst rects are transformed by the same view matrix, so their quad types are identical,
- // unless an entry provides a dstClip that forces quad type to be at least standard.
- GrQuadType baseQuadType = GrQuadTypeForTransformedRect(viewMatrix);
- fQuads.reserve(cnt, baseQuadType);
+ // Most dst rects are transformed by the same view matrix, so their quad types start
+ // identical, unless an entry provides a dstClip or additional transform that changes it.
+ // The quad list will automatically adapt to that.
+ fQuads.reserve(cnt, GrQuadTypeForTransformedRect(viewMatrix));
for (unsigned p = 0; p < fProxyCnt; ++p) {
fProxies[p].fProxy = SkRef(set[p].fProxy.get());
@@ -321,12 +321,17 @@
fCanSkipAllocatorGather = static_cast<unsigned>(false);
}
+ SkMatrix ctm = viewMatrix;
+ if (set[p].fPreViewMatrix) {
+ ctm.preConcat(*set[p].fPreViewMatrix);
+ }
+
// Use dstRect unless dstClip is provided, which is assumed to be a quad
auto quad = set[p].fDstClipQuad == nullptr ?
- GrPerspQuad::MakeFromRect(set[p].fDstRect, viewMatrix) :
- GrPerspQuad::MakeFromSkQuad(set[p].fDstClipQuad, viewMatrix);
- GrQuadType quadType = baseQuadType;
- if (set[p].fDstClipQuad && baseQuadType != GrQuadType::kPerspective) {
+ GrPerspQuad::MakeFromRect(set[p].fDstRect, ctm) :
+ GrPerspQuad::MakeFromSkQuad(set[p].fDstClipQuad, ctm);
+ GrQuadType quadType = GrQuadTypeForTransformedRect(ctm);
+ if (set[p].fDstClipQuad && quadType != GrQuadType::kPerspective) {
quadType = GrQuadType::kStandard;
}
@@ -342,7 +347,7 @@
}
if (!mustFilter && this->filter() != GrSamplerState::Filter::kNearest) {
mustFilter = quadType != GrQuadType::kRect ||
- GrTextureOp::GetFilterHasEffect(viewMatrix, set[p].fSrcRect,
+ GrTextureOp::GetFilterHasEffect(ctm, set[p].fSrcRect,
set[p].fDstRect);
}
float alpha = SkTPin(set[p].fAlpha, 0.f, 1.f);