Add support for wrap modes to GrYUVtoRGBEffect.

In support of this add support for border color to GrTextureEffect.
Currently non-zero border colors are always emulated in shader code.

Change-Id: I007e264411d713f8afaf8f160b6cea6d0f35c753
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/274282
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
This commit is contained in:
Brian Salomon 2020-02-29 19:43:30 -05:00 committed by Skia Commit-Bot
parent c6d0fdfb40
commit d71548adfb
6 changed files with 209 additions and 133 deletions

View File

@ -269,23 +269,21 @@ DEF_GM(return new YUVNV12toRGBEffect;)
//////////////////////////////////////////////////////////////////////////////
// This GM tests domain clamping on YUV multiplanar images where the U and V
// This GM tests subsetting YUV multiplanar images where the U and V
// planes have different resolution from Y. See skbug:8959
class YUVtoRGBDomainEffect : public GpuGM {
class YUVtoRGBSubsetEffect : public GpuGM {
public:
YUVtoRGBDomainEffect() {
YUVtoRGBSubsetEffect() {
this->setBGColor(0xFFFFFFFF);
}
protected:
SkString onShortName() override {
return SkString("yuv_to_rgb_domain_effect");
return SkString("yuv_to_rgb_subset_effect");
}
SkISize onISize() override {
return SkISize::Make((YSIZE + kTestPad) * 3 + kDrawPad, (YSIZE + kTestPad) * 2 + kDrawPad);
}
SkISize onISize() override { return {1310, 540}; }
void onOnceBeforeDraw() override {
SkImageInfo yinfo = SkImageInfo::MakeA8(YSIZE, YSIZE);
@ -295,13 +293,20 @@ protected:
SkImageInfo vinfo = SkImageInfo::MakeA8(VSIZE, VSIZE);
fBitmaps[2].allocPixels(vinfo);
int innerColor[] = {149, 43, 21};
int outerColor[] = {128, 128, 128};
int innerY = 149;
unsigned char innerU[4] = {43, 75, 145, 200};
unsigned char innerV[4] = {88, 180, 200, 43};
int outerYUV[] = {128, 128, 128};
for (int i = 0; i < 3; ++i) {
fBitmaps[i].eraseColor(SkColorSetARGB(outerColor[i], 0, 0, 0));
SkIRect innerRect = i == 0 ? SkIRect::MakeLTRB(2, 2, 6, 6) : SkIRect::MakeLTRB(1, 1, 3, 3);
fBitmaps[i].erase(SkColorSetARGB(innerColor[i], 0, 0, 0), innerRect);
fBitmaps[i].setImmutable();
fBitmaps[i].eraseColor(SkColorSetARGB(outerYUV[i], 0, 0, 0));
}
fBitmaps[0].eraseARGB(innerY, 0, 0, 0);
SkPixmap innerUPM(SkImageInfo::MakeA8(2, 2), innerU, 2);
SkPixmap innerVPM(SkImageInfo::MakeA8(2, 2), innerV, 2);
fBitmaps[1].writePixels(innerUPM, 1, 1);
fBitmaps[2].writePixels(innerVPM, 1, 1);
for (auto& fBitmap : fBitmaps) {
fBitmap.setImmutable();
}
}
@ -318,11 +323,9 @@ protected:
}
}
// 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);
static const SkRect kColorRect = SkRect::MakeLTRB(2.f, 2.f, 6.f, 6.f);
SkYUVAIndex yuvaIndices[4] = {
{ SkYUVAIndex::kY_Index, SkColorChannel::kR },
@ -330,28 +333,31 @@ protected:
{ SkYUVAIndex::kV_Index, SkColorChannel::kR },
{ -1, SkColorChannel::kA }
};
SkRect rect = SkRect::MakeWH(YSIZE, YSIZE);
// Outset to visualize wrap modes.
SkRect rect = SkRect::MakeWH(YSIZE, YSIZE).makeOutset(YSIZE/2, YSIZE/2);
SkScalar y = kDrawPad + kTestPad;
SkScalar y = kTestPad;
// Rows are filter modes.
for (uint32_t i = 0; i < SK_ARRAY_COUNT(kFilters); ++i) {
SkScalar x = kDrawPad + kTestPad;
for (uint32_t j = 0; j < 2; ++j) {
SkScalar x = kTestPad;
// Columns are non-subsetted followed by subsetted with each WrapMode in a row
for (uint32_t j = 0; j < GrSamplerState::kWrapModeCount + 1; ++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* subset = j > 0 ? &kColorRect : nullptr;
const SkRect* domainPtr = j > 0 ? &domain : nullptr;
GrSamplerState samplerState;
samplerState.setFilterMode(kFilters[i]);
if (j > 0) {
auto wm = static_cast<GrSamplerState::WrapMode>(j - 1);
samplerState.setWrapModeX(wm);
samplerState.setWrapModeY(wm);
}
const auto& caps = *context->priv().caps();
std::unique_ptr<GrFragmentProcessor> fp(
GrYUVtoRGBEffect::Make(views, yuvaIndices, kJPEG_SkYUVColorSpace,
kFilters[i], caps, SkMatrix::I(), domainPtr));
samplerState, caps, SkMatrix::I(), subset));
if (fp) {
GrPaint grPaint;
grPaint.addColorFragmentProcessor(std::move(fp));
@ -370,11 +376,10 @@ protected:
private:
SkBitmap fBitmaps[3];
static constexpr SkScalar kDrawPad = 10.f;
static constexpr SkScalar kTestPad = 10.f;
typedef GM INHERITED;
};
DEF_GM(return new YUVtoRGBDomainEffect;)
DEF_GM(return new YUVtoRGBSubsetEffect;)
}

View File

@ -65,9 +65,8 @@ std::unique_ptr<GrFragmentProcessor> GrYUVAImageTextureMaker::createFragmentProc
GrSamplerState::WrapMode wrapY,
const GrSamplerState::Filter* filterOrNullForBicubic) {
// Check simple cases to see if we need to fall back to flattening the image (or whether it's
// already been flattened.) GrYUVtoRGBEffect only supports kClamp (for now.)
if (!filterOrNullForBicubic || wrapX != GrSamplerState::WrapMode::kClamp ||
wrapY != GrSamplerState::WrapMode::kClamp || fImage->fRGBView.proxy()) {
// already been flattened.)
if (!filterOrNullForBicubic || fImage->fRGBView.proxy()) {
return this->INHERITED::createFragmentProcessor(
textureMatrix, constraintRect, filterConstraint, coordsLimitedToConstraintRect,
wrapX, wrapY, filterOrNullForBicubic);

View File

@ -18,10 +18,27 @@
using Mode = GrSamplerState::WrapMode;
using Filter = GrSamplerState::Filter;
struct GrTextureEffect::Sampling {
GrSamplerState fHWSampler;
ShaderMode fShaderModes[2] = {ShaderMode::kNone, ShaderMode::kNone};
SkRect fShaderSubset = {0, 0, 0, 0};
SkRect fShaderClamp = {0, 0, 0, 0};
float fBorder[4] = {0, 0, 0, 0};
Sampling(GrSamplerState::Filter filter) : fHWSampler(filter) {}
Sampling(const GrSurfaceProxy& proxy,
GrSamplerState sampler,
const SkRect&,
const SkRect*,
const float border[4],
const GrCaps&);
inline bool hasBorderAlpha() const;
};
GrTextureEffect::Sampling::Sampling(const GrSurfaceProxy& proxy,
GrSamplerState sampler,
const SkRect& subset,
const SkRect* domain,
const float border[4],
const GrCaps& caps) {
struct Span {
float fA = 0.f, fB = 0.f;
@ -46,13 +63,20 @@ GrTextureEffect::Sampling::Sampling(const GrSurfaceProxy& proxy,
auto type = proxy.asTextureProxy()->textureType();
auto filter = sampler.filter();
auto resolve = [type, &caps, filter](int size, Mode mode, Span subset, Span domain) {
auto resolve = [type, &caps, filter, &border](int size, Mode mode, Span subset, Span domain) {
Result1D r;
bool canDoHW = (mode != Mode::kClampToBorder || caps.clampToBorderSupport()) &&
(mode == Mode::kClamp || caps.npotTextureTileSupport() || SkIsPow2(size)) &&
(mode == Mode::kClamp || mode == Mode::kClampToBorder ||
type == GrTextureType::k2D);
if (canDoHW && size > 0 && subset.fA <= 0 && subset.fB >= size) {
bool canDoModeInHW = true;
// TODO: Use HW border color when available.
if (mode == Mode::kClampToBorder &&
(!caps.clampToBorderSupport() || border[0] || border[1] || border[2] || border[3])) {
canDoModeInHW = false;
} else if (mode != Mode::kClamp && !caps.npotTextureTileSupport() && !SkIsPow2(size)) {
canDoModeInHW = false;
} else if (type != GrTextureType::k2D &&
!(mode == Mode::kClamp || mode == Mode::kClampToBorder)) {
canDoModeInHW = false;
}
if (canDoModeInHW && size > 0 && subset.fA <= 0 && subset.fB >= size) {
r.fShaderMode = ShaderMode::kNone;
r.fHWMode = mode;
r.fShaderSubset = r.fShaderClamp = {0, 0};
@ -109,12 +133,19 @@ GrTextureEffect::Sampling::Sampling(const GrSurfaceProxy& proxy,
x.fShaderSubset.fB, y.fShaderSubset.fB};
fShaderClamp = {x.fShaderClamp.fA, y.fShaderClamp.fA,
x.fShaderClamp.fB, y.fShaderClamp.fB};
std::copy_n(border, 4, fBorder);
}
bool GrTextureEffect::Sampling::usesDecal() const {
return fShaderModes[0] == ShaderMode::kDecal || fShaderModes[1] == ShaderMode::kDecal ||
fHWSampler.wrapModeX() == GrSamplerState::WrapMode::kClampToBorder ||
fHWSampler.wrapModeY() == GrSamplerState::WrapMode::kClampToBorder;
bool GrTextureEffect::Sampling::hasBorderAlpha() const {
if (fHWSampler.wrapModeX() == GrSamplerState::WrapMode::kClampToBorder ||
fHWSampler.wrapModeY() == GrSamplerState::WrapMode::kClampToBorder) {
return true;
}
if (fShaderModes[0] == ShaderMode::kClampToBorder ||
fShaderModes[1] == ShaderMode::kClampToBorder) {
return fBorder[3] < 1.f;
}
return false;
}
std::unique_ptr<GrFragmentProcessor> GrTextureEffect::Make(GrSurfaceProxyView view,
@ -129,9 +160,10 @@ std::unique_ptr<GrFragmentProcessor> GrTextureEffect::Make(GrSurfaceProxyView vi
SkAlphaType alphaType,
const SkMatrix& matrix,
GrSamplerState sampler,
const GrCaps& caps) {
const GrCaps& caps,
const float border[4]) {
Sampling sampling(*view.proxy(), sampler, SkRect::Make(view.proxy()->dimensions()), nullptr,
caps);
border, caps);
return std::unique_ptr<GrFragmentProcessor>(
new GrTextureEffect(std::move(view), alphaType, matrix, sampling));
}
@ -141,8 +173,9 @@ std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeSubset(GrSurfaceProxyV
const SkMatrix& matrix,
GrSamplerState sampler,
const SkRect& subset,
const GrCaps& caps) {
Sampling sampling(*view.proxy(), sampler, subset, nullptr, caps);
const GrCaps& caps,
const float border[4]) {
Sampling sampling(*view.proxy(), sampler, subset, nullptr, border, caps);
return std::unique_ptr<GrFragmentProcessor>(
new GrTextureEffect(std::move(view), alphaType, matrix, sampling));
}
@ -153,8 +186,9 @@ std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeSubset(GrSurfaceProxyV
GrSamplerState sampler,
const SkRect& subset,
const SkRect& domain,
const GrCaps& caps) {
Sampling sampling(*view.proxy(), sampler, subset, &domain, caps);
const GrCaps& caps,
const float border[4]) {
Sampling sampling(*view.proxy(), sampler, subset, &domain, border, caps);
return std::unique_ptr<GrFragmentProcessor>(
new GrTextureEffect(std::move(view), alphaType, matrix, sampling));
}
@ -176,9 +210,9 @@ GrTextureEffect::FilterLogic GrTextureEffect::GetFilterLogic(ShaderMode mode,
return FilterLogic::kRepeatMipMap;
}
SkUNREACHABLE;
case ShaderMode::kDecal:
return filter > GrSamplerState::Filter::kNearest ? FilterLogic::kDecalFilter
: FilterLogic::kDecalNearest;
case ShaderMode::kClampToBorder:
return filter > GrSamplerState::Filter::kNearest ? FilterLogic::kClampToBorderFilter
: FilterLogic::kClampToBorderNearest;
}
SkUNREACHABLE;
}
@ -188,6 +222,7 @@ GrGLSLFragmentProcessor* GrTextureEffect::onCreateGLSLInstance() const {
UniformHandle fSubsetUni;
UniformHandle fClampUni;
UniformHandle fNormUni;
UniformHandle fBorderUni;
public:
void emitCode(EmitArgs& args) override {
@ -215,14 +250,14 @@ GrGLSLFragmentProcessor* GrTextureEffect::onCreateGLSLInstance() const {
// 1) Map the coordinates into the subset range [Repeat and MirrorRepeat], or pass
// through output of 0).
// 2) Clamp the coordinates to a 0.5 inset of the subset rect [Clamp, Repeat, and
// MirrorRepeat always or Decal only when filtering] or pass through output of
// 1). The clamp rect collapses to a line or point it if the subset rect is less
// than one pixel wide/tall.
// MirrorRepeat always or ClampToBorder only when filtering] or pass through
// output of 1). The clamp rect collapses to a line or point it if the subset
// rect is less than one pixel wide/tall.
// 3) Look up texture with output of 2) [All]
// 3) Use the difference between 1) and 2) to apply filtering at edge [Repeat or
// Decal]. In the Repeat case this requires extra texture lookups on the other
// side of the subset (up to 3 more reads). Or if Decal and not filtering
// do a hard less than/greater than test with the subset rect.
// ClampToBorder]. In the Repeat case this requires extra texture lookups on the
// other side of the subset (up to 3 more reads). Or if ClampToBorder and not
// filtering do a hard less than/greater than test with the subset rect.
// Convert possible projective texture coordinates into non-homogeneous half2.
fb->codeAppendf(
@ -236,14 +271,20 @@ GrGLSLFragmentProcessor* GrTextureEffect::onCreateGLSLInstance() const {
FilterLogic filterLogic[2] = {GetFilterLogic(m[0], filter),
GetFilterLogic(m[1], filter)};
const char* borderName = nullptr;
if (te.fShaderModes[0] == ShaderMode::kClampToBorder ||
te.fShaderModes[1] == ShaderMode::kClampToBorder) {
fBorderUni = args.fUniformHandler->addUniform(
kFragment_GrShaderFlag, kHalf4_GrSLType, "border", &borderName);
}
auto modeUsesSubset = [](ShaderMode m) {
return m == ShaderMode::kRepeat || m == ShaderMode::kMirrorRepeat ||
m == ShaderMode::kDecal;
m == ShaderMode::kClampToBorder;
};
auto modeUsesClamp = [filter](ShaderMode m) {
return m != ShaderMode::kNone &&
(m != ShaderMode::kDecal || filter > Filter::kNearest);
(m != ShaderMode::kClampToBorder || filter > Filter::kNearest);
};
bool useSubset[2] = {modeUsesSubset(m[0]), modeUsesSubset(m[1])};
@ -300,7 +341,7 @@ GrGLSLFragmentProcessor* GrTextureEffect::onCreateGLSLInstance() const {
// These modes either don't use the subset rect or don't need to map the
// coords to be within the subset.
case ShaderMode::kNone:
case ShaderMode::kDecal:
case ShaderMode::kClampToBorder:
case ShaderMode::kClamp:
fb->codeAppendf("subsetCoord.%s = inCoord.%s;", coordSwizzle,
coordSwizzle);
@ -445,17 +486,17 @@ GrGLSLFragmentProcessor* GrTextureEffect::onCreateGLSLInstance() const {
SkString repeatBilerpReadY;
// Calculate the amount the coord moved for clamping. This will be used
// to implement shader-based filtering for kDecal and kRepeat.
// to implement shader-based filtering for kClampToBorder and kRepeat.
if (filterLogic[0] == FilterLogic::kRepeatBilerp ||
filterLogic[0] == FilterLogic::kDecalFilter) {
filterLogic[0] == FilterLogic::kClampToBorderFilter) {
fb->codeAppend("half errX = half(subsetCoord.x - clampedCoord.x);");
fb->codeAppendf("float repeatCoordX = errX > 0 ? %s.x : %s.z;", clampName,
clampName);
repeatBilerpReadX = read("float2(repeatCoordX, clampedCoord.y)");
}
if (filterLogic[1] == FilterLogic::kRepeatBilerp ||
filterLogic[1] == FilterLogic::kDecalFilter) {
filterLogic[1] == FilterLogic::kClampToBorderFilter) {
fb->codeAppend("half errY = half(subsetCoord.y - clampedCoord.y);");
fb->codeAppendf("float repeatCoordY = errY > 0 ? %s.y : %s.w;", clampName,
clampName);
@ -496,32 +537,32 @@ GrGLSLFragmentProcessor* GrTextureEffect::onCreateGLSLInstance() const {
ifStr, repeatBilerpReadY.c_str());
}
// Do soft edge shader filtering against transparent black for kDecalFilter using
// Do soft edge shader filtering against border color for kClampToBorderFilter using
// the err values calculated above.
if (filterLogic[0] == FilterLogic::kDecalFilter) {
fb->codeAppendf(
"textureColor = mix(textureColor, half4(0), min(abs(errX), 1));");
if (filterLogic[0] == FilterLogic::kClampToBorderFilter) {
fb->codeAppendf("textureColor = mix(textureColor, %s, min(abs(errX), 1));",
borderName);
}
if (filterLogic[1] == FilterLogic::kDecalFilter) {
fb->codeAppendf(
"textureColor = mix(textureColor, half4(0), min(abs(errY), 1));");
if (filterLogic[1] == FilterLogic::kClampToBorderFilter) {
fb->codeAppendf("textureColor = mix(textureColor, %s, min(abs(errY), 1));",
borderName);
}
// Do hard-edge shader transition to transparent black for kDecalNearest at the
// Do hard-edge shader transition to border color for kClampToBorderNearest at the
// subset boundaries.
if (filterLogic[0] == FilterLogic::kDecalNearest) {
if (filterLogic[0] == FilterLogic::kClampToBorderNearest) {
fb->codeAppendf(
"if (inCoord.x < %s.x || inCoord.x > %s.z) {"
" textureColor = half4(0);"
" textureColor = %s;"
"}",
subsetName, subsetName);
subsetName, subsetName, borderName);
}
if (filterLogic[1] == FilterLogic::kDecalNearest) {
if (filterLogic[1] == FilterLogic::kClampToBorderNearest) {
fb->codeAppendf(
"if (inCoord.y < %s.y || inCoord.y > %s.w) {"
" textureColor = half4(0);"
" textureColor = %s;"
"}",
subsetName, subsetName);
subsetName, subsetName, borderName);
}
fb->codeAppendf("%s = %s * textureColor;", args.fOutputColor, args.fInputColor);
}
@ -569,6 +610,9 @@ GrGLSLFragmentProcessor* GrTextureEffect::onCreateGLSLInstance() const {
float subset[] = {c.fLeft, c.fTop, c.fRight, c.fBottom};
pushRect(subset, fClampUni);
}
if (fBorderUni.isValid()) {
pdm.set4fv(fBorderUni, 1, te.fBorder);
}
}
};
return new Impl;
@ -585,14 +629,24 @@ void GrTextureEffect::onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyB
bool GrTextureEffect::onIsEqual(const GrFragmentProcessor& other) const {
auto that = other.cast<GrTextureEffect>();
return fShaderModes[0] == that.fShaderModes[0] && fShaderModes[1] == that.fShaderModes[1] &&
fSubset == that.fSubset;
if (fShaderModes[0] != that.fShaderModes[0] || fShaderModes[1] != that.fShaderModes[1]) {
return false;
}
if (fSubset != that.fSubset) {
return false;
}
if ((fShaderModes[0] == ShaderMode::kClampToBorder ||
fShaderModes[1] == ShaderMode::kClampToBorder) &&
!std::equal(fBorder, fBorder + 4, that.fBorder)) {
return false;
}
return true;
}
GrTextureEffect::GrTextureEffect(GrSurfaceProxyView view, SkAlphaType alphaType,
const SkMatrix& matrix, const Sampling& sampling)
: GrFragmentProcessor(kGrTextureEffect_ClassID,
ModulateForSamplerOptFlags(alphaType, sampling.usesDecal()))
ModulateForSamplerOptFlags(alphaType, sampling.hasBorderAlpha()))
, fCoordTransform(matrix, view.proxy(), view.origin())
, fSampler(std::move(view), sampling.fHWSampler)
, fSubset(sampling.fShaderSubset)
@ -604,6 +658,7 @@ GrTextureEffect::GrTextureEffect(GrSurfaceProxyView view, SkAlphaType alphaType,
SkASSERT(fShaderModes[1] != ShaderMode::kNone || (fSubset.fTop == 0 && fSubset.fBottom == 0));
this->setTextureSamplerCnt(1);
this->addCoordTransform(&fCoordTransform);
std::copy_n(sampling.fBorder, 4, fBorder);
}
GrTextureEffect::GrTextureEffect(const GrTextureEffect& src)

View File

@ -15,6 +15,8 @@
class GrTextureEffect : public GrFragmentProcessor {
public:
static constexpr float kDefaultBorder[4] = {0};
/** Make from a filter. The sampler will be configured with clamp mode. */
static std::unique_ptr<GrFragmentProcessor> Make(
GrSurfaceProxyView,
@ -26,8 +28,10 @@ public:
* Make from a full GrSamplerState. Caps are required to determine support for kClampToBorder.
* This will be emulated in the shader if there is no hardware support.
*/
static std::unique_ptr<GrFragmentProcessor> Make(
GrSurfaceProxyView, SkAlphaType, const SkMatrix&, GrSamplerState, const GrCaps& caps);
static std::unique_ptr<GrFragmentProcessor> Make(GrSurfaceProxyView, SkAlphaType,
const SkMatrix&, GrSamplerState,
const GrCaps& caps,
const float border[4] = kDefaultBorder);
/**
* Makes a texture effect that samples a subset of a texture. The wrap modes of the
@ -44,7 +48,8 @@ public:
const SkMatrix&,
GrSamplerState,
const SkRect& subset,
const GrCaps& caps);
const GrCaps& caps,
const float border[4] = kDefaultBorder);
/**
* The same as above but also takes a 'domain' that specifies any known limit on the post-
@ -58,7 +63,8 @@ public:
GrSamplerState,
const SkRect& subset,
const SkRect& domain,
const GrCaps& caps);
const GrCaps& caps,
const float border[4] = kDefaultBorder);
std::unique_ptr<GrFragmentProcessor> clone() const override;
@ -69,39 +75,28 @@ private:
kClamp = static_cast<int>(GrSamplerState::WrapMode::kClamp),
kRepeat = static_cast<int>(GrSamplerState::WrapMode::kRepeat),
kMirrorRepeat = static_cast<int>(GrSamplerState::WrapMode::kMirrorRepeat),
kDecal = static_cast<int>(GrSamplerState::WrapMode::kClampToBorder),
kClampToBorder = static_cast<int>(GrSamplerState::WrapMode::kClampToBorder),
kNone,
};
struct Sampling {
GrSamplerState fHWSampler;
ShaderMode fShaderModes[2] = {ShaderMode::kNone, ShaderMode::kNone};
SkRect fShaderSubset = {0, 0, 0, 0};
SkRect fShaderClamp = {0, 0, 0, 0};
Sampling(GrSamplerState::Filter filter) : fHWSampler(filter) {}
Sampling(const GrSurfaceProxy& proxy,
GrSamplerState sampler,
const SkRect&,
const SkRect*,
const GrCaps&);
inline bool usesDecal() const;
};
struct Sampling;
/**
* Sometimes the implementation of a ShaderMode depends on which GrSamplerState::Filter is
* used.
*/
enum class FilterLogic {
kNone, // The shader isn't specialized for the filter.
kRepeatBilerp, // Filter across the subset boundary for kRepeat mode
kRepeatMipMap, // Logic for LOD selection with kRepeat mode.
kDecalFilter, // Logic for fading to transparent black when filtering with kDecal.
kDecalNearest, // Logic for hard transition to transparent black when not filtering.
kNone, // The shader isn't specialized for the filter.
kRepeatBilerp, // Filter across the subset boundary for kRepeat mode
kRepeatMipMap, // Logic for LOD selection with kRepeat mode.
kClampToBorderFilter, // Logic for fading to border color when filtering.
kClampToBorderNearest, // Logic for hard transition to border color when not filtering.
};
static FilterLogic GetFilterLogic(ShaderMode mode, GrSamplerState::Filter filter);
GrCoordTransform fCoordTransform;
TextureSampler fSampler;
float fBorder[4];
SkRect fSubset;
SkRect fClamp;
ShaderMode fShaderModes[2];

View File

@ -15,64 +15,86 @@
#include "src/sksl/SkSLCPP.h"
#include "src/sksl/SkSLUtil.h"
static void border_colors(SkYUVColorSpace cs,
const SkYUVAIndex yuvaIndices[4],
float planeBorders[4][4]) {
float m[20];
SkColorMatrix_RGB2YUV(cs, m);
for (int i = 0; i < 4; ++i) {
if (yuvaIndices[i].fIndex == -1) {
return;
}
auto c = static_cast<int>(yuvaIndices[i].fChannel);
planeBorders[yuvaIndices[i].fIndex][c] = m[i*5 + 4];
}
}
std::unique_ptr<GrFragmentProcessor> GrYUVtoRGBEffect::Make(GrSurfaceProxyView views[],
const SkYUVAIndex yuvaIndices[4],
SkYUVColorSpace yuvColorSpace,
GrSamplerState::Filter filterMode,
GrSamplerState samplerState,
const GrCaps& caps,
const SkMatrix& localMatrix,
const SkRect* domain) {
const SkRect* subset) {
int numPlanes;
SkAssertResult(SkYUVAIndex::AreValidIndices(yuvaIndices, &numPlanes));
const SkISize YDimensions =
views[yuvaIndices[SkYUVAIndex::kY_Index].fIndex].proxy()->dimensions();
const SkISize yDimensions =
views[yuvaIndices[SkYUVAIndex::kY_Index].fIndex].proxy()->dimensions();
// This promotion of nearest to bilinear for UV planes exists to mimic libjpeg[-turbo]'s
// do_fancy_upsampling option. However, skbug.com/9693.
GrSamplerState::Filter subsampledPlaneFilterMode = GrSamplerState::Filter::kMipMap == filterMode
? GrSamplerState::Filter::kMipMap
: GrSamplerState::Filter::kBilerp;
GrSamplerState::Filter subsampledPlaneFilterMode =
std::max(samplerState.filter(), GrSamplerState::Filter::kBilerp);
bool usesBorder = samplerState.wrapModeX() == GrSamplerState::WrapMode::kClampToBorder ||
samplerState.wrapModeY() == GrSamplerState::WrapMode::kClampToBorder;
float planeBorders[4][4] = {};
if (usesBorder) {
border_colors(yuvColorSpace, yuvaIndices, planeBorders);
}
std::unique_ptr<GrFragmentProcessor> planeFPs[4];
for (int i = 0; i < numPlanes; ++i) {
SkISize dimensions = views[i].proxy()->dimensions();
SkTCopyOnFirstWrite<SkMatrix> planeMatrix(&localMatrix);
GrSamplerState::Filter planeFilter = filterMode;
GrSamplerState::Filter planeFilter = samplerState.filter();
SkRect planeDomain;
if (dimensions != YDimensions) {
if (dimensions != yDimensions) {
// JPEG chroma subsampling of odd dimensions produces U and V planes with the ceiling of
// the image size divided by the subsampling factor (2). Our API for creating YUVA
// doesn't capture the intended subsampling (and we should fix that). This fixes up 2x
// subsampling for images with odd widths/heights (e.g. JPEG 420 or 422).
float sx = (float)dimensions.width() / YDimensions.width();
float sy = (float)dimensions.height() / YDimensions.height();
if ((YDimensions.width() & 0b1) && dimensions.width() == YDimensions.width() / 2 + 1) {
float sx = (float)dimensions.width() / yDimensions.width();
float sy = (float)dimensions.height() / yDimensions.height();
if ((yDimensions.width() & 0b1) && dimensions.width() == yDimensions.width() / 2 + 1) {
sx = 0.5f;
}
if ((YDimensions.height() & 0b1) &&
dimensions.height() == YDimensions.height() / 2 + 1) {
if ((yDimensions.height() & 0b1) &&
dimensions.height() == yDimensions.height() / 2 + 1) {
sy = 0.5f;
}
*planeMatrix.writable() = SkMatrix::MakeScale(sx, sy);
planeMatrix.writable()->preConcat(localMatrix);
planeFilter = subsampledPlaneFilterMode;
if (domain) {
planeDomain = {domain->fLeft * sx,
domain->fTop * sy,
domain->fRight * sx,
domain->fBottom * sy};
if (subset) {
planeDomain = {subset->fLeft * sx,
subset->fTop * sy,
subset->fRight * sx,
subset->fBottom * sy};
}
} else if (domain) {
planeDomain = *domain;
} else if (subset) {
planeDomain = *subset;
}
if (domain) {
samplerState.setFilterMode(planeFilter);
if (subset) {
SkASSERT(planeFilter != GrSamplerState::Filter::kMipMap);
planeFPs[i] = GrTextureEffect::MakeSubset(views[i], kUnknown_SkAlphaType,
*planeMatrix, planeFilter, planeDomain, caps);
planeFPs[i] =
GrTextureEffect::MakeSubset(views[i], kUnknown_SkAlphaType, *planeMatrix,
samplerState, planeDomain, caps, planeBorders[i]);
} else {
planeFPs[i] = GrTextureEffect::Make(views[i], kUnknown_SkAlphaType, *planeMatrix,
planeFilter);
samplerState, caps, planeBorders[i]);
}
}

View File

@ -23,10 +23,10 @@ public:
static std::unique_ptr<GrFragmentProcessor> Make(GrSurfaceProxyView views[],
const SkYUVAIndex indices[4],
SkYUVColorSpace yuvColorSpace,
GrSamplerState::Filter filterMode,
GrSamplerState samplerState,
const GrCaps&,
const SkMatrix& localMatrix = SkMatrix::I(),
const SkRect* domain = nullptr);
const SkRect* subset = nullptr);
#ifdef SK_DEBUG
SkString dumpInfo() const override;
#endif