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:
parent
c6d0fdfb40
commit
d71548adfb
@ -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;)
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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];
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user