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:
parent
134ff5c9cf
commit
0586f5cc97
@ -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,
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);");
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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) {
|
||||
|
@ -502,6 +502,7 @@ private:
|
||||
GrColor color,
|
||||
const SkPaint& skPaint, const SkSurfaceProps& props,
|
||||
const GrDistanceFieldAdjustTable* distanceAdjustTable,
|
||||
bool useSRGBDistanceTable,
|
||||
GrBatchFontCache* cache);
|
||||
|
||||
struct BigGlyph {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user