diff options
author | 2016-04-12 12:48:21 -0700 | |
---|---|---|
committer | 2016-04-12 12:48:21 -0700 | |
commit | 0586f5cc9713268238394411a5daa2c7758b092b (patch) | |
tree | 74d7c1a3786a306dab0dd3fdfe8e1bcb25f9f57f | |
parent | 134ff5c9cf99e1a930cb1b77bf8fb2b7b2c31d31 (diff) |
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
-rw-r--r-- | src/gpu/batches/GrAtlasTextBatch.cpp | 16 | ||||
-rw-r--r-- | src/gpu/batches/GrAtlasTextBatch.h | 3 | ||||
-rw-r--r-- | src/gpu/effects/GrDistanceFieldGeoProc.cpp | 26 | ||||
-rw-r--r-- | src/gpu/effects/GrDistanceFieldGeoProc.h | 7 | ||||
-rw-r--r-- | src/gpu/text/GrAtlasTextBlob.cpp | 13 | ||||
-rw-r--r-- | src/gpu/text/GrAtlasTextBlob.h | 1 | ||||
-rw-r--r-- | src/gpu/text/GrDistanceFieldAdjustTable.cpp | 15 | ||||
-rw-r--r-- | src/gpu/text/GrDistanceFieldAdjustTable.h | 14 |
8 files changed, 69 insertions, 26 deletions
diff --git a/src/gpu/batches/GrAtlasTextBatch.cpp b/src/gpu/batches/GrAtlasTextBatch.cpp index ca6b99ea33..03b3ad0363 100644 --- a/src/gpu/batches/GrAtlasTextBatch.cpp +++ b/src/gpu/batches/GrAtlasTextBatch.cpp @@ -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, diff --git a/src/gpu/batches/GrAtlasTextBatch.h b/src/gpu/batches/GrAtlasTextBatch.h index e883fa1d9f..629027a003 100644 --- a/src/gpu/batches/GrAtlasTextBatch.h +++ b/src/gpu/batches/GrAtlasTextBatch.h @@ -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 diff --git a/src/gpu/effects/GrDistanceFieldGeoProc.cpp b/src/gpu/effects/GrDistanceFieldGeoProc.cpp index 0c4125d962..e754661065 100644 --- a/src/gpu/effects/GrDistanceFieldGeoProc.cpp +++ b/src/gpu/effects/GrDistanceFieldGeoProc.cpp @@ -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);"); diff --git a/src/gpu/effects/GrDistanceFieldGeoProc.h b/src/gpu/effects/GrDistanceFieldGeoProc.h index 05fffa58d6..2786b7e823 100644 --- a/src/gpu/effects/GrDistanceFieldGeoProc.h +++ b/src/gpu/effects/GrDistanceFieldGeoProc.h @@ -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, }; /** diff --git a/src/gpu/text/GrAtlasTextBlob.cpp b/src/gpu/text/GrAtlasTextBlob.cpp index afbff46994..60f905df5f 100644 --- a/src/gpu/text/GrAtlasTextBlob.cpp +++ b/src/gpu/text/GrAtlasTextBlob.cpp @@ -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) { diff --git a/src/gpu/text/GrAtlasTextBlob.h b/src/gpu/text/GrAtlasTextBlob.h index bc1e0b4afc..9631ef1024 100644 --- a/src/gpu/text/GrAtlasTextBlob.h +++ b/src/gpu/text/GrAtlasTextBlob.h @@ -502,6 +502,7 @@ private: GrColor color, const SkPaint& skPaint, const SkSurfaceProps& props, const GrDistanceFieldAdjustTable* distanceAdjustTable, + bool useSRGBDistanceTable, GrBatchFontCache* cache); struct BigGlyph { diff --git a/src/gpu/text/GrDistanceFieldAdjustTable.cpp b/src/gpu/text/GrDistanceFieldAdjustTable.cpp index 1c5aeceb80..c6da175e0b 100644 --- a/src/gpu/text/GrDistanceFieldAdjustTable.cpp +++ b/src/gpu/text/GrDistanceFieldAdjustTable.cpp @@ -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); } diff --git a/src/gpu/text/GrDistanceFieldAdjustTable.h b/src/gpu/text/GrDistanceFieldAdjustTable.h index f7d8bee089..f9b5161e95 100644 --- a/src/gpu/text/GrDistanceFieldAdjustTable.h +++ b/src/gpu/text/GrDistanceFieldAdjustTable.h @@ -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 |