Add stroked ovals and CircleEdgeEffect.

Adds some optimizations to the circle and ellipse shaders, static effect 
instances for their GrEffects, and some minor changes to GrDrawState::setEffect
to make GrEffect setup faster.


git-svn-id: http://skia.googlecode.com/svn/trunk@8238 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
jvanverth@google.com 2013-03-19 18:51:02 +00:00
parent 0b735631b7
commit 65eb4d5a21
11 changed files with 475 additions and 166 deletions

View File

@ -122,6 +122,8 @@
'<(skia_src_path)/gpu/gr_unittests.cpp',
'<(skia_src_path)/gpu/effects/Gr1DKernelEffect.h',
'<(skia_src_path)/gpu/effects/GrCircleEdgeEffect.cpp',
'<(skia_src_path)/gpu/effects/GrCircleEdgeEffect.h',
'<(skia_src_path)/gpu/effects/GrConfigConversionEffect.cpp',
'<(skia_src_path)/gpu/effects/GrConfigConversionEffect.h',
'<(skia_src_path)/gpu/effects/GrConvolutionEffect.cpp',

View File

@ -894,7 +894,8 @@ private:
void internalDrawPath(const GrPaint& paint, const SkPath& path, const SkStrokeRec& stroke);
void internalDrawOval(const GrPaint& paint, const GrRect& oval, const SkStrokeRec& stroke);
bool canDrawOval(const GrPaint& paint, const GrRect& oval, const SkStrokeRec& stroke) const;
void internalDrawCircle(const GrPaint& paint, const GrRect& circle, const SkStrokeRec& stroke);
bool canDrawOval(const GrPaint& paint, const GrRect& oval, bool* isCircle) const;
GrTexture* createResizedTexture(const GrTextureDesc& desc,
const GrCacheID& cacheID,

View File

@ -116,7 +116,8 @@ public:
stage.fEffectRef->get()->incDeferredRefCounts();
fEffect = stage.fEffectRef->get();
fCoordChangeMatrix = stage.fCoordChangeMatrix;
fVertexAttribIndices = stage.fVertexAttribIndices;
fVertexAttribIndices[0] = stage.fVertexAttribIndices[0];
fVertexAttribIndices[1] = stage.fVertexAttribIndices[1];
}
SkDEBUGCODE(fInitialized = true;)
}
@ -127,7 +128,8 @@ public:
if (NULL != fEffect) {
stage->fEffectRef = GrEffect::CreateEffectRef(fEffect);
stage->fCoordChangeMatrix = fCoordChangeMatrix;
stage->fVertexAttribIndices = fVertexAttribIndices;
stage->fVertexAttribIndices[0] = fVertexAttribIndices[0];
stage->fVertexAttribIndices[1] = fVertexAttribIndices[1];
} else {
stage->fEffectRef = NULL;
}
@ -141,7 +143,8 @@ public:
return false;
}
if (fVertexAttribIndices != stage.fVertexAttribIndices) {
if (fVertexAttribIndices[0] != stage.fVertexAttribIndices[0]
|| fVertexAttribIndices[1] != stage.fVertexAttribIndices[1]) {
return false;
}
@ -155,7 +158,8 @@ public:
private:
const GrEffect* fEffect;
SkMatrix fCoordChangeMatrix;
SkSTArray<GrEffect::kMaxVertexAttribs, int, true> fVertexAttribIndices;
int fVertexAttribIndices[2];
int fVertexAttribCount;
SkDEBUGCODE(bool fInitialized;)
};
@ -169,28 +173,37 @@ public:
GrSafeSetNull(fEffectRef);
}
const GrEffectRef* setEffect(const GrEffectRef* EffectRef, const int* attribIndices = NULL) {
const GrEffectRef* setEffect(const GrEffectRef* EffectRef) {
GrAssert(0 == fSavedCoordChangeCnt);
GrSafeAssign(fEffectRef, EffectRef);
fCoordChangeMatrix.reset();
fVertexAttribIndices.reset();
int numVertexAttribs = (EffectRef == NULL) ? 0 : EffectRef->get()->numVertexAttribs();
GrAssert(numVertexAttribs == 0 || attribIndices != NULL);
fVertexAttribIndices.push_back_n(numVertexAttribs, attribIndices);
fVertexAttribIndices[0] = -1;
fVertexAttribIndices[1] = -1;
return EffectRef;
}
const GrEffectRef* setEffect(const GrEffectRef* EffectRef, int attr0, int attr1 = -1) {
GrAssert(0 == fSavedCoordChangeCnt);
GrSafeAssign(fEffectRef, EffectRef);
fCoordChangeMatrix.reset();
fVertexAttribIndices[0] = attr0;
fVertexAttribIndices[1] = attr1;
return EffectRef;
}
const GrEffectRef* getEffect() const { return fEffectRef; }
const int* getVertexAttribIndices() const { return fVertexAttribIndices.begin(); }
int getVertexAttribIndexCount() const { return fVertexAttribIndices.count(); }
const int* getVertexAttribIndices() const { return fVertexAttribIndices; }
int getVertexAttribIndexCount() const { return fEffectRef->get()->numVertexAttribs(); }
private:
SkMatrix fCoordChangeMatrix;
const GrEffectRef* fEffectRef;
SkSTArray<2, int, true> fVertexAttribIndices;
int fVertexAttribIndices[2];
GR_DEBUGCODE(mutable int fSavedCoordChangeCnt;)
};

View File

@ -12,6 +12,7 @@
#include "effects/GrConvolutionEffect.h"
#include "effects/GrSingleTextureEffect.h"
#include "effects/GrConfigConversionEffect.h"
#include "effects/GrCircleEdgeEffect.h"
#include "effects/GrEllipseEdgeEffect.h"
#include "GrBufferAllocPool.h"
@ -36,7 +37,8 @@ SK_DEFINE_INST_COUNT(GrDrawState)
// It can be useful to set this to false to test whether a bug is caused by using the
// InOrderDrawBuffer, to compare performance of using/not using InOrderDrawBuffer, or to make
// debugging simpler.
SK_CONF_DECLARE(bool, c_Defer, "gpu.deferContext", true, "Defers rendering in GrContext via GrInOrderDrawBuffer.");
SK_CONF_DECLARE(bool, c_Defer, "gpu.deferContext", true,
"Defers rendering in GrContext via GrInOrderDrawBuffer.");
#define BUFFERED_DRAW (c_Defer ? kYes_BufferedDraw : kNo_BufferedDraw)
@ -358,7 +360,8 @@ GrTexture* GrContext::createResizedTexture(const GrTextureDesc& desc,
{kVec2f_GrVertexAttribType, 0},
{kVec2f_GrVertexAttribType, sizeof(GrPoint)}
};
static const GrAttribBindings kAttribBindings = GrDrawState::ExplicitTexCoordAttribBindingsBit(0);
static const GrAttribBindings kAttribBindings =
GrDrawState::ExplicitTexCoordAttribBindingsBit(0);
drawState->setAttribBindings(kAttribBindings);
drawState->setVertexAttribs(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs));
drawState->setAttribIndex(GrDrawState::kPosition_AttribIndex, 0);
@ -389,7 +392,8 @@ GrTexture* GrContext::createResizedTexture(const GrTextureDesc& desc,
size_t stretchedRowBytes = rtDesc.fWidth * bpp;
SkDEBUGCODE(GrTexture* texture = )fGpu->createTexture(rtDesc, stretchedPixels.get(), stretchedRowBytes);
SkDEBUGCODE(GrTexture* texture = )fGpu->createTexture(rtDesc, stretchedPixels.get(),
stretchedRowBytes);
GrAssert(NULL != texture);
}
@ -999,6 +1003,15 @@ struct CircleVertex {
SkScalar fInnerRadius;
};
struct EllipseVertex {
GrPoint fPos;
GrPoint fCenter;
SkScalar fOuterXRadius;
SkScalar fOuterXYRatio;
SkScalar fInnerXRadius;
SkScalar fInnerXYRatio;
};
inline bool circleStaysCircle(const SkMatrix& m) {
return m.isSimilarity();
}
@ -1009,64 +1022,186 @@ void GrContext::drawOval(const GrPaint& paint,
const GrRect& oval,
const SkStrokeRec& stroke) {
if (!canDrawOval(paint, oval, stroke)) {
bool isCircle;
if (!canDrawOval(paint, oval, &isCircle)) {
SkPath path;
path.addOval(oval);
this->drawPath(paint, path, stroke);
return;
}
internalDrawOval(paint, oval, stroke);
if (isCircle) {
this->internalDrawCircle(paint, oval, stroke);
} else {
this->internalDrawOval(paint, oval, stroke);
}
}
bool GrContext::canDrawOval(const GrPaint& paint, const GrRect& oval, const SkStrokeRec& stroke) const {
bool GrContext::canDrawOval(const GrPaint& paint, const GrRect& oval, bool* isCircle) const {
GrAssert(isCircle != NULL);
if (!paint.isAntiAlias()) {
return false;
}
// we can draw circles in any style
bool isCircle = SkScalarNearlyEqual(oval.width(), oval.height())
&& circleStaysCircle(this->getMatrix());
// and for now, axis-aligned ellipses only with fill or stroke-and-fill
SkStrokeRec::Style style = stroke.getStyle();
bool isStroke = (style == SkStrokeRec::kStroke_Style || style == SkStrokeRec::kHairline_Style);
bool isFilledAxisAlignedEllipse = this->getMatrix().rectStaysRect() && !isStroke;
// we can draw circles
*isCircle = SkScalarNearlyEqual(oval.width(), oval.height())
&& circleStaysCircle(this->getMatrix());
// and axis-aligned ellipses only
bool isAxisAlignedEllipse = this->getMatrix().rectStaysRect();
return isCircle || isFilledAxisAlignedEllipse;
return *isCircle || isAxisAlignedEllipse;
}
void GrContext::internalDrawOval(const GrPaint& paint,
const GrRect& oval,
const SkStrokeRec& stroke) {
SkScalar xRadius = SkScalarHalf(oval.width());
SkScalar yRadius = SkScalarHalf(oval.height());
SkScalar strokeWidth = stroke.getWidth();
SkStrokeRec::Style style = stroke.getStyle();
bool isCircle = SkScalarNearlyEqual(xRadius, yRadius) && circleStaysCircle(this->getMatrix());
#ifdef SK_DEBUG
{
// we should have checked for this previously
bool isStroke = (style == SkStrokeRec::kStroke_Style || style == SkStrokeRec::kHairline_Style);
bool isFilledAxisAlignedEllipse = this->getMatrix().rectStaysRect() && !isStroke;
SkASSERT(paint.isAntiAlias() && (isCircle || isFilledAxisAlignedEllipse));
bool isAxisAlignedEllipse = this->getMatrix().rectStaysRect();
SkASSERT(paint.isAntiAlias() && isAxisAlignedEllipse);
}
#endif
GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW);
GrDrawState* drawState = target->drawState();
GrDrawState::AutoStageDisable atr(fDrawState);
const SkMatrix vm = drawState->getViewMatrix();
const GrRenderTarget* rt = drawState->getRenderTarget();
if (NULL == rt) {
return;
}
const SkMatrix vm = drawState->getViewMatrix();
GrDrawState::AutoDeviceCoordDraw adcd(drawState);
if (!adcd.succeeded()) {
return;
}
// position + edge
static const GrVertexAttrib kVertexAttribs[] = {
{kVec2f_GrVertexAttribType, 0},
{kVec2f_GrVertexAttribType, sizeof(GrPoint)},
{kVec4f_GrVertexAttribType, 2*sizeof(GrPoint)}
};
drawState->setVertexAttribs(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs));
drawState->setAttribIndex(GrDrawState::kPosition_AttribIndex, 0);
GrAssert(sizeof(EllipseVertex) == drawState->getVertexSize());
GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
if (!geo.succeeded()) {
GrPrintf("Failed to get space for vertices!\n");
return;
}
EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
GrPoint center = GrPoint::Make(oval.centerX(), oval.centerY());
vm.mapPoints(&center, 1);
SkStrokeRec::Style style = stroke.getStyle();
bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
enum {
// the edge effects share this stage with glyph rendering
// (kGlyphMaskStage in GrTextContext) && SW path rendering
// (kPathMaskStage in GrSWMaskHelper)
kEdgeEffectStage = GrPaint::kTotalStages,
};
drawState->setAttribBindings(GrDrawState::kDefault_AttribBindings);
GrEffectRef* effect = GrEllipseEdgeEffect::Create(isStroked);
static const int kEllipseCenterAttrIndex = 1;
static const int kEllipseEdgeAttrIndex = 2;
drawState->setEffect(kEdgeEffectStage, effect,
kEllipseCenterAttrIndex, kEllipseEdgeAttrIndex)->unref();
SkRect xformedRect;
vm.mapRect(&xformedRect, oval);
SkScalar xRadius = SkScalarHalf(xformedRect.width());
SkScalar yRadius = SkScalarHalf(xformedRect.height());
SkScalar innerXRadius = 0.0f;
SkScalar innerRatio = 1.0f;
if (SkStrokeRec::kFill_Style != style) {
SkScalar strokeWidth = stroke.getWidth();
// do (potentially) anisotropic mapping
SkVector scaledStroke;
scaledStroke.set(strokeWidth, strokeWidth);
vm.mapVectors(&scaledStroke, 1);
if (SkScalarNearlyZero(scaledStroke.length())) {
scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
} else {
scaledStroke.scale(0.5f);
}
// this is legit only if scale & translation (which should be the case at the moment)
if (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style) {
SkScalar innerYRadius = SkMaxScalar(0, yRadius - scaledStroke.fY);
if (innerYRadius > SK_ScalarNearlyZero) {
innerXRadius = SkMaxScalar(0, xRadius - scaledStroke.fX);
innerRatio = innerXRadius/innerYRadius;
}
}
xRadius += scaledStroke.fX;
yRadius += scaledStroke.fY;
}
SkScalar outerRatio = SkScalarDiv(xRadius, yRadius);
for (int i = 0; i < 4; ++i) {
verts[i].fCenter = center;
verts[i].fOuterXRadius = xRadius + 0.5f;
verts[i].fOuterXYRatio = outerRatio;
verts[i].fInnerXRadius = innerXRadius - 0.5f;
verts[i].fInnerXYRatio = innerRatio;
}
SkScalar L = -xRadius;
SkScalar R = +xRadius;
SkScalar T = -yRadius;
SkScalar B = +yRadius;
// We've extended the outer x radius out half a pixel to antialias.
// Expand the drawn rect here so all the pixels will be captured.
L += center.fX - SK_ScalarHalf;
R += center.fX + SK_ScalarHalf;
T += center.fY - SK_ScalarHalf;
B += center.fY + SK_ScalarHalf;
verts[0].fPos = SkPoint::Make(L, T);
verts[1].fPos = SkPoint::Make(R, T);
verts[2].fPos = SkPoint::Make(L, B);
verts[3].fPos = SkPoint::Make(R, B);
target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4);
}
void GrContext::internalDrawCircle(const GrPaint& paint,
const GrRect& circle,
const SkStrokeRec& stroke) {
SkScalar radius = SkScalarHalf(circle.width());
SkScalar strokeWidth = stroke.getWidth();
SkStrokeRec::Style style = stroke.getStyle();
GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW);
GrDrawState* drawState = target->drawState();
GrDrawState::AutoStageDisable atr(fDrawState);
const GrRenderTarget* rt = drawState->getRenderTarget();
if (NULL == rt) {
return;
}
const SkMatrix vm = drawState->getViewMatrix();
GrDrawState::AutoDeviceCoordDraw adcd(drawState);
if (!adcd.succeeded()) {
return;
@ -1089,95 +1224,54 @@ void GrContext::internalDrawOval(const GrPaint& paint,
CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
GrPoint center = GrPoint::Make(oval.centerX(), oval.centerY());
GrPoint center = GrPoint::Make(circle.centerX(), circle.centerY());
vm.mapPoints(&center, 1);
SkScalar L;
SkScalar R;
SkScalar T;
SkScalar B;
bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
enum {
// the edge effects share this stage with glyph rendering
// (kGlyphMaskStage in GrTextContext) && SW path rendering
// (kPathMaskStage in GrSWMaskHelper)
kEdgeEffectStage = GrPaint::kTotalStages,
};
drawState->setAttribBindings(GrDrawState::kDefault_AttribBindings);
GrEffectRef* effect = GrCircleEdgeEffect::Create(isStroked);
static const int kCircleEdgeAttrIndex = 1;
drawState->setEffect(kEdgeEffectStage, effect, kCircleEdgeAttrIndex)->unref();
if (isCircle) {
drawState->setAttribBindings(GrDrawState::kEdge_AttribBindingsBit);
drawState->setVertexEdgeType(GrDrawState::kCircle_EdgeType);
drawState->setAttribIndex(GrDrawState::kEdge_AttribIndex, 1);
radius = vm.mapRadius(radius);
xRadius = vm.mapRadius(xRadius);
SkScalar outerRadius = xRadius;
SkScalar innerRadius = 0;
SkScalar halfWidth = 0;
if (style != SkStrokeRec::kFill_Style) {
strokeWidth = vm.mapRadius(strokeWidth);
if (SkScalarNearlyZero(strokeWidth)) {
halfWidth = SK_ScalarHalf;
} else {
halfWidth = SkScalarHalf(strokeWidth);
}
outerRadius += halfWidth;
if (style == SkStrokeRec::kStroke_Style || style == SkStrokeRec::kHairline_Style) {
innerRadius = SkMaxScalar(0, xRadius - halfWidth);
}
SkScalar innerRadius = -2.0f;
SkScalar outerRadius = radius;
SkScalar halfWidth = 0;
if (style != SkStrokeRec::kFill_Style) {
strokeWidth = vm.mapRadius(strokeWidth);
if (SkScalarNearlyZero(strokeWidth)) {
halfWidth = SK_ScalarHalf;
} else {
halfWidth = SkScalarHalf(strokeWidth);
}
for (int i = 0; i < 4; ++i) {
verts[i].fCenter = center;
verts[i].fOuterRadius = outerRadius;
verts[i].fInnerRadius = innerRadius;
outerRadius += halfWidth;
if (isStroked) {
innerRadius = SkMaxScalar(0, radius - halfWidth);
}
L = -outerRadius;
R = +outerRadius;
T = -outerRadius;
B = +outerRadius;
} else { // is axis-aligned ellipse
drawState->setAttribBindings(GrDrawState::kDefault_AttribBindings);
enum {
// the edge effects share this stage with glyph rendering
// (kGlyphMaskStage in GrTextContext) && SW path rendering
// (kPathMaskStage in GrSWMaskHelper)
kEdgeEffectStage = GrPaint::kTotalStages,
};
GrEffectRef* effect = GrEllipseEdgeEffect::Create();
static const int kEdgeAttrIndex = 1;
drawState->setEffect(kEdgeEffectStage, effect, &kEdgeAttrIndex)->unref();
SkRect xformedRect;
vm.mapRect(&xformedRect, oval);
xRadius = SkScalarHalf(xformedRect.width());
yRadius = SkScalarHalf(xformedRect.height());
if (style == SkStrokeRec::kStrokeAndFill_Style && strokeWidth > 0.0f) {
SkScalar halfWidth = SkScalarHalf(strokeWidth);
// do (potentially) anisotropic mapping
SkVector scaledStroke;
scaledStroke.set(halfWidth, halfWidth);
vm.mapVectors(&scaledStroke, 1);
// this is legit only if scale & translation (which should be the case at the moment)
xRadius += scaledStroke.fX;
yRadius += scaledStroke.fY;
}
SkScalar ratio = SkScalarDiv(xRadius, yRadius);
for (int i = 0; i < 4; ++i) {
verts[i].fCenter = center;
verts[i].fOuterRadius = xRadius;
verts[i].fInnerRadius = ratio;
}
L = -xRadius;
R = +xRadius;
T = -yRadius;
B = +yRadius;
}
// The fragment shader will extend the radius out half a pixel
// to antialias. Expand the drawn rect here so all the pixels
// will be captured.
for (int i = 0; i < 4; ++i) {
verts[i].fCenter = center;
verts[i].fOuterRadius = outerRadius + 0.5f;
verts[i].fInnerRadius = innerRadius - 0.5f;
}
SkScalar L = -outerRadius;
SkScalar R = +outerRadius;
SkScalar T = -outerRadius;
SkScalar B = +outerRadius;
// We've extended the outer radius out half a pixel to antialias.
// Expand the drawn rect here so all the pixels will be captured.
L += center.fX - SK_ScalarHalf;
R += center.fX + SK_ScalarHalf;
T += center.fY - SK_ScalarHalf;
@ -1203,15 +1297,21 @@ void GrContext::drawPath(const GrPaint& paint, const SkPath& path, const SkStrok
SkRect ovalRect;
bool isOval = path.isOval(&ovalRect);
if (isOval && !path.isInverseFillType() && this->canDrawOval(paint, ovalRect, stroke)) {
this->drawOval(paint, ovalRect, stroke);
bool isCircle;
if (isOval && !path.isInverseFillType() && this->canDrawOval(paint, ovalRect, &isCircle)) {
if (isCircle) {
this->internalDrawCircle(paint, ovalRect, stroke);
} else {
this->internalDrawOval(paint, ovalRect, stroke);
}
return;
}
this->internalDrawPath(paint, path, stroke);
}
void GrContext::internalDrawPath(const GrPaint& paint, const SkPath& path, const SkStrokeRec& stroke) {
void GrContext::internalDrawPath(const GrPaint& paint, const SkPath& path,
const SkStrokeRec& stroke) {
// Note that below we may sw-rasterize the path into a scratch texture.
// Scratch textures can be recycled after they are returned to the texture

View File

@ -485,9 +485,14 @@ public:
/// @name Effect Stages
////
const GrEffectRef* setEffect(int stageIdx, const GrEffectRef* effect) {
fStages[stageIdx].setEffect(effect);
return effect;
}
const GrEffectRef* setEffect(int stageIdx, const GrEffectRef* effect,
const int* indices = NULL) {
fStages[stageIdx].setEffect(effect, indices);
int attr0, int attr1 = -1) {
fStages[stageIdx].setEffect(effect, attr0, attr1);
return effect;
}
@ -518,7 +523,7 @@ public:
}
void disableStage(int stageIdx) {
this->setEffect(stageIdx, NULL, NULL);
this->setEffect(stageIdx, NULL);
}
/**
@ -1023,9 +1028,6 @@ public:
/* Same as above but for hairline quadratics. Uses unsigned distance.
Coverage is min(0, 1-distance). */
kHairQuad_EdgeType,
/* Circle specified as center_x, center_y, outer_radius, inner_radius
all in window space (y-down). */
kCircle_EdgeType,
kVertexEdgeTypeCnt
};

View File

@ -0,0 +1,87 @@
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrCircleEdgeEffect.h"
#include "gl/GrGLEffect.h"
#include "gl/GrGLEffectMatrix.h"
#include "gl/GrGLSL.h"
#include "gl/GrGLTexture.h"
#include "GrTBackendEffectFactory.h"
#include "GrTexture.h"
#include "SkRTConf.h"
class GrGLCircleEdgeEffect : public GrGLEffect {
public:
GrGLCircleEdgeEffect(const GrBackendEffectFactory& factory, const GrEffectRef&)
: INHERITED (factory) {}
virtual void emitCode(GrGLShaderBuilder* builder,
const GrEffectStage& stage,
EffectKey key,
const char* vertexCoords,
const char* outputColor,
const char* inputColor,
const TextureSamplerArray& samplers) SK_OVERRIDE {
const GrCircleEdgeEffect& effect = GetEffectFromStage<GrCircleEdgeEffect>(stage);
const char *vsName, *fsName;
builder->addVarying(kVec4f_GrSLType, "CircleEdge", &vsName, &fsName);
const SkString* attrName =
builder->getEffectAttributeName(stage.getVertexAttribIndices()[0]);
builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str());
builder->fsCodeAppendf("\tfloat d = distance(%s.xy, %s.xy);\n",
builder->fragmentPosition(), fsName);
builder->fsCodeAppendf("\tfloat edgeAlpha = clamp(%s.z - d, 0.0, 1.0);\n", fsName);
if (effect.isStroked()) {
builder->fsCodeAppendf("\tfloat innerAlpha = clamp(d - %s.w, 0.0, 1.0);\n", fsName);
builder->fsCodeAppend("\tedgeAlpha *= innerAlpha;\n");
}
SkString modulate;
GrGLSLModulate4f(&modulate, inputColor, "edgeAlpha");
builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
}
static inline EffectKey GenKey(const GrEffectStage& stage, const GrGLCaps&) {
const GrCircleEdgeEffect& effect = GetEffectFromStage<GrCircleEdgeEffect>(stage);
return effect.isStroked() ? 0x1 : 0x0;
}
virtual void setData(const GrGLUniformManager& uman, const GrEffectStage& stage) SK_OVERRIDE {
}
private:
typedef GrGLEffect INHERITED;
};
///////////////////////////////////////////////////////////////////////////////
GrCircleEdgeEffect::GrCircleEdgeEffect(bool stroke) : GrEffect() {
this->addVertexAttrib(kVec4f_GrSLType);
fStroke = stroke;
}
void GrCircleEdgeEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
*validFlags = 0;
}
const GrBackendEffectFactory& GrCircleEdgeEffect::getFactory() const {
return GrTBackendEffectFactory<GrCircleEdgeEffect>::getInstance();
}
///////////////////////////////////////////////////////////////////////////////
GR_DEFINE_EFFECT_TEST(GrCircleEdgeEffect);
GrEffectRef* GrCircleEdgeEffect::TestCreate(SkMWCRandom* random,
GrContext* context,
GrTexture* textures[]) {
return GrCircleEdgeEffect::Create(random->nextBool());
}

View File

@ -0,0 +1,65 @@
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrCircleEdgeEffect_DEFINED
#define GrCircleEdgeEffect_DEFINED
#include "GrEffect.h"
class GrGLCircleEdgeEffect;
/**
* The output of this effect is a modulation of the input color and coverage for a circle,
* specified as center_x, center_y, x_radius, inner radius and outer radius in window space
* (y-down).
*/
class GrCircleEdgeEffect : public GrEffect {
public:
static GrEffectRef* Create(bool stroke) {
// we go through this so we only have one copy of each effect (stroked/filled)
static SkAutoTUnref<GrEffectRef> gCircleStrokeEdgeEffectRef(
CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(GrCircleEdgeEffect, (true)))));
static SkAutoTUnref<GrEffectRef> gCircleFillEdgeEffectRef(
CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(GrCircleEdgeEffect, (false)))));
if (stroke) {
gCircleStrokeEdgeEffectRef.get()->ref();
return gCircleStrokeEdgeEffectRef;
} else {
gCircleFillEdgeEffectRef.get()->ref();
return gCircleFillEdgeEffectRef;
}
}
virtual ~GrCircleEdgeEffect() {}
static const char* Name() { return "CircleEdge"; }
virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
typedef GrGLCircleEdgeEffect GLEffect;
virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
inline bool isStroked() const { return fStroke; }
private:
GrCircleEdgeEffect(bool stroke);
virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
return true;
}
bool fStroke;
GR_DECLARE_EFFECT_TEST;
typedef GrEffect INHERITED;
};
#endif

View File

@ -25,27 +25,49 @@ public:
const char* outputColor,
const char* inputColor,
const TextureSamplerArray& samplers) SK_OVERRIDE {
const char *vsName, *fsName;
builder->addVarying(kVec4f_GrSLType, "EllipseEdge", &vsName, &fsName);
const GrEllipseEdgeEffect& effect = GetEffectFromStage<GrEllipseEdgeEffect>(stage);
const char *vsCenterName, *fsCenterName;
const char *vsEdgeName, *fsEdgeName;
const SkString* attrName = builder->getEffectAttributeName(stage.getVertexAttribIndices()[0]);
builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str());
builder->addVarying(kVec2f_GrSLType, "EllipseCenter", &vsCenterName, &fsCenterName);
const SkString* attr0Name =
builder->getEffectAttributeName(stage.getVertexAttribIndices()[0]);
builder->vsCodeAppendf("\t%s = %s;\n", vsCenterName, attr0Name->c_str());
builder->addVarying(kVec4f_GrSLType, "EllipseEdge", &vsEdgeName, &fsEdgeName);
const SkString* attr1Name =
builder->getEffectAttributeName(stage.getVertexAttribIndices()[1]);
builder->vsCodeAppendf("\t%s = %s;\n", vsEdgeName, attr1Name->c_str());
builder->fsCodeAppend("\tfloat edgeAlpha;\n");
// translate to origin
builder->fsCodeAppendf("\tvec2 offset = (%s.xy - %s.xy);\n", builder->fragmentPosition(), fsName);
builder->fsCodeAppendf("\tvec2 outerOffset = (%s.xy - %s.xy);\n",
builder->fragmentPosition(), fsCenterName);
builder->fsCodeAppend("\tvec2 innerOffset = outerOffset;\n");
// scale y by xRadius/yRadius
builder->fsCodeAppendf("\toffset.y *= %s.w;\n", fsName);
builder->fsCodeAppend("\tfloat d = length(offset);\n");
// compare length against xRadius
builder->fsCodeAppendf("\tedgeAlpha = smoothstep(d - 0.5, d + 0.5, %s.z);\n", fsName);
builder->fsCodeAppendf("\touterOffset.y *= %s.y;\n", fsEdgeName);
builder->fsCodeAppend("\tfloat dOuter = length(outerOffset);\n");
// compare outer lengths against xOuterRadius
builder->fsCodeAppendf("\tfloat edgeAlpha = clamp(%s.x-dOuter, 0.0, 1.0);\n", fsEdgeName);
if (effect.isStroked()) {
builder->fsCodeAppendf("\tinnerOffset.y *= %s.w;\n", fsEdgeName);
builder->fsCodeAppend("\tfloat dInner = length(innerOffset);\n");
// compare inner lengths against xInnerRadius
builder->fsCodeAppendf("\tfloat innerAlpha = clamp(dInner-%s.z, 0.0, 1.0);\n", fsEdgeName);
builder->fsCodeAppend("\tedgeAlpha *= innerAlpha;\n");
}
SkString modulate;
GrGLSLModulate4f(&modulate, inputColor, "edgeAlpha");
builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
}
static inline EffectKey GenKey(const GrEffectStage& stage, const GrGLCaps&) {
return 0;
const GrEllipseEdgeEffect& effect = GetEffectFromStage<GrEllipseEdgeEffect>(stage);
return effect.isStroked() ? 0x1 : 0x0;
}
virtual void setData(const GrGLUniformManager& uman, const GrEffectStage& stage) SK_OVERRIDE {
@ -57,8 +79,11 @@ private:
///////////////////////////////////////////////////////////////////////////////
GrEllipseEdgeEffect::GrEllipseEdgeEffect() : GrEffect() {
GrEllipseEdgeEffect::GrEllipseEdgeEffect(bool stroke) : GrEffect() {
this->addVertexAttrib(kVec2f_GrSLType);
this->addVertexAttrib(kVec4f_GrSLType);
fStroke = stroke;
}
void GrEllipseEdgeEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
@ -76,5 +101,5 @@ GR_DEFINE_EFFECT_TEST(GrEllipseEdgeEffect);
GrEffectRef* GrEllipseEdgeEffect::TestCreate(SkMWCRandom* random,
GrContext* context,
GrTexture* textures[]) {
return GrEllipseEdgeEffect::Create();
return GrEllipseEdgeEffect::Create(random->nextBool());
}

View File

@ -19,10 +19,20 @@ class GrGLEllipseEdgeEffect;
class GrEllipseEdgeEffect : public GrEffect {
public:
static GrEffectRef* Create() {
// maybe only have one static copy?
AutoEffectUnref effect(SkNEW(GrEllipseEdgeEffect));
return CreateEffectRef(effect);
static GrEffectRef* Create(bool stroke) {
// we go through this so we only have one copy of each effect (stroked/filled)
static SkAutoTUnref<GrEffectRef> gEllipseStrokeEdgeEffectRef(
CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(GrEllipseEdgeEffect, (true)))));
static SkAutoTUnref<GrEffectRef> gEllipseFillEdgeEffectRef(
CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(GrEllipseEdgeEffect, (false)))));
if (stroke) {
gEllipseStrokeEdgeEffectRef.get()->ref();
return gEllipseStrokeEdgeEffectRef;
} else {
gEllipseFillEdgeEffectRef.get()->ref();
return gEllipseFillEdgeEffectRef;
}
}
virtual ~GrEllipseEdgeEffect() {}
@ -35,13 +45,17 @@ public:
virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
inline bool isStroked() const { return fStroke; }
private:
GrEllipseEdgeEffect();
GrEllipseEdgeEffect(bool stroke);
virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE {
return true;
}
bool fStroke;
GR_DECLARE_EFFECT_TEST;
typedef GrEffect INHERITED;

View File

@ -23,7 +23,8 @@ SK_DEFINE_INST_COUNT(GrGLProgram)
#define GL_CALL(X) GR_GL_CALL(fContext.interface(), X)
#define GL_CALL_RET(R, X) GR_GL_CALL_RET(fContext.interface(), R, X)
SK_CONF_DECLARE(bool, c_PrintShaders, "gpu.printShaders", false, "Print the source code for all shaders generated.");
SK_CONF_DECLARE(bool, c_PrintShaders, "gpu.printShaders", false,
"Print the source code for all shaders generated.");
#define TEX_ATTR_NAME "aTexCoord"
#define COL_ATTR_NAME "aColor"
@ -69,15 +70,16 @@ void GrGLProgram::BuildDesc(const GrDrawState& drawState,
desc->fEmitsPointSize = isPoints;
bool requiresAttributeColors = !skipColor &&
SkToBool(desc->fAttribBindings & GrDrawState::kColor_AttribBindingsBit);
bool requiresAttributeCoverage = !skipCoverage &&
SkToBool(desc->fAttribBindings & GrDrawState::kCoverage_AttribBindingsBit);
bool requiresAttributeColors =
!skipColor && SkToBool(desc->fAttribBindings & GrDrawState::kColor_AttribBindingsBit);
bool requiresAttributeCoverage =
!skipCoverage && SkToBool(desc->fAttribBindings & GrDrawState::kCoverage_AttribBindingsBit);
// fColorInput/fCoverageInput records how colors are specified for the program So we strip the
// bits from the bindings to avoid false negatives when searching for an existing program in the
// cache.
desc->fAttribBindings &= ~(GrDrawState::kColor_AttribBindingsBit | GrDrawState::kCoverage_AttribBindingsBit);
desc->fAttribBindings &=
~(GrDrawState::kColor_AttribBindingsBit | GrDrawState::kCoverage_AttribBindingsBit);
desc->fColorFilterXfermode = skipColor ?
SkXfermode::kDst_Mode :
@ -423,7 +425,8 @@ bool GrGLProgram::genEdgeCoverage(SkString* coverageVar,
builder->vsCodeAppendf("\t%s = " EDGE_ATTR_NAME ";\n", vsName);
switch (fDesc.fVertexEdgeType) {
case GrDrawState::kHairLine_EdgeType:
builder->fsCodeAppendf("\tfloat edgeAlpha = abs(dot(vec3(%s.xy,1), %s.xyz));\n", builder->fragmentPosition(), fsName);
builder->fsCodeAppendf("\tfloat edgeAlpha = abs(dot(vec3(%s.xy,1), %s.xyz));\n",
builder->fragmentPosition(), fsName);
builder->fsCodeAppendf("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
break;
case GrDrawState::kQuad_EdgeType:
@ -433,7 +436,8 @@ bool GrGLProgram::genEdgeCoverage(SkString* coverageVar,
builder->fsCodeAppendf("\tvec2 duvdy = dFdy(%s.xy);\n", fsName);
builder->fsCodeAppendf("\tif (%s.z > 0.0 && %s.w > 0.0) {\n", fsName, fsName);
// today we know z and w are in device space. We could use derivatives
builder->fsCodeAppendf("\t\tedgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);\n", fsName, fsName);
builder->fsCodeAppendf("\t\tedgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);\n", fsName,
fsName);
builder->fsCodeAppendf ("\t} else {\n");
builder->fsCodeAppendf("\t\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n"
"\t\t 2.0*%s.x*duvdy.x - duvdy.y);\n",
@ -451,20 +455,14 @@ bool GrGLProgram::genEdgeCoverage(SkString* coverageVar,
builder->fsCodeAppendf("\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n"
"\t 2.0*%s.x*duvdy.x - duvdy.y);\n",
fsName, fsName);
builder->fsCodeAppendf("\tfloat edgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName, fsName);
builder->fsCodeAppendf("\tfloat edgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName,
fsName);
builder->fsCodeAppend("\tedgeAlpha = sqrt(edgeAlpha*edgeAlpha / dot(gF, gF));\n");
builder->fsCodeAppend("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
if (kES2_GrGLBinding == fContext.info().binding()) {
builder->fHeader.printf("#extension GL_OES_standard_derivatives: enable\n");
}
break;
case GrDrawState::kCircle_EdgeType:
builder->fsCodeAppend("\tfloat edgeAlpha;\n");
builder->fsCodeAppendf("\tfloat d = distance(%s.xy, %s.xy);\n", builder->fragmentPosition(), fsName);
builder->fsCodeAppendf("\tfloat outerAlpha = smoothstep(d - 0.5, d + 0.5, %s.z);\n", fsName);
builder->fsCodeAppendf("\tfloat innerAlpha = %s.w == 0.0 ? 1.0 : smoothstep(%s.w - 0.5, %s.w + 0.5, d);\n", fsName, fsName, fsName);
builder->fsCodeAppend("\tedgeAlpha = outerAlpha * innerAlpha;\n");
break;
default:
GrCrash("Unknown Edge Type!");
break;
@ -905,7 +903,9 @@ bool GrGLProgram::genProgram(const GrEffectStage* stages[]) {
// discard if coverage is zero
if (fDesc.fDiscardIfOutsideEdge && !outCoverage.isEmpty()) {
builder.fsCodeAppendf("\tif (all(lessThanEqual(%s, vec4(0.0)))) {\n\t\tdiscard;\n\t}\n", outCoverage.c_str());
builder.fsCodeAppendf(
"\tif (all(lessThanEqual(%s, vec4(0.0)))) {\n\t\tdiscard;\n\t}\n",
outCoverage.c_str());
}
}

View File

@ -134,7 +134,7 @@ bool GrGpuGL::programUnitTest(int maxStages) {
for (int i = 0; i < effect.get()->get()->numVertexAttribs(); ++i) {
attribIndices[i] = currAttribIndex++;
}
stages[s].setEffect(effect.get(), attribIndices);
stages[s].setEffect(effect.get(), attribIndices[0], attribIndices[1]);
}
}
pdesc.setRandom(&random, this, stages);