diff --git a/gm/aarectmodes.cpp b/gm/aarectmodes.cpp new file mode 100644 index 0000000000..ffb267d892 --- /dev/null +++ b/gm/aarectmodes.cpp @@ -0,0 +1,157 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gm.h" +#include "SkCanvas.h" +#include "SkColorPriv.h" +#include "SkShader.h" + +static SkCanvas* create_canvas(int w, int h) { + SkBitmap bm; + bm.setConfig(SkBitmap::kARGB_8888_Config, w, h); + bm.allocPixels(); + bm.eraseColor(0); + return new SkCanvas(bm); +} + +static const SkBitmap& extract_bitmap(SkCanvas* canvas) { + return canvas->getDevice()->accessBitmap(false); +} + +static const struct { + SkXfermode::Mode fMode; + const char* fLabel; +} gModes[] = { + { SkXfermode::kClear_Mode, "Clear" }, + { SkXfermode::kSrc_Mode, "Src" }, + { SkXfermode::kDst_Mode, "Dst" }, + { SkXfermode::kSrcOver_Mode, "SrcOver" }, + { SkXfermode::kDstOver_Mode, "DstOver" }, + { SkXfermode::kSrcIn_Mode, "SrcIn" }, + { SkXfermode::kDstIn_Mode, "DstIn" }, + { SkXfermode::kSrcOut_Mode, "SrcOut" }, + { SkXfermode::kDstOut_Mode, "DstOut" }, + { SkXfermode::kSrcATop_Mode, "SrcATop" }, + { SkXfermode::kDstATop_Mode, "DstATop" }, + { SkXfermode::kXor_Mode, "Xor" }, +}; + +const int gWidth = 64; +const int gHeight = 64; +const SkScalar W = SkIntToScalar(gWidth); +const SkScalar H = SkIntToScalar(gHeight); + +static SkScalar drawCell(SkCanvas* canvas, SkXfermode* mode, + SkAlpha a0, SkAlpha a1) { + + SkPaint paint; + paint.setAntiAlias(true); + + SkRect r = SkRect::MakeWH(W, H); + r.inset(W/10, H/10); + + paint.setColor(SK_ColorBLUE); + paint.setAlpha(a0); + canvas->drawOval(r, paint); + + paint.setColor(SK_ColorRED); + paint.setAlpha(a1); + paint.setXfermode(mode); + + SkScalar offset = SK_Scalar1 / 3; + SkRect rect = SkRect::MakeXYWH(W / 4 + offset, + H / 4 + offset, + W / 2, H / 2); + canvas->drawRect(rect, paint); + + return H; +} + +static SkShader* make_bg_shader() { + SkBitmap bm; + bm.setConfig(SkBitmap::kARGB_8888_Config, 2, 2); + bm.allocPixels(); + *bm.getAddr32(0, 0) = *bm.getAddr32(1, 1) = 0xFFFFFFFF; + *bm.getAddr32(1, 0) = *bm.getAddr32(0, 1) = SkPackARGB32(0xFF, 0xCC, + 0xCC, 0xCC); + + SkShader* s = SkShader::CreateBitmapShader(bm, + SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode); + + SkMatrix m; + m.setScale(SkIntToScalar(6), SkIntToScalar(6)); + s->setLocalMatrix(m); + return s; +} + +namespace skiagm { + + class AARectModesGM : public GM { + SkPaint fBGPaint; + public: + AARectModesGM () { + fBGPaint.setShader(make_bg_shader())->unref(); + } + + protected: + + virtual SkString onShortName() { + return SkString("aarectmodes"); + } + + virtual SkISize onISize() { return make_isize(640, 480); } + + virtual void onDraw(SkCanvas* canvas) { + canvas->drawColor(SK_ColorWHITE); + + const SkRect bounds = SkRect::MakeWH(W, H); + static const SkAlpha gAlphaValue[] = { 0xFF, 0x88, 0x88 }; + + canvas->translate(SkIntToScalar(4), SkIntToScalar(4)); + + for (int alpha = 0; alpha < 4; ++alpha) { + canvas->save(); + canvas->save(); + for (size_t i = 0; i < SK_ARRAY_COUNT(gModes); ++i) { + if (6 == i) { + canvas->restore(); + canvas->translate(W * 5, 0); + canvas->save(); + } + SkXfermode* mode = SkXfermode::Create(gModes[i].fMode); + + canvas->drawRect(bounds, fBGPaint); + canvas->saveLayer(&bounds, NULL); + SkScalar dy = drawCell(canvas, mode, + gAlphaValue[alpha & 1], + gAlphaValue[alpha & 2]); + canvas->restore(); + + canvas->translate(0, dy * 5 / 4); + SkSafeUnref(mode); + } + canvas->restore(); + canvas->restore(); + canvas->translate(W * 5 / 4, 0); + } + } + + // disable pdf for now, since it crashes on mac + virtual uint32_t onGetFlags() const { return kSkipPDF_Flag; } + + private: + typedef GM INHERITED; + }; + +////////////////////////////////////////////////////////////////////////////// + + static GM* MyFactory(void*) { return new AARectModesGM; } + static GMRegistry reg(MyFactory); + +} + diff --git a/gpu/include/GrContext.h b/gpu/include/GrContext.h index eeb96ccc32..4a1f1da8c4 100644 --- a/gpu/include/GrContext.h +++ b/gpu/include/GrContext.h @@ -563,13 +563,13 @@ private: GrContext(GrGpu* gpu); void fillAARect(GrDrawTarget* target, - const GrPaint& paint, - const GrRect& devRect); + const GrRect& devRect, + bool useVertexCoverage); void strokeAARect(GrDrawTarget* target, - const GrPaint& paint, const GrRect& devRect, - const GrVec& devStrokeSize); + const GrVec& devStrokeSize, + bool useVertexCoverage); inline int aaFillRectIndexCount() const; GrIndexBuffer* aaFillRectIndexBuffer(); diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp index d9fb9e85c8..3f5ab5471b 100644 --- a/gpu/src/GrContext.cpp +++ b/gpu/src/GrContext.cpp @@ -677,6 +677,7 @@ bool GrContext::doOffscreenAA(GrDrawTarget* target, return false; } if (disable_coverage_aa_for_blend(target)) { + GrPrintf("Turning off AA to correctly apply blend.\n"); return false; } return true; @@ -937,18 +938,6 @@ static void setStrokeRectStrip(GrPoint verts[10], GrRect rect, verts[9] = verts[1]; } -static GrColor getColorForMesh(const GrPaint& paint) { - // FIXME: This was copied from SkGpuDevice, seems like - // we should have already smeared a in caller if that - // is what is desired. - if (paint.hasTexture()) { - unsigned a = GrColorUnpackA(paint.fColor); - return GrColorPackRGBA(a, a, a, a); - } else { - return paint.fColor; - } -} - 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); @@ -1019,11 +1008,26 @@ GrIndexBuffer* GrContext::aaStrokeRectIndexBuffer() { return fAAStrokeRectIndexBuffer; } +static GrVertexLayout aa_rect_layout(const GrDrawTarget* target, + bool useCoverage) { + GrVertexLayout layout = 0; + for (int s = 0; s < GrDrawTarget::kNumStages; ++s) { + if (NULL != target->getTexture(s)) { + layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s); + } + } + if (useCoverage) { + layout |= GrDrawTarget::kCoverage_VertexLayoutBit; + } else { + layout |= GrDrawTarget::kColor_VertexLayoutBit; + } + return layout; +} + void GrContext::fillAARect(GrDrawTarget* target, - const GrPaint& paint, - const GrRect& devRect) { - GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL) | - GrDrawTarget::kColor_VertexLayoutBit; + const GrRect& devRect, + bool useVertexCoverage) { + GrVertexLayout layout = aa_rect_layout(target, useVertexCoverage); size_t vsize = GrDrawTarget::VertexSize(layout); @@ -1051,7 +1055,13 @@ void GrContext::fillAARect(GrDrawTarget* target, *reinterpret_cast(verts + i * vsize) = 0; } - GrColor innerColor = getColorForMesh(paint); + GrColor innerColor; + if (useVertexCoverage) { + innerColor = GrColorPackRGBA(0,0,0,0xff); + } else { + innerColor = target->getColor(); + } + verts += 4 * vsize; for (int i = 0; i < 4; ++i) { *reinterpret_cast(verts + i * vsize) = innerColor; @@ -1063,16 +1073,15 @@ void GrContext::fillAARect(GrDrawTarget* target, 0, 8, this->aaFillRectIndexCount()); } -void GrContext::strokeAARect(GrDrawTarget* target, const GrPaint& paint, - const GrRect& devRect, const GrVec& devStrokeSize) { +void GrContext::strokeAARect(GrDrawTarget* target, + const GrRect& devRect, + const GrVec& devStrokeSize, + bool useVertexCoverage) { 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 = PaintStageVertexLayoutBits(paint, NULL) | - GrDrawTarget::kColor_VertexLayoutBit; - GrScalar spare; { GrScalar w = devRect.width() - dx; @@ -1083,10 +1092,10 @@ void GrContext::strokeAARect(GrDrawTarget* target, const GrPaint& paint, if (spare <= 0) { GrRect r(devRect); r.inset(-rx, -ry); - fillAARect(target, paint, r); + fillAARect(target, r, useVertexCoverage); return; } - + GrVertexLayout layout = aa_rect_layout(target, useVertexCoverage); size_t vsize = GrDrawTarget::VertexSize(layout); GrDrawTarget::AutoReleaseGeometry geo(target, layout, 16, 0); @@ -1117,7 +1126,12 @@ void GrContext::strokeAARect(GrDrawTarget* target, const GrPaint& paint, *reinterpret_cast(verts + i * vsize) = 0; } - GrColor innerColor = getColorForMesh(paint); + GrColor innerColor; + if (useVertexCoverage) { + innerColor = GrColorPackRGBA(0,0,0,0xff); + } else { + innerColor = target->getColor(); + } verts += 4 * vsize; for (int i = 0; i < 8; ++i) { *reinterpret_cast(verts + i * vsize) = innerColor; @@ -1146,7 +1160,8 @@ static bool apply_aa_to_rect(GrDrawTarget* target, GrScalar width, const GrMatrix* matrix, GrMatrix* combinedMatrix, - GrRect* devRect) { + GrRect* devRect, + bool* useVertexCoverage) { // 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 @@ -1156,8 +1171,22 @@ static bool apply_aa_to_rect(GrDrawTarget* target, return false; } + // we are keeping around the "tweak the alpha" trick because + // it is our only hope for the fixed-pipe implementation. + // In a shader implementation we can give a separate coverage input + *useVertexCoverage = false; if (!target->canTweakAlphaForCoverage()) { - return false; + if (target->getCaps().fSupportPerVertexCoverage) { + if (disable_coverage_aa_for_blend(target)) { + GrPrintf("Turning off AA to correctly apply blend.\n"); + return false; + } else { + *useVertexCoverage = true; + } + } else { + GrPrintf("Rect AA dropped because no support for coverage.\n"); + return false; + } } if (target->getRenderTarget()->isMultisampled()) { @@ -1204,8 +1233,9 @@ void GrContext::drawRect(const GrPaint& paint, GrRect devRect = rect; GrMatrix combinedMatrix; + bool useVertexCoverage; bool doAA = apply_aa_to_rect(target, rect, width, matrix, - &combinedMatrix, &devRect); + &combinedMatrix, &devRect, &useVertexCoverage); if (doAA) { GrDrawTarget::AutoViewMatrixRestore avm(target); @@ -1225,9 +1255,9 @@ void GrContext::drawRect(const GrPaint& paint, } else { strokeSize.set(GR_Scalar1, GR_Scalar1); } - strokeAARect(target, paint, devRect, strokeSize); + strokeAARect(target, devRect, strokeSize, useVertexCoverage); } else { - fillAARect(target, paint, devRect); + fillAARect(target, devRect, useVertexCoverage); } return; } @@ -1406,12 +1436,11 @@ void GrContext::drawVertices(const GrPaint& paint, } int texOffsets[GrDrawTarget::kMaxTexCoords]; int colorOffset; - int edgeOffset; GrDrawTarget::VertexSizeAndOffsetsByIdx(layout, texOffsets, &colorOffset, - &edgeOffset); - GrAssert(-1 == edgeOffset); + NULL, + NULL); void* curVertex = geo.vertices(); for (int i = 0; i < vertexCount; ++i) { @@ -1462,6 +1491,7 @@ void GrContext::drawPath(const GrPaint& paint, const GrPath& path, // aa. If we have some future driver-mojo path AA that can do the right // thing WRT to the blend then we'll need some query on the PR. if (disable_coverage_aa_for_blend(target)) { + GrPrintf("Turning off AA to correctly apply blend.\n"); target->disableState(GrDrawTarget::kAntialias_StateBit); } diff --git a/gpu/src/GrDrawTarget.cpp b/gpu/src/GrDrawTarget.cpp index 79b3da8482..2169fc7bdd 100644 --- a/gpu/src/GrDrawTarget.cpp +++ b/gpu/src/GrDrawTarget.cpp @@ -88,12 +88,30 @@ size_t GrDrawTarget::VertexSize(GrVertexLayout vertexLayout) { if (vertexLayout & kColor_VertexLayoutBit) { size += sizeof(GrColor); } + if (vertexLayout & kCoverage_VertexLayoutBit) { + size += sizeof(GrColor); + } if (vertexLayout & kEdge_VertexLayoutBit) { size += 4 * sizeof(GrScalar); } return size; } +//////////////////////////////////////////////////////////////////////////////// + +/** + * Functions for computing offsets of various components from the layout + * bitfield. + * + * Order of vertex components: + * Position + * Tex Coord 0 + * ... + * Tex Coord kMaxTexCoords-1 + * Color + * Coverage + */ + int GrDrawTarget::VertexStageCoordOffset(int stage, GrVertexLayout vertexLayout) { GrAssert(check_layout(vertexLayout)); if (StagePosAsTexCoordVertexLayoutBit(stage) & vertexLayout) { @@ -121,7 +139,6 @@ int GrDrawTarget::VertexStageCoordOffset(int stage, GrVertexLayout vertexLayout) int GrDrawTarget::VertexColorOffset(GrVertexLayout vertexLayout) { GrAssert(check_layout(vertexLayout)); - // color is after the pos and tex coords if (vertexLayout & kColor_VertexLayoutBit) { int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ? sizeof(GrGpuTextVertex) : @@ -131,6 +148,23 @@ int GrDrawTarget::VertexColorOffset(GrVertexLayout vertexLayout) { return -1; } +int GrDrawTarget::VertexCoverageOffset(GrVertexLayout vertexLayout) { + GrAssert(check_layout(vertexLayout)); + + if (vertexLayout & kCoverage_VertexLayoutBit) { + int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ? + sizeof(GrGpuTextVertex) : + sizeof(GrPoint); + + int offset = vecSize * (num_tex_coords(vertexLayout) + 1); + if (vertexLayout & kColor_VertexLayoutBit) { + offset += sizeof(GrColor); + } + return offset; + } + return -1; +} + int GrDrawTarget::VertexEdgeOffset(GrVertexLayout vertexLayout) { GrAssert(check_layout(vertexLayout)); @@ -143,21 +177,21 @@ int GrDrawTarget::VertexEdgeOffset(GrVertexLayout vertexLayout) { if (vertexLayout & kColor_VertexLayoutBit) { offset += sizeof(GrColor); } + if (vertexLayout & kCoverage_VertexLayoutBit) { + offset += sizeof(GrColor); + } return offset; } return -1; } int GrDrawTarget::VertexSizeAndOffsetsByIdx(GrVertexLayout vertexLayout, - int texCoordOffsetsByIdx[kMaxTexCoords], - int* colorOffset, - int* edgeOffset) { + int texCoordOffsetsByIdx[kMaxTexCoords], + int* colorOffset, + int* coverageOffset, + int* edgeOffset) { GrAssert(check_layout(vertexLayout)); - GrAssert(NULL != texCoordOffsetsByIdx); - GrAssert(NULL != colorOffset); - GrAssert(NULL != edgeOffset); - int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ? sizeof(GrGpuTextVertex) : sizeof(GrPoint); @@ -165,23 +199,45 @@ int GrDrawTarget::VertexSizeAndOffsetsByIdx(GrVertexLayout vertexLayout, for (int t = 0; t < kMaxTexCoords; ++t) { if (tex_coord_idx_mask(t) & vertexLayout) { - texCoordOffsetsByIdx[t] = size; + if (NULL != texCoordOffsetsByIdx) { + texCoordOffsetsByIdx[t] = size; + } size += vecSize; } else { - texCoordOffsetsByIdx[t] = -1; + if (NULL != texCoordOffsetsByIdx) { + texCoordOffsetsByIdx[t] = -1; + } } } if (kColor_VertexLayoutBit & vertexLayout) { - *colorOffset = size; + if (NULL != colorOffset) { + *colorOffset = size; + } size += sizeof(GrColor); } else { - *colorOffset = -1; + if (NULL != colorOffset) { + *colorOffset = -1; + } + } + if (kCoverage_VertexLayoutBit & vertexLayout) { + if (NULL != coverageOffset) { + *coverageOffset = size; + } + size += sizeof(GrColor); + } else { + if (NULL != coverageOffset) { + *coverageOffset = -1; + } } if (kEdge_VertexLayoutBit & vertexLayout) { - *edgeOffset = size; + if (NULL != edgeOffset) { + *edgeOffset = size; + } size += 4 * sizeof(GrScalar); } else { - *edgeOffset = -1; + if (NULL != edgeOffset) { + *edgeOffset = -1; + } } return size; } @@ -189,31 +245,35 @@ int GrDrawTarget::VertexSizeAndOffsetsByIdx(GrVertexLayout vertexLayout, int GrDrawTarget::VertexSizeAndOffsetsByStage(GrVertexLayout vertexLayout, int texCoordOffsetsByStage[kNumStages], int* colorOffset, + int* coverageOffset, int* edgeOffset) { GrAssert(check_layout(vertexLayout)); - GrAssert(NULL != texCoordOffsetsByStage); - GrAssert(NULL != colorOffset); - GrAssert(NULL != edgeOffset); - int texCoordOffsetsByIdx[kMaxTexCoords]; int size = VertexSizeAndOffsetsByIdx(vertexLayout, - texCoordOffsetsByIdx, + (NULL == texCoordOffsetsByStage) ? + NULL : + texCoordOffsetsByIdx, colorOffset, + coverageOffset, edgeOffset); - for (int s = 0; s < kNumStages; ++s) { - int tcIdx; - if (StagePosAsTexCoordVertexLayoutBit(s) & vertexLayout) { - texCoordOffsetsByStage[s] = 0; - } else if ((tcIdx = VertexTexCoordsForStage(s, vertexLayout)) >= 0) { - texCoordOffsetsByStage[s] = texCoordOffsetsByIdx[tcIdx]; - } else { - texCoordOffsetsByStage[s] = -1; + if (NULL != texCoordOffsetsByStage) { + for (int s = 0; s < kNumStages; ++s) { + int tcIdx; + if (StagePosAsTexCoordVertexLayoutBit(s) & vertexLayout) { + texCoordOffsetsByStage[s] = 0; + } else if ((tcIdx = VertexTexCoordsForStage(s, vertexLayout)) >= 0) { + texCoordOffsetsByStage[s] = texCoordOffsetsByIdx[tcIdx]; + } else { + texCoordOffsetsByStage[s] = -1; + } } } return size; } +//////////////////////////////////////////////////////////////////////////////// + bool GrDrawTarget::VertexUsesStage(int stage, GrVertexLayout vertexLayout) { GrAssert(stage < kNumStages); GrAssert(check_layout(vertexLayout)); @@ -241,6 +301,8 @@ int GrDrawTarget::VertexTexCoordsForStage(int stage, GrVertexLayout vertexLayout return -1; } +//////////////////////////////////////////////////////////////////////////////// + void GrDrawTarget::VertexLayoutUnitTest() { // not necessarily exhaustive static bool run; @@ -286,9 +348,11 @@ void GrDrawTarget::VertexLayoutUnitTest() { } GrAssert(-1 == VertexEdgeOffset(tcMask)); GrAssert(-1 == VertexColorOffset(tcMask)); + GrAssert(-1 == VertexCoverageOffset(tcMask)); #if GR_DEBUG GrVertexLayout withColor = tcMask | kColor_VertexLayoutBit; #endif + GrAssert(-1 == VertexCoverageOffset(withColor)); GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withColor)); GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withColor)); #if GR_DEBUG @@ -303,6 +367,19 @@ void GrDrawTarget::VertexLayoutUnitTest() { GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withColorAndEdge)); GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexEdgeOffset(withColorAndEdge)); GrAssert(4*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withColorAndEdge)); + #if GR_DEBUG + GrVertexLayout withCoverage = tcMask | kCoverage_VertexLayoutBit; + #endif + GrAssert(-1 == VertexColorOffset(withCoverage)); + GrAssert(2*sizeof(GrPoint) == VertexCoverageOffset(withCoverage)); + GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withCoverage)); + #if GR_DEBUG + GrVertexLayout withCoverageAndColor = tcMask | kCoverage_VertexLayoutBit | + kColor_VertexLayoutBit; + #endif + GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withCoverageAndColor)); + GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexCoverageOffset(withCoverageAndColor)); + GrAssert(2*sizeof(GrPoint) + 2 * sizeof(GrColor) == VertexSize(withCoverageAndColor)); } GrAssert(tex_coord_idx_mask(t) == tcMask); GrAssert(check_layout(tcMask)); @@ -310,10 +387,13 @@ void GrDrawTarget::VertexLayoutUnitTest() { int stageOffsets[kNumStages]; int colorOffset; int edgeOffset; + int coverageOffset; int size; - size = VertexSizeAndOffsetsByStage(tcMask, stageOffsets, &colorOffset, &edgeOffset); + size = VertexSizeAndOffsetsByStage(tcMask, stageOffsets, &colorOffset, + &coverageOffset, &edgeOffset); GrAssert(2*sizeof(GrPoint) == size); GrAssert(-1 == colorOffset); + GrAssert(-1 == coverageOffset); GrAssert(-1 == edgeOffset); for (int s = 0; s < kNumStages; ++s) { GrAssert(VertexUsesStage(s, tcMask)); @@ -765,8 +845,9 @@ bool GrDrawTarget::CanTweakAlphaForCoverage(GrBlendCoeff dstCoeff) { bool GrDrawTarget::CanDisableBlend(GrVertexLayout layout, const DrState& state) { // If we compute a coverage value (using edge AA or a coverage stage) then // we can't force blending off. - if (state.fEdgeAANumEdges > 0 || - layout & kEdge_VertexLayoutBit) { + if (state.fEdgeAANumEdges > 0 || + (layout & kEdge_VertexLayoutBit) || + (layout & kCoverage_VertexLayoutBit)) { return false; } for (int s = state.fFirstCoverageStage; s < kNumStages; ++s) { @@ -910,14 +991,10 @@ void GrDrawTarget::SetRectVertices(const GrRect& rect, #endif int stageOffsets[kNumStages]; - int colorOffset; - int edgeOffset; - int vsize = VertexSizeAndOffsetsByStage(layout, stageOffsets, - &colorOffset, &edgeOffset); - GrAssert(-1 == colorOffset); - GrAssert(-1 == edgeOffset); + int vsize = VertexSizeAndOffsetsByStage(layout, stageOffsets, + NULL, NULL, NULL); - GrTCast(vertices)->setRectFan(rect.fLeft, rect.fTop, + GrTCast(vertices)->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vsize); if (NULL != matrix) { @@ -926,10 +1003,10 @@ void GrDrawTarget::SetRectVertices(const GrRect& rect, for (int i = 0; i < kNumStages; ++i) { if (stageOffsets[i] > 0) { - GrPoint* coords = GrTCast(GrTCast(vertices) + + GrPoint* coords = GrTCast(GrTCast(vertices) + stageOffsets[i]); coords->setRectFan(srcRects[i]->fLeft, srcRects[i]->fTop, - srcRects[i]->fRight, srcRects[i]->fBottom, + srcRects[i]->fRight, srcRects[i]->fBottom, vsize); if (NULL != srcMatrices && NULL != srcMatrices[i]) { srcMatrices[i]->mapPointsWithStride(coords, vsize, 4); diff --git a/gpu/src/GrDrawTarget.h b/gpu/src/GrDrawTarget.h index a1667e410f..86aa3b43ea 100644 --- a/gpu/src/GrDrawTarget.h +++ b/gpu/src/GrDrawTarget.h @@ -53,6 +53,7 @@ public: bool fFSAASupport : 1; bool fDualSourceBlendingSupport : 1; bool fBufferLockSupport : 1; + bool fSupportPerVertexCoverage : 1; int fMinRenderTargetWidth; int fMinRenderTargetHeight; int fMaxRenderTargetSize; @@ -407,6 +408,12 @@ public: */ void setColor(GrColor); + /** + * Gets the currently set color. + * @return the current color. + */ + GrColor getColor() const { return fCurrDrawState.fColor; } + /** * Add a color filter that can be represented by a color and a mode. */ @@ -516,77 +523,6 @@ public: */ GrColor getBlendConstant() const { return fCurrDrawState.fBlendConstant; } - /** - * Used to save and restore the GrGpu's drawing state - */ - struct SavedDrawState { - private: - DrState fState; - friend class GrDrawTarget; - }; - - /** - * Saves the current draw state. The state can be restored at a later time - * with restoreDrawState. - * - * See also AutoStateRestore class. - * - * @param state will hold the state after the function returns. - */ - void saveCurrentDrawState(SavedDrawState* state) const; - - /** - * Restores previously saved draw state. The client guarantees that state - * was previously passed to saveCurrentDrawState and that the rendertarget - * and texture set at save are still valid. - * - * See also AutoStateRestore class. - * - * @param state the previously saved state to restore. - */ - void restoreDrawState(const SavedDrawState& state); - - /** - * Copies the draw state from another target to this target. - * - * @param srcTarget draw target used as src of the draw state. - */ - void copyDrawState(const GrDrawTarget& srcTarget); - - /** - * The format of vertices is represented as a bitfield of flags. - * Flags that indicate the layout of vertex data. Vertices always contain - * positions and may also contain up to kMaxTexCoords sets of 2D texture - * coordinates and per-vertex colors. Each stage can use any of the texture - * coordinates as its input texture coordinates or it may use the positions. - * - * If no texture coordinates are specified for a stage then the stage is - * disabled. - * - * Only one type of texture coord can be specified per stage. For - * example StageTexCoordVertexLayoutBit(0, 2) and - * StagePosAsTexCoordVertexLayoutBit(0) cannot both be specified. - * - * The order in memory is always (position, texture coord 0, ..., color) - * with any unused fields omitted. Note that this means that if only texture - * coordinates 1 is referenced then there is no texture coordinates 0 and - * the order would be (position, texture coordinate 1[, color]). - */ - - /** - * Generates a bit indicating that a texture stage uses texture coordinates - * - * @param stage the stage that will use texture coordinates. - * @param texCoordIdx the index of the texture coordinates to use - * - * @return the bit to add to a GrVertexLayout bitfield. - */ - static int StageTexCoordVertexLayoutBit(int stage, int texCoordIdx) { - GrAssert(stage < kNumStages); - GrAssert(texCoordIdx < kMaxTexCoords); - return 1 << (stage + (texCoordIdx * kNumStages)); - } - /** * Determines if blend is effectively disabled. * @@ -642,6 +578,79 @@ public: */ void setEdgeAAData(const Edge* edges, int numEdges); + /** + * Used to save and restore the GrGpu's drawing state + */ + struct SavedDrawState { + private: + DrState fState; + friend class GrDrawTarget; + }; + + /** + * Saves the current draw state. The state can be restored at a later time + * with restoreDrawState. + * + * See also AutoStateRestore class. + * + * @param state will hold the state after the function returns. + */ + void saveCurrentDrawState(SavedDrawState* state) const; + + /** + * Restores previously saved draw state. The client guarantees that state + * was previously passed to saveCurrentDrawState and that the rendertarget + * and texture set at save are still valid. + * + * See also AutoStateRestore class. + * + * @param state the previously saved state to restore. + */ + void restoreDrawState(const SavedDrawState& state); + + /** + * Copies the draw state from another target to this target. + * + * @param srcTarget draw target used as src of the draw state. + */ + void copyDrawState(const GrDrawTarget& srcTarget); + + /** + * The format of vertices is represented as a bitfield of flags. + * Flags that indicate the layout of vertex data. Vertices always contain + * positions and may also contain up to kMaxTexCoords sets of 2D texture + * coordinates, per-vertex colors, and per-vertex coverage. Each stage can + * use any of the texture coordinates as its input texture coordinates or it + * may use the positions as texture coordinates. + * + * If no texture coordinates are specified for a stage then the stage is + * disabled. + * + * Only one type of texture coord can be specified per stage. For + * example StageTexCoordVertexLayoutBit(0, 2) and + * StagePosAsTexCoordVertexLayoutBit(0) cannot both be specified. + * + * The order in memory is always (position, texture coord 0, ..., color, + * coverage) with any unused fields omitted. Note that this means that if + * only texture coordinates 1 is referenced then there is no texture + * coordinates 0 and the order would be (position, texture coordinate 1 + * [, color][, coverage]). + */ + + /** + * Generates a bit indicating that a texture stage uses texture coordinates + * + * @param stage the stage that will use texture coordinates. + * @param texCoordIdx the index of the texture coordinates to use + * + * @return the bit to add to a GrVertexLayout bitfield. + */ + static int StageTexCoordVertexLayoutBit(int stage, int texCoordIdx) { + GrAssert(stage < kNumStages); + GrAssert(texCoordIdx < kMaxTexCoords); + return 1 << (stage + (texCoordIdx * kNumStages)); + } + private: static const int TEX_COORD_BIT_CNT = kNumStages*kMaxTexCoords; public: @@ -658,6 +667,7 @@ public: GrAssert(stage < kNumStages); return (1 << (TEX_COORD_BIT_CNT + stage)); } + private: static const int STAGE_BIT_CNT = TEX_COORD_BIT_CNT + kNumStages; @@ -667,14 +677,15 @@ public: * Additional Bits that can be specified in GrVertexLayout. */ enum VertexLayoutBits { - /* vertices have colors */ + /* vertices have colors (GrColor) */ kColor_VertexLayoutBit = 1 << (STAGE_BIT_CNT + 0), - + /* vertices have coverage (GrColor where only the alpha chan is used */ + kCoverage_VertexLayoutBit = 1 << (STAGE_BIT_CNT + 1), /* Use text vertices. (Pos and tex coords may be a different type for text [GrGpuTextVertex vs GrPoint].) */ - kTextFormat_VertexLayoutBit = 1 << (STAGE_BIT_CNT + 1), + kTextFormat_VertexLayoutBit = 1 << (STAGE_BIT_CNT + 2), - kEdge_VertexLayoutBit = 1 << (STAGE_BIT_CNT + 2), + kEdge_VertexLayoutBit = 1 << (STAGE_BIT_CNT + 3), // for below assert kDummyVertexLayoutBit, kHighVertexLayoutBit = kDummyVertexLayoutBit - 1 @@ -1121,6 +1132,13 @@ public: */ static int VertexColorOffset(GrVertexLayout vertexLayout); + /** + * Helper function to compute the offset of the coverage in a vertex + * @return offset of coverage in vertex layout or -1 if the + * layout has no coverage. + */ + static int VertexCoverageOffset(GrVertexLayout vertexLayout); + /** * Helper function to compute the offset of the edge pts in a vertex * @return offset of edge in vertex layout or -1 if the @@ -1162,12 +1180,24 @@ public: * @param vertexLayout the layout to query * @param texCoordOffsetsByIdx after return it is the offset of each * tex coord index in the vertex or -1 if - * index isn't used. + * index isn't used. (optional) + * @param colorOffset after return it is the offset of the + * color field in each vertex, or -1 if + * there aren't per-vertex colors. (optional) + * @param coverageOffset after return it is the offset of the + * coverage field in each vertex, or -1 if + * there aren't per-vertex coeverages. + * (optional) + * @param edgeOffset after return it is the offset of the + * edge eq field in each vertex, or -1 if + * there aren't per-vertex edge equations. + * (optional) * @return size of a single vertex */ static int VertexSizeAndOffsetsByIdx(GrVertexLayout vertexLayout, int texCoordOffsetsByIdx[kMaxTexCoords], int *colorOffset, + int *coverageOffset, int* edgeOffset); /** @@ -1180,12 +1210,25 @@ public: * @param vertexLayout the layout to query * @param texCoordOffsetsByStage after return it is the offset of each * tex coord index in the vertex or -1 if - * index isn't used. + * index isn't used. (optional) + * @param colorOffset after return it is the offset of the + * color field in each vertex, or -1 if + * there aren't per-vertex colors. + * (optional) + * @param coverageOffset after return it is the offset of the + * coverage field in each vertex, or -1 if + * there aren't per-vertex coeverages. + * (optional) + * @param edgeOffset after return it is the offset of the + * edge eq field in each vertex, or -1 if + * there aren't per-vertex edge equations. + * (optional) * @return size of a single vertex */ static int VertexSizeAndOffsetsByStage(GrVertexLayout vertexLayout, int texCoordOffsetsByStage[kNumStages], int *colorOffset, + int *coverageOffset, int* edgeOffset); /** diff --git a/gpu/src/GrGLProgram.cpp b/gpu/src/GrGLProgram.cpp index cbe2f42486..4b97fab14c 100644 --- a/gpu/src/GrGLProgram.cpp +++ b/gpu/src/GrGLProgram.cpp @@ -81,6 +81,7 @@ struct ShaderCodeSegments { #define POS_ATTR_NAME "aPosition" #define COL_ATTR_NAME "aColor" +#define COV_ATTR_NAME "aCoverage" #define EDGE_ATTR_NAME "aEdge" #define COL_UNI_NAME "uColor" #define EDGES_UNI_NAME "uEdges" @@ -751,6 +752,23 @@ bool GrGLProgram::genProgram(const GrGLInterface* gl, // get edge AA coverage and use it as inCoverage to first coverage stage this->genEdgeCoverage(gl, layout, programData, &inCoverage, &segments); + // include explicit per-vertex coverage if we have it + if (GrDrawTarget::kCoverage_VertexLayoutBit & layout) { + segments.fVSAttrs.push_back().set(GrGLShaderVar::kFloat_Type, + COV_ATTR_NAME); + const char *vsName, *fsName; + append_varying(GrGLShaderVar::kFloat_Type, "Coverage", + &segments, &vsName, &fsName); + segments.fVSCode.appendf("\t%s = " COV_ATTR_NAME ";\n", vsName); + if (inCoverage.size()) { + segments.fFSCode.appendf("\tfloat edgeAndAttrCov = %s * %s;\n", + fsName, inCoverage.c_str()); + inCoverage = "edgeAndAttrCov"; + } else { + inCoverage = fsName; + } + } + GrStringBuilder outCoverage; const int& startStage = fProgramDesc.fFirstCoverageStage; for (int s = startStage; s < GrDrawTarget::kNumStages; ++s) { @@ -1100,6 +1118,8 @@ bool GrGLProgram::bindOutputsAttribsAndLinkProgram( GR_GL_CALL(gl, BindAttribLocation(progID, ColorAttributeIdx(), COL_ATTR_NAME)); + GR_GL_CALL(gl, BindAttribLocation(progID, CoverageAttributeIdx(), + COV_ATTR_NAME)); GR_GL_CALL(gl, BindAttribLocation(progID, EdgeAttributeIdx(), EDGE_ATTR_NAME)); diff --git a/gpu/src/GrGLProgram.h b/gpu/src/GrGLProgram.h index a79f877223..197da5d61c 100644 --- a/gpu/src/GrGLProgram.h +++ b/gpu/src/GrGLProgram.h @@ -66,7 +66,10 @@ public: static int PositionAttributeIdx() { return 0; } static int TexCoordAttributeIdx(int tcIdx) { return 1 + tcIdx; } static int ColorAttributeIdx() { return 1 + GrDrawTarget::kMaxTexCoords; } - static int EdgeAttributeIdx() { return 2 + GrDrawTarget::kMaxTexCoords; } + static int CoverageAttributeIdx() { + return 2 + GrDrawTarget::kMaxTexCoords; + } + static int EdgeAttributeIdx() { return 3 + GrDrawTarget::kMaxTexCoords; } static int ViewMatrixAttributeIdx() { return 2 + GrDrawTarget::kMaxTexCoords; diff --git a/gpu/src/GrGpuGLFixed.cpp b/gpu/src/GrGpuGLFixed.cpp index 195ca32502..c9d5844b4f 100644 --- a/gpu/src/GrGpuGLFixed.cpp +++ b/gpu/src/GrGpuGLFixed.cpp @@ -272,23 +272,29 @@ void GrGpuGLFixed::setupGeometry(int* startVertex, int indexCount) { int newColorOffset; + int newCoverageOffset; int newTexCoordOffsets[kNumStages]; int newEdgeOffset; GrGLsizei newStride = VertexSizeAndOffsetsByStage(this->getGeomSrc().fVertexLayout, newTexCoordOffsets, &newColorOffset, + &newCoverageOffset, &newEdgeOffset); GrAssert(-1 == newEdgeOffset); // not supported by fixed pipe + GrAssert(-1 == newCoverageOffset); // not supported by fixed pipe int oldColorOffset; + int oldCoverageOffset; int oldTexCoordOffsets[kNumStages]; int oldEdgeOffset; GrGLsizei oldStride = VertexSizeAndOffsetsByStage(fHWGeometryState.fVertexLayout, oldTexCoordOffsets, &oldColorOffset, + &oldCoverageOffset, &oldEdgeOffset); GrAssert(-1 == oldEdgeOffset); + GrAssert(-1 == oldCoverageOffset); bool indexed = NULL != startIndex; diff --git a/gpu/src/GrGpuGLShaders.cpp b/gpu/src/GrGpuGLShaders.cpp index 80f0268b02..3ca4346b88 100644 --- a/gpu/src/GrGpuGLShaders.cpp +++ b/gpu/src/GrGpuGLShaders.cpp @@ -219,6 +219,10 @@ bool GrGpuGLShaders::programUnitTest() { idx = (int)(random.nextF() * (kNumStages+1)); pdesc.fFirstCoverageStage = idx; + pdesc.fVertexLayout |= (random.nextF() > .5f) ? + GrDrawTarget::kCoverage_VertexLayoutBit : + 0; + #if GR_GL_EXPERIMENTAL_GS pdesc.fExperimentalGS = this->getCaps().fGeometryShaderSupport && random.nextF() > .5f; @@ -238,8 +242,8 @@ bool GrGpuGLShaders::programUnitTest() { } pdesc.fEdgeAANumEdges = 0; } else { - pdesc.fEdgeAANumEdges = SkToS8(1 + random.nextF() * - this->getMaxEdges()); + pdesc.fEdgeAANumEdges = static_cast(1 + random.nextF() * + this->getMaxEdges()); pdesc.fEdgeAAConcave = random.nextF() > .5f; } } else { @@ -312,6 +316,7 @@ GrGpuGLShaders::GrGpuGLShaders(const GrGLInterface* gl) // Enable supported shader-releated caps fCaps.fShaderSupport = true; + fCaps.fSupportPerVertexCoverage = true; if (kDesktop_GrGLBinding == this->glBinding()) { fCaps.fDualSourceBlendingSupport = this->glVersion() >= GR_GL_VER(3,3) || @@ -700,6 +705,7 @@ void GrGpuGLShaders::setupGeometry(int* startVertex, int indexCount) { int newColorOffset; + int newCoverageOffset; int newTexCoordOffsets[kMaxTexCoords]; int newEdgeOffset; @@ -707,8 +713,10 @@ void GrGpuGLShaders::setupGeometry(int* startVertex, this->getGeomSrc().fVertexLayout, newTexCoordOffsets, &newColorOffset, + &newCoverageOffset, &newEdgeOffset); int oldColorOffset; + int oldCoverageOffset; int oldTexCoordOffsets[kMaxTexCoords]; int oldEdgeOffset; @@ -716,6 +724,7 @@ void GrGpuGLShaders::setupGeometry(int* startVertex, fHWGeometryState.fVertexLayout, oldTexCoordOffsets, &oldColorOffset, + &oldCoverageOffset, &oldEdgeOffset); bool indexed = NULL != startIndex; @@ -792,6 +801,23 @@ void GrGpuGLShaders::setupGeometry(int* startVertex, GL_CALL(DisableVertexAttribArray(GrGLProgram::ColorAttributeIdx())); } + if (newCoverageOffset > 0) { + // bind just alpha channel. + GrGLvoid* coverageOffset = (int8_t*)(vertexOffset + newCoverageOffset + + GrColor_INDEX_A); + int idx = GrGLProgram::CoverageAttributeIdx(); + if (oldCoverageOffset <= 0) { + GL_CALL(EnableVertexAttribArray(idx)); + GL_CALL(VertexAttribPointer(idx, 1, GR_GL_UNSIGNED_BYTE, + true, newStride, coverageOffset)); + } else if (allOffsetsChange || newCoverageOffset != oldCoverageOffset) { + GL_CALL(VertexAttribPointer(idx, 1, GR_GL_UNSIGNED_BYTE, + true, newStride, coverageOffset)); + } + } else if (oldCoverageOffset > 0) { + GL_CALL(DisableVertexAttribArray(GrGLProgram::CoverageAttributeIdx())); + } + if (newEdgeOffset > 0) { GrGLvoid* edgeOffset = (int8_t*)(vertexOffset + newEdgeOffset); int idx = GrGLProgram::EdgeAttributeIdx(); @@ -954,13 +980,30 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type) { desc.fExperimentalGS = this->getCaps().fGeometryShaderSupport; #endif - // use canonical value when coverage/color distinction won't affect - // generated code to prevent duplicate programs. + // we want to avoid generating programs with different "first cov stage" + // values when they would compute the same result. + // We set field in the desc to kNumStages when either there are no + // coverage stages or the distinction between coverage and color is + // immaterial. + int firstCoverageStage = kNumStages; desc.fFirstCoverageStage = kNumStages; - if (fCurrDrawState.fFirstCoverageStage <= lastEnabledStage) { + bool hasCoverage = fCurrDrawState.fFirstCoverageStage <= lastEnabledStage; + if (hasCoverage) { + firstCoverageStage = fCurrDrawState.fFirstCoverageStage; + } + + // other coverage inputs + if (!hasCoverage) { + hasCoverage = + desc.fEdgeAANumEdges || + (desc.fVertexLayout & GrDrawTarget::kCoverage_VertexLayoutBit) || + (desc.fVertexLayout & GrDrawTarget::kEdge_VertexLayoutBit); + } + + if (hasCoverage) { // color filter is applied between color/coverage computation if (SkXfermode::kDst_Mode != desc.fColorFilterXfermode) { - desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage; + desc.fFirstCoverageStage = firstCoverageStage; } // We could consider cases where the final color is solid (0xff alpha) @@ -972,17 +1015,17 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type) { if (kZero_BlendCoeff == fCurrDrawState.fDstBlend) { // write the coverage value to second color desc.fDualSrcOutput = ProgramDesc::kCoverage_DualSrcOutput; - desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage; + desc.fFirstCoverageStage = firstCoverageStage; } else if (kSA_BlendCoeff == fCurrDrawState.fDstBlend) { // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially // cover desc.fDualSrcOutput = ProgramDesc::kCoverageISA_DualSrcOutput; - desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage; + desc.fFirstCoverageStage = firstCoverageStage; } else if (kSC_BlendCoeff == fCurrDrawState.fDstBlend) { // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially // cover desc.fDualSrcOutput = ProgramDesc::kCoverageISC_DualSrcOutput; - desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage; + desc.fFirstCoverageStage = firstCoverageStage; } } } diff --git a/gyp/SampleApp.gyp b/gyp/SampleApp.gyp index dfd1e4c352..a3ffd65f47 100644 --- a/gyp/SampleApp.gyp +++ b/gyp/SampleApp.gyp @@ -35,6 +35,7 @@ '../samplecode/OverView.cpp', '../samplecode/Sample2PtRadial.cpp', '../samplecode/SampleAARects.cpp', + '../samplecode/SampleAARectModes.cpp', '../samplecode/SampleAll.cpp', '../samplecode/SampleAnimator.cpp', '../samplecode/SampleApp.cpp', diff --git a/gyp/gm.gyp b/gyp/gm.gyp index fc43221947..575cb8d00f 100644 --- a/gyp/gm.gyp +++ b/gyp/gm.gyp @@ -9,6 +9,7 @@ 'target_name': 'gm', 'type': 'executable', 'sources': [ + '../gm/aarectmodes.cpp', '../gm/bitmapfilters.cpp', '../gm/bitmapscroll.cpp', '../gm/blurs.cpp', diff --git a/samplecode/SampleAARectModes.cpp b/samplecode/SampleAARectModes.cpp new file mode 100644 index 0000000000..7b9e7d5eb3 --- /dev/null +++ b/samplecode/SampleAARectModes.cpp @@ -0,0 +1,152 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkDevice.h" +#include "SkColorPriv.h" +#include "SkShader.h" + +static SkCanvas* create_canvas(int w, int h) { + SkBitmap bm; + bm.setConfig(SkBitmap::kARGB_8888_Config, w, h); + bm.allocPixels(); + bm.eraseColor(0); + return new SkCanvas(bm); +} + +static const SkBitmap& extract_bitmap(SkCanvas* canvas) { + return canvas->getDevice()->accessBitmap(false); +} + +static const struct { + SkXfermode::Mode fMode; + const char* fLabel; +} gModes[] = { + { SkXfermode::kClear_Mode, "Clear" }, + { SkXfermode::kSrc_Mode, "Src" }, + { SkXfermode::kDst_Mode, "Dst" }, + { SkXfermode::kSrcOver_Mode, "SrcOver" }, + { SkXfermode::kDstOver_Mode, "DstOver" }, + { SkXfermode::kSrcIn_Mode, "SrcIn" }, + { SkXfermode::kDstIn_Mode, "DstIn" }, + { SkXfermode::kSrcOut_Mode, "SrcOut" }, + { SkXfermode::kDstOut_Mode, "DstOut" }, + { SkXfermode::kSrcATop_Mode, "SrcATop" }, + { SkXfermode::kDstATop_Mode, "DstATop" }, + { SkXfermode::kXor_Mode, "Xor" }, +}; + +const int gWidth = 64; +const int gHeight = 64; +const SkScalar W = SkIntToScalar(gWidth); +const SkScalar H = SkIntToScalar(gHeight); + +static SkScalar drawCell(SkCanvas* canvas, SkXfermode* mode, + SkAlpha a0, SkAlpha a1) { + + SkPaint paint; + paint.setAntiAlias(true); + + SkRect r = SkRect::MakeWH(W, H); + r.inset(W/10, H/10); + + paint.setColor(SK_ColorBLUE); + paint.setAlpha(a0); + canvas->drawOval(r, paint); + + paint.setColor(SK_ColorRED); + paint.setAlpha(a1); + paint.setXfermode(mode); + + SkScalar offset = SK_Scalar1 / 3; + SkRect rect = SkRect::MakeXYWH(W / 4 + offset, + H / 4 + offset, + W / 2, H / 2); + canvas->drawRect(rect, paint); + + return H; +} + +static SkShader* make_bg_shader() { + SkBitmap bm; + bm.setConfig(SkBitmap::kARGB_8888_Config, 2, 2); + bm.allocPixels(); + *bm.getAddr32(0, 0) = *bm.getAddr32(1, 1) = 0xFFFFFFFF; + *bm.getAddr32(1, 0) = *bm.getAddr32(0, 1) = SkPackARGB32(0xFF, 0xCC, + 0xCC, 0xCC); + + SkShader* s = SkShader::CreateBitmapShader(bm, + SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode); + + SkMatrix m; + m.setScale(SkIntToScalar(6), SkIntToScalar(6)); + s->setLocalMatrix(m); + return s; +} + +class AARectsModesView : public SampleView { + SkPaint fBGPaint; +public: + AARectsModesView () { + fBGPaint.setShader(make_bg_shader())->unref(); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "AARectsModes"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + const SkRect bounds = SkRect::MakeWH(W, H); + static const SkAlpha gAlphaValue[] = { 0xFF, 0x88, 0x88 }; + + canvas->translate(SkIntToScalar(4), SkIntToScalar(4)); + + for (int alpha = 0; alpha < 4; ++alpha) { + canvas->save(); + canvas->save(); + for (size_t i = 0; i < SK_ARRAY_COUNT(gModes); ++i) { + if (6 == i) { + canvas->restore(); + canvas->translate(W * 5, 0); + canvas->save(); + } + SkXfermode* mode = SkXfermode::Create(gModes[i].fMode); + + canvas->drawRect(bounds, fBGPaint); + canvas->saveLayer(&bounds, NULL); + SkScalar dy = drawCell(canvas, mode, + gAlphaValue[alpha & 1], + gAlphaValue[alpha & 2]); + canvas->restore(); + + canvas->translate(0, dy * 5 / 4); + SkSafeUnref(mode); + } + canvas->restore(); + canvas->restore(); + canvas->translate(W * 5 / 4, 0); + } + } + +private: + typedef SampleView INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new AARectsModesView; } +static SkViewRegister reg(MyFactory); +