GPU implementation of drawArc.
This adds analytic shaders for filled arcs and butt-cap stroked arcs where the center point is not used. BUG=skia:5227 GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2259323003 Review-Url: https://codereview.chromium.org/2259323003
This commit is contained in:
parent
4c261d0c1e
commit
4f3a0ca85d
@ -230,9 +230,31 @@ public:
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkRect& oval,
|
||||
const GrStyle& style);
|
||||
/**
|
||||
* Draws a partial arc of an oval.
|
||||
*
|
||||
* @param paint describes how to color pixels.
|
||||
* @param viewMatrix transformation matrix.
|
||||
* @param oval the bounding rect of the oval.
|
||||
* @param startAngle starting angle in degrees.
|
||||
* @param sweepAngle angle to sweep in degrees. Must be in (-360, 360)
|
||||
* @param useCenter true means that the implied path begins at the oval center, connects as a
|
||||
* line to the point indicated by the start contains the arc indicated by
|
||||
* the sweep angle. If false the line beginning at the center point is
|
||||
* omitted.
|
||||
* @param style style to apply to the oval.
|
||||
*/
|
||||
void drawArc(const GrClip&,
|
||||
const GrPaint& paint,
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkRect& oval,
|
||||
SkScalar startAngle,
|
||||
SkScalar sweepAngle,
|
||||
bool useCenter,
|
||||
const GrStyle& style);
|
||||
|
||||
/**
|
||||
* Draw the image as a set of rects, specified by |iter|.
|
||||
* Draw the image as a set of rects, specified by |iter|.
|
||||
*/
|
||||
void drawImageLattice(const GrClip&,
|
||||
const GrPaint& paint,
|
||||
|
@ -997,6 +997,44 @@ void GrDrawContext::drawOval(const GrClip& clip,
|
||||
this->internalDrawPath(clip, paint, viewMatrix, path, style);
|
||||
}
|
||||
|
||||
void GrDrawContext::drawArc(const GrClip& clip,
|
||||
const GrPaint& paint,
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkRect& oval,
|
||||
SkScalar startAngle,
|
||||
SkScalar sweepAngle,
|
||||
bool useCenter,
|
||||
const GrStyle& style) {
|
||||
bool useHWAA;
|
||||
if (should_apply_coverage_aa(paint, fRenderTarget.get(), &useHWAA)) {
|
||||
GrShaderCaps* shaderCaps = fContext->caps()->shaderCaps();
|
||||
SkAutoTUnref<GrDrawBatch> batch(GrOvalRenderer::CreateArcBatch(paint.getColor(),
|
||||
viewMatrix,
|
||||
oval,
|
||||
startAngle,
|
||||
sweepAngle,
|
||||
useCenter,
|
||||
style,
|
||||
shaderCaps));
|
||||
if (batch) {
|
||||
GrPipelineBuilder pipelineBuilder(paint, useHWAA);
|
||||
this->getDrawTarget()->drawBatch(pipelineBuilder, this, clip, batch);
|
||||
return;
|
||||
}
|
||||
}
|
||||
SkPath path;
|
||||
path.setIsVolatile(true);
|
||||
if (useCenter) {
|
||||
path.moveTo(oval.centerX(), oval.centerY());
|
||||
}
|
||||
path.arcTo(oval, startAngle, sweepAngle, !useCenter);
|
||||
if (useCenter) {
|
||||
path.close();
|
||||
}
|
||||
this->internalDrawPath(clip, paint, viewMatrix, path, style);
|
||||
return;
|
||||
}
|
||||
|
||||
void GrDrawContext::drawImageLattice(const GrClip& clip,
|
||||
const GrPaint& paint,
|
||||
const SkMatrix& viewMatrix,
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "GrInvariantOutput.h"
|
||||
#include "GrProcessor.h"
|
||||
#include "GrResourceProvider.h"
|
||||
#include "GrStyle.h"
|
||||
#include "SkRRect.h"
|
||||
#include "SkStrokeRec.h"
|
||||
#include "batches/GrVertexBatch.h"
|
||||
@ -28,14 +29,6 @@
|
||||
|
||||
namespace {
|
||||
|
||||
struct CircleVertex {
|
||||
SkPoint fPos;
|
||||
GrColor fColor;
|
||||
SkPoint fOffset;
|
||||
SkScalar fOuterRadius;
|
||||
SkScalar fInnerRadius;
|
||||
};
|
||||
|
||||
struct EllipseVertex {
|
||||
SkPoint fPos;
|
||||
GrColor fColor;
|
||||
@ -75,20 +68,42 @@ inline bool circle_stays_circle(const SkMatrix& m) {
|
||||
* v is a normalized vector pointing to the outer edge
|
||||
* outerDistance is the distance to the outer edge, < 0 if we are outside of the shape
|
||||
* if stroking, innerDistance is the distance to the inner edge, < 0 if outside
|
||||
* Additional clip planes are supported for rendering circular arcs. The additional planes are
|
||||
* either intersected or unioned together. Up to three planes are supported (an initial plane,
|
||||
* a plane intersected with the initial plane, and a plane unioned with the first two). Only two
|
||||
* are useful for any given arc, but having all three in one instance allows batching different
|
||||
* types of arcs.
|
||||
*/
|
||||
|
||||
class CircleGeometryProcessor : public GrGeometryProcessor {
|
||||
public:
|
||||
CircleGeometryProcessor(bool stroke, const SkMatrix& localMatrix) : fLocalMatrix(localMatrix){
|
||||
CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
|
||||
const SkMatrix& localMatrix)
|
||||
: fLocalMatrix(localMatrix) {
|
||||
this->initClassID<CircleGeometryProcessor>();
|
||||
fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
|
||||
kHigh_GrSLPrecision);
|
||||
fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
|
||||
fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kVec4f_GrVertexAttribType);
|
||||
if (clipPlane) {
|
||||
fInClipPlane = &this->addVertexAttrib("inClipPlane", kVec3f_GrVertexAttribType);
|
||||
} else {
|
||||
fInClipPlane = nullptr;
|
||||
}
|
||||
if (isectPlane) {
|
||||
fInIsectPlane = &this->addVertexAttrib("inIsectPlane", kVec3f_GrVertexAttribType);
|
||||
} else {
|
||||
fInIsectPlane = nullptr;
|
||||
}
|
||||
if (unionPlane) {
|
||||
fInUnionPlane = &this->addVertexAttrib("inUnionPlane", kVec3f_GrVertexAttribType);
|
||||
} else {
|
||||
fInUnionPlane = nullptr;
|
||||
}
|
||||
fStroke = stroke;
|
||||
}
|
||||
|
||||
bool implementsDistanceVector() const override { return true; };
|
||||
bool implementsDistanceVector() const override { return !fInClipPlane; };
|
||||
|
||||
virtual ~CircleGeometryProcessor() {}
|
||||
|
||||
@ -112,15 +127,27 @@ private:
|
||||
GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
|
||||
GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
|
||||
GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
|
||||
GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
|
||||
|
||||
// emit attributes
|
||||
varyingHandler->emitAttributes(cgp);
|
||||
fragBuilder->codeAppend("vec4 circleEdge;");
|
||||
varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge");
|
||||
if (cgp.fInClipPlane) {
|
||||
fragBuilder->codeAppend("vec3 clipPlane;");
|
||||
varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
|
||||
}
|
||||
if (cgp.fInIsectPlane) {
|
||||
SkASSERT(cgp.fInClipPlane);
|
||||
fragBuilder->codeAppend("vec3 isectPlane;");
|
||||
varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
|
||||
}
|
||||
if (cgp.fInUnionPlane) {
|
||||
SkASSERT(cgp.fInClipPlane);
|
||||
fragBuilder->codeAppend("vec3 unionPlane;");
|
||||
varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
|
||||
}
|
||||
|
||||
GrGLSLVertToFrag v(kVec4f_GrSLType);
|
||||
varyingHandler->addVarying("CircleEdge", &v);
|
||||
vertBuilder->codeAppendf("%s = %s;", v.vsOut(), cgp.fInCircleEdge->fName);
|
||||
|
||||
GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
|
||||
// setup pass through color
|
||||
varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
|
||||
|
||||
@ -137,12 +164,11 @@ private:
|
||||
args.fTransformsIn,
|
||||
args.fTransformsOut);
|
||||
|
||||
fragBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
|
||||
fragBuilder->codeAppendf("float distanceToOuterEdge = %s.z * (1.0 - d);", v.fsIn());
|
||||
fragBuilder->codeAppendf("float edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);");
|
||||
fragBuilder->codeAppend("float d = length(circleEdge.xy);");
|
||||
fragBuilder->codeAppend("float distanceToOuterEdge = circleEdge.z * (1.0 - d);");
|
||||
fragBuilder->codeAppend("float edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);");
|
||||
if (cgp.fStroke) {
|
||||
fragBuilder->codeAppendf("float distanceToInnerEdge = %s.z * (d - %s.w);",
|
||||
v.fsIn(), v.fsIn());
|
||||
fragBuilder->codeAppend("float distanceToInnerEdge = circleEdge.z * (d - circleEdge.w);");
|
||||
fragBuilder->codeAppend("float innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);");
|
||||
fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
|
||||
}
|
||||
@ -154,12 +180,20 @@ private:
|
||||
"%s);", // no normalize
|
||||
args.fDistanceVectorName, innerEdgeDistance);
|
||||
fragBuilder->codeAppend ("} else {");
|
||||
fragBuilder->codeAppendf(" %s = vec4(normalize(%s.xy), distanceToOuterEdge, "
|
||||
"%s);",
|
||||
args.fDistanceVectorName, v.fsIn(), innerEdgeDistance);
|
||||
fragBuilder->codeAppendf(" %s = vec4(normalize(circleEdge.xy), distanceToOuterEdge, %s);",
|
||||
args.fDistanceVectorName, innerEdgeDistance);
|
||||
fragBuilder->codeAppend ("}");
|
||||
}
|
||||
|
||||
if (cgp.fInClipPlane) {
|
||||
fragBuilder->codeAppend("float clip = clamp(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + clipPlane.z, 0.0, 1.0);");
|
||||
if (cgp.fInIsectPlane) {
|
||||
fragBuilder->codeAppend("clip *= clamp(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + isectPlane.z, 0.0, 1.0);");
|
||||
}
|
||||
if (cgp.fInUnionPlane) {
|
||||
fragBuilder->codeAppend("clip += (1-clip)*clamp(circleEdge.z * dot(circleEdge.xy, unionPlane.xy) + unionPlane.z, 0.0, 1.0);");
|
||||
}
|
||||
fragBuilder->codeAppend("edgeAlpha *= clip;");
|
||||
}
|
||||
fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
|
||||
}
|
||||
|
||||
@ -167,14 +201,16 @@ private:
|
||||
const GrGLSLCaps&,
|
||||
GrProcessorKeyBuilder* b) {
|
||||
const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
|
||||
uint16_t key = cgp.fStroke ? 0x1 : 0x0;
|
||||
key |= cgp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
|
||||
uint16_t key;
|
||||
key = cgp.fStroke ? 0x01 : 0x0;
|
||||
key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0;
|
||||
key |= cgp.fInClipPlane ? 0x04 : 0x0;
|
||||
key |= cgp.fInIsectPlane ? 0x08 : 0x0;
|
||||
key |= cgp.fInUnionPlane ? 0x10 : 0x0;
|
||||
b->add32(key);
|
||||
}
|
||||
|
||||
void setData(const GrGLSLProgramDataManager& pdman,
|
||||
const GrPrimitiveProcessor& gp) override {
|
||||
}
|
||||
void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&) override {}
|
||||
|
||||
void setTransformData(const GrPrimitiveProcessor& primProc,
|
||||
const GrGLSLProgramDataManager& pdman,
|
||||
@ -192,6 +228,9 @@ private:
|
||||
const Attribute* fInPosition;
|
||||
const Attribute* fInColor;
|
||||
const Attribute* fInCircleEdge;
|
||||
const Attribute* fInClipPlane;
|
||||
const Attribute* fInIsectPlane;
|
||||
const Attribute* fInUnionPlane;
|
||||
bool fStroke;
|
||||
|
||||
GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
|
||||
@ -203,7 +242,9 @@ GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
|
||||
|
||||
sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
|
||||
return sk_sp<GrGeometryProcessor>(
|
||||
new CircleGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
|
||||
new CircleGeometryProcessor(d->fRandom->nextBool(), d->fRandom->nextBool(),
|
||||
d->fRandom->nextBool(), d->fRandom->nextBool(),
|
||||
GrTest::TestMatrix(d->fRandom)));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@ -529,19 +570,47 @@ class CircleBatch : public GrVertexBatch {
|
||||
public:
|
||||
DEFINE_BATCH_CLASS_ID
|
||||
|
||||
CircleBatch(GrColor color, const SkMatrix& viewMatrix, const SkRect& circle,
|
||||
const SkStrokeRec& stroke)
|
||||
: INHERITED(ClassID())
|
||||
, fViewMatrixIfUsingLocalCoords(viewMatrix) {
|
||||
SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
|
||||
/** Optional extra params to render a partial arc rather than a full circle. */
|
||||
struct ArcParams {
|
||||
SkScalar fStartAngleRadians;
|
||||
SkScalar fSweepAngleRadians;
|
||||
bool fUseCenter;
|
||||
};
|
||||
static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, SkPoint center,
|
||||
SkScalar radius, const GrStyle& style,
|
||||
const ArcParams* arcParams = nullptr) {
|
||||
SkASSERT(circle_stays_circle(viewMatrix));
|
||||
const SkStrokeRec& stroke = style.strokeRec();
|
||||
if (style.hasPathEffect()) {
|
||||
return nullptr;
|
||||
}
|
||||
SkStrokeRec::Style recStyle = stroke.getStyle();
|
||||
if (arcParams) {
|
||||
// Arc support depends on the style.
|
||||
switch (recStyle) {
|
||||
case SkStrokeRec::kStrokeAndFill_Style:
|
||||
// This produces a strange result that this batch doesn't implement.
|
||||
return nullptr;
|
||||
case SkStrokeRec::kFill_Style:
|
||||
// This supports all fills.
|
||||
break;
|
||||
case SkStrokeRec::kStroke_Style: // fall through
|
||||
case SkStrokeRec::kHairline_Style:
|
||||
// Strokes that don't use the center point are supported with butt cap.
|
||||
if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
|
||||
return nullptr;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
viewMatrix.mapPoints(¢er, 1);
|
||||
SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
|
||||
radius = viewMatrix.mapRadius(radius);
|
||||
SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
|
||||
|
||||
SkStrokeRec::Style style = stroke.getStyle();
|
||||
bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
|
||||
SkStrokeRec::kHairline_Style == style;
|
||||
bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
|
||||
bool isStrokeOnly = SkStrokeRec::kStroke_Style == recStyle ||
|
||||
SkStrokeRec::kHairline_Style == recStyle;
|
||||
bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
|
||||
|
||||
SkScalar innerRadius = 0.0f;
|
||||
SkScalar outerRadius = radius;
|
||||
@ -565,21 +634,111 @@ public:
|
||||
// rendered and the outset ensures the box will cover all partially covered by the circle.
|
||||
outerRadius += SK_ScalarHalf;
|
||||
innerRadius -= SK_ScalarHalf;
|
||||
CircleBatch* batch = new CircleBatch();
|
||||
batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
|
||||
|
||||
fGeoData.emplace_back(Geometry {
|
||||
color,
|
||||
innerRadius,
|
||||
outerRadius,
|
||||
SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
|
||||
center.fX + outerRadius, center.fY + outerRadius)
|
||||
});
|
||||
// This makes every point fully inside the intersection plane.
|
||||
static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
|
||||
// This makes every point fully outside the union plane.
|
||||
static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
|
||||
SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
|
||||
center.fX + outerRadius, center.fY + outerRadius);
|
||||
|
||||
if (arcParams) {
|
||||
// The shader operates in a space where the circle is translated to be centered at the
|
||||
// origin. Here we compute points on the unit circle at the starting and ending angles.
|
||||
SkPoint startPoint, stopPoint;
|
||||
startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
|
||||
SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
|
||||
stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
|
||||
// Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
|
||||
// radial lines. However, in both cases we have to be careful about the half-circle.
|
||||
// case. In that case the two radial lines are equal and so that edge gets clipped
|
||||
// twice. Since the shared edge goes through the center we fall back on the useCenter
|
||||
// case.
|
||||
bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
|
||||
!SkScalarNearlyEqual(SkScalarAbs(arcParams->fSweepAngleRadians),
|
||||
SK_ScalarPI);
|
||||
if (useCenter) {
|
||||
SkVector norm0 = {startPoint.fY, -startPoint.fX};
|
||||
SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
|
||||
if (arcParams->fSweepAngleRadians > 0) {
|
||||
norm0.negate();
|
||||
} else {
|
||||
norm1.negate();
|
||||
}
|
||||
batch->fClipPlane = true;
|
||||
if (SkScalarAbs(arcParams->fSweepAngleRadians) > SK_ScalarPI) {
|
||||
batch->fGeoData.emplace_back(Geometry {
|
||||
color,
|
||||
innerRadius,
|
||||
outerRadius,
|
||||
{norm0.fX, norm0.fY, 0.5f},
|
||||
{kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
|
||||
{norm1.fX, norm1.fY, 0.5f},
|
||||
devBounds
|
||||
});
|
||||
batch->fClipPlaneIsect = false;
|
||||
batch->fClipPlaneUnion = true;
|
||||
} else {
|
||||
batch->fGeoData.emplace_back(Geometry {
|
||||
color,
|
||||
innerRadius,
|
||||
outerRadius,
|
||||
{norm0.fX, norm0.fY, 0.5f},
|
||||
{norm1.fX, norm1.fY, 0.5f},
|
||||
{kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
|
||||
devBounds
|
||||
});
|
||||
batch->fClipPlaneIsect = true;
|
||||
batch->fClipPlaneUnion = false;
|
||||
}
|
||||
} else {
|
||||
// We clip to a secant of the original circle.
|
||||
startPoint.scale(radius);
|
||||
stopPoint.scale(radius);
|
||||
SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
|
||||
norm.normalize();
|
||||
if (arcParams->fSweepAngleRadians > 0) {
|
||||
norm.negate();
|
||||
}
|
||||
SkScalar d = -norm.dot(startPoint) + 0.5f;
|
||||
|
||||
batch->fGeoData.emplace_back(Geometry {
|
||||
color,
|
||||
innerRadius,
|
||||
outerRadius,
|
||||
{norm.fX, norm.fY, d},
|
||||
{kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
|
||||
{kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
|
||||
devBounds
|
||||
});
|
||||
batch->fClipPlane = true;
|
||||
batch->fClipPlaneIsect = false;
|
||||
batch->fClipPlaneUnion = false;
|
||||
}
|
||||
} else {
|
||||
batch->fGeoData.emplace_back(Geometry {
|
||||
color,
|
||||
innerRadius,
|
||||
outerRadius,
|
||||
{kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
|
||||
{kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
|
||||
{kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
|
||||
devBounds
|
||||
});
|
||||
batch->fClipPlane = false;
|
||||
batch->fClipPlaneIsect = false;
|
||||
batch->fClipPlaneUnion = false;
|
||||
}
|
||||
// Use the original radius and stroke radius for the bounds so that it does not include the
|
||||
// AA bloat.
|
||||
radius += halfWidth;
|
||||
this->setBounds({center.fX - radius, center.fY - radius,
|
||||
center.fX + radius, center.fY + radius},
|
||||
HasAABloat::kYes, IsZeroArea::kNo);
|
||||
fStroked = isStrokeOnly && innerRadius > 0;
|
||||
batch->setBounds({center.fX - radius, center.fY - radius,
|
||||
center.fX + radius, center.fY + radius},
|
||||
HasAABloat::kYes, IsZeroArea::kNo);
|
||||
batch->fStroked = isStrokeOnly && innerRadius > 0;
|
||||
return batch;
|
||||
}
|
||||
|
||||
const char* name() const override { return "CircleBatch"; }
|
||||
@ -608,6 +767,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
CircleBatch() : INHERITED(ClassID()) {}
|
||||
void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
|
||||
// Handle any overrides that affect our GP.
|
||||
overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
|
||||
@ -623,15 +783,29 @@ private:
|
||||
}
|
||||
|
||||
// Setup geometry processor
|
||||
SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(fStroked, localMatrix));
|
||||
SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(fStroked, fClipPlane,
|
||||
fClipPlaneIsect,
|
||||
fClipPlaneUnion,
|
||||
localMatrix));
|
||||
|
||||
struct CircleVertex {
|
||||
SkPoint fPos;
|
||||
GrColor fColor;
|
||||
SkPoint fOffset;
|
||||
SkScalar fOuterRadius;
|
||||
SkScalar fInnerRadius;
|
||||
// These planes may or may not be present in the vertex buffer.
|
||||
SkScalar fHalfPlanes[3][3];
|
||||
};
|
||||
|
||||
int instanceCount = fGeoData.count();
|
||||
size_t vertexStride = gp->getVertexStride();
|
||||
SkASSERT(vertexStride == sizeof(CircleVertex));
|
||||
SkASSERT(vertexStride == sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar))
|
||||
- (fClipPlaneIsect? 0 : 3 * sizeof(SkScalar))
|
||||
- (fClipPlaneUnion? 0 : 3 * sizeof(SkScalar)));
|
||||
QuadHelper helper;
|
||||
CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target, vertexStride,
|
||||
instanceCount));
|
||||
if (!verts) {
|
||||
char* vertices = reinterpret_cast<char*>(helper.init(target, vertexStride, instanceCount));
|
||||
if (!vertices) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -643,34 +817,57 @@ private:
|
||||
SkScalar outerRadius = geom.fOuterRadius;
|
||||
|
||||
const SkRect& bounds = geom.fDevBounds;
|
||||
CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + (4 * i + 0)*vertexStride);
|
||||
CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + (4 * i + 1)*vertexStride);
|
||||
CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + (4 * i + 2)*vertexStride);
|
||||
CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + (4 * i + 3)*vertexStride);
|
||||
|
||||
// The inner radius in the vertex data must be specified in normalized space.
|
||||
innerRadius = innerRadius / outerRadius;
|
||||
verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
|
||||
verts[0].fColor = color;
|
||||
verts[0].fOffset = SkPoint::Make(-1, -1);
|
||||
verts[0].fOuterRadius = outerRadius;
|
||||
verts[0].fInnerRadius = innerRadius;
|
||||
v0->fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
|
||||
v0->fColor = color;
|
||||
v0->fOffset = SkPoint::Make(-1, -1);
|
||||
v0->fOuterRadius = outerRadius;
|
||||
v0->fInnerRadius = innerRadius;
|
||||
|
||||
verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
|
||||
verts[1].fColor = color;
|
||||
verts[1].fOffset = SkPoint::Make(-1, 1);
|
||||
verts[1].fOuterRadius = outerRadius;
|
||||
verts[1].fInnerRadius = innerRadius;
|
||||
v1->fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
|
||||
v1->fColor = color;
|
||||
v1->fOffset = SkPoint::Make(-1, 1);
|
||||
v1->fOuterRadius = outerRadius;
|
||||
v1->fInnerRadius = innerRadius;
|
||||
|
||||
verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
|
||||
verts[2].fColor = color;
|
||||
verts[2].fOffset = SkPoint::Make(1, 1);
|
||||
verts[2].fOuterRadius = outerRadius;
|
||||
verts[2].fInnerRadius = innerRadius;
|
||||
v2->fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
|
||||
v2->fColor = color;
|
||||
v2->fOffset = SkPoint::Make(1, 1);
|
||||
v2->fOuterRadius = outerRadius;
|
||||
v2->fInnerRadius = innerRadius;
|
||||
|
||||
verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
|
||||
verts[3].fColor = color;
|
||||
verts[3].fOffset = SkPoint::Make(1, -1);
|
||||
verts[3].fOuterRadius = outerRadius;
|
||||
verts[3].fInnerRadius = innerRadius;
|
||||
v3->fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
|
||||
v3->fColor = color;
|
||||
v3->fOffset = SkPoint::Make(1, -1);
|
||||
v3->fOuterRadius = outerRadius;
|
||||
v3->fInnerRadius = innerRadius;
|
||||
|
||||
verts += kVerticesPerQuad;
|
||||
if (fClipPlane) {
|
||||
memcpy(v0->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
|
||||
memcpy(v1->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
|
||||
memcpy(v2->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
|
||||
memcpy(v3->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
|
||||
}
|
||||
int unionIdx = 1;
|
||||
if (fClipPlaneIsect) {
|
||||
memcpy(v0->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
|
||||
memcpy(v1->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
|
||||
memcpy(v2->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
|
||||
memcpy(v3->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
|
||||
unionIdx = 2;
|
||||
}
|
||||
if (fClipPlaneUnion) {
|
||||
memcpy(v0->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
|
||||
memcpy(v1->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
|
||||
memcpy(v2->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
|
||||
memcpy(v3->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
|
||||
}
|
||||
}
|
||||
helper.recordDraw(target, gp);
|
||||
}
|
||||
@ -686,6 +883,12 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Because we've set up the batches that don't use the planes with noop values
|
||||
// we can just accumulate used planes by later batches.
|
||||
fClipPlane |= that->fClipPlane;
|
||||
fClipPlaneIsect |= that->fClipPlaneIsect;
|
||||
fClipPlaneUnion |= that->fClipPlaneUnion;
|
||||
|
||||
if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
|
||||
return false;
|
||||
}
|
||||
@ -696,13 +899,19 @@ private:
|
||||
}
|
||||
|
||||
struct Geometry {
|
||||
GrColor fColor;
|
||||
GrColor fColor;
|
||||
SkScalar fInnerRadius;
|
||||
SkScalar fOuterRadius;
|
||||
SkRect fDevBounds;
|
||||
SkScalar fClipPlane[3];
|
||||
SkScalar fIsectPlane[3];
|
||||
SkScalar fUnionPlane[3];
|
||||
SkRect fDevBounds;
|
||||
};
|
||||
|
||||
bool fStroked;
|
||||
bool fClipPlane;
|
||||
bool fClipPlaneIsect;
|
||||
bool fClipPlaneUnion;
|
||||
SkMatrix fViewMatrixIfUsingLocalCoords;
|
||||
SkSTArray<1, Geometry, true> fGeoData;
|
||||
|
||||
@ -1243,7 +1452,17 @@ private:
|
||||
}
|
||||
|
||||
// Setup geometry processor
|
||||
SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(fStroked, localMatrix));
|
||||
SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(fStroked, false, false,
|
||||
false, localMatrix));
|
||||
|
||||
struct CircleVertex {
|
||||
SkPoint fPos;
|
||||
GrColor fColor;
|
||||
SkPoint fOffset;
|
||||
SkScalar fOuterRadius;
|
||||
SkScalar fInnerRadius;
|
||||
// No half plane, we don't use it here.
|
||||
};
|
||||
|
||||
int instanceCount = fGeoData.count();
|
||||
size_t vertexStride = gp->getVertexStride();
|
||||
@ -1631,7 +1850,7 @@ GrDrawBatch* GrOvalRenderer::CreateRRectBatch(GrColor color,
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkRRect& rrect,
|
||||
const SkStrokeRec& stroke,
|
||||
GrShaderCaps* shaderCaps) {
|
||||
const GrShaderCaps* shaderCaps) {
|
||||
if (rrect.isOval()) {
|
||||
return CreateOvalBatch(color, viewMatrix, rrect.getBounds(), stroke, shaderCaps);
|
||||
}
|
||||
@ -1649,10 +1868,13 @@ GrDrawBatch* GrOvalRenderer::CreateOvalBatch(GrColor color,
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkRect& oval,
|
||||
const SkStrokeRec& stroke,
|
||||
GrShaderCaps* shaderCaps) {
|
||||
const GrShaderCaps* shaderCaps) {
|
||||
// we can draw circles
|
||||
if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
|
||||
return new CircleBatch(color, viewMatrix, oval, stroke);
|
||||
SkScalar width = oval.width();
|
||||
if (SkScalarNearlyEqual(width, oval.height()) && circle_stays_circle(viewMatrix)) {
|
||||
SkPoint center = {oval.centerX(), oval.centerY()};
|
||||
return CircleBatch::Create(color, viewMatrix, center, width / 2.f,
|
||||
GrStyle(stroke, nullptr));
|
||||
}
|
||||
|
||||
// if we have shader derivative support, render as device-independent
|
||||
@ -1670,13 +1892,51 @@ GrDrawBatch* GrOvalRenderer::CreateOvalBatch(GrColor color,
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GrDrawBatch* GrOvalRenderer::CreateArcBatch(GrColor color,
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkRect& oval,
|
||||
SkScalar startAngle, SkScalar sweepAngle,
|
||||
bool useCenter,
|
||||
const GrStyle& style,
|
||||
const GrShaderCaps* shaderCaps) {
|
||||
SkScalar width = oval.width();
|
||||
if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
|
||||
return nullptr;
|
||||
}
|
||||
SkPoint center = {oval.centerX(), oval.centerY()};
|
||||
CircleBatch::ArcParams arcParams = {
|
||||
SkDegreesToRadians(startAngle),
|
||||
SkDegreesToRadians(sweepAngle),
|
||||
useCenter
|
||||
};
|
||||
return CircleBatch::Create(color, viewMatrix, center, width/2.f, style, &arcParams);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef GR_TEST_UTILS
|
||||
|
||||
DRAW_BATCH_TEST_DEFINE(CircleBatch) {
|
||||
SkMatrix viewMatrix = GrTest::TestMatrix(random);
|
||||
GrColor color = GrRandomColor(random);
|
||||
SkRect circle = GrTest::TestSquare(random);
|
||||
return new CircleBatch(color, viewMatrix, circle, GrTest::TestStrokeRec(random));
|
||||
do {
|
||||
SkMatrix viewMatrix = GrTest::TestMatrix(random);
|
||||
GrColor color = GrRandomColor(random);
|
||||
SkRect circle = GrTest::TestSquare(random);
|
||||
SkPoint center = {circle.centerX(), circle.centerY()};
|
||||
SkScalar radius = circle.width() / 2.f;
|
||||
SkStrokeRec stroke = GrTest::TestStrokeRec(random);
|
||||
CircleBatch::ArcParams arcParamsTmp;
|
||||
const CircleBatch::ArcParams* arcParams = nullptr;
|
||||
if (random->nextBool()) {
|
||||
arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
|
||||
arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
|
||||
arcParams = &arcParamsTmp;
|
||||
}
|
||||
GrDrawBatch* batch = CircleBatch::Create(color, viewMatrix, center, radius,
|
||||
GrStyle(stroke, nullptr), arcParams);
|
||||
if (batch) {
|
||||
return batch;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
DRAW_BATCH_TEST_DEFINE(EllipseBatch) {
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
class GrDrawBatch;
|
||||
class GrShaderCaps;
|
||||
class GrStyle;
|
||||
class SkMatrix;
|
||||
struct SkRect;
|
||||
class SkRRect;
|
||||
@ -26,15 +27,21 @@ public:
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkRect& oval,
|
||||
const SkStrokeRec& stroke,
|
||||
GrShaderCaps* shaderCaps);
|
||||
const GrShaderCaps* shaderCaps);
|
||||
static GrDrawBatch* CreateRRectBatch(GrColor,
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkRRect& rrect,
|
||||
const SkStrokeRec& stroke,
|
||||
GrShaderCaps* shaderCaps);
|
||||
const GrShaderCaps* shaderCaps);
|
||||
|
||||
private:
|
||||
GrOvalRenderer();
|
||||
static GrDrawBatch* CreateArcBatch(GrColor,
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkRect& oval,
|
||||
SkScalar startAngle,
|
||||
SkScalar sweepAngle,
|
||||
bool useCenter,
|
||||
const GrStyle&,
|
||||
const GrShaderCaps* shaderCaps);
|
||||
};
|
||||
|
||||
#endif // GrOvalRenderer_DEFINED
|
||||
|
@ -554,6 +554,25 @@ void SkGpuDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint
|
||||
fDrawContext->drawOval(fClip, grPaint, *draw.fMatrix, oval, GrStyle(paint));
|
||||
}
|
||||
|
||||
void SkGpuDevice::drawArc(const SkDraw& draw, const SkRect& oval, SkScalar startAngle,
|
||||
SkScalar sweepAngle, bool useCenter, const SkPaint& paint) {
|
||||
ASSERT_SINGLE_OWNER
|
||||
GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawArc", fContext);
|
||||
CHECK_SHOULD_DRAW(draw);
|
||||
|
||||
if (paint.getMaskFilter()) {
|
||||
this->INHERITED::drawArc(draw, oval, startAngle, sweepAngle, useCenter, paint);
|
||||
return;
|
||||
}
|
||||
GrPaint grPaint;
|
||||
if (!SkPaintToGrPaint(this->context(), fDrawContext.get(), paint, *draw.fMatrix, &grPaint)) {
|
||||
return;
|
||||
}
|
||||
|
||||
fDrawContext->drawArc(fClip, grPaint, *draw.fMatrix, oval, startAngle, sweepAngle, useCenter,
|
||||
GrStyle(paint));
|
||||
}
|
||||
|
||||
#include "SkMaskFilter.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -77,6 +77,8 @@ public:
|
||||
const SkRRect& inner, const SkPaint& paint) override;
|
||||
virtual void drawOval(const SkDraw&, const SkRect& oval,
|
||||
const SkPaint& paint) override;
|
||||
virtual void drawArc(const SkDraw&, const SkRect& oval, SkScalar startAngle,
|
||||
SkScalar sweepAngle, bool useCenter, const SkPaint& paint) override;
|
||||
virtual void drawPath(const SkDraw&, const SkPath& path,
|
||||
const SkPaint& paint, const SkMatrix* prePathMatrix,
|
||||
bool pathIsMutable) override;
|
||||
|
Loading…
Reference in New Issue
Block a user