set gamma flag for white, since GDI gives us different (better) values
authorreed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 26 Sep 2011 13:21:39 +0000 (13:21 +0000)
committerreed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 26 Sep 2011 13:21:39 +0000 (13:21 +0000)
for white-on-black text.

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

src/ports/SkFontHost_win.cpp

index 276e180f0c25e012fdb9b7667e2ca2e8e2de4f06..2c18e42b189092d0dbaefda50914c749b21551bb 100755 (executable)
@@ -288,6 +288,7 @@ public:
         fBits = NULL;
         fWidth = fHeight = 0;
         fIsBW = false;
+        fColor = kInvalid_Color;
     }
 
     ~HDCOffscreen() {
@@ -304,7 +305,8 @@ public:
         fXform = xform;
     }
 
-    const void* draw(const SkGlyph&, bool isBW, size_t* srcRBPtr);
+    const void* draw(const SkGlyph&, bool isBW, bool whiteFG,
+                     size_t* srcRBPtr);
 
 private:
     HDC     fDC;
@@ -312,12 +314,20 @@ private:
     HFONT   fFont;
     XFORM   fXform;
     void*   fBits;  // points into fBM
+    COLORREF fColor;
     int     fWidth;
     int     fHeight;
     bool    fIsBW;
+
+    enum {
+        // will always trigger us to reset the color, since we
+        // should only store 0 or 0xFFFFFF
+        kInvalid_Color = 12345
+    };
 };
 
-const void* HDCOffscreen::draw(const SkGlyph& glyph, bool isBW, size_t* srcRBPtr) {
+const void* HDCOffscreen::draw(const SkGlyph& glyph, bool isBW,
+                               bool whiteFG, size_t* srcRBPtr) {
     if (0 == fDC) {
         fDC = CreateCompatibleDC(0);
         if (0 == fDC) {
@@ -327,20 +337,25 @@ const void* HDCOffscreen::draw(const SkGlyph& glyph, bool isBW, size_t* srcRBPtr
         SetBkMode(fDC, TRANSPARENT);
         SetTextAlign(fDC, TA_LEFT | TA_BASELINE);
         SelectObject(fDC, fFont);
-        COLORREF color = SetTextColor(fDC, fIsBW ? 0xFFFFFF : 0);
-        SkASSERT(color != CLR_INVALID);
+        fColor = kInvalid_Color;
     }
 
     if (fBM && (fIsBW != isBW || fWidth < glyph.fWidth || fHeight < glyph.fHeight)) {
         DeleteObject(fBM);
         fBM = 0;
     }
+    fIsBW = isBW;
 
-    if (fIsBW != isBW) {
-        fIsBW = isBW;
-        COLORREF color = SetTextColor(fDC, fIsBW ? 0xFFFFFF : 0);
-        SkASSERT(color != CLR_INVALID);
+    COLORREF color = 0;
+    if (fIsBW || whiteFG) {
+        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);
 
@@ -374,7 +389,7 @@ const void* HDCOffscreen::draw(const SkGlyph& glyph, bool isBW, size_t* srcRBPtr
     // erase
     size_t srcRB = isBW ? (biWidth >> 3) : (fWidth << 2);
     size_t size = fHeight * srcRB;
-    memset(fBits, isBW ? 0 : 0xFF, size);
+    memset(fBits, ~color & 0xFF, size);
 
     XFORM xform = fXform;
     xform.eDx = (float)-glyph.fLeft;
@@ -668,18 +683,24 @@ typedef uint32_t SkGdiRGB;
 
 static inline uint8_t rgb_to_a8(SkGdiRGB rgb) {
     // can pick any component (low 3 bytes), since we're grayscale
-    // but must invert since we draw black-on-white
-    return ~rgb & 0xFF;
+    return rgb & 0xFF;
 }
 
 static inline uint16_t rgb_to_lcd16(SkGdiRGB rgb) {
-    rgb = ~rgb; // 255 - each component
     int r = (rgb >> 16) & 0xFF;
     int g = (rgb >>  8) & 0xFF;
     int b = (rgb >>  0) & 0xFF;
     return SkPackRGB16(SkR32ToR16(r), SkG32ToG16(g), SkB32ToB16(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);
+}
+
 // Is this GDI color neither black nor white? If so, we have to keep this
 // image as is, rather than smashing it down to a BW mask.
 //
@@ -705,7 +726,7 @@ static bool is_rgb_really_bw(const SkGdiRGB* src, int width, int height, int src
 }
 
 static void rgb_to_bw(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
-                      const SkGlyph& glyph) {
+                      const SkGlyph& glyph, int32_t xorMask) {
     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);
@@ -721,14 +742,14 @@ static void rgb_to_bw(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
         if (byteCount > 0) {
             unsigned byte = 0;
             for (int i = 0; i < byteCount; ++i) {
-                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);
+                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);
                 dst[i] = ~byte;
                 src += 8;
             }
@@ -737,7 +758,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] & mask;
+                byte |= ~(src[i] ^ xorMask) & mask;
                 mask >>= 1;
             }
             dst[byteCount] = byte;
@@ -748,14 +769,14 @@ static void rgb_to_bw(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
 }
 
 static void rgb_to_a8(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
-                      const SkGlyph& glyph) {
+                      const SkGlyph& glyph, int32_t xorMask) {
     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]);
+            dst[i] = rgb_to_a8(src[i] ^ xorMask);
         }
         src = (const SkGdiRGB*)((const char*)src + srcRB);
         dst -= dstRB;
@@ -763,20 +784,35 @@ static void rgb_to_a8(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
 }
 
 static void rgb_to_lcd16(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
-                         const SkGlyph& glyph) {
+                         const SkGlyph& glyph, int32_t xorMask) {
     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]);
+            dst[i] = rgb_to_lcd16(src[i] ^ xorMask);
         }
         src = (const SkGdiRGB*)((const char*)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) {
+    const size_t dstRB = glyph.rowBytes();
+    const int width = glyph.fWidth;
+    SkPMColor* SK_RESTRICT dst = (SkPMColor*)((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);
+        }
+        src = (const SkGdiRGB*)((const char*)src + srcRB);
+        dst = (SkPMColor*)((char*)dst - dstRB);
+    }
+}
+
 void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) {
 
     SkAutoMutexAcquire  ac(gFTMutex);
@@ -785,9 +821,11 @@ void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) {
 
     const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat;
     const bool isAA = !isLCD(fRec);
+    const bool isWhite = SkToBool(fRec.fFlags & SkScalerContext::kGammaForWhite_Flag);
+    const uint32_t rgbXOR = isWhite ? 0 : ~0;
 
     size_t srcRB;
-    const void* bits = fOffscreen.draw(glyph, isBW, &srcRB);
+    const void* bits = fOffscreen.draw(glyph, isBW, isWhite, &srcRB);
     if (!bits) {
         sk_bzero(glyph.fImage, glyph.computeImageSize());
         return;
@@ -812,15 +850,20 @@ void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) {
         } else
 #endif
         {
-            rgb_to_a8(src, srcRB, glyph);
+            rgb_to_a8(src, srcRB, glyph, rgbXOR);
         }
     } else {    // LCD16
         const SkGdiRGB* src = (const SkGdiRGB*)bits;
         if (is_rgb_really_bw(src, width, glyph.fHeight, srcRB)) {
-            rgb_to_bw(src, srcRB, glyph);
+            rgb_to_bw(src, srcRB, glyph, rgbXOR);
             ((SkGlyph*)&glyph)->fMaskFormat = SkMask::kBW_Format;
         } else {
-            rgb_to_lcd16(src, srcRB, glyph);
+            if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
+                rgb_to_lcd16(src, srcRB, glyph, rgbXOR);
+            } else {
+                SkASSERT(SkMask::kLCD32_Format == glyph.fMaskFormat);
+                rgb_to_lcd32(src, srcRB, glyph, rgbXOR);
+            }
         }
     }
 }
@@ -935,18 +978,12 @@ SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
     OUTLINETEXTMETRIC otm;
     if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm) ||
         !GetTextFace(hdc, LF_FACESIZE, lf.lfFaceName)) {
-        DWORD error_code = GetLastError();
-        SkASSERT(error_code == 0);  // GDI error, possibly caused by sandboxing.
-        SkASSERT(false);  // GDI corruption.
         goto Error;
     }
     lf.lfHeight = -SkToS32(otm.otmEMSquare);
     designFont = CreateFontIndirect(&lf);
     SelectObject(hdc, designFont);
     if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm)) {
-        DWORD error_code = GetLastError();
-        SkASSERT(error_code == 0);  // GDI error, possibly caused by sandboxing.
-        SkASSERT(false);  // GDI corruption.
         goto Error;
     }
     const unsigned glyphCount = calculateGlyphCount(hdc);
@@ -1156,15 +1193,6 @@ size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) {
         return 0;   // nothing to do
 }
 
-int SkFontHost::ComputeGammaFlag(const SkPaint& paint) {
-    return 0;
-}
-
-void SkFontHost::GetGammaTables(const uint8_t* tables[2]) {
-    tables[0] = NULL;   // black gamma (e.g. exp=1.4)
-    tables[1] = NULL;   // white gamma (e.g. exp= 1/1.4)
-}
-
 SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
     printf("SkFontHost::CreateTypefaceFromFile unimplemented");
     return NULL;
@@ -1208,6 +1236,49 @@ void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
         rec->fMaskFormat = SkMask::kA8_Format;
     }
 #endif
+
+#if 0
+    if (SkMask::kLCD16_Format == rec->fMaskFormat) {
+        rec->fMaskFormat = SkMask::kLCD32_Format;
+    }
+#endif
+    if (!isLCD(*rec)) {
+        rec->fFlags &= ~(SkScalerContext::kGammaForBlack_Flag |
+                         SkScalerContext::kGammaForWhite_Flag);
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void SkFontHost::GetGammaTables(const uint8_t* tables[2]) {
+    tables[0] = NULL;
+    tables[1] = NULL;
+}
+
+// If the luminance is <= this value, then apply the black gamma table
+#define BLACK_GAMMA_THRESHOLD   0x40
+
+// If the luminance is >= this value, then apply the white gamma table
+#define WHITE_GAMMA_THRESHOLD   0xA0
+
+int SkFontHost::ComputeGammaFlag(const SkPaint& paint) {
+    if (paint.getShader() == NULL && paint.getColorFilter() == NULL) {
+        SkColor c = paint.getColor();
+        int r = SkColorGetR(c);
+        int g = SkColorGetG(c);
+        int b = SkColorGetB(c);
+        int luminance = (r * 2 + g * 5 + b) >> 3;
+
+#if 0   // we basically default to black, so don't need to return it
+        if (luminance <= BLACK_GAMMA_THRESHOLD) {
+            return SkScalerContext::kGammaForBlack_Flag;
+        }
+#endif
+        if (luminance >= WHITE_GAMMA_THRESHOLD) {
+            return SkScalerContext::kGammaForWhite_Flag;
+        }
+    }
+    return 0;
 }
 
 #endif // WIN32