From 205d46067ace24bd4b111cf44efc96caff6c4d30 Mon Sep 17 00:00:00 2001 From: "bsalomon@google.com" Date: Mon, 25 Apr 2011 12:43:45 +0000 Subject: [PATCH] Move alpha-ramp AA to GrContext, detect cases when AA is applied via other methods (smooth lines, MSAA) or rect falls on integer coords and skip the alpha ramp path. Use pre-fab index buffer for alpha-ramped fill rects and stroke rects. Review URL: http://codereview.appspot.com/4449047/ git-svn-id: http://skia.googlecode.com/svn/trunk@1169 2bbb7eff-a529-9590-31e7-b0007b416f81 --- gpu/include/GrContext.h | 18 +++ gpu/include/GrGpu.h | 7 + gpu/include/GrMatrix.h | 24 ++- gpu/include/GrPoint.h | 8 + gpu/include/GrRect.h | 24 +++ gpu/include/GrScalar.h | 12 +- gpu/src/GrContext.cpp | 296 +++++++++++++++++++++++++++++++++-- gpu/src/GrGpuGL.cpp | 2 + gpu/src/GrMatrix.cpp | 20 +++ gyp/skia.gyp | 1 + samplecode/SampleAARects.cpp | 197 +++++++++++++++++++++++ src/gpu/SkGpuDevice.cpp | 199 ++++------------------- 12 files changed, 625 insertions(+), 183 deletions(-) create mode 100644 samplecode/SampleAARects.cpp diff --git a/gpu/include/GrContext.h b/gpu/include/GrContext.h index 5eda2b718..07d76f88e 100644 --- a/gpu/include/GrContext.h +++ b/gpu/include/GrContext.h @@ -568,8 +568,26 @@ private: GrIndexBufferAllocPool* fDrawBufferIBAllocPool; GrInOrderDrawBuffer* fDrawBuffer; + GrIndexBuffer* fAAFillRectIndexBuffer; + GrIndexBuffer* fAAStrokeRectIndexBuffer; + GrContext(GrGpu* gpu); + void fillAARect(GrDrawTarget* target, + const GrPaint& paint, + const GrRect& devRect); + + void strokeAARect(GrDrawTarget* target, + const GrPaint& paint, + const GrRect& devRect, + const GrVec& devStrokeSize); + + inline int aaFillRectIndexCount() const; + GrIndexBuffer* aaFillRectIndexBuffer(); + + inline int aaStrokeRectIndexCount() const; + GrIndexBuffer* aaStrokeRectIndexBuffer(); + void setupDrawBuffer(); void flushDrawBuffer(); diff --git a/gpu/include/GrGpu.h b/gpu/include/GrGpu.h index 7dd9959af..5bbe85d87 100644 --- a/gpu/include/GrGpu.h +++ b/gpu/include/GrGpu.h @@ -272,6 +272,12 @@ public: */ bool supportsBufferLocking() const { return fBufferLockSupport; } + /** + * Does the 3D API support anti-aliased lines. If so then line primitive + * types will use this functionality when the AA state flag is set. + */ + bool supportsAALines() const { return fAALineSupport; } + /** * Gets the minimum width of a render target. If a texture/rt is created * with a width less than this size the GrGpu object will clamp it to this @@ -445,6 +451,7 @@ protected: bool fNPOTRenderTargetSupport; bool fTwoSidedStencilSupport; bool fStencilWrapOpsSupport; + bool fAALineSupport; // set by subclass to true if index and vertex buffers can be locked, false // otherwise. diff --git a/gpu/include/GrMatrix.h b/gpu/include/GrMatrix.h index 9a2e66001..1ebc0b4e2 100644 --- a/gpu/include/GrMatrix.h +++ b/gpu/include/GrMatrix.h @@ -135,7 +135,7 @@ public: GrScalar scaleY, GrScalar transY, GrScalar persp0, - GrScalar persp1, + GrScalar persp1, GrScalar persp2) { fM[kScaleX] = scaleX; fM[kSkewX] = skewX; @@ -253,6 +253,21 @@ public: start = (GrPoint*)((intptr_t)start + stride); } } + + /** + * Transforms a vector by the matrix. Doesn't handle cases when a + * homogeneous vector maps to a point (i.e. perspective transform). + * In this case the desired answer is dependent on where the tail of + * the vector is in space. + */ + void mapVec(GrVec* vec) { + GrAssert(!this->hasPerspective()); + if (!this->isIdentity()) { + GrScalar x = vec->fX; + vec->fX = (*this)[kScaleX] * x + (*this)[kSkewX] * vec->fY; + vec->fY = (*this)[kSkewY ] * x + (*this)[kScaleY] * vec->fY; + } + } /** * Transform the 4 corners of the src rect, and return the bounding rect @@ -278,7 +293,12 @@ public: * @return true if matrix is idenity */ bool isIdentity() const; - + + /** + * Do axis-aligned lines stay axis aligned? May do 90 degree rotation / mirroring. + */ + bool preservesAxisAlignment() const; + /** * Calculates the maximum stretching factor of the matrix. Only defined if * the matrix does not have perspective. diff --git a/gpu/include/GrPoint.h b/gpu/include/GrPoint.h index c07543bb3..8c540f0b9 100644 --- a/gpu/include/GrPoint.h +++ b/gpu/include/GrPoint.h @@ -153,6 +153,14 @@ public: fX = x; fY = y; } + + /** + * set this to (abs(v.x), abs(v.y)) + */ + void setAbs(const GrVec& v) { + fX = GrScalarAbs(v.fX); + fY = GrScalarAbs(v.fY); + } /** * set vector to point from a to b. diff --git a/gpu/include/GrRect.h b/gpu/include/GrRect.h index 67e366c3b..a9ff6ec14 100644 --- a/gpu/include/GrRect.h +++ b/gpu/include/GrRect.h @@ -205,6 +205,14 @@ struct GrRect { return (fLeft > fRight) || (fTop > fBottom); } + /** + * Returns true if the rects edges are integer-aligned. + */ + bool isIRect() const { + return GrScalarIsInt(fLeft) && GrScalarIsInt(fTop) && + GrScalarIsInt(fRight) && GrScalarIsInt(fBottom); + } + /** * Does this rect contain a point. */ @@ -363,6 +371,22 @@ struct GrRect { return pts + 4; } + /** + * Swaps (left and right) and/or (top and bottom) if they are inverted + */ + void sort() { + if (fLeft > fRight) { + GrScalar temp = fLeft; + fLeft = fRight; + fRight = temp; + } + if (fTop > fBottom) { + GrScalar temp = fTop; + fTop = fBottom; + fBottom = temp; + } + } + bool operator ==(const GrRect& r) const { return fLeft == r.fLeft && fTop == r.fTop && diff --git a/gpu/include/GrScalar.h b/gpu/include/GrScalar.h index 1353fb214..7aaa43d33 100644 --- a/gpu/include/GrScalar.h +++ b/gpu/include/GrScalar.h @@ -56,11 +56,19 @@ */ #define GrFloatToFixed(x) ((GrFixed)((x) * GR_Fixed1)) -inline GrFixed GrFixedAbs(GrFixed x) { +static inline GrFixed GrFixedAbs(GrFixed x) { int32_t s = (x & 0x80000000) >> 31; return (GrFixed)(((int32_t)x ^ s) - s); } +static inline bool GrFixedIsInt(GrFixed x) { + return 0 == (x & 0xffff); +} + +static inline bool GrFloatIsInt(float x) { + return x == (float)(int)x; +} + /////////////////////////////////////////////////////////////////////////////// #if GR_SCALAR_IS_FIXED @@ -72,6 +80,7 @@ inline GrFixed GrFixedAbs(GrFixed x) { #define GrScalarHalf(x) ((x) >> 1) #define GrScalarAve(x,y) (((x)+(y)) >> 1) #define GrScalarAbs(x) GrFixedAbs(x) + #define GrScalarIsInt GrFixedIsInt #define GR_Scalar1 GR_Fixed1 #define GR_ScalarHalf GR_FixedHalf #define GR_ScalarMax GR_FixedMax @@ -85,6 +94,7 @@ inline GrFixed GrFixedAbs(GrFixed x) { #define GrScalarHalf(x) ((x) * 0.5f) #define GrScalarAbs(x) fabsf(x) #define GrScalarAve(x,y) (((x) + (y)) * 0.5f) + #define GrScalarIsInt GrFloatIsInt #define GR_Scalar1 1.f #define GR_ScalarHalf 0.5f #define GR_ScalarMax (FLT_MAX) diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp index f68564a2b..7cf0cca7c 100644 --- a/gpu/src/GrContext.cpp +++ b/gpu/src/GrContext.cpp @@ -58,29 +58,38 @@ GrContext* GrContext::CreateGLShaderContext() { GrContext::~GrContext() { this->flush(); - fGpu->unref(); delete fTextureCache; delete fFontCache; delete fDrawBuffer; delete fDrawBufferVBAllocPool; delete fDrawBufferIBAllocPool; GrSafeUnref(fCustomPathRenderer); + GrSafeUnref(fAAFillRectIndexBuffer); + GrSafeUnref(fAAStrokeRectIndexBuffer); + fGpu->unref(); } void GrContext::contextLost() { + // abandon first to so destructors + // don't try to free the resources in the API. + fGpu->abandonResources(); + delete fDrawBuffer; fDrawBuffer = NULL; + delete fDrawBufferVBAllocPool; fDrawBufferVBAllocPool = NULL; + delete fDrawBufferIBAllocPool; fDrawBufferIBAllocPool = NULL; + GrSafeSetNull(fAAFillRectIndexBuffer); + GrSafeSetNull(fAAStrokeRectIndexBuffer); + fTextureCache->removeAll(); fFontCache->freeAll(); fGpu->markContextDirty(); - fGpu->abandonResources(); - this->setupDrawBuffer(); } @@ -348,14 +357,17 @@ void GrContext::drawPaint(const GrPaint& paint) { this->drawRect(paint, r); } +//////////////////////////////////////////////////////////////////////////////// + /* create a triangle strip that strokes the specified triangle. There are 8 unique vertices, but we repreat the last 2 to close up. Alternatively we could use an indices array, and then only send 8 verts, but not sure that would be faster. */ -static void setStrokeRectStrip(GrPoint verts[10], const GrRect& rect, +static void setStrokeRectStrip(GrPoint verts[10], GrRect rect, GrScalar width) { const GrScalar rad = GrScalarHalf(width); + rect.sort(); verts[0].set(rect.fLeft + rad, rect.fTop + rad); verts[1].set(rect.fLeft - rad, rect.fTop - rad); @@ -369,6 +381,235 @@ static void setStrokeRectStrip(GrPoint verts[10], const GrRect& rect, verts[9] = verts[1]; } +static GrColor getColorForMesh(const GrPaint& paint) { + if (NULL == paint.getTexture()) { + return paint.fColor; + } else { + unsigned a = GrColorUnpackA(paint.fColor); + return GrColorPackRGBA(a, a, a, a); + } +} + +static void setInsetFan(GrPoint* pts, size_t stride, + const GrRect& r, GrScalar dx, GrScalar dy) { + pts->setRectFan(r.fLeft + dx, r.fTop + dy, r.fRight - dx, r.fBottom - dy, stride); +} + +static const uint16_t gFillAARectIdx[] = { + 0, 1, 5, 5, 4, 0, + 1, 2, 6, 6, 5, 1, + 2, 3, 7, 7, 6, 2, + 3, 0, 4, 4, 7, 3, + 4, 5, 6, 6, 7, 4, +}; + +int GrContext::aaFillRectIndexCount() const { + return GR_ARRAY_COUNT(gFillAARectIdx); +} + +GrIndexBuffer* GrContext::aaFillRectIndexBuffer() { + if (NULL == fAAFillRectIndexBuffer) { + fAAFillRectIndexBuffer = fGpu->createIndexBuffer(sizeof(gFillAARectIdx), + false); + GrAssert(NULL != fAAFillRectIndexBuffer); +#if GR_DEBUG + bool updated = +#endif + fAAFillRectIndexBuffer->updateData(gFillAARectIdx, + sizeof(gFillAARectIdx)); + GR_DEBUGASSERT(updated); + } + return fAAFillRectIndexBuffer; +} + +static const uint16_t gStrokeAARectIdx[] = { + 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0, + 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0, + 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0, + 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0, + + 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4, + 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4, + 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4, + 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4, + + 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8, + 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8, + 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8, + 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8, +}; + +int GrContext::aaStrokeRectIndexCount() const { + return GR_ARRAY_COUNT(gStrokeAARectIdx); +} + +GrIndexBuffer* GrContext::aaStrokeRectIndexBuffer() { + if (NULL == fAAStrokeRectIndexBuffer) { + fAAStrokeRectIndexBuffer = fGpu->createIndexBuffer(sizeof(gStrokeAARectIdx), + false); + GrAssert(NULL != fAAStrokeRectIndexBuffer); +#if GR_DEBUG + bool updated = +#endif + fAAStrokeRectIndexBuffer->updateData(gStrokeAARectIdx, + sizeof(gStrokeAARectIdx)); + GR_DEBUGASSERT(updated); + } + return fAAStrokeRectIndexBuffer; +} + +void GrContext::fillAARect(GrDrawTarget* target, + const GrPaint& paint, + const GrRect& devRect) { + + GrVertexLayout layout = GrDrawTarget::kColor_VertexLayoutBit; + if (NULL != paint.getTexture()) { + layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0); + } + + size_t vsize = GrDrawTarget::VertexSize(layout); + + GrDrawTarget::AutoReleaseGeometry geo(target, layout, 8, 0); + + intptr_t verts = reinterpret_cast(geo.vertices()); + + GrPoint* fan0Pos = reinterpret_cast(verts); + GrPoint* fan1Pos = reinterpret_cast(verts + 4 * vsize); + + setInsetFan(fan0Pos, vsize, devRect, -GR_ScalarHalf, -GR_ScalarHalf); + setInsetFan(fan1Pos, vsize, devRect, GR_ScalarHalf, GR_ScalarHalf); + + verts += sizeof(GrPoint); + for (int i = 0; i < 4; ++i) { + *reinterpret_cast(verts + i * vsize) = 0; + } + + GrColor innerColor = getColorForMesh(paint); + verts += 4 * vsize; + for (int i = 0; i < 4; ++i) { + *reinterpret_cast(verts + i * vsize) = innerColor; + } + + target->setIndexSourceToBuffer(this->aaFillRectIndexBuffer()); + + target->drawIndexed(kTriangles_PrimitiveType, 0, + 0, 8, this->aaFillRectIndexCount()); +} + +void GrContext::strokeAARect(GrDrawTarget* target, const GrPaint& paint, + const GrRect& devRect, const GrVec& devStrokeSize) { + const GrScalar& dx = devStrokeSize.fX; + const GrScalar& dy = devStrokeSize.fY; + const GrScalar rx = GrMul(dx, GR_ScalarHalf); + const GrScalar ry = GrMul(dy, GR_ScalarHalf); + + GrVertexLayout layout = GrDrawTarget::kColor_VertexLayoutBit; + + if (NULL != paint.getTexture()) { + layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0); + } + + GrScalar spare; + { + GrScalar w = devRect.width() - dx; + GrScalar h = devRect.height() - dy; + spare = GrMin(w, h); + } + + if (spare <= 0) { + GrRect r(devRect); + r.inset(-rx, -ry); + fillAARect(target, paint, r); + return; + } + + size_t vsize = GrDrawTarget::VertexSize(layout); + + GrDrawTarget::AutoReleaseGeometry geo(target, layout, 16, 0); + + intptr_t verts = reinterpret_cast(geo.vertices()); + + GrPoint* fan0Pos = reinterpret_cast(verts); + GrPoint* fan1Pos = reinterpret_cast(verts + 4 * vsize); + GrPoint* fan2Pos = reinterpret_cast(verts + 8 * vsize); + GrPoint* fan3Pos = reinterpret_cast(verts + 12 * vsize); + + setInsetFan(fan0Pos, vsize, devRect, -rx - GR_ScalarHalf, -ry - GR_ScalarHalf); + setInsetFan(fan1Pos, vsize, devRect, -rx + GR_ScalarHalf, -ry + GR_ScalarHalf); + setInsetFan(fan2Pos, vsize, devRect, rx - GR_ScalarHalf, ry - GR_ScalarHalf); + setInsetFan(fan3Pos, vsize, devRect, rx + GR_ScalarHalf, ry + GR_ScalarHalf); + + verts += sizeof(GrPoint); + for (int i = 0; i < 4; ++i) { + *reinterpret_cast(verts + i * vsize) = 0; + } + + GrColor innerColor = getColorForMesh(paint); + verts += 4 * vsize; + for (int i = 0; i < 8; ++i) { + *reinterpret_cast(verts + i * vsize) = innerColor; + } + + verts += 8 * vsize; + for (int i = 0; i < 8; ++i) { + *reinterpret_cast(verts + i * vsize) = 0; + } + + target->setIndexSourceToBuffer(aaStrokeRectIndexBuffer()); + target->drawIndexed(kTriangles_PrimitiveType, + 0, 0, 16, aaStrokeRectIndexCount()); +} + +static bool apply_aa_to_rect(GrDrawTarget* target, + GrGpu* gpu, + const GrPaint& paint, + const GrRect& rect, + GrScalar width, + const GrMatrix* matrix, + GrMatrix* combinedMatrix, + GrRect* devRect) { + // we use a simple alpha ramp to do aa on axis-aligned rects + // do AA with alpha ramp if the caller requested AA, the rect + // will be axis-aligned,the render target is not + // multisampled, and the rect won't land on integer coords. + + if (!paint.fAntiAlias) { + return false; + } + + if (target->getRenderTarget()->isMultisampled()) { + return false; + } + + if (0 == width && gpu->supportsAALines()) { + return false; + } + + if (!target->getViewMatrix().preservesAxisAlignment()) { + return false; + } + + if (NULL != matrix && + !matrix->preservesAxisAlignment()) { + return false; + } + + *combinedMatrix = target->getViewMatrix(); + if (NULL != matrix) { + combinedMatrix->preConcat(*matrix); + GrAssert(combinedMatrix->preservesAxisAlignment()); + } + + combinedMatrix->mapRect(devRect, rect); + devRect->sort(); + + if (width < 0) { + return !devRect->isIRect(); + } else { + return true; + } +} + void GrContext::drawRect(const GrPaint& paint, const GrRect& rect, GrScalar width, @@ -378,13 +619,43 @@ void GrContext::drawRect(const GrPaint& paint, GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory); + GrRect devRect = rect; + GrMatrix combinedMatrix; + bool doAA = apply_aa_to_rect(target, fGpu, paint, rect, width, matrix, + &combinedMatrix, &devRect); + + if (doAA) { + GrDrawTarget::AutoViewMatrixRestore avm(target); + if (textured) { + GrMatrix inv; + if (combinedMatrix.invert(&inv)) { + target->preConcatSamplerMatrix(0, inv); + } + } + target->setViewMatrix(GrMatrix::I()); + if (width >= 0) { + GrVec strokeSize;; + if (width > 0) { + strokeSize.set(width, width); + combinedMatrix.mapVec(&strokeSize); + strokeSize.setAbs(strokeSize); + } else { + strokeSize.set(GR_Scalar1, GR_Scalar1); + } + strokeAARect(target, paint, devRect, strokeSize); + } else { + fillAARect(target, paint, devRect); + } + return; + } + if (width >= 0) { // TODO: consider making static vertex buffers for these cases. // Hairline could be done by just adding closing vertex to // unitSquareVertexBuffer() - GrVertexLayout layout = (textured) ? - GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0) : - 0; + GrVertexLayout layout = textured ? + GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0) : + 0; static const int worstCaseVertCount = 10; GrDrawTarget::AutoReleaseGeometry geo(target, layout, worstCaseVertCount, 0); @@ -415,7 +686,9 @@ void GrContext::drawRect(const GrPaint& paint, if (NULL != matrix) { avmr.set(target); target->preConcatViewMatrix(*matrix); - target->preConcatSamplerMatrix(0, *matrix); + if (textured) { + target->preConcatSamplerMatrix(0, *matrix); + } } target->drawNonIndexed(primType, 0, vertCount); @@ -429,8 +702,8 @@ void GrContext::drawRect(const GrPaint& paint, GrDrawTarget::AutoViewMatrixRestore avmr(target); GrMatrix m; m.setAll(rect.width(), 0, rect.fLeft, - 0, rect.height(), rect.fTop, - 0, 0, GrMatrix::I()[8]); + 0, rect.height(), rect.fTop, + 0, 0, GrMatrix::I()[8]); if (NULL != matrix) { m.postConcat(*matrix); @@ -819,6 +1092,9 @@ GrContext::GrContext(GrGpu* gpu) : fDrawBufferVBAllocPool = NULL; fDrawBufferIBAllocPool = NULL; + fAAFillRectIndexBuffer = NULL; + fAAStrokeRectIndexBuffer = NULL; + this->setupDrawBuffer(); } diff --git a/gpu/src/GrGpuGL.cpp b/gpu/src/GrGpuGL.cpp index 64431a35f..f2ec450a0 100644 --- a/gpu/src/GrGpuGL.cpp +++ b/gpu/src/GrGpuGL.cpp @@ -361,6 +361,8 @@ GrGpuGL::GrGpuGL() { } } + fAALineSupport = GR_GL_SUPPORT_DESKTOP; + //////////////////////////////////////////////////////////////////////////// // Experiments to determine limitations that can't be queried. TODO: Make // these a preprocess that generate some compile time constants. diff --git a/gpu/src/GrMatrix.cpp b/gpu/src/GrMatrix.cpp index 0a2d1b2b5..92a38ee1e 100644 --- a/gpu/src/GrMatrix.cpp +++ b/gpu/src/GrMatrix.cpp @@ -259,6 +259,26 @@ bool GrMatrix::isIdentity() const { } +bool GrMatrix::preservesAxisAlignment() const { + + // check if matrix is trans and scale only + static const int gAllowedMask1 = kScale_TypeBit | kTranslate_TypeBit; + + if (!(~gAllowedMask1 & fTypeMask)) { + return true; + } + + // check matrix is trans and skew only (0 scale) + static const int gAllowedMask2 = kScale_TypeBit | kSkew_TypeBit | + kTranslate_TypeBit | kZeroScale_TypeBit; + + if (!(~gAllowedMask2 & fTypeMask) && (kZeroScale_TypeBit & fTypeMask)) { + return true; + } + + return false; +} + GrScalar GrMatrix::getMaxStretch() const { if (fTypeMask & kPerspective_TypeBit) { diff --git a/gyp/skia.gyp b/gyp/skia.gyp index 322f87cc8..94ba3cf8b 100644 --- a/gyp/skia.gyp +++ b/gyp/skia.gyp @@ -1444,6 +1444,7 @@ '../samplecode/SamplePicture.cpp', '../samplecode/SamplePoints.cpp', '../samplecode/SamplePolyToPoly.cpp', + '../samplecode/SampleAARects.cpp', '../samplecode/SampleRegion.cpp', '../samplecode/SampleRepeatTile.cpp', '../samplecode/SampleShaders.cpp', diff --git a/samplecode/SampleAARects.cpp b/samplecode/SampleAARects.cpp new file mode 100644 index 000000000..94f8ce98a --- /dev/null +++ b/samplecode/SampleAARects.cpp @@ -0,0 +1,197 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkDevice.h" +#include "SkPaint.h" +#include "SkShader.h" + +static SkBitmap createBitmap(int n) { + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, n, n); + bitmap.allocPixels(); + bitmap.eraseColor(SK_ColorGREEN); + + SkCanvas canvas(bitmap); + SkRect r; + r.set(0, 0, SkIntToScalar(n), SkIntToScalar(n)); + SkPaint paint; + paint.setAntiAlias(true); + + paint.setColor(SK_ColorRED); + canvas.drawOval(r, paint); + paint.setColor(SK_ColorBLUE); + paint.setStrokeWidth(SkIntToScalar(n)/15); + paint.setStyle(SkPaint::kStroke_Style); + canvas.drawLine(0, 0, r.fRight, r.fBottom, paint); + canvas.drawLine(0, r.fBottom, r.fRight, 0, paint); + + return bitmap; +} + +class AARectView : public SkView { + SkBitmap fBitmap; + enum { + N = 64 + }; +public: + AARectView() { + fBitmap = createBitmap(N); + + fWidth = N; + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "AA Rects"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void drawBG(SkCanvas* canvas) { + canvas->drawColor(SK_ColorWHITE); + } + + virtual void onDraw(SkCanvas* canvas) { + this->drawBG(canvas); + + canvas->translate(SkIntToScalar(10), SkIntToScalar(10)); + + SkPaint bluePaint; + bluePaint.setARGB(0xff, 0x0, 0x0, 0xff); + SkPaint bmpPaint; + SkShader* bmpShader = SkShader::CreateBitmapShader(fBitmap, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); + bmpPaint.setShader(bmpShader); + bmpShader->unref(); + + bluePaint.setStrokeWidth(3); + bmpPaint.setStrokeWidth(3); + + SkPaint paints[] = { bluePaint, bmpPaint }; + + SkRect rect; + + SkScalar dx = SkIntToScalar(80); + SkScalar dy = SkIntToScalar(100); + SkMatrix matrix; + for (int p = 0; p < SK_ARRAY_COUNT(paints); ++p) { + for (int stroke = 0; stroke < 2; ++stroke) { + paints[p].setStyle(stroke ? SkPaint::kStroke_Style : SkPaint::kFill_Style); + for (int a = 0; a < 3; ++ a) { + paints[p].setAntiAlias(a > 0); + paints[p].setAlpha(a > 1 ? 0x80 : 0xff); + + canvas->save(); + rect = SkRect::MakeLTRB(SkFloatToScalar(0.f), + SkFloatToScalar(0.f), + SkFloatToScalar(40.f), + SkFloatToScalar(40.f)); + canvas->drawRect(rect, paints[p]); + canvas->translate(dx, 0); + + rect = SkRect::MakeLTRB(SkFloatToScalar(0.5f), + SkFloatToScalar(0.5f), + SkFloatToScalar(40.5f), + SkFloatToScalar(40.5f)); + canvas->drawRect(rect, paints[p]); + canvas->translate(dx, 0); + + rect = SkRect::MakeLTRB(SkFloatToScalar(0.5f), + SkFloatToScalar(0.5f), + SkFloatToScalar(40.f), + SkFloatToScalar(40.f)); + canvas->drawRect(rect, paints[p]); + canvas->translate(dx, 0); + + rect = SkRect::MakeLTRB(SkFloatToScalar(0.75f), + SkFloatToScalar(0.75f), + SkFloatToScalar(40.75f), + SkFloatToScalar(40.75f)); + canvas->drawRect(rect, paints[p]); + canvas->translate(dx, 0); + + canvas->save(); + canvas->translate(SkFloatToScalar(.33f), SkFloatToScalar(.67f)); + rect = SkRect::MakeLTRB(SkFloatToScalar(0.0f), + SkFloatToScalar(0.0f), + SkFloatToScalar(40.0f), + SkFloatToScalar(40.0f)); + canvas->drawRect(rect, paints[p]); + canvas->restore(); + canvas->translate(dx, 0); + + canvas->save(); + matrix.setRotate(SkFloatToScalar(45.f)); + canvas->concat(matrix); + canvas->translate(SkFloatToScalar(20.0f / sqrtf(2.f)), + SkFloatToScalar(20.0f / sqrtf(2.f))); + rect = SkRect::MakeLTRB(SkFloatToScalar(-20.0f), + SkFloatToScalar(-20.0f), + SkFloatToScalar(20.0f), + SkFloatToScalar(20.0f)); + canvas->drawRect(rect, paints[p]); + canvas->restore(); + canvas->translate(dx, 0); + + canvas->save(); + canvas->rotate(SkFloatToScalar(90.f)); + rect = SkRect::MakeLTRB(SkFloatToScalar(0.0f), + SkFloatToScalar(0.0f), + SkFloatToScalar(40.0f), + SkFloatToScalar(-40.0f)); + canvas->drawRect(rect, paints[p]); + canvas->restore(); + canvas->translate(dx, 0); + + canvas->save(); + canvas->rotate(SkFloatToScalar(90.f)); + rect = SkRect::MakeLTRB(SkFloatToScalar(0.5f), + SkFloatToScalar(0.5f), + SkFloatToScalar(40.5f), + SkFloatToScalar(-40.5f)); + canvas->drawRect(rect, paints[p]); + canvas->restore(); + canvas->translate(dx, 0); + + canvas->save(); + matrix.setScale(SkFloatToScalar(-1.f), SkFloatToScalar(-1.f)); + canvas->concat(matrix); + rect = SkRect::MakeLTRB(SkFloatToScalar(0.5f), + SkFloatToScalar(0.5f), + SkFloatToScalar(-40.5f), + SkFloatToScalar(-40.5f)); + canvas->drawRect(rect, paints[p]); + canvas->restore(); + canvas->translate(dx, 0); + + canvas->save(); + matrix.setScale(SkFloatToScalar(2.1f), SkFloatToScalar(4.1f)); + canvas->concat(matrix); + rect = SkRect::MakeLTRB(SkFloatToScalar(0.1f), + SkFloatToScalar(0.1f), + SkFloatToScalar(19.1f), + SkFloatToScalar(9.1f)); + canvas->drawRect(rect, paints[p]); + canvas->restore(); + canvas->translate(dx, 0); + + canvas->restore(); + canvas->translate(0, dy); + } + } + } + } + +private: + int fWidth; + + typedef SkView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new AARectView; } +static SkViewRegister reg(MyFactory); + diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index f89d4b18a..1c797c3b9 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -653,178 +653,37 @@ void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, /////////////////////////////////////////////////////////////////////////////// -static void setInsetFan(GrPoint pts[4], const GrRect& r, - GrScalar dx, GrScalar dy) { - pts->setRectFan(r.fLeft + dx, r.fTop + dy, r.fRight - dx, r.fBottom - dy); -} - -static GrColor getColorForMesh(const GrPaint& paint) { - if (NULL == paint.getTexture()) { - return paint.fColor; - } else { - unsigned a = GrColorUnpackA(paint.fColor); - return GrColorPackRGBA(a, a, a, a); - } -} - -static const uint16_t gFillAARectIdx1[] = { - 0, 1, 5, 5, 4, 0, - 1, 2, 6, 6, 5, 1, - 2, 3, 7, 7, 6, 2, - 3, 0, 4, 4, 7, 3, - 4, 5, 6, 6, 7, 4, -}; - -static void fillDevAARect(GrContext* ctx, const GrPaint& paint, - const GrRect& rect) { - if (rect.isEmpty()) { - return; - } - - GrAutoMatrix avm(ctx, GrMatrix::I()); - - GrPoint verts[8]; - GrPoint* texs = NULL; - GrColor colors[8]; - - setInsetFan(&verts[ 0], rect, -0.5f, -0.5f); - setInsetFan(&verts[ 4], rect, 0.5f, 0.5f); - - sk_memset32(&colors[ 0], 0, 4); - sk_memset32(&colors[ 4], getColorForMesh(paint), 4); - - ctx->drawVertices(paint, kTriangles_PrimitiveType, - 8, verts, texs, colors, - gFillAARectIdx1, SK_ARRAY_COUNT(gFillAARectIdx1)); -} - -static const uint16_t gStrokeAARectIdx[] = { - 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0, - 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0, - 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0, - 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0, - - 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4, - 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4, - 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4, - 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4, - - 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8, - 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8, - 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8, - 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8, -}; - -static void strokeDevAARect(GrContext* ctx, const GrPaint& paint, - const GrRect& rect, const SkPoint& strokeSize) { - const GrScalar dx = SkScalarToGrScalar(strokeSize.fX); - const GrScalar dy = SkScalarToGrScalar(strokeSize.fY); - const GrScalar rx = dx * 0.5f; - const GrScalar ry = dy * 0.5f; - - GrScalar spare; - { - GrScalar w = rect.width() - dx; - GrScalar h = rect.height() - dy; - spare = GrMin(w, h); - } - - if (spare <= 0) { - GrRect r(rect); - r.inset(-rx, -ry); - fillDevAARect(ctx, paint, r); - return; - } - - GrAutoMatrix avm(ctx, GrMatrix::I()); - - GrPoint verts[16]; - GrPoint* texs = NULL; - GrColor colors[16]; - - setInsetFan(&verts[ 0], rect, -rx - 0.5f, -ry - 0.5f); - setInsetFan(&verts[ 4], rect, -rx + 0.5f, -ry + 0.5f); - setInsetFan(&verts[ 8], rect, rx - 0.5f, ry - 0.5f); - setInsetFan(&verts[12], rect, rx + 0.5f, ry + 0.5f); - - sk_memset32(&colors[ 0], 0, 4); - sk_memset32(&colors[ 4], getColorForMesh(paint), 8); - sk_memset32(&colors[12], 0, 4); - - ctx->drawVertices(paint, kTriangles_PrimitiveType, - 16, verts, texs, colors, - gStrokeAARectIdx, SK_ARRAY_COUNT(gStrokeAARectIdx)); -} - -/* - * If the paint has a texture, preconcat the ctx's inverse, since when we - * draw verts which are already in device coordinates, we need to "undo" that - * before we run our vertex shaders, which expect the coordinates to be local. - */ -static void preConcatInverseToTextureMatrix(GrContext* ctx, GrPaint* paint) { - if (paint->getTexture()) { - GrMatrix inverse; - if (ctx->getMatrix().invert(&inverse)) { - paint->fSampler.preConcatMatrix(inverse); - } - } -} - void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect, const SkPaint& paint) { - CHECK_SHOULD_DRAW(draw); - - const SkMatrix& matrix = *draw.fMatrix; - SkPoint strokeSize; - SkDraw::RectType type = SkDraw::ComputeRectType(paint, matrix, &strokeSize); - - if (SkDraw::kPath_RectType == type) { - SkPath path; - path.addRect(rect); - this->drawPath(draw, path, paint, NULL, true); - } else { - GrPaint grPaint; - SkAutoCachedTexture act; - if (!this->skPaint2GrPaintShader(paint, &act, matrix, &grPaint)) { - return; - } - - bool doAA = paint.isAntiAlias(); - - if (SkDraw::kHair_RectType == type && doAA) { - strokeSize.set(SK_Scalar1, SK_Scalar1); - type = SkDraw::kStroke_RectType; - } - - switch (type) { - case SkDraw::kHair_RectType: - SkASSERT(!doAA); - fContext->drawRect(grPaint, Sk2Gr(rect), 0); - break; - case SkDraw::kFill_RectType: - if (doAA) { - SkRect devRect; - matrix.mapRect(&devRect, rect); - preConcatInverseToTextureMatrix(fContext, &grPaint); - fillDevAARect(fContext, grPaint, Sk2Gr(devRect)); - } else { - fContext->drawRect(grPaint, Sk2Gr(rect), -1); - } - break; - case SkDraw::kStroke_RectType: - if (doAA) { - SkRect devRect; - matrix.mapRect(&devRect, rect); - preConcatInverseToTextureMatrix(fContext, &grPaint); - strokeDevAARect(fContext, grPaint, Sk2Gr(devRect), strokeSize); - } else { - fContext->drawRect(grPaint, Sk2Gr(rect), paint.getStrokeWidth()); - } - break; - default: - SkASSERT(!"bad value for RectType"); - } - } + CHECK_SHOULD_DRAW(draw); + + bool doStroke = paint.getStyle() == SkPaint::kStroke_Style; + SkScalar width = paint.getStrokeWidth(); + + /* + We have special code for hairline strokes, miter-strokes, and fills. + Anything else we just call our path code. + */ + bool usePath = doStroke && width > 0 && + paint.getStrokeJoin() != SkPaint::kMiter_Join; + // another reason we might need to call drawPath... + if (paint.getMaskFilter()) { + usePath = true; + } + + if (usePath) { + SkPath path; + path.addRect(rect); + this->drawPath(draw, path, paint, NULL, true); + return; + } + + GrPaint grPaint; + SkAutoCachedTexture act; + if (!this->skPaint2GrPaintShader(paint, &act, *draw.fMatrix, &grPaint)) { + return; + } + fContext->drawRect(grPaint, Sk2Gr(rect), doStroke ? width : -1); } #include "SkMaskFilter.h"