Make non-AA hairline stroke rects snap to pixels centers so they close.

BUG=skia:3717

Review URL: https://codereview.chromium.org/1101663007
This commit is contained in:
bsalomon 2015-04-27 10:07:04 -07:00 committed by Commit bot
parent f0c000df55
commit d79c549467
10 changed files with 58 additions and 34 deletions

View File

@ -746,7 +746,7 @@ bool GrClipMaskManager::createStencilClipMask(GrRenderTarget* rt,
// if the target is MSAA then we want MSAA enabled when the clip is soft
if (rt->isMultisampled()) {
pipelineBuilder.setState(GrPipelineBuilder::kHWAntialias_StateBit, element->isAA());
pipelineBuilder.setState(GrPipelineBuilder::kHWAntialias_Flag, element->isAA());
}
bool fillInverted = false;

View File

@ -798,6 +798,11 @@ void GrContext::drawRect(GrRenderTarget* rt,
SkScalar rad = SkScalarHalf(width);
bounds.outset(rad, rad);
viewMatrix.mapRect(&bounds);
// Depending on sub-pixel coordinates and the particular GPU, we may lose a corner of
// hairline rects. We jam all the vertices to pixel centers to avoid this, but not when MSAA
// is enabled because it can cause ugly artifacts.
pipelineBuilder.setState(GrPipelineBuilder::kSnapVerticesToPixelCenters_Flag,
0 == width && !rt->isMultisampled());
target->drawBatch(&pipelineBuilder, batch, &bounds);
} else {
// filled BW rect

View File

@ -64,6 +64,9 @@ GrPipeline::GrPipeline(const GrPipelineBuilder& pipelineBuilder,
if (pipelineBuilder.isDither()) {
fFlags |= kDither_Flag;
}
if (pipelineBuilder.snapVerticesToPixelCenters()) {
fFlags |= kSnapVertices_Flag;
}
int firstColorStageIdx = colorPOI.firstEffectiveStageIndex();

View File

@ -80,6 +80,7 @@ public:
bool isDitherState() const { return SkToBool(fFlags & kDither_Flag); }
bool isHWAntialiasState() const { return SkToBool(fFlags & kHWAA_Flag); }
bool snapVerticesToPixelCenters() const { return SkToBool(fFlags & kSnapVertices_Flag); }
// Skip any draws that refer to this pipeline (they should be a no-op).
bool mustSkip() const { return NULL == this->getRenderTarget(); }
@ -119,6 +120,7 @@ private:
enum Flags {
kDither_Flag = 0x1,
kHWAA_Flag = 0x2,
kSnapVertices_Flag = 0x4,
};
typedef GrPendingIOResource<GrRenderTarget, kWrite_GrIOType> RenderTarget;

View File

@ -16,7 +16,7 @@
#include "effects/GrPorterDuffXferProcessor.h"
GrPipelineBuilder::GrPipelineBuilder()
: fFlagBits(0x0)
: fFlags(0x0)
, fDrawFace(kBoth_DrawFace)
, fColorProcInfoValid(false)
, fCoverageProcInfoValid(false)
@ -27,7 +27,7 @@ GrPipelineBuilder::GrPipelineBuilder()
GrPipelineBuilder& GrPipelineBuilder::operator=(const GrPipelineBuilder& that) {
fRenderTarget.reset(SkSafeRef(that.fRenderTarget.get()));
fFlagBits = that.fFlagBits;
fFlags = that.fFlags;
fStencilSettings = that.fStencilSettings;
fDrawFace = that.fDrawFace;
fXPFactory.reset(SkRef(that.getXPFactory()));
@ -69,12 +69,12 @@ void GrPipelineBuilder::setFromPaint(const GrPaint& paint, GrRenderTarget* rt, c
// These have no equivalent in GrPaint, set them to defaults
fDrawFace = kBoth_DrawFace;
fStencilSettings.setDisabled();
fFlagBits = 0;
fFlags = 0;
fClip = clip;
this->setState(GrPipelineBuilder::kDither_StateBit, paint.isDither());
this->setState(GrPipelineBuilder::kHWAntialias_StateBit,
this->setState(GrPipelineBuilder::kDither_Flag, paint.isDither());
this->setState(GrPipelineBuilder::kHWAntialias_Flag,
rt->isMultisampled() && paint.isAntiAlias());
fColorProcInfoValid = false;

View File

@ -275,49 +275,56 @@ public:
* Flags that affect rendering. Controlled using enable/disableState(). All
* default to disabled.
*/
enum StateBits {
enum Flags {
/**
* Perform dithering. TODO: Re-evaluate whether we need this bit
*/
kDither_StateBit = 0x01,
kDither_Flag = 0x01,
/**
* Perform HW anti-aliasing. This means either HW FSAA, if supported by the render target,
* or smooth-line rendering if a line primitive is drawn and line smoothing is supported by
* the 3D API.
*/
kHWAntialias_StateBit = 0x02,
kHWAntialias_Flag = 0x02,
kLast_StateBit = kHWAntialias_StateBit,
/**
* Modifies the vertex shader so that vertices will be positioned at pixel centers.
*/
kSnapVerticesToPixelCenters_Flag = 0x04,
kLast_Flag = kSnapVerticesToPixelCenters_Flag,
};
bool isDither() const { return 0 != (fFlagBits & kDither_StateBit); }
bool isHWAntialias() const { return 0 != (fFlagBits & kHWAntialias_StateBit); }
bool isDither() const { return SkToBool(fFlags & kDither_Flag); }
bool isHWAntialias() const { return SkToBool(fFlags & kHWAntialias_Flag); }
bool snapVerticesToPixelCenters() const {
return SkToBool(fFlags & kSnapVerticesToPixelCenters_Flag); }
/**
* Enable render state settings.
*
* @param stateBits bitfield of StateBits specifying the states to enable
* @param flags bitfield of Flags specifying the states to enable
*/
void enableState(uint32_t stateBits) { fFlagBits |= stateBits; }
void enableState(uint32_t flags) { fFlags |= flags; }
/**
* Disable render state settings.
*
* @param stateBits bitfield of StateBits specifying the states to disable
* @param flags bitfield of Flags specifying the states to disable
*/
void disableState(uint32_t stateBits) { fFlagBits &= ~(stateBits); }
void disableState(uint32_t flags) { fFlags &= ~(flags); }
/**
* Enable or disable stateBits based on a boolean.
* Enable or disable flags based on a boolean.
*
* @param stateBits bitfield of StateBits to enable or disable
* @param flags bitfield of Flags to enable or disable
* @param enable if true enable stateBits, otherwise disable
*/
void setState(uint32_t stateBits, bool enable) {
void setState(uint32_t flags, bool enable) {
if (enable) {
this->enableState(stateBits);
this->enableState(flags);
} else {
this->disableState(stateBits);
this->disableState(flags);
}
}
@ -422,7 +429,7 @@ private:
typedef SkSTArray<4, GrFragmentStage> FragmentStageArray;
SkAutoTUnref<GrRenderTarget> fRenderTarget;
uint32_t fFlagBits;
uint32_t fFlags;
GrStencilSettings fStencilSettings;
DrawFace fDrawFace;
mutable SkAutoTUnref<const GrXPFactory> fXPFactory;

View File

@ -56,10 +56,11 @@ public:
uint8_t fFragPosKey; // set by GrGLShaderBuilder if there are
// effects that read the fragment position.
// Otherwise, 0.
uint8_t fSnapVerticesToPixelCenters;
int8_t fColorEffectCnt;
int8_t fCoverageEffectCnt;
};
GR_STATIC_ASSERT(sizeof(KeyHeader) == 4);
int numColorEffects() const {
return this->header().fColorEffectCnt;

View File

@ -143,7 +143,7 @@ bool GrGLProgramDescBuilder::Build(GrProgramDesc* desc,
} else {
header->fFragPosKey = 0;
}
header->fSnapVerticesToPixelCenters = pipeline.snapVerticesToPixelCenters();
header->fColorEffectCnt = pipeline.numColorFragmentStages();
header->fCoverageEffectCnt = pipeline.numCoverageFragmentStages();
glDesc->finalize();

View File

@ -42,11 +42,18 @@ void GrGLVertexBuilder::transformToNormalizedDeviceSpace(const GrShaderVar& posV
kVec4f_GrSLType, kDefault_GrSLPrecision,
fProgramBuilder->rtAdjustment(),
&fRtAdjustName);
// Transform from Skia's device coords to GL's normalized device coords. Note that
// because we want to "nudge" the device space positions we are converting to
// non-homogeneous NDC.
if (kVec3f_GrSLType == posVar.getType()) {
if (this->getProgramBuilder()->desc().header().fSnapVerticesToPixelCenters) {
if (kVec3f_GrSLType == posVar.getType()) {
const char* p = posVar.c_str();
this->codeAppendf("{vec2 _posTmp = vec2(%s.x/%s.z, %s.y/%s.z);", p, p, p, p);
} else {
SkASSERT(kVec2f_GrSLType == posVar.getType());
this->codeAppendf("{vec2 _posTmp = %s;", posVar.c_str());
}
this->codeAppendf("_posTmp = floor(_posTmp) + vec2(0.5, 0.5);"
"gl_Position = vec4(_posTmp.x * %s.x + %s.y, _posTmp.y * %s.z + %s.w, 0, 1);}",
fRtAdjustName, fRtAdjustName, fRtAdjustName, fRtAdjustName);
} else if (kVec3f_GrSLType == posVar.getType()) {
this->codeAppendf("gl_Position = vec4(dot(%s.xz, %s.xy)/%s.z, dot(%s.yz, %s.zw)/%s.z, 0, 1);",
posVar.c_str(), fRtAdjustName, posVar.c_str(),
posVar.c_str(), fRtAdjustName, posVar.c_str());
@ -56,9 +63,8 @@ void GrGLVertexBuilder::transformToNormalizedDeviceSpace(const GrShaderVar& posV
posVar.c_str(), fRtAdjustName, fRtAdjustName,
posVar.c_str(), fRtAdjustName, fRtAdjustName);
}
// We could have the GrGeometryProcessor do this, but its just easier to have it performed here.
// If we ever need to set variable pointsize, then we can reinvestigate
// We could have the GrGeometryProcessor do this, but its just easier to have it performed
// here. If we ever need to set variable pointsize, then we can reinvestigate
this->codeAppend("gl_PointSize = 1.0;");
}

View File

@ -186,7 +186,7 @@ static void set_random_color_coverage_stages(GrGLGpu* gpu,
static void set_random_state(GrPipelineBuilder* pipelineBuilder, SkRandom* random) {
int state = 0;
for (int i = 1; i <= GrPipelineBuilder::kLast_StateBit; i <<= 1) {
for (int i = 1; i <= GrPipelineBuilder::kLast_Flag; i <<= 1) {
state |= random->nextBool() * i;
}
pipelineBuilder->enableState(state);