aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
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
parentfa3d892f2e3beba39ccb9957be78aad529521740 (diff)
Gamma correcting masks.
Diffstat (limited to 'src')
-rw-r--r--src/core/SkMaskGamma.cpp91
-rw-r--r--src/core/SkMaskGamma.h211
-rw-r--r--src/core/SkPaint.cpp197
-rw-r--r--src/core/SkScalerContext.cpp167
-rw-r--r--src/ports/SkFontHost_FreeType.cpp206
-rw-r--r--src/ports/SkFontHost_gamma.cpp107
-rw-r--r--src/ports/SkFontHost_gamma_none.cpp23
-rw-r--r--src/ports/SkFontHost_mac_coretext.cpp555
-rwxr-xr-xsrc/ports/SkFontHost_win.cpp256
-rw-r--r--src/ports/sk_predefined_gamma.h51
10 files changed, 900 insertions, 964 deletions
diff --git a/src/core/SkMaskGamma.cpp b/src/core/SkMaskGamma.cpp
new file mode 100644
index 0000000000..47903fbdbb
--- /dev/null
+++ b/src/core/SkMaskGamma.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTypes.h"
+
+#include "SkColor.h"
+#include "SkFloatingPoint.h"
+#include "SkMaskGamma.h"
+
+SkScalar SkSRGBLuminance::toLuma(SkScalar luminance) const {
+ //The magic numbers are derived from the sRGB specification.
+ //See http://www.color.org/chardata/rgb/srgb.xalter .
+ if (luminance <= SkFloatToScalar(0.04045f)) {
+ return luminance / SkFloatToScalar(12.92f);
+ }
+ return SkScalarPow((luminance + SkFloatToScalar(0.055f)) / SkFloatToScalar(1.055f),
+ SkFloatToScalar(2.4f));
+}
+
+SkScalar SkSRGBLuminance::fromLuma(SkScalar luma) const {
+ //The magic numbers are derived from the sRGB specification.
+ //See http://www.color.org/chardata/rgb/srgb.xalter .
+ if (luma <= SkFloatToScalar(0.0031308f)) {
+ return luma * SkFloatToScalar(12.92f);
+ }
+ return SkFloatToScalar(1.055f) * SkScalarPow(luma, SkScalarInvert(SkFloatToScalar(2.4f)))
+ - SkFloatToScalar(0.055f);
+}
+
+SkGammaLuminance::SkGammaLuminance(SkScalar gamma)
+ : fGamma(gamma)
+ , fGammaInverse(SkScalarInvert(gamma)) {
+}
+
+float SkGammaLuminance::toLuma(SkScalar luminance) const {
+ return SkScalarPow(luminance, fGamma);
+}
+
+float SkGammaLuminance::fromLuma(SkScalar luma) const {
+ return SkScalarPow(luma, fGammaInverse);
+}
+
+static float apply_contrast(float srca, float contrast) {
+ return srca + ((1.0f - srca) * contrast * srca);
+}
+
+void SkTMaskGamma_build_correcting_lut(uint8_t table[256], U8CPU srcI, SkScalar contrast,
+ const SkColorSpaceLuminance& srcConvert,
+ const SkColorSpaceLuminance& dstConvert) {
+ const float src = (float)srcI / 255.0f;
+ const float linSrc = srcConvert.toLuma(src);
+ //Guess at the dst.
+ const float linDst = 1.0f - linSrc;
+ const float dst = dstConvert.fromLuma(linDst);
+
+ //Contrast value tapers off to 0 as the src luminance becomes white
+ const float adjustedContrast = SkScalarToFloat(contrast) * linDst;
+ const float step = 1.0f / 255.0f;
+
+ //Remove discontinuity and instability when src is close to dst.
+ //The value 1/256 is arbitrary and appears to contain the instability.
+ if (fabs(src - dst) < (1.0f / 256.0f)) {
+ float rawSrca = 0.0f;
+ for (int i = 0; i < 256; ++i, rawSrca += step) {
+ float srca = apply_contrast(rawSrca, adjustedContrast);
+ table[i] = SkToU8(sk_float_round2int(255.0f * srca));
+ }
+ } else {
+ float rawSrca = 0.0f;
+ for (int i = 0; i < 256; ++i, rawSrca += step) {
+ float srca = apply_contrast(rawSrca, adjustedContrast);
+ SkASSERT(srca <= 1.0f);
+ float dsta = 1.0f - srca;
+
+ //Calculate the output we want.
+ float linOut = (linSrc * srca + dsta * linDst);
+ SkASSERT(linOut <= 1.0f);
+ float out = dstConvert.fromLuma(linOut);
+
+ //Undo what the blit blend will do.
+ float result = (out - dst) / (src - dst);
+ SkASSERT(sk_float_round2int(255.0f * result) <= 255);
+
+ table[i] = SkToU8(sk_float_round2int(255.0f * result));
+ }
+ }
+}
diff --git a/src/core/SkMaskGamma.h b/src/core/SkMaskGamma.h
new file mode 100644
index 0000000000..e8fa703f36
--- /dev/null
+++ b/src/core/SkMaskGamma.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkMaskGamma_DEFINED
+#define SkMaskGamma_DEFINED
+
+#include "SkTypes.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkRefCnt.h"
+
+/**
+ * SkColorSpaceLuminance is used to convert luminances to and from linear and
+ * perceptual color spaces.
+ *
+ * Luma is used to specify a linear luminance value [0.0, 1.0].
+ * Luminance is used to specify a luminance value in an arbitrary color space [0.0, 1.0].
+ */
+class SkColorSpaceLuminance : SkNoncopyable {
+public:
+ /** Converts a color component luminance in the color space to a linear luma. */
+ virtual SkScalar toLuma(SkScalar luminance) const = 0;
+ /** Converts a linear luma to a color component luminance in the color space. */
+ virtual SkScalar fromLuma(SkScalar luma) const = 0;
+
+ /** Converts a color to a luminance value. */
+ U8CPU computeLuminance(SkColor c) const {
+ SkScalar r = toLuma(SkIntToScalar(SkColorGetR(c)) / 255);
+ SkScalar g = toLuma(SkIntToScalar(SkColorGetG(c)) / 255);
+ SkScalar b = toLuma(SkIntToScalar(SkColorGetB(c)) / 255);
+ SkScalar luma = r * SkFloatToScalar(SK_LUM_COEFF_R) +
+ g * SkFloatToScalar(SK_LUM_COEFF_G) +
+ b * SkFloatToScalar(SK_LUM_COEFF_B);
+ SkASSERT(luma <= SK_Scalar1);
+ return SkScalarRoundToInt(fromLuma(luma) * 255);
+ }
+};
+
+class SkSRGBLuminance : public SkColorSpaceLuminance {
+public:
+ SkScalar toLuma(SkScalar luminance) const SK_OVERRIDE;
+ SkScalar fromLuma(SkScalar luma) const SK_OVERRIDE;
+};
+
+class SkGammaLuminance : public SkColorSpaceLuminance {
+public:
+ SkGammaLuminance(SkScalar gamma);
+ SkScalar toLuma(SkScalar luminance) const SK_OVERRIDE;
+ SkScalar fromLuma(SkScalar luma) const SK_OVERRIDE;
+private:
+ SkScalar fGamma;
+ SkScalar fGammaInverse;
+};
+
+///@{
+/**
+ * Scales base <= 2^N-1 to 2^8-1
+ * @param N [1, 8] the number of bits used by base.
+ * @param base the number to be scaled to [0, 255].
+ */
+template<U8CPU N> static inline U8CPU sk_t_scale255(U8CPU base) {
+ base <<= (8 - N);
+ U8CPU lum = base;
+ for (unsigned int i = N; i < 8; i += N) {
+ lum |= base >> i;
+ }
+ return lum;
+}
+template<> /*static*/ inline U8CPU sk_t_scale255<1>(U8CPU base) {
+ return base * 0xFF;
+}
+template<> /*static*/ inline U8CPU sk_t_scale255<2>(U8CPU base) {
+ return base * 0x55;
+}
+template<> /*static*/ inline U8CPU sk_t_scale255<4>(U8CPU base) {
+ return base * 0x11;
+}
+template<> /*static*/ inline U8CPU sk_t_scale255<8>(U8CPU base) {
+ return base;
+}
+///@}
+
+template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskPreBlend;
+
+void SkTMaskGamma_build_correcting_lut(uint8_t table[256], U8CPU srcI, SkScalar contrast,
+ const SkColorSpaceLuminance& srcConvert,
+ const SkColorSpaceLuminance& dstConvert);
+
+/**
+ * A regular mask contains linear alpha values. A gamma correcting mask
+ * contains non-linear alpha values in an attempt to create gamma correct blits
+ * in the presence of a gamma incorrect (linear) blend in the blitter.
+ *
+ * SkMaskGamma creates and maintains tables which convert linear alpha values
+ * to gamma correcting alpha values.
+ * @param R The number of luminance bits to use [1, 8] from the red channel.
+ * @param G The number of luminance bits to use [1, 8] from the green channel.
+ * @param B The number of luminance bits to use [1, 8] from the blue channel.
+ */
+template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskGamma : public SkRefCnt {
+public:
+ /**
+ * Creates tables to convert linear alpha values to gamma correcting alpha
+ * values.
+ *
+ * @param contrast A value in the range [0.0, 1.0] which indicates the
+ * amount of artificial contrast to add.
+ * @param paint The color space in which the paint color was chosen.
+ * @param device The color space of the target device.
+ */
+ SkTMaskGamma(SkScalar contrast,
+ const SkColorSpaceLuminance& paint,
+ const SkColorSpaceLuminance& device) {
+ for (U8CPU i = 0; i < (1 << kLuminanceBits_Max); ++i) {
+ U8CPU lum = sk_t_scale255<kLuminanceBits_Max>(i);
+ SkTMaskGamma_build_correcting_lut(fGammaTables[i], lum, contrast, paint, device);
+ }
+ }
+
+ /** Given a color, returns the closest cannonical color. */
+ SkColor cannonicalColor(SkColor color) {
+ return SkColorSetRGB(
+ sk_t_scale255<kLuminanceBits_R>(SkColorGetR(color) >> (8 - kLuminanceBits_R)),
+ sk_t_scale255<kLuminanceBits_G>(SkColorGetG(color) >> (8 - kLuminanceBits_G)),
+ sk_t_scale255<kLuminanceBits_B>(SkColorGetB(color) >> (8 - kLuminanceBits_B)));
+ }
+
+ /** The type of the mask pre-blend which will be returned from preBlend(SkColor). */
+ typedef SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS> PreBlend;
+
+ /**
+ * Provides access to the tables appropriate for converting linear alpha
+ * values into gamma correcting alpha values when drawing the given color
+ * through the mask. The destination color will be approximated.
+ */
+ PreBlend preBlend(SkColor color);
+
+private:
+ enum LuminanceBits {
+ kLuminanceBits_R = R_LUM_BITS,
+ kLuminanceBits_G = G_LUM_BITS,
+ kLuminanceBits_B = B_LUM_BITS,
+ kLuminanceBits_Max = B_LUM_BITS > (R_LUM_BITS > G_LUM_BITS ? R_LUM_BITS : G_LUM_BITS)
+ ? B_LUM_BITS
+ : (R_LUM_BITS > G_LUM_BITS ? R_LUM_BITS : G_LUM_BITS)
+ };
+ uint8_t fGammaTables[1 << kLuminanceBits_Max][256];
+};
+
+/**
+ * SkTMaskPreBlend is a tear-off of SkTMaskGamma. It provides the tables to
+ * convert a linear alpha value for a given channel to a gamma correcting alpha
+ * value for that channel. This class is immutable.
+ */
+template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskPreBlend {
+private:
+ SkTMaskPreBlend(SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>* parent,
+ const uint8_t* r,
+ const uint8_t* g,
+ const uint8_t* b)
+ : fParent(parent), fR(r), fG(g), fB(b) {
+ parent->ref();
+ }
+ SkAutoTUnref<SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS> > fParent;
+ friend class SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>;
+public:
+ /**
+ * This copy contructor exists for correctness, but should never be called
+ * when return value optimization is enabled.
+ */
+ SkTMaskPreBlend(const SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>& that)
+ : fParent(that.fParent.get()), fR(that.fR), fG(that.fG), fB(that.fB) {
+ fParent.get()->ref();
+ }
+ ~SkTMaskPreBlend() { }
+ const uint8_t* fR;
+ const uint8_t* fG;
+ const uint8_t* fB;
+};
+
+template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS>
+SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>
+SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>::preBlend(SkColor color) {
+ return SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>(
+ this,
+ fGammaTables[SkColorGetR(color) >> (8 - kLuminanceBits_Max)],
+ fGammaTables[SkColorGetG(color) >> (8 - kLuminanceBits_Max)],
+ fGammaTables[SkColorGetB(color) >> (8 - kLuminanceBits_Max)]);
+}
+
+///@{
+/**
+ * If APPLY_LUT is false, returns component unchanged.
+ * If APPLY_LUT is true, returns lut[component].
+ * @param APPLY_LUT whether or not the look-up table should be applied to component.
+ * @component the initial component.
+ * @lut a look-up table which transforms the component.
+ */
+template<bool APPLY_LUT> static inline U8CPU sk_apply_lut_if(U8CPU component, const uint8_t*) {
+ return component;
+}
+template<> /*static*/ inline U8CPU sk_apply_lut_if<true>(U8CPU component, const uint8_t* lut) {
+ return lut[component];
+}
+///@}
+
+#endif
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"
diff --git a/src/core/SkScalerContext.cpp b/src/core/SkScalerContext.cpp
index 7c26921bfb..9d99713c40 100644
--- a/src/core/SkScalerContext.cpp
+++ b/src/core/SkScalerContext.cpp
@@ -14,6 +14,7 @@
#include "SkFontHost.h"
#include "SkGlyph.h"
#include "SkMaskFilter.h"
+#include "SkMaskGamma.h"
#include "SkOrderedReadBuffer.h"
#include "SkPathEffect.h"
#include "SkRasterizer.h"
@@ -74,16 +75,16 @@ static SkFlattenable* load_flattenable(const SkDescriptor* desc, uint32_t tag) {
}
SkScalerContext::SkScalerContext(const SkDescriptor* desc)
- : fPathEffect(NULL), fMaskFilter(NULL)
+ : fRec(*static_cast<const Rec*>(desc->findEntry(kRec_SkDescriptorTag, NULL)))
+ , fBaseGlyphCount(0)
+ , fPathEffect(static_cast<SkPathEffect*>(load_flattenable(desc, kPathEffect_SkDescriptorTag)))
+ , fMaskFilter(static_cast<SkMaskFilter*>(load_flattenable(desc, kMaskFilter_SkDescriptorTag)))
+ , fRasterizer(static_cast<SkRasterizer*>(load_flattenable(desc, kRasterizer_SkDescriptorTag)))
+ // initialize based on our settings. subclasses can also force this
+ , fGenerateImageFromPath(fRec.fFrameWidth > 0 || fPathEffect != NULL || fRasterizer != NULL)
+ , fNextContext(NULL)
+ , fMaskPreBlend(SkScalerContext::GetMaskPreBlend(fRec))
{
- fBaseGlyphCount = 0;
- fNextContext = NULL;
-
- const Rec* rec = (const Rec*)desc->findEntry(kRec_SkDescriptorTag, NULL);
- SkASSERT(rec);
-
- fRec = *rec;
-
#ifdef DUMP_REC
desc->assertChecksum();
SkDebugf("SkScalarContext checksum %x count %d length %d\n",
@@ -98,14 +99,6 @@ SkScalerContext::SkScalerContext(const SkDescriptor* desc)
desc->findEntry(kPathEffect_SkDescriptorTag, NULL),
desc->findEntry(kMaskFilter_SkDescriptorTag, NULL));
#endif
-
- fPathEffect = (SkPathEffect*)load_flattenable(desc, kPathEffect_SkDescriptorTag);
- fMaskFilter = (SkMaskFilter*)load_flattenable(desc, kMaskFilter_SkDescriptorTag);
- fRasterizer = (SkRasterizer*)load_flattenable(desc, kRasterizer_SkDescriptorTag);
-
- // initialize based on our settings. subclasses can also force this
- fGenerateImageFromPath = fRec.fFrameWidth > 0 || fPathEffect != NULL ||
- fRasterizer != NULL;
}
SkScalerContext::~SkScalerContext() {
@@ -342,77 +335,39 @@ SK_ERROR:
glyph->fMaskFormat = fRec.fMaskFormat;
}
-#if 0 // UNUSED
-static bool isLCD(const SkScalerContext::Rec& rec) {
- return SkMask::kLCD16_Format == rec.fMaskFormat ||
- SkMask::kLCD32_Format == rec.fMaskFormat;
-}
-#endif
-
-#if 0 // UNUSED
-static uint16_t a8_to_rgb565(unsigned a8) {
- return SkPackRGB16(a8 >> 3, a8 >> 2, a8 >> 3);
-}
-
-static void copyToLCD16(const SkBitmap& src, const SkMask& dst) {
+template<bool APPLY_PREBLEND>
+static void pack3xHToLCD16(const SkBitmap& src, const SkMask& dst, SkMaskGamma::PreBlend* maskPreBlend) {
SkASSERT(SkBitmap::kA8_Config == src.config());
SkASSERT(SkMask::kLCD16_Format == dst.fFormat);
-
+
const int width = dst.fBounds.width();
const int height = dst.fBounds.height();
- const uint8_t* srcP = src.getAddr8(0, 0);
- size_t srcRB = src.rowBytes();
uint16_t* dstP = (uint16_t*)dst.fImage;
size_t dstRB = dst.fRowBytes;
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- dstP[x] = a8_to_rgb565(srcP[x]);
- }
- srcP += srcRB;
- dstP = (uint16_t*)((char*)dstP + dstRB);
- }
-}
-#endif
-
-#define SK_FREETYPE_LCD_LERP 160
-
-static int lerp(int start, int end) {
- SkASSERT((unsigned)SK_FREETYPE_LCD_LERP <= 256);
- return start + ((end - start) * (SK_FREETYPE_LCD_LERP) >> 8);
-}
-
-static uint16_t packLCD16(unsigned r, unsigned g, unsigned b) {
- if (SK_FREETYPE_LCD_LERP) {
- // want (a+b+c)/3, but we approx to avoid the divide
- unsigned ave = (5 * (r + g + b) + g) >> 4;
- r = lerp(r, ave);
- g = lerp(g, ave);
- b = lerp(b, ave);
+
+ const uint8_t* maskPreBlendR = NULL;
+ const uint8_t* maskPreBlendG = NULL;
+ const uint8_t* maskPreBlendB = NULL;
+ if (APPLY_PREBLEND) {
+ maskPreBlendR = maskPreBlend->fR;
+ maskPreBlendG = maskPreBlend->fG;
+ maskPreBlendB = maskPreBlend->fB;
}
- return SkPackRGB16(r >> 3, g >> 2, b >> 3);
-}
-static void pack3xHToLCD16(const SkBitmap& src, const SkMask& dst) {
- SkASSERT(SkBitmap::kA8_Config == src.config());
- SkASSERT(SkMask::kLCD16_Format == dst.fFormat);
-
- const int width = dst.fBounds.width();
- const int height = dst.fBounds.height();
- uint16_t* dstP = (uint16_t*)dst.fImage;
- size_t dstRB = dst.fRowBytes;
for (int y = 0; y < height; ++y) {
const uint8_t* srcP = src.getAddr8(0, y);
for (int x = 0; x < width; ++x) {
- unsigned r = *srcP++;
- unsigned g = *srcP++;
- unsigned b = *srcP++;
- dstP[x] = packLCD16(r, g, b);
+ U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlendR);
+ U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlendG);
+ U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlendB);
+ dstP[x] = SkPack888ToRGB16(r, g, b);
}
dstP = (uint16_t*)((char*)dstP + dstRB);
}
}
-static void pack3xHToLCD32(const SkBitmap& src, const SkMask& dst) {
+template<bool APPLY_PREBLEND>
+static void pack3xHToLCD32(const SkBitmap& src, const SkMask& dst, SkMaskGamma::PreBlend* maskPreBlend) {
SkASSERT(SkBitmap::kA8_Config == src.config());
SkASSERT(SkMask::kLCD32_Format == dst.fFormat);
@@ -420,20 +375,29 @@ static void pack3xHToLCD32(const SkBitmap& src, const SkMask& dst) {
const int height = dst.fBounds.height();
SkPMColor* dstP = (SkPMColor*)dst.fImage;
size_t dstRB = dst.fRowBytes;
+
+ const uint8_t* maskPreBlendR = NULL;
+ const uint8_t* maskPreBlendG = NULL;
+ const uint8_t* maskPreBlendB = NULL;
+ if (APPLY_PREBLEND) {
+ maskPreBlendR = maskPreBlend->fR;
+ maskPreBlendG = maskPreBlend->fG;
+ maskPreBlendB = maskPreBlend->fB;
+ }
+
for (int y = 0; y < height; ++y) {
const uint8_t* srcP = src.getAddr8(0, y);
for (int x = 0; x < width; ++x) {
- unsigned r = *srcP++;
- unsigned g = *srcP++;
- unsigned b = *srcP++;
- unsigned a = SkMax32(SkMax32(r, g), b);
- dstP[x] = SkPackARGB32(a, r, g, b);
+ U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlendR);
+ U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlendG);
+ U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlendB);
+ dstP[x] = SkPackARGB32(0xFF, r, g, b);
}
dstP = (SkPMColor*)((char*)dstP + dstRB);
}
}
-static void generateMask(const SkMask& mask, const SkPath& path) {
+static void generateMask(const SkMask& mask, const SkPath& path, SkMaskGamma::PreBlend* maskPreBlend) {
SkBitmap::Config config;
SkPaint paint;
@@ -493,10 +457,18 @@ static void generateMask(const SkMask& mask, const SkPath& path) {
if (0 == dstRB) {
switch (mask.fFormat) {
case SkMask::kLCD16_Format:
- pack3xHToLCD16(bm, mask);
+ if (maskPreBlend) {
+ pack3xHToLCD16<true>(bm, mask, maskPreBlend);
+ } else {
+ pack3xHToLCD16<false>(bm, mask, maskPreBlend);
+ }
break;
case SkMask::kLCD32_Format:
- pack3xHToLCD32(bm, mask);
+ if (maskPreBlend) {
+ pack3xHToLCD32<true>(bm, mask, maskPreBlend);
+ } else {
+ pack3xHToLCD32<false>(bm, mask, maskPreBlend);
+ }
break;
default:
SkDEBUGFAIL("bad format for copyback");
@@ -504,10 +476,23 @@ static void generateMask(const SkMask& mask, const SkPath& path) {
}
}
+static void applyLUTToA8Glyph(const SkGlyph& glyph, const uint8_t* lut) {
+ uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
+ unsigned rowBytes = glyph.rowBytes();
+
+ for (int y = glyph.fHeight - 1; y >= 0; --y) {
+ for (int x = glyph.fWidth - 1; x >= 0; --x) {
+ dst[x] = lut[dst[x]];
+ }
+ dst += rowBytes;
+ }
+}
+
void SkScalerContext::getImage(const SkGlyph& origGlyph) {
const SkGlyph* glyph = &origGlyph;
SkGlyph tmpGlyph;
-
+ SkMaskGamma::PreBlend* maskPreBlend = &fMaskPreBlend;
+
if (fMaskFilter) { // restore the prefilter bounds
tmpGlyph.init(origGlyph.fID);
@@ -523,6 +508,7 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) {
SkASSERT(tmpGlyph.fWidth <= origGlyph.fWidth);
SkASSERT(tmpGlyph.fHeight <= origGlyph.fHeight);
glyph = &tmpGlyph;
+ maskPreBlend = NULL;
}
if (fGenerateImageFromPath) {
@@ -542,11 +528,19 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) {
SkMask::kJustRenderImage_CreateMode)) {
return;
}
+ //apply maskPreBlend to a8 (if not NULL)
+ if (maskPreBlend) {
+ applyLUTToA8Glyph(*glyph, maskPreBlend->fG);
+ }
} else {
- generateMask(mask, devPath);
+ generateMask(mask, devPath, maskPreBlend);
+ //apply maskPreBlend to a8 (if not NULL) -- already applied to lcd.
+ if (maskPreBlend && mask.fFormat == SkMask::kA8_Format) {
+ applyLUTToA8Glyph(*glyph, maskPreBlend->fG);
+ }
}
} else {
- this->getGlyphContext(*glyph)->generateImage(*glyph);
+ this->getGlyphContext(*glyph)->generateImage(*glyph, maskPreBlend);
}
if (fMaskFilter) {
@@ -581,6 +575,11 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) {
dst += dstRB;
}
SkMask::FreeImage(dstM.fImage);
+
+ /* Pre-blend is not currently applied to filtered text.
+ The primary filter is blur, for which contrast makes no sense,
+ and for which the destination guess error is more visible. */
+ //applyLUTToA8Glyph(origGlyph, fMaskPreBlend.fG);
}
}
}
@@ -749,7 +748,7 @@ protected:
virtual void generateMetrics(SkGlyph* glyph) {
glyph->zeroMetrics();
}
- virtual void generateImage(const SkGlyph& glyph) {}
+ virtual void generateImage(const SkGlyph& glyph, SkMaskGamma::PreBlend* maskPreBlend) {}
virtual void generatePath(const SkGlyph& glyph, SkPath* path) {}
virtual void generateFontMetrics(SkPaint::FontMetrics* mx,
SkPaint::FontMetrics* my) {
diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp
index 0d72fa1dc5..497a259a5b 100644
--- a/src/ports/SkFontHost_FreeType.cpp
+++ b/src/ports/SkFontHost_FreeType.cpp
@@ -16,6 +16,7 @@
#include "SkFontHost.h"
#include "SkGlyph.h"
#include "SkMask.h"
+#include "SkMaskGamma.h"
#include "SkAdvancedTypefaceMetrics.h"
#include "SkScalerContext.h"
#include "SkStream.h"
@@ -57,14 +58,6 @@
//#define DUMP_STRIKE_CREATION
//#define SK_GAMMA_APPLY_TO_A8
-//#define SK_GAMMA_SRGB
-
-#ifndef SK_GAMMA_CONTRAST
- #define SK_GAMMA_CONTRAST 0x66
-#endif
-#ifndef SK_GAMMA_EXPONENT
- #define SK_GAMMA_EXPONENT 2.2
-#endif
#ifdef SK_DEBUG
#define SkASSERT_CONTINUE(pred) \
@@ -100,8 +93,6 @@ static bool gLCDSupportValid; // true iff |gLCDSupport| has been set.
static bool gLCDSupport; // true iff LCD is supported by the runtime.
static int gLCDExtra; // number of extra pixels for filtering.
-static const uint8_t* gGammaTables[2];
-
/////////////////////////////////////////////////////////////////////////
// See http://freetype.sourceforge.net/freetype2/docs/reference/ft2-bitmap_handling.html#FT_Bitmap_Embolden
@@ -153,7 +144,7 @@ protected:
virtual uint16_t generateCharToGlyph(SkUnichar uni);
virtual void generateAdvance(SkGlyph* glyph);
virtual void generateMetrics(SkGlyph* glyph);
- virtual void generateImage(const SkGlyph& glyph);
+ virtual void generateImage(const SkGlyph& glyph, SkMaskGamma::PreBlend* maskPreBlend);
virtual void generatePath(const SkGlyph& glyph, SkPath* path);
virtual void generateFontMetrics(SkPaint::FontMetrics* mx,
SkPaint::FontMetrics* my);
@@ -670,25 +661,9 @@ void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
#endif
rec->setHinting(h);
-#ifndef SK_USE_COLOR_LUMINANCE
- // for compatibility at the moment, discretize luminance to 3 settings
- // black, white, gray. This helps with fontcache utilization, since we
- // won't create multiple entries that in the end map to the same results.
- {
- unsigned lum = rec->getLuminanceByte();
- if (gGammaTables[0] || gGammaTables[1]) {
- if (lum <= BLACK_LUMINANCE_LIMIT) {
- lum = 0;
- } else if (lum >= WHITE_LUMINANCE_LIMIT) {
- lum = SkScalerContext::kLuminance_Max;
- } else {
- lum = SkScalerContext::kLuminance_Max >> 1;
- }
- } else {
- lum = 0; // no gamma correct, so use 0 since SkPaint uses that
- // when measuring text w/o regard for luminance
- }
- rec->setLuminanceBits(lum);
+#ifndef SK_GAMMA_APPLY_TO_A8
+ if (!isLCD(*rec)) {
+ rec->ignorePreBlend();
}
#endif
}
@@ -716,7 +691,6 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc)
if (!InitFreetype()) {
sk_throw();
}
- SkFontHost::GetGammaTables(gGammaTables);
}
++gFTCount;
@@ -1169,103 +1143,6 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
///////////////////////////////////////////////////////////////////////////////
-#ifdef SK_USE_COLOR_LUMINANCE
-
-static float apply_contrast(float srca, float contrast) {
- return srca + ((1.0f - srca) * contrast * srca);
-}
-
-#ifdef SK_GAMMA_SRGB
-static float lin(float per) {
- if (per <= 0.04045f) {
- return per / 12.92f;
- }
- return powf((per + 0.055f) / 1.055, 2.4f);
-}
-static float per(float lin) {
- if (lin <= 0.0031308f) {
- return lin * 12.92f;
- }
- return 1.055f * powf(lin, 1.0f / 2.4f) - 0.055f;
-}
-#else //SK_GAMMA_SRGB
-static float lin(float per) {
- const float g = SK_GAMMA_EXPONENT;
- return powf(per, g);
-}
-static float per(float lin) {
- const float g = SK_GAMMA_EXPONENT;
- return powf(lin, 1.0f / g);
-}
-#endif //SK_GAMMA_SRGB
-
-static void build_gamma_table(uint8_t table[256], int srcI) {
- const float src = (float)srcI / 255.0f;
- const float linSrc = lin(src);
- const float linDst = 1.0f - linSrc;
- const float dst = per(linDst);
-
- // have our contrast value taper off to 0 as the src luminance becomes white
- const float contrast = SK_GAMMA_CONTRAST / 255.0f * linDst;
- const float step = 1.0f / 256.0f;
-
- //Remove discontinuity and instability when src is close to dst.
- if (fabs(src - dst) < 0.01f) {
- float rawSrca = 0.0f;
- for (int i = 0; i < 256; ++i, rawSrca += step) {
- float srca = apply_contrast(rawSrca, contrast);
- table[i] = sk_float_round2int(255.0f * srca);
- }
- } else {
- float rawSrca = 0.0f;
- for (int i = 0; i < 256; ++i, rawSrca += step) {
- float srca = apply_contrast(rawSrca, contrast);
- SkASSERT(srca <= 1.0f);
- float dsta = 1 - srca;
-
- //Calculate the output we want.
- float linOut = (linSrc * srca + dsta * linDst);
- SkASSERT(linOut <= 1.0f);
- float out = per(linOut);
-
- //Undo what the blit blend will do.
- float result = (out - dst) / (src - dst);
- SkASSERT(sk_float_round2int(255.0f * result) <= 255);
-
- table[i] = sk_float_round2int(255.0f * result);
- }
- }
-}
-
-static const uint8_t* getGammaTable(U8CPU luminance) {
- static uint8_t gGammaTables[4][256];
- static bool gInited;
- if (!gInited) {
- build_gamma_table(gGammaTables[0], 0x00);
- build_gamma_table(gGammaTables[1], 0x55);
- build_gamma_table(gGammaTables[2], 0xAA);
- build_gamma_table(gGammaTables[3], 0xFF);
-
- gInited = true;
- }
- SkASSERT(0 == (luminance >> 8));
- return gGammaTables[luminance >> 6];
-}
-
-#else //SK_USE_COLOR_LUMINANCE
-static const uint8_t* getIdentityTable() {
- static bool gOnce;
- static uint8_t gIdentityTable[256];
- if (!gOnce) {
- for (int i = 0; i < 256; ++i) {
- gIdentityTable[i] = i;
- }
- gOnce = true;
- }
- return gIdentityTable;
-}
-#endif //SK_USE_COLOR_LUMINANCE
-
static uint16_t packTriple(unsigned r, unsigned g, unsigned b) {
return SkPackRGB16(r >> 3, g >> 2, b >> 3);
}
@@ -1281,6 +1158,7 @@ static int bittst(const uint8_t data[], int bitOffset) {
return lowBit & 1;
}
+template<bool APPLY_PREBLEND>
static void copyFT2LCD16(const SkGlyph& glyph, const FT_Bitmap& bitmap,
int lcdIsBGR, bool lcdIsVert, const uint8_t* tableR,
const uint8_t* tableG, const uint8_t* tableB) {
@@ -1325,25 +1203,25 @@ static void copyFT2LCD16(const SkGlyph& glyph, const FT_Bitmap& bitmap,
SkTSwap(srcR, srcB);
}
for (int x = 0; x < width; x++) {
- dst[x] = packTriple(tableR[*srcR++],
- tableG[*srcG++],
- tableB[*srcB++]);
+ dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(*srcR++, tableR),
+ sk_apply_lut_if<APPLY_PREBLEND>(*srcG++, tableG),
+ sk_apply_lut_if<APPLY_PREBLEND>(*srcB++, tableB));
}
src += 3 * bitmap.pitch;
} else { // horizontal stripes
const uint8_t* triple = src;
if (lcdIsBGR) {
for (int x = 0; x < width; x++) {
- dst[x] = packTriple(tableR[triple[2]],
- tableG[triple[1]],
- tableB[triple[0]]);
+ dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableR),
+ sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG),
+ sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableB));
triple += 3;
}
} else {
for (int x = 0; x < width; x++) {
- dst[x] = packTriple(tableR[triple[0]],
- tableG[triple[1]],
- tableB[triple[2]]);
+ dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableR),
+ sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG),
+ sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableB));
triple += 3;
}
}
@@ -1355,7 +1233,7 @@ static void copyFT2LCD16(const SkGlyph& glyph, const FT_Bitmap& bitmap,
}
}
-void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {
+void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph, SkMaskGamma::PreBlend* maskPreBlend) {
SkAutoMutexAcquire ac(gFTMutex);
FT_Error err;
@@ -1373,25 +1251,15 @@ void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {
return;
}
-#ifdef SK_USE_COLOR_LUMINANCE
- SkColor lumColor = fRec.getLuminanceColor();
- const uint8_t* tableR = getGammaTable(SkColorGetR(lumColor));
- const uint8_t* tableG = getGammaTable(SkColorGetG(lumColor));
- const uint8_t* tableB = getGammaTable(SkColorGetB(lumColor));
-#else
- unsigned lum = fRec.getLuminanceByte();
- const uint8_t* tableR;
- const uint8_t* tableG;
- const uint8_t* tableB;
-
- bool isWhite = lum >= WHITE_LUMINANCE_LIMIT;
- bool isBlack = lum <= BLACK_LUMINANCE_LIMIT;
- if ((gGammaTables[0] || gGammaTables[1]) && (isBlack || isWhite)) {
- tableR = tableG = tableB = gGammaTables[isBlack ? 0 : 1];
- } else {
- tableR = tableG = tableB = getIdentityTable();
+ //Must be careful not to use these if maskPreBlend == NULL
+ const uint8_t* tableR = NULL;
+ const uint8_t* tableG = NULL;
+ const uint8_t* tableB = NULL;
+ if (maskPreBlend) {
+ tableR = maskPreBlend->fR;
+ tableG = maskPreBlend->fG;
+ tableB = maskPreBlend->fB;
}
-#endif
const bool doBGR = SkToBool(fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag);
const bool doVert = fLCDIsVert;
@@ -1427,8 +1295,13 @@ void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {
if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
FT_Render_Glyph(fFace->glyph, doVert ? FT_RENDER_MODE_LCD_V : FT_RENDER_MODE_LCD);
- copyFT2LCD16(glyph, fFace->glyph->bitmap, doBGR, doVert,
- tableR, tableG, tableB);
+ if (maskPreBlend) {
+ copyFT2LCD16<true>(glyph, fFace->glyph->bitmap, doBGR, doVert,
+ tableR, tableG, tableB);
+ } else {
+ copyFT2LCD16<false>(glyph, fFace->glyph->bitmap, doBGR, doVert,
+ tableR, tableG, tableB);
+ }
} else {
target.width = glyph.fWidth;
target.rows = glyph.fHeight;
@@ -1493,8 +1366,13 @@ void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {
dst += glyph.rowBytes();
}
} else if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
- copyFT2LCD16(glyph, fFace->glyph->bitmap, doBGR, doVert,
- tableR, tableG, tableB);
+ if (maskPreBlend) {
+ copyFT2LCD16<true>(glyph, fFace->glyph->bitmap, doBGR, doVert,
+ tableR, tableG, tableB);
+ } else {
+ copyFT2LCD16<false>(glyph, fFace->glyph->bitmap, doBGR, doVert,
+ tableR, tableG, tableB);
+ }
} else {
SkDEBUGFAIL("unknown glyph bitmap transform needed");
}
@@ -1507,16 +1385,14 @@ void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {
// We used to always do this pre-USE_COLOR_LUMINANCE, but with colorlum,
// it is optional
-#if defined(SK_GAMMA_APPLY_TO_A8) || !defined(SK_USE_COLOR_LUMINANCE)
- if (SkMask::kA8_Format == glyph.fMaskFormat) {
- SkASSERT(tableR == tableG && tableR == tableB);
- const uint8_t* table = tableR;
+#if defined(SK_GAMMA_APPLY_TO_A8)
+ if (SkMask::kA8_Format == glyph.fMaskFormat && maskPreBlend) {
uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
unsigned rowBytes = glyph.rowBytes();
for (int y = glyph.fHeight - 1; y >= 0; --y) {
for (int x = glyph.fWidth - 1; x >= 0; --x) {
- dst[x] = table[dst[x]];
+ dst[x] = tableG[dst[x]];
}
dst += rowBytes;
}
diff --git a/src/ports/SkFontHost_gamma.cpp b/src/ports/SkFontHost_gamma.cpp
deleted file mode 100644
index 0d154142fc..0000000000
--- a/src/ports/SkFontHost_gamma.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SkFontHost.h"
-#include <math.h>
-
-// define this to use pre-compiled tables for gamma. This is slightly faster,
-// and doesn't create any RW global memory, but means we cannot change the
-// gamma at runtime.
-//#define USE_PREDEFINED_GAMMA_TABLES
-
-#ifndef USE_PREDEFINED_GAMMA_TABLES
- // define this if you want to spew out the "C" code for the tables, given
- // the current values for SK_BLACK_GAMMA and SK_WHITE_GAMMA.
- #define DUMP_GAMMA_TABLESx
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-
-#include "SkGraphics.h"
-
-// declared here, so we can link against it elsewhere
-void skia_set_text_gamma(float blackGamma, float whiteGamma);
-
-#ifdef USE_PREDEFINED_GAMMA_TABLES
-
-#include "sk_predefined_gamma.h"
-
-void skia_set_text_gamma(float blackGamma, float whiteGamma) {}
-
-#else // use writable globals for gamma tables
-
-static void build_power_table(uint8_t table[], float ee) {
-// SkDebugf("------ build_power_table %g\n", ee);
- for (int i = 0; i < 256; i++) {
- float x = i / 255.f;
- // printf(" %d %g", i, x);
- x = powf(x, ee);
- // printf(" %g", x);
- int xx = SkScalarRound(SkFloatToScalar(x * 255));
- // printf(" %d\n", xx);
- table[i] = SkToU8(xx);
- }
-}
-
-static bool gGammaIsBuilt;
-static uint8_t gBlackGamma[256], gWhiteGamma[256];
-
-static float gBlackGammaCoeff = 1.4f;
-static float gWhiteGammaCoeff = 1/1.4f;
-
-void skia_set_text_gamma(float blackGamma, float whiteGamma) {
- gBlackGammaCoeff = blackGamma;
- gWhiteGammaCoeff = whiteGamma;
- gGammaIsBuilt = false;
- SkGraphics::PurgeFontCache();
- build_power_table(gBlackGamma, gBlackGammaCoeff);
- build_power_table(gWhiteGamma, gWhiteGammaCoeff);
-}
-
-#ifdef DUMP_GAMMA_TABLES
-
-#include "SkString.h"
-
-static void dump_a_table(const char name[], const uint8_t table[],
- float gamma) {
- SkDebugf("\n");
- SkDebugf("\/\/ Gamma table for %g\n", gamma);
- SkDebugf("static const uint8_t %s[] = {\n", name);
- for (int y = 0; y < 16; y++) {
- SkString line, tmp;
- for (int x = 0; x < 16; x++) {
- tmp.printf("0x%02X, ", *table++);
- line.append(tmp);
- }
- SkDebugf(" %s\n", line.c_str());
- }
- SkDebugf("};\n");
-}
-
-#endif
-
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-
-void SkFontHost::GetGammaTables(const uint8_t* tables[2]) {
-#ifndef USE_PREDEFINED_GAMMA_TABLES
- if (!gGammaIsBuilt) {
- build_power_table(gBlackGamma, gBlackGammaCoeff);
- build_power_table(gWhiteGamma, gWhiteGammaCoeff);
- gGammaIsBuilt = true;
-
-#ifdef DUMP_GAMMA_TABLES
- dump_a_table("gBlackGamma", gBlackGamma, gBlackGammaCoeff);
- dump_a_table("gWhiteGamma", gWhiteGamma, gWhiteGammaCoeff);
-#endif
- }
-#endif
- tables[0] = gBlackGamma;
- tables[1] = gWhiteGamma;
-}
-
diff --git a/src/ports/SkFontHost_gamma_none.cpp b/src/ports/SkFontHost_gamma_none.cpp
deleted file mode 100644
index 18f113c888..0000000000
--- a/src/ports/SkFontHost_gamma_none.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-
-/*
- * Copyright 2008 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-// -----------------------------------------------------------------------------
-// This is a noop gamma implementation for systems where gamma is already
-// corrected, or dealt with in a system wide fashion. For example, on X windows
-// one uses the xgamma utility to set the server-wide gamma correction value.
-// -----------------------------------------------------------------------------
-
-#include "SkFontHost.h"
-
-void SkFontHost::GetGammaTables(const uint8_t* tables[2])
-{
- tables[0] = NULL;
- tables[1] = NULL;
-}
-
diff --git a/src/ports/SkFontHost_mac_coretext.cpp b/src/ports/SkFontHost_mac_coretext.cpp
index cdd4a74d7e..c723c8848b 100644
--- a/src/ports/SkFontHost_mac_coretext.cpp
+++ b/src/ports/SkFontHost_mac_coretext.cpp
@@ -24,6 +24,7 @@
#include "SkFontDescriptor.h"
#include "SkFloatingPoint.h"
#include "SkGlyph.h"
+#include "SkMaskGamma.h"
#include "SkPaint.h"
#include "SkString.h"
#include "SkStream.h"
@@ -265,13 +266,42 @@ static SkScalar getFontScale(CGFontRef cgFont) {
#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
#define BITMAP_INFO_GRAY (kCGImageAlphaNone)
+/**
+ * There does not appear to be a publicly accessable API for determining if lcd
+ * font smoothing will be applied if we request it. The main issue is that if
+ * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0.
+ */
+static bool supports_LCD() {
+ static int gSupportsLCD = -1;
+ if (gSupportsLCD >= 0) {
+ return (bool) gSupportsLCD;
+ }
+ int rgb = 0;
+ CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
+ CGContextRef cgContext = CGBitmapContextCreate(&rgb, 1, 1, 8, 4, colorspace,
+ BITMAP_INFO_RGB);
+ CGContextSelectFont(cgContext, "Helvetica", 16, kCGEncodingMacRoman);
+ CGContextSetShouldSmoothFonts(cgContext, true);
+ CGContextSetShouldAntialias(cgContext, true);
+ CGContextSetTextDrawingMode(cgContext, kCGTextFill);
+ CGContextSetGrayFillColor( cgContext, 1, 1);
+ CGContextShowTextAtPoint(cgContext, -1, 0, "|", 1);
+ CFSafeRelease(colorspace);
+ CFSafeRelease(cgContext);
+ int r = (rgb >> 16) & 0xFF;
+ int g = (rgb >> 8) & 0xFF;
+ int b = (rgb >> 0) & 0xFF;
+ gSupportsLCD = r != g || r != b;
+ return (bool) gSupportsLCD;
+}
+
class Offscreen {
public:
Offscreen();
~Offscreen();
CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
- bool fgColorIsWhite, CGGlyph glyphID, size_t* rowBytesPtr);
+ CGGlyph glyphID, size_t* rowBytesPtr);
private:
enum {
@@ -283,7 +313,6 @@ private:
// cached state
CGContextRef fCG;
SkISize fSize;
- bool fFgColorIsWhite;
bool fDoAA;
bool fDoLCD;
@@ -576,13 +605,13 @@ public:
protected:
- unsigned generateGlyphCount(void);
- uint16_t generateCharToGlyph(SkUnichar uni);
- void generateAdvance(SkGlyph* glyph);
- void generateMetrics(SkGlyph* glyph);
- void generateImage(const SkGlyph& glyph);
- void generatePath( const SkGlyph& glyph, SkPath* path);
- void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);
+ unsigned generateGlyphCount(void) SK_OVERRIDE;
+ uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE;
+ void generateAdvance(SkGlyph* glyph) SK_OVERRIDE;
+ void generateMetrics(SkGlyph* glyph) SK_OVERRIDE;
+ void generateImage(const SkGlyph& glyph, SkMaskGamma::PreBlend* maskPreBlend) SK_OVERRIDE;
+ void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE;
+ void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY) SK_OVERRIDE;
private:
@@ -597,12 +626,7 @@ private:
SkMatrix fVerticalMatrix; // unit rotated
SkMatrix fMatrix; // with font size
SkMatrix fAdjustBadMatrix; // lion-specific fix
-#ifdef SK_USE_COLOR_LUMINANCE
- Offscreen fBlackScreen;
- Offscreen fWhiteScreen;
-#else
Offscreen fOffscreen;
-#endif
CTFontRef fCTFont;
CTFontRef fCTVerticalFont; // for vertical advance
CGFontRef fCGFont;
@@ -698,8 +722,11 @@ SkScalerContext_Mac::~SkScalerContext_Mac() {
}
CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
- bool fgColorIsWhite, CGGlyph glyphID, size_t* rowBytesPtr) {
+ CGGlyph glyphID, size_t* rowBytesPtr) {
if (!fRGBSpace) {
+ //It doesn't appear to matter what color space is specified.
+ //Regular blends and antialiased text are always (s*a + d*(1-a))
+ //and smoothed text is always g=2.0.
fRGBSpace = CGColorSpaceCreateDeviceRGB();
}
@@ -710,13 +737,14 @@ CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph&
switch (glyph.fMaskFormat) {
case SkMask::kLCD16_Format:
case SkMask::kLCD32_Format:
+ case SkMask::kA8_Format: //Draw A8 as LCD, then downsample
doLCD = true;
doAA = true;
break;
- case SkMask::kA8_Format:
- doLCD = false;
- doAA = true;
- break;
+ //case SkMask::kA8_Format:
+ // doLCD = false;
+ // doAA = true;
+ // break;
default:
break;
}
@@ -749,10 +777,13 @@ CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph&
CGContextSetAllowsFontSubpixelPositioning(fCG, context.fDoSubPosition);
CGContextSetShouldSubpixelPositionFonts(fCG, context.fDoSubPosition);
+ // Draw white on black to create mask.
+ // TODO: Draw black on white and invert, CG has a special case codepath.
+ CGContextSetGrayFillColor(fCG, 1.0f, 1.0f);
+
// force our checks below to happen
fDoAA = !doAA;
fDoLCD = !doLCD;
- fFgColorIsWhite = !fgColorIsWhite;
}
if (fDoAA != doAA) {
@@ -763,23 +794,13 @@ CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph&
CGContextSetShouldSmoothFonts(fCG, doLCD);
fDoLCD = doLCD;
}
- if (fFgColorIsWhite != fgColorIsWhite) {
- CGContextSetGrayFillColor(fCG, fgColorIsWhite ? 1 : 0, 1);
- fFgColorIsWhite = fgColorIsWhite;
- }
CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
// skip rows based on the glyph's height
image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth;
- // erase with the "opposite" of the fgColor
- uint32_t erase = fgColorIsWhite ? 0 : ~0;
-#if 0
- sk_memset_rect(image, erase, glyph.fWidth * sizeof(CGRGBPixel),
- glyph.fHeight, rowBytes);
-#else
- sk_memset_rect32(image, erase, glyph.fWidth, glyph.fHeight, rowBytes);
-#endif
+ // erase to black
+ sk_memset_rect32(image, 0, glyph.fWidth, glyph.fHeight, rowBytes);
float subX = 0;
float subY = 0;
@@ -1060,62 +1081,27 @@ void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
static void build_power_table(uint8_t table[], float ee) {
for (int i = 0; i < 256; i++) {
float x = i / 255.f;
- x = powf(x, ee);
+ x = sk_float_pow(x, ee);
int xx = SkScalarRoundToInt(SkFloatToScalar(x * 255));
table[i] = SkToU8(xx);
}
}
-static const uint8_t* getInverseTable(bool isWhite) {
- static uint8_t gWhiteTable[256];
- static uint8_t gTable[256];
- static bool gInited;
- if (!gInited) {
- build_power_table(gWhiteTable, 1.5f);
- build_power_table(gTable, 2.2f);
- gInited = true;
- }
- return isWhite ? gWhiteTable : gTable;
-}
-
-#ifdef SK_USE_COLOR_LUMINANCE
-static const uint8_t* getGammaTable(U8CPU luminance) {
- static uint8_t gGammaTables[4][256];
+/**
+ * This will invert the gamma applied by CoreGraphics, so we can get linear
+ * values.
+ *
+ * CoreGraphics obscurely defaults to 2.0 as the smoothing gamma value.
+ * The color space used does not appear to affect this choice.
+ */
+static const uint8_t* getInverseGammaTableCoreGraphicSmoothing() {
static bool gInited;
+ static uint8_t gTableCoreGraphicsSmoothing[256];
if (!gInited) {
-#if 1
- float start = 1.1f;
- float stop = 2.1f;
- for (int i = 0; i < 4; ++i) {
- float g = start + (stop - start) * i / 3;
- build_power_table(gGammaTables[i], 1/g);
- }
-#else
- build_power_table(gGammaTables[0], 1);
- build_power_table(gGammaTables[1], 1);
- build_power_table(gGammaTables[2], 1);
- build_power_table(gGammaTables[3], 1);
-#endif
+ build_power_table(gTableCoreGraphicsSmoothing, 2.0f);
gInited = true;
}
- SkASSERT(0 == (luminance >> 8));
- return gGammaTables[luminance >> 6];
-}
-#endif
-
-static void invertGammaMask(bool isWhite, CGRGBPixel rgb[], int width,
- int height, size_t rb) {
- const uint8_t* table = getInverseTable(isWhite);
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- uint32_t c = rgb[x];
- int r = (c >> 16) & 0xFF;
- int g = (c >> 8) & 0xFF;
- int b = (c >> 0) & 0xFF;
- rgb[x] = (table[r] << 16) | (table[g] << 8) | table[b];
- }
- rgb = (CGRGBPixel*)((char*)rgb + rb);
- }
+ return gTableCoreGraphicsSmoothing;
}
static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
@@ -1131,230 +1117,164 @@ static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
}
}
-#ifdef SK_USE_COLOR_LUMINANCE
-static int lerpScale(int dst, int src, int scale) {
- return dst + (scale * (src - dst) >> 23);
+template<bool APPLY_PREBLEND>
+static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
+ U8CPU r = (rgb >> 16) & 0xFF;
+ U8CPU g = (rgb >> 8) & 0xFF;
+ U8CPU b = (rgb >> 0) & 0xFF;
+ return sk_apply_lut_if<APPLY_PREBLEND>((r + g + b) / 3, table8);
}
-
-static CGRGBPixel lerpPixel(CGRGBPixel dst, CGRGBPixel src,
- int scaleR, int scaleG, int scaleB) {
- int sr = (src >> 16) & 0xFF;
- int sg = (src >> 8) & 0xFF;
- int sb = (src >> 0) & 0xFF;
- int dr = (dst >> 16) & 0xFF;
- int dg = (dst >> 8) & 0xFF;
- int db = (dst >> 0) & 0xFF;
-
- int rr = lerpScale(dr, sr, scaleR);
- int rg = lerpScale(dg, sg, scaleG);
- int rb = lerpScale(db, sb, scaleB);
- return (rr << 16) | (rg << 8) | rb;
-}
-
-static void lerpPixels(CGRGBPixel dst[], const CGRGBPixel src[], int width,
- int height, int rowBytes, int lumBits) {
- int scaleR = (1 << 23) * SkColorGetR(lumBits) / 0xFF;
- int scaleG = (1 << 23) * SkColorGetG(lumBits) / 0xFF;
- int scaleB = (1 << 23) * SkColorGetB(lumBits) / 0xFF;
-
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- // bit-not the src, since it was drawn from black, so we need the
- // compliment of those bits
- dst[x] = lerpPixel(dst[x], ~src[x], scaleR, scaleG, scaleB);
+template<bool APPLY_PREBLEND>
+static void rgb_to_a8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
+ const SkGlyph& glyph, const uint8_t* table8) {
+ const int width = glyph.fWidth;
+ size_t dstRB = glyph.rowBytes();
+ uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
+
+ for (int y = 0; y < glyph.fHeight; y++) {
+ for (int i = 0; i < width; ++i) {
+ dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
}
- src = (CGRGBPixel*)((char*)src + rowBytes);
- dst = (CGRGBPixel*)((char*)dst + rowBytes);
+ cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
+ dst += dstRB;
+ }
+}
+
+template<bool APPLY_PREBLEND>
+static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb, const uint8_t* tableR,
+ const uint8_t* tableG,
+ const uint8_t* tableB) {
+ U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
+ U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
+ U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
+ return SkPack888ToRGB16(r, g, b);
+}
+template<bool APPLY_PREBLEND>
+static void rgb_to_lcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
+ const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
+ const int width = glyph.fWidth;
+ size_t dstRB = glyph.rowBytes();
+ uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage;
+
+ for (int y = 0; y < glyph.fHeight; y++) {
+ for (int i = 0; i < width; i++) {
+ dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
+ }
+ cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
+ dst = (uint16_t*)((char*)dst + dstRB);
}
}
-#endif
-#if 1
-static inline int r32_to_16(int x) { return SkR32ToR16(x); }
-static inline int g32_to_16(int x) { return SkG32ToG16(x); }
-static inline int b32_to_16(int x) { return SkB32ToB16(x); }
-#else
-static inline int round8to5(int x) {
- return (x + 3 - (x >> 5) + (x >> 7)) >> 3;
-}
-static inline int round8to6(int x) {
- int xx = (x + 1 - (x >> 6) + (x >> 7)) >> 2;
- SkASSERT((unsigned)xx <= 63);
-
- int ix = x >> 2;
- SkASSERT(SkAbs32(xx - ix) <= 1);
- return xx;
+template<bool APPLY_PREBLEND>
+static inline uint32_t rgb_to_lcd32(CGRGBPixel rgb, const uint8_t* tableR,
+ const uint8_t* tableG,
+ const uint8_t* tableB) {
+ U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
+ U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
+ U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
+ return SkPackARGB32(0xFF, r, g, b);
}
-
-static inline int r32_to_16(int x) { return round8to5(x); }
-static inline int g32_to_16(int x) { return round8to6(x); }
-static inline int b32_to_16(int x) { return round8to5(x); }
-#endif
-
-static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb) {
- int r = (rgb >> 16) & 0xFF;
- int g = (rgb >> 8) & 0xFF;
- int b = (rgb >> 0) & 0xFF;
-
- return SkPackRGB16(r32_to_16(r), g32_to_16(g), b32_to_16(b));
+template<bool APPLY_PREBLEND>
+static void rgb_to_lcd32(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
+ const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
+ const int width = glyph.fWidth;
+ size_t dstRB = glyph.rowBytes();
+ uint32_t* SK_RESTRICT dst = (uint32_t*)glyph.fImage;
+ for (int y = 0; y < glyph.fHeight; y++) {
+ for (int i = 0; i < width; i++) {
+ dst[i] = rgb_to_lcd32<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
+ }
+ cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
+ dst = (uint32_t*)((char*)dst + dstRB);
+ }
}
-static inline uint32_t rgb_to_lcd32(CGRGBPixel rgb) {
- int r = (rgb >> 16) & 0xFF;
- int g = (rgb >> 8) & 0xFF;
- int b = (rgb >> 0) & 0xFF;
-
- return SkPackARGB32(0xFF, r, g, b);
+template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) {
+ return (T*)((char*)ptr + byteOffset);
}
-#define BLACK_LUMINANCE_LIMIT 0x40
-#define WHITE_LUMINANCE_LIMIT 0xA0
-
-void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
+void SkScalerContext_Mac::generateImage(const SkGlyph& glyph, SkMaskGamma::PreBlend* maskPreBlend) {
CGGlyph cgGlyph = (CGGlyph) glyph.getGlyphID(fBaseGlyphCount);
- const bool isLCD = isLCDFormat(glyph.fMaskFormat);
-#ifdef SK_USE_COLOR_LUMINANCE
- const bool isBW = SkMask::kBW_Format == glyph.fMaskFormat;
- const bool isA8 = !isLCD && !isBW;
-
- unsigned lumBits = fRec.getLuminanceColor();
- uint32_t xorMask = 0;
-
- if (isA8) {
- // for A8, we just want a component (they're all the same)
- lumBits = SkColorGetR(lumBits);
- }
-#else
- bool fgColorIsWhite = true;
- bool isWhite = fRec.getLuminanceByte() >= WHITE_LUMINANCE_LIMIT;
- bool isBlack = fRec.getLuminanceByte() <= BLACK_LUMINANCE_LIMIT;
- uint32_t xorMask;
- bool invertGamma = false;
-
- /* For LCD16, we first create a temp offscreen cg-context in 32bit,
- * erase to white, and then draw a black glyph into it. Then we can
- * extract the r,g,b values, invert-them, and now we have the original
- * src mask components, which we pack into our 16bit mask.
- */
- if (isLCD) {
- if (isBlack) {
- xorMask = ~0U;
- fgColorIsWhite = false;
- } else { /* white or neutral */
- xorMask = 0;
- invertGamma = true;
- }
- }
-#endif
-
+ // Draw the glyph
size_t cgRowBytes;
-#ifdef SK_USE_COLOR_LUMINANCE
- CGRGBPixel* cgPixels;
- const uint8_t* gammaTable = NULL;
+ CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes);
+ if (cgPixels == NULL) {
+ return;
+ }
- if (isLCD) {
- CGRGBPixel* wtPixels = NULL;
- CGRGBPixel* bkPixels = NULL;
- bool needBlack = true;
- bool needWhite = true;
-
- if (SK_ColorWHITE == lumBits) {
- needBlack = false;
- } else if (SK_ColorBLACK == lumBits) {
- needWhite = false;
- }
-
- if (needBlack) {
- bkPixels = fBlackScreen.getCG(*this, glyph, false, cgGlyph, &cgRowBytes);
- cgPixels = bkPixels;
- xorMask = ~0;
- }
- if (needWhite) {
- wtPixels = fWhiteScreen.getCG(*this, glyph, true, cgGlyph, &cgRowBytes);
- cgPixels = wtPixels;
- xorMask = 0;
- }
+ //TODO: see if drawing black on white and inverting is faster (at least in
+ //lcd case) as core graphics appears to have special case code for drawing
+ //black text.
- if (wtPixels && bkPixels) {
- lerpPixels(wtPixels, bkPixels, glyph.fWidth, glyph.fHeight, cgRowBytes,
- ~lumBits);
- }
- } else { // isA8 or isBW
- cgPixels = fWhiteScreen.getCG(*this, glyph, true, cgGlyph, &cgRowBytes);
- if (isA8) {
- gammaTable = getGammaTable(lumBits);
+ // Fix the glyph
+ const bool isLCD = isLCDFormat(glyph.fMaskFormat);
+ if (isLCD || (glyph.fMaskFormat == SkMask::kA8_Format && supports_LCD())) {
+ const uint8_t* table = getInverseGammaTableCoreGraphicSmoothing();
+
+ //Note that the following cannot really be integrated into the
+ //pre-blend, since we may not be applying the pre-blend; when we aren't
+ //applying the pre-blend it means that a filter wants linear anyway.
+ //Other code may also be applying the pre-blend, so we'd need another
+ //one with this and one without.
+ CGRGBPixel* addr = cgPixels;
+ for (int y = 0; y < glyph.fHeight; ++y) {
+ for (int x = 0; x < glyph.fWidth; ++x) {
+ int r = (addr[x] >> 16) & 0xFF;
+ int g = (addr[x] >> 8) & 0xFF;
+ int b = (addr[x] >> 0) & 0xFF;
+ addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
+ }
+ addr = SkTAddByteOffset(addr, cgRowBytes);
}
}
-#else
- CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, fgColorIsWhite, cgGlyph,
- &cgRowBytes);
-#endif
-
- // Draw the glyph
- if (cgPixels != NULL) {
-
-#ifdef SK_USE_COLOR_LUMINANCE
-#else
- if (invertGamma) {
- invertGammaMask(isWhite, (uint32_t*)cgPixels,
- glyph.fWidth, glyph.fHeight, cgRowBytes);
- }
-#endif
-
- int width = glyph.fWidth;
- switch (glyph.fMaskFormat) {
- case SkMask::kLCD32_Format: {
- uint32_t* dst = (uint32_t*)glyph.fImage;
- size_t dstRB = glyph.rowBytes();
- for (int y = 0; y < glyph.fHeight; y++) {
- for (int i = 0; i < width; i++) {
- dst[i] = rgb_to_lcd32(cgPixels[i] ^ xorMask);
- }
- cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
- dst = (uint32_t*)((char*)dst + dstRB);
- }
- } break;
- case SkMask::kLCD16_Format: {
- // downsample from rgba to rgb565
- uint16_t* dst = (uint16_t*)glyph.fImage;
- size_t dstRB = glyph.rowBytes();
- for (int y = 0; y < glyph.fHeight; y++) {
- for (int i = 0; i < width; i++) {
- dst[i] = rgb_to_lcd16(cgPixels[i] ^ xorMask);
- }
- cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
- dst = (uint16_t*)((char*)dst + dstRB);
- }
- } break;
- case SkMask::kA8_Format: {
- uint8_t* dst = (uint8_t*)glyph.fImage;
- size_t dstRB = glyph.rowBytes();
- for (int y = 0; y < glyph.fHeight; y++) {
- for (int i = 0; i < width; ++i) {
- unsigned alpha8 = CGRGBPixel_getAlpha(cgPixels[i]);
-#ifdef SK_USE_COLOR_LUMINANCE
- alpha8 = gammaTable[alpha8];
-#endif
- dst[i] = alpha8;
- }
- cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
- dst += dstRB;
- }
- } break;
- case SkMask::kBW_Format: {
- uint8_t* dst = (uint8_t*)glyph.fImage;
- size_t dstRB = glyph.rowBytes();
- for (int y = 0; y < glyph.fHeight; y++) {
- cgpixels_to_bits(dst, cgPixels, width);
- cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
- dst += dstRB;
- }
- } break;
- default:
- SkDEBUGFAIL("unexpected mask format");
- break;
- }
+
+ // Must be careful not to use these if maskPreBlend == NULL
+ const uint8_t* tableR = NULL;
+ const uint8_t* tableG = NULL;
+ const uint8_t* tableB = NULL;
+ if (maskPreBlend) {
+ tableR = maskPreBlend->fR;
+ tableG = maskPreBlend->fG;
+ tableB = maskPreBlend->fB;
+ }
+
+ // Convert glyph to mask
+ switch (glyph.fMaskFormat) {
+ case SkMask::kLCD32_Format: {
+ if (maskPreBlend) {
+ rgb_to_lcd32<true>(cgPixels, cgRowBytes, glyph, tableR, tableG, tableB);
+ } else {
+ rgb_to_lcd32<false>(cgPixels, cgRowBytes, glyph, tableR, tableG, tableB);
+ }
+ } break;
+ case SkMask::kLCD16_Format: {
+ if (maskPreBlend) {
+ rgb_to_lcd16<true>(cgPixels, cgRowBytes, glyph, tableR, tableG, tableB);
+ } else {
+ rgb_to_lcd16<false>(cgPixels, cgRowBytes, glyph, tableR, tableG, tableB);
+ }
+ } break;
+ case SkMask::kA8_Format: {
+ if (maskPreBlend) {
+ rgb_to_a8<true>(cgPixels, cgRowBytes, glyph, tableG);
+ } else {
+ rgb_to_a8<false>(cgPixels, cgRowBytes, glyph, tableG);
+ }
+ } break;
+ case SkMask::kBW_Format: {
+ const int width = glyph.fWidth;
+ size_t dstRB = glyph.rowBytes();
+ uint8_t* dst = (uint8_t*)glyph.fImage;
+ for (int y = 0; y < glyph.fHeight; y++) {
+ cgpixels_to_bits(dst, cgPixels, width);
+ cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
+ dst += dstRB;
+ }
+ } break;
+ default:
+ SkDEBUGFAIL("unexpected mask format");
+ break;
}
}
@@ -1880,30 +1800,6 @@ SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
return nextFontID;
}
-static bool supports_LCD() {
- static int gSupportsLCD = -1;
- if (gSupportsLCD >= 0) {
- return (bool) gSupportsLCD;
- }
- int rgb = 0;
- CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
- CGContextRef cgContext = CGBitmapContextCreate(&rgb, 1, 1, 8, 4, colorspace,
- BITMAP_INFO_RGB);
- CGContextSelectFont(cgContext, "Helvetica", 16, kCGEncodingMacRoman);
- CGContextSetShouldSmoothFonts(cgContext, true);
- CGContextSetShouldAntialias(cgContext, true);
- CGContextSetTextDrawingMode(cgContext, kCGTextFill);
- CGContextSetGrayFillColor( cgContext, 1, 1);
- CGContextShowTextAtPoint(cgContext, -1, 0, "|", 1);
- CFSafeRelease(colorspace);
- CFSafeRelease(cgContext);
- int r = (rgb >> 16) & 0xFF;
- int g = (rgb >> 8) & 0xFF;
- int b = (rgb >> 0) & 0xFF;
- gSupportsLCD = r != g || r != b;
- return (bool) gSupportsLCD;
-}
-
void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
SkScalerContext::kAutohinting_Flag;
@@ -1918,37 +1814,24 @@ void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
h = SkPaint::kNormal_Hinting;
}
rec->setHinting(h);
-
-#ifdef SK_USE_COLOR_LUMINANCE
+
+ bool lcdSupport = supports_LCD();
if (isLCDFormat(rec->fMaskFormat)) {
- SkColor c = rec->getLuminanceColor();
- // apply our chosen scaling between Black and White cg output
- int r = SkColorGetR(c)*2/3;
- int g = SkColorGetG(c)*2/3;
- int b = SkColorGetB(c)*2/3;
- rec->setLuminanceColor(SkColorSetRGB(r, g, b));
- }
-#else
- {
- unsigned lum = rec->getLuminanceByte();
- if (lum <= BLACK_LUMINANCE_LIMIT) {
- lum = 0;
- } else if (lum >= WHITE_LUMINANCE_LIMIT) {
- lum = SkScalerContext::kLuminance_Max;
+ if (lcdSupport) {
+ //CoreGraphics creates 555 masks for smoothed text anyway.
+ rec->fMaskFormat = SkMask::kLCD16_Format;
} else {
- lum = SkScalerContext::kLuminance_Max >> 1;
+ rec->fMaskFormat = SkMask::kA8_Format;
}
- rec->setLuminanceBits(lum);
}
+
+ if (lcdSupport) {
+ //CoreGraphics dialates smoothed text as needed.
+ rec->setContrast(0);
+ } else {
+#ifndef SK_GAMMA_APPLY_TO_A8
+ rec->ignorePreBlend();
#endif
-
- if (SkMask::kLCD16_Format == rec->fMaskFormat
- || SkMask::kLCD32_Format == rec->fMaskFormat) {
- if (supports_LCD()) {
- rec->fMaskFormat = SkMask::kLCD32_Format;
- } else {
- rec->fMaskFormat = SkMask::kA8_Format;
- }
}
}
diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp
index 082e6e48aa..fbe4bb13e6 100755
--- a/src/ports/SkFontHost_win.cpp
+++ b/src/ports/SkFontHost_win.cpp
@@ -13,6 +13,7 @@
#include "SkFontDescriptor.h"
#include "SkFontHost.h"
#include "SkGlyph.h"
+#include "SkMaskGamma.h"
#include "SkOTUtils.h"
#include "SkStream.h"
#include "SkString.h"
@@ -397,7 +398,6 @@ public:
fBits = NULL;
fWidth = fHeight = 0;
fIsBW = false;
- fColor = kInvalid_Color;
}
~HDCOffscreen() {
@@ -414,8 +414,7 @@ public:
fXform = xform;
}
- const void* draw(const SkGlyph&, bool isBW, SkGdiRGB fgColor,
- size_t* srcRBPtr);
+ const void* draw(const SkGlyph&, bool isBW, size_t* srcRBPtr);
private:
HDC fDC;
@@ -423,7 +422,6 @@ private:
HFONT fFont;
XFORM fXform;
void* fBits; // points into fBM
- COLORREF fColor;
int fWidth;
int fHeight;
bool fIsBW;
@@ -436,7 +434,7 @@ private:
};
const void* HDCOffscreen::draw(const SkGlyph& glyph, bool isBW,
- SkGdiRGB fgColor, size_t* srcRBPtr) {
+ size_t* srcRBPtr) {
if (0 == fDC) {
fDC = CreateCompatibleDC(0);
if (0 == fDC) {
@@ -446,7 +444,10 @@ const void* HDCOffscreen::draw(const SkGlyph& glyph, bool isBW,
SetBkMode(fDC, TRANSPARENT);
SetTextAlign(fDC, TA_LEFT | TA_BASELINE);
SelectObject(fDC, fFont);
- fColor = kInvalid_Color;
+
+ COLORREF color = 0x00FFFFFF;
+ COLORREF prev = SetTextColor(fDC, color);
+ SkASSERT(prev != CLR_INVALID);
}
if (fBM && (fIsBW != isBW || fWidth < glyph.fWidth || fHeight < glyph.fHeight)) {
@@ -455,16 +456,6 @@ const void* HDCOffscreen::draw(const SkGlyph& glyph, bool isBW,
}
fIsBW = isBW;
- COLORREF color = fgColor;
- if (fIsBW) {
- color = 0xFFFFFF;
- }
- if (fColor != color) {
- fColor = color;
- COLORREF prev = SetTextColor(fDC, color);
- SkASSERT(prev != CLR_INVALID);
- }
-
fWidth = SkMax32(fWidth, glyph.fWidth);
fHeight = SkMax32(fHeight, glyph.fHeight);
@@ -498,8 +489,7 @@ const void* HDCOffscreen::draw(const SkGlyph& glyph, bool isBW,
// erase
size_t srcRB = isBW ? (biWidth >> 3) : (fWidth << 2);
size_t size = fHeight * srcRB;
- unsigned bg = (0 == color) ? 0xFF : 0;
- memset(fBits, bg, size);
+ memset(fBits, 0, size);
XFORM xform = fXform;
xform.eDx = (float)-glyph.fLeft;
@@ -525,14 +515,13 @@ public:
virtual ~SkScalerContext_Windows();
protected:
- virtual unsigned generateGlyphCount();
- virtual uint16_t generateCharToGlyph(SkUnichar uni);
- virtual void generateAdvance(SkGlyph* glyph);
- virtual void generateMetrics(SkGlyph* glyph);
- virtual void generateImage(const SkGlyph& glyph);
- virtual void generatePath(const SkGlyph& glyph, SkPath* path);
- virtual void generateFontMetrics(SkPaint::FontMetrics* mX,
- SkPaint::FontMetrics* mY);
+ virtual unsigned generateGlyphCount() SK_OVERRIDE;
+ virtual uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE;
+ virtual void generateAdvance(SkGlyph* glyph) SK_OVERRIDE;
+ virtual void generateMetrics(SkGlyph* glyph) SK_OVERRIDE;
+ virtual void generateImage(const SkGlyph& glyph, SkMaskGamma::PreBlend* maskPreBlend) SK_OVERRIDE;
+ virtual void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE;
+ virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY) SK_OVERRIDE;
private:
HDCOffscreen fOffscreen;
@@ -914,59 +903,82 @@ void SkScalerContext_Windows::generateFontMetrics(SkPaint::FontMetrics* mx, SkPa
static void build_power_table(uint8_t table[], float ee) {
for (int i = 0; i < 256; i++) {
float x = i / 255.f;
- x = powf(x, ee);
+ x = sk_float_pow(x, ee);
int xx = SkScalarRound(SkFloatToScalar(x * 255));
table[i] = SkToU8(xx);
}
}
-// This will invert the gamma applied by GDI, so we can sort-of get linear values.
-// Needed when we draw non-black, non-white text, and don't know how to bias it.
-static const uint8_t* getInverseGammaTable() {
+/**
+ * This will invert the gamma applied by GDI (gray-scale antialiased), so we
+ * can get linear values.
+ *
+ * GDI grayscale appears to use a hard-coded gamma of 2.3.
+ *
+ * GDI grayscale appears to draw using the black and white rasterizer at four
+ * times the size and then downsamples to compute the coverage mask. As a
+ * result there are only seventeen total grays. This lack of fidelity means
+ * that shifting into other color spaces is imprecise.
+ */
+static const uint8_t* getInverseGammaTableGDI() {
+ static bool gInited;
+ static uint8_t gTableGdi[256];
+ if (!gInited) {
+ build_power_table(gTableGdi, 2.3f);
+ gInited = true;
+ }
+ return gTableGdi;
+}
+
+/**
+ * This will invert the gamma applied by GDI ClearType, so we can get linear
+ * values.
+ *
+ * GDI ClearType uses SPI_GETFONTSMOOTHINGCONTRAST / 1000 as the gamma value.
+ * If this value is not specified, the default is a gamma of 1.4.
+ */
+static const uint8_t* getInverseGammaTableClearType() {
static bool gInited;
- static uint8_t gTable[256];
+ static uint8_t gTableClearType[256];
if (!gInited) {
UINT level = 0;
if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &level, 0) || !level) {
// can't get the data, so use a default
level = 1400;
}
- build_power_table(gTable, level / 1000.0f);
+ build_power_table(gTableClearType, level / 1000.0f);
gInited = true;
}
- return gTable;
+ return gTableClearType;
}
#include "SkColorPriv.h"
-// gdi's bitmap is upside-down, so we reverse dst walking in Y
-// whenever we copy it into skia's buffer
-
-static int compute_luminance(int r, int g, int b) {
-// return (r * 2 + g * 5 + b) >> 3;
- return (r * 27 + g * 92 + b * 9) >> 7;
-}
-
-static inline uint8_t rgb_to_a8(SkGdiRGB rgb) {
- int r = (rgb >> 16) & 0xFF;
- int g = (rgb >> 8) & 0xFF;
- int b = (rgb >> 0) & 0xFF;
- return compute_luminance(r, g, b);
+template<bool APPLY_PREBLEND>
+static inline uint8_t rgb_to_a8(SkGdiRGB rgb, const uint8_t* table8) {
+ SkASSERT( ((rgb >> 16) & 0xFF) == ((rgb >> 8) & 0xFF) &&
+ ((rgb >> 16) & 0xFF) == ((rgb >> 0) & 0xFF) );
+ return sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, table8);
}
-static inline uint16_t rgb_to_lcd16(SkGdiRGB rgb) {
- int r = (rgb >> 16) & 0xFF;
- int g = (rgb >> 8) & 0xFF;
- int b = (rgb >> 0) & 0xFF;
- return SkPackRGB16(SkR32ToR16(r), SkG32ToG16(g), SkB32ToB16(b));
+template<bool APPLY_PREBLEND>
+static inline uint16_t rgb_to_lcd16(SkGdiRGB rgb, const uint8_t* tableR,
+ const uint8_t* tableG,
+ const uint8_t* tableB) {
+ U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
+ U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
+ U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
+ return SkPack888ToRGB16(r, g, b);
}
-static inline SkPMColor rgb_to_lcd32(SkGdiRGB rgb) {
- int r = (rgb >> 16) & 0xFF;
- int g = (rgb >> 8) & 0xFF;
- int b = (rgb >> 0) & 0xFF;
- int a = SkMax32(r, SkMax32(g, b));
- return SkPackARGB32(a, r, g, b);
+template<bool APPLY_PREBLEND>
+static inline SkPMColor rgb_to_lcd32(SkGdiRGB rgb, const uint8_t* tableR,
+ const uint8_t* tableG,
+ const uint8_t* tableB) {
+ U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
+ U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
+ U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
+ return SkPackARGB32(0xFF, r, g, b);
}
// Is this GDI color neither black nor white? If so, we have to keep this
@@ -993,8 +1005,10 @@ static bool is_rgb_really_bw(const SkGdiRGB* src, int width, int height, int src
return true;
}
+// gdi's bitmap is upside-down, so we reverse dst walking in Y
+// whenever we copy it into skia's buffer
static void rgb_to_bw(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
- const SkGlyph& glyph, int32_t xorMask) {
+ const SkGlyph& glyph) {
const int width = glyph.fWidth;
const size_t dstRB = (width + 7) >> 3;
uint8_t* SK_RESTRICT dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
@@ -1010,14 +1024,14 @@ static void rgb_to_bw(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
if (byteCount > 0) {
for (int i = 0; i < byteCount; ++i) {
unsigned byte = 0;
- byte |= (src[0] ^ xorMask) & (1 << 7);
- byte |= (src[1] ^ xorMask) & (1 << 6);
- byte |= (src[2] ^ xorMask) & (1 << 5);
- byte |= (src[3] ^ xorMask) & (1 << 4);
- byte |= (src[4] ^ xorMask) & (1 << 3);
- byte |= (src[5] ^ xorMask) & (1 << 2);
- byte |= (src[6] ^ xorMask) & (1 << 1);
- byte |= (src[7] ^ xorMask) & (1 << 0);
+ byte |= src[0] & (1 << 7);
+ byte |= src[1] & (1 << 6);
+ byte |= src[2] & (1 << 5);
+ byte |= src[3] & (1 << 4);
+ byte |= src[4] & (1 << 3);
+ byte |= src[5] & (1 << 2);
+ byte |= src[6] & (1 << 1);
+ byte |= src[7] & (1 << 0);
dst[i] = byte;
src += 8;
}
@@ -1026,7 +1040,7 @@ static void rgb_to_bw(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
unsigned byte = 0;
unsigned mask = 0x80;
for (int i = 0; i < bitCount; i++) {
- byte |= (src[i] ^ xorMask) & mask;
+ byte |= src[i] & mask;
mask >>= 1;
}
dst[byteCount] = byte;
@@ -1036,48 +1050,51 @@ static void rgb_to_bw(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
}
}
+template<bool APPLY_PREBLEND>
static void rgb_to_a8(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
- const SkGlyph& glyph, int32_t xorMask) {
+ const SkGlyph& glyph, const uint8_t* table8) {
const size_t dstRB = glyph.rowBytes();
const int width = glyph.fWidth;
uint8_t* SK_RESTRICT dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
for (int y = 0; y < glyph.fHeight; y++) {
for (int i = 0; i < width; i++) {
- dst[i] = rgb_to_a8(src[i] ^ xorMask);
+ dst[i] = rgb_to_a8<APPLY_PREBLEND>(src[i], table8);
}
src = SkTAddByteOffset(src, srcRB);
dst -= dstRB;
}
}
-static void rgb_to_lcd16(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
- const SkGlyph& glyph, int32_t xorMask) {
+template<bool APPLY_PREBLEND>
+static void rgb_to_lcd16(const SkGdiRGB* SK_RESTRICT src, size_t srcRB, const SkGlyph& glyph,
+ const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
const size_t dstRB = glyph.rowBytes();
const int width = glyph.fWidth;
uint16_t* SK_RESTRICT dst = (uint16_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
for (int y = 0; y < glyph.fHeight; y++) {
for (int i = 0; i < width; i++) {
- dst[i] = rgb_to_lcd16(src[i] ^ xorMask);
+ dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(src[i], tableR, tableG, tableB);
}
src = SkTAddByteOffset(src, srcRB);
dst = (uint16_t*)((char*)dst - dstRB);
}
}
-static void rgb_to_lcd32(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
- const SkGlyph& glyph, int32_t xorMask) {
+template<bool APPLY_PREBLEND>
+static void rgb_to_lcd32(const SkGdiRGB* SK_RESTRICT src, size_t srcRB, const SkGlyph& glyph,
+ const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
const size_t dstRB = glyph.rowBytes();
const int width = glyph.fWidth;
- SkPMColor* SK_RESTRICT dst = (SkPMColor*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
+ uint32_t* SK_RESTRICT dst = (uint32_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
for (int y = 0; y < glyph.fHeight; y++) {
for (int i = 0; i < width; i++) {
- dst[i] = rgb_to_lcd32(src[i] ^ xorMask);
+ dst[i] = rgb_to_lcd32<APPLY_PREBLEND>(src[i], tableR, tableG, tableB);
}
src = SkTAddByteOffset(src, srcRB);
- dst = (SkPMColor*)((char*)dst - dstRB);
+ dst = (uint32_t*)((char*)dst - dstRB);
}
}
@@ -1086,46 +1103,44 @@ static inline unsigned clamp255(unsigned x) {
return x - (x >> 8);
}
-#define WHITE_LUMINANCE_LIMIT 0xA0
-#define BLACK_LUMINANCE_LIMIT 0x40
-
-void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) {
- SkAutoMutexAcquire ac(gFTMutex);
-
+void SkScalerContext_Windows::generateImage(const SkGlyph& glyph, SkMaskGamma::PreBlend* maskPreBlend) {
+ SkAutoMutexAcquire ac(gFTMutex);
SkASSERT(fDDC);
+ //Must be careful not to use these if maskPreBlend == NULL
+ const uint8_t* tableR = NULL;
+ const uint8_t* tableG = NULL;
+ const uint8_t* tableB = NULL;
+ if (maskPreBlend) {
+ tableR = maskPreBlend->fR;
+ tableG = maskPreBlend->fG;
+ tableB = maskPreBlend->fB;
+ }
+
const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat;
const bool isAA = !isLCD(fRec);
- bool isWhite = fRec.getLuminanceByte() >= WHITE_LUMINANCE_LIMIT;
- bool isBlack = fRec.getLuminanceByte() <= BLACK_LUMINANCE_LIMIT;
-
- SkGdiRGB fgColor;
- uint32_t rgbXOR;
- const uint8_t* table = NULL;
- if (isBW || isWhite) {
- fgColor = 0x00FFFFFF;
- rgbXOR = 0;
- } else if (isBlack) {
- fgColor = 0;
- rgbXOR = ~0;
- } else {
- table = getInverseGammaTable();
- fgColor = 0x00FFFFFF;
- rgbXOR = 0;
- }
size_t srcRB;
- const void* bits = fOffscreen.draw(glyph, isBW, fgColor, &srcRB);
+ const void* bits = fOffscreen.draw(glyph, isBW, &srcRB);
if (NULL == bits) {
ensure_typeface_accessible(fRec.fFontID);
- bits = fOffscreen.draw(glyph, isBW, fgColor, &srcRB);
+ bits = fOffscreen.draw(glyph, isBW, &srcRB);
if (NULL == bits) {
sk_bzero(glyph.fImage, glyph.computeImageSize());
return;
}
}
- if (table) {
+ if (!isBW) {
+ const uint8_t* table = getInverseGammaTableClearType();
+ if (isAA) {
+ table = getInverseGammaTableGDI();
+ }
+ //Note that the following cannot really be integrated into the
+ //pre-blend, since we may not be applying the pre-blend; when we aren't
+ //applying the pre-blend it means that a filter wants linear anyway.
+ //Other code may also be applying the pre-blend, so we'd need another
+ //one with this and one without.
SkGdiRGB* addr = (SkGdiRGB*)bits;
for (int y = 0; y < glyph.fHeight; ++y) {
for (int x = 0; x < glyph.fWidth; ++x) {
@@ -1152,18 +1167,30 @@ void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) {
// since the caller may require A8 for maskfilters, we can't check for BW
// ... until we have the caller tell us that explicitly
const SkGdiRGB* src = (const SkGdiRGB*)bits;
- rgb_to_a8(src, srcRB, glyph, rgbXOR);
+ if (maskPreBlend) {
+ rgb_to_a8<true>(src, srcRB, glyph, tableG);
+ } else {
+ rgb_to_a8<false>(src, srcRB, glyph, tableG);
+ }
} else { // LCD16
const SkGdiRGB* src = (const SkGdiRGB*)bits;
if (is_rgb_really_bw(src, width, glyph.fHeight, srcRB)) {
- rgb_to_bw(src, srcRB, glyph, rgbXOR);
+ rgb_to_bw(src, srcRB, glyph);
((SkGlyph*)&glyph)->fMaskFormat = SkMask::kBW_Format;
} else {
if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
- rgb_to_lcd16(src, srcRB, glyph, rgbXOR);
+ if (maskPreBlend) {
+ rgb_to_lcd16<true>(src, srcRB, glyph, tableR, tableG, tableB);
+ } else {
+ rgb_to_lcd16<false>(src, srcRB, glyph, tableR, tableG, tableB);
+ }
} else {
SkASSERT(SkMask::kLCD32_Format == glyph.fMaskFormat);
- rgb_to_lcd32(src, srcRB, glyph, rgbXOR);
+ if (maskPreBlend) {
+ rgb_to_lcd32<true>(src, srcRB, glyph, tableR, tableG, tableB);
+ } else {
+ rgb_to_lcd32<false>(src, srcRB, glyph, tableR, tableG, tableB);
+ }
}
}
}
@@ -1665,21 +1692,6 @@ void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
#endif
rec->setHinting(h);
- // for compatibility at the moment, discretize luminance to 3 settings
- // black, white, gray. This helps with fontcache utilization, since we
- // won't create multiple entries that in the end map to the same results.
- {
- unsigned lum = rec->getLuminanceByte();
- if (lum <= BLACK_LUMINANCE_LIMIT) {
- lum = 0;
- } else if (lum >= WHITE_LUMINANCE_LIMIT) {
- lum = SkScalerContext::kLuminance_Max;
- } else {
- lum = SkScalerContext::kLuminance_Max >> 1;
- }
- rec->setLuminanceBits(lum);
- }
-
// turn this off since GDI might turn A8 into BW! Need a bigger fix.
#if 0
// Disable LCD when rotated, since GDI's output is ugly
diff --git a/src/ports/sk_predefined_gamma.h b/src/ports/sk_predefined_gamma.h
deleted file mode 100644
index d363594900..0000000000
--- a/src/ports/sk_predefined_gamma.h
+++ /dev/null
@@ -1,51 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#ifndef SK_PREDEFINED_GAMMA_H
-#define SK_PREDEFINED_GAMMA_H
-
-// Gamma table for 1.4
-static const uint8_t gBlackGamma[] = {
- 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05,
- 0x05, 0x06, 0x06, 0x07, 0x07, 0x08, 0x08, 0x09, 0x09, 0x0A, 0x0A, 0x0B, 0x0C, 0x0C, 0x0D, 0x0D,
- 0x0E, 0x0F, 0x0F, 0x10, 0x10, 0x11, 0x12, 0x12, 0x13, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17, 0x18,
- 0x19, 0x19, 0x1A, 0x1B, 0x1C, 0x1C, 0x1D, 0x1E, 0x1F, 0x1F, 0x20, 0x21, 0x22, 0x22, 0x23, 0x24,
- 0x25, 0x26, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x31,
- 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40,
- 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
- 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60,
- 0x61, 0x62, 0x63, 0x64, 0x65, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
- 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x84,
- 0x85, 0x86, 0x87, 0x88, 0x89, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x91, 0x92, 0x93, 0x94, 0x95, 0x97,
- 0x98, 0x99, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA3, 0xA4, 0xA5, 0xA6, 0xA8, 0xA9, 0xAA,
- 0xAB, 0xAD, 0xAE, 0xAF, 0xB0, 0xB2, 0xB3, 0xB4, 0xB5, 0xB7, 0xB8, 0xB9, 0xBB, 0xBC, 0xBD, 0xBE,
- 0xC0, 0xC1, 0xC2, 0xC4, 0xC5, 0xC6, 0xC8, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF, 0xD1, 0xD2, 0xD3,
- 0xD5, 0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDD, 0xDE, 0xDF, 0xE1, 0xE2, 0xE3, 0xE5, 0xE6, 0xE8, 0xE9,
- 0xEA, 0xEC, 0xED, 0xEE, 0xF0, 0xF1, 0xF2, 0xF4, 0xF5, 0xF7, 0xF8, 0xF9, 0xFB, 0xFC, 0xFE, 0xFF,
-};
-
-// Gamma table for 0.714286
-static const uint8_t gWhiteGamma[] = {
- 0x00, 0x05, 0x08, 0x0B, 0x0D, 0x0F, 0x12, 0x14, 0x16, 0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20, 0x22,
- 0x23, 0x25, 0x26, 0x28, 0x29, 0x2B, 0x2C, 0x2E, 0x2F, 0x31, 0x32, 0x33, 0x35, 0x36, 0x37, 0x39,
- 0x3A, 0x3B, 0x3C, 0x3E, 0x3F, 0x40, 0x41, 0x43, 0x44, 0x45, 0x46, 0x48, 0x49, 0x4A, 0x4B, 0x4C,
- 0x4D, 0x4E, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E,
- 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E,
- 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E,
- 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D,
- 0x8E, 0x8F, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x97, 0x98, 0x99, 0x9A, 0x9B,
- 0x9C, 0x9D, 0x9E, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9,
- 0xAA, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB4, 0xB5, 0xB6,
- 0xB7, 0xB8, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC0, 0xC1, 0xC2, 0xC3,
- 0xC4, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCC, 0xCD, 0xCE, 0xCF, 0xCF,
- 0xD0, 0xD1, 0xD2, 0xD3, 0xD3, 0xD4, 0xD5, 0xD6, 0xD6, 0xD7, 0xD8, 0xD9, 0xD9, 0xDA, 0xDB, 0xDC,
- 0xDC, 0xDD, 0xDE, 0xDF, 0xDF, 0xE0, 0xE1, 0xE2, 0xE2, 0xE3, 0xE4, 0xE5, 0xE5, 0xE6, 0xE7, 0xE8,
- 0xE8, 0xE9, 0xEA, 0xEB, 0xEB, 0xEC, 0xED, 0xEE, 0xEE, 0xEF, 0xF0, 0xF1, 0xF1, 0xF2, 0xF3, 0xF3,
- 0xF4, 0xF5, 0xF6, 0xF6, 0xF7, 0xF8, 0xF9, 0xF9, 0xFA, 0xFB, 0xFB, 0xFC, 0xFD, 0xFE, 0xFE, 0xFF,
-};
-
-#endif