Add per-vertex coverage field, use in AA rects when alpha tweak is not valid, add relevant sample/gm

Review URL: http://codereview.appspot.com/5181044/



git-svn-id: http://skia.googlecode.com/svn/trunk@2440 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
bsalomon@google.com 2011-10-10 14:08:47 +00:00
parent f12449b5d2
commit a310826855
12 changed files with 696 additions and 163 deletions

157
gm/aarectmodes.cpp Normal file
View File

@ -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);
}

View File

@ -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();

View File

@ -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<GrColor*>(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<GrColor*>(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<GrColor*>(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<GrColor*>(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);
}

View File

@ -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<GrPoint*>(vertices)->setRectFan(rect.fLeft, rect.fTop,
GrTCast<GrPoint*>(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<GrPoint*>(GrTCast<intptr_t>(vertices) +
GrPoint* coords = GrTCast<GrPoint*>(GrTCast<intptr_t>(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);

View File

@ -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);
/**

View File

@ -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));

View File

@ -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;

View File

@ -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;

View File

@ -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<int>(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;
}
}
}

View File

@ -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',

View File

@ -9,6 +9,7 @@
'target_name': 'gm',
'type': 'executable',
'sources': [
'../gm/aarectmodes.cpp',
'../gm/bitmapfilters.cpp',
'../gm/bitmapscroll.cpp',
'../gm/blurs.cpp',

View File

@ -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);