Support domain clamping and transform matrices in YUVToRGB effects
This helps avoid flattening of YUV/A images when drawn with a strict src
rect constraint. SkiaRenderer almost always provides a strict constraint
for their YUV videos.
This adds a GM that replicates the issue in skbug:8959, and adds a GM to
the wacky_yuv set that checks domain clamping across all of the different
formats.
Bug: 8959
Change-Id: I53f531a94f3b63f81d8c3cbe22d868e3356aeabd
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/207020
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/gm/wacky_yuv_formats.cpp b/gm/wacky_yuv_formats.cpp
index 4870f01..332abcc 100644
--- a/gm/wacky_yuv_formats.cpp
+++ b/gm/wacky_yuv_formats.cpp
@@ -24,6 +24,7 @@
static const int kTileWidthHeight = 128;
static const int kLabelWidth = 64;
static const int kLabelHeight = 32;
+static const int kDomainPadding = 8;
static const int kPad = 1;
enum YUVFormat {
@@ -153,12 +154,14 @@
}
static SkBitmap make_bitmap(SkColorType colorType, const SkPath& path,
- const SkTDArray<SkRect>& circles, bool opaque) {
+ const SkTDArray<SkRect>& circles, bool opaque, bool padWithRed) {
const SkColor kGreen = ToolUtils::color_to_565(SkColorSetARGB(0xFF, 178, 240, 104));
const SkColor kBlue = ToolUtils::color_to_565(SkColorSetARGB(0xFF, 173, 167, 252));
const SkColor kYellow = ToolUtils::color_to_565(SkColorSetARGB(0xFF, 255, 221, 117));
- SkImageInfo ii = SkImageInfo::Make(kTileWidthHeight, kTileWidthHeight,
+ int widthHeight = kTileWidthHeight + (padWithRed ? 2 * kDomainPadding : 0);
+
+ SkImageInfo ii = SkImageInfo::Make(widthHeight, widthHeight,
colorType, kPremul_SkAlphaType);
SkBitmap bm;
@@ -167,6 +170,11 @@
std::unique_ptr<SkCanvas> canvas = SkCanvas::MakeRasterDirect(ii,
bm.getPixels(),
bm.rowBytes());
+ if (padWithRed) {
+ canvas->clear(SK_ColorRED);
+ canvas->translate(kDomainPadding, kDomainPadding);
+ canvas->clipRect(SkRect::MakeWH(kTileWidthHeight, kTileWidthHeight));
+ }
canvas->clear(opaque ? kGreen : SK_ColorTRANSPARENT);
SkPaint paint;
@@ -800,7 +808,9 @@
// YV12
class WackyYUVFormatsGM : public GM {
public:
- WackyYUVFormatsGM(bool useTargetColorSpace) : fUseTargetColorSpace(useTargetColorSpace) {
+ WackyYUVFormatsGM(bool useTargetColorSpace, bool useDomain)
+ : fUseTargetColorSpace(useTargetColorSpace)
+ , fUseDomain(useDomain) {
this->setBGColor(0xFFCCCCCC);
}
@@ -811,6 +821,9 @@
if (fUseTargetColorSpace) {
name += "_cs";
}
+ if (fUseDomain) {
+ name += "_domain";
+ }
return name;
}
@@ -818,8 +831,9 @@
SkISize onISize() override {
int numCols = 2 * (kLastEnum_SkYUVColorSpace + 1); // opacity x color-space
int numRows = 1 + (kLast_YUVFormat + 1); // origin + # yuv formats
- return SkISize::Make(kLabelWidth + numCols * (kTileWidthHeight + kPad),
- kLabelHeight + numRows * (kTileWidthHeight + kPad));
+ int wh = SkScalarCeilToInt(kTileWidthHeight * (fUseDomain ? 1.5f : 1.f));
+ return SkISize::Make(kLabelWidth + numCols * (wh + kPad),
+ kLabelHeight + numRows * (wh + kPad));
}
void onOnceBeforeDraw() override {
@@ -831,14 +845,14 @@
// transparent
SkTDArray<SkRect> circles;
SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 5, &circles);
- fOriginalBMs[0] = make_bitmap(kRGBA_8888_SkColorType, path, circles, false);
+ fOriginalBMs[0] = make_bitmap(kRGBA_8888_SkColorType, path, circles, false, fUseDomain);
}
{
// opaque
SkTDArray<SkRect> circles;
SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 7, &circles);
- fOriginalBMs[1] = make_bitmap(kRGBA_8888_SkColorType, path, circles, true);
+ fOriginalBMs[1] = make_bitmap(kRGBA_8888_SkColorType, path, circles, true, fUseDomain);
}
if (fUseTargetColorSpace) {
@@ -887,6 +901,14 @@
}
int counterMod = counter % 3;
+ if (format == kY410_YUVFormat && counterMod == 2) {
+ // This format doesn't work as pixmaps
+ counterMod = 1;
+ } else if (fUseDomain && counterMod == 0) {
+ // Copies flatten to RGB when they copy the YUVA data, which doesn't
+ // know about the intended domain and the domain padding bleeds in
+ counterMod = 1;
+ }
switch (counterMod) {
case 0:
fImages[opaque][cs][format] = SkImage::MakeFromYUVATexturesCopy(
@@ -933,38 +955,52 @@
void onDraw(SkCanvas* canvas) override {
this->createImages(canvas->getGrContext());
- int x = kLabelWidth;
+ SkRect srcRect = SkRect::MakeWH(fOriginalBMs[0].width(), fOriginalBMs[0].height());
+ SkRect dstRect = SkRect::MakeXYWH(kLabelWidth, 0.f, srcRect.width(), srcRect.height());
+
+ SkCanvas::SrcRectConstraint constraint = SkCanvas::kFast_SrcRectConstraint;
+ if (fUseDomain) {
+ srcRect.inset(kDomainPadding, kDomainPadding);
+ // Draw a larger rectangle to ensure bilerp filtering would normally read outside the
+ // srcRect and hit the red pixels, if strict constraint weren't used.
+ dstRect.fRight = kLabelWidth + 1.5f * srcRect.width();
+ dstRect.fBottom = 1.5f * srcRect.height();
+ constraint = SkCanvas::kStrict_SrcRectConstraint;
+ }
+
for (int cs = kJPEG_SkYUVColorSpace; cs <= kLastEnum_SkYUVColorSpace; ++cs) {
SkPaint paint;
+ paint.setFilterQuality(kLow_SkFilterQuality);
if (kIdentity_SkYUVColorSpace == cs) {
// The identity color space needs post processing to appear correctly
paint.setColorFilter(yuv_to_rgb_colorfilter());
}
for (int opaque : { 0, 1 }) {
- int y = kLabelHeight;
+ dstRect.offsetTo(dstRect.fLeft, kLabelHeight);
- draw_col_label(canvas, x+kTileWidthHeight/2, cs, opaque);
+ draw_col_label(canvas, dstRect.fLeft + dstRect.height() / 2, cs, opaque);
- canvas->drawBitmap(fOriginalBMs[opaque], x, y);
- y += kTileWidthHeight + kPad;
+ canvas->drawBitmapRect(fOriginalBMs[opaque], srcRect, dstRect, nullptr, constraint);
+ dstRect.offset(0.f, dstRect.height() + kPad);
for (int format = kAYUV_YUVFormat; format <= kLast_YUVFormat; ++format) {
- draw_row_label(canvas, y, format);
+ draw_row_label(canvas, dstRect.fTop, format);
if (fUseTargetColorSpace && fImages[opaque][cs][format]) {
// Making a CS-specific version of a kIdentity_SkYUVColorSpace YUV image
// doesn't make a whole lot of sense. The colorSpace conversion will
// operate on the YUV components rather than the RGB components.
sk_sp<SkImage> csImage =
fImages[opaque][cs][format]->makeColorSpace(fTargetColorSpace);
- canvas->drawImage(csImage, x, y, &paint);
+ canvas->drawImageRect(csImage, srcRect, dstRect, &paint, constraint);
} else {
- canvas->drawImage(fImages[opaque][cs][format], x, y, &paint);
+ canvas->drawImageRect(fImages[opaque][cs][format], srcRect, dstRect, &paint,
+ constraint);
}
- y += kTileWidthHeight + kPad;
+ dstRect.offset(0.f, dstRect.height() + kPad);
}
- x += kTileWidthHeight + kPad;
+ dstRect.offset(dstRect.width() + kPad, 0.f);
}
}
if (auto context = canvas->getGrContext()) {
@@ -987,6 +1023,7 @@
sk_sp<SkImage> fImages[2][kLastEnum_SkYUVColorSpace + 1][kLast_YUVFormat + 1];
SkTArray<GrBackendTexture> fBackendTextures;
bool fUseTargetColorSpace;
+ bool fUseDomain;
sk_sp<SkColorSpace> fTargetColorSpace;
typedef GM INHERITED;
@@ -994,8 +1031,9 @@
//////////////////////////////////////////////////////////////////////////////
-DEF_GM(return new WackyYUVFormatsGM(false);)
-DEF_GM(return new WackyYUVFormatsGM(true);)
+DEF_GM(return new WackyYUVFormatsGM(/* cs */ false, /* domain */ false);)
+DEF_GM(return new WackyYUVFormatsGM(/* cs */ true, /* domain */ false);)
+DEF_GM(return new WackyYUVFormatsGM(/* cs */ false, /* domain */ true);)
class YUVMakeColorSpaceGM : public GpuGM {
public:
@@ -1024,14 +1062,14 @@
// transparent
SkTDArray<SkRect> circles;
SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 5, &circles);
- fOriginalBMs[0] = make_bitmap(kN32_SkColorType, path, circles, false);
+ fOriginalBMs[0] = make_bitmap(kN32_SkColorType, path, circles, false, false);
}
{
// opaque
SkTDArray<SkRect> circles;
SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 7, &circles);
- fOriginalBMs[1] = make_bitmap(kN32_SkColorType, path, circles, true);
+ fOriginalBMs[1] = make_bitmap(kN32_SkColorType, path, circles, true, false);
}
fTargetColorSpace = SkColorSpace::MakeSRGB()->makeColorSpin();
diff --git a/gm/yuvtorgbeffect.cpp b/gm/yuvtorgbeffect.cpp
index fe8d127..30d9129 100644
--- a/gm/yuvtorgbeffect.cpp
+++ b/gm/yuvtorgbeffect.cpp
@@ -9,6 +9,7 @@
#include "gm.h"
+#include "GrClip.h"
#include "GrContext.h"
#include "GrContextPriv.h"
#include "GrProxyProvider.h"
@@ -249,4 +250,115 @@
};
DEF_GM(return new YUVNV12toRGBEffect;)
+
+//////////////////////////////////////////////////////////////////////////////
+
+// This GM tests domain clamping on YUV multiplanar images where the U and V
+// planes have different resolution from Y. See skbug:8959
+
+class YUVtoRGBDomainEffect : public GpuGM {
+public:
+ YUVtoRGBDomainEffect() {
+ this->setBGColor(0xFFFFFFFF);
+ }
+
+protected:
+ SkString onShortName() override {
+ return SkString("yuv_to_rgb_domain_effect");
+ }
+
+ SkISize onISize() override {
+ return SkISize::Make((YSIZE + kTestPad) * 3 + kDrawPad, (YSIZE + kTestPad) * 2 + kDrawPad);
+ }
+
+ void onOnceBeforeDraw() override {
+ SkBitmap bmp[3];
+ SkImageInfo yinfo = SkImageInfo::MakeA8(YSIZE, YSIZE);
+ bmp[0].allocPixels(yinfo);
+ SkImageInfo uinfo = SkImageInfo::MakeA8(USIZE, USIZE);
+ bmp[1].allocPixels(uinfo);
+ SkImageInfo vinfo = SkImageInfo::MakeA8(VSIZE, VSIZE);
+ bmp[2].allocPixels(vinfo);
+
+ int innerColor[] = {149, 43, 21};
+ int outerColor[] = {128, 128, 128};
+ for (int i = 0; i < 3; ++i) {
+ bmp[i].eraseColor(SkColorSetARGB(outerColor[i], 0, 0, 0));
+ SkIRect innerRect = i == 0 ? SkIRect::MakeLTRB(2, 2, 6, 6) : SkIRect::MakeLTRB(1, 1, 3, 3);
+ bmp[i].erase(SkColorSetARGB(innerColor[i], 0, 0, 0), innerRect);
+ fImage[i] = SkImage::MakeFromBitmap(bmp[i]);
+ }
+ }
+
+ DrawResult onDraw(GrContext* context, GrRenderTargetContext* renderTargetContext,
+ SkCanvas* canvas, SkString* errorMsg) override {
+ GrProxyProvider* proxyProvider = context->priv().proxyProvider();
+ sk_sp<GrTextureProxy> proxies[3];
+
+ for (int i = 0; i < 3; ++i) {
+ proxies[i] = proxyProvider->createTextureProxy(fImage[i], kNone_GrSurfaceFlags, 1,
+ SkBudgeted::kYes, SkBackingFit::kExact);
+ if (!proxies[i]) {
+ *errorMsg = "Failed to create proxy";
+ return DrawResult::kFail;
+ }
+ }
+
+ // Draw a 2x2 grid of the YUV images.
+ // Rows = kNearest, kBilerp, Cols = No clamp, clamp
+ static const GrSamplerState::Filter kFilters[] = {
+ GrSamplerState::Filter::kNearest, GrSamplerState::Filter::kBilerp };
+ static const SkRect kGreenRect = SkRect::MakeLTRB(2.f, 2.f, 6.f, 6.f);
+
+ SkYUVAIndex yuvaIndices[4] = {
+ { SkYUVAIndex::kY_Index, SkColorChannel::kR },
+ { SkYUVAIndex::kU_Index, SkColorChannel::kR },
+ { SkYUVAIndex::kV_Index, SkColorChannel::kR },
+ { -1, SkColorChannel::kA }
+ };
+ SkRect rect = SkRect::MakeWH(YSIZE, YSIZE);
+
+ SkScalar y = kDrawPad + kTestPad;
+ for (uint32_t i = 0; i < SK_ARRAY_COUNT(kFilters); ++i) {
+ SkScalar x = kDrawPad + kTestPad;
+
+ for (uint32_t j = 0; j < 2; ++j) {
+ SkMatrix ctm = SkMatrix::MakeTrans(x, y);
+ ctm.postScale(10.f, 10.f);
+
+ SkRect domain = kGreenRect;
+ if (kFilters[i] == GrSamplerState::Filter::kNearest) {
+ // Make a very small inset for nearest-neighbor filtering so that 0.5px
+ // centers don't round out beyond the green pixels.
+ domain.inset(0.01f, 0.01f);
+ }
+
+ const SkRect* domainPtr = j > 0 ? &domain : nullptr;
+ std::unique_ptr<GrFragmentProcessor> fp(GrYUVtoRGBEffect::Make(proxies, yuvaIndices,
+ kJPEG_SkYUVColorSpace, kFilters[i], SkMatrix::I(), domainPtr));
+ if (fp) {
+ GrPaint grPaint;
+ grPaint.addColorFragmentProcessor(std::move(fp));
+ renderTargetContext->drawRect(
+ GrNoClip(), std::move(grPaint), GrAA::kYes, ctm, rect);
+ }
+ x += rect.width() + kTestPad;
+ }
+
+ y += rect.height() + kTestPad;
+ }
+
+ return DrawResult::kOk;
+ }
+
+private:
+ sk_sp<SkImage> fImage[3];
+
+ static constexpr SkScalar kDrawPad = 10.f;
+ static constexpr SkScalar kTestPad = 10.f;
+
+ typedef GM INHERITED;
+};
+
+DEF_GM(return new YUVtoRGBDomainEffect;)
}
diff --git a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-ASAN.json b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-ASAN.json
index 37c0a27..9f092dc 100644
--- a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-ASAN.json
+++ b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-ASAN.json
@@ -272,6 +272,10 @@
"serialize-8888",
"gm",
"_",
+ "wacky_yuv_formats_domain",
+ "serialize-8888",
+ "gm",
+ "_",
"bitmapfilters",
"serialize-8888",
"gm",
diff --git a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-BonusConfigs.json b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-BonusConfigs.json
index 9f08685..913aa19 100644
--- a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-BonusConfigs.json
+++ b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-BonusConfigs.json
@@ -348,6 +348,10 @@
"serialize-8888",
"gm",
"_",
+ "wacky_yuv_formats_domain",
+ "serialize-8888",
+ "gm",
+ "_",
"bitmapfilters",
"serialize-8888",
"gm",
diff --git a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-MSAN.json b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-MSAN.json
index 746d91f..2b5d0bf 100644
--- a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-MSAN.json
+++ b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-MSAN.json
@@ -266,6 +266,10 @@
"serialize-8888",
"gm",
"_",
+ "wacky_yuv_formats_domain",
+ "serialize-8888",
+ "gm",
+ "_",
"bitmapfilters",
"serialize-8888",
"gm",
diff --git a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json
index 9fc449f..0ff90c3 100644
--- a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json
+++ b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json
@@ -267,6 +267,10 @@
"serialize-8888",
"gm",
"_",
+ "wacky_yuv_formats_domain",
+ "serialize-8888",
+ "gm",
+ "_",
"bitmapfilters",
"serialize-8888",
"gm",
diff --git a/infra/bots/recipes/test.py b/infra/bots/recipes/test.py
index 9a8dce6..70f1aa4 100644
--- a/infra/bots/recipes/test.py
+++ b/infra/bots/recipes/test.py
@@ -443,7 +443,8 @@
'fontmgr_bounds',
'fontmgr_match',
'fontmgr_iter',
- 'imagemasksubset']
+ 'imagemasksubset',
+ 'wacky_yuv_formats_domain']
# skia:5589
bad_serialize_gms.extend(['bitmapfilters',
diff --git a/src/gpu/GrImageTextureMaker.cpp b/src/gpu/GrImageTextureMaker.cpp
index 5e5327f..0ce1f7b 100644
--- a/src/gpu/GrImageTextureMaker.cpp
+++ b/src/gpu/GrImageTextureMaker.cpp
@@ -92,13 +92,7 @@
const GrSamplerState::Filter* filterOrNullForBicubic) {
// Check simple cases to see if we need to fall back to flattening the image
- // TODO: See if we can relax this -- for example, if filterConstraint
- // is kYes_FilterConstraint we still may not need a TextureDomain
- // in some cases. Or allow YUVtoRGBEffect to take a wrap mode to
- // handle ClampToBorder when a decal is needed.
- if (!textureMatrix.isIdentity() || kNo_FilterConstraint != filterConstraint ||
- !coordsLimitedToConstraintRect || !filterOrNullForBicubic ||
- this->domainNeedsDecal()) {
+ if (!filterOrNullForBicubic || this->domainNeedsDecal()) {
return this->INHERITED::createFragmentProcessor(textureMatrix, constraintRect,
filterConstraint,
coordsLimitedToConstraintRect,
@@ -106,15 +100,24 @@
}
// Check to see if the client has given us pre-mipped textures or we can generate them
- // If not, fall back to bilerp
+ // If not, fall back to bilerp. Also fall back to bilerp when a domain is requested
GrSamplerState::Filter filter = *filterOrNullForBicubic;
if (GrSamplerState::Filter::kMipMap == filter &&
- !fImage->setupMipmapsForPlanes(this->context())) {
+ (filterConstraint == GrTextureProducer::kYes_FilterConstraint ||
+ !fImage->setupMipmapsForPlanes(this->context()))) {
filter = GrSamplerState::Filter::kBilerp;
}
+ // Cannot rely on GrTextureProducer's domain infrastructure since we need to calculate domain's
+ // per plane, which may be different, so respect the filterConstraint without any additional
+ // analysis.
+ const SkRect* domain = nullptr;
+ if (filterConstraint == GrTextureProducer::kYes_FilterConstraint) {
+ domain = &constraintRect;
+ }
+
auto fp = GrYUVtoRGBEffect::Make(fImage->fProxies, fImage->fYUVAIndices,
- fImage->fYUVColorSpace, filter);
+ fImage->fYUVColorSpace, filter, textureMatrix, domain);
if (fImage->fFromColorSpace) {
fp = GrColorSpaceXformEffect::Make(std::move(fp), fImage->fFromColorSpace.get(),
fImage->alphaType(), fImage->colorSpace());
diff --git a/src/gpu/GrImageTextureMaker.h b/src/gpu/GrImageTextureMaker.h
index ba7772b..4fa5e9b 100644
--- a/src/gpu/GrImageTextureMaker.h
+++ b/src/gpu/GrImageTextureMaker.h
@@ -47,6 +47,9 @@
public:
GrYUVAImageTextureMaker(GrContext* context, const SkImage* client, bool useDecal = false);
+ // This could be made more nuanced and compare all of the texture proxy resolutions, but
+ // it's probably not worth the effort.
+ bool hasMixedResolutions() const override { return true; }
protected:
// TODO: consider overriding this, for the case where the underlying generator might be
// able to efficiently produce a "stretched" texture natively (e.g. picture-backed)
diff --git a/src/gpu/GrTextureProducer.h b/src/gpu/GrTextureProducer.h
index b1d94df..bdb5859 100644
--- a/src/gpu/GrTextureProducer.h
+++ b/src/gpu/GrTextureProducer.h
@@ -107,6 +107,8 @@
bool domainNeedsDecal() const { return fDomainNeedsDecal; }
virtual SkAlphaType alphaType() const = 0;
virtual SkColorSpace* colorSpace() const = 0;
+ // If the "texture" samples multiple images that have different resolutions (e.g. YUV420)
+ virtual bool hasMixedResolutions() const { return false; }
protected:
friend class GrTextureProducer_TestAccess;
diff --git a/src/gpu/SkGpuDevice_drawTexture.cpp b/src/gpu/SkGpuDevice_drawTexture.cpp
index 8f4d772..57de71c 100644
--- a/src/gpu/SkGpuDevice_drawTexture.cpp
+++ b/src/gpu/SkGpuDevice_drawTexture.cpp
@@ -284,7 +284,8 @@
// Check for optimization to drop the src rect constraint when on bilerp.
if (filterMode && GrSamplerState::Filter::kBilerp == *filterMode &&
- GrTextureAdjuster::kYes_FilterConstraint == constraintMode && coordsAllInsideSrcRect) {
+ GrTextureAdjuster::kYes_FilterConstraint == constraintMode && coordsAllInsideSrcRect &&
+ !producer->hasMixedResolutions()) {
SkMatrix combinedMatrix;
combinedMatrix.setConcat(ctm, srcToDst);
if (can_ignore_bilerp_constraint(*producer, src, combinedMatrix, rtc->fsaaType())) {
diff --git a/src/gpu/effects/GrYUVtoRGBEffect.cpp b/src/gpu/effects/GrYUVtoRGBEffect.cpp
index b8b93fd..963ed77 100644
--- a/src/gpu/effects/GrYUVtoRGBEffect.cpp
+++ b/src/gpu/effects/GrYUVtoRGBEffect.cpp
@@ -7,6 +7,13 @@
#include "GrYUVtoRGBEffect.h"
+#include "glsl/GrGLSLFragmentProcessor.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+#include "glsl/GrGLSLProgramBuilder.h"
+#include "GrTexture.h"
+#include "SkSLCPP.h"
+#include "SkSLUtil.h"
+
static const float kJPEGConversionMatrix[16] = {
1.0f, 0.0f, 1.402f, -0.703749f,
1.0f, -0.344136f, -0.714136f, 0.531211f,
@@ -31,7 +38,9 @@
std::unique_ptr<GrFragmentProcessor> GrYUVtoRGBEffect::Make(const sk_sp<GrTextureProxy> proxies[],
const SkYUVAIndex yuvaIndices[4],
SkYUVColorSpace yuvColorSpace,
- GrSamplerState::Filter filterMode) {
+ GrSamplerState::Filter filterMode,
+ const SkMatrix& localMatrix,
+ const SkRect* domain) {
int numPlanes;
SkAssertResult(SkYUVAIndex::AreValidIndices(yuvaIndices, &numPlanes));
@@ -51,7 +60,8 @@
}
return std::unique_ptr<GrFragmentProcessor>(new GrYUVtoRGBEffect(
- proxies, scales, filterModes, numPlanes, yuvaIndices, yuvColorSpace));
+ proxies, scales, filterModes, numPlanes, yuvaIndices, yuvColorSpace, localMatrix,
+ domain));
}
#ifdef SK_DEBUG
@@ -68,105 +78,107 @@
}
#endif
-#include "glsl/GrGLSLFragmentProcessor.h"
-#include "glsl/GrGLSLFragmentShaderBuilder.h"
-#include "glsl/GrGLSLProgramBuilder.h"
-#include "GrTexture.h"
-#include "SkSLCPP.h"
-#include "SkSLUtil.h"
-class GrGLSLYUVtoRGBEffect : public GrGLSLFragmentProcessor {
-public:
- GrGLSLYUVtoRGBEffect() {}
-
- void emitCode(EmitArgs& args) override {
- GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
- const GrYUVtoRGBEffect& _outer = args.fFp.cast<GrYUVtoRGBEffect>();
- (void)_outer;
-
- if (kIdentity_SkYUVColorSpace != _outer.yuvColorSpace()) {
- fColorSpaceMatrixVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag,
- kHalf4x4_GrSLType,
- "colorSpaceMatrix");
- }
-
- int numSamplers = args.fTexSamplers.count();
-
- SkString coords[4];
- for (int i = 0; i < numSamplers; ++i) {
- coords[i] = fragBuilder->ensureCoords2D(args.fTransformedCoords[i]);
- }
-
- for (int i = 0; i < numSamplers; ++i) {
- fragBuilder->codeAppendf(
- "half4 tmp%d = texture(%s, %s).%s;",
- i,
- fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[i]).c_str(),
- coords[i].c_str(),
- fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[i]).c_str());
- }
-
- static const char kChannelToChar[4] = { 'x', 'y', 'z', 'w' };
-
- fragBuilder->codeAppendf(
- "half4 yuvOne = half4(half(tmp%d.%c), half(tmp%d.%c), half(tmp%d.%c), 1.0);",
- _outer.yuvaIndex(0).fIndex, kChannelToChar[(int)_outer.yuvaIndex(0).fChannel],
- _outer.yuvaIndex(1).fIndex, kChannelToChar[(int)_outer.yuvaIndex(1).fChannel],
- _outer.yuvaIndex(2).fIndex, kChannelToChar[(int)_outer.yuvaIndex(2).fChannel]);
-
- if (kIdentity_SkYUVColorSpace != _outer.yuvColorSpace()) {
- SkASSERT(fColorSpaceMatrixVar.isValid());
- fragBuilder->codeAppendf(
- "yuvOne *= %s;", args.fUniformHandler->getUniformCStr(fColorSpaceMatrixVar));
- }
-
-
- if (_outer.yuvaIndex(3).fIndex >= 0) {
- fragBuilder->codeAppendf(
- "half a = tmp%d.%c;", _outer.yuvaIndex(3).fIndex,
- kChannelToChar[(int)_outer.yuvaIndex(3).fChannel]);
- // premultiply alpha
- fragBuilder->codeAppend("yuvOne *= a;");
- } else {
- fragBuilder->codeAppendf("half a = 1.0;");
- }
-
- fragBuilder->codeAppendf("%s = half4(yuvOne.xyz, a);", args.fOutputColor);
- }
-
-private:
- void onSetData(const GrGLSLProgramDataManager& pdman,
- const GrFragmentProcessor& _proc) override {
- const GrYUVtoRGBEffect& _outer = _proc.cast<GrYUVtoRGBEffect>();
-
- switch (_outer.yuvColorSpace()) {
- case kJPEG_SkYUVColorSpace:
- SkASSERT(fColorSpaceMatrixVar.isValid());
- pdman.setMatrix4f(fColorSpaceMatrixVar, kJPEGConversionMatrix);
- break;
- case kRec601_SkYUVColorSpace:
- SkASSERT(fColorSpaceMatrixVar.isValid());
- pdman.setMatrix4f(fColorSpaceMatrixVar, kRec601ConversionMatrix);
- break;
- case kRec709_SkYUVColorSpace:
- SkASSERT(fColorSpaceMatrixVar.isValid());
- pdman.setMatrix4f(fColorSpaceMatrixVar, kRec709ConversionMatrix);
- break;
- case kIdentity_SkYUVColorSpace:
- break;
- }
- }
-
- UniformHandle fColorSpaceMatrixVar;
-};
-
GrGLSLFragmentProcessor* GrYUVtoRGBEffect::onCreateGLSLInstance() const {
- return new GrGLSLYUVtoRGBEffect();
+ class GrGLSLYUVtoRGBEffect : public GrGLSLFragmentProcessor {
+ public:
+ GrGLSLYUVtoRGBEffect() {}
+
+ void emitCode(EmitArgs& args) override {
+ GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+ const GrYUVtoRGBEffect& _outer = args.fFp.cast<GrYUVtoRGBEffect>();
+ (void)_outer;
+
+ if (kIdentity_SkYUVColorSpace != _outer.yuvColorSpace()) {
+ fColorSpaceMatrixVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag,
+ kHalf4x4_GrSLType,
+ "colorSpaceMatrix");
+ }
+
+ int numSamplers = args.fTexSamplers.count();
+
+ SkString coords[4];
+ for (int i = 0; i < numSamplers; ++i) {
+ coords[i] = fragBuilder->ensureCoords2D(args.fTransformedCoords[i]);
+ }
+
+ for (int i = 0; i < numSamplers; ++i) {
+ SkString sampleVar;
+ sampleVar.printf("tmp%d", i);
+ fragBuilder->codeAppendf("half4 %s;", sampleVar.c_str());
+ fGLDomains[i].sampleTexture(fragBuilder, args.fUniformHandler, args.fShaderCaps,
+ _outer.fDomains[i], sampleVar.c_str(), coords[i], args.fTexSamplers[i]);
+ }
+
+ static const char kChannelToChar[4] = { 'x', 'y', 'z', 'w' };
+
+ fragBuilder->codeAppendf(
+ "half4 yuvOne = half4(tmp%d.%c, tmp%d.%c, tmp%d.%c, 1.0);",
+ _outer.yuvaIndex(0).fIndex, kChannelToChar[(int)_outer.yuvaIndex(0).fChannel],
+ _outer.yuvaIndex(1).fIndex, kChannelToChar[(int)_outer.yuvaIndex(1).fChannel],
+ _outer.yuvaIndex(2).fIndex, kChannelToChar[(int)_outer.yuvaIndex(2).fChannel]);
+
+ if (kIdentity_SkYUVColorSpace != _outer.yuvColorSpace()) {
+ SkASSERT(fColorSpaceMatrixVar.isValid());
+ fragBuilder->codeAppendf(
+ "yuvOne *= %s;", args.fUniformHandler->getUniformCStr(fColorSpaceMatrixVar));
+ }
+
+ if (_outer.yuvaIndex(3).fIndex >= 0) {
+ fragBuilder->codeAppendf(
+ "half a = tmp%d.%c;", _outer.yuvaIndex(3).fIndex,
+ kChannelToChar[(int)_outer.yuvaIndex(3).fChannel]);
+ // premultiply alpha
+ fragBuilder->codeAppend("yuvOne *= a;");
+ } else {
+ fragBuilder->codeAppend("half a = 1.0;");
+ }
+
+ fragBuilder->codeAppendf("%s = half4(yuvOne.xyz, a);", args.fOutputColor);
+ }
+
+ private:
+ void onSetData(const GrGLSLProgramDataManager& pdman,
+ const GrFragmentProcessor& _proc) override {
+ const GrYUVtoRGBEffect& _outer = _proc.cast<GrYUVtoRGBEffect>();
+
+ switch (_outer.yuvColorSpace()) {
+ case kJPEG_SkYUVColorSpace:
+ SkASSERT(fColorSpaceMatrixVar.isValid());
+ pdman.setMatrix4f(fColorSpaceMatrixVar, kJPEGConversionMatrix);
+ break;
+ case kRec601_SkYUVColorSpace:
+ SkASSERT(fColorSpaceMatrixVar.isValid());
+ pdman.setMatrix4f(fColorSpaceMatrixVar, kRec601ConversionMatrix);
+ break;
+ case kRec709_SkYUVColorSpace:
+ SkASSERT(fColorSpaceMatrixVar.isValid());
+ pdman.setMatrix4f(fColorSpaceMatrixVar, kRec709ConversionMatrix);
+ break;
+ case kIdentity_SkYUVColorSpace:
+ break;
+ }
+
+ int numSamplers = _outer.numTextureSamplers();
+ for (int i = 0; i < numSamplers; ++i) {
+ fGLDomains[i].setData(pdman, _outer.fDomains[i],
+ _outer.textureSampler(i).proxy(), _outer.textureSampler(i).samplerState());
+ }
+ }
+
+ UniformHandle fColorSpaceMatrixVar;
+ GrTextureDomain::GLDomain fGLDomains[4];
+ };
+
+ return new GrGLSLYUVtoRGBEffect;
}
void GrYUVtoRGBEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
GrProcessorKeyBuilder* b) const {
+ using Domain = GrTextureDomain::GLDomain;
+
b->add32(this->numTextureSamplers());
uint32_t packed = 0;
+ uint32_t domain = 0;
for (int i = 0; i < 4; ++i) {
if (this->yuvaIndex(i).fIndex < 0) {
continue;
@@ -178,12 +190,15 @@
SkASSERT(index < 4 && chann < 4);
packed |= (index | (chann << 2)) << (i * 4);
+
+ domain |= Domain::DomainKey(fDomains[i]) << (i * Domain::kDomainKeyBits);
}
if (kIdentity_SkYUVColorSpace == this->yuvColorSpace()) {
packed |= 0x1 << 16;
}
b->add32(packed);
+ b->add32(domain);
}
bool GrYUVtoRGBEffect::onIsEqual(const GrFragmentProcessor& other) const {
const GrYUVtoRGBEffect& that = other.cast<GrYUVtoRGBEffect>();
@@ -199,6 +214,9 @@
if (fSamplerTransforms[i] != that.fSamplerTransforms[i]) {
return false;
}
+ if (!(fDomains[i] == that.fDomains[i])) {
+ return false;
+ }
}
if (fYUVColorSpace != that.fYUVColorSpace) {
@@ -209,6 +227,7 @@
}
GrYUVtoRGBEffect::GrYUVtoRGBEffect(const GrYUVtoRGBEffect& src)
: INHERITED(kGrYUVtoRGBEffect_ClassID, src.optimizationFlags())
+ , fDomains{src.fDomains[0], src.fDomains[1], src.fDomains[2], src.fDomains[3]}
, fYUVColorSpace(src.fYUVColorSpace) {
int numPlanes = src.numTextureSamplers();
for (int i = 0; i < numPlanes; ++i) {
diff --git a/src/gpu/effects/GrYUVtoRGBEffect.h b/src/gpu/effects/GrYUVtoRGBEffect.h
index 93ec5238..9e4a843 100644
--- a/src/gpu/effects/GrYUVtoRGBEffect.h
+++ b/src/gpu/effects/GrYUVtoRGBEffect.h
@@ -12,15 +12,22 @@
#include "GrFragmentProcessor.h"
#include "GrCoordTransform.h"
+#include "GrTextureDomain.h"
#include "SkYUVAIndex.h"
class GrYUVtoRGBEffect : public GrFragmentProcessor {
public:
+ // The domain supported by this effect is more limited than the general GrTextureDomain due
+ // to the multi-planar, varying resolution images that it has to sample. If 'domain' is provided
+ // it is the Y plane's domain. This will automatically inset for bilinear filtering, and only
+ // the clamp wrap mode is supported.
static std::unique_ptr<GrFragmentProcessor> Make(const sk_sp<GrTextureProxy> proxies[],
const SkYUVAIndex indices[4],
SkYUVColorSpace yuvColorSpace,
- GrSamplerState::Filter filterMode);
+ GrSamplerState::Filter filterMode,
+ const SkMatrix& localMatrix = SkMatrix::I(),
+ const SkRect* domain = nullptr);
#ifdef SK_DEBUG
SkString dumpInfo() const override;
#endif
@@ -35,13 +42,31 @@
private:
GrYUVtoRGBEffect(const sk_sp<GrTextureProxy> proxies[], const SkSize scales[],
const GrSamplerState::Filter filterModes[], int numPlanes,
- const SkYUVAIndex yuvaIndices[4], SkYUVColorSpace yuvColorSpace)
+ const SkYUVAIndex yuvaIndices[4], SkYUVColorSpace yuvColorSpace,
+ const SkMatrix& localMatrix, const SkRect* domain)
: INHERITED(kGrYUVtoRGBEffect_ClassID, kNone_OptimizationFlags)
+ , fDomains{GrTextureDomain::IgnoredDomain(), GrTextureDomain::IgnoredDomain(),
+ GrTextureDomain::IgnoredDomain(), GrTextureDomain::IgnoredDomain()}
, fYUVColorSpace(yuvColorSpace) {
for (int i = 0; i < numPlanes; ++i) {
+ SkMatrix planeMatrix = SkMatrix::MakeScale(scales[i].width(), scales[i].height());
+ if (domain) {
+ SkASSERT(filterModes[i] != GrSamplerState::Filter::kMipMap);
+
+ SkRect scaledDomain = planeMatrix.mapRect(*domain);
+ if (filterModes[i] != GrSamplerState::Filter::kNearest) {
+ // Inset by half a pixel for bilerp, after scaling to the size of the plane
+ scaledDomain.inset(0.5f, 0.5f);
+ }
+
+ fDomains[i] = GrTextureDomain(proxies[i].get(), scaledDomain,
+ GrTextureDomain::kClamp_Mode, GrTextureDomain::kClamp_Mode, i);
+ }
+
+ planeMatrix.postConcat(localMatrix);
fSamplers[i].reset(std::move(proxies[i]),
GrSamplerState(GrSamplerState::WrapMode::kClamp, filterModes[i]));
- fSamplerTransforms[i] = SkMatrix::MakeScale(scales[i].width(), scales[i].height());
+ fSamplerTransforms[i] = planeMatrix;
fSamplerCoordTransforms[i] =
GrCoordTransform(fSamplerTransforms[i], fSamplers[i].proxy());
}
@@ -62,6 +87,7 @@
TextureSampler fSamplers[4];
SkMatrix44 fSamplerTransforms[4];
GrCoordTransform fSamplerCoordTransforms[4];
+ GrTextureDomain fDomains[4];
SkYUVAIndex fYUVAIndices[4];
SkYUVColorSpace fYUVColorSpace;