Index: src/ports/SkFontHost_win.cpp |
=================================================================== |
--- src/ports/SkFontHost_win.cpp (revision 9661) |
+++ src/ports/SkFontHost_win.cpp (working copy) |
@@ -19,6 +19,7 @@ |
#include "SkPath.h" |
#include "SkStream.h" |
#include "SkString.h" |
+#include "SkTemplates.h" |
#include "SkThread.h" |
#include "SkTypeface_win.h" |
#include "SkTypefaceCache.h" |
@@ -46,18 +47,11 @@ |
// always packed xxRRGGBB |
typedef uint32_t SkGdiRGB; |
-template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) { |
- return (T*)((char*)ptr + byteOffset); |
-} |
- |
// define this in your Makefile or .gyp to enforce AA requests |
// which GDI ignores at small sizes. This flag guarantees AA |
// for rotated text, regardless of GDI's notions. |
//#define SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS |
-// client3d has to undefine this for now |
-#define CAN_USE_LOGFONT_NAME |
- |
static bool isLCD(const SkScalerContext::Rec& rec) { |
return SkMask::kLCD16_Format == rec.fMaskFormat || |
SkMask::kLCD32_Format == rec.fMaskFormat; |
@@ -91,9 +85,6 @@ |
using namespace skia_advanced_typeface_metrics_utils; |
-static const uint16_t BUFFERSIZE = (16384 - 32); |
-static uint8_t glyphbuf[BUFFERSIZE]; |
- |
/** |
* Since LOGFONT wants its textsize as an int, and we support fractional sizes, |
* and since we have a cache of LOGFONTs for our tyepfaces, we always set the |
@@ -554,8 +545,6 @@ |
return SkFixedToFIXED(SkFloatToFixed(x)); |
} |
-SK_DECLARE_STATIC_MUTEX(gFTMutex); |
- |
#define HIRES_TEXTSIZE 2048 |
#define HIRES_SHIFT 11 |
static inline SkFixed HiResToFixed(int value) { |
@@ -589,9 +578,8 @@ |
, fFont(0) |
, fSavefont(0) |
, fSC(0) |
- , fGlyphCount(-1) { |
- SkAutoMutexAcquire ac(gFTMutex); |
- |
+ , fGlyphCount(-1) |
+{ |
LogFontTypeface* typeface = reinterpret_cast<LogFontTypeface*>(rawTypeface); |
fDDC = ::CreateCompatibleDC(NULL); |
@@ -924,10 +912,17 @@ |
* that shifting into other color spaces is imprecise. |
*/ |
static const uint8_t* getInverseGammaTableGDI() { |
+ // Since build_power_table is idempotent, many threads can build gTableGdi |
+ // simultaneously. |
static bool gInited; |
static uint8_t gTableGdi[256]; |
- if (!gInited) { |
+ if (gInited) { |
+ // Need a L/L (read) barrier (acquire not needed). If gInited is observed |
+ // true then gTableGdi is observable, but it must be requested. |
+ } else { |
build_power_table(gTableGdi, 2.3f); |
+ // Need a S/S (write) barrier (release not needed) here so that this |
+ // write to gInited becomes observable after gTableGdi. |
gInited = true; |
} |
return gTableGdi; |
@@ -941,15 +936,22 @@ |
* If this value is not specified, the default is a gamma of 1.4. |
*/ |
static const uint8_t* getInverseGammaTableClearType() { |
+ // We don't expect SPI_GETFONTSMOOTHINGCONTRAST to ever change, so building |
+ // gTableClearType with build_power_table is effectively idempotent. |
static bool gInited; |
static uint8_t gTableClearType[256]; |
- if (!gInited) { |
+ if (gInited) { |
+ // Need a L/L (read) barrier (acquire not needed). If gInited is observed |
+ // true then gTableClearType is observable, but it must be requested. |
+ } else { |
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(gTableClearType, level / 1000.0f); |
+ // Need a S/S (write) barrier (release not needed) here so that this |
+ // write to gInited becomes observable after gTableClearType. |
gInited = true; |
} |
return gTableClearType; |
@@ -1005,7 +1007,7 @@ |
return false; |
} |
} |
- src = SkTAddByteOffset(src, srcRB); |
+ src = SkTAddOffset<const SkGdiRGB>(src, srcRB); |
} |
return true; |
} |
@@ -1050,7 +1052,7 @@ |
} |
dst[byteCount] = byte; |
} |
- src = SkTAddByteOffset(src, srcRB); |
+ src = SkTAddOffset<const SkGdiRGB>(src, srcRB); |
dst -= dstRB; |
} |
} |
@@ -1066,7 +1068,7 @@ |
for (int i = 0; i < width; i++) { |
dst[i] = rgb_to_a8<APPLY_PREBLEND>(src[i], table8); |
} |
- src = SkTAddByteOffset(src, srcRB); |
+ src = SkTAddOffset<const SkGdiRGB>(src, srcRB); |
dst -= dstRB; |
} |
} |
@@ -1082,7 +1084,7 @@ |
for (int i = 0; i < width; i++) { |
dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(src[i], tableR, tableG, tableB); |
} |
- src = SkTAddByteOffset(src, srcRB); |
+ src = SkTAddOffset<const SkGdiRGB>(src, srcRB); |
dst = (uint16_t*)((char*)dst - dstRB); |
} |
} |
@@ -1098,7 +1100,7 @@ |
for (int i = 0; i < width; i++) { |
dst[i] = rgb_to_lcd32<APPLY_PREBLEND>(src[i], tableR, tableG, tableB); |
} |
- src = SkTAddByteOffset(src, srcRB); |
+ src = SkTAddOffset<const SkGdiRGB>(src, srcRB); |
dst = (uint32_t*)((char*)dst - dstRB); |
} |
} |
@@ -1109,7 +1111,6 @@ |
} |
void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) { |
- SkAutoMutexAcquire ac(gFTMutex); |
SkASSERT(fDDC); |
const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat; |
@@ -1148,7 +1149,7 @@ |
int b = (addr[x] >> 0) & 0xFF; |
addr[x] = (table[r] << 16) | (table[g] << 8) | table[b]; |
} |
- addr = SkTAddByteOffset(addr, srcRB); |
+ addr = SkTAddOffset<SkGdiRGB>(addr, srcRB); |
} |
} |
@@ -1200,23 +1201,49 @@ |
} |
void SkScalerContext_Windows::generatePath(const SkGlyph& glyph, SkPath* path) { |
- |
- SkAutoMutexAcquire ac(gFTMutex); |
- |
SkASSERT(&glyph && path); |
SkASSERT(fDDC); |
path->reset(); |
GLYPHMETRICS gm; |
- uint32_t total_size = GetGlyphOutlineW(fDDC, glyph.fID, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, BUFFERSIZE, glyphbuf, &fMat22); |
+ |
+ // Out of all the fonts on a typical Windows box, |
+ // 25% of glyphs require more than 2KB. |
+ // 1% of glyphs require more than 4KB. |
+ // 0.01% of glyphs require more than 8KB. |
+ // 8KB is less than 1% of the normal 1MB stack on Windows. |
+ // Note that some web fonts glyphs require more than 20KB. |
+ static const uint16_t BUFFERSIZE = (1 << 13); |
+ SkAutoSTMalloc<BUFFERSIZE, uint8_t> glyphbuf(BUFFERSIZE); |
+ |
+ const UINT flags = GGO_NATIVE | GGO_GLYPH_INDEX; |
+ DWORD total_size = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, BUFFERSIZE, glyphbuf, &fMat22); |
if (GDI_ERROR == total_size) { |
- LogFontTypeface::EnsureAccessible(this->getTypeface()); |
- total_size = GetGlyphOutlineW(fDDC, glyph.fID, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, BUFFERSIZE, glyphbuf, &fMat22); |
+ // GDI_ERROR because the BUFFERSIZE was too small, or because the data was not accessible. |
+ // When the data is not accessable GetGlyphOutlineW fails rather quickly, |
+ // so just try to get the size. If that fails then ensure the data is accessible. |
+ total_size = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, 0, NULL, &fMat22); |
if (GDI_ERROR == total_size) { |
- SkASSERT(false); |
- return; |
+ LogFontTypeface::EnsureAccessible(this->getTypeface()); |
+ total_size = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, 0, NULL, &fMat22); |
+ if (GDI_ERROR == total_size) { |
+ SkASSERT(false); |
+ return; |
+ } |
} |
+ |
+ glyphbuf.reset(total_size); |
+ |
+ DWORD ret = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, total_size, glyphbuf, &fMat22); |
+ if (GDI_ERROR == ret) { |
+ LogFontTypeface::EnsureAccessible(this->getTypeface()); |
+ ret = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, total_size, glyphbuf, &fMat22); |
+ if (GDI_ERROR == ret) { |
+ SkASSERT(false); |
+ return; |
+ } |
+ } |
} |
const uint8_t* cur_glyph = glyphbuf; |