Gamma correcting masks.
authorbungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 30 Jul 2012 20:40:50 +0000 (20:40 +0000)
committerbungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 30 Jul 2012 20:40:50 +0000 (20:40 +0000)
https://codereview.appspot.com/6244068/

git-svn-id: http://skia.googlecode.com/svn/trunk@4841 2bbb7eff-a529-9590-31e7-b0007b416f81

17 files changed:
gyp/common.gypi
gyp/common_conditions.gypi
gyp/core.gyp
gyp/ports.gyp
include/core/SkColorPriv.h
include/core/SkFontHost.h
include/core/SkScalerContext.h
src/core/SkMaskGamma.cpp [new file with mode: 0644]
src/core/SkMaskGamma.h [new file with mode: 0644]
src/core/SkPaint.cpp
src/core/SkScalerContext.cpp
src/ports/SkFontHost_FreeType.cpp
src/ports/SkFontHost_gamma.cpp [deleted file]
src/ports/SkFontHost_gamma_none.cpp [deleted file]
src/ports/SkFontHost_mac_coretext.cpp
src/ports/SkFontHost_win.cpp
src/ports/sk_predefined_gamma.h [deleted file]

index 06db4a8..4b558f8 100644 (file)
   ],
 
   'target_defaults': {
+    'defines': [
+      'SK_GAMMA_SRGB',
+      'SK_GAMMA_APPLY_TO_A8',
+    ],
 
     # Validate the 'skia_os' setting against 'OS', because only certain
     # combinations work.  You should only override 'skia_os' for certain
index 2f58c1b..6dcfd81 100644 (file)
         'defines': [
           'SK_SAMPLES_FOR_X',
           'SK_BUILD_FOR_UNIX',
-          'SK_USE_COLOR_LUMINANCE',
-          'SK_GAMMA_APPLY_TO_A8',
         ],
         'configurations': {
           'Debug': {
             'cflags': ['-g']
           },
           'Release': {
-            'cflags': ['-O3'],
+            'cflags': ['-O3 -g'],
             'defines': [ 'NDEBUG' ],
           },
         },
index b4753b8..2ad0da8 100644 (file)
@@ -85,6 +85,8 @@
         '../src/core/SkMallocPixelRef.cpp',
         '../src/core/SkMask.cpp',
         '../src/core/SkMaskFilter.cpp',
+        '../src/core/SkMaskGamma.cpp',
+        '../src/core/SkMaskGamma.h',
         '../src/core/SkMath.cpp',
         '../src/core/SkMatrix.cpp',
         '../src/core/SkMetaData.cpp',
index 9436970..b9c1412 100644 (file)
         '../src/ports/SkTime_Unix.cpp',
         '../src/ports/SkTime_win.cpp',
         '../src/ports/SkXMLParser_empty.cpp',
-        '../src/ports/sk_predefined_gamma.h',
       ],
       'conditions': [
         [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
           'sources': [
             '../src/ports/SkThread_pthread.cpp',
             '../src/ports/SkFontHost_FreeType.cpp',
-            '../src/ports/SkFontHost_gamma_none.cpp',
             '../src/ports/SkFontHost_linux.cpp',
           ],
         }],
@@ -54,7 +52,6 @@
             '../src/utils/mac/SkStream_mac.cpp',
 #            '../src/ports/SkFontHost_FreeType.cpp',
 #            '../src/ports/SkFontHost_freetype_mac.cpp',
-#            '../src/ports/SkFontHost_gamma_none.cpp',
             '../src/ports/SkThread_pthread.cpp',
           ],
           'sources!': [
@@ -94,7 +91,6 @@
             '../src/ports/SkDebug_android.cpp',
             '../src/ports/SkThread_pthread.cpp',
             '../src/ports/SkFontHost_android.cpp',
-            '../src/ports/SkFontHost_gamma.cpp',
             '../src/ports/SkFontHost_FreeType.cpp',
             '../src/ports/FontHostConfiguration_android.cpp',
             #TODO: include the ports/SkImageRef_ashmem.cpp for non-NDK builds
index a2a9c58..073a4f5 100644 (file)
 #include "SkColor.h"
 #include "SkMath.h"
 
+///@{
+/** See ITU-R Recommendation BT.709 at http://www.itu.int/rec/R-REC-BT.709/ .*/
+#define SK_ITU_BT709_LUM_COEFF_R (0.2126f)
+#define SK_ITU_BT709_LUM_COEFF_G (0.7152f)
+#define SK_ITU_BT709_LUM_COEFF_B (0.0722f)
+///@}
+
+///@{
+/** A float value which specifies this channel's contribution to luminance. */
+#define SK_LUM_COEFF_R SK_ITU_BT709_LUM_COEFF_R
+#define SK_LUM_COEFF_G SK_ITU_BT709_LUM_COEFF_G
+#define SK_LUM_COEFF_B SK_ITU_BT709_LUM_COEFF_B
+///@}
+
 /** Turn 0..255 into 0..256 by adding 1 at the half-way point. Used to turn a
     byte into a scale value, so that we can say scale * value >> 8 instead of
     alpha * value / 255.
index 31f628f..7e89ee3 100644 (file)
@@ -227,15 +227,6 @@ public:
 
     ///////////////////////////////////////////////////////////////////////////
 
-    /** DEPRECATED -- only called by SkFontHost_FreeType internally
-
-        Return NULL or a pointer to 256 bytes for the black (table[0]) and
-        white (table[1]) gamma tables.
-    */
-    static void GetGammaTables(const uint8_t* tables[2]);
-
-    ///////////////////////////////////////////////////////////////////////////
-
     /** LCDs either have their color elements arranged horizontally or
         vertically. When rendering subpixel glyphs we need to know which way
         round they are.
index ccc4fa9..adbdf8a 100644 (file)
@@ -9,22 +9,27 @@
 #define SkScalerContext_DEFINED
 
 #include "SkMask.h"
+#include "SkMaskGamma.h"
 #include "SkMatrix.h"
 #include "SkPaint.h"
 
 #ifdef SK_BUILD_FOR_ANDROID
-  //For SkFontID
-  #include "SkTypeface.h"
+    //For SkFontID
+    #include "SkTypeface.h"
 #endif
 
-//#define SK_USE_COLOR_LUMINANCE
-
 struct SkGlyph;
 class SkDescriptor;
 class SkMaskFilter;
 class SkPathEffect;
 class SkRasterizer;
 
+//The following typedef hides from the rest of the implementation the number of
+//most significant bits to consider when creating mask gamma tables. Two bits
+//per channel was chosen as a balance between fidelity (more bits) and cache
+//sizes (fewer bits).
+typedef SkTMaskGamma<2, 2, 2> SkMaskGamma;
+
 class SkScalerContext {
 public:
     enum Flags {
@@ -49,24 +54,11 @@ public:
         // Generate A8 from LCD source (for GDI), only meaningful if fMaskFormat is kA8
         // Perhaps we can store this (instead) in fMaskFormat, in hight bit?
         kGenA8FromLCD_Flag        = 0x0800,
-
-#ifdef SK_USE_COLOR_LUMINANCE
-        kLuminance_Bits           = 3
-#else
-        // luminance : 0 for black text, kLuminance_Max for white text
-        kLuminance_Shift          = 13, // shift to land in the high 3-bits of Flags
-        kLuminance_Bits           = 3  // ensure Flags doesn't exceed 16bits
-#endif
     };
     
     // computed values
     enum {
         kHinting_Mask   = kHintingBit1_Flag | kHintingBit2_Flag,
-#ifdef SK_USE_COLOR_LUMINANCE
-#else
-        kLuminance_Max  = (1 << kLuminance_Bits) - 1,
-        kLuminance_Mask = kLuminance_Max << kLuminance_Shift
-#endif
     };
 
     struct Rec {
@@ -75,9 +67,49 @@ public:
         SkScalar    fTextSize, fPreScaleX, fPreSkewX;
         SkScalar    fPost2x2[2][2];
         SkScalar    fFrameWidth, fMiterLimit;
-#ifdef SK_USE_COLOR_LUMINANCE
+
+        //These describe the parameters to create (uniquely identify) the pre-blend.
         uint32_t    fLumBits;
-#endif
+        uint8_t     fDeviceGamma; //2.6, (0.0, 4.0) gamma, 0.0 for sRGB
+        uint8_t     fPaintGamma;  //2.6, (0.0, 4.0) gamma, 0.0 for sRGB
+        uint8_t     fContrast;    //0.8+1, [0.0, 1.0] artificial contrast
+        uint8_t     fReservedAlign;
+
+        SkScalar getDeviceGamma() const {
+            return SkIntToScalar(fDeviceGamma) / (1 << 6);
+        }
+        void setDeviceGamma(SkScalar dg) {
+            SkASSERT(0 <= dg && dg < SkIntToScalar(4));
+            fDeviceGamma = SkScalarFloorToInt(dg * (1 << 6));
+        }
+
+        SkScalar getPaintGamma() const {
+            return SkIntToScalar(fPaintGamma) / (1 << 6);
+        }
+        void setPaintGamma(SkScalar pg) {
+            SkASSERT(0 <= pg && pg < SkIntToScalar(4));
+            fPaintGamma = SkScalarFloorToInt(pg * (1 << 6));
+        }
+
+        SkScalar getContrast() const {
+            return SkIntToScalar(fContrast) / ((1 << 8) - 1);
+        }
+        void setContrast(SkScalar c) {
+            SkASSERT(0 <= c && c <= SK_Scalar1);
+            fContrast = SkScalarRoundToInt(c * ((1 << 8) - 1));
+        }
+
+        /**
+         *  Causes the luminance color and contrast to be ignored, and the
+         *  paint and device gamma to be effectively 1.0.
+         */
+        void ignorePreBlend() {
+            setLuminanceColor(0x00000000);
+            setPaintGamma(SK_Scalar1);
+            setDeviceGamma(SK_Scalar1);
+            setContrast(0);
+        }
+
         uint8_t     fMaskFormat;
         uint8_t     fStrokeJoin;
         uint16_t    fFlags;
@@ -102,8 +134,7 @@ public:
         SkMask::Format getFormat() const {
             return static_cast<SkMask::Format>(fMaskFormat);
         }
-        
-#ifdef SK_USE_COLOR_LUMINANCE
+
         SkColor getLuminanceColor() const {
             return fLumBits;
         }
@@ -111,24 +142,6 @@ public:
         void setLuminanceColor(SkColor c) {
             fLumBits = c;
         }
-#else
-        unsigned getLuminanceBits() const {
-            return (fFlags & kLuminance_Mask) >> kLuminance_Shift;
-        }
-        
-        void setLuminanceBits(unsigned lum) {
-            SkASSERT(lum <= kLuminance_Max);
-            fFlags = (fFlags & ~kLuminance_Mask) | (lum << kLuminance_Shift);
-        }
-
-        U8CPU getLuminanceByte() const {
-            SkASSERT(3 == kLuminance_Bits);
-            unsigned lum = this->getLuminanceBits();
-            lum |= (lum << kLuminance_Bits);
-            lum |= (lum << kLuminance_Bits*2);
-            return lum >> (4*kLuminance_Bits - 8);
-        }
-#endif
     };
 
     SkScalerContext(const SkDescriptor* desc);
@@ -185,9 +198,10 @@ public:
 #endif
 
     static inline void MakeRec(const SkPaint&, const SkMatrix*, Rec* rec);
-    static inline void PostMakeRec(Rec*);
+    static inline void PostMakeRec(const SkPaint&, Rec*);
 
     static SkScalerContext* Create(const SkDescriptor*);
+    static SkMaskGamma::PreBlend GetMaskPreBlend(const Rec& rec);
 
 protected:
     Rec         fRec;
@@ -197,7 +211,7 @@ protected:
     virtual uint16_t generateCharToGlyph(SkUnichar) = 0;
     virtual void generateAdvance(SkGlyph*) = 0;
     virtual void generateMetrics(SkGlyph*) = 0;
-    virtual void generateImage(const SkGlyph&) = 0;
+    virtual void generateImage(const SkGlyph&, SkMaskGamma::PreBlend* maskPreBlend) = 0;
     virtual void generatePath(const SkGlyph&, SkPath*) = 0;
     virtual void generateFontMetrics(SkPaint::FontMetrics* mX,
                                      SkPaint::FontMetrics* mY) = 0;
@@ -227,6 +241,9 @@ private:
 
     // link-list of context, to handle missing chars. null-terminated.
     SkScalerContext* fNextContext;
+
+    // converts linear masks to gamma correcting masks.
+    SkMaskGamma::PreBlend fMaskPreBlend;
 };
 
 #define kRec_SkDescriptorTag            SkSetFourByteTag('s', 'r', 'e', 'c')
diff --git a/src/core/SkMaskGamma.cpp b/src/core/SkMaskGamma.cpp
new file mode 100644 (file)
index 0000000..47903fb
--- /dev/null
@@ -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 (file)
index 0000000..e8fa703
--- /dev/null
@@ -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
index f240737..04a8f5a 100644 (file)
@@ -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"
index 7c26921..9d99713 100644 (file)
@@ -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) {
index 0d72fa1..497a259 100644 (file)
@@ -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"
 //#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 (file)
index 0d15414..0000000
+++ /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 (file)
index 18f113c..0000000
+++ /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;
-}
-
index cdd4a74..c723c88 100644 (file)
@@ -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;
-        }
     }
 }
 
index 082e6e4..fbe4bb1 100755 (executable)
@@ -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 (file)
index d363594..0000000
+++ /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