sRGB support for distance field text.

Add a second distance field adjust table that only applies contrast,
not fake-gamma correction. Store a flag in the batch at creation time,
using the same logic we apply elsewhere (render target format, plus
paint flags).

That gets us close, but not as good as bitmap text. The final step is
to use a linear step function (rather than smoothstep) to map distance
to coverage, when we have sRGB output. Smoothstep's nonlinear response
is actually doing some fake-gamma, so it ends up over-correcting when
the output is already gamma-correct.

Results are now very close between L32 (old table, smoothstep) and S32
(contrast-only table, linstep).

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1885613002

Review URL: https://codereview.chromium.org/1885613002
This commit is contained in:
brianosman 2016-04-12 12:48:21 -07:00 committed by Commit bot
parent 134ff5c9cf
commit 0586f5cc97
8 changed files with 69 additions and 26 deletions

View File

@ -261,6 +261,7 @@ GrGeometryProcessor* GrAtlasTextBatch::setupDfProcessor(const SkMatrix& viewMatr
// set up any flags
uint32_t flags = viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
flags |= viewMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
flags |= fUseSRGBDistanceTable ? kSRGB_DistanceFieldEffectFlag : 0;
// see if we need to create a new effect
if (isLCD) {
@ -269,12 +270,12 @@ GrGeometryProcessor* GrAtlasTextBatch::setupDfProcessor(const SkMatrix& viewMatr
GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
float redCorrection =
(*fDistanceAdjustTable)[GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift];
float greenCorrection =
(*fDistanceAdjustTable)[GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift];
float blueCorrection =
(*fDistanceAdjustTable)[GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift];
float redCorrection = fDistanceAdjustTable->getAdjustment(
GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift, fUseSRGBDistanceTable);
float greenCorrection = fDistanceAdjustTable->getAdjustment(
GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift, fUseSRGBDistanceTable);
float blueCorrection = fDistanceAdjustTable->getAdjustment(
GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift, fUseSRGBDistanceTable);
GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(redCorrection,
greenCorrection,
@ -290,7 +291,8 @@ GrGeometryProcessor* GrAtlasTextBatch::setupDfProcessor(const SkMatrix& viewMatr
} else {
#ifdef SK_GAMMA_APPLY_TO_A8
U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT, filteredColor);
float correction = (*fDistanceAdjustTable)[lum >> kDistanceAdjustLumShift];
float correction = fDistanceAdjustTable->getAdjustment(
lum >> kDistanceAdjustLumShift, fUseSRGBDistanceTable);
return GrDistanceFieldA8TextGeoProc::Create(color,
viewMatrix,
texture,

View File

@ -58,6 +58,7 @@ public:
static GrAtlasTextBatch* CreateDistanceField(
int glyphCount, GrBatchFontCache* fontCache,
const GrDistanceFieldAdjustTable* distanceAdjustTable,
bool useSRGBDistanceTable,
SkColor filteredColor, bool isLCD,
bool useBGR) {
GrAtlasTextBatch* batch = new GrAtlasTextBatch;
@ -65,6 +66,7 @@ public:
batch->fFontCache = fontCache;
batch->fMaskType = isLCD ? kLCDDistanceField_MaskType : kGrayscaleDistanceField_MaskType;
batch->fDistanceAdjustTable.reset(SkRef(distanceAdjustTable));
batch->fUseSRGBDistanceTable = useSRGBDistanceTable;
batch->fFilteredColor = filteredColor;
batch->fUseBGR = useBGR;
batch->fBatch.fNumGlyphs = glyphCount;
@ -182,6 +184,7 @@ private:
// Distance field properties
SkAutoTUnref<const GrDistanceFieldAdjustTable> fDistanceAdjustTable;
SkColor fFilteredColor;
bool fUseSRGBDistanceTable;
friend class GrBlobRegenHelper; // Needs to trigger flushes

View File

@ -82,6 +82,7 @@ public:
bool isUniformScale = (dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) ==
kUniformScale_DistanceFieldEffectMask;
bool isSimilarity = SkToBool(dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag);
bool srgbOutput = SkToBool(dfTexEffect.getFlags() & kSRGB_DistanceFieldEffectFlag);
varyingHandler->addVarying("TextureCoords", &uv, kHigh_GrSLPrecision);
vertBuilder->codeAppendf("%s = %s;", uv.vsOut(), dfTexEffect.inTextureCoords()->fName);
@ -153,7 +154,16 @@ public:
// this gives us a smooth step across approximately one fragment
fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);");
}
fragBuilder->codeAppend("float val = smoothstep(-afwidth, afwidth, distance);");
// The smoothstep falloff compensates for the non-linear sRGB response curve. If we are
// doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want distance
// mapped linearly to coverage, so use a linear step:
if (srgbOutput) {
fragBuilder->codeAppend(
"float val = clamp(distance + afwidth / (2.0 * afwidth), 0.0, 1.0);");
} else {
fragBuilder->codeAppend("float val = smoothstep(-afwidth, afwidth, distance);");
}
fragBuilder->codeAppendf("%s = vec4(val);", args.fOutputCoverage);
}
@ -553,6 +563,7 @@ public:
bool isUniformScale = (dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) ==
kUniformScale_DistanceFieldEffectMask;
bool isSimilarity = SkToBool(dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag);
bool srgbOutput = SkToBool(dfTexEffect.getFlags() & kSRGB_DistanceFieldEffectFlag);
GrGLSLVertToFrag recipScale(kFloat_GrSLType);
GrGLSLVertToFrag uv(kVec2f_GrSLType);
varyingHandler->addVarying("TextureCoords", &uv, kHigh_GrSLPrecision);
@ -667,8 +678,17 @@ public:
fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);");
}
fragBuilder->codeAppend(
"vec4 val = vec4(smoothstep(vec3(-afwidth), vec3(afwidth), distance), 1.0);");
// The smoothstep falloff compensates for the non-linear sRGB response curve. If we are
// doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want distance
// mapped linearly to coverage, so use a linear step:
if (srgbOutput) {
fragBuilder->codeAppend("vec4 val = "
"vec4(clamp(distance + vec3(afwidth) / vec3(2.0 * afwidth), 0.0, 1.0), 1.0f);");
} else {
fragBuilder->codeAppend(
"vec4 val = vec4(smoothstep(vec3(-afwidth), vec3(afwidth), distance), 1.0);");
}
// set alpha to be max of rgb coverage
fragBuilder->codeAppend("val.a = max(max(val.r, val.g), val.b);");

View File

@ -22,6 +22,7 @@ enum GrDistanceFieldEffectFlags {
kUseLCD_DistanceFieldEffectFlag = 0x04, // use lcd text
kBGR_DistanceFieldEffectFlag = 0x08, // lcd display has bgr order
kPortrait_DistanceFieldEffectFlag = 0x10, // lcd display is in portrait mode (not used yet)
kSRGB_DistanceFieldEffectFlag = 0x20, // assume sRGB dest (use linstep, not smoothstep)
kInvalid_DistanceFieldEffectFlag = 0x80, // invalid state (for initialization)
@ -29,12 +30,14 @@ enum GrDistanceFieldEffectFlags {
kScaleOnly_DistanceFieldEffectFlag,
// The subset of the flags relevant to GrDistanceFieldA8TextGeoProc
kNonLCD_DistanceFieldEffectMask = kSimilarity_DistanceFieldEffectFlag |
kScaleOnly_DistanceFieldEffectFlag,
kScaleOnly_DistanceFieldEffectFlag |
kSRGB_DistanceFieldEffectFlag,
// The subset of the flags relevant to GrDistanceFieldLCDTextGeoProc
kLCD_DistanceFieldEffectMask = kSimilarity_DistanceFieldEffectFlag |
kScaleOnly_DistanceFieldEffectFlag |
kUseLCD_DistanceFieldEffectFlag |
kBGR_DistanceFieldEffectFlag,
kBGR_DistanceFieldEffectFlag |
kSRGB_DistanceFieldEffectFlag,
};
/**

View File

@ -257,6 +257,7 @@ inline GrDrawBatch* GrAtlasTextBlob::createBatch(
GrColor color,
const SkPaint& skPaint, const SkSurfaceProps& props,
const GrDistanceFieldAdjustTable* distanceAdjustTable,
bool useSRGBDistanceTable,
GrBatchFontCache* cache) {
GrMaskFormat format = info.maskFormat();
GrColor subRunColor;
@ -278,8 +279,8 @@ inline GrDrawBatch* GrAtlasTextBlob::createBatch(
}
bool useBGR = SkPixelGeometryIsBGR(props.pixelGeometry());
batch = GrAtlasTextBatch::CreateDistanceField(glyphCount, cache,
distanceAdjustTable, filteredColor,
info.hasUseLCDText(), useBGR);
distanceAdjustTable, useSRGBDistanceTable,
filteredColor, info.hasUseLCDText(), useBGR);
} else {
batch = GrAtlasTextBatch::CreateBitmap(format, glyphCount, cache);
}
@ -310,10 +311,14 @@ void GrAtlasTextBlob::flushRun(GrDrawContext* dc, GrPipelineBuilder* pipelineBui
continue;
}
bool useSRGBDistanceTable = GrPixelConfigIsSRGB(dc->accessRenderTarget()->config()) &&
!pipelineBuilder->getDisableOutputConversionToSRGB();
SkAutoTUnref<GrDrawBatch> batch(this->createBatch(info, glyphCount, run,
subRun, viewMatrix, x, y, color,
skPaint, props,
distanceAdjustTable, cache));
distanceAdjustTable, useSRGBDistanceTable,
cache));
dc->drawBatch(pipelineBuilder, batch);
}
}
@ -463,7 +468,7 @@ GrDrawBatch* GrAtlasTextBlob::test_createBatch(
GrBatchFontCache* cache) {
const GrAtlasTextBlob::Run::SubRunInfo& info = fRuns[run].fSubRunInfo[subRun];
return this->createBatch(info, glyphCount, run, subRun, viewMatrix, x, y, color, skPaint,
props, distanceAdjustTable, cache);
props, distanceAdjustTable, false, cache);
}
void GrAtlasTextBlob::AssertEqual(const GrAtlasTextBlob& l, const GrAtlasTextBlob& r) {

View File

@ -502,6 +502,7 @@ private:
GrColor color,
const SkPaint& skPaint, const SkSurfaceProps& props,
const GrDistanceFieldAdjustTable* distanceAdjustTable,
bool useSRGBDistanceTable,
GrBatchFontCache* cache);
struct BigGlyph {

View File

@ -11,7 +11,7 @@
SkDEBUGCODE(static const int kExpectedDistanceAdjustTableSize = 8;)
void GrDistanceFieldAdjustTable::buildDistanceAdjustTable() {
SkScalar* build_distance_adjust_table(SkScalar paintGamma, SkScalar deviceGamma) {
// This is used for an approximation of the mask gamma hack, used by raster and bitmap
// text. The mask gamma hack is based off of guessing what the blend color is going to
// be, and adjusting the mask so that when run through the linear blend will
@ -55,14 +55,12 @@ void GrDistanceFieldAdjustTable::buildDistanceAdjustTable() {
#else
SkScalar contrast = 0.5f;
#endif
SkScalar paintGamma = SK_GAMMA_EXPONENT;
SkScalar deviceGamma = SK_GAMMA_EXPONENT;
size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
&width, &height);
SkASSERT(kExpectedDistanceAdjustTableSize == height);
fTable = new SkScalar[height];
SkScalar* table = new SkScalar[height];
SkAutoTArray<uint8_t> data((int)size);
SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
@ -85,9 +83,16 @@ void GrDistanceFieldAdjustTable::buildDistanceAdjustTable() {
const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor
float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor;
fTable[row] = d;
table[row] = d;
break;
}
}
}
return table;
}
void GrDistanceFieldAdjustTable::buildDistanceAdjustTables() {
fTable = build_distance_adjust_table(SK_GAMMA_EXPONENT, SK_GAMMA_EXPONENT);
fSRGBTable = build_distance_adjust_table(SK_Scalar1, SK_Scalar1);
}

View File

@ -15,17 +15,21 @@
// Because the GrAtlasTextContext can go out of scope before the final flush, this needs to be
// refcnted and malloced
struct GrDistanceFieldAdjustTable : public SkNVRefCnt<GrDistanceFieldAdjustTable> {
GrDistanceFieldAdjustTable() { this->buildDistanceAdjustTable(); }
~GrDistanceFieldAdjustTable() { delete[] fTable; }
GrDistanceFieldAdjustTable() { this->buildDistanceAdjustTables(); }
~GrDistanceFieldAdjustTable() {
delete[] fTable;
delete[] fSRGBTable;
}
const SkScalar& operator[] (int i) const {
return fTable[i];
const SkScalar& getAdjustment(int i, bool useSRGBTable) const {
return useSRGBTable ? fSRGBTable[i] : fTable[i];
}
private:
void buildDistanceAdjustTable();
void buildDistanceAdjustTables();
SkScalar* fTable;
SkScalar* fSRGBTable;
};
#endif