check-point for kLCD16_Format mask support
authorreed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 10 Mar 2011 15:06:27 +0000 (15:06 +0000)
committerreed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 10 Mar 2011 15:06:27 +0000 (15:06 +0000)
disabled for now in SkPaint.cpp (for further testing)

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

include/core/SkMask.h
include/core/SkScalerContext.h
samplecode/SampleXfermodes.cpp
src/core/SkBlitter_ARGB32.cpp
src/core/SkPaint.cpp
src/core/SkScalerContext.cpp
src/ports/SkFontHost_mac_coretext.cpp

index 58a249341fcf9e36811dda1806f42cc5a4f4f3b1..ebb870feaf9ef3be356c1366f72e307c792c5819 100644 (file)
@@ -47,6 +47,7 @@ struct SkMask {
         kHorizontalLCD_Format,  //!< 4 bytes/pixel: a/r/g/b
         kVerticalLCD_Format,    //!< 4 bytes/pixel: a/r/g/b
         kARGB32_Format,         //!< SkPMColor
+        kLCD16_Format           //!< 565 alpha for r/g/b
     };
 
     enum {
@@ -96,6 +97,19 @@ struct SkMask {
         return fImage + x - fBounds.fLeft + (y - fBounds.fTop) * fRowBytes;
     }
 
+    /**
+     *  Return the address of the specified 16bit mask. In the debug build,
+     *  this asserts that the mask's format is kLCD16_Format, and that (x,y)
+     *  are contained in the mask's fBounds.
+     */
+    uint16_t* getAddrLCD16(int x, int y) const {
+        SkASSERT(kLCD16_Format == fFormat);
+        SkASSERT(fBounds.contains(x, y));
+        SkASSERT(fImage != NULL);
+        uint16_t* row = (uint16_t*)(fImage + (y - fBounds.fTop) * fRowBytes);
+        return row + (x - fBounds.fLeft);
+    }
+
     /** Return an address into the 32-bit plane of an LCD or VerticalLCD mask
         for the given position.
     */
@@ -120,7 +134,7 @@ struct SkMask {
 
     static uint8_t* AllocImage(size_t bytes);
     static void FreeImage(void* image);
-    
+
     enum CreateMode {
         kJustComputeBounds_CreateMode,      //!< compute bounds and return
         kJustRenderImage_CreateMode,        //!< render into preallocate mask
index 317658dbd1073f93363dc8f8a02aefd7d9e57403..584b86bfb65128e1f7da913795b0a8c2dbf0dcc8 100644 (file)
@@ -53,18 +53,27 @@ struct SkGlyph {
         fMaskFormat     = MASK_FORMAT_UNKNOWN;
     }
 
-    unsigned rowBytes() const {
-        unsigned rb = fWidth;
-        if (SkMask::kBW_Format == fMaskFormat) {
+    /**
+     *  Compute the rowbytes for the specified width and mask-format.
+     */
+    static unsigned ComputeRowBytes(unsigned width, SkMask::Format format) {
+        unsigned rb = width;
+        if (SkMask::kBW_Format == format) {
             rb = (rb + 7) >> 3;
-               } else if (SkMask::kARGB32_Format == fMaskFormat) {
+               } else if (SkMask::kARGB32_Format == format) {
                        rb <<= 2;
+               } else if (SkMask::kLCD16_Format == format) {
+                       rb = SkAlign4(rb << 1);
         } else {
             rb = SkAlign4(rb);
         }
         return rb;
     }
 
+    unsigned rowBytes() const {
+        return ComputeRowBytes(fWidth, (SkMask::Format)fMaskFormat);
+    }
+
     bool isJustAdvance() const {
         return MASK_FORMAT_JUST_ADVANCE == fMaskFormat;
     }
index a85bee2240cb78fc501b6f94f212895b22d0ea91..b5361e35a6df7c11d28320f18c872a2987cc63d4 100644 (file)
 #include "SkColorPriv.h"
 #include "SkImageDecoder.h"
 
+static void setNamedTypeface(SkPaint* paint, const char name[]) {
+    SkTypeface* face = SkTypeface::CreateFromName(name, SkTypeface::kNormal);
+    paint->setTypeface(face);
+    SkSafeUnref(face);
+}
+
 #if 0
 static int newscale(U8CPU a, U8CPU b, int shift) {
     unsigned prod = a * b + (1 << (shift - 1));
@@ -35,7 +41,7 @@ static void test_srcover565(SkCanvas* canvas) {
     bm1.setConfig(SkBitmap::kRGB_565_Config, width, 256); bm1.allocPixels(NULL);
     bm2.setConfig(SkBitmap::kRGB_565_Config, width, 256); bm2.allocPixels(NULL);
     bm3.setConfig(SkBitmap::kRGB_565_Config, width, 256); bm3.allocPixels(NULL);
-    
+
     int rgb = 0x18;
     int r = rgb >> 3;
     int g = rgb >> 2;
@@ -44,7 +50,7 @@ static void test_srcover565(SkCanvas* canvas) {
         SkPMColor pm = SkPreMultiplyARGB(alpha, rgb, rgb, rgb);
         uint16_t newdst = SkSrcOver32To16(pm, dst);
         sk_memset16(bm1.getAddr16(0, alpha), newdst, bm1.width());
-        
+
         int ia = 255 - alpha;
         int iscale = SkAlpha255To256(ia);
         int dr = (SkGetPackedR32(pm) + (r * iscale >> 5)) >> 3;
@@ -54,7 +60,7 @@ static void test_srcover565(SkCanvas* canvas) {
 
         int dr2 = (SkMulDiv255Round(alpha, rgb) + newscale(r, ia, 5)) >> 3;
         int dg2 = (SkMulDiv255Round(alpha, rgb) + newscale(g, ia, 6)) >> 2;
-        
+
         sk_memset16(bm3.getAddr16(0, alpha), SkPackRGB16(dr2, dg2, dr2), bm3.width());
 
 //        if (mr != dr || mg != dg)
@@ -62,13 +68,13 @@ static void test_srcover565(SkCanvas* canvas) {
 //            SkDebugf("[%d] macro [%d %d] inline [%d %d] new [%d %d]\n", alpha, mr, mg, dr, dg, dr2, dg2);
         }
     }
-    
+
     SkScalar dx = SkIntToScalar(width+4);
-    
+
     canvas->drawBitmap(bm1, 0, 0, NULL); canvas->translate(dx, 0);
     canvas->drawBitmap(bm2, 0, 0, NULL); canvas->translate(dx, 0);
     canvas->drawBitmap(bm3, 0, 0, NULL); canvas->translate(dx, 0);
-    
+
     SkRect rect = { 0, 0, SkIntToScalar(bm1.width()), SkIntToScalar(bm1.height()) };
     SkPaint p;
     p.setARGB(0xFF, rgb, rgb, rgb);
@@ -76,7 +82,7 @@ static void test_srcover565(SkCanvas* canvas) {
 }
 #endif
 
-static void make_bitmaps(int w, int h, SkBitmap* src, SkBitmap* dst) {    
+static void make_bitmaps(int w, int h, SkBitmap* src, SkBitmap* dst) {
     src->setConfig(SkBitmap::kARGB_8888_Config, w, h);
     src->allocPixels();
     src->eraseColor(0);
@@ -88,10 +94,10 @@ static void make_bitmaps(int w, int h, SkBitmap* src, SkBitmap* dst) {
     SkScalar hh = SkIntToScalar(h);
 
     p.setAntiAlias(true);
-    p.setColor(0xFFFFCC44);    
+    p.setColor(0xFFFFCC44);
     r.set(0, 0, ww*3/4, hh*3/4);
     c.drawOval(r, p);
-    
+
     dst->setConfig(SkBitmap::kARGB_8888_Config, w, h);
     dst->allocPixels();
     dst->eraseColor(0);
@@ -112,26 +118,26 @@ class XfermodesView : public SkView {
                    SkScalar x, SkScalar y) {
         SkPaint p;
 
-        canvas->drawBitmap(fSrcB, x, y, &p);        
+        canvas->drawBitmap(fSrcB, x, y, &p);
         p.setAlpha(alpha);
         p.setXfermode(mode);
         canvas->drawBitmap(fDstB, x, y, &p);
     }
-    
+
 public:
     const static int W = 64;
     const static int H = 64;
        XfermodesView() {
         const int W = 64;
         const int H = 64;
-        
+
         fBG.setConfig(SkBitmap::kARGB_4444_Config, 2, 2, 4);
         fBG.setPixels(gBG);
         fBG.setIsOpaque(true);
-        
+
         make_bitmaps(W, H, &fSrcB, &fDstB);
     }
-    
+
 protected:
     // overrides from SkEventSink
     virtual bool onQuery(SkEvent* evt) {
@@ -145,12 +151,12 @@ protected:
     void drawBG(SkCanvas* canvas) {
         canvas->drawColor(SK_ColorWHITE);
     }
-    
+
     virtual void onDraw(SkCanvas* canvas) {
         canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
 
         this->drawBG(canvas);
-        
+
         const struct {
             SkXfermode::Mode  fMode;
             const char*         fLabel;
@@ -190,11 +196,14 @@ protected:
         SkMatrix m;
         m.setScale(SkIntToScalar(6), SkIntToScalar(6));
         s->setLocalMatrix(m);
-        
+
         SkPaint labelP;
         labelP.setAntiAlias(true);
+        labelP.setLCDRenderText(true);
         labelP.setTextAlign(SkPaint::kCenter_Align);
-        
+        setNamedTypeface(&labelP, "Menlo Regular");
+//        labelP.setTextSize(SkIntToScalar(11));
+
         const int W = 5;
 
         SkScalar x0 = 0;
@@ -210,7 +219,7 @@ protected:
                 p.setStyle(SkPaint::kFill_Style);
                 p.setShader(s);
                 canvas->drawRect(r, p);
-                
+
                 canvas->saveLayer(&r, NULL, SkCanvas::kARGB_ClipLayer_SaveFlag);
          //       canvas->save();
                 draw_mode(canvas, mode, twice ? 0x88 : 0xFF, r.fLeft, r.fTop);
index 09cbe13710eae5e64111464a47e5caec0d067e0e..d923315c16b1795581897baf2e076247e160122e 100644 (file)
@@ -38,6 +38,74 @@ extern uint32_t BlendLCDPixelWithBlack(const uint32_t alphaPixel, const uint32_t
 using namespace skia_blitter_support;
 #endif
 
+///////////////////////////////////////////////////////////////////////////////
+
+static int upscale31To256(int value) {
+    SkASSERT((unsigned)value <= 31);
+    // 0..31 -> 0..255
+    value = (value << 3) | (value >> 2);
+    // 0..255 -> 0..256
+    value += (value >> 7);
+    SkASSERT((unsigned)value <= 256);
+    return value;
+}
+
+static void blit_lcd16_opaque(SkPMColor dst[], const uint16_t src[],
+                              SkPMColor color, int width) {
+    int srcR = SkGetPackedR32(color);
+    int srcG = SkGetPackedG32(color);
+    int srcB = SkGetPackedB32(color);
+
+    for (int i = 0; i < width; i++) {
+        uint16_t mask = src[i];
+        if (0 == mask) {
+            continue;
+        }
+
+        /*  We want all of these in 5bits, hence the shifts in case one of them
+         *  (green) is 6bits.
+         */
+        int maskR = SkGetPackedR16(mask) >> (SK_R16_BITS - 5);
+        int maskG = SkGetPackedG16(mask) >> (SK_G16_BITS - 5);
+        int maskB = SkGetPackedB16(mask) >> (SK_B16_BITS - 5);
+
+        // Now upscale them to 0..256, so we can use SkAlphaBlend
+        maskR = upscale31To256(maskR);
+        maskG = upscale31To256(maskG);
+        maskB = upscale31To256(maskB);
+
+        int maskA = SkMax32(SkMax32(maskR, maskG), maskB);
+
+        SkPMColor d = dst[i];
+        int dstA = SkGetPackedA32(d);
+        int dstR = SkGetPackedR32(d);
+        int dstG = SkGetPackedG32(d);
+        int dstB = SkGetPackedB32(d);
+
+        dst[i] = SkPackARGB32(SkAlphaBlend(0xFF, dstA, maskA),
+                              SkAlphaBlend(srcR, dstR, maskR),
+                              SkAlphaBlend(srcG, dstG, maskG),
+                              SkAlphaBlend(srcB, dstB, maskB));
+    }
+}
+
+static void blitmask_lcd16(const SkBitmap& device, const SkMask& mask,
+                           const SkIRect& clip, SkPMColor srcColor) {
+    int x = clip.fLeft;
+    int y = clip.fTop;
+    int width = clip.width();
+    int height = clip.height();
+
+    SkPMColor*         dstRow = device.getAddr32(x, y);
+    const uint16_t* srcRow = mask.getAddrLCD16(x, y);
+
+    do {
+        blit_lcd16_opaque(dstRow, srcRow, srcColor, width);
+        dstRow = (SkPMColor*)((char*)dstRow + device.rowBytes());
+        srcRow = (const uint16_t*)((const char*)srcRow + mask.fRowBytes);
+    } while (--height != 0);
+}
+
 //////////////////////////////////////////////////////////////////////////////////////
 
 static void SkARGB32_Blit32(const SkBitmap& device, const SkMask& mask,
@@ -189,6 +257,9 @@ void SkARGB32_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) {
     } else if (SkMask::kARGB32_Format == mask.fFormat) {
                SkARGB32_Blit32(fDevice, mask, clip, fPMColor);
                return;
+    } else if (SkMask::kLCD16_Format == mask.fFormat) {
+        blitmask_lcd16(fDevice, mask, clip, fPMColor);
+        return;
     }
 
     int x = clip.fLeft;
@@ -210,6 +281,9 @@ void SkARGB32_Opaque_Blitter::blitMask(const SkMask& mask,
     } else if (SkMask::kARGB32_Format == mask.fFormat) {
                SkARGB32_Blit32(fDevice, mask, clip, fPMColor);
                return;
+    } else if (SkMask::kLCD16_Format == mask.fFormat) {
+        blitmask_lcd16(fDevice, mask, clip, fPMColor);
+        return;
        }
 
     int x = clip.fLeft;
@@ -319,6 +393,8 @@ void SkARGB32_Black_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) {
         SkARGB32_BlitBW(fDevice, mask, clip, black);
     } else if (SkMask::kARGB32_Format == mask.fFormat) {
                SkARGB32_Blit32(fDevice, mask, clip, fPMColor);
+    } else if (SkMask::kLCD16_Format == mask.fFormat) {
+        blitmask_lcd16(fDevice, mask, clip, fPMColor);
     } else {
 #if defined(SK_SUPPORT_LCDTEXT)
         const bool      lcdMode = mask.fFormat == SkMask::kHorizontalLCD_Format;
index 0be0d8fbf19449eed7d9b65e419f36515b9d323d..3543f7497c23dc819efc78cc3719dab679ee3c88 100644 (file)
@@ -1104,6 +1104,22 @@ static void add_flattenable(SkDescriptor* desc, uint32_t tag,
     buffer->flatten(desc->addEntry(tag, buffer->size(), NULL));
 }
 
+static bool canSupportLCD16(const SkPaint& paint) {
+#if 0
+    return  !paint.getShader() &&
+            !paint.getXfermode() && // unless its srcover
+            !paint.getMaskFilter() &&
+            !paint.getRasterizer() &&
+            !paint.getColorFilter() &&
+            !paint.getPathEffect() &&
+            !paint.isFakeBoldText() &&
+            paint.getStyle() == SkPaint::kFill_Style;
+#else
+    // disable for now, while we test more
+    return false;
+#endif
+}
+
 static SkMask::Format computeMaskFormat(const SkPaint& paint) {
     uint32_t flags = paint.getFlags();
 
@@ -1111,6 +1127,12 @@ static SkMask::Format computeMaskFormat(const SkPaint& paint) {
     if (!(flags & SkPaint::kAntiAlias_Flag))
         return SkMask::kBW_Format;
 
+    if (flags & SkPaint::kLCDRenderText_Flag) {
+        if (canSupportLCD16(paint)) {
+            return SkMask::kLCD16_Format;
+        }
+    }
+
 #if defined(SK_SUPPORT_LCDTEXT)
     if (flags & SkPaint::kLCDRenderText_Flag) {
         return SkFontHost::GetSubpixelOrientation() == SkFontHost::kHorizontal_LCDOrientation ?
index bbe01585ec349b0375ea10a2b5514d72dedb663f..ff8e1689d4c207b67e1ea4b53de561d2ad5017bc 100644 (file)
@@ -497,6 +497,7 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) {
 
     if (NULL == fMaskFilter &&
         fRec.fMaskFormat != SkMask::kBW_Format &&
+        fRec.fMaskFormat != SkMask::kLCD16_Format &&
         (fRec.fFlags & (kGammaForBlack_Flag | kGammaForWhite_Flag)) != 0)
     {
         const uint8_t* table = (fRec.fFlags & kGammaForBlack_Flag) ? gBlackGammaTable : gWhiteGammaTable;
index c1540278f81fd92ba69142a92559c9984f72c70a..ae393619856dd1531ee8e3a0379b8ddf83e3bdc6 100644 (file)
@@ -1,16 +1,16 @@
 /*
  ** Copyright 2006, The Android Open Source Project
  **
- ** Licensed under the Apache License, Version 2.0 (the "License"); 
- ** you may not use this file except in compliance with the License. 
- ** You may obtain a copy of the License at 
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
  **
- **     http://www.apache.org/licenses/LICENSE-2.0 
+ **     http://www.apache.org/licenses/LICENSE-2.0
  **
- ** Unless required by applicable law or agreed to in writing, software 
- ** distributed under the License is distributed on an "AS IS" BASIS, 
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
- ** See the License for the specific language governing permissions and 
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
  ** limitations under the License.
 */
 #include <vector>
@@ -21,7 +21,7 @@
 #include "SkString.h"
 #include "SkPaint.h"
 #include "SkFloatingPoint.h"
-
+#include "SkUtils.h"
 
 
 
@@ -88,13 +88,13 @@ public:
 
     // Is a font ID valid?
     bool                                IsValid(SkFontID fontID);
-    
-    
+
+
     // Get a font
     CTFontRef                           GetFont(SkFontID fontID);
     SkNativeFontInfo                    GetFontInfo(const SkString &theName, SkTypeface::Style theStyle);
-    
-    
+
+
     // Create a font
     SkNativeFontInfo                    CreateFont(const SkString &theName, SkTypeface::Style theStyle);
 
@@ -132,7 +132,7 @@ SkNativeFontCache::SkNativeFontCache(void)
     fontInfo.style   = SkTypeface::kNormal;
     fontInfo.fontID  = kSkInvalidFontID;
     fontInfo.fontRef = NULL;
-        
+
     mFonts.push_back(fontInfo);
 }
 
@@ -190,7 +190,7 @@ SkNativeFontInfo SkNativeFontCache::GetFontInfo(const SkString &theName, SkTypef
         if (theIter->name == theName && theIter->style == theStyle)
             return(*theIter);
         }
-    
+
     return(fontInfo);
 }
 
@@ -328,7 +328,8 @@ private:
 
 
 private:
-    CGColorSpaceRef                     mColorSpace;
+    CGColorSpaceRef                     mColorSpaceGray;
+    CGColorSpaceRef                     mColorSpaceRGB;
     CGAffineTransform                   mTransform;
 
     CTFontRef                           mFont;
@@ -352,7 +353,11 @@ SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc)
 
 
     // Initialise ourselves
-    mColorSpace = CGColorSpaceCreateDeviceGray();
+//    mColorSpaceRGB = CGColorSpaceCreateDeviceRGB();
+//    mColorSpaceRGB = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
+    mColorSpaceRGB = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGBLinear);
+    mColorSpaceGray = CGColorSpaceCreateDeviceGray();
+
     const float inv = 1.0f / FONT_CANONICAL_POINTSIZE;
     mTransform  = CGAffineTransformMake(SkScalarToFloat(skMatrix[SkMatrix::kMScaleX]) * inv,
                                         -SkScalarToFloat(skMatrix[SkMatrix::kMSkewY]) * inv,
@@ -369,7 +374,8 @@ SkScalerContext_Mac::~SkScalerContext_Mac(void)
 {
 
     // Clean up
-    CFSafeRelease(mColorSpace);
+    CFSafeRelease(mColorSpaceGray);
+    CFSafeRelease(mColorSpaceRGB);
     CFSafeRelease(mFont);
 }
 
@@ -437,8 +443,27 @@ void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph)
     glyph->fLeft     =  sk_float_round2int(CGRectGetMinX(theBounds));
 }
 
-void SkScalerContext_Mac::generateImage(const SkGlyph& glyph)
-{   CGContextRef        cgContext;
+#include "SkColorPriv.h"
+
+static inline uint16_t rgb_to_lcd16(uint32_t rgb) {
+    int r = (rgb >> 16) & 0xFF;
+    int g = (rgb >>  8) & 0xFF;
+    int b = (rgb >>  0) & 0xFF;
+
+    // invert, since we draw black-on-white, but we want the original
+    // src mask values.
+    r = 255 - r;
+    g = 255 - g;
+    b = 255 - b;
+
+    return SkPackRGB16(SkR32ToR16(r), SkG32ToG16(g), SkB32ToB16(b));
+}
+
+#define BITMAP_INFO_RGB     (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
+#define BITMAP_INFO_GRAY    (kCGImageAlphaNone)
+
+void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
+    CGContextRef        cgContext;
     CGGlyph             cgGlyph;
     CGFontRef           cgFont;
 
@@ -447,17 +472,61 @@ void SkScalerContext_Mac::generateImage(const SkGlyph& glyph)
 
     cgGlyph   = (CGGlyph) glyph.getGlyphID(fBaseGlyphCount);
     cgFont    = CTFontCopyGraphicsFont(mFont, NULL);
-    cgContext = CGBitmapContextCreate(  glyph.fImage, glyph.fWidth, glyph.fHeight, 8,
-                                        glyph.rowBytes(), mColorSpace, kCGImageAlphaNone);
+
+    SkAutoSMalloc<1024> storage;
+
+    CGColorSpaceRef colorspace = mColorSpaceGray;
+    uint32_t info = BITMAP_INFO_GRAY;
+    void* image = glyph.fImage;
+    size_t rowBytes = glyph.rowBytes();
+    float grayColor = 1; // white
+
+    /*  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 (SkMask::kLCD16_Format == glyph.fMaskFormat) {
+        colorspace = mColorSpaceRGB;
+        info = BITMAP_INFO_RGB;
+        // need tmp storage for 32bit RGB offscreen
+        rowBytes = glyph.fWidth << 2;
+        size_t size = glyph.fHeight * rowBytes;
+        image = storage.realloc(size);
+        // we draw black-on-white (and invert in rgb_to_lcd16)
+        sk_memset32((uint32_t*)image, 0xFFFFFFFF, size >> 2);
+        grayColor = 0;  // black
+    }
+
+    cgContext = CGBitmapContextCreate(image, glyph.fWidth, glyph.fHeight, 8,
+                                      rowBytes, colorspace, info);
 
     // Draw the glyph
     if (cgFont != NULL && cgContext != NULL) {
-        CGContextSetGrayFillColor(  cgContext, 1.0, 1.0);
+        CGContextSetAllowsFontSubpixelQuantization(cgContext, true);
+        CGContextSetShouldSubpixelQuantizeFonts(cgContext, true);
+
+        CGContextSetGrayFillColor(  cgContext, grayColor, 1.0);
         CGContextSetTextDrawingMode(cgContext, kCGTextFill);
         CGContextSetFont(           cgContext, cgFont);
         CGContextSetFontSize(       cgContext, FONT_CANONICAL_POINTSIZE);
         CGContextSetTextMatrix(     cgContext, mTransform);
         CGContextShowGlyphsAtPoint( cgContext, -glyph.fLeft, glyph.fTop + glyph.fHeight, &cgGlyph, 1);
+
+        if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
+            // downsample from rgba to rgb565
+            int width = glyph.fWidth;
+            const uint32_t* src = (const uint32_t*)image;
+            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(src[i]);
+                }
+                src = (const uint32_t*)((const char*)src + rowBytes);
+                dst = (uint16_t*)((char*)dst + dstRB);
+            }
+        }
     }
 
     // Clean up
@@ -539,7 +608,7 @@ void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element
         case kCGPathElementCloseSubpath:
             skPath->close();
             break;
-        
+
         default:
             SkASSERT("Unknown path element!");
             break;
@@ -783,7 +852,7 @@ size_t SkFontHost::GetTableSize(SkFontID fontID, SkFontTableTag tag)
         theSize = CFDataGetLength(cfData);
         CFSafeRelease(cfData);
         }
-    
+
     return(theSize);
 }