aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gpu/include/GrClip.h152
-rw-r--r--gpu/include/GrClipIterator.h50
-rw-r--r--gpu/include/GrContext.h13
-rw-r--r--gpu/include/GrDrawTarget.h159
-rw-r--r--gpu/include/GrGLTexture.h9
-rw-r--r--gpu/include/GrGpu.h122
-rw-r--r--gpu/include/GrPath.h4
-rw-r--r--gpu/include/GrPathIter.h30
-rw-r--r--gpu/include/GrRect.h107
-rw-r--r--gpu/include/GrStencil.h211
-rw-r--r--gpu/include/GrTArray.h2
-rw-r--r--gpu/include/GrTexture.h32
-rw-r--r--gpu/include/GrTypes.h20
-rw-r--r--gpu/src/GrClip.cpp167
-rw-r--r--gpu/src/GrContext.cpp21
-rw-r--r--gpu/src/GrDrawTarget.cpp8
-rw-r--r--gpu/src/GrGLTexture.cpp6
-rw-r--r--gpu/src/GrGpu.cpp315
-rw-r--r--gpu/src/GrGpuGL.cpp480
-rw-r--r--gpu/src/GrGpuGL.h3
-rw-r--r--gpu/src/GrInOrderDrawBuffer.cpp26
-rw-r--r--gpu/src/GrPath.cpp25
-rw-r--r--gpu/src/GrPathRenderer.cpp279
-rw-r--r--gpu/src/GrPathRenderer.h84
-rw-r--r--gpu/src/GrStencil.cpp398
-rw-r--r--gpu/src/GrTextContext.cpp20
-rw-r--r--gpu/src/gr_files.mk3
-rw-r--r--gpu/src/gr_unittests.cpp62
-rw-r--r--include/core/SkCanvas.h3
-rw-r--r--include/core/SkClipStack.h10
-rw-r--r--include/core/SkDeque.h7
-rw-r--r--include/core/SkDevice.h3
-rw-r--r--include/core/SkRegion.h2
-rw-r--r--include/gpu/SkGpuDevice.h3
-rw-r--r--include/gpu/SkGr.h87
-rw-r--r--src/core/SkCanvas.cpp7
-rw-r--r--src/core/SkClipStack.cpp10
-rw-r--r--src/core/SkDeque.cpp20
-rw-r--r--src/gpu/SkGpuDevice.cpp38
-rw-r--r--src/gpu/SkGr.cpp74
-rw-r--r--src/utils/mac/SkOSWindow_Mac.cpp1
-rw-r--r--src/utils/win/SkOSWindow_Win.cpp7
-rw-r--r--vs/SampleApp/SampleApp.vcxproj3
-rw-r--r--xcode/gpu/gpu.xcodeproj/project.pbxproj12
-rw-r--r--xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj16
45 files changed, 2301 insertions, 810 deletions
diff --git a/gpu/include/GrClip.h b/gpu/include/GrClip.h
index 8e3030c753..414a6d6806 100644
--- a/gpu/include/GrClip.h
+++ b/gpu/include/GrClip.h
@@ -20,96 +20,116 @@
#include "GrClipIterator.h"
#include "GrRect.h"
-#include "GrTDArray.h"
+#include "GrPath.h"
+#include "GrTArray.h"
+
class GrClip {
public:
GrClip();
GrClip(const GrClip& src);
- GrClip(GrClipIterator* iter);
+ GrClip(GrClipIterator* iter, const GrRect* bounds = NULL);
+ GrClip(const GrIRect& rect);
+ GrClip(const GrRect& rect);
+
~GrClip();
GrClip& operator=(const GrClip& src);
- bool isEmpty() const { return fBounds.isEmpty(); }
- bool isComplex() const { return fList.count() > 0; }
- bool isRect() const {
- return !this->isEmpty() && !this->isComplex();
- }
-
- const GrIRect& getBounds() const { return fBounds; }
+ bool hasBounds() const { return fBoundsValid; }
- /**
- * Resets this clip to be empty (fBounds is empty, and fList is empty)
- */
- void setEmpty();
+ const GrRect& getBounds() const { return fBounds; }
- /**
- * Resets this clip to have fBounds == rect, and fList is empty.
- */
- void setRect(const GrIRect& rect);
+ int getElementCount() const { return fList.count(); }
- /**
- * Append a rect to an existing clip. The call must ensure that rect does
- * not overlap with any previous rect in this clip (either from setRect
- * or addRect). fBounds is automatically updated to reflect the union of
- * all rects that have been added.
- */
- void addRect(const GrIRect&);
+ GrClipType getElementType(int i) const { return fList[i].fType; }
- void setFromIterator(GrClipIterator* iter);
+ const GrPath& getPath(int i) const {
+ GrAssert(kPath_ClipType == fList[i].fType);
+ return fList[i].fPath;
+ }
- friend bool operator==(const GrClip& a, const GrClip& b) {
- return a.fBounds == b.fBounds && a.fList == b.fList;
+ GrPathFill getPathFill(int i) const {
+ GrAssert(kPath_ClipType == fList[i].fType);
+ return fList[i].fPathFill;
}
- friend bool operator!=(const GrClip& a, const GrClip& b) {
- return !(a == b);
+
+ const GrRect& getRect(int i) const {
+ GrAssert(kRect_ClipType == fList[i].fType);
+ return fList[i].fRect;
}
- /**
- * Return the number of rects in this clip: 0 for empty, 1 for a rect,
- * or N for a complex clip.
- */
- int countRects() const {
- return this->isEmpty() ? 0 : GrMax<int>(1, fList.count());
+ const GrSetOp getOp(int i) const { return fList[i].fOp; }
+
+ bool isRect() const {
+ if (1 == fList.count() && kRect_ClipType == fList[0].fType) {
+ GrAssert(fBoundsValid);
+ GrAssert(fBounds == fList[0].fRect);
+ return true;
+ } else {
+ return false;
+ }
}
+ bool isEmpty() const { return 0 == fList.count(); }
+
/**
- * Return an array of rects for this clip. Use countRects() to know the
- * number of entries.
+ * Resets this clip to be empty
*/
- const GrIRect* getRects() const {
- return fList.count() > 0 ? fList.begin() : &fBounds;
- }
-
-#if GR_DEBUG
- void validate() const;
-#else
- void validate() const {}
-#endif
+ void setEmpty();
+ void setFromIterator(GrClipIterator* iter, const GrRect* bounds = NULL);
+ void setFromRect(const GrRect& rect);
+ void setFromIRect(const GrIRect& rect);
-private:
- GrTDArray<GrIRect> fList;
- GrIRect fBounds;
-};
+ friend bool operator==(const GrClip& a, const GrClip& b) {
+ if (a.fList.count() != b.fList.count()) {
+ return false;
+ }
+ int count = a.fList.count();
+ for (int i = 0; i < count; ++i) {
+ if (a.fList[i] != b.fList[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ friend bool operator!=(const GrClip& a, const GrClip& b) {
+ return !(a == b);
+ }
-class GrClipIter : public GrClipIterator {
-public:
- GrClipIter(const GrClip& clip) : fClip(&clip), fIndex(0) {}
- GrClipIter() : fClip(NULL), fIndex(0) {}
-
- void reset(const GrClip& clip);
-
- virtual bool isDone();
- virtual void rewind();
- virtual void getRect(GrIRect* r);
- virtual void next();
- virtual void computeBounds(GrIRect* r);
-
private:
- const GrClip* fClip;
- int fIndex;
+ struct Element {
+ GrClipType fType;
+ GrRect fRect;
+ GrPath fPath;
+ GrPathFill fPathFill;
+ GrSetOp fOp;
+ bool operator ==(const Element& e) const {
+ if (e.fType != fType || e.fOp != fOp) {
+ return false;
+ }
+ switch (fType) {
+ case kRect_ClipType:
+ return fRect == e.fRect;
+ break;
+ case kPath_ClipType:
+ return fPath == e.fPath;
+ default:
+ GrCrash("Unknown clip element type.");
+ return false; // suppress warning
+ }
+ }
+ bool operator !=(const Element& e) const { return !(*this == e); }
+ };
+
+ GrRect fBounds;
+ bool fBoundsValid;
+
+ enum {
+ kPreAllocElements = 4,
+ };
+ uint8_t fListMemory[sizeof(Element) * kPreAllocElements];
+ GrTArray<Element> fList;
};
-
#endif
diff --git a/gpu/include/GrClipIterator.h b/gpu/include/GrClipIterator.h
index d1fe4dde2f..abad619623 100644
--- a/gpu/include/GrClipIterator.h
+++ b/gpu/include/GrClipIterator.h
@@ -18,54 +18,60 @@
#ifndef GrClipIterator_DEFINED
#define GrClipIterator_DEFINED
+#include "GrPath.h"
#include "GrRect.h"
+/**
+ * A clip is a list of paths and/or rects with set operations to combine them.
+ */
class GrClipIterator {
public:
- GrClipIterator() : fNeedBounds(true) {}
virtual ~GrClipIterator() {}
/**
* Returns true if there are no more rects to process
*/
- virtual bool isDone() = 0;
+ virtual bool isDone() const = 0;
/**
- * Rewind the iterate to replay the set of rects again
+ * Rewind the iterator to replay the set of clip elements again
*/
virtual void rewind() = 0;
/**
- * Return the current rect. It is an error to call this when done() is true
+ * Get the type of the current clip element
*/
- virtual void getRect(GrIRect*) = 0;
+ virtual GrClipType getType() const = 0;
/**
- * Call to move to the next rect in the set
+ * Return the current path. It is an error to call this when isDone() is
+ * true or when getType() is kRect_Type.
*/
- virtual void next() = 0;
+ virtual GrPathIter* getPathIter() = 0;
/**
- * Set bounds to be the bounds of the clip.
+ * Return the fill rule for the path. It is an error to call this when
+ * isDone() is true or when getType is kRect_Type.
*/
- virtual void computeBounds(GrIRect* bounds) = 0;
+ virtual GrPathFill getPathFill() const = 0;
+
+ /**
+ * Return the current rect. It is an error to call this when isDone is true
+ * or when getType() is kPath_Type.
+ */
+ virtual void getRect(GrRect* rect) const = 0;
/**
- * Subclass should call this whenever their underlying bounds has changed.
+ * Gets the operation used to apply the current item to previously iterated
+ * items. Iterators should not produce a Replace op.
*/
- void invalidateBoundsCache() { fNeedBounds = true; }
-
- const GrIRect& getBounds() {
- if (fNeedBounds) {
- this->computeBounds(&fBounds);
- fNeedBounds = false;
- }
- return fBounds;
- }
+ virtual GrSetOp getOp() const = 0;
-private:
- GrIRect fBounds;
- bool fNeedBounds;
+ /**
+ * Call to move to the next rect in the set, previous path iter can be made
+ * invalid.
+ */
+ virtual void next() = 0;
};
/**
diff --git a/gpu/include/GrContext.h b/gpu/include/GrContext.h
index caba6efcec..b43375cb48 100644
--- a/gpu/include/GrContext.h
+++ b/gpu/include/GrContext.h
@@ -278,16 +278,23 @@ public:
const GrMatrix* srcMatrix = NULL);
/**
- * Tessellates and draws a path.
+ * Draws a path.
*
* @param paint describes how to color pixels.
- * @param path the path to draw
+ * @param pathIter the path to draw
* @param fill the path filling rule to use.
* @param translate optional additional translation applied to the
* path.
*/
void drawPath(const GrPaint& paint,
- GrPathIter* path,
+ GrPathIter* pathIter,
+ GrPathFill fill,
+ const GrPoint* translate = NULL);
+ /**
+ * Helper version of drawPath that takes a GrPath
+ */
+ void drawPath(const GrPaint& paint,
+ const GrPath& path,
GrPathFill fill,
const GrPoint* translate = NULL);
/**
diff --git a/gpu/include/GrDrawTarget.h b/gpu/include/GrDrawTarget.h
index 10c6d48462..576bd7ae41 100644
--- a/gpu/include/GrDrawTarget.h
+++ b/gpu/include/GrDrawTarget.h
@@ -23,9 +23,10 @@
#include "GrRefCnt.h"
#include "GrSamplerState.h"
#include "GrClip.h"
+#include "GrTexture.h"
+#include "GrStencil.h"
class GrTexture;
-class GrRenderTarget;
class GrClipIterator;
class GrVertexBuffer;
class GrIndexBuffer;
@@ -72,56 +73,63 @@ public:
kClip_StateBit = 0x4,//<! Controls whether drawing is clipped
// against the region specified by
// setClip.
+ kNoColorWrites_StateBit = 0x8,//<! If set it disables writing colors.
+ // Useful while performing stencil ops.
+
+ // subclass may use additional bits internally
+ kDummyStateBit,
+ kLastPublicStateBit = kDummyStateBit-1
+ };
+
+ enum DrawFace {
+ kBoth_DrawFace,
+ kCCW_DrawFace,
+ kCW_DrawFace,
};
/**
- * StencilPass
- *
- * Sets the stencil state for subsequent draw calls. Used to fill paths.
- *
- * Winding requires two passes when the GPU/API doesn't support separate
- * stencil.
- *
- * The color pass for path fill is used to zero out stencil bits used for
- * path filling. Every pixel covere by a winding/EO stencil pass must get
- * covered by the color pass in order to leave stencil buffer in the correct
- * state for the next path draw.
- *
- * NOTE: Stencil-based Winding fill has alias-to-zero problems. (e.g. A
- * winding count of 128,256,512,etc with a 8 bit stencil buffer
- * will be unfilled)
+ * The DrawTarget may reserve some of the high bits of the stencil. The draw
+ * target will automatically trim reference and mask values so that the
+ * client doesn't overwrite these bits.
+ * The number of bits available is relative to the currently set render
+ *target.
+ * @return the number of bits usable by the draw target client.
*/
- enum StencilPass {
- kNone_StencilPass, //<! Not drawing a path or clip.
- kEvenOddStencil_StencilPass, //<! records in/out in stencil buffer
- // using the Even/Odd fill rule.
- kEvenOddColor_StencilPass, //<! writes colors to color target in
- // pixels marked inside the fill by
- // kEOFillStencil_StencilPass. Clears
- // stencil in pixels covered by
- // geometry.
- kWindingStencil1_StencilPass, //<! records in/out in stencil buffer
- // using the Winding fill rule.
- kWindingStencil2_StencilPass, //<! records in/out in stencil buffer
- // using the Winding fill rule.
- // Run when single-stencil-pass winding
- // not supported (i.e. no separate
- // stencil support)
- kWindingColor_StencilPass, //<! writes colors to color target in
- // pixels marked inside the fill by
- // kWindFillStencil_StencilPass. Clears
- // stencil in pixels covered by
- // geometry.
- kDrawTargetCount_StencilPass //<! Subclass may extend this enum to use
- // the stencil for other purposes (e.g.
- // to do stencil-based clipping)
- // This value is provided as basis for
- // defining these extended enum values.
- };
+ int getUsableStencilBits() const {
+ int bits = fCurrDrawState.fRenderTarget->stencilBits();
+ if (bits) {
+ return bits - 1;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Sets the stencil settings to use for the next draw.
+ * @param settings the stencil settings to use.
+ */
+ void setStencil(const GrStencilSettings& settings) {
+ fCurrDrawState.fStencilSettings = settings;
+ }
+
+ /**
+ * Shortcut to disable stencil testing and ops.
+ */
+ void disableStencil() {
+ fCurrDrawState.fStencilSettings.setDisabled();
+ }
protected:
struct DrState {
+ DrState() {
+ // make sure any pad is zero for memcmp
+ // all DrState members should default to something
+ // valid by the memset
+ memset(this, 0, sizeof(DrState));
+ GrAssert((intptr_t)(void*)NULL == 0LL);
+ GrAssert(fStencilSettings.isDisabled());
+ }
uint32_t fFlagBits;
GrBlendCoeff fSrcBlend;
GrBlendCoeff fDstBlend;
@@ -129,8 +137,9 @@ protected:
GrSamplerState fSamplerStates[kNumStages];
GrRenderTarget* fRenderTarget;
GrColor fColor;
- StencilPass fStencilPass;
- bool fReverseFill;
+ DrawFace fDrawFace;
+
+ GrStencilSettings fStencilSettings;
GrMatrix fViewMatrix;
bool operator ==(const DrState& s) const {
return 0 == memcmp(this, &s, sizeof(DrState));
@@ -196,7 +205,7 @@ public:
/**
* Sets the sampler state for a stage used in subsequent draws.
*
- * The sampler state determines how texture coordinates are
+ * The sampler state determines how texture coordinates are
* intepretted and used to sample the texture.
*
* @param stage the stage of the sampler to set
@@ -291,19 +300,17 @@ public:
void setAlpha(uint8_t alpha);
/**
- * Sets pass for path rendering
- *
- * @param pass of path rendering
+ * Controls whether clockwise, counterclockwise, or both faces are drawn.
+ * @param face the face(s) to draw.
*/
- void setStencilPass(StencilPass pass);
+ void setDrawFace(DrawFace face) { fCurrDrawState.fDrawFace = face; }
/**
- * Reveses the in/out decision of the fill rule for path rendering.
- * Only affects kEOFillColor_StencilPass and kWindingFillColor_StencilPass
- *
- * @param reverse true to reverse, false otherwise
+ * Gets whether the target is drawing clockwise, counterclockwise,
+ * or both faces.
+ * @return the current draw face(s).
*/
- void setReverseFill(bool reverse);
+ DrawFace getDrawFace() const { return fCurrDrawState.fDrawFace; }
/**
* Enable render state settings.
@@ -327,6 +334,10 @@ public:
return 0 != (fCurrDrawState.fFlagBits & kClip_StateBit);
}
+ bool isColorWriteDisabled() const {
+ return 0 != (fCurrDrawState.fFlagBits & kNoColorWrites_StateBit);
+ }
+
/**
* Sets the blending function coeffecients.
*
@@ -457,8 +468,8 @@ public:
// text [GrGpuTextVertex vs
// GrPoint].)
// for below assert
- kDummy,
- kHighVertexLayoutBit = kDummy - 1
+ kDummyVertexLayoutBit,
+ kHighVertexLayoutBit = kDummyVertexLayoutBit - 1
};
// make sure we haven't exceeded the number of bits in GrVertexLayout.
GR_STATIC_ASSERT(kHighVertexLayoutBit < (1 << 8*sizeof(GrVertexLayout)));
@@ -648,7 +659,7 @@ public:
* have changed. They should be reestablished before the next drawIndexed
* or drawNonIndexed. This cannot be called between reserving and releasing
* geometry. The GrDrawTarget subclass may be able to perform additional
- * optimizations if drawRect is used rather than drawIndexed or
+ * optimizations if drawRect is used rather than drawIndexed or
* drawNonIndexed.
* @param rect the rect to draw
* @param matrix optional matrix applied to rect (before viewMatrix)
@@ -665,18 +676,18 @@ public:
* srcMatrix[i]. srcMatrices can be NULL when no
* srcMatrices are desired.
*/
- virtual void drawRect(const GrRect& rect,
+ virtual void drawRect(const GrRect& rect,
const GrMatrix* matrix,
StageBitfield stageEnableBitfield,
const GrRect* srcRects[],
const GrMatrix* srcMatrices[]);
/**
- * Helper for drawRect when the caller doesn't need separate src rects or
+ * Helper for drawRect when the caller doesn't need separate src rects or
* matrices.
*/
- void drawSimpleRect(const GrRect& rect,
- const GrMatrix* matrix,
+ void drawSimpleRect(const GrRect& rect,
+ const GrMatrix* matrix,
StageBitfield stageEnableBitfield) {
drawRect(rect, matrix, stageEnableBitfield, NULL, NULL);
}
@@ -912,20 +923,20 @@ public:
* Defaults to zero (corresponding to vertex position)
* @return pointer to the vertex component as a GrPoint
*/
- static GrPoint* GetVertexPoint(void* vertices,
+ static GrPoint* GetVertexPoint(void* vertices,
int vertexIndex,
int vertexSize,
int offset = 0) {
intptr_t start = GrTCast<intptr_t>(vertices);
- return GrTCast<GrPoint*>(start + offset +
+ return GrTCast<GrPoint*>(start + offset +
vertexIndex * vertexSize);
}
static const GrPoint* GetVertexPoint(const void* vertices,
int vertexIndex,
- int vertexSize,
+ int vertexSize,
int offset = 0) {
intptr_t start = GrTCast<intptr_t>(vertices);
- return GrTCast<const GrPoint*>(start + offset +
+ return GrTCast<const GrPoint*>(start + offset +
vertexIndex * vertexSize);
}
@@ -937,20 +948,20 @@ public:
* @param offset the offset in bytes of the vertex color
* @return pointer to the vertex component as a GrColor
*/
- static GrColor* GetVertexColor(void* vertices,
+ static GrColor* GetVertexColor(void* vertices,
int vertexIndex,
int vertexSize,
int offset) {
intptr_t start = GrTCast<intptr_t>(vertices);
- return GrTCast<GrColor*>(start + offset +
+ return GrTCast<GrColor*>(start + offset +
vertexIndex * vertexSize);
}
static const GrColor* GetVertexColor(const void* vertices,
int vertexIndex,
- int vertexSize,
+ int vertexSize,
int offset) {
const intptr_t start = GrTCast<intptr_t>(vertices);
- return GrTCast<const GrColor*>(start + offset +
+ return GrTCast<const GrColor*>(start + offset +
vertexIndex * vertexSize);
}
@@ -985,10 +996,10 @@ protected:
const GrRect* srcRects[]);
static void SetRectVertices(const GrRect& rect,
- const GrMatrix* matrix,
- const GrRect* srcRects[],
+ const GrMatrix* matrix,
+ const GrRect* srcRects[],
const GrMatrix* srcMatrices[],
- GrVertexLayout layout,
+ GrVertexLayout layout,
void* vertices);
enum GeometrySrcType {
@@ -997,7 +1008,7 @@ protected:
kBuffer_GeometrySrcType // src was set using set*SourceToBuffer
};
- struct {
+ struct ReservedGeometry {
bool fLocked;
uint32_t fVertexCount;
uint32_t fIndexCount;
diff --git a/gpu/include/GrGLTexture.h b/gpu/include/GrGLTexture.h
index fc12ee0878..14370abb80 100644
--- a/gpu/include/GrGLTexture.h
+++ b/gpu/include/GrGLTexture.h
@@ -36,9 +36,6 @@ public:
GLuint renderFBOID() const { return fRTFBOID; }
GLuint textureFBOID() const { return fTexFBOID; }
- GLuint getStencilBits() const { return fStencilBits; }
-
- const GrGLIRect& viewport() const { return fViewport; }
void abandon();
protected:
@@ -58,17 +55,13 @@ protected:
GrGpuGL* gl);
void setViewport(const GrGLIRect& rect) { fViewport = rect; }
-
- virtual int width() const { return fViewport.fWidth; }
- virtual int height() const { return fViewport.fHeight; }
-
+ const GrGLIRect& getViewport() const { return fViewport; }
private:
GrGpuGL* fGL;
GLuint fRTFBOID;
GLuint fTexFBOID;
GLuint fStencilRenderbufferID;
GLuint fMSColorRenderbufferID;
- GLuint fStencilBits;
// Should this object delete IDs when it is destroyed or does someone
// else own them.
diff --git a/gpu/include/GrGpu.h b/gpu/include/GrGpu.h
index 661708b51a..5a88ef481f 100644
--- a/gpu/include/GrGpu.h
+++ b/gpu/include/GrGpu.h
@@ -25,6 +25,7 @@
class GrVertexBufferAllocPool;
class GrIndexBufferAllocPool;
+class GrPathRenderer;
class GrGpu : public GrDrawTarget {
@@ -229,15 +230,21 @@ public:
bool supports8BitPalette() const { return f8bitPaletteSupport; }
/**
- * If single stencil pass winding is supported then one stencil pass
- * (kWindingStencil1_PathPass) is required to do winding rule path filling
- * (or inverse winding rule). Otherwise, two passes are required
- * (kWindingStencil1_PathPass followed by kWindingStencil2_PathPass).
- *
+ * returns true if two sided stenciling is supported. If false then only
+ * the front face values of the GrStencilSettings
* @return true if only a single stencil pass is needed.
*/
- bool supportsSingleStencilPassWinding() const
- { return fSingleStencilPassForWinding; }
+ bool supportsTwoSidedStencil() const
+ { return fTwoSidedStencilSupport; }
+
+ /**
+ * returns true if stencil wrap is supported. If false then
+ * kIncWrap_StencilOp and kDecWrap_StencilOp are treated as
+ * kIncClamp_StencilOp and kDecClamp_StencilOp, respectively.
+ * @return true if stencil wrap ops are supported.
+ */
+ bool supportsStencilWrapOps() const
+ { return fStencilWrapOpsSupport; }
/**
* Checks whether locking vertex and index buffers is supported.
@@ -304,7 +311,7 @@ public:
const GrIndexBuffer* getQuadIndexBuffer() const;
/**
- * Returns a vertex buffer with four position-only vertices [(0,0), (1,0),
+ * Returns a vertex buffer with four position-only vertices [(0,0), (1,0),
* (1,1), (0,1)].
* @ return unit square vertex buffer
*/
@@ -326,16 +333,12 @@ public:
void printStats() const;
protected:
- /**
- * Extensions to GrDrawTarget::StencilPass to implement stencil clipping
- */
- enum GpuStencilPass {
- kSetClip_StencilPass = kDrawTargetCount_StencilPass,
- /* rendering a hard clip to the stencil
- buffer. Subsequent draws with other
- StencilPass values will be clipped
- if kClip_StateBit is set. */
- kGpuCount_StencilPass
+ enum PrivateStateBits {
+ kFirstBit = (kLastPublicStateBit << 1),
+
+ kModifyStencilClip_StateBit = kFirstBit, // allows draws to modify
+ // stencil bits used for
+ // clipping.
};
/**
@@ -344,7 +347,6 @@ protected:
struct ClipState {
bool fClipInStencil;
bool fClipIsDirty;
- GrRenderTarget* fStencilClipTarget;
} fClipState;
// GrDrawTarget override
@@ -353,6 +355,21 @@ protected:
// prepares clip flushes gpu state before a draw
bool setupClipAndFlushState(GrPrimitiveType type);
+ // Functions used to map clip-respecting stencil tests into normal
+ // stencil funcs supported by GPUs.
+ static GrStencilFunc ConvertStencilFunc(bool stencilInClip,
+ GrStencilFunc func);
+ static void ConvertStencilFuncAndMask(GrStencilFunc func,
+ bool clipInStencil,
+ unsigned int clipBit,
+ unsigned int userBits,
+ unsigned int* ref,
+ unsigned int* mask);
+
+ // stencil settings to clip drawing when stencil clipping is in effect
+ // and the client isn't using the stencil test.
+ static const GrStencilSettings gClipStencilSettings;
+
// defaults to false, subclass can set true to support palleted textures
bool f8bitPaletteSupport;
@@ -360,10 +377,8 @@ protected:
bool fNPOTTextureSupport;
bool fNPOTTextureTileSupport;
bool fNPOTRenderTargetSupport;
-
- // True if only one stencil pass is required to implement the winding path
- // fill rule. Subclass responsible for setting this value.
- bool fSingleStencilPassForWinding;
+ bool fTwoSidedStencilSupport;
+ bool fStencilWrapOpsSupport;
// set by subclass to true if index and vertex buffers can be locked, false
// otherwise.
@@ -427,13 +442,15 @@ protected:
virtual void flushScissor(const GrIRect* rect) = 0;
// GrGpu subclass removes the clip from the stencil buffer
- virtual void eraseStencilClip() = 0;
+ virtual void eraseStencilClip(const GrIRect& rect) = 0;
private:
-
+ // readies the pools to provide vertex/index data.
void prepareVertexPool();
void prepareIndexPool();
+ GrPathRenderer* getPathRenderer();
+
GrVertexBufferAllocPool* fVertexPool;
GrIndexBufferAllocPool* fIndexPool;
@@ -443,6 +460,61 @@ private:
mutable GrVertexBuffer* fUnitSquareVertexBuffer; // mutable so it can be
// created on-demand
+
+ GrPathRenderer* fPathRenderer;
+
+ // when in an internal draw these indicate whether the pools are in use
+ // by one of the outer draws. If false then it is safe to reset the
+ // pool.
+ bool fVertexPoolInUse;
+ bool fIndexPoolInUse;
+
+ // used to save and restore state when the GrGpu needs
+ // to make its geometry pools available internally
+ class AutoInternalDrawGeomRestore {
+ public:
+ AutoInternalDrawGeomRestore(GrGpu* gpu) : fAgsr(gpu) {
+ fGpu = gpu;
+
+ fVertexPoolWasInUse = gpu->fVertexPoolInUse;
+ fIndexPoolWasInUse = gpu->fIndexPoolInUse;
+
+ gpu->fVertexPoolInUse = fVertexPoolWasInUse ||
+ (kBuffer_GeometrySrcType !=
+ gpu->fGeometrySrc.fVertexSrc);
+ gpu->fIndexPoolInUse = fIndexPoolWasInUse ||
+ (kBuffer_GeometrySrcType !=
+ gpu->fGeometrySrc.fIndexSrc);;
+
+ fSavedPoolVertexBuffer = gpu->fCurrPoolVertexBuffer;
+ fSavedPoolStartVertex = gpu->fCurrPoolStartVertex;
+ fSavedPoolIndexBuffer = gpu->fCurrPoolIndexBuffer;
+ fSavedPoolStartIndex = gpu->fCurrPoolStartIndex;
+
+ fSavedReservedGeometry = gpu->fReservedGeometry;
+ gpu->fReservedGeometry.fLocked = false;
+ }
+ ~AutoInternalDrawGeomRestore() {
+ fGpu->fCurrPoolVertexBuffer = fSavedPoolVertexBuffer;
+ fGpu->fCurrPoolStartVertex = fSavedPoolStartVertex;
+ fGpu->fCurrPoolIndexBuffer = fSavedPoolIndexBuffer;
+ fGpu->fCurrPoolStartIndex = fSavedPoolStartIndex;
+ fGpu->fVertexPoolInUse = fVertexPoolWasInUse;
+ fGpu->fIndexPoolInUse = fIndexPoolWasInUse;
+ fGpu->fReservedGeometry = fSavedReservedGeometry;
+ }
+ private:
+ AutoGeometrySrcRestore fAgsr;
+ GrGpu* fGpu;
+ const GrVertexBuffer* fSavedPoolVertexBuffer;
+ int fSavedPoolStartVertex;
+ const GrIndexBuffer* fSavedPoolIndexBuffer;
+ int fSavedPoolStartIndex;
+ bool fVertexPoolWasInUse;
+ bool fIndexPoolWasInUse;
+ ReservedGeometry fSavedReservedGeometry;
+ };
+
typedef GrDrawTarget INHERITED;
};
diff --git a/gpu/include/GrPath.h b/gpu/include/GrPath.h
index cf7b97f2d8..23fc4a86b6 100644
--- a/gpu/include/GrPath.h
+++ b/gpu/include/GrPath.h
@@ -35,6 +35,8 @@ public:
void resetFromIter(GrPathIter*);
+ bool operator ==(const GrPath& path) const;
+ bool operator !=(const GrPath& path) const { return !(*this == path); }
// overrides from GrPathSink
virtual void moveTo(GrScalar x, GrScalar y);
@@ -50,7 +52,7 @@ public:
// overrides from GrPathIter
virtual Command next(GrPoint points[]);
- virtual ConvexHint hint() const;
+ virtual ConvexHint convexHint() const;
virtual Command next();
virtual void rewind();
private:
diff --git a/gpu/include/GrPathIter.h b/gpu/include/GrPathIter.h
index 028faaa14d..140cbcba56 100644
--- a/gpu/include/GrPathIter.h
+++ b/gpu/include/GrPathIter.h
@@ -30,7 +30,7 @@ struct GrPoint;
class GrPathIter {
public:
/**
- Returned by next(). Indicates the next piece of the path.
+ Returned by next(). Indicates the next piece of the path.
*/
enum Command {
kMove_Command, //!< next() returns 1 pt
@@ -44,35 +44,35 @@ public:
// Adds a cubic segment
kClose_Command, //!< next() returns 0 pts
kEnd_Command //!< next() returns 0 pts
- // Implictly closes the last
+ // Implictly closes the last
// point
};
-
+
enum ConvexHint {
- kNone_ConvexHint, //<! No hint about convexity
+ kNone_ConvexHint, //<! No hint about convexity
// of the path
kConvex_ConvexHint, //<! Path is one convex piece
- kNonOverlappingConvexPieces_ConvexHint, //<! Multiple convex pieces,
+ kNonOverlappingConvexPieces_ConvexHint, //<! Multiple convex pieces,
// pieces are known to be
// disjoint
- kSameWindingConvexPieces_ConvexHint, //<! Multiple convex pieces,
+ kSameWindingConvexPieces_ConvexHint, //<! Multiple convex pieces,
// may or may not intersect,
- // either all wind cw or all
+ // either all wind cw or all
// wind ccw.
- kConcave_ConvexHint //<! Path is known to be
+ kConcave_ConvexHint //<! Path is known to be
// concave
};
-
+
static int NumCommandPoints(Command cmd) {
static const int numPoints[] = {
1, 2, 3, 4, 0, 0
};
return numPoints[cmd];
}
-
+
virtual ~GrPathIter() {};
- /**
+ /**
Iterates through the path. Should not be called after
kEnd_Command has been returned once. This version retrieves the
points for the command.
@@ -85,14 +85,14 @@ public:
/**
* If the host API has knowledge of the convexity of the path
* it can be communicated by this hint. Ganesh can make these
- * determinations itself. So it is not necessary to compute
+ * determinations itself. So it is not necessary to compute
* convexity status if it isn't already determined.
*
* @return a hint about the convexity of the path.
- */
- virtual ConvexHint hint() const { return kNone_ConvexHint; }
+ */
+ virtual ConvexHint convexHint() const { return kNone_ConvexHint; }
- /**
+ /**
Iterates through the path. Should not be called after
kEnd_Command has been returned once. This version does not retrieve the
points for the command.
diff --git a/gpu/include/GrRect.h b/gpu/include/GrRect.h
index e98913a57c..96d302f53d 100644
--- a/gpu/include/GrRect.h
+++ b/gpu/include/GrRect.h
@@ -22,7 +22,7 @@
struct GrIRect {
int32_t fLeft, fTop, fRight, fBottom;
-
+
GrIRect() {}
GrIRect(int32_t left, int32_t top, int32_t right, int32_t bottom) {
fLeft = left;
@@ -47,23 +47,22 @@ struct GrIRect {
fRight = x + w;
fBottom = y + h;
}
-
+
void setLTRB(int32_t l, int32_t t, int32_t r, int32_t b) {
fLeft = l;
fTop = t;
fRight = r;
fBottom = b;
}
-
+
/**
* Make the largest representable rectangle
-
*/
void setLargest() {
fLeft = fTop = GR_Int32Min;
fRight = fBottom = GR_Int32Max;
}
-
+
bool quickReject(int l, int t, int r, int b) const {
return l >= fRight || fLeft >= r || t >= fBottom || fTop >= b;
}
@@ -75,10 +74,28 @@ struct GrIRect {
if (fBottom < r.fBottom) fBottom = r.fBottom;
}
+ /**
+ * Sets this rect to the intersection with a clip rect. If there is no
+ * intersection then this rect will be made empty.
+ */
+ void intersectWith(const GrIRect& clipRect) {
+ if (fRight < clipRect.fLeft ||
+ fLeft > clipRect.fRight ||
+ fBottom < clipRect.fTop ||
+ fTop > clipRect.fBottom) {
+ this->setEmpty();
+ } else {
+ fLeft = GrMax(fLeft, clipRect.fLeft);
+ fRight = GrMin(fRight, clipRect.fRight);
+ fTop = GrMax(fTop, clipRect.fTop);
+ fBottom = GrMin(fBottom, clipRect.fBottom);
+ }
+ }
+
friend bool operator==(const GrIRect& a, const GrIRect& b) {
return 0 == memcmp(&a, &b, sizeof(a));
}
-
+
friend bool operator!=(const GrIRect& a, const GrIRect& b) {
return 0 != memcmp(&a, &b, sizeof(a));
}
@@ -91,7 +108,7 @@ struct GrIRect {
return fLeft == x && fTop == y &&
this->width() == w && this->height() == h;
}
-
+
bool contains(const GrIRect& r) const {
return fLeft <= r.fLeft &&
fRight >= r.fRight &&
@@ -102,12 +119,12 @@ struct GrIRect {
struct GrIRect16 {
int16_t fLeft, fTop, fRight, fBottom;
-
+
int width() const { return fRight - fLeft; }
int height() const { return fBottom - fTop; }
int area() const { return this->width() * this->height(); }
bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
-
+
void set(const GrIRect& r) {
fLeft = GrToS16(r.fLeft);
fTop = GrToS16(r.fTop);
@@ -116,12 +133,12 @@ struct GrIRect16 {
}
};
-/**
+/**
* 2D Rect struct
*/
struct GrRect {
GrScalar fLeft, fTop, fRight, fBottom;
-
+
/**
* Uninitialized rectangle.
*/
@@ -158,7 +175,7 @@ struct GrRect {
GrScalar top() const { return fTop; }
GrScalar right() const { return fRight; }
GrScalar bottom() const { return fBottom; }
-
+
GrScalar diagonalLengthSqd() const {
GrScalar w = width();
GrScalar h = height();
@@ -169,18 +186,18 @@ struct GrRect {
// TODO: fixed point sqrt
return GrFloatToScalar(sqrtf(GrScalarToFloat(diagonalLengthSqd())));
}
-
+
/**
* Returns true if the width or height is <= 0
*/
bool isEmpty() const {
return fLeft >= fRight || fTop >= fBottom;
}
-
+
void setEmpty() {
fLeft = fTop = fRight = fBottom = 0;
}
-
+
/**
* returns true if the rectangle is inverted either in x or y
*/
@@ -192,7 +209,7 @@ struct GrRect {
return point.fX >= fLeft && point.fX < fRight &&
point.fY >= fTop && point.fY < fBottom;
}
-
+
/**
* Initialize a rectangle to a point.
* @param pt the point used to initialize the rectangle.
@@ -226,16 +243,16 @@ struct GrRect {
/**
* Make the largest representable rectangle
- * Set the rect to fLeft = fTop = GR_ScalarMin and
+ * Set the rect to fLeft = fTop = GR_ScalarMin and
* fRight = fBottom = GR_ScalarMax.
*/
void setLargest() {
fLeft = fTop = GR_ScalarMin;
fRight = fBottom = GR_ScalarMax;
}
-
+
/**
- Set the rect to fLeft = fTop = GR_ScalarMax and
+ Set the rect to fLeft = fTop = GR_ScalarMax and
fRight = fBottom = GR_ScalarMin.
Useful for initializing a bounding rectangle.
*/
@@ -243,24 +260,24 @@ struct GrRect {
fLeft = fTop = GR_ScalarMax;
fRight = fBottom = GR_ScalarMin;
}
-
- void setLTRB(GrScalar left,
- GrScalar top,
- GrScalar right,
+
+ void setLTRB(GrScalar left,
+ GrScalar top,
+ GrScalar right,
GrScalar bottom) {
fLeft = left;
fTop = top;
fRight = right;
fBottom = bottom;
}
-
+
void setXYWH(GrScalar x, GrScalar y, GrScalar width, GrScalar height) {
fLeft = x;
fTop = y;
fRight = x + width;
fBottom = y + height;
}
-
+
/**
Expand the edges of the rectangle to include a point.
Useful for constructing a bounding rectangle.
@@ -269,12 +286,43 @@ struct GrRect {
void growToInclude(const GrPoint& pt) {
fLeft = GrMin(pt.fX, fLeft);
fRight = GrMax(pt.fX, fRight);
-
+
fTop = GrMin(pt.fY, fTop);
fBottom = GrMax(pt.fY, fBottom);
}
/**
+ * Grows a rect to include another rect.
+ * @param rect the rect to include
+ */
+ void growToInclude(const GrRect& rect) {
+ GrAssert(!rect.isEmpty());
+ fLeft = GrMin(rect.fLeft, fLeft);
+ fRight = GrMax(rect.fRight, fRight);
+
+ fTop = GrMin(rect.fTop, fTop);
+ fBottom = GrMax(rect.fBottom, fBottom);
+ }
+
+ /**
+ * Sets this rect to the intersection with a clip rect. If there is no
+ * intersection then this rect will be made empty.
+ */
+ void intersectWith(const GrRect& clipRect) {
+ if (fRight < clipRect.fLeft ||
+ fLeft > clipRect.fRight ||
+ fBottom < clipRect.fTop ||
+ fTop > clipRect.fBottom) {
+ this->setEmpty();
+ } else {
+ fLeft = GrMax(fLeft, clipRect.fLeft);
+ fRight = GrMin(fRight, clipRect.fRight);
+ fTop = GrMax(fTop, clipRect.fTop);
+ fBottom = GrMin(fBottom, clipRect.fBottom);
+ }
+ }
+
+ /**
* Assigns 4 sequential points in order to construct a counter-clockwise
* triangle fan, given the corners of this rect. Returns the address of
* the next point, treating pts as an array.
@@ -283,6 +331,13 @@ struct GrRect {
pts->setRectFan(fLeft, fTop, fRight, fBottom);
return pts + 4;
}
+
+ bool operator ==(const GrRect& r) const {
+ return fLeft == r.fLeft &&
+ fTop == r.fTop &&
+ fRight == r.fRight &&
+ fBottom == r.fBottom;
+ }
};
#endif
diff --git a/gpu/include/GrStencil.h b/gpu/include/GrStencil.h
new file mode 100644
index 0000000000..be3e0f66fb
--- /dev/null
+++ b/gpu/include/GrStencil.h
@@ -0,0 +1,211 @@
+/*
+ Copyright 2011 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#ifndef GrStencil_DEFINED
+#define GrStencil_DEFINED
+
+#include "GrTypes.h"
+/**
+ * Gr uses the stencil buffer to implement complex clipping inside the
+ * GrDrawTarget class. The GrDrawTarget makes a subset of the stencil buffer
+ * bits available for other uses by external code (clients). Client code can
+ * modify these bits. GrDrawTarget will ignore ref, mask, and writemask bits
+ * provided by clients that overlap the bits used to implement clipping. The
+ * client can use the getUsableStencilBits() function to find out how many
+ * client accessible stencil bits are available.
+ *
+ * When code outside the GrDrawTarget class uses the stencil buffer the contract
+ * is as follows:
+ *
+ * > Normal stencil funcs allow the GrGpu client to modify the client bits of
+ * the stencil buffer outside of the clip.
+ * > Special functions allow a test against the clip. These are more limited
+ * than the general stencil functions.
+ * > Client can assume all client bits are zero initially.
+ * > Client must ensure that after all its passes are finished it has only
+ * written to the color buffer in the region inside the clip. Furthermore, it
+ * must zero all client bits that were modifed (both inside and outside the
+ * clip).
+ */
+
+/**
+ * Determines which pixels pass / fail the stencil test.
+ * Stencil test passes if (ref & mask) FUNC (stencil & mask) is true
+ */
+enum GrStencilFunc {
+ kAlways_StencilFunc = 0,
+ kNever_StencilFunc,
+ kGreater_StencilFunc,
+ kGEqual_StencilFunc,
+ kLess_StencilFunc,
+ kLEqual_StencilFunc,
+ kEqual_StencilFunc,
+ kNotEqual_StencilFunc,
+
+ // Gr stores the current clip in the
+ // stencil buffer in the high bits that
+ // are not directly accessible modifiable
+ // via the GrDrawTarget interface. The below
+ // stencil funcs test against the current
+ // clip in addition to the GrDrawTarget
+ // client's stencil bits.
+
+ // pass if inside the clip
+ kAlwaysIfInClip_StencilFunc,
+ kEqualIfInClip_StencilFunc,
+ kLessIfInClip_StencilFunc,
+ kLEqualIfInClip_StencilFunc,
+ kNonZeroIfInClip_StencilFunc, // this one forces the ref to be 0
+
+ // counts
+ kStencilFuncCount,
+ kClipStencilFuncCount = kNonZeroIfInClip_StencilFunc -
+ kAlwaysIfInClip_StencilFunc + 1,
+ kBasicStencilFuncCount = kStencilFuncCount - kClipStencilFuncCount
+};
+
+/**
+ * Operations to perform based on whether stencil test passed failed.
+ */
+enum GrStencilOp {
+ kKeep_StencilOp = 0, // preserve existing stencil value
+ kReplace_StencilOp, // replace with reference value from stencl test
+ kIncWrap_StencilOp, // increment and wrap at max
+ kIncClamp_StencilOp, // increment and clamp at max
+ kDecWrap_StencilOp, // decrement and wrap at 0
+ kDecClamp_StencilOp, // decrement and clamp at 0
+ kZero_StencilOp, // zero stencil bits
+ kInvert_StencilOp, // invert stencil bits
+
+ kStencilOpCount
+};
+
+/**
+ * Struct representing stencil state.
+ */
+struct GrStencilSettings {
+ GrStencilOp fFrontPassOp; // op to perform when front faces pass
+ GrStencilOp fBackPassOp; // op to perform when back faces pass
+ GrStencilOp fFrontFailOp; // op to perform when front faces fail
+ GrStencilOp fBackFailOp; // op to perform when back faces fail
+ GrStencilFunc fFrontFunc; // test function for front faces
+ GrStencilFunc fBackFunc; // test function for back faces
+ unsigned int fFrontFuncMask; // mask for front face test
+ unsigned int fBackFuncMask; // mask for back face test
+ unsigned int fFrontFuncRef; // reference value for front face test
+ unsigned int fBackFuncRef; // reference value for back face test
+ unsigned int fFrontWriteMask; // stencil write mask for front faces
+ unsigned int fBackWriteMask; // stencil write mask for back faces
+
+ bool operator == (const GrStencilSettings& s) const {
+ // make sure this is tightly packed.
+ GR_STATIC_ASSERT(0 == sizeof(GrStencilOp)%4);
+ GR_STATIC_ASSERT(0 == sizeof(GrStencilFunc)%4);
+ GR_STATIC_ASSERT(sizeof(GrStencilSettings) ==
+ 4*sizeof(GrStencilOp) +
+ 2*sizeof(GrStencilFunc) +
+ 6*sizeof(unsigned int));
+ return 0 == memcmp(this, &s, sizeof(GrStencilSettings));
+ }
+
+ bool operator != (const GrStencilSettings& s) const {
+ return !(*this == s);
+ }
+
+ GrStencilSettings& operator =(const GrStencilSettings& s) {
+ memcpy(this, &s, sizeof(GrStencilSettings));
+ return *this;
+ }
+
+ void setSame(GrStencilOp passOp,
+ GrStencilOp failOp,
+ GrStencilFunc func,
+ unsigned int funcMask,
+ unsigned int funcRef,
+ unsigned int writeMask) {
+ fFrontPassOp = passOp;
+ fBackPassOp = passOp;
+ fFrontFailOp = failOp;
+ fBackFailOp = failOp;
+ fFrontFunc = func;
+ fBackFunc = func;
+ fFrontFuncMask = funcMask;
+ fBackFuncMask = funcMask;
+ fFrontFuncRef = funcRef;
+ fBackFuncRef = funcRef;
+ fFrontWriteMask = writeMask;
+ fBackWriteMask = writeMask;
+ }
+
+ // canonical value for disabled stenciling
+ static const GrStencilSettings gDisabled;
+ void setDisabled() {
+ *this = gDisabled;
+ }
+ bool isDisabled() const {
+ return kKeep_StencilOp == fFrontPassOp &&
+ kKeep_StencilOp == fBackPassOp &&
+ kKeep_StencilOp == fFrontFailOp &&
+ kKeep_StencilOp == fFrontFailOp &&
+ kAlways_StencilFunc == fFrontFunc &&
+ kAlways_StencilFunc == fBackFunc;
+ }
+ void invalidate() {
+ // just write an illegal value to the first member
+ fFrontPassOp = (GrStencilOp)-1;
+ }
+
+private:
+ friend class GrGpu;
+
+ enum {
+ kMaxStencilClipPasses = 2 // maximum number of passes to add a clip
+ // element to the stencil buffer.
+ };
+
+ /**
+ * Given a thing to draw into the stencil clip, a fill type, and a set op
+ * this function determines:
+ * 1. Whether the thing can be draw directly to the stencil clip or
+ * needs to be drawn to the client portion of the stencil first.
+ * 2. How many passes are needed.
+ * 3. What those passes are.
+ * 4. The fill rule that should actually be used to render (will
+ * always be non-inverted).
+ *
+ * @param op the set op to combine this element with the
+ * existing clip
+ * @param stencilClipMask mask with just the stencil bit used for clipping
+ * enabled.
+ * @param fill in: the fill rule of the element to draw.
+ * out: the fill rule that should be used to draw
+ * @param numPasses out: the number of passes needed to add the
+ * element to the clip.
+ * @param settings out: the stencil settings to use for each pass
+ *
+ * @return true if the clip element's geometry can be drawn directly to the
+ * stencil clip bit. Will only be true if canBeDirect is true.
+ * numPasses will be 1 if return value is true.
+ */
+ static bool GetClipPasses(GrSetOp op,
+ bool canBeDirect,
+ unsigned int stencilClipMask,
+ GrPathFill* fill,
+ int* numPasses,
+ GrStencilSettings settings[kMaxStencilClipPasses]);
+};
+
+#endif
diff --git a/gpu/include/GrTArray.h b/gpu/include/GrTArray.h
index c31c820884..6ef1a13558 100644
--- a/gpu/include/GrTArray.h
+++ b/gpu/include/GrTArray.h
@@ -176,6 +176,8 @@ public:
}
}
+ void reset() { this->pop_back_n(fCount); }
+
int count() const { return fCount; }
bool empty() const { return !fCount; }
diff --git a/gpu/include/GrTexture.h b/gpu/include/GrTexture.h
index 098ac595a2..2d3928cb25 100644
--- a/gpu/include/GrTexture.h
+++ b/gpu/include/GrTexture.h
@@ -19,6 +19,7 @@
#define GrTexture_DEFINED
#include "GrRefCnt.h"
+#include "GrClip.h"
class GrTexture;
@@ -34,11 +35,16 @@ public:
/**
* @return the width of the rendertarget
*/
- virtual int width() const = 0;
+ int width() const { return fWidth; }
/**
* @return the height of the rendertarget
*/
- virtual int height() const = 0;
+ int height() const { return fHeight; }
+
+ /**
+ * @return the number of stencil bits in the rendertarget
+ */
+ int stencilBits() const { return fStencilBits; }
/**
* @return the texture associated with the rendertarget, may be NULL.
@@ -46,8 +52,28 @@ public:
GrTexture* asTexture() {return fTexture;}
protected:
- GrRenderTarget(GrTexture* texture) : fTexture(texture) {}
+ GrRenderTarget(GrTexture* texture,
+ int width,
+ int height,
+ int stencilBits)
+ : fTexture(texture),
+ fWidth(width),
+ fHeight(height),
+ fStencilBits(stencilBits) {}
+
+
GrTexture* fTexture;
+ int fWidth;
+ int fHeight;
+ int fStencilBits;
+
+private:
+ // GrGpu keeps a cached clip in the render target to avoid redundantly
+ // rendering the clip into the same stencil buffer.
+ friend class GrGpu;
+ GrClip fLastStencilClip;
+
+ typedef GrRefCnt INHERITED;
};
class GrTexture : public GrRefCnt {
diff --git a/gpu/include/GrTypes.h b/gpu/include/GrTypes.h
index 02a652a4c1..29e847fd09 100644
--- a/gpu/include/GrTypes.h
+++ b/gpu/include/GrTypes.h
@@ -201,6 +201,26 @@ enum GrBlendCoeff {
kIDA_BlendCoeff, //<! one minus dst alpha
};
+/**
+ * Set Operations used to construct clips.
+ */
+enum GrSetOp {
+ kReplace_SetOp,
+ kIntersect_SetOp,
+ kUnion_SetOp,
+ kXor_SetOp,
+ kDifference_SetOp,
+ kReverseDifference_SetOp,
+};
+
+/**
+ * Clips are composed from these objects.
+ */
+enum GrClipType {
+ kRect_ClipType,
+ kPath_ClipType
+};
+
///////////////////////////////////////////////////////////////////////////////
// this is included only to make it easy to use this debugging facility
diff --git a/gpu/src/GrClip.cpp b/gpu/src/GrClip.cpp
index b7a839bfd2..924c01b9f8 100644
--- a/gpu/src/GrClip.cpp
+++ b/gpu/src/GrClip.cpp
@@ -17,18 +17,30 @@
#include "GrClip.h"
-GrClip::GrClip() {
+GrClip::GrClip()
+ : fList(fListMemory, kPreAllocElements) {
fBounds.setEmpty();
- this->validate();
+ fBoundsValid = true;
}
-GrClip::GrClip(const GrClip& src) {
+GrClip::GrClip(const GrClip& src)
+ : fList(fListMemory, kPreAllocElements) {
*this = src;
}
-GrClip::GrClip(GrClipIterator* iter) {
- fBounds.setEmpty();
- this->setFromIterator(iter);
+GrClip::GrClip(const GrIRect& rect)
+ : fList(fListMemory, kPreAllocElements) {
+ this->setFromIRect(rect);
+}
+
+GrClip::GrClip(const GrRect& rect)
+ : fList(fListMemory, kPreAllocElements) {
+ this->setFromRect(rect);
+}
+
+GrClip::GrClip(GrClipIterator* iter, const GrRect* bounds)
+ : fList(fListMemory, kPreAllocElements) {
+ this->setFromIterator(iter, bounds);
}
GrClip::~GrClip() {}
@@ -36,101 +48,100 @@ GrClip::~GrClip() {}
GrClip& GrClip::operator=(const GrClip& src) {
fList = src.fList;
fBounds = src.fBounds;
- this->validate();
+ fBoundsValid = src.fBoundsValid;
return *this;
}
void GrClip::setEmpty() {
fList.reset();
fBounds.setEmpty();
- this->validate();
+ fBoundsValid = true;
}
-void GrClip::setRect(const GrIRect& r) {
+void GrClip::setFromRect(const GrRect& r) {
fList.reset();
-
- // we need a canonical "empty" rect, so that our operator== will behave
- // correctly with two empty clips.
if (r.isEmpty()) {
- fBounds.setEmpty();
+ // use a canonical empty rect for == testing.
+ setEmpty();
} else {
+ fList.push_back();
+ fList.back().fRect = r;
+ fList.back().fType = kRect_ClipType;
fBounds = r;
- }
- this->validate();
-}
-
-void GrClip::addRect(const GrIRect& r) {
- if (!r.isEmpty()) {
- this->validate();
- if (this->isEmpty()) {
- GrAssert(fList.count() == 0);
- fBounds = r;
- } else {
- if (this->isRect()) {
- *fList.append() = fBounds;
- }
- *fList.append() = r;
- fBounds.unionWith(r);
- }
- this->validate();
+ fBoundsValid = true;
}
}
-void GrClip::setFromIterator(GrClipIterator* iter) {
- this->setEmpty();
- if (iter) {
- for (iter->rewind(); !iter->isDone(); iter->next()) {
- GrIRect r;
- iter->getRect(&r);
- this->addRect(r);
- }
+void GrClip::setFromIRect(const GrIRect& r) {
+ fList.reset();
+ if (r.isEmpty()) {
+ // use a canonical empty rect for == testing.
+ setEmpty();
+ } else {
+ fList.push_back();
+ fList.back().fRect.set(r);
+ fList.back().fType = kRect_ClipType;
+ fBounds.set(r);
+ fBoundsValid = true;
}
}
-///////////////////////////////////////////////////////////////////////////////
+void GrClip::setFromIterator(GrClipIterator* iter, const GrRect* bounds) {
+ fList.reset();
-void GrClipIter::reset(const GrClip& clip) { fClip = &clip; fIndex = 0; }
+ int rectCount = 0;
-bool GrClipIter::isDone() {
- return (NULL == fClip) || (fIndex >= fClip->countRects());
-}
+ // compute bounds for common case of series of intersecting rects.
+ bool isectRectValid = true;
-void GrClipIter::rewind() { fIndex = 0; }
-void GrClipIter::getRect(GrIRect* r) { *r = fClip->getRects()[fIndex]; }
-void GrClipIter::next() { fIndex += 1; }
-void GrClipIter::computeBounds(GrIRect* r) {
- if (NULL == fClip) {
- r->setEmpty();
- } else {
- *r = fClip->getBounds();
+ if (iter) {
+ for (iter->rewind(); !iter->isDone(); iter->next()) {
+ Element& e = fList.push_back();
+ e.fType = iter->getType();
+ e.fOp = iter->getOp();
+ // iterators should not emit replace
+ GrAssert(kReplace_SetOp != e.fOp);
+ switch (e.fType) {
+ case kRect_ClipType:
+ iter->getRect(&e.fRect);
+ ++rectCount;
+ if (isectRectValid) {
+ if (1 == rectCount || kIntersect_SetOp == e.fOp) {
+ GrAssert(fList.count() <= 2);
+ if (fList.count() > 1) {
+ GrAssert(2 == rectCount);
+ rectCount = 1;
+ fList.pop_back();
+ GrAssert(kRect_ClipType == fList.back().fType);
+ fList.back().fRect.intersectWith(e.fRect);
+ }
+ } else {
+ isectRectValid = false;
+ }
+ }
+ break;
+ case kPath_ClipType:
+ e.fPath.resetFromIter(iter->getPathIter());
+ e.fPathFill = iter->getPathFill();
+ isectRectValid = false;
+ break;
+ default:
+ GrCrash("Unknown clip element type.");
+ }
+ }
}
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#if GR_DEBUG
-
-void GrClip::validate() const {
- if (fBounds.isEmpty()) {
- GrAssert(0 == fBounds.fLeft);
- GrAssert(0 == fBounds.fTop);
- GrAssert(0 == fBounds.fRight);
- GrAssert(0 == fBounds.fBottom);
- GrAssert(0 == fList.count());
- } else {
- int count = fList.count();
- if (count > 0) {
- GrAssert(count > 1);
- GrAssert(!fList[0].isEmpty());
- GrIRect bounds = fList[0];
- for (int i = 1; i < count; i++) {
- GrAssert(!fList[i].isEmpty());
- bounds.unionWith(fList[i]);
+ fBoundsValid = false;
+ if (NULL == bounds) {
+ if (isectRectValid) {
+ fBoundsValid = true;
+ if (rectCount > 0) {
+ fBounds = fList[0].fRect;
+ } else {
+ fBounds.setEmpty();
}
- GrAssert(fBounds == bounds);
}
+ } else {
+ fBounds = *bounds;
+ fBoundsValid = true;
}
}
-
-#endif
-
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index 840e773313..a047895ced 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -145,7 +145,7 @@ GrTextureEntry* GrContext::createAndLockTexture(GrTextureKey* key,
GrDrawTarget::AutoStateRestore asr(fGpu);
fGpu->setRenderTarget(texture->asRenderTarget());
fGpu->setTexture(0, clampEntry->texture());
- fGpu->setStencilPass(GrDrawTarget::kNone_StencilPass);
+ fGpu->disableStencil();
fGpu->setViewMatrix(GrMatrix::I());
fGpu->setAlpha(0xff);
fGpu->setBlendFunc(kOne_BlendCoeff, kZero_BlendCoeff);
@@ -284,7 +284,7 @@ void GrContext::setClip(const GrClip& clip) {
void GrContext::setClip(const GrIRect& rect) {
GrClip clip;
- clip.setRect(rect);
+ clip.setFromIRect(rect);
fGpu->setClip(clip);
}
@@ -297,7 +297,10 @@ void GrContext::eraseColor(GrColor color) {
void GrContext::drawPaint(const GrPaint& paint) {
// set rect to be big enough to fill the space, but not super-huge, so we
// don't overflow fixed-point implementations
- GrRect r(fGpu->getClip().getBounds());
+ GrRect r;
+ r.setLTRB(0, 0,
+ GrIntToScalar(getRenderTarget()->width()),
+ GrIntToScalar(getRenderTarget()->height()));
GrMatrix inverse;
if (fGpu->getViewInverse(&inverse)) {
inverse.mapRect(&r);
@@ -546,6 +549,15 @@ void GrContext::drawPath(const GrPaint& paint,
fPathRenderer->drawPath(target, enabledStages, path, fill, translate);
}
+void GrContext::drawPath(const GrPaint& paint,
+ const GrPath& path,
+ GrPathFill fill,
+ const GrPoint* translate) {
+ GrPath::Iter iter(path);
+ this->drawPath(paint, &iter, fill, translate);
+}
+
+
////////////////////////////////////////////////////////////////////////////////
void GrContext::flush(bool flushRenderTarget) {
@@ -751,7 +763,8 @@ GrContext::GrContext(GrGpu* gpu) {
#if BATCH_RECT_TO_RECT
fDrawBuffer->setQuadIndexBuffer(this->getQuadIndexBuffer());
#endif
- fPathRenderer = new GrDefaultPathRenderer(fGpu->supportsSingleStencilPassWinding());
+ fPathRenderer = new GrDefaultPathRenderer(fGpu->supportsTwoSidedStencil(),
+ fGpu->supportsStencilWrapOps());
}
bool GrContext::finalizeTextureKey(GrTextureKey* key,
diff --git a/gpu/src/GrDrawTarget.cpp b/gpu/src/GrDrawTarget.cpp
index ca525b3b02..7973361890 100644
--- a/gpu/src/GrDrawTarget.cpp
+++ b/gpu/src/GrDrawTarget.cpp
@@ -358,14 +358,6 @@ void GrDrawTarget::setSamplerState(int stage, const GrSamplerState& state) {
fCurrDrawState.fSamplerStates[stage] = state;
}
-void GrDrawTarget::setStencilPass(StencilPass pass) {
- fCurrDrawState.fStencilPass = pass;
-}
-
-void GrDrawTarget::setReverseFill(bool reverse) {
- fCurrDrawState.fReverseFill = reverse;
-}
-
void GrDrawTarget::enableState(uint32_t bits) {
fCurrDrawState.fFlagBits |= bits;
}
diff --git a/gpu/src/GrGLTexture.cpp b/gpu/src/GrGLTexture.cpp
index 9fd1f57edf..ae991e8362 100644
--- a/gpu/src/GrGLTexture.cpp
+++ b/gpu/src/GrGLTexture.cpp
@@ -22,12 +22,14 @@ GrGLRenderTarget::GrGLRenderTarget(const GLRenderTargetIDs& ids,
GLuint stencilBits,
const GrGLIRect& viewport,
GrGLTexture* texture,
- GrGpuGL* gl) : INHERITED(texture) {
+ GrGpuGL* gl) : INHERITED(texture,
+ viewport.fWidth,
+ viewport.fHeight,
+ stencilBits) {
fGL = gl;
fRTFBOID = ids.fRTFBOID;
fTexFBOID = ids.fTexFBOID;
fStencilRenderbufferID = ids.fStencilRenderbufferID;
- fStencilBits = stencilBits;
fMSColorRenderbufferID = ids.fMSColorRenderbufferID;
fNeedsResolve = false;
fViewport = viewport;
diff --git a/gpu/src/GrGpu.cpp b/gpu/src/GrGpu.cpp
index 019c99ff6d..f42e316082 100644
--- a/gpu/src/GrGpu.cpp
+++ b/gpu/src/GrGpu.cpp
@@ -22,12 +22,13 @@
#include "GrIndexBuffer.h"
#include "GrVertexBuffer.h"
#include "GrBufferAllocPool.h"
+#include "GrPathRenderer.h"
// probably makes no sense for this to be less than a page
static const size_t VERTEX_POOL_VB_SIZE = 1 << 12;
static const int VERTEX_POOL_VB_COUNT = 1;
-///////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
size_t GrTexture::BytesPerPixel(PixelConfig config) {
switch (config) {
@@ -56,7 +57,7 @@ bool GrTexture::PixelConfigIsOpaque(PixelConfig config) {
}
-///////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
extern void gr_run_unittests();
@@ -68,7 +69,10 @@ GrGpu::GrGpu() : f8bitPaletteSupport(false),
fVertexPool(NULL),
fIndexPool(NULL),
fQuadIndexBuffer(NULL),
- fUnitSquareVertexBuffer(NULL) {
+ fUnitSquareVertexBuffer(NULL),
+ fPathRenderer(NULL),
+ fVertexPoolInUse(false),
+ fIndexPoolInUse(false) {
#if GR_DEBUG
// gr_run_unittests();
#endif
@@ -80,6 +84,7 @@ GrGpu::~GrGpu() {
GrSafeUnref(fUnitSquareVertexBuffer);
delete fVertexPool;
delete fIndexPool;
+ delete fPathRenderer;
}
void GrGpu::resetContext() {
@@ -91,7 +96,7 @@ void GrGpu::unimpl(const char msg[]) {
#endif
}
-///////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
static const int MAX_QUADS = 1 << 12; // max possible: (1 << 14) - 1;
@@ -159,7 +164,7 @@ const GrVertexBuffer* GrGpu::getUnitSquareVertexBuffer() const {
return fUnitSquareVertexBuffer;
}
-///////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
void GrGpu::clipWillBeSet(const GrClip& newClip) {
if (newClip != fClip) {
@@ -167,8 +172,113 @@ void GrGpu::clipWillBeSet(const GrClip& newClip) {
}
}
+////////////////////////////////////////////////////////////////////////////////
+
+// stencil settings to use when clip is in stencil
+const GrStencilSettings GrGpu::gClipStencilSettings = {
+ kKeep_StencilOp, kKeep_StencilOp,
+ kKeep_StencilOp, kKeep_StencilOp,
+ kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
+ 0, 0,
+ 0, 0,
+ 0, 0
+};
+
+// converts special stencil func to
+static const GrStencilFunc gGrClipToNormalStencilFunc[2][kClipStencilFuncCount] = {
+ {// Stencil-Clipping is DISABLED, effectively always inside the clip
+ // In the Clip Funcs
+ kAlways_StencilFunc, // kAlwaysIfInClip_StencilFunc
+ kEqual_StencilFunc, // kEqualIfInClip_StencilFunc
+ kLess_StencilFunc, // kLessIfInClip_StencilFunc
+ kLEqual_StencilFunc, // kLEqualIfInClip_StencilFunc
+ // Special in the clip func that forces user's ref to be 0.
+ kNotEqual_StencilFunc, // kNonZeroIfInClip_StencilFunc
+ // make ref 0 and do normal nequal.
+ },
+ {// Stencil-Clipping is ENABLED
+ // In the Clip Funcs
+ kEqual_StencilFunc, // kAlwaysIfInClip_StencilFunc
+ // eq stencil clip bit, mask
+ // out user bits.
+
+ kEqual_StencilFunc, // kEqualIfInClip_StencilFunc
+ // add stencil bit to mask and ref
+
+ kLess_StencilFunc, // kLessIfInClip_StencilFunc
+ kLEqual_StencilFunc, // kLEqualIfInClip_StencilFunc
+ // for both of these we can add
+ // the clip bit to the mask and
+ // ref and compare as normal
+ // Special in the clip func that forces user's ref to be 0.
+ kLess_StencilFunc, // kNonZeroIfInClip_StencilFunc
+ // make ref have only the clip bit set
+ // and make comparison be less
+ // 10..0 < 1..user_bits..
+ }
+};
+
+GrStencilFunc GrGpu::ConvertStencilFunc(bool stencilInClip, GrStencilFunc func) {
+ GrAssert(func >= 0);
+ if (func >= kBasicStencilFuncCount) {
+ GrAssert(func < kStencilFuncCount);
+ func = gGrClipToNormalStencilFunc[stencilInClip ? 1 : 0][func - kBasicStencilFuncCount];
+ GrAssert(func >= 0 && func < kBasicStencilFuncCount);
+ }
+ return func;
+}
+
+void GrGpu::ConvertStencilFuncAndMask(GrStencilFunc func,
+ bool clipInStencil,
+ unsigned int clipBit,
+ unsigned int userBits,
+ unsigned int* ref,
+ unsigned int* mask) {
+ if (func < kBasicStencilFuncCount) {
+ *mask &= userBits;
+ *ref &= userBits;
+ } else {
+ if (clipInStencil) {
+ switch (func) {
+ case kAlwaysIfInClip_StencilFunc:
+ *mask = clipBit;
+ *ref = clipBit;
+ break;
+ case kEqualIfInClip_StencilFunc:
+ case kLessIfInClip_StencilFunc:
+ case kLEqualIfInClip_StencilFunc:
+ *mask = (*mask & userBits) | clipBit;
+ *ref = (*ref & userBits) | clipBit;
+ break;
+ case kNonZeroIfInClip_StencilFunc:
+ *mask = (*mask & userBits) | clipBit;
+ *ref = clipBit;
+ break;
+ default:
+ GrCrash("Unknown stencil func");
+ }
+ } else {
+ *mask &= userBits;
+ *ref &= userBits;
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+#define VISUALIZE_COMPLEX_CLIP 0
+
+#if VISUALIZE_COMPLEX_CLIP
+ #include "GrRandom.h"
+ GrRandom gRandom;
+ #define SET_RANDOM_COLOR this->setColor(0xff000000 | gRandom.nextU());
+#else
+ #define SET_RANDOM_COLOR
+#endif
+
bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) {
const GrIRect* r = NULL;
+ GrIRect clipRect;
// we check this early because we need a valid
// render target to setup stencil clipping
@@ -179,80 +289,135 @@ bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) {
}
if (fCurrDrawState.fFlagBits & kClip_StateBit) {
- fClipState.fClipInStencil = fClip.countRects() > 1;
+ GrRenderTarget& rt = *fCurrDrawState.fRenderTarget;
+
+ GrRect bounds;
+ GrRect rtRect;
+ rtRect.setLTRB(0, 0,
+ GrIntToScalar(rt.width()), GrIntToScalar(rt.height()));
+ if (fClip.hasBounds()) {
+ bounds = fClip.getBounds();
+ bounds.intersectWith(rtRect);
+ } else {
+ bounds = rtRect;
+ }
+
+ bounds.roundOut(&clipRect);
+ if (clipRect.isEmpty()) {
+ clipRect.setLTRB(0,0,0,0);
+ }
+ r = &clipRect;
+
+ fClipState.fClipInStencil = !fClip.isRect() &&
+ !fClip.isEmpty() &&
+ !bounds.isEmpty();
if (fClipState.fClipInStencil &&
(fClipState.fClipIsDirty ||
- fClipState.fStencilClipTarget != fCurrDrawState.fRenderTarget)) {
+ fClip != rt.fLastStencilClip)) {
+
+ rt.fLastStencilClip = fClip;
+ // we set the current clip to the bounds so that our recursive
+ // draws are scissored to them. We use the copy of the complex clip
+ // in the rt to render
+ const GrClip& clip = rt.fLastStencilClip;
+ fClip.setFromRect(bounds);
AutoStateRestore asr(this);
- AutoGeometrySrcRestore agsr(this);
-
- // We have to use setVertexSourceToBuffer (and index) in order
- // to ensure we correctly restore the client's geom sources.
- // We tack the clip verts onto the vertex pool but we don't
- // use the various helper functions because of their side effects.
-
- int rectTotal = fClip.countRects();
- if (NULL == fVertexPool) {
- fVertexPool = new GrVertexBufferAllocPool(this,
- true,
- VERTEX_POOL_VB_SIZE,
- VERTEX_POOL_VB_COUNT);
- } else if (kBuffer_GeometrySrcType == fGeometrySrc.fVertexSrc) {
- // we can't reset if vertex source is array or reserved
- // because then the client data is in the pool!
- fVertexPool->reset();
- }
- const GrVertexBuffer* vertexBuffer;
- int vStart;
- GrPoint* rectVertices =
- reinterpret_cast<GrPoint*>(fVertexPool->makeSpace(0,
- rectTotal * 4,
- &vertexBuffer,
- &vStart));
- for (int r = 0; r < rectTotal; ++r) {
- const GrIRect& rect = fClip.getRects()[r];
- rectVertices[4 * r].setIRectFan(rect.fLeft, rect.fTop,
- rect.fRight, rect.fBottom);
- }
- fVertexPool->unlock();
- this->setVertexSourceToBuffer(0, vertexBuffer);
- this->setIndexSourceToBuffer(getQuadIndexBuffer());
+ AutoInternalDrawGeomRestore aidgr(this);
+
this->setViewMatrix(GrMatrix::I());
- // don't clip the clip or recurse!
- this->disableState(kClip_StateBit);
- this->eraseStencilClip();
- this->setStencilPass((GrDrawTarget::StencilPass)kSetClip_StencilPass);
- int currRect = 0;
- while (currRect < rectTotal) {
- int rectCount = GrMin(MAX_QUADS,
- rectTotal - currRect);
- this->drawIndexed(kTriangles_PrimitiveType,
- vStart + currRect * 4,
- 0,
- rectCount*4,
- rectCount*6);
- currRect += rectCount;
+ this->eraseStencilClip(clipRect);
+ this->flushScissor(NULL);
+#if !VISUALIZE_COMPLEX_CLIP
+ this->enableState(kNoColorWrites_StateBit);
+#else
+ this->disableState(kNoColorWrites_StateBit);
+#endif
+ int count = clip.getElementCount();
+ int clipBit = rt.stencilBits();
+ clipBit = (1 << (clipBit-1));
+
+ // walk through each clip element and perform its set op
+ // with the existing clip.
+ for (int c = 0; c < count; ++c) {
+ GrPathFill fill;
+ // enabled at bottom of loop
+ this->disableState(kModifyStencilClip_StateBit);
+
+ bool canDrawDirectToClip;
+ if (kRect_ClipType == clip.getElementType(c)) {
+ canDrawDirectToClip = true;
+ fill = kEvenOdd_PathFill;
+ } else {
+ fill = clip.getPathFill(c);
+ canDrawDirectToClip = getPathRenderer()->requiresStencilPass(clip.getPath(c));
+ }
+
+ GrSetOp op = 0 == c ? kReplace_SetOp : clip.getOp(c);
+ int passes;
+ GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses];
+
+ canDrawDirectToClip = GrStencilSettings::GetClipPasses(op, canDrawDirectToClip,
+ clipBit, &fill,
+ &passes, stencilSettings);
+
+ // draw the element to the client stencil bits if necessary
+ if (!canDrawDirectToClip) {
+ if (kRect_ClipType == clip.getElementType(c)) {
+ static const GrStencilSettings gDrawToStencil = {
+ kIncClamp_StencilOp, kIncClamp_StencilOp,
+ kIncClamp_StencilOp, kIncClamp_StencilOp,
+ kAlways_StencilFunc, kAlways_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x00000000, 0x00000000,
+ 0xffffffff, 0xffffffff,
+ };
+ this->setStencil(gDrawToStencil);
+ SET_RANDOM_COLOR
+ this->drawSimpleRect(clip.getRect(c), NULL, 0);
+ } else {
+ SET_RANDOM_COLOR
+ getPathRenderer()->drawPathToStencil(this, clip.getPath(c), fill, NULL);
+ }
+ }
+
+ // now we modify the clip bit by rendering either the clip
+ // element directly or a bounding rect of the entire clip.
+ this->enableState(kModifyStencilClip_StateBit);
+ for (int p = 0; p < passes; ++p) {
+ this->setStencil(stencilSettings[p]);
+ if (canDrawDirectToClip) {
+ if (kRect_ClipType == clip.getElementType(c)) {
+ SET_RANDOM_COLOR
+ this->drawSimpleRect(clip.getRect(c), NULL, 0);
+ } else {
+ SET_RANDOM_COLOR
+ getPathRenderer()->drawPath(this, 0, clip.getPath(c), fill, NULL);
+ }
+ } else {
+ SET_RANDOM_COLOR
+ this->drawSimpleRect(bounds, 0, NULL);
+ }
+ }
}
- fClipState.fStencilClipTarget = fCurrDrawState.fRenderTarget;
+ fClip = clip;
+ // recusive draws would have disabled this.
+ fClipState.fClipInStencil = true;
}
fClipState.fClipIsDirty = false;
- if (!fClipState.fClipInStencil) {
- r = &fClip.getBounds();
- }
}
+
// Must flush the scissor after graphics state
- if (!flushGraphicsState(type)) {
+ if (!this->flushGraphicsState(type)) {
return false;
}
- flushScissor(r);
+ this->flushScissor(r);
return true;
}
-
-///////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
void GrGpu::drawIndexed(GrPrimitiveType type,
int startVertex,
@@ -314,10 +479,11 @@ void GrGpu::finalizeReservedIndices() {
void GrGpu::prepareVertexPool() {
if (NULL == fVertexPool) {
- fVertexPool = new GrVertexBufferAllocPool(this, true,
- VERTEX_POOL_VB_SIZE,
+ fVertexPool = new GrVertexBufferAllocPool(this, true,
+ VERTEX_POOL_VB_SIZE,
VERTEX_POOL_VB_COUNT);
- } else {
+ } else if (!fVertexPoolInUse) {
+ // the client doesn't have valid data in the pool
fVertexPool->reset();
}
}
@@ -325,7 +491,8 @@ void GrGpu::prepareVertexPool() {
void GrGpu::prepareIndexPool() {
if (NULL == fVertexPool) {
fIndexPool = new GrIndexBufferAllocPool(this, true, 0, 1);
- } else {
+ } else if (!fIndexPoolInUse) {
+ // the client doesn't have valid data in the pool
fIndexPool->reset();
}
}
@@ -339,7 +506,7 @@ bool GrGpu::acquireGeometryHelper(GrVertexLayout vertexLayout,
if (fReservedGeometry.fVertexCount) {
GrAssert(NULL != vertices);
- prepareVertexPool();
+ this->prepareVertexPool();
*vertices = fVertexPool->makeSpace(vertexLayout,
fReservedGeometry.fVertexCount,
@@ -354,7 +521,7 @@ bool GrGpu::acquireGeometryHelper(GrVertexLayout vertexLayout,
if (fReservedGeometry.fIndexCount) {
GrAssert(NULL != indices);
- prepareIndexPool();
+ this->prepareIndexPool();
*indices = fIndexPool->makeSpace(fReservedGeometry.fIndexCount,
&fCurrPoolIndexBuffer,
@@ -397,7 +564,17 @@ void GrGpu::setIndexSourceToArrayHelper(const void* indexArray, int indexCount)
GR_DEBUGASSERT(success);
}
-///////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+GrPathRenderer* GrGpu::getPathRenderer() {
+ if (NULL == fPathRenderer) {
+ fPathRenderer = new GrDefaultPathRenderer(this->supportsTwoSidedStencil(),
+ this->supportsStencilWrapOps());
+ }
+ return fPathRenderer;
+}
+
+////////////////////////////////////////////////////////////////////////////////
const GrGpu::Stats& GrGpu::getStats() const {
return fStats;
diff --git a/gpu/src/GrGpuGL.cpp b/gpu/src/GrGpuGL.cpp
index b30dd027cf..2574b157a6 100644
--- a/gpu/src/GrGpuGL.cpp
+++ b/gpu/src/GrGpuGL.cpp
@@ -49,8 +49,8 @@ static const GLenum gXfermodeCoeff2Blend[] = {
///////////////////////////////////////////////////////////////////////////////
-void GrGpuGL::AdjustTextureMatrix(const GrGLTexture* texture,
- GrSamplerState::SampleMode mode,
+void GrGpuGL::AdjustTextureMatrix(const GrGLTexture* texture,
+ GrSamplerState::SampleMode mode,
GrMatrix* matrix) {
GrAssert(NULL != texture);
GrAssert(NULL != matrix);
@@ -80,7 +80,7 @@ void GrGpuGL::AdjustTextureMatrix(const GrGLTexture* texture,
}
}
-bool GrGpuGL::TextureMatrixIsIdentity(const GrGLTexture* texture,
+bool GrGpuGL::TextureMatrixIsIdentity(const GrGLTexture* texture,
const GrSamplerState& sampler) {
GrAssert(NULL != texture);
if (!sampler.getMatrix().isIdentity()) {
@@ -104,7 +104,7 @@ bool GrGpuGL::TextureMatrixIsIdentity(const GrGLTexture* texture,
static bool gPrintStartupSpew;
-bool fbo_test(GrGLExts exts, int w, int h) {
+static bool fbo_test(GrGLExts exts, int w, int h) {
GLint savedFBO;
GLint savedTexUnit;
@@ -169,8 +169,6 @@ GrGpuGL::GrGpuGL() {
GrAssert(maxTextureUnits > kNumStages);
#endif
- fCurrDrawState = fHWDrawState;
-
////////////////////////////////////////////////////////////////////////////
// Check for supported features.
@@ -263,14 +261,24 @@ GrGpuGL::GrGpuGL() {
// we could also look for GL_ATI_separate_stencil extension or
// GL_EXT_stencil_two_side but they use different function signatures
// than GL2.0+ (and than each other).
- fSingleStencilPassForWinding = (major >= 2);
+ fTwoSidedStencilSupport = (major >= 2);
+ // supported on GL 1.4 and higher or by extension
+ fStencilWrapOpsSupport = (major > 1) ||
+ (1 == major) && (minor >= 4) ||
+ has_gl_extension("GL_EXT_stencil_wrap");
#else
// ES 2 has two sided stencil but 1.1 doesn't. There doesn't seem to be
// an ES1 extension.
- fSingleStencilPassForWinding = (major >= 2);
+ fTwoSidedStencilSupport = (major >= 2);
+ // stencil wrap support is in ES2, ES1 requires extension.
+ fStencilWrapOpsSupport = (major > 1) ||
+ has_gl_extension("GL_OES_stencil_wrap");
+
#endif
if (gPrintStartupSpew) {
- GrPrintf("Single Stencil Pass For Winding: %s\n", (fSingleStencilPassForWinding ? "YES" : "NO"));
+ GrPrintf("Stencil Caps: TwoSide: %s, Wrap: %s\n",
+ (fTwoSidedStencilSupport ? "YES" : "NO"),
+ (fStencilWrapOpsSupport ? "YES" : "NO"));
}
#if GR_SUPPORT_GLDESKTOP
@@ -424,8 +432,9 @@ void GrGpuGL::resetContextHelper() {
fHWBlendDisabled = false;
GR_GL(Enable(GL_BLEND));
- // this is always disabled
GR_GL(Disable(GL_CULL_FACE));
+ GR_GL(FrontFace(GL_CCW));
+ fHWDrawState.fDrawFace = kBoth_DrawFace;
GR_GL(Disable(GL_DITHER));
#if GR_SUPPORT_GLDESKTOP
@@ -434,14 +443,15 @@ void GrGpuGL::resetContextHelper() {
GR_GL(Disable(GL_MULTISAMPLE));
#endif
+ GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
+ fHWDrawState.fFlagBits = 0;
+
// we only ever use lines in hairline mode
GR_GL(LineWidth(1));
// invalid
fActiveTextureUnitIdx = -1;
- fHWDrawState.fFlagBits = 0;
-
// illegal values
fHWDrawState.fSrcBlend = (GrBlendCoeff)-1;
fHWDrawState.fDstBlend = (GrBlendCoeff)-1;
@@ -454,7 +464,7 @@ void GrGpuGL::resetContextHelper() {
fHWDrawState.fSamplerStates[s].setRadial2Params(-GR_ScalarMax,
-GR_ScalarMax,
true);
-
+
fHWDrawState.fSamplerStates[s].setMatrix(GrMatrix::InvalidMatrix());
}
@@ -463,16 +473,9 @@ void GrGpuGL::resetContextHelper() {
GR_GL(Disable(GL_SCISSOR_TEST));
fHWBounds.fViewportRect.invalidate();
- // disabling the stencil test also disables
- // stencil buffer writes
- GR_GL(Disable(GL_STENCIL_TEST));
- GR_GL(StencilMask(0xffffffff));
- GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
- fHWDrawState.fReverseFill = false;
- fHWDrawState.fStencilPass = kNone_StencilPass;
+ fHWDrawState.fStencilSettings.invalidate();
fHWStencilClip = false;
fClipState.fClipIsDirty = true;
- fClipState.fStencilClipTarget = NULL;
fHWGeometryState.fIndexBuffer = NULL;
fHWGeometryState.fVertexBuffer = NULL;
@@ -480,6 +483,7 @@ void GrGpuGL::resetContextHelper() {
GR_GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
fHWGeometryState.fArrayPtrsDirty = true;
+ GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
fHWDrawState.fRenderTarget = NULL;
}
@@ -491,7 +495,7 @@ void GrGpuGL::resetContext() {
GrRenderTarget* GrGpuGL::createPlatformRenderTarget(
intptr_t platformRenderTarget,
int stencilBits,
- int width,
+ int width,
int height) {
GrGLRenderTarget::GLRenderTargetIDs rtIDs;
rtIDs.fStencilRenderbufferID = 0;
@@ -1030,11 +1034,11 @@ GrIndexBuffer* GrGpuGL::createIndexBuffer(uint32_t size, bool dynamic) {
void GrGpuGL::flushScissor(const GrIRect* rect) {
GrAssert(NULL != fCurrDrawState.fRenderTarget);
const GrGLIRect& vp =
- ((GrGLRenderTarget*)fCurrDrawState.fRenderTarget)->viewport();
+ ((GrGLRenderTarget*)fCurrDrawState.fRenderTarget)->getViewport();
GrGLIRect scissor;
if (NULL != rect) {
- scissor.setRelativeTo(vp, rect->fLeft, rect->fTop,
+ scissor.setRelativeTo(vp, rect->fLeft, rect->fTop,
rect->width(), rect->height());
if (scissor.contains(vp)) {
rect = NULL;
@@ -1068,12 +1072,12 @@ void GrGpuGL::eraseColor(GrColor color) {
fHWBounds.fScissorEnabled = false;
}
GR_GL(ColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE));
+ fHWDrawState.fFlagBits &= ~kNoColorWrites_StateBit;
GR_GL(ClearColor(GrColorUnpackR(color)/255.f,
GrColorUnpackG(color)/255.f,
GrColorUnpackB(color)/255.f,
GrColorUnpackA(color)/255.f));
GR_GL(Clear(GL_COLOR_BUFFER_BIT));
- fDirtyFlags.fWriteMaskChanged = true;
}
void GrGpuGL::eraseStencil(uint32_t value, uint32_t mask) {
@@ -1088,15 +1092,21 @@ void GrGpuGL::eraseStencil(uint32_t value, uint32_t mask) {
GR_GL(StencilMask(mask));
GR_GL(ClearStencil(value));
GR_GL(Clear(GL_STENCIL_BUFFER_BIT));
- fDirtyFlags.fWriteMaskChanged = true;
+ fHWDrawState.fStencilSettings.invalidate();
}
-void GrGpuGL::eraseStencilClip() {
+void GrGpuGL::eraseStencilClip(const GrIRect& rect) {
GrAssert(NULL != fCurrDrawState.fRenderTarget);
- GLint stencilBitCount = ((GrGLRenderTarget*)fCurrDrawState.fRenderTarget)->getStencilBits();
+ GLint stencilBitCount = fCurrDrawState.fRenderTarget->stencilBits();
GrAssert(stencilBitCount > 0);
GLint clipStencilMask = (1 << (stencilBitCount - 1));
- eraseStencil(0, clipStencilMask);
+
+ flushRenderTarget();
+ flushScissor(&rect);
+ GR_GL(StencilMask(clipStencilMask));
+ GR_GL(ClearStencil(0));
+ GR_GL(Clear(GL_STENCIL_BUFFER_BIT));
+ fHWDrawState.fStencilSettings.invalidate();
}
void GrGpuGL::forceRenderTargetFlush() {
@@ -1117,13 +1127,13 @@ bool GrGpuGL::readPixels(int left, int top, int width, int height,
}
flushRenderTarget();
- const GrGLIRect& glvp = ((GrGLRenderTarget*)fCurrDrawState.fRenderTarget)->viewport();
-
+ const GrGLIRect& glvp = ((GrGLRenderTarget*)fCurrDrawState.fRenderTarget)->getViewport();
+
// the read rect is viewport-relative
GrGLIRect readRect;
readRect.setRelativeTo(glvp, left, top, width, height);
GR_GL(ReadPixels(readRect.fLeft, readRect.fBottom,
- readRect.fWidth, readRect.fHeight,
+ readRect.fWidth, readRect.fHeight,
format, type, buffer));
// now reverse the order of the rows, since GL's are bottom-to-top, but our
@@ -1166,7 +1176,7 @@ void GrGpuGL::flushRenderTarget() {
#endif
fDirtyFlags.fRenderTargetChanged = true;
fHWDrawState.fRenderTarget = fCurrDrawState.fRenderTarget;
- const GrGLIRect& vp = rt->viewport();
+ const GrGLIRect& vp = rt->getViewport();
if (true || fHWBounds.fViewportRect != vp) {
vp.pushToGLViewport();
fHWBounds.fViewportRect = vp;
@@ -1183,6 +1193,27 @@ GLenum gPrimitiveType2GLMode[] = {
GL_LINE_STRIP
};
+#define SWAP_PER_DRAW 0
+
+#if SWAP_PER_DRAW
+ #if GR_MAC_BUILD
+ #include <AGL/agl.h>
+ #elif GR_WIN32_BUILD
+ void SwapBuf() {
+ DWORD procID = GetCurrentProcessId();
+ HWND hwnd = GetTopWindow(GetDesktopWindow());
+ while(hwnd) {
+ DWORD wndProcID = 0;
+ GetWindowThreadProcessId(hwnd, &wndProcID);
+ if(wndProcID == procID) {
+ SwapBuffers(GetDC(hwnd));
+ }
+ hwnd = GetNextWindow(hwnd, GW_HWNDNEXT);
+ }
+ }
+ #endif
+#endif
+
void GrGpuGL::drawIndexedHelper(GrPrimitiveType type,
uint32_t startVertex,
uint32_t startIndex,
@@ -1201,6 +1232,18 @@ void GrGpuGL::drawIndexedHelper(GrPrimitiveType type,
GR_GL(DrawElements(gPrimitiveType2GLMode[type], indexCount,
GL_UNSIGNED_SHORT, indices));
+#if SWAP_PER_DRAW
+ glFlush();
+ #if GR_MAC_BUILD
+ aglSwapBuffers(aglGetCurrentContext());
+ int set_a_break_pt_here = 9;
+ aglSwapBuffers(aglGetCurrentContext());
+ #elif GR_WIN32_BUILD
+ SwapBuf();
+ int set_a_break_pt_here = 9;
+ SwapBuf();
+ #endif
+#endif
}
void GrGpuGL::drawNonIndexedHelper(GrPrimitiveType type,
@@ -1218,6 +1261,18 @@ void GrGpuGL::drawNonIndexedHelper(GrPrimitiveType type,
// account for startVertex in the DrawElements case. So we always
// rely on setupGeometry to have accounted for startVertex.
GR_GL(DrawArrays(gPrimitiveType2GLMode[type], 0, vertexCount));
+#if SWAP_PER_DRAW
+ glFlush();
+ #if GR_MAC_BUILD
+ aglSwapBuffers(aglGetCurrentContext());
+ int set_a_break_pt_here = 9;
+ aglSwapBuffers(aglGetCurrentContext());
+ #elif GR_WIN32_BUILD
+ SwapBuf();
+ int set_a_break_pt_here = 9;
+ SwapBuf();
+ #endif
+#endif
}
void GrGpuGL::resolveTextureRenderTarget(GrGLTexture* texture) {
@@ -1257,203 +1312,162 @@ void GrGpuGL::resolveTextureRenderTarget(GrGLTexture* texture) {
}
}
+static const GLenum grToGLStencilFunc[] = {
+ GL_ALWAYS, // kAlways_StencilFunc
+ GL_NEVER, // kNever_StencilFunc
+ GL_GREATER, // kGreater_StencilFunc
+ GL_GEQUAL, // kGEqual_StencilFunc
+ GL_LESS, // kLess_StencilFunc
+ GL_LEQUAL, // kLEqual_StencilFunc,
+ GL_EQUAL, // kEqual_StencilFunc,
+ GL_NOTEQUAL, // kNotEqual_StencilFunc,
+};
+GR_STATIC_ASSERT(GR_ARRAY_COUNT(grToGLStencilFunc) == kBasicStencilFuncCount);
+GR_STATIC_ASSERT(0 == kAlways_StencilFunc);
+GR_STATIC_ASSERT(1 == kNever_StencilFunc);
+GR_STATIC_ASSERT(2 == kGreater_StencilFunc);
+GR_STATIC_ASSERT(3 == kGEqual_StencilFunc);
+GR_STATIC_ASSERT(4 == kLess_StencilFunc);
+GR_STATIC_ASSERT(5 == kLEqual_StencilFunc);
+GR_STATIC_ASSERT(6 == kEqual_StencilFunc);
+GR_STATIC_ASSERT(7 == kNotEqual_StencilFunc);
+
+static const GLenum grToGLStencilOp[] = {
+ GL_KEEP, // kKeep_StencilOp
+ GL_REPLACE, // kReplace_StencilOp
+ GL_INCR_WRAP, // kIncWrap_StencilOp
+ GL_INCR, // kIncClamp_StencilOp
+ GL_DECR_WRAP, // kDecWrap_StencilOp
+ GL_DECR, // kDecClamp_StencilOp
+ GL_ZERO, // kZero_StencilOp
+ GL_INVERT, // kInvert_StencilOp
+};
+GR_STATIC_ASSERT(GR_ARRAY_COUNT(grToGLStencilOp) == kStencilOpCount);
+GR_STATIC_ASSERT(0 == kKeep_StencilOp);
+GR_STATIC_ASSERT(1 == kReplace_StencilOp);
+GR_STATIC_ASSERT(2 == kIncWrap_StencilOp);
+GR_STATIC_ASSERT(3 == kIncClamp_StencilOp);
+GR_STATIC_ASSERT(4 == kDecWrap_StencilOp);
+GR_STATIC_ASSERT(5 == kDecClamp_StencilOp);
+GR_STATIC_ASSERT(6 == kZero_StencilOp);
+GR_STATIC_ASSERT(7 == kInvert_StencilOp);
+
void GrGpuGL::flushStencil() {
+ const GrStencilSettings* settings = &fCurrDrawState.fStencilSettings;
// use stencil for clipping if clipping is enabled and the clip
// has been written into the stencil.
bool stencilClip = fClipState.fClipInStencil &&
(kClip_StateBit & fCurrDrawState.fFlagBits);
- bool stencilChange =
- fDirtyFlags.fWriteMaskChanged ||
- fHWStencilClip != stencilClip ||
- fHWDrawState.fStencilPass != fCurrDrawState.fStencilPass ||
- (kNone_StencilPass != fCurrDrawState.fStencilPass &&
- (StencilPass)kSetClip_StencilPass != fCurrDrawState.fStencilPass &&
- fHWDrawState.fReverseFill != fCurrDrawState.fReverseFill);
+ bool stencilChange = fHWStencilClip != stencilClip ||
+ fHWDrawState.fStencilSettings != *settings ||
+ ((fHWDrawState.fFlagBits & kModifyStencilClip_StateBit) !=
+ (fCurrDrawState.fFlagBits & kModifyStencilClip_StateBit));
if (stencilChange) {
- GLint clipStencilMask;
- GLint pathStencilMask;
- GLint stencilBitCount = ((GrGLRenderTarget*)fCurrDrawState.fRenderTarget)->getStencilBits();
- GrAssert(stencilBitCount > 0 ||
- kNone_StencilPass == fCurrDrawState.fStencilPass);
- clipStencilMask = (1 << (stencilBitCount - 1));
- pathStencilMask = clipStencilMask - 1;
- switch (fCurrDrawState.fStencilPass) {
- case kNone_StencilPass:
- if (stencilClip) {
- GR_GL(Enable(GL_STENCIL_TEST));
- GR_GL(StencilFunc(GL_EQUAL,
- clipStencilMask,
- clipStencilMask));
- GR_GL(StencilOp(GL_KEEP, GL_KEEP, GL_KEEP));
- } else {
- GR_GL(Disable(GL_STENCIL_TEST));
- }
- GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
- if (!fSingleStencilPassForWinding) {
- GR_GL(Disable(GL_CULL_FACE));
- }
- break;
- case kEvenOddStencil_StencilPass:
- GR_GL(Enable(GL_STENCIL_TEST));
- if (stencilClip) {
- GR_GL(StencilFunc(GL_EQUAL, clipStencilMask, clipStencilMask));
- } else {
- GR_GL(StencilFunc(GL_ALWAYS, 0x0, 0x0));
- }
- GR_GL(StencilMask(pathStencilMask));
- GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
- GR_GL(StencilOp(GL_KEEP, GL_INVERT, GL_INVERT));
- GR_GL(ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE));
- if (!fSingleStencilPassForWinding) {
- GR_GL(Disable(GL_CULL_FACE));
- }
- break;
- case kEvenOddColor_StencilPass: {
- GR_GL(Enable(GL_STENCIL_TEST));
- GLint funcRef = 0;
- GLuint funcMask = pathStencilMask;
- if (stencilClip) {
- funcRef |= clipStencilMask;
- funcMask |= clipStencilMask;
- }
- if (!fCurrDrawState.fReverseFill) {
- funcRef |= pathStencilMask;
- }
- GR_GL(StencilFunc(GL_EQUAL, funcRef, funcMask));
- GR_GL(StencilMask(pathStencilMask));
- GR_GL(StencilOp(GL_ZERO, GL_ZERO, GL_ZERO));
- GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
- if (!fSingleStencilPassForWinding) {
- GR_GL(Disable(GL_CULL_FACE));
- }
- } break;
- case kWindingStencil1_StencilPass:
- GR_GL(Enable(GL_STENCIL_TEST));
- if (fHasStencilWrap) {
- if (stencilClip) {
- GR_GL(StencilFunc(GL_EQUAL,
- clipStencilMask,
- clipStencilMask));
- } else {
- GR_GL(StencilFunc(GL_ALWAYS, 0x0, 0x0));
- }
- if (fSingleStencilPassForWinding) {
- GR_GL(StencilOpSeparate(GL_FRONT, GL_KEEP,
- GL_INCR_WRAP, GL_INCR_WRAP));
- GR_GL(StencilOpSeparate(GL_BACK, GL_KEEP,
- GL_DECR_WRAP, GL_DECR_WRAP));
- } else {
- GR_GL(StencilOp(GL_KEEP, GL_INCR_WRAP, GL_INCR_WRAP));
- GR_GL(Enable(GL_CULL_FACE));
- GR_GL(CullFace(GL_BACK));
- }
- } else {
- // If we don't have wrap then we use the Func to detect
- // values that would wrap (0 on decr and mask on incr). We
- // make the func fail on these values and use the sfail op
- // to effectively wrap by inverting.
- // This applies whether we are doing a two-pass (front faces
- // followed by back faces) or a single pass (separate func/op)
-
- // Note that in the case where we are also using stencil to
- // clip this means we will write into the path bits in clipped
- // out pixels. We still apply the clip bit in the color pass
- // stencil func so we don't draw color outside the clip.
- // We also will clear the stencil bits in clipped pixels by
- // using zero in the sfail op with write mask set to the
- // path mask.
- GR_GL(Enable(GL_STENCIL_TEST));
- if (fSingleStencilPassForWinding) {
- GR_GL(StencilFuncSeparate(GL_FRONT,
- GL_NOTEQUAL,
- pathStencilMask,
- pathStencilMask));
- GR_GL(StencilFuncSeparate(GL_BACK,
- GL_NOTEQUAL,
- 0x0,
- pathStencilMask));
- GR_GL(StencilOpSeparate(GL_FRONT, GL_INVERT,
- GL_INCR, GL_INCR));
- GR_GL(StencilOpSeparate(GL_BACK, GL_INVERT,
- GL_DECR, GL_DECR));
- } else {
- GR_GL(StencilFunc(GL_NOTEQUAL,
- pathStencilMask,
- pathStencilMask));
- GR_GL(StencilOp(GL_INVERT, GL_INCR, GL_INCR));
- GR_GL(Enable(GL_CULL_FACE));
- GR_GL(CullFace(GL_BACK));
- }
- }
- GR_GL(StencilMask(pathStencilMask));
- GR_GL(ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE));
- break;
- case kWindingStencil2_StencilPass:
- GrAssert(!fSingleStencilPassForWinding);
- GR_GL(Enable(GL_STENCIL_TEST));
- if (fHasStencilWrap) {
- if (stencilClip) {
- GR_GL(StencilFunc(GL_EQUAL,
+ // we can't simultaneously perform stencil-clipping and modify the stencil clip
+ GrAssert(!stencilClip || !(fCurrDrawState.fFlagBits & kModifyStencilClip_StateBit));
+
+ if (settings->isDisabled()) {
+ if (stencilClip) {
+ settings = &gClipStencilSettings;
+ }
+ }
+
+ if (settings->isDisabled()) {
+ GR_GL(Disable(GL_STENCIL_TEST));
+ } else {
+ GR_GL(Enable(GL_STENCIL_TEST));
+ #if GR_DEBUG
+ if (!fStencilWrapOpsSupport) {
+ GrAssert(settings->fFrontPassOp != kIncWrap_StencilOp);
+ GrAssert(settings->fFrontPassOp != kDecWrap_StencilOp);
+ GrAssert(settings->fFrontFailOp != kIncWrap_StencilOp);
+ GrAssert(settings->fBackFailOp != kDecWrap_StencilOp);
+ GrAssert(settings->fBackPassOp != kIncWrap_StencilOp);
+ GrAssert(settings->fBackPassOp != kDecWrap_StencilOp);
+ GrAssert(settings->fBackFailOp != kIncWrap_StencilOp);
+ GrAssert(settings->fFrontFailOp != kDecWrap_StencilOp);
+ }
+ #endif
+ int stencilBits = fCurrDrawState.fRenderTarget->stencilBits();
+ GrAssert(stencilBits ||
+ (GrStencilSettings::gDisabled ==
+ fCurrDrawState.fStencilSettings));
+ GLuint clipStencilMask = 1 << (stencilBits - 1);
+ GLuint userStencilMask = clipStencilMask - 1;
+
+ unsigned int frontRef = settings->fFrontFuncRef;
+ unsigned int frontMask = settings->fFrontFuncMask;
+ unsigned int frontWriteMask = settings->fFrontWriteMask;
+ GLenum frontFunc;
+
+ if (fCurrDrawState.fFlagBits & kModifyStencilClip_StateBit) {
+
+ GrAssert(settings->fFrontFunc < kBasicStencilFuncCount);
+ frontFunc = grToGLStencilFunc[settings->fFrontFunc];
+ } else {
+ frontFunc = grToGLStencilFunc[ConvertStencilFunc(stencilClip, settings->fFrontFunc)];
+
+ ConvertStencilFuncAndMask(settings->fFrontFunc,
+ stencilClip,
clipStencilMask,
- clipStencilMask));
- } else {
- GR_GL(StencilFunc(GL_ALWAYS, 0x0, 0x0));
- }
- GR_GL(StencilOp(GL_DECR_WRAP, GL_DECR_WRAP, GL_DECR_WRAP));
- } else {
- GR_GL(StencilFunc(GL_NOTEQUAL, 0x0, pathStencilMask));
- GR_GL(StencilOp(GL_INVERT, GL_DECR, GL_DECR));
- }
- GR_GL(StencilMask(pathStencilMask));
- GR_GL(Enable(GL_CULL_FACE));
- GR_GL(CullFace(GL_FRONT));
- GR_GL(ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE));
- break;
- case kWindingColor_StencilPass: {
- GR_GL(Enable(GL_STENCIL_TEST));
- GLint funcRef = 0;
- GLuint funcMask = pathStencilMask;
- GLenum funcFunc;
-
- if (stencilClip) {
- funcRef |= clipStencilMask;
- funcMask |= clipStencilMask;
- }
- if (fCurrDrawState.fReverseFill) {
- funcFunc = GL_EQUAL;
+ userStencilMask,
+ &frontRef,
+ &frontMask);
+ frontWriteMask &= userStencilMask;
+ }
+ GrAssert(settings->fFrontFailOp >= 0 &&
+ settings->fFrontFailOp < GR_ARRAY_COUNT(grToGLStencilOp));
+ GrAssert(settings->fFrontPassOp >= 0 &&
+ settings->fFrontPassOp < GR_ARRAY_COUNT(grToGLStencilOp));
+ GrAssert(settings->fBackFailOp >= 0 &&
+ settings->fBackFailOp < GR_ARRAY_COUNT(grToGLStencilOp));
+ GrAssert(settings->fBackPassOp >= 0 &&
+ settings->fBackPassOp < GR_ARRAY_COUNT(grToGLStencilOp));
+ if (fTwoSidedStencilSupport) {
+ GLenum backFunc;
+
+ unsigned int backRef = settings->fBackFuncRef;
+ unsigned int backMask = settings->fBackFuncMask;
+ unsigned int backWriteMask = settings->fBackWriteMask;
+
+
+ if (fCurrDrawState.fFlagBits & kModifyStencilClip_StateBit) {
+ GrAssert(settings->fBackFunc < kBasicStencilFuncCount);
+ backFunc = grToGLStencilFunc[settings->fBackFunc];
} else {
- funcFunc = GL_LESS;
+ backFunc = grToGLStencilFunc[ConvertStencilFunc(stencilClip, settings->fBackFunc)];
+ ConvertStencilFuncAndMask(settings->fBackFunc,
+ stencilClip,
+ clipStencilMask,
+ userStencilMask,
+ &backRef,
+ &backMask);
+ backWriteMask &= userStencilMask;
}
- GR_GL(StencilFunc(funcFunc, funcRef, funcMask));
- GR_GL(StencilMask(pathStencilMask));
- // must zero in sfail because winding w/o wrap will write
- // path stencil bits in clipped out pixels
- GR_GL(StencilOp(GL_ZERO, GL_ZERO, GL_ZERO));
- GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
- if (!fSingleStencilPassForWinding) {
- GR_GL(Disable(GL_CULL_FACE));
- }
- } break;
- case kSetClip_StencilPass:
- GR_GL(Enable(GL_STENCIL_TEST));
- GR_GL(StencilFunc(GL_ALWAYS, clipStencilMask, clipStencilMask));
- GR_GL(StencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE));
- GR_GL(StencilMask(clipStencilMask));
- GR_GL(ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE));
- if (!fSingleStencilPassForWinding) {
- GR_GL(Disable(GL_CULL_FACE));
- }
- break;
- default:
- GrAssert(!"Unexpected stencil pass.");
- break;
+ GR_GL(StencilFuncSeparate(GL_FRONT, frontFunc, frontRef, frontMask));
+ GR_GL(StencilMaskSeparate(GL_FRONT, frontWriteMask));
+ GR_GL(StencilFuncSeparate(GL_BACK, backFunc, backRef, backMask));
+ GR_GL(StencilMaskSeparate(GL_BACK, backWriteMask));
+ GR_GL(StencilOpSeparate(GL_FRONT, grToGLStencilOp[settings->fFrontFailOp],
+ grToGLStencilOp[settings->fFrontPassOp],
+ grToGLStencilOp[settings->fFrontPassOp]));
+
+ GR_GL(StencilOpSeparate(GL_BACK, grToGLStencilOp[settings->fBackFailOp],
+ grToGLStencilOp[settings->fBackPassOp],
+ grToGLStencilOp[settings->fBackPassOp]));
+ } else {
+ GR_GL(StencilFunc(frontFunc, frontRef, frontMask));
+ GR_GL(StencilMask(frontWriteMask));
+ GR_GL(StencilOp(grToGLStencilOp[settings->fFrontFailOp],
+ grToGLStencilOp[settings->fFrontPassOp],
+ grToGLStencilOp[settings->fFrontPassOp]));
+ }
}
- fHWDrawState.fStencilPass = fCurrDrawState.fStencilPass;
- fHWDrawState.fReverseFill = fCurrDrawState.fReverseFill;
+ fHWDrawState.fStencilSettings = fCurrDrawState.fStencilSettings;
fHWStencilClip = stencilClip;
}
}
@@ -1544,6 +1558,17 @@ bool GrGpuGL::flushGLStateCommon(GrPrimitiveType type) {
}
}
+ if ((fCurrDrawState.fFlagBits & kNoColorWrites_StateBit) !=
+ (fHWDrawState.fFlagBits & kNoColorWrites_StateBit)) {
+ GLenum mask;
+ if (fCurrDrawState.fFlagBits & kNoColorWrites_StateBit) {
+ mask = GL_FALSE;
+ } else {
+ mask = GL_TRUE;
+ }
+ GR_GL(ColorMask(mask, mask, mask, mask));
+ }
+
#if GR_SUPPORT_GLDESKTOP
// ES doesn't support toggling GL_MULTISAMPLE and doesn't have
// smooth lines.
@@ -1592,6 +1617,25 @@ bool GrGpuGL::flushGLStateCommon(GrPrimitiveType type) {
}
}
+ if (fHWDrawState.fDrawFace != fCurrDrawState.fDrawFace) {
+ switch (fCurrDrawState.fDrawFace) {
+ case kCCW_DrawFace:
+ glEnable(GL_CULL_FACE);
+ GR_GL(CullFace(GL_BACK));
+ break;
+ case kCW_DrawFace:
+ GR_GL(Enable(GL_CULL_FACE));
+ GR_GL(CullFace(GL_FRONT));
+ break;
+ case kBoth_DrawFace:
+ GR_GL(Disable(GL_CULL_FACE));
+ break;
+ default:
+ GrCrash("Unknown draw face.");
+ }
+ fHWDrawState.fDrawFace = fCurrDrawState.fDrawFace;
+ }
+
#if GR_DEBUG
// check for circular rendering
for (int s = 0; s < kNumStages; ++s) {
@@ -1605,6 +1649,7 @@ bool GrGpuGL::flushGLStateCommon(GrPrimitiveType type) {
flushStencil();
+ // flushStencil may look at the private state bits, so keep it before this.
fHWDrawState.fFlagBits = fCurrDrawState.fFlagBits;
return true;
}
@@ -1654,9 +1699,6 @@ void GrGpuGL::notifyRenderTargetDelete(GrRenderTarget* renderTarget) {
if (fHWDrawState.fRenderTarget == renderTarget) {
fHWDrawState.fRenderTarget = NULL;
}
- if (fClipState.fStencilClipTarget == renderTarget) {
- fClipState.fStencilClipTarget = NULL;
- }
}
void GrGpuGL::notifyTextureDelete(GrGLTexture* texture) {
diff --git a/gpu/src/GrGpuGL.h b/gpu/src/GrGpuGL.h
index f6216c5c6e..a2905c5d30 100644
--- a/gpu/src/GrGpuGL.h
+++ b/gpu/src/GrGpuGL.h
@@ -80,7 +80,6 @@ protected:
// call resetDirtyFlags after its flush is complete
struct {
bool fRenderTargetChanged : 1;
- bool fWriteMaskChanged : 1;
int fTextureChangedMask;
} fDirtyFlags;
GR_STATIC_ASSERT(8 * sizeof(int) >= kNumStages);
@@ -108,7 +107,7 @@ protected:
uint32_t numVertices);
virtual void flushScissor(const GrIRect* rect);
void eraseStencil(uint32_t value, uint32_t mask);
- virtual void eraseStencilClip();
+ virtual void eraseStencilClip(const GrIRect& rect);
// binds texture unit in GL
void setTextureUnit(int unitIdx);
diff --git a/gpu/src/GrInOrderDrawBuffer.cpp b/gpu/src/GrInOrderDrawBuffer.cpp
index 68590fc8ab..a249364752 100644
--- a/gpu/src/GrInOrderDrawBuffer.cpp
+++ b/gpu/src/GrInOrderDrawBuffer.cpp
@@ -67,17 +67,17 @@ void GrInOrderDrawBuffer::setQuadIndexBuffer(const GrIndexBuffer* indexBuffer) {
fCurrQuad = 0;
fMaxQuads = (NULL == indexBuffer) ? 0 : indexBuffer->maxQuads();
} else {
- GrAssert((NULL == indexBuffer && 0 == fMaxQuads) ||
+ GrAssert((NULL == indexBuffer && 0 == fMaxQuads) ||
(indexBuffer->maxQuads() == fMaxQuads));
}
}
-void GrInOrderDrawBuffer::drawRect(const GrRect& rect,
+void GrInOrderDrawBuffer::drawRect(const GrRect& rect,
const GrMatrix* matrix,
StageBitfield stageEnableBitfield,
const GrRect* srcRects[],
const GrMatrix* srcMatrices[]) {
-
+
GrAssert(!(NULL == fQuadIndexBuffer && fCurrQuad));
GrAssert(!(fDraws.empty() && fCurrQuad));
GrAssert(!(0 != fMaxQuads && NULL == fQuadIndexBuffer));
@@ -85,7 +85,7 @@ void GrInOrderDrawBuffer::drawRect(const GrRect& rect,
// if we have a quad IB then either append to the previous run of
// rects or start a new run
if (fMaxQuads) {
-
+
bool appendToPreviousDraw = false;
GrVertexLayout layout = GetRectVertexLayout(stageEnableBitfield, srcRects);
AutoReleaseGeometry geo(this, layout, 4, 0);
@@ -103,10 +103,14 @@ void GrInOrderDrawBuffer::drawRect(const GrRect& rect,
// the rect.
bool disabledClip = false;
if (this->isClipState() && fClip.isRect()) {
- GrRect clipRect = GrRect(*fClip.getRects());
+
+ // single rect clip should have bounds
+ GrAssert(fClip.hasBounds());
+
+ GrRect clipRect = GrRect(fClip.getBounds());
// If the clip rect touches the edge of the viewport, extended it
// out (close) to infinity to avoid bogus intersections.
- // We might consider a more exact clip to viewport if this
+ // We might consider a more exact clip to viewport if this
// conservative test fails.
const GrRenderTarget* target = this->getRenderTarget();
if (0 >= clipRect.fLeft) {
@@ -135,11 +139,11 @@ void GrInOrderDrawBuffer::drawRect(const GrRect& rect,
disabledClip = true;
}
}
- if (!needsNewClip() && !needsNewState() && fCurrQuad > 0 &&
+ if (!needsNewClip() && !needsNewState() && fCurrQuad > 0 &&
fCurrQuad < fMaxQuads && layout == fLastRectVertexLayout) {
int vsize = VertexSize(layout);
-
+
Draw& lastDraw = fDraws.back();
GrAssert(lastDraw.fIndexBuffer == fQuadIndexBuffer);
@@ -500,7 +504,7 @@ void GrInOrderDrawBuffer::pushState() {
}
this->saveCurrentDrawState(&fStates.push_back());
}
-
+
bool GrInOrderDrawBuffer::needsNewClip() const {
if (fCurrDrawState.fFlagBits & kClip_StateBit) {
if (fClips.empty() || (fClipSet && fClips.back() != fClip)) {
@@ -509,12 +513,12 @@ bool GrInOrderDrawBuffer::needsNewClip() const {
}
return false;
}
-
+
void GrInOrderDrawBuffer::pushClip() {
fClips.push_back() = fClip;
fClipSet = false;
}
-
+
void GrInOrderDrawBuffer::clipWillBeSet(const GrClip& newClip) {
fClipSet = true;
}
diff --git a/gpu/src/GrPath.cpp b/gpu/src/GrPath.cpp
index 554b3b915a..ca5c43baa2 100644
--- a/gpu/src/GrPath.cpp
+++ b/gpu/src/GrPath.cpp
@@ -3,6 +3,8 @@
GrPath::GrPath() {}
GrPath::GrPath(const GrPath& src) : INHERITED() {
+ GrPath::Iter iter(src);
+ this->resetFromIter(&iter);
}
GrPath::GrPath(GrPathIter& iter) {
@@ -12,6 +14,26 @@ GrPath::GrPath(GrPathIter& iter) {
GrPath::~GrPath() {
}
+bool GrPath::operator ==(const GrPath& path) const {
+ if (fVerbs.count() != path.fVerbs.count() ||
+ fPts.count() != path.fPts.count()) {
+ return false;
+ }
+
+ for (int v = 0; v < fVerbs.count(); ++v) {
+ if (fVerbs[v] != path.fVerbs[v]) {
+ return false;
+ }
+ }
+
+ for (int p = 0; p < fPts.count(); ++p) {
+ if (fPts[p] != path.fPts[p]) {
+ return false;
+ }
+ }
+ return true;
+}
+
void GrPath::ensureMoveTo() {
if (fVerbs.isEmpty() || this->wasLastVerb(kClose)) {
*fVerbs.append() = kMove;
@@ -90,6 +112,7 @@ void GrPath::resetFromIter(GrPathIter* iter) {
break;
}
}
+ fConvexHint = iter->convexHint();
}
///////////////////////////////////////////////////////////////////////////////
@@ -157,7 +180,7 @@ GrPathIter::Command GrPath::Iter::next(GrPoint points[]) {
return (GrPathIter::Command)cmd;
}
-GrPathIter::ConvexHint GrPath::Iter::hint() const {
+GrPathIter::ConvexHint GrPath::Iter::convexHint() const {
return fPath.getConvexHint();
}
diff --git a/gpu/src/GrPathRenderer.cpp b/gpu/src/GrPathRenderer.cpp
index c47b6e522c..3e2b4b35c5 100644
--- a/gpu/src/GrPathRenderer.cpp
+++ b/gpu/src/GrPathRenderer.cpp
@@ -6,15 +6,145 @@
#include "GrMemory.h"
#include "GrTexture.h"
-
-
-GrDefaultPathRenderer::GrDefaultPathRenderer(bool singlePassWindingStencil)
- : fSinglePassWindingStencil(singlePassWindingStencil) {
+GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport,
+ bool stencilWrapOpsSupport)
+ : fSeparateStencil(separateStencilSupport),
+ fStencilWrapOps(stencilWrapOpsSupport) {
}
////////////////////////////////////////////////////////////////////////////////
-// Helpers for draw Path
+// Stencil rules for paths
+
+////// Even/Odd
+
+static const GrStencilSettings gEOStencilPass = {
+ kInvert_StencilOp, kInvert_StencilOp,
+ kKeep_StencilOp, kKeep_StencilOp,
+ kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff
+};
+
+// ok not to check clip b/c stencil pass only wrote inside clip
+static const GrStencilSettings gEOColorPass = {
+ kZero_StencilOp, kZero_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kNotEqual_StencilFunc, kNotEqual_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x0, 0x0,
+ 0xffffffff, 0xffffffff
+};
+
+// have to check clip b/c outside clip will always be zero.
+static const GrStencilSettings gInvEOColorPass = {
+ kZero_StencilOp, kZero_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kEqualIfInClip_StencilFunc, kEqualIfInClip_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x0, 0x0,
+ 0xffffffff, 0xffffffff
+};
+
+////// Winding
+
+// when we have separate stencil we increment front faces / decrement back faces
+// when we don't have wrap incr and decr we use the stencil test to simulate
+// them.
+
+static const GrStencilSettings gWindStencilSeparateWithWrap = {
+ kIncWrap_StencilOp, kDecWrap_StencilOp,
+ kKeep_StencilOp, kKeep_StencilOp,
+ kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff
+};
+
+// if inc'ing the max value, invert to make 0
+// if dec'ing zero invert to make all ones.
+// we can't avoid touching the stencil on both passing and
+// failing, so we can't resctrict ourselves to the clip.
+static const GrStencilSettings gWindStencilSeparateNoWrap = {
+ kInvert_StencilOp, kInvert_StencilOp,
+ kIncClamp_StencilOp, kDecClamp_StencilOp,
+ kEqual_StencilFunc, kEqual_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0xffffffff, 0x0,
+ 0xffffffff, 0xffffffff
+};
+
+// When there are no separate faces we do two passes to setup the winding rule
+// stencil. First we draw the front faces and inc, then we draw the back faces
+// and dec. These are same as the above two split into the incrementing and
+// decrementing passes.
+static const GrStencilSettings gWindSingleStencilWithWrapInc = {
+ kIncWrap_StencilOp, kIncWrap_StencilOp,
+ kKeep_StencilOp, kKeep_StencilOp,
+ kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff
+};
+static const GrStencilSettings gWindSingleStencilWithWrapDec = {
+ kDecWrap_StencilOp, kDecWrap_StencilOp,
+ kKeep_StencilOp, kKeep_StencilOp,
+ kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff
+};
+static const GrStencilSettings gWindSingleStencilNoWrapInc = {
+ kInvert_StencilOp, kInvert_StencilOp,
+ kIncClamp_StencilOp, kIncClamp_StencilOp,
+ kEqual_StencilFunc, kEqual_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff
+};
+static const GrStencilSettings gWindSingleStencilNoWrapDec = {
+ kInvert_StencilOp, kInvert_StencilOp,
+ kDecClamp_StencilOp, kDecClamp_StencilOp,
+ kEqual_StencilFunc, kEqual_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x0, 0x0,
+ 0xffffffff, 0xffffffff
+};
+
+static const GrStencilSettings gWindColorPass = {
+ kZero_StencilOp, kZero_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kNonZeroIfInClip_StencilFunc, kNonZeroIfInClip_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x0, 0x0,
+ 0xffffffff, 0xffffffff
+};
+
+static const GrStencilSettings gInvWindColorPass = {
+ kZero_StencilOp, kZero_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kEqualIfInClip_StencilFunc, kEqualIfInClip_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x0, 0x0,
+ 0xffffffff, 0xffffffff
+};
+
+////// Normal render to stencil
+
+// Sometimes the default path renderer can draw a path directly to the stencil
+// buffer without having to first resolve the interior / exterior.
+static const GrStencilSettings gDirectToStencil = {
+ kZero_StencilOp, kZero_StencilOp,
+ kIncClamp_StencilOp, kIncClamp_StencilOp,
+ kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x0, 0x0,
+ 0xffffffff, 0xffffffff
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Helpers for drawPath
#define STENCIL_OFF 0 // Always disable stencil (even when needed)
static const GrScalar gTolerance = GR_Scalar1;
@@ -146,11 +276,11 @@ static inline bool single_pass_path(const GrPathIter& path,
return true;
#else
if (kEvenOdd_PathFill == fill) {
- GrPathIter::ConvexHint hint = path.hint();
+ GrPathIter::ConvexHint hint = path.convexHint();
return hint == GrPathIter::kConvex_ConvexHint ||
hint == GrPathIter::kNonOverlappingConvexPieces_ConvexHint;
} else if (kWinding_PathFill == fill) {
- GrPathIter::ConvexHint hint = path.hint();
+ GrPathIter::ConvexHint hint = path.convexHint();
return hint == GrPathIter::kConvex_ConvexHint ||
hint == GrPathIter::kNonOverlappingConvexPieces_ConvexHint ||
(hint == GrPathIter::kSameWindingConvexPieces_ConvexHint &&
@@ -161,13 +291,17 @@ static inline bool single_pass_path(const GrPathIter& path,
#endif
}
-void GrDefaultPathRenderer::drawPath(GrDrawTarget* target,
- GrDrawTarget::StageBitfield stages,
- GrPathIter* path,
- GrPathFill fill,
- const GrPoint* translate) {
+void GrDefaultPathRenderer::drawPathHelper(GrDrawTarget* target,
+ GrDrawTarget::StageBitfield stages,
+ GrPathIter* path,
+ GrPathFill fill,
+ const GrPoint* translate,
+ bool stencilOnly) {
GrDrawTarget::AutoStateRestore asr(target);
+ bool colorWritesWereDisabled = target->isColorWriteDisabled();
+ // face culling doesn't make sense here
+ GrAssert(GrDrawTarget::kBoth_DrawFace == target->getDrawFace());
GrMatrix viewM = target->getViewMatrix();
// In order to tesselate the path we get a bound on how much the matrix can
@@ -185,6 +319,8 @@ void GrDefaultPathRenderer::drawPath(GrDrawTarget* target,
}
GrScalar tolSqd = GrMul(tol, tol);
+ path->rewind();
+
int subpathCnt;
int maxPts = worst_case_point_count(path,
&subpathCnt,
@@ -211,42 +347,91 @@ void GrDefaultPathRenderer::drawPath(GrDrawTarget* target,
// TODO: use primitve restart if available rather than multiple draws
GrPrimitiveType type;
int passCount = 0;
- GrDrawTarget::StencilPass passes[3];
+ const GrStencilSettings* passes[3];
+ GrDrawTarget::DrawFace drawFace[3];
bool reverse = false;
+ bool lastPassIsBounds;
if (kHairLine_PathFill == fill) {
type = kLineStrip_PrimitiveType;
passCount = 1;
- passes[0] = GrDrawTarget::kNone_StencilPass;
+ if (stencilOnly) {
+ passes[0] = &gDirectToStencil;
+ } else {
+ passes[0] = &GrStencilSettings::gDisabled;
+ }
+ lastPassIsBounds = false;
+ drawFace[0] = GrDrawTarget::kBoth_DrawFace;
} else {
type = kTriangleFan_PrimitiveType;
if (single_pass_path(*path, fill, *target)) {
passCount = 1;
- passes[0] = GrDrawTarget::kNone_StencilPass;
+ if (stencilOnly) {
+ passes[0] = &gDirectToStencil;
+ } else {
+ passes[0] = &GrStencilSettings::gDisabled;
+ }
+ drawFace[0] = GrDrawTarget::kBoth_DrawFace;
+ lastPassIsBounds = false;
} else {
switch (fill) {
case kInverseEvenOdd_PathFill:
reverse = true;
// fallthrough
case kEvenOdd_PathFill:
- passCount = 2;
- passes[0] = GrDrawTarget::kEvenOddStencil_StencilPass;
- passes[1] = GrDrawTarget::kEvenOddColor_StencilPass;
+ passes[0] = &gEOStencilPass;
+ if (stencilOnly) {
+ passCount = 1;
+ lastPassIsBounds = false;
+ } else {
+ passCount = 2;
+ lastPassIsBounds = true;
+ if (reverse) {
+ passes[1] = &gInvEOColorPass;
+ } else {
+ passes[1] = &gEOColorPass;
+ }
+ }
+ drawFace[0] = drawFace[1] = GrDrawTarget::kBoth_DrawFace;
break;
case kInverseWinding_PathFill:
reverse = true;
// fallthrough
case kWinding_PathFill:
- passes[0] = GrDrawTarget::kWindingStencil1_StencilPass;
- if (fSinglePassWindingStencil) {
- passes[1] = GrDrawTarget::kWindingColor_StencilPass;
+ if (fSeparateStencil) {
+ if (fStencilWrapOps) {
+ passes[0] = &gWindStencilSeparateWithWrap;
+ } else {
+ passes[0] = &gWindStencilSeparateNoWrap;
+ }
passCount = 2;
+ drawFace[0] = GrDrawTarget::kBoth_DrawFace;
} else {
- passes[1] = GrDrawTarget::kWindingStencil2_StencilPass;
- passes[2] = GrDrawTarget::kWindingColor_StencilPass;
+ if (fStencilWrapOps) {
+ passes[0] = &gWindSingleStencilWithWrapInc;
+ passes[1] = &gWindSingleStencilWithWrapDec;
+ } else {
+ passes[0] = &gWindSingleStencilNoWrapInc;
+ passes[1] = &gWindSingleStencilNoWrapDec;
+ }
+ // which is cw and which is ccw is arbitrary.
+ drawFace[0] = GrDrawTarget::kCW_DrawFace;
+ drawFace[1] = GrDrawTarget::kCCW_DrawFace;
passCount = 3;
}
+ if (stencilOnly) {
+ lastPassIsBounds = false;
+ --passCount;
+ } else {
+ lastPassIsBounds = true;
+ drawFace[passCount-1] = GrDrawTarget::kBoth_DrawFace;
+ if (reverse) {
+ passes[passCount-1] = &gInvWindColorPass;
+ } else {
+ passes[passCount-1] = &gWindColorPass;
+ }
+ }
break;
default:
GrAssert(!"Unknown path fill!");
@@ -254,7 +439,6 @@ void GrDefaultPathRenderer::drawPath(GrDrawTarget* target,
}
}
}
- target->setReverseFill(reverse);
GrPoint pts[4];
@@ -309,11 +493,12 @@ FINISHED:
}
}
- // arbitrary path complexity cutoff
- bool useBounds = fill != kHairLine_PathFill &&
- (reverse || (vert - base) > 8);
- GrPoint* boundsVerts = base + maxPts;
- if (useBounds) {
+ // if we're stenciling we will follow with a pass that draws
+ // a bounding rect to set the color. We're stenciling when
+ // passCount > 1.
+ const int& boundVertexStart = maxPts;
+ GrPoint* boundsVerts = base + boundVertexStart;
+ if (lastPassIsBounds) {
GrRect bounds;
if (reverse) {
GrAssert(NULL != target->getRenderTarget());
@@ -333,20 +518,44 @@ FINISHED:
}
for (int p = 0; p < passCount; ++p) {
- target->setStencilPass(passes[p]);
- if (useBounds && (GrDrawTarget::kEvenOddColor_StencilPass == passes[p] ||
- GrDrawTarget::kWindingColor_StencilPass == passes[p])) {
+ target->setDrawFace(drawFace[p]);
+ target->setStencil(*passes[p]);
+
+ if (lastPassIsBounds && (p == passCount-1)) {
+ if (!colorWritesWereDisabled) {
+ target->disableState(GrDrawTarget::kNoColorWrites_StateBit);
+ }
target->drawNonIndexed(kTriangleFan_PrimitiveType,
- maxPts, 4);
+ boundVertexStart, 4);
} else {
+ if (passCount > 1) {
+ target->enableState(GrDrawTarget::kNoColorWrites_StateBit);
+ }
int baseVertex = 0;
for (int sp = 0; sp < subpathCnt; ++sp) {
target->drawNonIndexed(type,
- baseVertex,
- subpathVertCount[sp]);
+ baseVertex,
+ subpathVertCount[sp]);
baseVertex += subpathVertCount[sp];
}
}
}
}
+
+void GrDefaultPathRenderer::drawPath(GrDrawTarget* target,
+ GrDrawTarget::StageBitfield stages,
+ GrPathIter* path,
+ GrPathFill fill,
+ const GrPoint* translate) {
+ this->drawPathHelper(target, stages, path, fill, translate, false);
+}
+
+void GrDefaultPathRenderer::drawPathToStencil(GrDrawTarget* target,
+ GrPathIter* path,
+ GrPathFill fill,
+ const GrPoint* translate) {
+ GrAssert(kInverseEvenOdd_PathFill != fill);
+ GrAssert(kInverseWinding_PathFill != fill);
+ this->drawPathHelper(target, 0, path, fill, translate, true);
+ }
diff --git a/gpu/src/GrPathRenderer.h b/gpu/src/GrPathRenderer.h
index e3fd71507a..467b0a04d3 100644
--- a/gpu/src/GrPathRenderer.h
+++ b/gpu/src/GrPathRenderer.h
@@ -34,31 +34,107 @@ public:
* @param stages indicates which stages the are already
* in use. All enabled stages expect positions
* as texture coordinates. The path renderer
- * use the remaining stages for its path
+ * use the remaining stages for its path
* filling algorithm.
* @param path the path to draw.
* @param fill the fill rule to apply.
* @param translate optional additional translation to apply to
- * the path NULL means (0,0).
+ * the path. NULL means (0,0).
*/
virtual void drawPath(GrDrawTarget* target,
GrDrawTarget::StageBitfield stages,
GrPathIter* path,
GrPathFill fill,
const GrPoint* translate) = 0;
+
+ void drawPath(GrDrawTarget* target,
+ GrDrawTarget::StageBitfield stages,
+ const GrPath& path,
+ GrPathFill fill,
+ const GrPoint* translate) {
+ GrPath::Iter iter(path);
+ this->drawPath(target, stages, &iter, fill, translate);
+ }
+
+ /**
+ * For complex clips Gr uses the stencil buffer. The path renderer must be
+ * able to render paths into the stencil buffer. However, the path renderer
+ * itself may require the stencil buffer to resolve the path fill rule. This
+ * function queries whether the path render requires its own stencil
+ * pass. If this returns false then drawPath() should not modify the
+ * the target's stencil settings.
+ *
+ * @return false if this path renderer can generate interior-only fragments
+ * without changing the stencil settings on the target. If it
+ * returns true the drawPathToStencil will be used when rendering
+ * clips.
+ */
+ virtual bool requiresStencilPass(GrPathIter*) const { return false; }
+
+ bool requiresStencilPass(const GrPath& path) const {
+ GrPath::Iter iter(path);
+ return requiresStencilPass(&iter);
+ }
+
+ /**
+ * Draws a path to the stencil buffer. Assume the writable bits are zero
+ * prior and write a nonzero value in interior samples. The default
+ * implementation assumes the path filling algorithm doesn't require a
+ * separate stencil pass and so just calls drawPath.
+ *
+ * Fill will never be an inverse fill rule.
+ *
+ * @param target the target to draw into.
+ * @param path the path to draw.
+ * @param fill the fill rule to apply.
+ * @param translate optional additional translation to apply to
+ * the path. NULL means (0,0).
+ */
+ virtual void drawPathToStencil(GrDrawTarget* target,
+ GrPathIter* path,
+ GrPathFill fill,
+ const GrPoint* translate) {
+ GrAssert(kInverseEvenOdd_PathFill != fill);
+ GrAssert(kInverseWinding_PathFill != fill);
+
+ this->drawPath(target, 0, path, fill, translate);
+ }
+
+ void drawPathToStencil(GrDrawTarget* target,
+ const GrPath& path,
+ GrPathFill fill,
+ const GrPoint* translate) {
+ GrPath::Iter iter(path);
+ this->drawPathToStencil(target, &iter, fill, translate);
+ }
};
class GrDefaultPathRenderer : public GrPathRenderer {
public:
- GrDefaultPathRenderer(bool singlePassWindingStencil);
+ GrDefaultPathRenderer(bool separateStencilSupport,
+ bool stencilWrapOpsSupport);
virtual void drawPath(GrDrawTarget* target,
GrDrawTarget::StageBitfield stages,
GrPathIter* path,
GrPathFill fill,
const GrPoint* translate);
+ virtual bool requiresStencilPass(GrPath&) const { return true; }
+ virtual void drawPathToStencil(GrDrawTarget* target,
+ GrPathIter* path,
+ GrPathFill fill,
+ const GrPoint* translate);
private:
- bool fSinglePassWindingStencil;
+
+ void drawPathHelper(GrDrawTarget* target,
+ GrDrawTarget::StageBitfield stages,
+ GrPathIter* path,
+ GrPathFill fill,
+ const GrPoint* translate,
+ bool stencilOnly);
+
+ bool fSeparateStencil;
+ bool fStencilWrapOps;
};
#endif \ No newline at end of file
diff --git a/gpu/src/GrStencil.cpp b/gpu/src/GrStencil.cpp
new file mode 100644
index 0000000000..9d68c65ebd
--- /dev/null
+++ b/gpu/src/GrStencil.cpp
@@ -0,0 +1,398 @@
+/*
+ Copyright 2011 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#include "GrStencil.h"
+
+const GrStencilSettings GrStencilSettings::gDisabled = {};
+GR_STATIC_ASSERT(0 == kKeep_StencilOp);
+GR_STATIC_ASSERT(0 == kAlways_StencilFunc);
+
+////////////////////////////////////////////////////////////////////////////////
+// Stencil Rules for Merging user stencil space into clip
+
+// We can't include the clip bit in the ref or mask values because the division
+// between user and clip bits in the stencil depends on the number of stencil
+// bits in the runtime. Comments below indicate what the code should do to
+// incorporate the clip bit into these settings.
+
+///////
+// Replace
+
+// set the ref to be the clip bit, but mask it out for the test
+static const GrStencilSettings gUserToClipReplace = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kLess_StencilFunc, kLess_StencilFunc,
+ 0xffffffff, 0xffffffff, // unset clip bit
+ 0x0, 0x0, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+static const GrStencilSettings gInvUserToClipReplace = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kEqual_StencilFunc, kEqual_StencilFunc,
+ 0xffffffff, 0xffffffff, // unset clip bit
+ 0x0, 0x0, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+
+///////
+// Intersect
+static const GrStencilSettings gUserToClipIsect = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kLess_StencilFunc, kLess_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x0, 0x0, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+static const GrStencilSettings gInvUserToClipIsect = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kEqual_StencilFunc, kEqual_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x0, 0x0, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+
+///////
+// Difference
+static const GrStencilSettings gUserToClipDiff = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kEqual_StencilFunc, kEqual_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x0, 0x0, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+static const GrStencilSettings gInvUserToClipDiff = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kLess_StencilFunc, kLess_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x0, 0x0, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+
+///////
+// Union
+
+// first pass makes all the passing cases >= just clip bit set.
+static const GrStencilSettings gUserToClipUnionPass0 = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kKeep_StencilOp, kKeep_StencilOp,
+ kLEqual_StencilFunc, kLEqual_StencilFunc,
+ 0xffffffff, 0xffffffff, // unset clip bit
+ 0x00000001, 0x00000001, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+
+// second pass allows anything greater than just clip bit set to pass
+static const GrStencilSettings gUserToClipUnionPass1 = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kLEqual_StencilFunc, kLEqual_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x00000000, 0x00000000, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+
+// for inverse first pass finds non-zerp user with clip bit set
+// and converts it to just clip bit set
+static const GrStencilSettings gInvUserToClipUnionPass0 = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kKeep_StencilOp, kKeep_StencilOp,
+ kLess_StencilFunc, kLess_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x00000000, 0x00000000, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+
+// second pass lets anything through with a nonzero user portion
+// and writes a ref value with just the clip bit set to it.
+static const GrStencilSettings gInvUserToClipUnionPass1 = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kLess_StencilFunc, kLess_StencilFunc,
+ 0xffffffff, 0xffffffff, // unset clip bit
+ 0x00000000, 0x00000000, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+
+///////
+// Xor
+static const GrStencilSettings gUserToClipXorPass0 = {
+ kInvert_StencilOp, kInvert_StencilOp,
+ kKeep_StencilOp, kKeep_StencilOp,
+ kEqual_StencilFunc, kEqual_StencilFunc,
+ 0xffffffff, 0xffffffff, // unset clip bit
+ 0x00000000, 0x00000000,
+ 0xffffffff, 0xffffffff
+};
+
+static const GrStencilSettings gUserToClipXorPass1 = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kGreater_StencilFunc, kGreater_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x00000000, 0x00000000, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+
+static const GrStencilSettings gInvUserToClipXorPass0 = {
+ kInvert_StencilOp, kInvert_StencilOp,
+ kKeep_StencilOp, kKeep_StencilOp,
+ kEqual_StencilFunc, kEqual_StencilFunc,
+ 0xffffffff, 0xffffffff, // unset clip bit
+ 0x00000000, 0x00000000,
+ 0xffffffff, 0xffffffff
+};
+
+static const GrStencilSettings gInvUserToClipXorPass1 = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kLess_StencilFunc, kLess_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x00000000, 0x00000000, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+
+///////
+// Reverse Diff
+static const GrStencilSettings gUserToClipRDiffPass0 = {
+ kInvert_StencilOp, kInvert_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kLess_StencilFunc, kLess_StencilFunc,
+ 0xffffffff, 0xffffffff, // unset clip bit
+ 0x00000000, 0x00000000, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+
+static const GrStencilSettings gUserToClipRDiffPass1 = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kEqual_StencilFunc, kEqual_StencilFunc,
+ 0x00000000, 0x00000000, // set clip bit
+ 0x00000000, 0x00000000, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+
+static const GrStencilSettings gInvUserToClipRDiff = {
+ kInvert_StencilOp, kInvert_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kEqual_StencilFunc, kEqual_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000 // set clip bit
+};
+///////
+// Direct to Stencil
+
+// We can render a clip element directly without first writing to the client
+// portion of the clip when the fill is not inverse and the set operation will
+// only modify the in/out status of samples covered by the clip element.
+
+// this one only works if used right after stencil clip was cleared.
+// Our GrClip doesn't allow midstream replace ops.
+static const GrStencilSettings gReplaceClip = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kReplace_StencilOp, kReplace_StencilOp,
+ kAlways_StencilFunc, kAlways_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x00000000, 0x00000000, // set clip bit
+ 0x00000000, 0x00000000 // set clipBit
+};
+
+static const GrStencilSettings gUnionClip = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kReplace_StencilOp, kReplace_StencilOp,
+ kAlways_StencilFunc, kAlways_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x00000000, 0x00000000, // set clip bit
+ 0x00000000, 0x00000000 // set clip bit
+};
+
+static const GrStencilSettings gXorClip = {
+ kInvert_StencilOp, kInvert_StencilOp,
+ kInvert_StencilOp, kInvert_StencilOp,
+ kAlways_StencilFunc, kAlways_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000 // set clip bit
+};
+
+static const GrStencilSettings gDiffClip = {
+ kZero_StencilOp, kZero_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kAlways_StencilFunc, kAlways_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000 // set clip bit
+};
+
+static const GrPathFill gNonInvertedFills[] = {
+ kWinding_PathFill, // kWinding_PathFill
+ kEvenOdd_PathFill, // kEvenOdd_PathFill
+ kWinding_PathFill, // kInverseWinding_PathFill
+ kEvenOdd_PathFill, // kInverseEvenOdd_PathFill
+ kWinding_PathFill, // kHairLine_PathFill
+};
+
+static const bool gIsFillInverted[] = {
+ false, // kWinding_PathFill
+ false, // kEvenOdd_PathFill
+ true, // kInverseWinding_PathFill
+ true, // kInverseEvenOdd_PathFill
+ false, // kHairLine_PathFill
+};
+GR_STATIC_ASSERT(0 == kWinding_PathFill);
+GR_STATIC_ASSERT(1 == kEvenOdd_PathFill);
+GR_STATIC_ASSERT(2 == kInverseWinding_PathFill);
+GR_STATIC_ASSERT(3 == kInverseEvenOdd_PathFill);
+GR_STATIC_ASSERT(4 == kHairLine_PathFill);
+GR_STATIC_ASSERT(5 == kPathFillCount);
+
+bool GrStencilSettings::GetClipPasses(GrSetOp op,
+ bool canBeDirect,
+ unsigned int stencilClipMask,
+ GrPathFill* fill,
+ int* numPasses,
+ GrStencilSettings settings[kMaxStencilClipPasses]) {
+ if (canBeDirect) {
+ if (!gIsFillInverted[*fill]) {
+ *numPasses = 0;
+ switch (op) {
+ case kReplace_SetOp:
+ *numPasses = 1;
+ settings[0] = gReplaceClip;
+ break;
+ case kUnion_SetOp:
+ *numPasses = 1;
+ settings[0] = gUnionClip;
+ break;
+ case kXor_SetOp:
+ *numPasses = 1;
+ settings[0] = gXorClip;
+ break;
+ case kDifference_SetOp:
+ *numPasses = 1;
+ settings[0] = gDiffClip;
+ break;
+ default: // suppress warning
+ break;
+ }
+ if (1 == *numPasses) {
+ settings[0].fFrontFuncRef |= stencilClipMask;
+ settings[0].fFrontWriteMask |= stencilClipMask;
+ settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
+ settings[0].fBackWriteMask = settings[0].fFrontWriteMask;
+ return true;
+ }
+ }
+ }
+ switch (op) {
+ case kReplace_SetOp:
+ *numPasses= 1;
+ settings[0] = gIsFillInverted[*fill] ? gInvUserToClipReplace : gUserToClipReplace;
+ settings[0].fFrontFuncMask &= ~stencilClipMask;
+ settings[0].fFrontFuncRef |= stencilClipMask;
+ settings[0].fBackFuncMask = settings[0].fFrontFuncMask;
+ settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
+
+ case kIntersect_SetOp:
+ *numPasses = 1;
+ settings[0] = gIsFillInverted[*fill] ? gInvUserToClipIsect : gUserToClipIsect;
+ settings[0].fFrontFuncRef = stencilClipMask;
+ settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
+ break;
+ case kUnion_SetOp:
+ *numPasses = 2;
+ if (gIsFillInverted[*fill]) {
+ settings[0] = gInvUserToClipUnionPass0;
+ settings[0].fFrontFuncRef |= stencilClipMask;
+ settings[0].fBackFuncRef = settings[0].fFrontFuncMask;
+
+ settings[1] = gInvUserToClipUnionPass1;
+ settings[1].fFrontFuncMask &= ~stencilClipMask;
+ settings[1].fFrontFuncRef |= stencilClipMask;
+ settings[1].fBackFuncMask = settings[1].fFrontFuncMask;
+ settings[1].fBackFuncRef = settings[1].fFrontFuncRef;
+
+ } else {
+ settings[0] = gUserToClipUnionPass0;
+ settings[0].fFrontFuncMask &= ~stencilClipMask;
+ settings[0].fFrontFuncRef |= stencilClipMask;
+ settings[0].fBackFuncMask = settings[0].fFrontFuncMask;
+ settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
+
+ settings[1] = gUserToClipUnionPass1;
+ settings[1].fFrontFuncRef |= stencilClipMask;
+ settings[1].fBackFuncRef = settings[1].fFrontFuncRef;
+ }
+ break;
+ case kXor_SetOp:
+ *numPasses = 2;
+ if (gIsFillInverted[*fill]) {
+ settings[0] = gInvUserToClipXorPass0;
+ settings[0].fFrontFuncMask &= ~stencilClipMask;
+ settings[0].fBackFuncMask = settings[0].fFrontFuncMask;
+
+ settings[1] = gInvUserToClipXorPass1;
+ settings[1].fFrontFuncRef |= stencilClipMask;
+ settings[1].fBackFuncRef = settings[1].fFrontFuncRef;
+ } else {
+ settings[0] = gUserToClipXorPass0;
+ settings[0].fFrontFuncMask &= ~stencilClipMask;
+ settings[0].fBackFuncMask = settings[0].fFrontFuncMask;
+
+ settings[1] = gUserToClipXorPass1;
+ settings[1].fFrontFuncRef |= stencilClipMask;
+ settings[1].fBackFuncRef = settings[1].fFrontFuncRef;
+ }
+ break;
+ case kDifference_SetOp:
+ *numPasses = 1;
+ settings[0] = gIsFillInverted[*fill] ? gInvUserToClipDiff : gUserToClipDiff;
+ settings[0].fFrontFuncRef |= stencilClipMask;
+ settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
+ break;
+ case kReverseDifference_SetOp:
+ if (gIsFillInverted[*fill]) {
+ *numPasses = 1;
+ settings[0] = gInvUserToClipRDiff;
+ settings[0].fFrontWriteMask |= stencilClipMask;
+ settings[0].fBackWriteMask = settings[0].fFrontWriteMask;
+ } else {
+ *numPasses = 2;
+ settings[0] = gUserToClipRDiffPass0;
+ settings[0].fFrontFuncMask &= ~stencilClipMask;
+ settings[0].fBackFuncMask = settings[0].fFrontFuncMask;
+ settings[0].fFrontFuncRef |= stencilClipMask;
+ settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
+
+ settings[1] = gUserToClipRDiffPass1;
+ settings[1].fFrontFuncMask |= stencilClipMask;
+ settings[1].fFrontFuncRef |= stencilClipMask;
+ settings[1].fBackFuncMask = settings[1].fFrontFuncMask;
+ settings[1].fBackFuncRef = settings[1].fFrontFuncRef;
+ }
+ break;
+ default:
+ GrCrash("Unknown set op");
+ }
+ *fill = gNonInvertedFills[*fill];
+ return false;
+} \ No newline at end of file
diff --git a/gpu/src/GrTextContext.cpp b/gpu/src/GrTextContext.cpp
index 8ce45e40eb..802e3e3ab7 100644
--- a/gpu/src/GrTextContext.cpp
+++ b/gpu/src/GrTextContext.cpp
@@ -67,21 +67,25 @@ GrTextContext::GrTextContext(GrContext* context,
fCurrTexture = NULL;
fCurrVertex = 0;
- fClipRect = context->getClip().getBounds();
if (NULL != extMatrix) {
fExtMatrix = *extMatrix;
} else {
fExtMatrix = GrMatrix::I();
}
- if (!fExtMatrix.isIdentity()) {
- GrMatrix inverse;
- GrRect r;
- r.set(fClipRect);
- if (fExtMatrix.invert(&inverse)) {
- inverse.mapRect(&r);
- r.roundOut(&fClipRect);
+ if (context->getClip().hasBounds()) {
+ if (!fExtMatrix.isIdentity()) {
+ GrMatrix inverse;
+ GrRect r = context->getClip().getBounds();
+ if (fExtMatrix.invert(&inverse)) {
+ inverse.mapRect(&r);
+ r.roundOut(&fClipRect);
+ }
+ } else {
+ context->getClip().getBounds().roundOut(&fClipRect);
}
+ } else {
+ fClipRect.setLargest();
}
// save the context's original matrix off and restore in destructor
diff --git a/gpu/src/gr_files.mk b/gpu/src/gr_files.mk
index 88aa84b222..d0f41e748f 100644
--- a/gpu/src/gr_files.mk
+++ b/gpu/src/gr_files.mk
@@ -22,4 +22,5 @@ SOURCE := \
GrTextContext.cpp \
GrTextStrike.cpp \
GrBufferAllocPool.cpp\
- GrPathRenderer.cpp
+ GrPathRenderer.cpp \
+ GrStencil.cpp
diff --git a/gpu/src/gr_unittests.cpp b/gpu/src/gr_unittests.cpp
index 8ef61b1605..9caaa1fb2b 100644
--- a/gpu/src/gr_unittests.cpp
+++ b/gpu/src/gr_unittests.cpp
@@ -73,71 +73,9 @@ static void test_bsearch() {
}
}
-static void dump(const GrClip& clip, const char message[]) {
- GrPrintf("--- dump clip %s\n", message);
- GrClipIter iter(clip);
- while (!iter.isDone()) {
- GrIRect r;
- iter.getRect(&r);
- GrPrintf("--- [%d %d %d %d]\n", r.fLeft, r.fTop, r.fRight, r.fBottom);
- iter.next();
- }
-}
-
-static void test_clip() {
- GrClip clip;
- GrAssert(clip.isEmpty());
- GrAssert(!clip.isRect());
- GrAssert(!clip.isComplex());
- GrAssert(clip.getBounds().equalsLTRB(0, 0, 0, 0));
- GrAssert(0 == clip.countRects());
-
- clip.setRect(GrIRect(10, 10, 10, 10));
- GrAssert(clip.isEmpty());
- GrAssert(!clip.isRect());
- GrAssert(!clip.isComplex());
- GrAssert(clip.getBounds().equalsLTRB(0, 0, 0, 0));
- GrAssert(0 == clip.countRects());
- dump(clip, "empty");
-
- clip.setRect(GrIRect(10, 10, 20, 20));
- GrAssert(!clip.isEmpty());
- GrAssert(clip.isRect());
- GrAssert(!clip.isComplex());
- GrAssert(clip.getBounds().equalsLTRB(10, 10, 20, 20));
- GrAssert(1 == clip.countRects());
- GrAssert(clip.getRects()[0] == clip.getBounds());
- dump(clip, "rect");
-
- clip.addRect(GrIRect(20, 20, 25, 25));
- GrAssert(!clip.isEmpty());
- GrAssert(!clip.isRect());
- GrAssert(clip.isComplex());
- GrAssert(clip.getBounds().equalsLTRB(10, 10, 25, 25));
- GrAssert(2 == clip.countRects());
- dump(clip, "complex");
-
- GrClip c1(clip);
- GrAssert(c1 == clip);
- GrClip c2;
- GrAssert(c2 != c1);
- c2 = clip;
- GrAssert(c2 == clip);
-
- clip.setEmpty();
- GrAssert(clip.isEmpty());
- GrAssert(!clip.isRect());
- GrAssert(!clip.isComplex());
- GrAssert(clip.getBounds().equalsLTRB(0, 0, 0, 0));
-
- GrAssert(c1 != clip);
- GrAssert(c2 != clip);
-}
-
void gr_run_unittests() {
test_tdarray();
test_bsearch();
- test_clip();
GrMatrix::UnitTest();
GrRedBlackTree<int>::UnitTest();
}
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index 1e153078c7..eef3aea572 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -794,7 +794,8 @@ private:
SkDevice* fLastDeviceToGainFocus;
SkDeviceFactory* fDeviceFactory;
- void prepareForDeviceDraw(SkDevice*, const SkMatrix&, const SkRegion&);
+ void prepareForDeviceDraw(SkDevice*, const SkMatrix&, const SkRegion&,
+ const SkClipStack& clipStack);
bool fDeviceCMDirty; // cleared by updateDeviceCMCache()
void updateDeviceCMCache();
diff --git a/include/core/SkClipStack.h b/include/core/SkClipStack.h
index fb941553b3..db42e4df26 100644
--- a/include/core/SkClipStack.h
+++ b/include/core/SkClipStack.h
@@ -29,6 +29,11 @@ public:
class B2FIter {
public:
+ /**
+ * Creates an uninitialized iterator. Must be reset()
+ */
+ B2FIter();
+
B2FIter(const SkClipStack& stack);
struct Clip {
@@ -48,6 +53,11 @@ public:
*/
const Clip* next();
+ /**
+ * Restarts the iterator on a clip stack.
+ */
+ void reset(const SkClipStack& stack);
+
private:
Clip fClip;
SkDeque::F2BIter fIter;
diff --git a/include/core/SkDeque.h b/include/core/SkDeque.h
index 99c8dd46ab..92d515300a 100644
--- a/include/core/SkDeque.h
+++ b/include/core/SkDeque.h
@@ -52,9 +52,16 @@ private:
public:
class F2BIter {
public:
+ /**
+ * Creates an uninitialized iterator. Must be reset()
+ */
+ F2BIter();
+
F2BIter(const SkDeque& d);
void* next();
+ void reset(const SkDeque& d);
+
private:
SkDeque::Head* fHead;
char* fPos;
diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h
index a7903993a4..c0d71c3f8e 100644
--- a/include/core/SkDevice.h
+++ b/include/core/SkDevice.h
@@ -141,7 +141,8 @@ public:
/** Called when this device gains focus (i.e becomes the current device
for drawing).
*/
- virtual void gainFocus(SkCanvas*, const SkMatrix&, const SkRegion&) {}
+ virtual void gainFocus(SkCanvas*, const SkMatrix&, const SkRegion&,
+ const SkClipStack&) {}
/** Causes any deferred drawing to the device to be completed.
*/
diff --git a/include/core/SkRegion.h b/include/core/SkRegion.h
index 8d9ff013ed..58f4f3fb3f 100644
--- a/include/core/SkRegion.h
+++ b/include/core/SkRegion.h
@@ -260,7 +260,7 @@ public:
bool rewind();
// reset the iterator, using the new region
void reset(const SkRegion&);
- bool done() { return fDone; }
+ bool done() const { return fDone; }
void next();
const SkIRect& rect() const { return fRect; }
// may return null
diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h
index 2db33800ed..3fed99ab69 100644
--- a/include/gpu/SkGpuDevice.h
+++ b/include/gpu/SkGpuDevice.h
@@ -68,7 +68,8 @@ public:
* Override from SkGpuDevice, so we can set our FBO to be the render target
* The canvas parameter must be a SkGpuCanvas
*/
- virtual void gainFocus(SkCanvas*, const SkMatrix&, const SkRegion&);
+ virtual void gainFocus(SkCanvas*, const SkMatrix&, const SkRegion&,
+ const SkClipStack& clipStack);
virtual SkGpuTexture* accessTexture() { return (SkGpuTexture*)fTexture; }
diff --git a/include/gpu/SkGr.h b/include/gpu/SkGr.h
index ccdd400933..d6a3fab8cc 100644
--- a/include/gpu/SkGr.h
+++ b/include/gpu/SkGr.h
@@ -20,7 +20,7 @@
#include <stddef.h>
-// tetrark headers
+// Gr headers
#include "GrConfig.h"
#include "GrContext.h"
#include "GrFontScaler.h"
@@ -33,6 +33,7 @@
#include "SkPoint.h"
#include "SkRegion.h"
#include "SkShader.h"
+#include "SkClipStack.h"
#if (GR_DEBUG && defined(SK_RELEASE)) || (GR_RELEASE && defined(SK_DEBUG))
// #error "inconsistent GR_DEBUG and SK_DEBUG"
@@ -170,39 +171,99 @@ public:
class SkGrPathIter : public GrPathIter {
public:
- SkGrPathIter(const SkPath& path) : fIter(path, false), fPath(path) {}
+ SkGrPathIter() { fPath = NULL; }
+ SkGrPathIter(const SkPath& path) { reset(path); }
virtual Command next(GrPoint pts[]);
virtual Command next();
virtual void rewind();
virtual ConvexHint hint() const;
+
+ void reset(const SkPath& path) {
+ fPath = &path;
+ fIter.setPath(path, false);
+ }
private:
#if !SK_SCALAR_IS_GR_SCALAR
SkPoint fPoints[4];
#endif
SkPath::Iter fIter;
- const SkPath& fPath;
+ const SkPath* fPath;
};
class SkGrClipIterator : public GrClipIterator {
public:
- void reset(const SkRegion& clip) {
- fIter.reset(clip);
- this->invalidateBoundsCache();
- }
+ SkGrClipIterator() { fClipStack = NULL; fCurr = NULL; }
+ SkGrClipIterator(const SkClipStack& clipStack) { this->reset(clipStack); }
+
+ void reset(const SkClipStack& clipStack);
// overrides
+ virtual bool isDone() const { return NULL == fCurr; }
+ virtual void next() { fCurr = fIter.next(); }
+ virtual void rewind() { this->reset(*fClipStack); }
+ virtual GrClipType getType() const;
+
+ virtual GrSetOp getOp() const;
+
+ virtual void getRect(GrRect* rect) const {
+ *rect = Sk2Gr(*fCurr->fRect);
+ }
- virtual bool isDone() { return fIter.done(); }
- virtual void getRect(GrIRect* rect) {
- SkGr::SetIRect(rect, fIter.rect());
+ virtual GrPathIter* getPathIter() {
+ fPathIter.reset(*fCurr->fPath);
+ return &fPathIter;
}
+
+ virtual GrPathFill getPathFill() const;
+
+private:
+ const SkClipStack* fClipStack;
+ SkClipStack::B2FIter fIter;
+ SkGrPathIter fPathIter;
+ // SkClipStack's auto advances on each get
+ // so we store the current pos here.
+ const SkClipStack::B2FIter::Clip* fCurr;
+};
+
+class SkGrRegionIterator : public GrClipIterator {
+public:
+ SkGrRegionIterator() {}
+ SkGrRegionIterator(const SkRegion& region) { this->reset(region); }
+
+ void reset(const SkRegion& region) {
+ fRegion = &region;
+ fIter.reset(region);
+ }
+
+ // overrides
+ virtual bool isDone() const { return fIter.done(); }
virtual void next() { fIter.next(); }
- virtual void rewind() { fIter.rewind(); }
- virtual void computeBounds(GrIRect* bounds);
+ virtual void rewind() { this->reset(*fRegion); }
+ virtual GrClipType getType() const { return kRect_ClipType; }
+
+ virtual GrSetOp getOp() const { return kUnion_SetOp; }
+ virtual void getRect(GrRect* rect) const {
+ const SkIRect& r = fIter.rect();
+ rect->fLeft = GrIntToScalar(r.fLeft);
+ rect->fTop = GrIntToScalar(r.fTop);
+ rect->fRight = GrIntToScalar(r.fRight);
+ rect->fBottom = GrIntToScalar(r.fBottom);
+ }
+
+ virtual GrPathIter* getPathIter() {
+ SkASSERT(0);
+ return NULL;
+ }
+
+ virtual GrPathFill getPathFill() const {
+ SkASSERT(0);
+ return kWinding_PathFill;
+ }
private:
- SkRegion::Iterator fIter;
+ const SkRegion* fRegion;
+ SkRegion::Iterator fIter;
};
class SkGlyphCache;
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index e2d6af4bec..e636d2b67d 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -264,7 +264,7 @@ public:
}
// fCurrLayer may be NULL now
- fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip);
+ fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip, *fClipStack);
return true;
}
return false;
@@ -632,10 +632,11 @@ void SkCanvas::updateDeviceCMCache() {
}
void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
- const SkRegion& clip) {
+ const SkRegion& clip,
+ const SkClipStack& clipStack) {
SkASSERT(device);
if (fLastDeviceToGainFocus != device) {
- device->gainFocus(this, matrix, clip);
+ device->gainFocus(this, matrix, clip, clipStack);
fLastDeviceToGainFocus = device;
}
}
diff --git a/src/core/SkClipStack.cpp b/src/core/SkClipStack.cpp
index 2b63aea9a0..864f23a4b2 100644
--- a/src/core/SkClipStack.cpp
+++ b/src/core/SkClipStack.cpp
@@ -116,7 +116,11 @@ void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op) {
///////////////////////////////////////////////////////////////////////////////
-SkClipStack::B2FIter::B2FIter(const SkClipStack& stack) : fIter(stack.fDeque) {
+SkClipStack::B2FIter::B2FIter() {
+}
+
+SkClipStack::B2FIter::B2FIter(const SkClipStack& stack) {
+ this->reset(stack);
}
const SkClipStack::B2FIter::Clip* SkClipStack::B2FIter::next() {
@@ -142,3 +146,7 @@ const SkClipStack::B2FIter::Clip* SkClipStack::B2FIter::next() {
fClip.fOp = rec->fOp;
return &fClip;
}
+
+void SkClipStack::B2FIter::reset(const SkClipStack& stack) {
+ fIter.reset(stack.fDeque);
+}
diff --git a/src/core/SkDeque.cpp b/src/core/SkDeque.cpp
index 2c6ce64fae..9d685eed8b 100644
--- a/src/core/SkDeque.cpp
+++ b/src/core/SkDeque.cpp
@@ -225,12 +225,12 @@ void SkDeque::pop_back() {
///////////////////////////////////////////////////////////////////////////////
-SkDeque::F2BIter::F2BIter(const SkDeque& d) : fElemSize(d.fElemSize) {
- fHead = d.fFront;
- while (fHead != NULL && fHead->fBegin == NULL) {
- fHead = fHead->fNext;
- }
- fPos = fHead ? fHead->fBegin : NULL;
+SkDeque::F2BIter::F2BIter() {
+ fPos = NULL;
+}
+
+SkDeque::F2BIter::F2BIter(const SkDeque& d) {
+ this->reset(d);
}
void* SkDeque::F2BIter::next() {
@@ -250,3 +250,11 @@ void* SkDeque::F2BIter::next() {
return pos;
}
+void SkDeque::F2BIter::reset(const SkDeque& d) {
+ fElemSize = d.fElemSize;
+ fHead = d.fFront;
+ while (fHead != NULL && fHead->fBegin == NULL) {
+ fHead = fHead->fNext;
+ }
+ fPos = fHead ? fHead->fBegin : NULL;
+}
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 3dfe02aa14..e925e53074 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -267,19 +267,30 @@ void SkGpuDevice::writePixels(const SkBitmap& bitmap, int x, int y) {
///////////////////////////////////////////////////////////////////////////////
+#define USE_CLIP_STACK 0
+
static void convert_matrixclip(GrContext* context, const SkMatrix& matrix,
- const SkRegion& clip) {
+ const SkClipStack& clipStack,
+ const SkRegion& clipRegion) {
GrMatrix grmat;
SkGr::SkMatrix2GrMatrix(matrix, &grmat);
context->setMatrix(grmat);
+#if USE_CLIP_STACK
SkGrClipIterator iter;
- iter.reset(clip);
- GrClip grc(&iter);
- if (context->getClip() == grc) {
- } else {
- context->setClip(grc);
- }
+ iter.reset(clipStack);
+#else
+ SkGrRegionIterator iter;
+ iter.reset(clipRegion);
+#endif
+ const SkIRect& skBounds = clipRegion.getBounds();
+ GrRect bounds;
+ bounds.setLTRB(GrIntToScalar(skBounds.fLeft),
+ GrIntToScalar(skBounds.fTop),
+ GrIntToScalar(skBounds.fRight),
+ GrIntToScalar(skBounds.fBottom));
+ GrClip grc(&iter, NULL);
+ context->setClip(grc);
}
// call this ever each draw call, to ensure that the context reflects our state,
@@ -289,7 +300,9 @@ void SkGpuDevice::prepareRenderTarget(const SkDraw& draw) {
fContext->getRenderTarget() != fRenderTarget) {
fContext->setRenderTarget(fRenderTarget);
- convert_matrixclip(fContext, *draw.fMatrix, *draw.fClip);
+ SkASSERT(draw.fClipStack);
+ convert_matrixclip(fContext, *draw.fMatrix,
+ *draw.fClipStack, *draw.fClip);
fNeedPrepareRenderTarget = false;
}
}
@@ -298,16 +311,17 @@ void SkGpuDevice::setMatrixClip(const SkMatrix& matrix, const SkRegion& clip,
const SkClipStack& clipStack) {
this->INHERITED::setMatrixClip(matrix, clip, clipStack);
- convert_matrixclip(fContext, matrix, clip);
+ convert_matrixclip(fContext, matrix, clipStack, clip);
}
void SkGpuDevice::gainFocus(SkCanvas* canvas, const SkMatrix& matrix,
- const SkRegion& clip) {
+ const SkRegion& clip, const SkClipStack& clipStack) {
+
fContext->setRenderTarget(fRenderTarget);
- this->INHERITED::gainFocus(canvas, matrix, clip);
+ this->INHERITED::gainFocus(canvas, matrix, clip, clipStack);
- convert_matrixclip(fContext, matrix, clip);
+ convert_matrixclip(fContext, matrix, clipStack, clip);
if (fNeedClear) {
fContext->eraseColor(0x0);
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index 41cf1bd520..4c7bf6c115 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -135,22 +135,80 @@ SkGrPathIter::Command SkGrPathIter::next() {
}
void SkGrPathIter::rewind() {
- fIter.setPath(fPath, false);
+ fIter.setPath(*fPath, false);
}
GrPathIter::ConvexHint SkGrPathIter::hint() const {
- return fPath.isConvex() ? GrPathIter::kConvex_ConvexHint :
- GrPathIter::kNone_ConvexHint;
+ return fPath->isConvex() ? GrPathIter::kConvex_ConvexHint :
+ GrPathIter::kNone_ConvexHint;
}
///////////////////////////////////////////////////////////////////////////////
-void SkGrClipIterator::computeBounds(GrIRect* bounds) {
- const SkRegion* rgn = fIter.rgn();
- if (rgn) {
- SkGr::SetIRect(bounds, rgn->getBounds());
+void SkGrClipIterator::reset(const SkClipStack& clipStack) {
+ fClipStack = &clipStack;
+ fIter.reset(clipStack);
+ // Gr has no notion of replace, skip to the
+ // last replace in the clip stack.
+ int lastReplace = 0;
+ int curr = 0;
+ while (NULL != (fCurr = fIter.next())) {
+ if (SkRegion::kReplace_Op == fCurr->fOp) {
+ lastReplace = curr;
+ }
+ ++curr;
+ }
+ fIter.reset(clipStack);
+ for (int i = 0; i < lastReplace+1; ++i) {
+ fCurr = fIter.next();
+ }
+}
+
+GrClipType SkGrClipIterator::getType() const {
+ GrAssert(!this->isDone());
+ if (NULL != fCurr->fRect) {
+ return kRect_ClipType;
} else {
- bounds->setEmpty();
+ GrAssert(NULL != fCurr->fPath);
+ return kPath_ClipType;
+ }
+}
+
+GrSetOp SkGrClipIterator::getOp() const {
+ // we skipped to the last "replace" op
+ // when this iter was reset.
+ // GrClip doesn't allow replace, so treat it as
+ // intersect.
+ GrSetOp skToGrOps[] = {
+ kDifference_SetOp, // kDifference_Op
+ kIntersect_SetOp, // kIntersect_Op
+ kUnion_SetOp, // kUnion_Op
+ kXor_SetOp, // kXOR_Op
+ kReverseDifference_SetOp, // kReverseDifference_Op
+ kIntersect_SetOp // kReplace_op
+ };
+ GR_STATIC_ASSERT(0 == SkRegion::kDifference_Op);
+ GR_STATIC_ASSERT(1 == SkRegion::kIntersect_Op);
+ GR_STATIC_ASSERT(2 == SkRegion::kUnion_Op);
+ GR_STATIC_ASSERT(3 == SkRegion::kXOR_Op);
+ GR_STATIC_ASSERT(4 == SkRegion::kReverseDifference_Op);
+ GR_STATIC_ASSERT(5 == SkRegion::kReplace_Op);
+ return skToGrOps[fCurr->fOp];
+}
+
+GrPathFill SkGrClipIterator::getPathFill() const {
+ switch (fCurr->fPath->getFillType()) {
+ case SkPath::kWinding_FillType:
+ return kWinding_PathFill;
+ case SkPath::kEvenOdd_FillType:
+ return kEvenOdd_PathFill;
+ case SkPath::kInverseWinding_FillType:
+ return kInverseWinding_PathFill;
+ case SkPath::kInverseEvenOdd_FillType:
+ return kInverseEvenOdd_PathFill;
+ default:
+ GrCrash("Unsupported path fill in clip.");
+ return kWinding_PathFill; // suppress warning
}
}
diff --git a/src/utils/mac/SkOSWindow_Mac.cpp b/src/utils/mac/SkOSWindow_Mac.cpp
index 77161854b7..05f97a7fed 100644
--- a/src/utils/mac/SkOSWindow_Mac.cpp
+++ b/src/utils/mac/SkOSWindow_Mac.cpp
@@ -531,7 +531,6 @@ void SkOSWindow::detachGL() {
void SkOSWindow::presentGL() {
aglSwapBuffers((AGLContext)fAGLCtx);
- glFlush();
}
#endif
diff --git a/src/utils/win/SkOSWindow_Win.cpp b/src/utils/win/SkOSWindow_Win.cpp
index 3594c2fc30..96a026d6a2 100644
--- a/src/utils/win/SkOSWindow_Win.cpp
+++ b/src/utils/win/SkOSWindow_Win.cpp
@@ -444,10 +444,6 @@ bool SkOSWindow::attachGL(const SkBitmap* offscreen) {
}
}
if (wglMakeCurrent(GetDC((HWND)fHWND), (HGLRC)fHGLRC)) {
- glViewport(0, 0, this->width(), this->height());
- glClearColor(0, 0, 0, 0);
- glClearStencil(0);
- glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
fGLAttached = true;
return true;
}
@@ -462,9 +458,6 @@ void SkOSWindow::detachGL() {
void SkOSWindow::presentGL() {
glFlush();
SwapBuffers(GetDC((HWND)fHWND));
- glClearColor(0,0,0,0);
- glClearStencil(0);
- glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
}
IDirect3DDevice9* create_d3d9_device(HWND hwnd) {
diff --git a/vs/SampleApp/SampleApp.vcxproj b/vs/SampleApp/SampleApp.vcxproj
index 8c6fe83ff9..8de34d018f 100644
--- a/vs/SampleApp/SampleApp.vcxproj
+++ b/vs/SampleApp/SampleApp.vcxproj
@@ -133,6 +133,7 @@
<ClInclude Include="..\..\gpu\include\GrRefCnt.h" />
<ClInclude Include="..\..\gpu\include\GrSamplerState.h" />
<ClInclude Include="..\..\gpu\include\GrScalar.h" />
+ <ClInclude Include="..\..\gpu\include\GrStencil.h" />
<ClInclude Include="..\..\gpu\include\GrStopwatch.h" />
<ClInclude Include="..\..\gpu\include\GrStringBuilder.h" />
<ClInclude Include="..\..\gpu\include\GrTArray.h" />
@@ -233,6 +234,7 @@
<ClCompile Include="..\..\gpu\src\GrPath.cpp" />
<ClCompile Include="..\..\gpu\src\GrPathRenderer.cpp" />
<ClCompile Include="..\..\gpu\src\GrRectanizer.cpp" />
+ <ClCompile Include="..\..\gpu\src\GrStencil.cpp" />
<ClCompile Include="..\..\gpu\src\GrTextContext.cpp" />
<ClCompile Include="..\..\gpu\src\GrTextStrike.cpp" />
<ClCompile Include="..\..\gpu\src\GrTextureCache.cpp" />
@@ -246,6 +248,7 @@
<ClCompile Include="..\..\samplecode\SampleBlur.cpp" />
<ClCompile Include="..\..\samplecode\SampleCamera.cpp" />
<ClCompile Include="..\..\samplecode\SampleCircle.cpp" />
+ <ClCompile Include="..\..\samplecode\SampleComplexClip.cpp" />
<ClCompile Include="..\..\samplecode\SampleCull.cpp" />
<ClCompile Include="..\..\samplecode\SampleDither.cpp" />
<ClCompile Include="..\..\samplecode\SampleDitherBitmap.cpp" />
diff --git a/xcode/gpu/gpu.xcodeproj/project.pbxproj b/xcode/gpu/gpu.xcodeproj/project.pbxproj
index 58a6358394..e891f26d3f 100644
--- a/xcode/gpu/gpu.xcodeproj/project.pbxproj
+++ b/xcode/gpu/gpu.xcodeproj/project.pbxproj
@@ -90,6 +90,8 @@
00216E5E130F0B03009A2160 /* GrGLIRect.h in Headers */ = {isa = PBXBuildFile; fileRef = 00216E5D130F0B03009A2160 /* GrGLIRect.h */; };
D539049B12EA01E30025F3D6 /* GrContext_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = D539049A12EA01E30025F3D6 /* GrContext_impl.h */; };
D53904A112EA026E0025F3D6 /* GrPaint.h in Headers */ = {isa = PBXBuildFile; fileRef = D53904A012EA026E0025F3D6 /* GrPaint.h */; };
+ D542EAAD131C87E90065FC9D /* GrStencil.h in Headers */ = {isa = PBXBuildFile; fileRef = D542EAAC131C87E90065FC9D /* GrStencil.h */; };
+ D5558AE3131EB9BB00C71009 /* GrStencil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5558AE2131EB9BB00C71009 /* GrStencil.cpp */; };
D58CAF9A12E7212100CB9277 /* GrGLUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D58CAF9812E7212100CB9277 /* GrGLUtil.cpp */; };
D5ED886F1313F92C00B98D64 /* GrRedBlackTree.h in Headers */ = {isa = PBXBuildFile; fileRef = D5ED886E1313F92C00B98D64 /* GrRedBlackTree.h */; };
D5ED88EB13144FD600B98D64 /* GrPathRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5ED88E913144FD600B98D64 /* GrPathRenderer.cpp */; };
@@ -184,6 +186,8 @@
D2AAC046055464E500DB518D /* libgpu.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libgpu.a; sourceTree = BUILT_PRODUCTS_DIR; };
D539049A12EA01E30025F3D6 /* GrContext_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GrContext_impl.h; path = ../../gpu/include/GrContext_impl.h; sourceTree = SOURCE_ROOT; };
D53904A012EA026E0025F3D6 /* GrPaint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GrPaint.h; path = ../../gpu/include/GrPaint.h; sourceTree = SOURCE_ROOT; };
+ D542EAAC131C87E90065FC9D /* GrStencil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GrStencil.h; path = ../../gpu/include/GrStencil.h; sourceTree = SOURCE_ROOT; };
+ D5558AE2131EB9BB00C71009 /* GrStencil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GrStencil.cpp; path = ../../gpu/src/GrStencil.cpp; sourceTree = SOURCE_ROOT; };
D58CAF9812E7212100CB9277 /* GrGLUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GrGLUtil.cpp; path = ../../gpu/src/GrGLUtil.cpp; sourceTree = SOURCE_ROOT; };
D5ED886E1313F92C00B98D64 /* GrRedBlackTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GrRedBlackTree.h; path = ../../gpu/src/GrRedBlackTree.h; sourceTree = SOURCE_ROOT; };
D5ED88E913144FD600B98D64 /* GrPathRenderer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GrPathRenderer.cpp; path = ../../gpu/src/GrPathRenderer.cpp; sourceTree = SOURCE_ROOT; };
@@ -263,6 +267,7 @@
00115E6D12C116CA008296FE /* GrUserConfig.h */,
00115E6E12C116CA008296FE /* GrVertexBuffer.h */,
D53904A012EA026E0025F3D6 /* GrPaint.h */,
+ D542EAAC131C87E90065FC9D /* GrStencil.h */,
);
name = include;
sourceTree = "<group>";
@@ -281,8 +286,6 @@
08FB7795FE84155DC02AAC07 /* Source */ = {
isa = PBXGroup;
children = (
- D5ED88E913144FD600B98D64 /* GrPathRenderer.cpp */,
- D5ED88EA13144FD600B98D64 /* GrPathRenderer.h */,
D5ED886E1313F92C00B98D64 /* GrRedBlackTree.h */,
D539049A12EA01E30025F3D6 /* GrContext_impl.h */,
00115DD812C1167A008296FE /* gr_unittests.cpp */,
@@ -308,8 +311,11 @@
00115DEE12C1167A008296FE /* GrMatrix.cpp */,
00115DEF12C1167A008296FE /* GrMemory.cpp */,
00115DF012C1167A008296FE /* GrPath.cpp */,
+ D5ED88EA13144FD600B98D64 /* GrPathRenderer.h */,
+ D5ED88E913144FD600B98D64 /* GrPathRenderer.cpp */,
00115DF412C1167A008296FE /* GrRectanizer_fifo.cpp */,
00115DF512C1167A008296FE /* GrRectanizer.cpp */,
+ D5558AE2131EB9BB00C71009 /* GrStencil.cpp */,
00115DF612C1167A008296FE /* GrTextContext.cpp */,
00115DF712C1167A008296FE /* GrTextStrike_impl.h */,
00115DF812C1167A008296FE /* GrTextStrike.cpp */,
@@ -405,6 +411,7 @@
00216E5E130F0B03009A2160 /* GrGLIRect.h in Headers */,
D5ED886F1313F92C00B98D64 /* GrRedBlackTree.h in Headers */,
D5ED88EC13144FD600B98D64 /* GrPathRenderer.h in Headers */,
+ D542EAAD131C87E90065FC9D /* GrStencil.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -483,6 +490,7 @@
D58CAF9A12E7212100CB9277 /* GrGLUtil.cpp in Sources */,
D5FAF22313072C27001550A4 /* GrBufferAllocPool.cpp in Sources */,
D5ED88EB13144FD600B98D64 /* GrPathRenderer.cpp in Sources */,
+ D5558AE3131EB9BB00C71009 /* GrStencil.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj b/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj
index 1a39d9add5..28205469ba 100644
--- a/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj
+++ b/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj
@@ -30,7 +30,6 @@
0021F3A21120B29C0062682F /* SkStaticTextView.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0021F3A11120B29C0062682F /* SkStaticTextView.cpp */; };
0021F3D31120B61F0062682F /* SampleUnitMapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00995E1510A079D80054AD6D /* SampleUnitMapper.cpp */; };
00244D1B10642BBA00B8F4D8 /* SampleStrokePath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0009E21F1057E96800B0DE6F /* SampleStrokePath.cpp */; };
- 00244D97106A539500B8F4D8 /* SampleXfermodes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A7CB20F01658C00A2D6EE /* SampleXfermodes.cpp */; };
00244DE2106A681600B8F4D8 /* SampleShaders.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A7CA80F01658C00A2D6EE /* SampleShaders.cpp */; };
00281C751083CF7E00BCCB06 /* libAnimator.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00281C711083CF6600BCCB06 /* libAnimator.a */; };
00281D071084ED1200BCCB06 /* SkXMLParser_expat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00281D061084ED1200BCCB06 /* SkXMLParser_expat.cpp */; };
@@ -81,7 +80,6 @@
009F9D1A12C3EB2600C7FD4A /* SampleGM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 27C4625412BFB2F300DBB1F6 /* SampleGM.cpp */; };
00A728270FD43D0400D5051F /* SampleMovie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2762F6760FCCCB01002BD8B4 /* SampleMovie.cpp */; };
00A7282F0FD43D3700D5051F /* SkMovie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00A7282D0FD43D3700D5051F /* SkMovie.cpp */; };
- 00A7295D0FD8397600D5051F /* SampleAll.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2762F6740FCCCB01002BD8B4 /* SampleAll.cpp */; };
00AF787E0FE94433007F9650 /* SamplePath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00003C640EFC22A8000FF73A /* SamplePath.cpp */; };
00AF9B18103CD5EB00CBBCB3 /* SampleDitherBitmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00AF9B17103CD5EB00CBBCB3 /* SampleDitherBitmap.cpp */; };
00BB289B104781D00057BF7E /* SampleForth.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00BB289A104781D00057BF7E /* SampleForth.cpp */; };
@@ -148,10 +146,13 @@
8D0C4E8D0486CD37000505A6 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0867D6AAFE840B52C02AAC07 /* InfoPlist.strings */; };
8D0C4E8E0486CD37000505A6 /* main.nib in Resources */ = {isa = PBXBuildFile; fileRef = 02345980000FD03B11CA0E72 /* main.nib */; };
8D0C4E920486CD37000505A6 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 20286C33FDCF999611CA2CEA /* Carbon.framework */; };
+ D5558AEF131EBA1E00C71009 /* SampleComplexClip.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5558AEE131EBA1E00C71009 /* SampleComplexClip.cpp */; };
+ D5558B29131EBC9C00C71009 /* SampleFillType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0041CE270F00A12400695E8C /* SampleFillType.cpp */; };
D55BEE6712EF44B90055D6FD /* shadertext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D55BEE6612EF44B90055D6FD /* shadertext.cpp */; };
D5962B3A12EDFC7600B478DF /* SampleShaderText.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5962B3812EDFC7600B478DF /* SampleShaderText.cpp */; };
D5A682D712E9CE8500CDDDC6 /* SamplePatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0041CE340F00A12400695E8C /* SamplePatch.cpp */; };
- D5F4A21F12E9D75300DE986A /* SampleFillType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0041CE270F00A12400695E8C /* SampleFillType.cpp */; };
+ D5BF1C5A131D449500C4B94B /* SampleAll.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2762F6740FCCCB01002BD8B4 /* SampleAll.cpp */; };
+ D5BF1CFD131D500A00C4B94B /* SampleXfermodes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A7CB20F01658C00A2D6EE /* SampleXfermodes.cpp */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -414,6 +415,7 @@
4A9504CAFFE6A41611CA0CBA /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = /System/Library/Frameworks/CoreServices.framework; sourceTree = "<absolute>"; };
8D0C4E960486CD37000505A6 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
8D0C4E970486CD37000505A6 /* CICarbonSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CICarbonSample.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ D5558AEE131EBA1E00C71009 /* SampleComplexClip.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleComplexClip.cpp; path = ../../samplecode/SampleComplexClip.cpp; sourceTree = SOURCE_ROOT; };
D55BEE6612EF44B90055D6FD /* shadertext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = shadertext.cpp; path = ../../gm/shadertext.cpp; sourceTree = SOURCE_ROOT; };
D5962B3812EDFC7600B478DF /* SampleShaderText.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleShaderText.cpp; path = ../../samplecode/SampleShaderText.cpp; sourceTree = SOURCE_ROOT; };
/* End PBXFileReference section */
@@ -442,6 +444,7 @@
00003C610EFC2287000FF73A /* samples */ = {
isa = PBXGroup;
children = (
+ D5558AEE131EBA1E00C71009 /* SampleComplexClip.cpp */,
27C4625412BFB2F300DBB1F6 /* SampleGM.cpp */,
003EE651111239D5001AB759 /* SampleWarp.cpp */,
27A34E8B119892DD00860515 /* SampleTextBox.cpp */,
@@ -937,7 +940,6 @@
2762F66E0FCCCABE002BD8B4 /* SkPageFlipper.cpp in Sources */,
00A728270FD43D0400D5051F /* SampleMovie.cpp in Sources */,
00A7282F0FD43D3700D5051F /* SkMovie.cpp in Sources */,
- 00A7295D0FD8397600D5051F /* SampleAll.cpp in Sources */,
00AF787E0FE94433007F9650 /* SamplePath.cpp in Sources */,
005E92DC0FF08507008965B9 /* SampleFilter2.cpp in Sources */,
27005D16100903C100E275B6 /* SampleLines.cpp in Sources */,
@@ -950,7 +952,6 @@
00EB4593104DBB18002B413E /* ForthTests.cpp in Sources */,
009887F1106142FC0020D19B /* SampleNinePatch.cpp in Sources */,
00244D1B10642BBA00B8F4D8 /* SampleStrokePath.cpp in Sources */,
- 00244D97106A539500B8F4D8 /* SampleXfermodes.cpp in Sources */,
00244DE2106A681600B8F4D8 /* SampleShaders.cpp in Sources */,
00281D071084ED1200BCCB06 /* SkXMLParser_expat.cpp in Sources */,
009230D8109F111F00AD3F12 /* OverView.cpp in Sources */,
@@ -1027,9 +1028,12 @@
009F9D0A12C3E8AF00C7FD4A /* SampleFilter.cpp in Sources */,
009F9D1A12C3EB2600C7FD4A /* SampleGM.cpp in Sources */,
D5A682D712E9CE8500CDDDC6 /* SamplePatch.cpp in Sources */,
- D5F4A21F12E9D75300DE986A /* SampleFillType.cpp in Sources */,
D5962B3A12EDFC7600B478DF /* SampleShaderText.cpp in Sources */,
D55BEE6712EF44B90055D6FD /* shadertext.cpp in Sources */,
+ D5BF1C5A131D449500C4B94B /* SampleAll.cpp in Sources */,
+ D5BF1CFD131D500A00C4B94B /* SampleXfermodes.cpp in Sources */,
+ D5558AEF131EBA1E00C71009 /* SampleComplexClip.cpp in Sources */,
+ D5558B29131EBC9C00C71009 /* SampleFillType.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};