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:
parent
8405ef9854
commit
4d517fdbb1
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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*);
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user