Rename domain types and vars to subset.
Makes nomenclature more conistent across different classes. Change-Id: I9f052bbd38082d95714702b2ae960c4e15fdc181 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/284718 Commit-Queue: Brian Salomon <bsalomon@google.com> Reviewed-by: Michael Ludwig <michaelludwig@google.com>
This commit is contained in:
parent
d8f611dff1
commit
2432d061ed
@ -408,7 +408,7 @@ private:
|
||||
// Fixed vertex spec for extracting the picture frame geometry
|
||||
static const GrQuadPerEdgeAA::VertexSpec kSpec =
|
||||
{GrQuad::Type::kGeneral, GrQuadPerEdgeAA::ColorType::kNone,
|
||||
GrQuad::Type::kAxisAligned, false, GrQuadPerEdgeAA::Domain::kNo,
|
||||
GrQuad::Type::kAxisAligned, false, GrQuadPerEdgeAA::Subset::kNo,
|
||||
GrAAType::kCoverage, false, GrQuadPerEdgeAA::IndexBufferOption::kPictureFramed};
|
||||
static const GrQuad kIgnored(SkRect::MakeEmpty());
|
||||
|
||||
|
@ -829,7 +829,7 @@ void GrRenderTargetContext::drawTexturedQuad(const GrClip& clip,
|
||||
SkBlendMode blendMode,
|
||||
GrAA aa,
|
||||
DrawQuad* quad,
|
||||
const SkRect* domain) {
|
||||
const SkRect* subset) {
|
||||
ASSERT_SINGLE_OWNER
|
||||
RETURN_IF_ABANDONED
|
||||
SkDEBUGCODE(this->validate();)
|
||||
@ -851,12 +851,12 @@ void GrRenderTargetContext::drawTexturedQuad(const GrClip& clip,
|
||||
auto clampType = GrColorTypeClampType(this->colorInfo().colorType());
|
||||
auto saturate = clampType == GrClampType::kManual ? GrTextureOp::Saturate::kYes
|
||||
: GrTextureOp::Saturate::kNo;
|
||||
// Use the provided domain, although hypothetically we could detect that the cropped local
|
||||
// quad is sufficiently inside the domain and the constraint could be dropped.
|
||||
// Use the provided subset, although hypothetically we could detect that the cropped local
|
||||
// quad is sufficiently inside the subset and the constraint could be dropped.
|
||||
this->addDrawOp(finalClip,
|
||||
GrTextureOp::Make(fContext, std::move(proxyView), srcAlphaType,
|
||||
std::move(textureXform), filter, color, saturate,
|
||||
blendMode, aaType, quad, domain));
|
||||
blendMode, aaType, quad, subset));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -280,30 +280,30 @@ public:
|
||||
const SkRect& srcRect, const SkRect& dstRect, GrAA aa, GrQuadAAFlags edgeAA,
|
||||
SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix,
|
||||
sk_sp<GrColorSpaceXform> texXform) {
|
||||
const SkRect* domain = constraint == SkCanvas::kStrict_SrcRectConstraint ?
|
||||
const SkRect* subset = constraint == SkCanvas::kStrict_SrcRectConstraint ?
|
||||
&srcRect : nullptr;
|
||||
DrawQuad quad{GrQuad::MakeFromRect(dstRect, viewMatrix), GrQuad(srcRect), edgeAA};
|
||||
|
||||
this->drawTexturedQuad(clip, std::move(view), srcAlphaType, std::move(texXform),
|
||||
filter, color, mode, aa, &quad, domain);
|
||||
filter, color, mode, aa, &quad, subset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Variant of drawTexture that instead draws the texture applied to 'dstQuad' transformed by
|
||||
* 'viewMatrix', using the 'srcQuad' texture coordinates clamped to the optional 'domain'. If
|
||||
* 'domain' is null, it's equivalent to using the fast src rect constraint. If 'domain' is
|
||||
* provided, the strict src rect constraint is applied using 'domain'.
|
||||
* 'viewMatrix', using the 'srcQuad' texture coordinates clamped to the optional 'subset'. If
|
||||
* 'subset' is null, it's equivalent to using the fast src rect constraint. If 'subset' is
|
||||
* provided, the strict src rect constraint is applied using 'subset'.
|
||||
*/
|
||||
void drawTextureQuad(const GrClip& clip, GrSurfaceProxyView view, GrColorType srcColorType,
|
||||
SkAlphaType srcAlphaType, GrSamplerState::Filter filter, SkBlendMode mode,
|
||||
const SkPMColor4f& color, const SkPoint srcQuad[4],
|
||||
const SkPoint dstQuad[4], GrAA aa, GrQuadAAFlags edgeAA,
|
||||
const SkRect* domain, const SkMatrix& viewMatrix,
|
||||
const SkRect* subset, const SkMatrix& viewMatrix,
|
||||
sk_sp<GrColorSpaceXform> texXform) {
|
||||
DrawQuad quad{GrQuad::MakeFromSkQuad(dstQuad, viewMatrix),
|
||||
GrQuad::MakeFromSkQuad(srcQuad, SkMatrix::I()), edgeAA};
|
||||
this->drawTexturedQuad(clip, std::move(view), srcAlphaType, std::move(texXform),
|
||||
filter, color, mode, aa, &quad, domain);
|
||||
filter, color, mode, aa, &quad, subset);
|
||||
}
|
||||
|
||||
/** Used with drawTextureSet */
|
||||
@ -669,7 +669,7 @@ private:
|
||||
SkBlendMode blendMode,
|
||||
GrAA aa,
|
||||
DrawQuad* quad,
|
||||
const SkRect* domain = nullptr);
|
||||
const SkRect* subset = nullptr);
|
||||
|
||||
void drawShapeUsingPathRenderer(const GrClip&, GrPaint&&, GrAA, const SkMatrix&,
|
||||
const GrStyledShape&);
|
||||
|
@ -212,7 +212,7 @@ private:
|
||||
fQuads.count());
|
||||
|
||||
return VertexSpec(fQuads.deviceQuadType(), fColorType, fQuads.localQuadType(),
|
||||
fHelper.usesLocalCoords(), GrQuadPerEdgeAA::Domain::kNo,
|
||||
fHelper.usesLocalCoords(), GrQuadPerEdgeAA::Subset::kNo,
|
||||
fHelper.aaType(),
|
||||
fHelper.compatibleWithCoverageAsAlpha(), indexBufferOption);
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ namespace {
|
||||
static void write_quad_generic(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
|
||||
const GrQuad* deviceQuad, const GrQuad* localQuad,
|
||||
const float coverage[4], const SkPMColor4f& color,
|
||||
const SkRect& geomDomain, const SkRect& texDomain) {
|
||||
const SkRect& geomSubset, const SkRect& texSubset) {
|
||||
static constexpr auto If = GrVertexWriter::If<float>;
|
||||
|
||||
SkASSERT(!spec.hasLocalCoords() || localQuad);
|
||||
@ -58,14 +58,14 @@ static void write_quad_generic(GrVertexWriter* vb, const GrQuadPerEdgeAA::Vertex
|
||||
If(spec.localQuadType() == GrQuad::Type::kPerspective, localQuad->w(i)));
|
||||
}
|
||||
|
||||
// save the geometry domain
|
||||
if (spec.requiresGeometryDomain()) {
|
||||
vb->write(geomDomain);
|
||||
// save the geometry subset
|
||||
if (spec.requiresGeometrySubset()) {
|
||||
vb->write(geomSubset);
|
||||
}
|
||||
|
||||
// save the texture domain
|
||||
if (spec.hasDomain()) {
|
||||
vb->write(texDomain);
|
||||
// save the texture subset
|
||||
if (spec.hasSubset()) {
|
||||
vb->write(texSubset);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -73,20 +73,20 @@ static void write_quad_generic(GrVertexWriter* vb, const GrQuadPerEdgeAA::Vertex
|
||||
// Specialized WriteQuadProcs for particular VertexSpecs that show up frequently (determined
|
||||
// experimentally through recorded GMs, SKPs, and SVGs, as well as SkiaRenderer's usage patterns):
|
||||
|
||||
// 2D (XY), no explicit coverage, vertex color, no locals, no geometry domain, no texture domain
|
||||
// 2D (XY), no explicit coverage, vertex color, no locals, no geometry subset, no texture subsetn
|
||||
// This represents simple, solid color or shader, non-AA (or AA with cov. as alpha) rects.
|
||||
static void write_2d_color(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
|
||||
const GrQuad* deviceQuad, const GrQuad* localQuad,
|
||||
const float coverage[4], const SkPMColor4f& color,
|
||||
const SkRect& geomDomain, const SkRect& texDomain) {
|
||||
const SkRect& geomSubset, const SkRect& texSubset) {
|
||||
// Assert assumptions about VertexSpec
|
||||
SkASSERT(spec.deviceQuadType() != GrQuad::Type::kPerspective);
|
||||
SkASSERT(!spec.hasLocalCoords());
|
||||
SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kNone ||
|
||||
spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kWithColor);
|
||||
SkASSERT(spec.hasVertexColors());
|
||||
SkASSERT(!spec.requiresGeometryDomain());
|
||||
SkASSERT(!spec.hasDomain());
|
||||
SkASSERT(!spec.requiresGeometrySubset());
|
||||
SkASSERT(!spec.hasSubset());
|
||||
// We don't assert that localQuad == nullptr, since it is possible for GrFillRectOp to
|
||||
// accumulate local coords conservatively (paint not trivial), and then after analysis realize
|
||||
// the processors don't need local coordinates.
|
||||
@ -100,19 +100,19 @@ static void write_2d_color(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec
|
||||
}
|
||||
}
|
||||
|
||||
// 2D (XY), no explicit coverage, UV locals, no color, no geometry domain, no texture domain
|
||||
// 2D (XY), no explicit coverage, UV locals, no color, no geometry subset, no texture subset
|
||||
// This represents opaque, non AA, textured rects
|
||||
static void write_2d_uv(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
|
||||
const GrQuad* deviceQuad, const GrQuad* localQuad,
|
||||
const float coverage[4], const SkPMColor4f& color,
|
||||
const SkRect& geomDomain, const SkRect& texDomain) {
|
||||
const SkRect& geomSubset, const SkRect& texSubset) {
|
||||
// Assert assumptions about VertexSpec
|
||||
SkASSERT(spec.deviceQuadType() != GrQuad::Type::kPerspective);
|
||||
SkASSERT(spec.hasLocalCoords() && spec.localQuadType() != GrQuad::Type::kPerspective);
|
||||
SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kNone);
|
||||
SkASSERT(!spec.hasVertexColors());
|
||||
SkASSERT(!spec.requiresGeometryDomain());
|
||||
SkASSERT(!spec.hasDomain());
|
||||
SkASSERT(!spec.requiresGeometrySubset());
|
||||
SkASSERT(!spec.hasSubset());
|
||||
SkASSERT(localQuad);
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
@ -120,20 +120,20 @@ static void write_2d_uv(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& s
|
||||
}
|
||||
}
|
||||
|
||||
// 2D (XY), no explicit coverage, UV locals, vertex color, no geometry or texture domains
|
||||
// 2D (XY), no explicit coverage, UV locals, vertex color, no geometry or texture subsets
|
||||
// This represents transparent, non AA (or AA with cov. as alpha), textured rects
|
||||
static void write_2d_color_uv(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
|
||||
const GrQuad* deviceQuad, const GrQuad* localQuad,
|
||||
const float coverage[4], const SkPMColor4f& color,
|
||||
const SkRect& geomDomain, const SkRect& texDomain) {
|
||||
const SkRect& geomSubset, const SkRect& texSubset) {
|
||||
// Assert assumptions about VertexSpec
|
||||
SkASSERT(spec.deviceQuadType() != GrQuad::Type::kPerspective);
|
||||
SkASSERT(spec.hasLocalCoords() && spec.localQuadType() != GrQuad::Type::kPerspective);
|
||||
SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kNone ||
|
||||
spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kWithColor);
|
||||
SkASSERT(spec.hasVertexColors());
|
||||
SkASSERT(!spec.requiresGeometryDomain());
|
||||
SkASSERT(!spec.hasDomain());
|
||||
SkASSERT(!spec.requiresGeometrySubset());
|
||||
SkASSERT(!spec.hasSubset());
|
||||
SkASSERT(localQuad);
|
||||
|
||||
bool wide = spec.colorType() == GrQuadPerEdgeAA::ColorType::kFloat;
|
||||
@ -146,19 +146,19 @@ static void write_2d_color_uv(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexS
|
||||
}
|
||||
}
|
||||
|
||||
// 2D (XY), explicit coverage, UV locals, no color, no geometry domain, no texture domain
|
||||
// 2D (XY), explicit coverage, UV locals, no color, no geometry subset, no texture subset
|
||||
// This represents opaque, AA, textured rects
|
||||
static void write_2d_cov_uv(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
|
||||
const GrQuad* deviceQuad, const GrQuad* localQuad,
|
||||
const float coverage[4], const SkPMColor4f& color,
|
||||
const SkRect& geomDomain, const SkRect& texDomain) {
|
||||
const SkRect& geomSubset, const SkRect& texSubset) {
|
||||
// Assert assumptions about VertexSpec
|
||||
SkASSERT(spec.deviceQuadType() != GrQuad::Type::kPerspective);
|
||||
SkASSERT(spec.hasLocalCoords() && spec.localQuadType() != GrQuad::Type::kPerspective);
|
||||
SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kWithPosition);
|
||||
SkASSERT(!spec.hasVertexColors());
|
||||
SkASSERT(!spec.requiresGeometryDomain());
|
||||
SkASSERT(!spec.hasDomain());
|
||||
SkASSERT(!spec.requiresGeometrySubset());
|
||||
SkASSERT(!spec.hasSubset());
|
||||
SkASSERT(localQuad);
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
@ -168,45 +168,45 @@ static void write_2d_cov_uv(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpe
|
||||
}
|
||||
|
||||
// NOTE: The three _strict specializations below match the non-strict uv functions above, except
|
||||
// that they also write the UV domain. These are included to benefit SkiaRenderer, which must make
|
||||
// use of both fast and strict constrained domains. When testing _strict was not that common across
|
||||
// that they also write the UV subset. These are included to benefit SkiaRenderer, which must make
|
||||
// use of both fast and strict constrained subsets. When testing _strict was not that common across
|
||||
// GMS, SKPs, and SVGs but we have little visibility into actual SkiaRenderer statistics. If
|
||||
// SkiaRenderer can avoid domains more, these 3 functions should probably be removed for simplicity.
|
||||
// SkiaRenderer can avoid subsets more, these 3 functions should probably be removed for simplicity.
|
||||
|
||||
// 2D (XY), no explicit coverage, UV locals, no color, tex domain but no geometry domain
|
||||
// 2D (XY), no explicit coverage, UV locals, no color, tex subset but no geometry subset
|
||||
// This represents opaque, non AA, textured rects with strict uv sampling
|
||||
static void write_2d_uv_strict(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
|
||||
const GrQuad* deviceQuad, const GrQuad* localQuad,
|
||||
const float coverage[4], const SkPMColor4f& color,
|
||||
const SkRect& geomDomain, const SkRect& texDomain) {
|
||||
const SkRect& geomSubset, const SkRect& texSubset) {
|
||||
// Assert assumptions about VertexSpec
|
||||
SkASSERT(spec.deviceQuadType() != GrQuad::Type::kPerspective);
|
||||
SkASSERT(spec.hasLocalCoords() && spec.localQuadType() != GrQuad::Type::kPerspective);
|
||||
SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kNone);
|
||||
SkASSERT(!spec.hasVertexColors());
|
||||
SkASSERT(!spec.requiresGeometryDomain());
|
||||
SkASSERT(spec.hasDomain());
|
||||
SkASSERT(!spec.requiresGeometrySubset());
|
||||
SkASSERT(spec.hasSubset());
|
||||
SkASSERT(localQuad);
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
vb->write(deviceQuad->x(i), deviceQuad->y(i), localQuad->x(i), localQuad->y(i), texDomain);
|
||||
vb->write(deviceQuad->x(i), deviceQuad->y(i), localQuad->x(i), localQuad->y(i), texSubset);
|
||||
}
|
||||
}
|
||||
|
||||
// 2D (XY), no explicit coverage, UV locals, vertex color, tex domain but no geometry domain
|
||||
// 2D (XY), no explicit coverage, UV locals, vertex color, tex subset but no geometry subset
|
||||
// This represents transparent, non AA (or AA with cov. as alpha), textured rects with strict sample
|
||||
static void write_2d_color_uv_strict(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
|
||||
const GrQuad* deviceQuad, const GrQuad* localQuad,
|
||||
const float coverage[4], const SkPMColor4f& color,
|
||||
const SkRect& geomDomain, const SkRect& texDomain) {
|
||||
const SkRect& geomSubset, const SkRect& texSubset) {
|
||||
// Assert assumptions about VertexSpec
|
||||
SkASSERT(spec.deviceQuadType() != GrQuad::Type::kPerspective);
|
||||
SkASSERT(spec.hasLocalCoords() && spec.localQuadType() != GrQuad::Type::kPerspective);
|
||||
SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kNone ||
|
||||
spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kWithColor);
|
||||
SkASSERT(spec.hasVertexColors());
|
||||
SkASSERT(!spec.requiresGeometryDomain());
|
||||
SkASSERT(spec.hasDomain());
|
||||
SkASSERT(!spec.requiresGeometrySubset());
|
||||
SkASSERT(spec.hasSubset());
|
||||
SkASSERT(localQuad);
|
||||
|
||||
bool wide = spec.colorType() == GrQuadPerEdgeAA::ColorType::kFloat;
|
||||
@ -215,28 +215,28 @@ static void write_2d_color_uv_strict(GrVertexWriter* vb, const GrQuadPerEdgeAA::
|
||||
SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kWithColor ||
|
||||
coverage[i] == 1.f);
|
||||
vb->write(deviceQuad->x(i), deviceQuad->y(i), GrVertexColor(color * coverage[i], wide),
|
||||
localQuad->x(i), localQuad->y(i), texDomain);
|
||||
localQuad->x(i), localQuad->y(i), texSubset);
|
||||
}
|
||||
}
|
||||
|
||||
// 2D (XY), explicit coverage, UV locals, no color, tex domain but no geometry domain
|
||||
// 2D (XY), explicit coverage, UV locals, no color, tex subset but no geometry subset
|
||||
// This represents opaque, AA, textured rects with strict uv sampling
|
||||
static void write_2d_cov_uv_strict(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
|
||||
const GrQuad* deviceQuad, const GrQuad* localQuad,
|
||||
const float coverage[4], const SkPMColor4f& color,
|
||||
const SkRect& geomDomain, const SkRect& texDomain) {
|
||||
const SkRect& geomSubset, const SkRect& texSubset) {
|
||||
// Assert assumptions about VertexSpec
|
||||
SkASSERT(spec.deviceQuadType() != GrQuad::Type::kPerspective);
|
||||
SkASSERT(spec.hasLocalCoords() && spec.localQuadType() != GrQuad::Type::kPerspective);
|
||||
SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kWithPosition);
|
||||
SkASSERT(!spec.hasVertexColors());
|
||||
SkASSERT(!spec.requiresGeometryDomain());
|
||||
SkASSERT(spec.hasDomain());
|
||||
SkASSERT(!spec.requiresGeometrySubset());
|
||||
SkASSERT(spec.hasSubset());
|
||||
SkASSERT(localQuad);
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
vb->write(deviceQuad->x(i), deviceQuad->y(i), coverage[i],
|
||||
localQuad->x(i), localQuad->y(i), texDomain);
|
||||
localQuad->x(i), localQuad->y(i), texSubset);
|
||||
}
|
||||
}
|
||||
|
||||
@ -266,10 +266,10 @@ ColorType MinColorType(SkPMColor4f color) {
|
||||
////////////////// Tessellator Implementation
|
||||
|
||||
Tessellator::WriteQuadProc Tessellator::GetWriteQuadProc(const VertexSpec& spec) {
|
||||
// All specialized writing functions requires 2D geometry and no geometry domain. This is not
|
||||
// All specialized writing functions requires 2D geometry and no geometry subset. This is not
|
||||
// the same as just checking device type vs. kRectilinear since non-AA general 2D quads do not
|
||||
// require a geometry domain and could then go through a fast path.
|
||||
if (spec.deviceQuadType() != GrQuad::Type::kPerspective && !spec.requiresGeometryDomain()) {
|
||||
// require a geometry subset and could then go through a fast path.
|
||||
if (spec.deviceQuadType() != GrQuad::Type::kPerspective && !spec.requiresGeometrySubset()) {
|
||||
CoverageMode mode = spec.coverageMode();
|
||||
if (spec.hasVertexColors()) {
|
||||
if (mode != CoverageMode::kWithPosition) {
|
||||
@ -279,7 +279,7 @@ Tessellator::WriteQuadProc Tessellator::GetWriteQuadProc(const VertexSpec& spec)
|
||||
return write_2d_color;
|
||||
} else if (spec.localQuadType() != GrQuad::Type::kPerspective) {
|
||||
// UV locals with vertex colors (possibly with coverage-as-alpha)
|
||||
return spec.hasDomain() ? write_2d_color_uv_strict : write_2d_color_uv;
|
||||
return spec.hasSubset() ? write_2d_color_uv_strict : write_2d_color_uv;
|
||||
}
|
||||
}
|
||||
// Else fall through; this is a spec that requires vertex colors and explicit coverage,
|
||||
@ -288,10 +288,10 @@ Tessellator::WriteQuadProc Tessellator::GetWriteQuadProc(const VertexSpec& spec)
|
||||
} else if (spec.hasLocalCoords() && spec.localQuadType() != GrQuad::Type::kPerspective) {
|
||||
if (mode == CoverageMode::kWithPosition) {
|
||||
// UV locals with explicit coverage
|
||||
return spec.hasDomain() ? write_2d_cov_uv_strict : write_2d_cov_uv;
|
||||
return spec.hasSubset() ? write_2d_cov_uv_strict : write_2d_cov_uv;
|
||||
} else {
|
||||
SkASSERT(mode == CoverageMode::kNone);
|
||||
return spec.hasDomain() ? write_2d_uv_strict : write_2d_uv;
|
||||
return spec.hasSubset() ? write_2d_uv_strict : write_2d_uv;
|
||||
}
|
||||
}
|
||||
// Else fall through to generic vertex function; this is a spec that has no vertex colors
|
||||
@ -308,7 +308,7 @@ Tessellator::Tessellator(const VertexSpec& spec, char* vertices)
|
||||
, fWriteProc(Tessellator::GetWriteQuadProc(spec)) {}
|
||||
|
||||
void Tessellator::append(GrQuad* deviceQuad, GrQuad* localQuad,
|
||||
const SkPMColor4f& color, const SkRect& uvDomain, GrQuadAAFlags aaFlags) {
|
||||
const SkPMColor4f& color, const SkRect& uvSubset, GrQuadAAFlags aaFlags) {
|
||||
// We allow Tessellator to be created with a null vertices pointer for convenience, but it is
|
||||
// assumed it will never actually be used in those cases.
|
||||
SkASSERT(fVertexWriter.fPtr);
|
||||
@ -318,28 +318,28 @@ void Tessellator::append(GrQuad* deviceQuad, GrQuad* localQuad,
|
||||
|
||||
static const float kFullCoverage[4] = {1.f, 1.f, 1.f, 1.f};
|
||||
static const float kZeroCoverage[4] = {0.f, 0.f, 0.f, 0.f};
|
||||
static const SkRect kIgnoredDomain = SkRect::MakeEmpty();
|
||||
static const SkRect kIgnoredSubset = SkRect::MakeEmpty();
|
||||
|
||||
if (fVertexSpec.usesCoverageAA()) {
|
||||
SkASSERT(fVertexSpec.coverageMode() == CoverageMode::kWithColor ||
|
||||
fVertexSpec.coverageMode() == CoverageMode::kWithPosition);
|
||||
// Must calculate inner and outer quadrilaterals for the vertex coverage ramps, and possibly
|
||||
// a geometry domain if corners are not right angles
|
||||
SkRect geomDomain;
|
||||
if (fVertexSpec.requiresGeometryDomain()) {
|
||||
geomDomain = deviceQuad->bounds();
|
||||
geomDomain.outset(0.5f, 0.5f); // account for AA expansion
|
||||
// a geometry subset if corners are not right angles
|
||||
SkRect geomSubset;
|
||||
if (fVertexSpec.requiresGeometrySubset()) {
|
||||
geomSubset = deviceQuad->bounds();
|
||||
geomSubset.outset(0.5f, 0.5f); // account for AA expansion
|
||||
}
|
||||
|
||||
if (aaFlags == GrQuadAAFlags::kNone) {
|
||||
// Have to write the coverage AA vertex structure, but there's no math to be done for a
|
||||
// non-aa quad batched into a coverage AA op.
|
||||
fWriteProc(&fVertexWriter, fVertexSpec, deviceQuad, localQuad, kFullCoverage, color,
|
||||
geomDomain, uvDomain);
|
||||
geomSubset, uvSubset);
|
||||
// Since we pass the same corners in, the outer vertex structure will have 0 area and
|
||||
// the coverage interpolation from 1 to 0 will not be visible.
|
||||
fWriteProc(&fVertexWriter, fVertexSpec, deviceQuad, localQuad, kZeroCoverage, color,
|
||||
geomDomain, uvDomain);
|
||||
geomSubset, uvSubset);
|
||||
} else {
|
||||
// Reset the tessellation helper to match the current geometry
|
||||
fAAHelper.reset(*deviceQuad, localQuad);
|
||||
@ -360,19 +360,19 @@ void Tessellator::append(GrQuad* deviceQuad, GrQuad* localQuad,
|
||||
float coverage[4];
|
||||
fAAHelper.inset(edgeDistances, deviceQuad, localQuad).store(coverage);
|
||||
fWriteProc(&fVertexWriter, fVertexSpec, deviceQuad, localQuad, coverage, color,
|
||||
geomDomain, uvDomain);
|
||||
geomSubset, uvSubset);
|
||||
|
||||
// Then outer vertices, which use 0.f for their coverage
|
||||
fAAHelper.outset(edgeDistances, deviceQuad, localQuad);
|
||||
fWriteProc(&fVertexWriter, fVertexSpec, deviceQuad, localQuad, kZeroCoverage, color,
|
||||
geomDomain, uvDomain);
|
||||
geomSubset, uvSubset);
|
||||
}
|
||||
} else {
|
||||
// No outsetting needed, just write a single quad with full coverage
|
||||
SkASSERT(fVertexSpec.coverageMode() == CoverageMode::kNone &&
|
||||
!fVertexSpec.requiresGeometryDomain());
|
||||
!fVertexSpec.requiresGeometrySubset());
|
||||
fWriteProc(&fVertexWriter, fVertexSpec, deviceQuad, localQuad, kFullCoverage, color,
|
||||
kIgnoredDomain, uvDomain);
|
||||
kIgnoredSubset, uvSubset);
|
||||
}
|
||||
}
|
||||
|
||||
@ -459,10 +459,10 @@ int VertexSpec::localDimensionality() const {
|
||||
CoverageMode VertexSpec::coverageMode() const {
|
||||
if (this->usesCoverageAA()) {
|
||||
if (this->compatibleWithCoverageAsAlpha() && this->hasVertexColors() &&
|
||||
!this->requiresGeometryDomain()) {
|
||||
// Using a geometric domain acts as a second source of coverage and folding
|
||||
!this->requiresGeometrySubset()) {
|
||||
// Using a geometric subset acts as a second source of coverage and folding
|
||||
// the original coverage into color makes it impossible to apply the color's
|
||||
// alpha to the geometric domain's coverage when the original shape is clipped.
|
||||
// alpha to the geometric subset's coverage when the original shape is clipped.
|
||||
return CoverageMode::kWithColor;
|
||||
} else {
|
||||
return CoverageMode::kWithPosition;
|
||||
@ -494,7 +494,7 @@ size_t VertexSpec::vertexSize() const {
|
||||
}
|
||||
}
|
||||
|
||||
if (this->requiresGeometryDomain()) {
|
||||
if (this->requiresGeometrySubset()) {
|
||||
count += GrVertexAttribTypeSize(kFloat4_GrVertexAttribType);
|
||||
}
|
||||
|
||||
@ -506,7 +506,7 @@ size_t VertexSpec::vertexSize() const {
|
||||
count += GrVertexAttribTypeSize(kFloat4_GrVertexAttribType);
|
||||
}
|
||||
|
||||
if (this->hasDomain()) {
|
||||
if (this->hasSubset()) {
|
||||
count += GrVertexAttribTypeSize(kFloat4_GrVertexAttribType);
|
||||
}
|
||||
|
||||
@ -540,7 +540,7 @@ public:
|
||||
|
||||
void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
|
||||
// texturing, device-dimensions are single bit flags
|
||||
uint32_t x = (fTexDomain.isInitialized() ? 0 : 0x1)
|
||||
uint32_t x = (fTexSubset.isInitialized() ? 0 : 0x1)
|
||||
| (fSampler.isInitialized() ? 0 : 0x2)
|
||||
| (fNeedsPerspective ? 0 : 0x4)
|
||||
| (fSaturate == Saturate::kNo ? 0 : 0x8);
|
||||
@ -553,10 +553,10 @@ public:
|
||||
x |= kUByte4_norm_GrVertexAttribType == fColor.cpuType() ? 0x40 : 0x80;
|
||||
}
|
||||
// and coverage mode, 00 for none, 01 for withposition, 10 for withcolor, 11 for
|
||||
// position+geomdomain
|
||||
SkASSERT(!fGeomDomain.isInitialized() || fCoverageMode == CoverageMode::kWithPosition);
|
||||
// position+geomsubset
|
||||
SkASSERT(!fGeomSubset.isInitialized() || fCoverageMode == CoverageMode::kWithPosition);
|
||||
if (fCoverageMode != CoverageMode::kNone) {
|
||||
x |= fGeomDomain.isInitialized()
|
||||
x |= fGeomSubset.isInitialized()
|
||||
? 0x300
|
||||
: (CoverageMode::kWithPosition == fCoverageMode ? 0x100 : 0x200);
|
||||
}
|
||||
@ -632,7 +632,7 @@ public:
|
||||
// If there is a texture, must also handle texture coordinates and reading from
|
||||
// the texture in the fragment shader before continuing to fragment processors.
|
||||
if (gp.fSampler.isInitialized()) {
|
||||
// Texture coordinates clamped by the domain on the fragment shader; if the GP
|
||||
// Texture coordinates clamped by the subset on the fragment shader; if the GP
|
||||
// has a texture, it's guaranteed to have local coordinates
|
||||
args.fFragBuilder->codeAppend("float2 texCoord;");
|
||||
if (gp.fLocalCoord.cpuType() == kFloat3_GrVertexAttribType) {
|
||||
@ -647,13 +647,13 @@ public:
|
||||
args.fVaryingHandler->addPassThroughAttribute(gp.fLocalCoord, "texCoord");
|
||||
}
|
||||
|
||||
// Clamp the now 2D localCoordName variable by the domain if it is provided
|
||||
if (gp.fTexDomain.isInitialized()) {
|
||||
args.fFragBuilder->codeAppend("float4 domain;");
|
||||
args.fVaryingHandler->addPassThroughAttribute(gp.fTexDomain, "domain",
|
||||
// Clamp the now 2D localCoordName variable by the subset if it is provided
|
||||
if (gp.fTexSubset.isInitialized()) {
|
||||
args.fFragBuilder->codeAppend("float4 subset;");
|
||||
args.fVaryingHandler->addPassThroughAttribute(gp.fTexSubset, "subset",
|
||||
Interpolation::kCanBeFlat);
|
||||
args.fFragBuilder->codeAppend(
|
||||
"texCoord = clamp(texCoord, domain.xy, domain.zw);");
|
||||
"texCoord = clamp(texCoord, subset.xy, subset.zw);");
|
||||
}
|
||||
|
||||
// Now modulate the starting output color by the texture lookup
|
||||
@ -690,18 +690,18 @@ public:
|
||||
args.fFragBuilder->codeAppendf("float coverage = %s;", coverage.fsIn());
|
||||
}
|
||||
|
||||
if (gp.fGeomDomain.isInitialized()) {
|
||||
// Calculate distance from sk_FragCoord to the 4 edges of the domain
|
||||
if (gp.fGeomSubset.isInitialized()) {
|
||||
// Calculate distance from sk_FragCoord to the 4 edges of the subset
|
||||
// and clamp them to (0, 1). Use the minimum of these and the original
|
||||
// coverage. This only has to be done in the exterior triangles, the
|
||||
// interior of the quad geometry can never be clipped by the domain box.
|
||||
args.fFragBuilder->codeAppend("float4 geoDomain;");
|
||||
args.fVaryingHandler->addPassThroughAttribute(gp.fGeomDomain, "geoDomain",
|
||||
// interior of the quad geometry can never be clipped by the subset box.
|
||||
args.fFragBuilder->codeAppend("float4 geoSubset;");
|
||||
args.fVaryingHandler->addPassThroughAttribute(gp.fGeomSubset, "geoSubset",
|
||||
Interpolation::kCanBeFlat);
|
||||
args.fFragBuilder->codeAppend(
|
||||
"if (coverage < 0.5) {"
|
||||
" float4 dists4 = clamp(float4(1, 1, -1, -1) * "
|
||||
"(sk_FragCoord.xyxy - geoDomain), 0, 1);"
|
||||
"(sk_FragCoord.xyxy - geoSubset), 0, 1);"
|
||||
" float2 dists2 = dists4.xy * dists4.zw;"
|
||||
" coverage = min(coverage, dists2.x * dists2.y);"
|
||||
"}");
|
||||
@ -712,7 +712,7 @@ public:
|
||||
} else {
|
||||
// Set coverage to 1, since it's either non-AA or the coverage was already
|
||||
// folded into the output color
|
||||
SkASSERT(!gp.fGeomDomain.isInitialized());
|
||||
SkASSERT(!gp.fGeomSubset.isInitialized());
|
||||
args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputCoverage);
|
||||
}
|
||||
}
|
||||
@ -727,7 +727,7 @@ private:
|
||||
QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec)
|
||||
: INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
|
||||
, fTextureColorSpaceXform(nullptr) {
|
||||
SkASSERT(!spec.hasDomain());
|
||||
SkASSERT(!spec.hasSubset());
|
||||
this->initializeAttrs(spec);
|
||||
this->setTextureSamplerCnt(0);
|
||||
}
|
||||
@ -768,10 +768,10 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
// Need a geometry domain when the quads are AA and not rectilinear, since their AA
|
||||
// Need a geometry subset when the quads are AA and not rectilinear, since their AA
|
||||
// outsetting can go beyond a half pixel.
|
||||
if (spec.requiresGeometryDomain()) {
|
||||
fGeomDomain = {"geomDomain", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
|
||||
if (spec.requiresGeometrySubset()) {
|
||||
fGeomSubset = {"geomSubset", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
|
||||
}
|
||||
|
||||
int localDim = spec.localDimensionality();
|
||||
@ -785,8 +785,8 @@ private:
|
||||
fColor = MakeColorAttribute("color", ColorType::kFloat == spec.colorType());
|
||||
}
|
||||
|
||||
if (spec.hasDomain()) {
|
||||
fTexDomain = {"texDomain", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
|
||||
if (spec.hasSubset()) {
|
||||
fTexSubset = {"texSubset", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
|
||||
}
|
||||
|
||||
this->setVertexAttributes(&fPosition, 6);
|
||||
@ -798,8 +798,8 @@ private:
|
||||
Attribute fCoverage; // Used for non-perspective position to avoid Intel Metal issues
|
||||
Attribute fColor; // May have coverage modulated in if the FPs support it
|
||||
Attribute fLocalCoord;
|
||||
Attribute fGeomDomain; // Screen-space bounding box on geometry+aa outset
|
||||
Attribute fTexDomain; // Texture-space bounding box on local coords
|
||||
Attribute fGeomSubset; // Screen-space bounding box on geometry+aa outset
|
||||
Attribute fTexSubset; // Texture-space bounding box on local coords
|
||||
|
||||
// The positions attribute may have coverage built into it, so float3 is an ambiguous type
|
||||
// and may mean 2d with coverage, or 3d with no coverage
|
||||
|
@ -29,7 +29,7 @@ namespace GrQuadPerEdgeAA {
|
||||
using Saturate = GrTextureOp::Saturate;
|
||||
|
||||
enum class CoverageMode { kNone, kWithPosition, kWithColor };
|
||||
enum class Domain : bool { kNo = false, kYes = true };
|
||||
enum class Subset : bool { kNo = false, kYes = true };
|
||||
enum class ColorType { kNone, kByte, kFloat, kLast = kFloat };
|
||||
static const int kColorTypeCount = static_cast<int>(ColorType::kLast) + 1;
|
||||
|
||||
@ -47,7 +47,7 @@ namespace GrQuadPerEdgeAA {
|
||||
ColorType MinColorType(SkPMColor4f);
|
||||
|
||||
// Specifies the vertex configuration for an op that renders per-edge AA quads. The vertex
|
||||
// order (when enabled) is device position, color, local position, domain, aa edge equations.
|
||||
// order (when enabled) is device position, color, local position, subset, aa edge equations.
|
||||
// This order matches the constructor argument order of VertexSpec and is the order that
|
||||
// GPAttributes maintains. If hasLocalCoords is false, then the local quad type can be ignored.
|
||||
struct VertexSpec {
|
||||
@ -58,23 +58,24 @@ namespace GrQuadPerEdgeAA {
|
||||
, fIndexBufferOption(0) // kPictureFramed
|
||||
, fHasLocalCoords(false)
|
||||
, fColorType(0) // kNone
|
||||
, fHasDomain(false)
|
||||
, fHasSubset(false)
|
||||
, fUsesCoverageAA(false)
|
||||
, fCompatibleWithCoverageAsAlpha(false)
|
||||
, fRequiresGeometryDomain(false) {}
|
||||
, fRequiresGeometrySubset(false) {}
|
||||
|
||||
VertexSpec(GrQuad::Type deviceQuadType, ColorType colorType, GrQuad::Type localQuadType,
|
||||
bool hasLocalCoords, Domain domain, GrAAType aa, bool coverageAsAlpha,
|
||||
bool hasLocalCoords,
|
||||
Subset subset, GrAAType aa, bool coverageAsAlpha,
|
||||
IndexBufferOption indexBufferOption)
|
||||
: fDeviceQuadType(static_cast<unsigned>(deviceQuadType))
|
||||
, fLocalQuadType(static_cast<unsigned>(localQuadType))
|
||||
, fIndexBufferOption(static_cast<unsigned>(indexBufferOption))
|
||||
, fHasLocalCoords(hasLocalCoords)
|
||||
, fColorType(static_cast<unsigned>(colorType))
|
||||
, fHasDomain(static_cast<unsigned>(domain))
|
||||
, fHasSubset(static_cast<unsigned>(subset))
|
||||
, fUsesCoverageAA(aa == GrAAType::kCoverage)
|
||||
, fCompatibleWithCoverageAsAlpha(coverageAsAlpha)
|
||||
, fRequiresGeometryDomain(aa == GrAAType::kCoverage &&
|
||||
, fRequiresGeometrySubset(aa == GrAAType::kCoverage &&
|
||||
deviceQuadType > GrQuad::Type::kRectilinear) { }
|
||||
|
||||
GrQuad::Type deviceQuadType() const { return static_cast<GrQuad::Type>(fDeviceQuadType); }
|
||||
@ -85,10 +86,10 @@ namespace GrQuadPerEdgeAA {
|
||||
bool hasLocalCoords() const { return fHasLocalCoords; }
|
||||
ColorType colorType() const { return static_cast<ColorType>(fColorType); }
|
||||
bool hasVertexColors() const { return ColorType::kNone != this->colorType(); }
|
||||
bool hasDomain() const { return fHasDomain; }
|
||||
bool hasSubset() const { return fHasSubset; }
|
||||
bool usesCoverageAA() const { return fUsesCoverageAA; }
|
||||
bool compatibleWithCoverageAsAlpha() const { return fCompatibleWithCoverageAsAlpha; }
|
||||
bool requiresGeometryDomain() const { return fRequiresGeometryDomain; }
|
||||
bool requiresGeometrySubset() const { return fRequiresGeometrySubset; }
|
||||
// Will always be 2 or 3
|
||||
int deviceDimensionality() const;
|
||||
// Will always be 0 if hasLocalCoords is false, otherwise will be 2 or 3
|
||||
@ -122,12 +123,12 @@ namespace GrQuadPerEdgeAA {
|
||||
unsigned fIndexBufferOption: 2;
|
||||
unsigned fHasLocalCoords: 1;
|
||||
unsigned fColorType : 2;
|
||||
unsigned fHasDomain: 1;
|
||||
unsigned fHasSubset : 1;
|
||||
unsigned fUsesCoverageAA: 1;
|
||||
unsigned fCompatibleWithCoverageAsAlpha: 1;
|
||||
// The geometry domain serves to clip off pixels touched by quads with sharp corners that
|
||||
// The geometry subset serves to clip off pixels touched by quads with sharp corners that
|
||||
// would otherwise exceed the miter limit for the AA-outset geometry.
|
||||
unsigned fRequiresGeometryDomain: 1;
|
||||
unsigned fRequiresGeometrySubset : 1;
|
||||
};
|
||||
|
||||
// A Tessellator is responsible for processing a series of device+local GrQuads into a VBO,
|
||||
@ -143,7 +144,7 @@ namespace GrQuadPerEdgeAA {
|
||||
// damage the provided GrQuads (as this is intended to work with GrQuadBuffer::Iter).
|
||||
// 'localQuad' can be null if the VertexSpec does not use local coords.
|
||||
void append(GrQuad* deviceQuad, GrQuad* localQuad,
|
||||
const SkPMColor4f& color, const SkRect& uvDomain, GrQuadAAFlags aaFlags);
|
||||
const SkPMColor4f& color, const SkRect& uvSubset, GrQuadAAFlags aaFlags);
|
||||
|
||||
SkDEBUGCODE(char* vertices() const { return (char*) fVertexWriter.fPtr; })
|
||||
|
||||
@ -155,7 +156,7 @@ namespace GrQuadPerEdgeAA {
|
||||
typedef void (*WriteQuadProc)(GrVertexWriter* vertices, const VertexSpec& spec,
|
||||
const GrQuad* deviceQuad, const GrQuad* localQuad,
|
||||
const float coverage[4], const SkPMColor4f& color,
|
||||
const SkRect& geomDomain, const SkRect& texDomain);
|
||||
const SkRect& geomSubset, const SkRect& texSubset);
|
||||
static WriteQuadProc GetWriteQuadProc(const VertexSpec& spec);
|
||||
|
||||
GrQuadUtils::TessellationHelper fAAHelper;
|
||||
|
@ -43,7 +43,7 @@
|
||||
|
||||
namespace {
|
||||
|
||||
using Domain = GrQuadPerEdgeAA::Domain;
|
||||
using Subset = GrQuadPerEdgeAA::Subset;
|
||||
using VertexSpec = GrQuadPerEdgeAA::VertexSpec;
|
||||
using ColorType = GrQuadPerEdgeAA::ColorType;
|
||||
|
||||
@ -120,30 +120,30 @@ static NormalizationParams proxy_normalization_params(const GrSurfaceProxy* prox
|
||||
}
|
||||
}
|
||||
|
||||
static SkRect inset_domain_for_bilerp(const NormalizationParams& params, const SkRect& domainRect) {
|
||||
static SkRect inset_subset_for_bilerp(const NormalizationParams& params, const SkRect& subsetRect) {
|
||||
// Normalized pixel size is also equal to iw and ih, so the insets for bilerp are just
|
||||
// in those units and can be applied safely after normalization. However, if the domain is
|
||||
// in those units and can be applied safely after normalization. However, if the subset is
|
||||
// smaller than a texel, it should clamp to the center of that axis.
|
||||
float dw = domainRect.width() < params.fIW ? domainRect.width() : params.fIW;
|
||||
float dh = domainRect.height() < params.fIH ? domainRect.height() : params.fIH;
|
||||
return domainRect.makeInset(0.5f * dw, 0.5f * dh);
|
||||
float dw = subsetRect.width() < params.fIW ? subsetRect.width() : params.fIW;
|
||||
float dh = subsetRect.height() < params.fIH ? subsetRect.height() : params.fIH;
|
||||
return subsetRect.makeInset(0.5f * dw, 0.5f * dh);
|
||||
}
|
||||
|
||||
// Normalize the domain. If 'domainRect' is null, it is assumed no domain constraint is desired,
|
||||
// Normalize the subset. If 'subsetRect' is null, it is assumed no subset constraint is desired,
|
||||
// so a sufficiently large rect is returned even if the quad ends up batched with an op that uses
|
||||
// domains overall.
|
||||
static SkRect normalize_domain(GrSamplerState::Filter filter,
|
||||
// subsets overall.
|
||||
static SkRect normalize_subset(GrSamplerState::Filter filter,
|
||||
const NormalizationParams& params,
|
||||
const SkRect* domainRect) {
|
||||
const SkRect* subsetRect) {
|
||||
static constexpr SkRect kLargeRect = {-100000, -100000, 1000000, 1000000};
|
||||
if (!domainRect) {
|
||||
// Either the quad has no domain constraint and is batched with a domain constrained op
|
||||
// (in which case we want a domain that doesn't restrict normalized tex coords), or the
|
||||
// entire op doesn't use the domain, in which case the returned value is ignored.
|
||||
if (!subsetRect) {
|
||||
// Either the quad has no subset constraint and is batched with a subset constrained op
|
||||
// (in which case we want a subset that doesn't restrict normalized tex coords), or the
|
||||
// entire op doesn't use the subset, in which case the returned value is ignored.
|
||||
return kLargeRect;
|
||||
}
|
||||
|
||||
auto ltrb = skvx::Vec<4, float>::Load(domainRect);
|
||||
auto ltrb = skvx::Vec<4, float>::Load(subsetRect);
|
||||
// Normalize and offset
|
||||
ltrb = mad(ltrb, {params.fIW, params.fIH, params.fIW, params.fIH},
|
||||
{0.f, params.fYOffset, 0.f, params.fYOffset});
|
||||
@ -197,10 +197,10 @@ public:
|
||||
GrTextureOp::Saturate saturate,
|
||||
GrAAType aaType,
|
||||
DrawQuad* quad,
|
||||
const SkRect* domain) {
|
||||
const SkRect* subset) {
|
||||
GrOpMemoryPool* pool = context->priv().opMemoryPool();
|
||||
return pool->allocate<TextureOp>(std::move(proxyView), std::move(textureXform), filter,
|
||||
color, saturate, aaType, quad, domain);
|
||||
color, saturate, aaType, quad, subset);
|
||||
}
|
||||
|
||||
static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
|
||||
@ -255,13 +255,13 @@ public:
|
||||
while(i < fViewCountPairs[p].fQuadCnt && iter.next()) {
|
||||
const GrQuad* quad = iter.deviceQuad();
|
||||
GrQuad uv = iter.isLocalValid() ? *(iter.localQuad()) : GrQuad();
|
||||
const ColorDomainAndAA& info = iter.metadata();
|
||||
const ColorSubsetAndAA& info = iter.metadata();
|
||||
str.appendf(
|
||||
"%d: Color: 0x%08x, Domain(%d): [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n"
|
||||
"%d: Color: 0x%08x, Subset(%d): [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n"
|
||||
" UVs [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n"
|
||||
" Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
|
||||
i, info.fColor.toBytes_RGBA(), fMetadata.fDomain, info.fDomainRect.fLeft,
|
||||
info.fDomainRect.fTop, info.fDomainRect.fRight, info.fDomainRect.fBottom,
|
||||
i, info.fColor.toBytes_RGBA(), fMetadata.fSubset, info.fSubsetRect.fLeft,
|
||||
info.fSubsetRect.fTop, info.fSubsetRect.fRight, info.fSubsetRect.fBottom,
|
||||
quad->point(0).fX, quad->point(0).fY, quad->point(1).fX, quad->point(1).fY,
|
||||
quad->point(2).fX, quad->point(2).fY, quad->point(3).fX, quad->point(3).fY,
|
||||
uv.point(0).fX, uv.point(0).fY, uv.point(1).fX, uv.point(1).fY,
|
||||
@ -309,18 +309,18 @@ public:
|
||||
private:
|
||||
friend class ::GrOpMemoryPool;
|
||||
|
||||
struct ColorDomainAndAA {
|
||||
ColorDomainAndAA(const SkPMColor4f& color, const SkRect& domainRect, GrQuadAAFlags aaFlags)
|
||||
struct ColorSubsetAndAA {
|
||||
ColorSubsetAndAA(const SkPMColor4f& color, const SkRect& subsetRect, GrQuadAAFlags aaFlags)
|
||||
: fColor(color)
|
||||
, fDomainRect(domainRect)
|
||||
, fSubsetRect(subsetRect)
|
||||
, fAAFlags(static_cast<uint16_t>(aaFlags)) {
|
||||
SkASSERT(fAAFlags == static_cast<uint16_t>(aaFlags));
|
||||
}
|
||||
|
||||
SkPMColor4f fColor;
|
||||
// If the op doesn't use domains, this is ignored. If the op uses domains and the specific
|
||||
// If the op doesn't use subsets, this is ignored. If the op uses subsets and the specific
|
||||
// entry does not, this rect will equal kLargeRect, so it automatically has no effect.
|
||||
SkRect fDomainRect;
|
||||
SkRect fSubsetRect;
|
||||
unsigned fAAFlags : 4;
|
||||
|
||||
GrQuadAAFlags aaFlags() const { return static_cast<GrQuadAAFlags>(fAAFlags); }
|
||||
@ -340,14 +340,14 @@ private:
|
||||
struct Metadata {
|
||||
// AAType must be filled after initialization; ColorType is determined in finalize()
|
||||
Metadata(const GrSwizzle& swizzle, GrSamplerState::Filter filter,
|
||||
GrQuadPerEdgeAA::Domain domain, GrTextureOp::Saturate saturate)
|
||||
GrQuadPerEdgeAA::Subset subset, GrTextureOp::Saturate saturate)
|
||||
: fSwizzle(swizzle)
|
||||
, fProxyCount(1)
|
||||
, fTotalQuadCount(1)
|
||||
, fFilter(static_cast<uint16_t>(filter))
|
||||
, fAAType(static_cast<uint16_t>(GrAAType::kNone))
|
||||
, fColorType(static_cast<uint16_t>(ColorType::kNone))
|
||||
, fDomain(static_cast<uint16_t>(domain))
|
||||
, fSubset(static_cast<uint16_t>(subset))
|
||||
, fSaturate(static_cast<uint16_t>(saturate)) {}
|
||||
|
||||
GrSwizzle fSwizzle; // sizeof(GrSwizzle) == uint16_t
|
||||
@ -359,7 +359,7 @@ private:
|
||||
uint16_t fFilter : 2; // GrSamplerState::Filter
|
||||
uint16_t fAAType : 2; // GrAAType
|
||||
uint16_t fColorType : 2; // GrQuadPerEdgeAA::ColorType
|
||||
uint16_t fDomain : 1; // bool
|
||||
uint16_t fSubset : 1; // bool
|
||||
uint16_t fSaturate : 1; // bool
|
||||
uint16_t fUnused : 8; // # of bits left before Metadata exceeds 8 bytes
|
||||
|
||||
@ -368,7 +368,7 @@ private:
|
||||
}
|
||||
GrAAType aaType() const { return static_cast<GrAAType>(fAAType); }
|
||||
ColorType colorType() const { return static_cast<ColorType>(fColorType); }
|
||||
Domain domain() const { return static_cast<Domain>(fDomain); }
|
||||
Subset subset() const { return static_cast<Subset>(fSubset); }
|
||||
GrTextureOp::Saturate saturate() const {
|
||||
return static_cast<GrTextureOp::Saturate>(fSaturate);
|
||||
}
|
||||
@ -418,7 +418,7 @@ private:
|
||||
|
||||
};
|
||||
|
||||
// If domainRect is not null it will be used to apply a strict src rect-style constraint.
|
||||
// If subsetRect is not null it will be used to apply a strict src rect-style constraint.
|
||||
TextureOp(GrSurfaceProxyView proxyView,
|
||||
sk_sp<GrColorSpaceXform> textureColorSpaceXform,
|
||||
GrSamplerState::Filter filter,
|
||||
@ -426,12 +426,12 @@ private:
|
||||
GrTextureOp::Saturate saturate,
|
||||
GrAAType aaType,
|
||||
DrawQuad* quad,
|
||||
const SkRect* domainRect)
|
||||
const SkRect* subsetRect)
|
||||
: INHERITED(ClassID())
|
||||
, fQuads(1, true /* includes locals */)
|
||||
, fTextureColorSpaceXform(std::move(textureColorSpaceXform))
|
||||
, fDesc(nullptr)
|
||||
, fMetadata(proxyView.swizzle(), filter, Domain(!!domainRect), saturate) {
|
||||
, fMetadata(proxyView.swizzle(), filter, Subset(!!subsetRect), saturate) {
|
||||
|
||||
// Clean up disparities between the overall aa type and edge configuration and apply
|
||||
// optimizations based on the rect and matrix when appropriate
|
||||
@ -440,30 +440,30 @@ private:
|
||||
fMetadata.fAAType = static_cast<uint16_t>(aaType);
|
||||
|
||||
// We expect our caller to have already caught this optimization.
|
||||
SkASSERT(!domainRect ||
|
||||
!domainRect->contains(proxyView.proxy()->backingStoreBoundsRect()));
|
||||
SkASSERT(!subsetRect ||
|
||||
!subsetRect->contains(proxyView.proxy()->backingStoreBoundsRect()));
|
||||
|
||||
// We may have had a strict constraint with nearest filter solely due to possible AA bloat.
|
||||
// If we don't have (or determined we don't need) coverage AA then we can skip using a
|
||||
// domain.
|
||||
if (domainRect && filter == GrSamplerState::Filter::kNearest &&
|
||||
// subset.
|
||||
if (subsetRect && filter == GrSamplerState::Filter::kNearest &&
|
||||
aaType != GrAAType::kCoverage) {
|
||||
domainRect = nullptr;
|
||||
fMetadata.fDomain = static_cast<uint16_t>(Domain::kNo);
|
||||
subsetRect = nullptr;
|
||||
fMetadata.fSubset = static_cast<uint16_t>(Subset::kNo);
|
||||
}
|
||||
|
||||
// Normalize src coordinates and the domain (if set)
|
||||
// Normalize src coordinates and the subset (if set)
|
||||
NormalizationParams params = proxy_normalization_params(proxyView.proxy(),
|
||||
proxyView.origin());
|
||||
normalize_src_quad(params, &quad->fLocal);
|
||||
SkRect domain = normalize_domain(filter, params, domainRect);
|
||||
SkRect subset = normalize_subset(filter, params, subsetRect);
|
||||
|
||||
// Set bounds before clipping so we don't have to worry about unioning the bounds of
|
||||
// the two potential quads (GrQuad::bounds() is perspective-safe).
|
||||
this->setBounds(quad->fDevice.bounds(), HasAABloat(aaType == GrAAType::kCoverage),
|
||||
IsHairline::kNo);
|
||||
|
||||
int quadCount = this->appendQuad(quad, color, domain);
|
||||
int quadCount = this->appendQuad(quad, color, subset);
|
||||
fViewCountPairs[0] = {proxyView.detachProxy(), quadCount};
|
||||
}
|
||||
|
||||
@ -481,7 +481,7 @@ private:
|
||||
, fTextureColorSpaceXform(std::move(textureColorSpaceXform))
|
||||
, fDesc(nullptr)
|
||||
, fMetadata(set[0].fProxyView.swizzle(), GrSamplerState::Filter::kNearest,
|
||||
Domain::kNo, saturate) {
|
||||
Subset::kNo, saturate) {
|
||||
// Update counts to reflect the batch op
|
||||
fMetadata.fProxyCount = SkToUInt(proxyRunCnt);
|
||||
fMetadata.fTotalQuadCount = SkToUInt(cnt);
|
||||
@ -489,7 +489,7 @@ private:
|
||||
SkRect bounds = SkRectPriv::MakeLargestInverted();
|
||||
|
||||
GrAAType netAAType = GrAAType::kNone; // aa type maximally compatible with all dst rects
|
||||
Domain netDomain = Domain::kNo;
|
||||
Subset netSubset = Subset::kNo;
|
||||
GrSamplerState::Filter netFilter = GrSamplerState::Filter::kNearest;
|
||||
|
||||
const GrSurfaceProxy* curProxy = nullptr;
|
||||
@ -561,7 +561,7 @@ private:
|
||||
}
|
||||
|
||||
// Calculate metadata for the entry
|
||||
const SkRect* domainForQuad = nullptr;
|
||||
const SkRect* subsetForQuad = nullptr;
|
||||
if (constraint == SkCanvas::kStrict_SrcRectConstraint) {
|
||||
// Check (briefly) if the strict constraint is needed for this set entry
|
||||
if (!set[q].fSrcRect.contains(curProxy->backingStoreBoundsRect()) &&
|
||||
@ -570,20 +570,20 @@ private:
|
||||
// Can't rely on hardware clamping and the draw will access outer texels
|
||||
// for AA and/or bilerp. Unlike filter quality, this op still has per-quad
|
||||
// control over AA so that can check aaForQuad, not netAAType.
|
||||
netDomain = Domain::kYes;
|
||||
domainForQuad = &set[q].fSrcRect;
|
||||
netSubset = Subset::kYes;
|
||||
subsetForQuad = &set[q].fSrcRect;
|
||||
}
|
||||
}
|
||||
// This domain may represent a no-op, otherwise it will have the origin and dimensions
|
||||
// This subset may represent a no-op, otherwise it will have the origin and dimensions
|
||||
// of the texture applied to it. Insetting for bilinear filtering is deferred until
|
||||
// on[Pre]Prepare so that the overall filter can be lazily determined.
|
||||
SkRect domain = normalize_domain(filter, proxyParams, domainForQuad);
|
||||
SkRect subset = normalize_subset(filter, proxyParams, subsetForQuad);
|
||||
|
||||
// Always append a quad (or 2 if perspective clipped), it just may refer back to a prior
|
||||
// ViewCountPair (this frequently happens when Chrome draws 9-patches).
|
||||
float alpha = SkTPin(set[q].fAlpha, 0.f, 1.f);
|
||||
fViewCountPairs[p].fQuadCnt += this->appendQuad(
|
||||
&quad, {alpha, alpha, alpha, alpha}, domain);
|
||||
&quad, {alpha, alpha, alpha, alpha}, subset);
|
||||
}
|
||||
// The # of proxy switches should match what was provided (+1 because we incremented p
|
||||
// when a new proxy was encountered).
|
||||
@ -592,12 +592,12 @@ private:
|
||||
|
||||
fMetadata.fAAType = static_cast<uint16_t>(netAAType);
|
||||
fMetadata.fFilter = static_cast<uint16_t>(netFilter);
|
||||
fMetadata.fDomain = static_cast<uint16_t>(netDomain);
|
||||
fMetadata.fSubset = static_cast<uint16_t>(netSubset);
|
||||
|
||||
this->setBounds(bounds, HasAABloat(netAAType == GrAAType::kCoverage), IsHairline::kNo);
|
||||
}
|
||||
|
||||
int appendQuad(DrawQuad* quad, const SkPMColor4f& color, const SkRect& domain) {
|
||||
int appendQuad(DrawQuad* quad, const SkPMColor4f& color, const SkRect& subset) {
|
||||
DrawQuad extra;
|
||||
// Only clip when there's anti-aliasing. When non-aa, the GPU clips just fine and there's
|
||||
// no inset/outset math that requires w > 0.
|
||||
@ -609,9 +609,9 @@ private:
|
||||
quad->fEdgeFlags = GrQuadAAFlags::kNone;
|
||||
quadCount = 1;
|
||||
}
|
||||
fQuads.append(quad->fDevice, {color, domain, quad->fEdgeFlags}, &quad->fLocal);
|
||||
fQuads.append(quad->fDevice, {color, subset, quad->fEdgeFlags}, &quad->fLocal);
|
||||
if (quadCount > 1) {
|
||||
fQuads.append(extra.fDevice, {color, domain, extra.fEdgeFlags}, &extra.fLocal);
|
||||
fQuads.append(extra.fDevice, {color, subset, extra.fEdgeFlags}, &extra.fLocal);
|
||||
fMetadata.fTotalQuadCount++;
|
||||
}
|
||||
return quadCount;
|
||||
@ -698,11 +698,11 @@ private:
|
||||
|
||||
for (int i = 0; i < quadCnt && iter.next(); ++i) {
|
||||
SkASSERT(iter.isLocalValid());
|
||||
const ColorDomainAndAA& info = iter.metadata();
|
||||
const ColorSubsetAndAA& info = iter.metadata();
|
||||
|
||||
tessellator.append(iter.deviceQuad(), iter.localQuad(), info.fColor,
|
||||
inset ? inset_domain_for_bilerp(params, info.fDomainRect)
|
||||
: info.fDomainRect,
|
||||
inset ? inset_subset_for_bilerp(params, info.fSubsetRect)
|
||||
: info.fSubsetRect,
|
||||
info.aaFlags());
|
||||
}
|
||||
|
||||
@ -763,7 +763,7 @@ private:
|
||||
GrQuad::Type quadType = GrQuad::Type::kAxisAligned;
|
||||
ColorType colorType = ColorType::kNone;
|
||||
GrQuad::Type srcQuadType = GrQuad::Type::kAxisAligned;
|
||||
Domain domain = Domain::kNo;
|
||||
Subset subset = Subset::kNo;
|
||||
GrAAType overallAAType = fMetadata.aaType();
|
||||
|
||||
desc->fNumProxies = 0;
|
||||
@ -777,8 +777,8 @@ private:
|
||||
if (op.fQuads.localQuadType() > srcQuadType) {
|
||||
srcQuadType = op.fQuads.localQuadType();
|
||||
}
|
||||
if (op.fMetadata.domain() == Domain::kYes) {
|
||||
domain = Domain::kYes;
|
||||
if (op.fMetadata.subset() == Subset::kYes) {
|
||||
subset = Subset::kYes;
|
||||
}
|
||||
colorType = std::max(colorType, op.fMetadata.colorType());
|
||||
desc->fNumProxies += op.fMetadata.fProxyCount;
|
||||
@ -801,7 +801,7 @@ private:
|
||||
maxQuadsPerMesh);
|
||||
|
||||
desc->fVertexSpec = VertexSpec(quadType, colorType, srcQuadType, /* hasLocal */ true,
|
||||
domain, overallAAType, /* alpha as coverage */ true,
|
||||
subset, overallAAType, /* alpha as coverage */ true,
|
||||
indexBufferOption);
|
||||
|
||||
SkASSERT(desc->fNumTotalQuads <= GrQuadPerEdgeAA::QuadLimit(indexBufferOption));
|
||||
@ -922,8 +922,8 @@ private:
|
||||
return CombineResult::kCannotCombine;
|
||||
}
|
||||
|
||||
if (fMetadata.domain() != that->fMetadata.domain()) {
|
||||
// It is technically possible to combine operations across domain modes, but performance
|
||||
if (fMetadata.subset() != that->fMetadata.subset()) {
|
||||
// It is technically possible to combine operations across subset modes, but performance
|
||||
// testing suggests it's better to make more draw calls where some take advantage of
|
||||
// the more optimal shader path without coordinate clamping.
|
||||
return CombineResult::kCannotCombine;
|
||||
@ -967,7 +967,7 @@ private:
|
||||
return CombineResult::kCannotCombine;
|
||||
}
|
||||
|
||||
fMetadata.fDomain |= that->fMetadata.fDomain;
|
||||
fMetadata.fSubset |= that->fMetadata.fSubset;
|
||||
fMetadata.fColorType = std::max(fMetadata.fColorType, that->fMetadata.fColorType);
|
||||
if (upgradeToCoverageAAOnMerge) {
|
||||
fMetadata.fAAType = static_cast<uint16_t>(GrAAType::kCoverage);
|
||||
@ -981,7 +981,7 @@ private:
|
||||
return CombineResult::kMerged;
|
||||
}
|
||||
|
||||
GrQuadBuffer<ColorDomainAndAA> fQuads;
|
||||
GrQuadBuffer<ColorSubsetAndAA> fQuads;
|
||||
sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
|
||||
// Most state of TextureOp is packed into these two field to minimize the op's size.
|
||||
// Historically, increasing the size of TextureOp has caused surprising perf regressions, so
|
||||
@ -1015,11 +1015,11 @@ std::unique_ptr<GrDrawOp> GrTextureOp::Make(GrRecordingContext* context,
|
||||
SkBlendMode blendMode,
|
||||
GrAAType aaType,
|
||||
DrawQuad* quad,
|
||||
const SkRect* domain) {
|
||||
const SkRect* subset) {
|
||||
// Apply optimizations that are valid whether or not using GrTextureOp or GrFillRectOp
|
||||
if (domain && domain->contains(proxyView.proxy()->backingStoreBoundsRect())) {
|
||||
// No need for a shader-based domain if hardware clamping achieves the same effect
|
||||
domain = nullptr;
|
||||
if (subset && subset->contains(proxyView.proxy()->backingStoreBoundsRect())) {
|
||||
// No need for a shader-based subset if hardware clamping achieves the same effect
|
||||
subset = nullptr;
|
||||
}
|
||||
|
||||
if (filter != GrSamplerState::Filter::kNearest &&
|
||||
@ -1029,7 +1029,7 @@ std::unique_ptr<GrDrawOp> GrTextureOp::Make(GrRecordingContext* context,
|
||||
|
||||
if (blendMode == SkBlendMode::kSrcOver) {
|
||||
return TextureOp::Make(context, std::move(proxyView), std::move(textureXform), filter,
|
||||
color, saturate, aaType, std::move(quad), domain);
|
||||
color, saturate, aaType, std::move(quad), subset);
|
||||
} else {
|
||||
// Emulate complex blending using GrFillRectOp
|
||||
GrPaint paint;
|
||||
@ -1037,15 +1037,15 @@ std::unique_ptr<GrDrawOp> GrTextureOp::Make(GrRecordingContext* context,
|
||||
paint.setXPFactory(SkBlendMode_AsXPFactory(blendMode));
|
||||
|
||||
std::unique_ptr<GrFragmentProcessor> fp;
|
||||
if (domain) {
|
||||
if (subset) {
|
||||
const auto& caps = *context->priv().caps();
|
||||
SkRect localRect;
|
||||
if (quad->fLocal.asRect(&localRect)) {
|
||||
fp = GrTextureEffect::MakeSubset(std::move(proxyView), alphaType, SkMatrix::I(), filter,
|
||||
*domain, localRect, caps);
|
||||
*subset, localRect, caps);
|
||||
} else {
|
||||
fp = GrTextureEffect::MakeSubset(std::move(proxyView), alphaType, SkMatrix::I(), filter,
|
||||
*domain, caps);
|
||||
*subset, caps);
|
||||
}
|
||||
} else {
|
||||
fp = GrTextureEffect::Make(std::move(proxyView), alphaType, SkMatrix::I(), filter);
|
||||
@ -1160,12 +1160,12 @@ void GrTextureOp::AddTextureSetOps(GrRenderTargetContext* rtc,
|
||||
quad.fLocal = GrQuad(set[i].fSrcRect);
|
||||
}
|
||||
|
||||
const SkRect* domain = constraint == SkCanvas::kStrict_SrcRectConstraint
|
||||
const SkRect* subset = constraint == SkCanvas::kStrict_SrcRectConstraint
|
||||
? &set[i].fSrcRect : nullptr;
|
||||
|
||||
auto op = Make(context, set[i].fProxyView, set[i].fSrcAlphaType, textureColorSpaceXform,
|
||||
filter, {alpha, alpha, alpha, alpha}, saturate, blendMode, aaType,
|
||||
&quad, domain);
|
||||
&quad, subset);
|
||||
rtc->addDrawOp(clip, std::move(op));
|
||||
}
|
||||
return;
|
||||
@ -1290,7 +1290,7 @@ GR_DRAW_OP_TEST_DEFINE(TextureOp) {
|
||||
aaFlags |= random->nextBool() ? GrQuadAAFlags::kTop : GrQuadAAFlags::kNone;
|
||||
aaFlags |= random->nextBool() ? GrQuadAAFlags::kRight : GrQuadAAFlags::kNone;
|
||||
aaFlags |= random->nextBool() ? GrQuadAAFlags::kBottom : GrQuadAAFlags::kNone;
|
||||
bool useDomain = random->nextBool();
|
||||
bool useSubset = random->nextBool();
|
||||
auto saturate = random->nextBool() ? GrTextureOp::Saturate::kYes : GrTextureOp::Saturate::kNo;
|
||||
GrSurfaceProxyView proxyView(
|
||||
std::move(proxy), origin,
|
||||
@ -1301,7 +1301,7 @@ GR_DRAW_OP_TEST_DEFINE(TextureOp) {
|
||||
DrawQuad quad = {GrQuad::MakeFromRect(rect, viewMatrix), GrQuad(srcRect), aaFlags};
|
||||
return GrTextureOp::Make(context, std::move(proxyView), alphaType, std::move(texXform), filter,
|
||||
color, saturate, SkBlendMode::kSrcOver, aaType,
|
||||
&quad, useDomain ? &srcRect : nullptr);
|
||||
&quad, useSubset ? &srcRect : nullptr);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -32,7 +32,7 @@ public:
|
||||
/**
|
||||
* Creates an op that draws a sub-quadrilateral of a texture. The passed color is modulated by
|
||||
* the texture's color. 'deviceQuad' specifies the device-space coordinates to draw, using
|
||||
* 'localQuad' to map into the proxy's texture space. If non-null, 'domain' represents the
|
||||
* 'localQuad' to map into the proxy's texture space. If non-null, 'subset' represents the
|
||||
* boundary for the strict src rect constraint. If GrAAType is kCoverage then AA is applied to
|
||||
* the edges indicated by GrQuadAAFlags. Otherwise, GrQuadAAFlags is ignored.
|
||||
*
|
||||
@ -50,7 +50,7 @@ public:
|
||||
SkBlendMode,
|
||||
GrAAType,
|
||||
DrawQuad*,
|
||||
const SkRect* domain = nullptr);
|
||||
const SkRect* subset = nullptr);
|
||||
|
||||
// Automatically falls back to using one GrFillRectOp per entry if dynamic states are not
|
||||
// supported, or if the blend mode is not src-over. 'cnt' is the size of the entry array.
|
||||
|
Loading…
Reference in New Issue
Block a user