#include "SkPath.h"
#include "SkStream.h"
#include "SkString.h"
+#include "SkTemplates.h"
#include "SkThread.h"
#include "SkTypeface_win.h"
#include "SkTypefaceCache.h"
// 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;
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
return SkFixedToFIXED(SkFloatToFixed(x));
}
-SK_DECLARE_STATIC_MUTEX(gFTMutex);
-
#define HIRES_TEXTSIZE 2048
#define HIRES_SHIFT 11
static inline SkFixed HiResToFixed(int value) {
, fFont(0)
, fSavefont(0)
, fSC(0)
- , fGlyphCount(-1) {
- SkAutoMutexAcquire ac(gFTMutex);
-
+ , fGlyphCount(-1)
+{
LogFontTypeface* typeface = reinterpret_cast<LogFontTypeface*>(rawTypeface);
fDDC = ::CreateCompatibleDC(NULL);
* that shifting into other color spaces is imprecise.
*/
static const uint8_t* getInverseGammaTableGDI() {
- static bool gInited;
+ // Since build_power_table is idempotent, many threads can build gTableGdi
+ // simultaneously.
+
+ // Microsoft Specific:
+ // Making gInited volatile provides read-aquire and write-release in vc++.
+ // In VS2012, see compiler option /volatile:(ms|iso).
+ // Replace with C++11 atomics when possible.
+ static volatile bool gInited;
static uint8_t gTableGdi[256];
- if (!gInited) {
+ if (gInited) {
+ // Need a L/L (read) barrier (full 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 (full release not needed) here so that this
+ // write to gInited becomes observable after gTableGdi.
gInited = true;
}
return gTableGdi;
* If this value is not specified, the default is a gamma of 1.4.
*/
static const uint8_t* getInverseGammaTableClearType() {
- static bool gInited;
+ // We don't expect SPI_GETFONTSMOOTHINGCONTRAST to ever change, so building
+ // gTableClearType with build_power_table is effectively idempotent.
+
+ // Microsoft Specific:
+ // Making gInited volatile provides read-aquire and write-release in vc++.
+ // In VS2012, see compiler option /volatile:(ms|iso).
+ // Replace with C++11 atomics when possible.
+ static volatile 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;
return false;
}
}
- src = SkTAddByteOffset(src, srcRB);
+ src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
}
return true;
}
}
dst[byteCount] = byte;
}
- src = SkTAddByteOffset(src, srcRB);
+ src = SkTAddOffset<const SkGdiRGB>(src, srcRB);
dst -= dstRB;
}
}
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;
}
}
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);
}
}
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);
}
}
}
void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) {
- SkAutoMutexAcquire ac(gFTMutex);
SkASSERT(fDDC);
const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat;
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);
}
}
}
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;
+ }
}
}