aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/SkPaint.cpp
diff options
context:
space:
mode:
authorGravatar bungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-07-30 20:40:50 +0000
committerGravatar bungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-07-30 20:40:50 +0000
commit97efada074e4806479f1350ab1508939c2fdcb53 (patch)
tree374659467fad6bb854be612ec5bcb8398a44c97a /src/core/SkPaint.cpp
parentfa3d892f2e3beba39ccb9957be78aad529521740 (diff)
Gamma correcting masks.
Diffstat (limited to 'src/core/SkPaint.cpp')
-rw-r--r--src/core/SkPaint.cpp197
1 files changed, 121 insertions, 76 deletions
diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp
index f240737f7e..04a8f5a756 100644
--- a/src/core/SkPaint.cpp
+++ b/src/core/SkPaint.cpp
@@ -12,6 +12,7 @@
#include "SkFontHost.h"
#include "SkImageFilter.h"
#include "SkMaskFilter.h"
+#include "SkMaskGamma.h"
#include "SkPathEffect.h"
#include "SkRasterizer.h"
#include "SkShader.h"
@@ -1432,7 +1433,6 @@ static bool justAColor(const SkPaint& paint, SkColor* color) {
return true;
}
-#ifdef SK_USE_COLOR_LUMINANCE
static SkColor computeLuminanceColor(const SkPaint& paint) {
SkColor c;
if (!justAColor(paint, &c)) {
@@ -1443,53 +1443,6 @@ static SkColor computeLuminanceColor(const SkPaint& paint) {
#define assert_byte(x) SkASSERT(0 == ((x) >> 8))
-static U8CPU reduce_lumbits(U8CPU x) {
- static const uint8_t gReduceBits[] = {
- 0x0, 0x55, 0xAA, 0xFF
- };
- assert_byte(x);
- return gReduceBits[x >> 6];
-}
-
-static unsigned computeLuminance(SkColor c) {
- int r = SkColorGetR(c);
- int g = SkColorGetG(c);
- int b = SkColorGetB(c);
- // compute luminance
- // R=0.2126 G=0.7152 B=0.0722
- // scaling by 127 yields 27, 92, 9
- int luminance = r * 27 + g * 92 + b * 9;
- luminance >>= 7;
- assert_byte(luminance);
- return luminance;
-}
-
-#else
-// returns 0..kLuminance_Max
-static unsigned computeLuminance(const SkPaint& paint) {
- SkColor c;
- if (justAColor(paint, &c)) {
- int r = SkColorGetR(c);
- int g = SkColorGetG(c);
- int b = SkColorGetB(c);
- // compute luminance
- // R=0.2126 G=0.7152 B=0.0722
- // scaling by 127 yields 27, 92, 9
-#if 1
- int luminance = r * 27 + g * 92 + b * 9;
- luminance >>= 15 - SkScalerContext::kLuminance_Bits;
-#else
- int luminance = r * 2 + g * 5 + b * 1;
- luminance >>= 11 - SkScalerContext::kLuminance_Bits;
-#endif
- SkASSERT(luminance <= SkScalerContext::kLuminance_Max);
- return luminance;
- }
- // if we're not a single color, return the middle of the luminance range
- return SkScalerContext::kLuminance_Max >> 1;
-}
-#endif
-
// Beyond this size, LCD doesn't appreciably improve quality, but it always
// cost more RAM and draws slower, so we set a cap.
#ifndef SK_MAX_SIZE_FOR_LCDTEXT
@@ -1518,6 +1471,20 @@ static SkScalar sk_relax(SkScalar x) {
#endif
}
+//#define SK_GAMMA_SRGB
+#ifndef SK_GAMMA_CONTRAST
+ /**
+ * A value of 0.5 for SK_GAMMA_CONTRAST appears to be a good compromise.
+ * With lower values small text appears washed out (though correctly so).
+ * With higher values lcd fringing is worse and the smoothing effect of
+ * partial coverage is diminished.
+ */
+ #define SK_GAMMA_CONTRAST (0.5f)
+#endif
+#ifndef SK_GAMMA_EXPONENT
+ #define SK_GAMMA_EXPONENT (2.2f)
+#endif
+
void SkScalerContext::MakeRec(const SkPaint& paint,
const SkMatrix* deviceMatrix, Rec* rec) {
SkASSERT(deviceMatrix == NULL || !deviceMatrix->hasPerspective());
@@ -1619,12 +1586,17 @@ void SkScalerContext::MakeRec(const SkPaint& paint,
// these modify fFlags, so do them after assigning fFlags
rec->setHinting(computeHinting(paint));
-#ifdef SK_USE_COLOR_LUMINANCE
+
rec->setLuminanceColor(computeLuminanceColor(paint));
+#ifdef SK_GAMMA_SRGB
+ rec->setDeviceGamma(0);
+ rec->setPaintGamma(0);
#else
- rec->setLuminanceBits(computeLuminance(paint));
+ rec->setDeviceGamma(SkFloatToScalar(SK_GAMMA_EXPONENT));
+ rec->setPaintGamma(SkFloatToScalar(SK_GAMMA_EXPONENT));
#endif
-
+ rec->setContrast(SkFloatToScalar(SK_GAMMA_CONTRAST));
+
/* Allow the fonthost to modify our rec before we use it as a key into the
cache. This way if we're asking for something that they will ignore,
they can modify our rec up front, so we don't create duplicate cache
@@ -1636,46 +1608,111 @@ void SkScalerContext::MakeRec(const SkPaint& paint,
}
/**
- * We ensure that the rec is self-consistent and efficient (where possible)
+ * In order to call cachedDeviceLuminance, cachedPaintLuminance, or
+ * cachedMaskGamma the caller must hold the gMaskGammaCacheMutex and continue
+ * to hold it until the returned pointer is refed or forgotten.
*/
-void SkScalerContext::PostMakeRec(SkScalerContext::Rec* rec) {
+SK_DECLARE_STATIC_MUTEX(gMaskGammaCacheMutex);
+/**
+ * The caller must hold the gMaskGammaCacheMutex and continue to hold it until
+ * the returned SkColorSpaceLuminance pointer is refed or forgotten.
+ */
+static SkColorSpaceLuminance* cachedDeviceLuminance(SkScalar gammaExponent) {
+ static SkColorSpaceLuminance* gDeviceLuminance = NULL;
+ static SkScalar gGammaExponent = SK_ScalarMin;
+ if (gGammaExponent != gammaExponent) {
+ if (0 == gammaExponent) {
+ gDeviceLuminance = SkNEW(SkSRGBLuminance);
+ } else {
+ gDeviceLuminance = SkNEW_ARGS(SkGammaLuminance, (gammaExponent));
+ }
+ gGammaExponent = gammaExponent;
+ }
+ return gDeviceLuminance;
+}
+
+/**
+ * The caller must hold the gMaskGammaCacheMutex and continue to hold it until
+ * the returned SkColorSpaceLuminance pointer is refed or forgotten.
+ */
+static SkColorSpaceLuminance* cachedPaintLuminance(SkScalar gammaExponent) {
+ static SkColorSpaceLuminance* gPaintLuminance = NULL;
+ static SkScalar gGammaExponent = SK_ScalarMin;
+ if (gGammaExponent != gammaExponent) {
+ if (0 == gammaExponent) {
+ gPaintLuminance = SkNEW(SkSRGBLuminance);
+ } else {
+ gPaintLuminance = SkNEW_ARGS(SkGammaLuminance, (gammaExponent));
+ }
+ gGammaExponent = gammaExponent;
+ }
+ return gPaintLuminance;
+}
+
+/**
+ * The caller must hold the gMaskGammaCacheMutex and continue to hold it until
+ * the returned SkMaskGamma pointer is refed or forgotten.
+ */
+static SkMaskGamma* cachedMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) {
+ static SkMaskGamma* gMaskGamma = NULL;
+ static SkScalar gContrast = SK_ScalarMin;
+ static SkScalar gPaintGamma = SK_ScalarMin;
+ static SkScalar gDeviceGamma = SK_ScalarMin;
+ if (gContrast != contrast || gPaintGamma != paintGamma || gDeviceGamma != deviceGamma) {
+ SkSafeUnref(gMaskGamma);
+ SkColorSpaceLuminance* paintLuminance = cachedPaintLuminance(paintGamma);
+ SkColorSpaceLuminance* deviceLuminance = cachedDeviceLuminance(deviceGamma);
+ gMaskGamma = SkNEW_ARGS(SkMaskGamma, (contrast, *paintLuminance, *deviceLuminance));
+ gContrast = contrast;
+ gPaintGamma = paintGamma;
+ gDeviceGamma = deviceGamma;
+ }
+ return gMaskGamma;
+}
+
+/**
+ * We ensure that the rec is self-consistent and efficient (where possible)
+ */
+void SkScalerContext::PostMakeRec(const SkPaint& paint, SkScalerContext::Rec* rec) {
/**
* If we're asking for A8, we force the colorlum to be gray, since that
- * that limits the number of unique entries, and the scaler will only
- * look at the lum of one of them.
+ * limits the number of unique entries, and the scaler will only look at
+ * the lum of one of them.
*/
switch (rec->fMaskFormat) {
case SkMask::kLCD16_Format:
case SkMask::kLCD32_Format: {
-#ifdef SK_USE_COLOR_LUMINANCE
// filter down the luminance color to a finite number of bits
- SkColor c = rec->getLuminanceColor();
- c = SkColorSetRGB(reduce_lumbits(SkColorGetR(c)),
- reduce_lumbits(SkColorGetG(c)),
- reduce_lumbits(SkColorGetB(c)));
- rec->setLuminanceColor(c);
-#endif
+ SkColor color = rec->getLuminanceColor();
+ SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
+ SkMaskGamma* maskGamma = cachedMaskGamma(rec->getContrast(),
+ rec->getPaintGamma(),
+ rec->getDeviceGamma());
+ rec->setLuminanceColor(maskGamma->cannonicalColor(color));
break;
}
case SkMask::kA8_Format: {
-#ifdef SK_USE_COLOR_LUMINANCE
// filter down the luminance to a single component, since A8 can't
// use per-component information
- unsigned lum = computeLuminance(rec->getLuminanceColor());
+
+ SkColor color = rec->getLuminanceColor();
+ SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
+ U8CPU lum = cachedPaintLuminance(rec->getPaintGamma())->computeLuminance(color);
+ // HACK: Prevents green from being pre-blended as white.
+ lum -= ((255 - lum) * lum) / 255;
+
// reduce to our finite number of bits
- lum = reduce_lumbits(lum);
- rec->setLuminanceColor(SkColorSetRGB(lum, lum, lum));
-#endif
+ SkMaskGamma* maskGamma = cachedMaskGamma(rec->getContrast(),
+ rec->getPaintGamma(),
+ rec->getDeviceGamma());
+ color = SkColorSetRGB(lum, lum, lum);
+ rec->setLuminanceColor(maskGamma->cannonicalColor(color));
break;
}
case SkMask::kBW_Format:
// No need to differentiate gamma if we're BW
-#ifdef SK_USE_COLOR_LUMINANCE
rec->setLuminanceColor(0);
-#else
- rec->setLuminanceBits(0);
-#endif
break;
}
}
@@ -1700,11 +1737,7 @@ void SkPaint::descriptorProc(const SkMatrix* deviceMatrix,
SkScalerContext::MakeRec(*this, deviceMatrix, &rec);
if (ignoreGamma) {
-#ifdef SK_USE_COLOR_LUMINANCE
rec.setLuminanceColor(0);
-#else
- rec.setLuminanceBits(0);
-#endif
}
size_t descSize = sizeof(rec);
@@ -1739,7 +1772,7 @@ void SkPaint::descriptorProc(const SkMatrix* deviceMatrix,
///////////////////////////////////////////////////////////////////////////
// Now that we're done tweaking the rec, call the PostMakeRec cleanup
- SkScalerContext::PostMakeRec(&rec);
+ SkScalerContext::PostMakeRec(*this, &rec);
descSize += SkDescriptor::ComputeOverhead(entryCount);
@@ -1814,6 +1847,18 @@ SkGlyphCache* SkPaint::detachCache(const SkMatrix* deviceMatrix) const {
return cache;
}
+/**
+ * Expands fDeviceGamma, fPaintGamma, fContrast, and fLumBits into a mask pre-blend.
+ */
+//static
+SkMaskGamma::PreBlend SkScalerContext::GetMaskPreBlend(const SkScalerContext::Rec& rec) {
+ SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
+ SkMaskGamma* maskGamma = cachedMaskGamma(rec.getContrast(),
+ rec.getPaintGamma(),
+ rec.getDeviceGamma());
+ return maskGamma->preBlend(rec.getLuminanceColor());
+}
+
///////////////////////////////////////////////////////////////////////////////
#include "SkStream.h"