Gamma correction for distance field text.

Handles both non-LCD and LCD text. Uses a texture to look up the gamma correction values for a given text color or luminance.

BUG=skia:
R=reed@google.com, bungeman@google.com, robertphillips@google.com, bsalomon@google.com

Author: jvanverth@google.com

Review URL: https://codereview.chromium.org/258883002
This commit is contained in:
jvanverth 2014-06-09 12:04:55 -07:00 committed by Commit bot
parent 8405ef9854
commit 4d517fdbb1
9 changed files with 374 additions and 71 deletions

View File

@ -1081,7 +1081,8 @@ private:
SkScalar measure_text(SkGlyphCache*, const char* text, size_t length,
int* count, SkRect* bounds) const;
SkGlyphCache* detachCache(const SkDeviceProperties* deviceProperties, const SkMatrix*) const;
SkGlyphCache* detachCache(const SkDeviceProperties* deviceProperties, const SkMatrix*,
bool ignoreGamma) const;
void descriptorProc(const SkDeviceProperties* deviceProperties, const SkMatrix* deviceMatrix,
void (*proc)(SkTypeface*, const SkDescriptor*, void*),
@ -1131,6 +1132,7 @@ private:
}
friend class SkAutoGlyphCache;
friend class SkAutoGlyphCacheNoGamma;
friend class SkCanvas;
friend class SkDraw;
friend class SkGraphics; // So Term() can be called.

View File

@ -244,23 +244,8 @@ private:
friend class SkGlyphCache_Globals;
};
class SkAutoGlyphCache {
class SkAutoGlyphCacheBase {
public:
SkAutoGlyphCache(SkGlyphCache* cache) : fCache(cache) {}
SkAutoGlyphCache(SkTypeface* typeface, const SkDescriptor* desc) {
fCache = SkGlyphCache::DetachCache(typeface, desc);
}
SkAutoGlyphCache(const SkPaint& paint,
const SkDeviceProperties* deviceProperties,
const SkMatrix* matrix) {
fCache = paint.detachCache(deviceProperties, matrix);
}
~SkAutoGlyphCache() {
if (fCache) {
SkGlyphCache::AttachCache(fCache);
}
}
SkGlyphCache* getCache() const { return fCache; }
void release() {
@ -270,11 +255,61 @@ public:
}
}
private:
protected:
// Hide the constructors so we can't create one of these directly.
// Create SkAutoGlyphCache or SkAutoGlyphCacheNoCache instead.
SkAutoGlyphCacheBase(SkGlyphCache* cache) : fCache(cache) {}
SkAutoGlyphCacheBase(SkTypeface* typeface, const SkDescriptor* desc) {
fCache = SkGlyphCache::DetachCache(typeface, desc);
}
SkAutoGlyphCacheBase(const SkPaint& paint,
const SkDeviceProperties* deviceProperties,
const SkMatrix* matrix) {
fCache = NULL;
}
SkAutoGlyphCacheBase() {}
SkGlyphCache* fCache;
private:
static bool DetachProc(const SkGlyphCache*, void*);
};
class SkAutoGlyphCache : public SkAutoGlyphCacheBase {
public:
SkAutoGlyphCache(SkGlyphCache* cache) : SkAutoGlyphCacheBase(cache) {}
SkAutoGlyphCache(SkTypeface* typeface, const SkDescriptor* desc) :
SkAutoGlyphCacheBase(typeface, desc) {}
SkAutoGlyphCache(const SkPaint& paint,
const SkDeviceProperties* deviceProperties,
const SkMatrix* matrix) {
fCache = paint.detachCache(deviceProperties, matrix, false);
}
SkAutoGlyphCache() : SkAutoGlyphCacheBase() {
if (fCache) {
SkGlyphCache::AttachCache(fCache);
}
}
};
#define SkAutoGlyphCache(...) SK_REQUIRE_LOCAL_VAR(SkAutoGlyphCache)
class SkAutoGlyphCacheNoGamma : public SkAutoGlyphCacheBase {
public:
SkAutoGlyphCacheNoGamma(SkGlyphCache* cache) : SkAutoGlyphCacheBase(cache) {}
SkAutoGlyphCacheNoGamma(SkTypeface* typeface, const SkDescriptor* desc) :
SkAutoGlyphCacheBase(typeface, desc) {}
SkAutoGlyphCacheNoGamma(const SkPaint& paint,
const SkDeviceProperties* deviceProperties,
const SkMatrix* matrix) {
fCache = paint.detachCache(deviceProperties, matrix, true);
}
SkAutoGlyphCacheNoGamma() : SkAutoGlyphCacheBase() {
if (fCache) {
SkGlyphCache::AttachCache(fCache);
}
}
};
#define SkAutoGlyphCacheNoGamma(...) SK_REQUIRE_LOCAL_VAR(SkAutoGlyphCacheNoGamma)
#endif

View File

@ -136,6 +136,22 @@ public:
*/
PreBlend preBlend(SkColor color) const;
/**
* Get dimensions for the full table set, so it can be allocated as a block.
*/
void getGammaTableDimensions(int* tableWidth, int* numTables) const {
*tableWidth = 256;
*numTables = (1 << MAX_LUM_BITS);
}
/**
* Provides direct access to the full table set, so it can be uploaded
* into a texture.
*/
const uint8_t* getGammaTables() const {
return (const uint8_t*) fGammaTables;
}
private:
static const int MAX_LUM_BITS =
B_LUM_BITS > (R_LUM_BITS > G_LUM_BITS ? R_LUM_BITS : G_LUM_BITS)

View File

@ -1814,10 +1814,8 @@ void SkScalerContext::PostMakeRec(const SkPaint&, SkScalerContext::Rec* rec) {
/*
* ignoreGamma tells us that the caller just wants metrics that are unaffected
* by gamma correction, so we jam the luminance field to 0 (most common value
* for black text) in hopes that we get a cache hit easier. A better solution
* would be for the fontcache lookup to know to ignore the luminance field
* entirely, but not sure how to do that and keep it fast.
* by gamma correction, so we set the rec to ignore preblend: i.e. gamma = 1,
* contrast = 0, luminanceColor = transparent black.
*/
void SkPaint::descriptorProc(const SkDeviceProperties* deviceProperties,
const SkMatrix* deviceMatrix,
@ -1827,7 +1825,7 @@ void SkPaint::descriptorProc(const SkDeviceProperties* deviceProperties,
SkScalerContext::MakeRec(*this, deviceProperties, deviceMatrix, &rec);
if (ignoreGamma) {
rec.setLuminanceColor(0);
rec.ignorePreBlend();
}
size_t descSize = sizeof(rec);
@ -1951,9 +1949,10 @@ void SkPaint::descriptorProc(const SkDeviceProperties* deviceProperties,
}
SkGlyphCache* SkPaint::detachCache(const SkDeviceProperties* deviceProperties,
const SkMatrix* deviceMatrix) const {
const SkMatrix* deviceMatrix,
bool ignoreGamma) const {
SkGlyphCache* cache;
this->descriptorProc(deviceProperties, deviceMatrix, DetachDescProc, &cache, false);
this->descriptorProc(deviceProperties, deviceMatrix, DetachDescProc, &cache, ignoreGamma);
return cache;
}
@ -1969,6 +1968,33 @@ SkMaskGamma::PreBlend SkScalerContext::GetMaskPreBlend(const SkScalerContext::Re
return maskGamma.preBlend(rec.getLuminanceColor());
}
size_t SkScalerContext::GetGammaLUTSize(SkScalar contrast, SkScalar paintGamma,
SkScalar deviceGamma, int* width, int* height) {
SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
const SkMaskGamma& maskGamma = cachedMaskGamma(contrast,
paintGamma,
deviceGamma);
maskGamma.getGammaTableDimensions(width, height);
size_t size = (*width)*(*height)*sizeof(uint8_t);
return size;
}
void SkScalerContext::GetGammaLUTData(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma,
void* data) {
SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
const SkMaskGamma& maskGamma = cachedMaskGamma(contrast,
paintGamma,
deviceGamma);
int width, height;
maskGamma.getGammaTableDimensions(&width, &height);
size_t size = width*height*sizeof(uint8_t);
const uint8_t* gammaTables = maskGamma.getGammaTables();
memcpy(data, gammaTables, size);
}
///////////////////////////////////////////////////////////////////////////////
#include "SkStream.h"
@ -2557,7 +2583,7 @@ SkTextToPathIter::SkTextToPathIter( const char text[], size_t length,
fPaint.setPathEffect(NULL);
}
fCache = fPaint.detachCache(NULL, NULL);
fCache = fPaint.detachCache(NULL, NULL, false);
SkPaint::Style style = SkPaint::kFill_Style;
SkPathEffect* pe = NULL;

View File

@ -184,6 +184,17 @@ public:
void getPath(const SkGlyph&, SkPath*);
void getFontMetrics(SkPaint::FontMetrics*);
/** Return the size in bytes of the associated gamma lookup table
*/
static size_t GetGammaLUTSize(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma,
int* width, int* height);
/** Get the associated gamma lookup table. The 'data' pointer must point to pre-allocated
memory, with size in bytes greater than or equal to the return value of getGammaLUTSize().
*/
static void GetGammaLUTData(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma,
void* data);
#ifdef SK_BUILD_FOR_ANDROID
unsigned getBaseGlyphCount(SkUnichar charCode);

View File

@ -7,10 +7,12 @@
#include "GrDistanceFieldTextContext.h"
#include "GrAtlas.h"
#include "SkColorFilter.h"
#include "GrDrawTarget.h"
#include "GrDrawTargetCaps.h"
#include "GrFontScaler.h"
#include "SkGlyphCache.h"
#include "GrGpu.h"
#include "GrIndexBuffer.h"
#include "GrTextStrike.h"
#include "GrTextStrike_impl.h"
@ -43,6 +45,7 @@ GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context,
fEnableDFRendering = enable;
#endif
fStrike = NULL;
fGammaTexture = NULL;
fCurrTexture = NULL;
fCurrVertex = 0;
@ -53,6 +56,7 @@ GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context,
GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
this->flushGlyphs();
SkSafeSetNull(fGammaTexture);
}
bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint) {
@ -107,14 +111,26 @@ void GrDistanceFieldTextContext::flushGlyphs() {
SkASSERT(SkIsAlign4(fCurrVertex));
SkASSERT(fCurrTexture);
GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
// Effects could be stored with one of the cache objects (atlas?)
SkColor filteredColor;
SkColorFilter* colorFilter = fSkPaint.getColorFilter();
if (NULL != colorFilter) {
filteredColor = colorFilter->filterColor(fSkPaint.getColor());
} else {
filteredColor = fSkPaint.getColor();
}
if (fUseLCDText) {
GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
bool useBGR = SkDeviceProperties::Geometry::kBGR_Layout ==
fDeviceProperties.fGeometry.getLayout();
drawState->addCoverageEffect(GrDistanceFieldLCDTextureEffect::Create(
fCurrTexture,
params,
fGammaTexture,
gammaParams,
colorNoPreMul,
fContext->getMatrix().rectStaysRect() &&
fContext->getMatrix().isSimilarity(),
useBGR),
@ -133,13 +149,24 @@ void GrDistanceFieldTextContext::flushGlyphs() {
// paintAlpha
drawState->setColor(SkColorSetARGB(a, a, a, a));
// paintColor
drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaint.getColor()));
drawState->setBlendConstant(colorNoPreMul);
drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
} else {
drawState->addCoverageEffect(GrDistanceFieldTextureEffect::Create(fCurrTexture, params,
#ifdef SK_GAMMA_APPLY_TO_A8
U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.fGamma,
filteredColor);
drawState->addCoverageEffect(GrDistanceFieldTextureEffect::Create(
fCurrTexture, params,
fGammaTexture, gammaParams,
lum/255.f,
fContext->getMatrix().isSimilarity()),
kGlyphCoordsAttributeIndex)->unref();
#else
drawState->addCoverageEffect(GrDistanceFieldTextureEffect::Create(
fCurrTexture, params,
fContext->getMatrix().isSimilarity()),
kGlyphCoordsAttributeIndex)->unref();
#endif
// set back to normal in case we took LCD path previously.
drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
drawState->setColor(fPaint.getColor());
@ -356,7 +383,9 @@ inline void GrDistanceFieldTextContext::init(const GrPaint& paint, const SkPaint
fSkPaint.setLCDRenderText(false);
fSkPaint.setAutohinted(false);
fSkPaint.setHinting(SkPaint::kNormal_Hinting);
fSkPaint.setSubpixelText(true);
}
inline void GrDistanceFieldTextContext::finish() {
@ -365,6 +394,46 @@ inline void GrDistanceFieldTextContext::finish() {
GrTextContext::finish();
}
static void setup_gamma_texture(GrContext* context, const SkGlyphCache* cache,
const SkDeviceProperties& deviceProperties,
GrTexture** gammaTexture) {
if (NULL == *gammaTexture) {
int width, height;
size_t size;
#ifdef SK_GAMMA_CONTRAST
SkScalar contrast = SK_GAMMA_CONTRAST;
#else
SkScalar contrast = 0.5f;
#endif
SkScalar paintGamma = deviceProperties.fGamma;
SkScalar deviceGamma = deviceProperties.fGamma;
size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
&width, &height);
SkAutoTArray<uint8_t> data((int)size);
SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
// TODO: Update this to use the cache rather than directly creating a texture.
GrTextureDesc desc;
desc.fFlags = kDynamicUpdate_GrTextureFlagBit;
desc.fWidth = width;
desc.fHeight = height;
desc.fConfig = kAlpha_8_GrPixelConfig;
*gammaTexture = context->getGpu()->createTexture(desc, NULL, 0);
if (NULL == *gammaTexture) {
return;
}
context->writeTexturePixels(*gammaTexture,
0, 0, width, height,
(*gammaTexture)->config(), data.get(), 0,
GrContext::kDontFlush_PixelOpsFlag);
}
}
void GrDistanceFieldTextContext::drawText(const GrPaint& paint, const SkPaint& skPaint,
const char text[], size_t byteLength,
SkScalar x, SkScalar y) {
@ -382,9 +451,11 @@ void GrDistanceFieldTextContext::drawText(const GrPaint& paint, const SkPaint& s
SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL);
SkGlyphCache* cache = autoCache.getCache();
GrFontScaler* fontScaler = GetGrFontScaler(cache);
SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL);
SkGlyphCache* cache = autoCache.getCache();
GrFontScaler* fontScaler = GetGrFontScaler(cache);
setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
// need to measure first
// TODO - generate positions and pre-load cache as well?
@ -455,9 +526,11 @@ void GrDistanceFieldTextContext::drawPosText(const GrPaint& paint, const SkPaint
SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL);
SkGlyphCache* cache = autoCache.getCache();
GrFontScaler* fontScaler = GetGrFontScaler(cache);
SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL);
SkGlyphCache* cache = autoCache.getCache();
GrFontScaler* fontScaler = GetGrFontScaler(cache);
setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
const char* stop = text + byteLength;

View File

@ -34,6 +34,7 @@ private:
SkScalar fTextRatio;
bool fUseLCDText;
bool fEnableDFRendering;
GrTexture* fGammaTexture;
void init(const GrPaint&, const SkPaint&);
void drawPackedGlyph(GrGlyph::PackedID, SkFixed left, SkFixed top, GrFontScaler*);

View File

@ -15,6 +15,19 @@
#include "SkDistanceFieldGen.h"
// To get optical sizes people don't complain about when we blit correctly,
// we need to slightly bold each glyph. On the Mac, we need a larger bold value.
#if defined(SK_BUILD_FOR_MAC)
#define SK_DistanceFieldLCDFactor "0.33"
#define SK_DistanceFieldNonLCDFactor "0.25"
#else
#define SK_DistanceFieldLCDFactor "0.05"
#define SK_DistanceFieldNonLCDFactor "0.05"
#endif
// Assuming a radius of the diagonal of the fragment, hence a factor of sqrt(2)/2
#define SK_DistanceFieldAAFactor "0.7071"
class GrGLDistanceFieldTextureEffect : public GrGLVertexEffect {
public:
GrGLDistanceFieldTextureEffect(const GrBackendEffectFactory& factory,
@ -56,7 +69,8 @@ public:
kVec2f_GrSLType);
builder->fsCodeAppend(";\n");
builder->fsCodeAppend("\tfloat distance = "
SK_DistanceFieldMultiplier "*(texColor.r - " SK_DistanceFieldThreshold ");\n");
SK_DistanceFieldMultiplier "*(texColor.r - " SK_DistanceFieldThreshold ")"
"+ " SK_DistanceFieldNonLCDFactor ";\n");
// we adjust for the effect of the transformation on the distance by using
// the length of the gradient of the texture coordinates. We use st coordinates
@ -66,8 +80,7 @@ public:
builder->fsCodeAppend("\tfloat afwidth;\n");
if (dfTexEffect.isSimilarity()) {
// this gives us a smooth step across approximately one fragment
// (assuming a radius of the diagonal of the fragment, hence a factor of sqrt(2)/2)
builder->fsCodeAppend("\tafwidth = 0.7071*dFdx(st.x);\n");
builder->fsCodeAppend("\tafwidth = " SK_DistanceFieldAAFactor "*dFdx(st.x);\n");
} else {
builder->fsCodeAppend("\tvec2 Jdx = dFdx(st);\n");
builder->fsCodeAppend("\tvec2 Jdy = dFdy(st);\n");
@ -88,12 +101,25 @@ public:
builder->fsCodeAppend("\t uv_grad.x*Jdx.y + uv_grad.y*Jdy.y);\n");
// this gives us a smooth step across approximately one fragment
// (assuming a radius of the diagonal of the fragment, hence a factor of sqrt(2)/2)
builder->fsCodeAppend("\tafwidth = 0.7071*length(grad);\n");
builder->fsCodeAppend("\tafwidth = " SK_DistanceFieldAAFactor "*length(grad);\n");
}
builder->fsCodeAppend("\tfloat val = smoothstep(-afwidth, afwidth, distance);\n");
#ifdef SK_GAMMA_APPLY_TO_A8
// adjust based on gamma
const char* luminanceUniName = NULL;
// width, height, 1/(3*width)
fLuminanceUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
kFloat_GrSLType, "Luminance",
&luminanceUniName);
builder->fsCodeAppendf("\tuv = vec2(val, %s);\n", luminanceUniName);
builder->fsCodeAppend("\tvec4 gammaColor = ");
builder->fsAppendTextureLookup(samplers[1], "uv", kVec2f_GrSLType);
builder->fsCodeAppend(";\n");
builder->fsCodeAppend("\tval = gammaColor.r;\n");
#endif
builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
(GrGLSLExpr4(inputColor) * GrGLSLExpr1("val")).c_str());
}
@ -110,6 +136,15 @@ public:
SkIntToScalar(fTextureSize.width()),
SkIntToScalar(fTextureSize.height()));
}
#ifdef SK_GAMMA_APPLY_TO_A8
const GrDistanceFieldTextureEffect& dfTexEffect =
drawEffect.castEffect<GrDistanceFieldTextureEffect>();
float luminance = dfTexEffect.getLuminance();
if (luminance != fLuminance) {
uman.set1f(fLuminanceUni, luminance);
fLuminance = luminance;
}
#endif
}
static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
@ -122,6 +157,8 @@ public:
private:
GrGLUniformManager::UniformHandle fTextureSizeUni;
SkISize fTextureSize;
GrGLUniformManager::UniformHandle fLuminanceUni;
float fLuminance;
typedef GrGLVertexEffect INHERITED;
};
@ -130,10 +167,22 @@ private:
GrDistanceFieldTextureEffect::GrDistanceFieldTextureEffect(GrTexture* texture,
const GrTextureParams& params,
#ifdef SK_GAMMA_APPLY_TO_A8
GrTexture* gamma,
const GrTextureParams& gammaParams,
float luminance,
#endif
bool similarity)
: fTextureAccess(texture, params)
#ifdef SK_GAMMA_APPLY_TO_A8
, fGammaTextureAccess(gamma, gammaParams)
, fLuminance(luminance)
#endif
, fIsSimilarity(similarity) {
this->addTextureAccess(&fTextureAccess);
#ifdef SK_GAMMA_APPLY_TO_A8
this->addTextureAccess(&fGammaTextureAccess);
#endif
this->addVertexAttrib(kVec2f_GrSLType);
}
@ -166,6 +215,10 @@ GrEffectRef* GrDistanceFieldTextureEffect::TestCreate(SkRandom* random,
GrTexture* textures[]) {
int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
GrEffectUnitTest::kAlphaTextureIdx;
#ifdef SK_GAMMA_APPLY_TO_A8
int texIdx2 = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
GrEffectUnitTest::kAlphaTextureIdx;
#endif
static const SkShader::TileMode kTileModes[] = {
SkShader::kClamp_TileMode,
SkShader::kRepeat_TileMode,
@ -177,8 +230,16 @@ GrEffectRef* GrDistanceFieldTextureEffect::TestCreate(SkRandom* random,
};
GrTextureParams params(tileModes, random->nextBool() ? GrTextureParams::kBilerp_FilterMode :
GrTextureParams::kNone_FilterMode);
#ifdef SK_GAMMA_APPLY_TO_A8
GrTextureParams params2(tileModes, random->nextBool() ? GrTextureParams::kBilerp_FilterMode :
GrTextureParams::kNone_FilterMode);
#endif
return GrDistanceFieldTextureEffect::Create(textures[texIdx], params,
#ifdef SK_GAMMA_APPLY_TO_A8
textures[texIdx2], params2,
random->nextF(),
#endif
random->nextBool());
}
@ -211,7 +272,7 @@ public:
fsCoordName = fsCoordNamePtr;
const char* attrName0 =
builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0])->c_str();
builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0])->c_str();
builder->vsCodeAppendf("\t%s = %s;\n", vsCoordName, attrName0);
const char* textureSizeUniName = NULL;
@ -237,22 +298,24 @@ public:
builder->fsAppendTextureLookup(samplers[0], "uv", kVec2f_GrSLType);
builder->fsCodeAppend(";\n");
builder->fsCodeAppend("\tvec3 distance;\n");
builder->fsCodeAppend("\tdistance.y = "
SK_DistanceFieldMultiplier "*(texColor.r - " SK_DistanceFieldThreshold ");\n");
builder->fsCodeAppend("\tdistance.y = texColor.r;\n");
// red is distance to left offset
builder->fsCodeAppend("\tvec2 uv_adjusted = uv - offset;\n");
builder->fsCodeAppend("\ttexColor = ");
builder->fsAppendTextureLookup(samplers[0], "uv_adjusted", kVec2f_GrSLType);
builder->fsCodeAppend(";\n");
builder->fsCodeAppend("\tdistance.x = "
SK_DistanceFieldMultiplier "*(texColor.r - " SK_DistanceFieldThreshold ");\n");
builder->fsCodeAppend("\tdistance.x = texColor.r;\n");
// blue is distance to right offset
builder->fsCodeAppend("\tuv_adjusted = uv + offset;\n");
builder->fsCodeAppend("\ttexColor = ");
builder->fsAppendTextureLookup(samplers[0], "uv_adjusted", kVec2f_GrSLType);
builder->fsCodeAppend(";\n");
builder->fsCodeAppend("\tdistance.z = "
SK_DistanceFieldMultiplier "*(texColor.r - " SK_DistanceFieldThreshold ");\n");
builder->fsCodeAppend("\tdistance.z = texColor.r;\n");
builder->fsCodeAppend("\tdistance = "
"vec3(" SK_DistanceFieldMultiplier ")*(distance - vec3(" SK_DistanceFieldThreshold"))"
"+ vec3(" SK_DistanceFieldLCDFactor ");\n");
// we adjust for the effect of the transformation on the distance by using
// the length of the gradient of the texture coordinates. We use st coordinates
// to ensure we're mapping 1:1 from texel space to pixel space.
@ -264,8 +327,7 @@ public:
builder->fsCodeAppend("\tfloat afwidth;\n");
if (dfTexEffect.isUniformScale()) {
// this gives us a smooth step across approximately one fragment
// (assuming a radius of the diagonal of the fragment, hence a factor of sqrt(2)/2)
builder->fsCodeAppend("\tafwidth = 0.7071*dx;\n");
builder->fsCodeAppend("\tafwidth = " SK_DistanceFieldAAFactor "*dx;\n");
} else {
builder->fsCodeAppend("\tvec2 uv_grad;\n");
if (builder->ctxInfo().caps()->dropsTileOnZeroDivide()) {
@ -283,12 +345,36 @@ public:
builder->fsCodeAppend("\t uv_grad.x*Jdx.y + uv_grad.y*Jdy.y);\n");
// this gives us a smooth step across approximately one fragment
// (assuming a radius of the diagonal of the fragment, hence a factor of sqrt(2)/2)
builder->fsCodeAppend("\tafwidth = 0.7071*length(grad);\n");
builder->fsCodeAppend("\tafwidth = " SK_DistanceFieldAAFactor "*length(grad);\n");
}
builder->fsCodeAppend("\tvec4 val = vec4(smoothstep(vec3(-afwidth), vec3(afwidth), distance), 1.0);\n");
// adjust based on gamma
const char* textColorUniName = NULL;
// width, height, 1/(3*width)
fTextColorUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
kVec3f_GrSLType, "TextColor",
&textColorUniName);
builder->fsCodeAppendf("\tuv = vec2(val.x, %s.x);\n", textColorUniName);
builder->fsCodeAppend("\tvec4 gammaColor = ");
builder->fsAppendTextureLookup(samplers[1], "uv", kVec2f_GrSLType);
builder->fsCodeAppend(";\n");
builder->fsCodeAppend("\tval.x = gammaColor.r;\n");
builder->fsCodeAppendf("\tuv = vec2(val.y, %s.y);\n", textColorUniName);
builder->fsCodeAppend("\tgammaColor = ");
builder->fsAppendTextureLookup(samplers[1], "uv", kVec2f_GrSLType);
builder->fsCodeAppend(";\n");
builder->fsCodeAppend("\tval.y = gammaColor.r;\n");
builder->fsCodeAppendf("\tuv = vec2(val.z, %s.z);\n", textColorUniName);
builder->fsCodeAppend("\tgammaColor = ");
builder->fsAppendTextureLookup(samplers[1], "uv", kVec2f_GrSLType);
builder->fsCodeAppend(";\n");
builder->fsCodeAppend("\tval.z = gammaColor.r;\n");
builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
(GrGLSLExpr4(inputColor) * GrGLSLExpr4("val")).c_str());
}
@ -296,12 +382,13 @@ public:
virtual void setData(const GrGLUniformManager& uman,
const GrDrawEffect& drawEffect) SK_OVERRIDE {
SkASSERT(fTextureSizeUni.isValid());
SkASSERT(fTextColorUni.isValid());
const GrDistanceFieldLCDTextureEffect& dfTexEffect =
drawEffect.castEffect<GrDistanceFieldLCDTextureEffect>();
GrTexture* texture = drawEffect.effect()->get()->texture(0);
if (texture->width() != fTextureSize.width() ||
texture->height() != fTextureSize.height()) {
const GrDistanceFieldLCDTextureEffect& dfTexEffect =
drawEffect.castEffect<GrDistanceFieldLCDTextureEffect>();
fTextureSize = SkISize::Make(texture->width(), texture->height());
float delta = 1.0f/(3.0f*texture->width());
if (dfTexEffect.useBGR()) {
@ -312,40 +399,55 @@ public:
SkIntToScalar(fTextureSize.height()),
delta);
}
GrColor textColor = dfTexEffect.getTextColor();
if (textColor != fTextColor) {
static const float ONE_OVER_255 = 1.f / 255.f;
uman.set3f(fTextColorUni,
GrColorUnpackR(textColor) * ONE_OVER_255,
GrColorUnpackG(textColor) * ONE_OVER_255,
GrColorUnpackB(textColor) * ONE_OVER_255);
fTextColor = textColor;
}
}
static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
const GrDistanceFieldLCDTextureEffect& dfTexEffect =
drawEffect.castEffect<GrDistanceFieldLCDTextureEffect>();
int uniformScale = dfTexEffect.isUniformScale() ? 0x01 : 0x00;
int useBGR = dfTexEffect.useBGR() ? 0x10 : 0x00;
return uniformScale | useBGR;
return dfTexEffect.isUniformScale() ? 0x01 : 0x00;;
}
private:
GrGLUniformManager::UniformHandle fTextureSizeUni;
SkISize fTextureSize;
GrGLUniformManager::UniformHandle fTextColorUni;
SkColor fTextColor;
typedef GrGLVertexEffect INHERITED;
};
///////////////////////////////////////////////////////////////////////////////
GrDistanceFieldLCDTextureEffect::GrDistanceFieldLCDTextureEffect(GrTexture* texture,
const GrTextureParams& params,
bool uniformScale,
bool useBGR)
GrDistanceFieldLCDTextureEffect::GrDistanceFieldLCDTextureEffect(
GrTexture* texture, const GrTextureParams& params,
GrTexture* gamma, const GrTextureParams& gParams,
SkColor textColor,
bool uniformScale, bool useBGR)
: fTextureAccess(texture, params)
, fGammaTextureAccess(gamma, gParams)
, fTextColor(textColor)
, fUniformScale(uniformScale)
, fUseBGR(useBGR) {
this->addTextureAccess(&fTextureAccess);
this->addTextureAccess(&fGammaTextureAccess);
this->addVertexAttrib(kVec2f_GrSLType);
}
bool GrDistanceFieldLCDTextureEffect::onIsEqual(const GrEffect& other) const {
const GrDistanceFieldLCDTextureEffect& cte = CastEffect<GrDistanceFieldLCDTextureEffect>(other);
return fTextureAccess == cte.fTextureAccess;
const GrDistanceFieldLCDTextureEffect& cte =
CastEffect<GrDistanceFieldLCDTextureEffect>(other);
return (fTextureAccess == cte.fTextureAccess && fGammaTextureAccess == cte.fGammaTextureAccess);
}
void GrDistanceFieldLCDTextureEffect::getConstantColorComponents(GrColor* color,
@ -371,7 +473,9 @@ GrEffectRef* GrDistanceFieldLCDTextureEffect::TestCreate(SkRandom* random,
const GrDrawTargetCaps&,
GrTexture* textures[]) {
int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
GrEffectUnitTest::kAlphaTextureIdx;
GrEffectUnitTest::kAlphaTextureIdx;
int texIdx2 = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
GrEffectUnitTest::kAlphaTextureIdx;
static const SkShader::TileMode kTileModes[] = {
SkShader::kClamp_TileMode,
SkShader::kRepeat_TileMode,
@ -383,7 +487,14 @@ GrEffectRef* GrDistanceFieldLCDTextureEffect::TestCreate(SkRandom* random,
};
GrTextureParams params(tileModes, random->nextBool() ? GrTextureParams::kBilerp_FilterMode :
GrTextureParams::kNone_FilterMode);
GrTextureParams params2(tileModes, random->nextBool() ? GrTextureParams::kBilerp_FilterMode :
GrTextureParams::kNone_FilterMode);
GrColor textColor = GrColorPackRGBA(random->nextULessThan(256),
random->nextULessThan(256),
random->nextULessThan(256),
random->nextULessThan(256));
return GrDistanceFieldLCDTextureEffect::Create(textures[texIdx], params,
textures[texIdx2], params2,
textColor,
random->nextBool(), random->nextBool());
}

View File

@ -18,12 +18,23 @@ class GrGLDistanceFieldLCDTextureEffect;
* The output color of this effect is a modulation of the input color and a sample from a
* distance field texture (using a smoothed step function near 0.5).
* It allows explicit specification of the filtering and wrap modes (GrTextureParams). The input
* coords are a custom attribute.
* coords are a custom attribute. Gamma correction is handled via a texture LUT.
*/
class GrDistanceFieldTextureEffect : public GrVertexEffect {
public:
static GrEffectRef* Create(GrTexture* tex, const GrTextureParams& params, bool similarity) {
AutoEffectUnref effect(SkNEW_ARGS(GrDistanceFieldTextureEffect, (tex, params, similarity)));
#ifdef SK_GAMMA_APPLY_TO_A8
static GrEffectRef* Create(GrTexture* tex, const GrTextureParams& params,
GrTexture* gamma, const GrTextureParams& gammaParams, float lum,
bool similarity) {
AutoEffectUnref effect(SkNEW_ARGS(GrDistanceFieldTextureEffect, (tex, params,
gamma, gammaParams, lum,
similarity)));
#else
static GrEffectRef* Create(GrTexture* tex, const GrTextureParams& params,
bool similarity) {
AutoEffectUnref effect(SkNEW_ARGS(GrDistanceFieldTextureEffect, (tex, params,
similarity)));
#endif
return CreateEffectRef(effect);
}
@ -32,6 +43,9 @@ public:
static const char* Name() { return "DistanceFieldTexture"; }
virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
#ifdef SK_GAMMA_APPLY_TO_A8
float getLuminance() const { return fLuminance; }
#endif
bool isSimilarity() const { return fIsSimilarity; }
typedef GrGLDistanceFieldTextureEffect GLEffect;
@ -40,11 +54,18 @@ public:
private:
GrDistanceFieldTextureEffect(GrTexture* texture, const GrTextureParams& params,
#ifdef SK_GAMMA_APPLY_TO_A8
GrTexture* gamma, const GrTextureParams& gammaParams, float lum,
#endif
bool uniformScale);
virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE;
GrTextureAccess fTextureAccess;
#ifdef SK_GAMMA_APPLY_TO_A8
GrTextureAccess fGammaTextureAccess;
float fLuminance;
#endif
bool fIsSimilarity;
GR_DECLARE_EFFECT_TEST;
@ -56,14 +77,17 @@ private:
* The output color of this effect is a modulation of the input color and samples from a
* distance field texture (using a smoothed step function near 0.5), adjusted for LCD displays.
* It allows explicit specification of the filtering and wrap modes (GrTextureParams). The input
* coords are a custom attribute.
* coords are a custom attribute. Gamma correction is handled via a texture LUT.
*/
class GrDistanceFieldLCDTextureEffect : public GrVertexEffect {
public:
static GrEffectRef* Create(GrTexture* tex, const GrTextureParams& params,
GrTexture* gamma, const GrTextureParams& gammaParams,
SkColor textColor,
bool uniformScale, bool useBGR) {
AutoEffectUnref effect(SkNEW_ARGS(GrDistanceFieldLCDTextureEffect,
(tex, params, uniformScale, useBGR)));
(tex, params, gamma, gammaParams, textColor, uniformScale,
useBGR)));
return CreateEffectRef(effect);
}
@ -72,6 +96,7 @@ public:
static const char* Name() { return "DistanceFieldLCDTexture"; }
virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
GrColor getTextColor() const { return fTextColor; }
bool isUniformScale() const { return fUniformScale; }
bool useBGR() const { return fUseBGR; }
@ -81,11 +106,15 @@ public:
private:
GrDistanceFieldLCDTextureEffect(GrTexture* texture, const GrTextureParams& params,
GrTexture* gamma, const GrTextureParams& gammaParams,
SkColor textColor,
bool uniformScale, bool useBGR);
virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE;
GrTextureAccess fTextureAccess;
GrTextureAccess fGammaTextureAccess;
GrColor fTextColor;
bool fUniformScale;
bool fUseBGR;
@ -94,5 +123,4 @@ private:
typedef GrVertexEffect INHERITED;
};
#endif