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