diff options
-rw-r--r-- | src/ports/SkScalerContext_win_dw.cpp | 246 | ||||
-rw-r--r-- | src/ports/SkScalerContext_win_dw.h | 3 | ||||
-rw-r--r-- | src/ports/SkTypeface_win_dw.cpp | 7 | ||||
-rw-r--r-- | src/ports/SkTypeface_win_dw.h | 9 |
4 files changed, 170 insertions, 95 deletions
diff --git a/src/ports/SkScalerContext_win_dw.cpp b/src/ports/SkScalerContext_win_dw.cpp index d3cea9d642..b2a61cc6ab 100644 --- a/src/ports/SkScalerContext_win_dw.cpp +++ b/src/ports/SkScalerContext_win_dw.cpp @@ -68,35 +68,33 @@ static bool is_hinted_without_gasp(DWriteFontTypeface* typeface) { return !gasp.fExists; } -/** A PPEMRange is inclusive, [min, max]. */ -struct PPEMRange { +/** A GaspRange is inclusive, [min, max]. */ +struct GaspRange { int min; int max; + SkOTTableGridAndScanProcedure::GaspRange::behavior flags; }; -/** If the rendering mode for the specified 'size' is gridfit, then place - * the gridfit range into 'range'. Otherwise, leave 'range' alone. - */ -static void expand_range_if_gridfit_only(DWriteFontTypeface* typeface, int size, PPEMRange* range) { +bool get_gasp_range(DWriteFontTypeface* typeface, int size, GaspRange* range) { AutoTDWriteTable<SkOTTableGridAndScanProcedure> gasp(typeface->fDWriteFontFace.get()); if (!gasp.fExists) { - return; + return false; } if (gasp.fSize < sizeof(SkOTTableGridAndScanProcedure)) { - return; + return false; } if (gasp->version != SkOTTableGridAndScanProcedure::version0 && gasp->version != SkOTTableGridAndScanProcedure::version1) { - return; + return false; } uint16_t numRanges = SkEndianSwap16(gasp->numRanges); if (numRanges > 1024 || gasp.fSize < sizeof(SkOTTableGridAndScanProcedure) + - sizeof(SkOTTableGridAndScanProcedure::GaspRange) * numRanges) + sizeof(SkOTTableGridAndScanProcedure::GaspRange) * numRanges) { - return; + return false; } const SkOTTableGridAndScanProcedure::GaspRange* rangeTable = @@ -104,58 +102,40 @@ static void expand_range_if_gridfit_only(DWriteFontTypeface* typeface, int size, int minPPEM = -1; for (uint16_t i = 0; i < numRanges; ++i, ++rangeTable) { int maxPPEM = SkEndianSwap16(rangeTable->maxPPEM); - // Test that the size is in range and the range is gridfit only. - if (minPPEM < size && size <= maxPPEM && - rangeTable->flags.raw.value == SkOTTableGridAndScanProcedure::GaspRange::behavior::Raw::GridfitMask) - { + if (minPPEM < size && size <= maxPPEM) { range->min = minPPEM + 1; range->max = maxPPEM; - return; + range->flags = rangeTable->flags; + return true; } minPPEM = maxPPEM; } + return false; +} +/** If the rendering mode for the specified 'size' is gridfit, then place + * the gridfit range into 'range'. Otherwise, leave 'range' alone. + */ +static bool is_gridfit_only(SkOTTableGridAndScanProcedure::GaspRange::behavior flags) { + return flags.raw.value == SkOTTableGridAndScanProcedure::GaspRange::behavior::Raw::GridfitMask; } /** If the rendering mode for the specified 'size' sets SymmetricSmoothing, return true. */ -static bool gasp_allows_cleartype_symmetric(DWriteFontTypeface* typeface, int size) { +static bool gasp_allows_cleartype_symmetric(SkOTTableGridAndScanProcedure::GaspRange::behavior flags) { #ifdef SK_IGNORE_DIRECTWRITE_GASP_FIX return true; #endif - AutoTDWriteTable<SkOTTableGridAndScanProcedure> gasp(typeface->fDWriteFontFace.get()); - if (!gasp.fExists) { - return false; - } - if (gasp.fSize < sizeof(SkOTTableGridAndScanProcedure)) { - return false; - } - if (gasp->version != SkOTTableGridAndScanProcedure::version0 && - gasp->version != SkOTTableGridAndScanProcedure::version1) - { - return false; - } - - uint16_t numRanges = SkEndianSwap16(gasp->numRanges); - if (numRanges > 1024 || - gasp.fSize < sizeof(SkOTTableGridAndScanProcedure) + - sizeof(SkOTTableGridAndScanProcedure::GaspRange) * numRanges) - { - return false; - } + return flags.field.SymmetricSmoothing; +} - const SkOTTableGridAndScanProcedure::GaspRange* rangeTable = - SkTAfter<const SkOTTableGridAndScanProcedure::GaspRange>(gasp.get()); - int minPPEM = -1; - for (uint16_t i = 0; i < numRanges; ++i, ++rangeTable) { - int maxPPEM = SkEndianSwap16(rangeTable->maxPPEM); - if (minPPEM < size && size <= maxPPEM) { - return rangeTable->flags.field.SymmetricSmoothing; - } - minPPEM = maxPPEM; +/** If gidfitting is enabled at this size, return true. */ +static bool gasp_allows_gridfit(SkOTTableGridAndScanProcedure::GaspRange::behavior flags, DWRITE_RENDERING_MODE renderingMode) { + if (renderingMode == DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC) { + return flags.field.SymmetricGridfit; } - return false; + return flags.field.Gridfit; } -static bool has_bitmap_strike(DWriteFontTypeface* typeface, PPEMRange range) { +static bool has_bitmap_strike(DWriteFontTypeface* typeface, GaspRange range) { SkAutoExclusive l(DWriteFactoryMutex); { AutoTDWriteTable<SkOTTableEmbeddedBitmapLocation> eblc(typeface->fDWriteFontFace.get()); @@ -248,13 +228,11 @@ SkScalerContext_DW::SkScalerContext_DW(sk_sp<DWriteFontTypeface> typefaceRef, , fGlyphCount(-1) { DWriteFontTypeface* typeface = this->getDWriteTypeface(); - typeface->fFactory->QueryInterface<IDWriteFactory2>(&fFactory2); - - SkTScopedComPtr<IDWriteFontFace2> fontFace2; - typeface->fDWriteFontFace->QueryInterface<IDWriteFontFace2>(&fontFace2); - fIsColorFont = fFactory2.get() && fontFace2.get() && fontFace2->IsColorFont(); + fIsColorFont = typeface->fFactory2 && + typeface->fDWriteFontFace2 && + typeface->fDWriteFontFace2->IsColorFont(); - // In general, all glyphs should use CLEARTYPE_NATURAL_SYMMETRIC + // In general, all glyphs should use NATURAL_SYMMETRIC // except when bi-level rendering is requested or there are embedded // bi-level bitmaps (and the embedded bitmap flag is set and no rotation). // @@ -306,8 +284,12 @@ SkScalerContext_DW::SkScalerContext_DW(sk_sp<DWriteFontTypeface> typefaceRef, // When embedded bitmaps are requested, treat the entire range like // a bitmap strike if the range is gridfit only and contains a bitmap. int bitmapPPEM = SkScalarTruncToInt(gdiTextSize); - PPEMRange range = { bitmapPPEM, bitmapPPEM }; - expand_range_if_gridfit_only(typeface, bitmapPPEM, &range); + GaspRange range{bitmapPPEM, bitmapPPEM, 0}; + if (get_gasp_range(typeface, bitmapPPEM, &range)) { + if (!is_gridfit_only(range.flags)) { + range = GaspRange{bitmapPPEM, bitmapPPEM, 0}; + } + } treatLikeBitmap = has_bitmap_strike(typeface, range); axisAlignedBitmap = is_axis_aligned(fRec); @@ -325,7 +307,7 @@ SkScalerContext_DW::SkScalerContext_DW(sk_sp<DWriteFontTypeface> typefaceRef, // This will not always provide a bitmap, but matches expected behavior. } else if (treatLikeBitmap && axisAlignedBitmap) { fTextSizeRender = gdiTextSize; - fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC; + fRenderingMode = DWRITE_RENDERING_MODE_GDI_CLASSIC; fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; fTextSizeMeasure = gdiTextSize; fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; @@ -334,7 +316,7 @@ SkScalerContext_DW::SkScalerContext_DW(sk_sp<DWriteFontTypeface> typefaceRef, // render high quality rotated glyphs but measure using bitmap metrics. } else if (treatLikeBitmap) { fTextSizeRender = gdiTextSize; - fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; + fRenderingMode = DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC; fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; fTextSizeMeasure = gdiTextSize; fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; @@ -345,7 +327,7 @@ SkScalerContext_DW::SkScalerContext_DW(sk_sp<DWriteFontTypeface> typefaceRef, // drop out control in the y direction in order to be legible. } else if (is_hinted_without_gasp(typeface)) { fTextSizeRender = gdiTextSize; - fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL; + fRenderingMode = DWRITE_RENDERING_MODE_NATURAL; fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; fTextSizeMeasure = realTextSize; fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL; @@ -353,14 +335,37 @@ SkScalerContext_DW::SkScalerContext_DW(sk_sp<DWriteFontTypeface> typefaceRef, // The normal case is to use natural symmetric rendering (if permitted) and linear metrics. } else { fTextSizeRender = realTextSize; - fRenderingMode = gasp_allows_cleartype_symmetric(typeface, realTextSize) - ? DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC - : DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL; + GaspRange range = {0, 0xFFFF, 0}; + get_gasp_range(typeface, SkScalarTruncToInt(fTextSizeRender), &range); + fRenderingMode = gasp_allows_cleartype_symmetric(range.flags) + ? DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC + : DWRITE_RENDERING_MODE_NATURAL; fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; fTextSizeMeasure = realTextSize; fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL; } + // DirectWrite2 allows for grayscale hinting. + fAntiAliasMode = DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE; +#ifndef SK_IGNORE_DW_GRAY_FIX + if (typeface->fFactory2 && typeface->fDWriteFontFace2 && + !isLCD(fRec) && !(fRec.fFlags & SkScalerContext::kGenA8FromLCD_Flag)) + { + // DWRITE_TEXTURE_ALIASED_1x1 is now misnamed, it must also be used with grayscale. + fTextureType = DWRITE_TEXTURE_ALIASED_1x1; + fAntiAliasMode = DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE; + } +#endif + + // DirectWrite2 allows hinting to be disabled. + fGridFitMode = DWRITE_GRID_FIT_MODE_ENABLED; + if (fRec.getHinting() == SkPaint::kNo_Hinting) { + fGridFitMode = DWRITE_GRID_FIT_MODE_DISABLED; + if (fRenderingMode != DWRITE_RENDERING_MODE_ALIASED) { + fRenderingMode = DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC; + } + } + if (this->isSubpixel()) { fTextSizeMeasure = realTextSize; fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL; @@ -468,16 +473,33 @@ HRESULT SkScalerContext_DW::getBoundingBox(SkGlyph* glyph, SkTScopedComPtr<IDWriteGlyphRunAnalysis> glyphRunAnalysis; { SkAutoExclusive l(DWriteFactoryMutex); - HRM(this->getDWriteTypeface()->fFactory->CreateGlyphRunAnalysis( - &run, - 1.0f, // pixelsPerDip, - &fXform, - renderingMode, - fMeasuringMode, - 0.0f, // baselineOriginX, - 0.0f, // baselineOriginY, - &glyphRunAnalysis), - "Could not create glyph run analysis."); + // IDWriteFactory2::CreateGlyphRunAnalysis is very bad at aliased glyphs. + if (this->getDWriteTypeface()->fFactory2 && + (fGridFitMode == DWRITE_GRID_FIT_MODE_DISABLED || + fAntiAliasMode == DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE)) + { + HRM(this->getDWriteTypeface()->fFactory2->CreateGlyphRunAnalysis( + &run, + &fXform, + renderingMode, + fMeasuringMode, + fGridFitMode, + fAntiAliasMode, + 0.0f, // baselineOriginX, + 0.0f, // baselineOriginY, + &glyphRunAnalysis), + "Could not create DW2 glyph run analysis."); + } else { + HRM(this->getDWriteTypeface()->fFactory->CreateGlyphRunAnalysis(&run, + 1.0f, // pixelsPerDip, + &fXform, + renderingMode, + fMeasuringMode, + 0.0f, // baselineOriginX, + 0.0f, // baselineOriginY, + &glyphRunAnalysis), + "Could not create glyph run analysis."); + } } { Shared l(DWriteFactoryMutex); @@ -528,7 +550,7 @@ bool SkScalerContext_DW::getColorGlyphRun(const SkGlyph& glyph, run.isSideways = FALSE; run.glyphOffsets = &offset; - HRESULT hr = fFactory2->TranslateColorGlyphRun( + HRESULT hr = this->getDWriteTypeface()->fFactory2->TranslateColorGlyphRun( 0, 0, &run, nullptr, fMeasuringMode, &fXform, 0, colorGlyph); if (hr == DWRITE_E_NOCOLOR) { return false; @@ -680,6 +702,22 @@ static void bilevel_to_bw(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph) } template<bool APPLY_PREBLEND> +static void grayscale_to_a8(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph, + const uint8_t* table8) { + const size_t dstRB = glyph.rowBytes(); + const U16CPU width = glyph.fWidth; + uint8_t* SK_RESTRICT dst = static_cast<uint8_t*>(glyph.fImage); + + for (U16CPU y = 0; y < glyph.fHeight; y++) { + for (U16CPU i = 0; i < width; i++) { + U8CPU a = *(src++); + dst[i] = sk_apply_lut_if<APPLY_PREBLEND>(a, table8); + } + dst = SkTAddOffset<uint8_t>(dst, dstRB); + } +} + +template<bool APPLY_PREBLEND> static void rgb_to_a8(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph, const uint8_t* table8) { const size_t dstRB = glyph.rowBytes(); const U16CPU width = glyph.fWidth; @@ -692,7 +730,7 @@ static void rgb_to_a8(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph, cons U8CPU b = *(src++); dst[i] = sk_apply_lut_if<APPLY_PREBLEND>((r + g + b) / 3, table8); } - dst = (uint8_t*)((char*)dst + dstRB); + dst = SkTAddOffset<uint8_t>(dst, dstRB); } } @@ -717,7 +755,7 @@ static void rgb_to_lcd16(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph, } dst[i] = SkPack888ToRGB16(r, g, b); } - dst = (uint16_t*)((char*)dst + dstRB); + dst = SkTAddOffset<uint16_t>(dst, dstRB); } } @@ -726,7 +764,7 @@ const void* SkScalerContext_DW::drawDWMask(const SkGlyph& glyph, DWRITE_TEXTURE_TYPE textureType) { int sizeNeeded = glyph.fWidth * glyph.fHeight; - if (DWRITE_RENDERING_MODE_ALIASED != renderingMode) { + if (DWRITE_TEXTURE_CLEARTYPE_3x1 == textureType) { sizeNeeded *= 3; } if (sizeNeeded > fBits.count()) { @@ -757,19 +795,35 @@ const void* SkScalerContext_DW::drawDWMask(const SkGlyph& glyph, run.isSideways = FALSE; run.glyphOffsets = &offset; { - SkTScopedComPtr<IDWriteGlyphRunAnalysis> glyphRunAnalysis; { SkAutoExclusive l(DWriteFactoryMutex); - HRNM(this->getDWriteTypeface()->fFactory->CreateGlyphRunAnalysis(&run, - 1.0f, // pixelsPerDip, - &fXform, - renderingMode, - fMeasuringMode, - 0.0f, // baselineOriginX, - 0.0f, // baselineOriginY, - &glyphRunAnalysis), - "Could not create glyph run analysis."); + // IDWriteFactory2::CreateGlyphRunAnalysis is very bad at aliased glyphs. + if (this->getDWriteTypeface()->fFactory2 && + (fGridFitMode == DWRITE_GRID_FIT_MODE_DISABLED || + fAntiAliasMode == DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE)) + { + HRNM(this->getDWriteTypeface()->fFactory2->CreateGlyphRunAnalysis(&run, + &fXform, + renderingMode, + fMeasuringMode, + fGridFitMode, + fAntiAliasMode, + 0.0f, // baselineOriginX, + 0.0f, // baselineOriginY, + &glyphRunAnalysis), + "Could not create DW2 glyph run analysis."); + } else { + HRNM(this->getDWriteTypeface()->fFactory->CreateGlyphRunAnalysis(&run, + 1.0f, // pixelsPerDip, + &fXform, + renderingMode, + fMeasuringMode, + 0.0f, // baselineOriginX, + 0.0f, // baselineOriginY, + &glyphRunAnalysis), + "Could not create glyph run analysis."); + } } //NOTE: this assumes that the glyph has already been measured //with an exact same glyph run analysis. @@ -781,9 +835,9 @@ const void* SkScalerContext_DW::drawDWMask(const SkGlyph& glyph, { Shared l(DWriteFactoryMutex); HRNM(glyphRunAnalysis->CreateAlphaTexture(textureType, - &bbox, - fBits.begin(), - sizeNeeded), + &bbox, + fBits.begin(), + sizeNeeded), "Could not draw mask."); } } @@ -883,10 +937,18 @@ void SkScalerContext_DW::generateImage(const SkGlyph& glyph) { bilevel_to_bw(src, glyph); const_cast<SkGlyph&>(glyph).fMaskFormat = SkMask::kBW_Format; } else if (!isLCD(fRec)) { - if (fPreBlend.isApplicable()) { - rgb_to_a8<true>(src, glyph, fPreBlend.fG); + if (textureType == DWRITE_TEXTURE_ALIASED_1x1) { + if (fPreBlend.isApplicable()) { + grayscale_to_a8<true>(src, glyph, fPreBlend.fG); + } else { + grayscale_to_a8<false>(src, glyph, fPreBlend.fG); + } } else { - rgb_to_a8<false>(src, glyph, fPreBlend.fG); + if (fPreBlend.isApplicable()) { + rgb_to_a8<true>(src, glyph, fPreBlend.fG); + } else { + rgb_to_a8<false>(src, glyph, fPreBlend.fG); + } } } else { SkASSERT(SkMask::kLCD16_Format == glyph.fMaskFormat); diff --git a/src/ports/SkScalerContext_win_dw.h b/src/ports/SkScalerContext_win_dw.h index e13e01a61b..f186ea52dc 100644 --- a/src/ports/SkScalerContext_win_dw.h +++ b/src/ports/SkScalerContext_win_dw.h @@ -76,7 +76,8 @@ private: DWRITE_RENDERING_MODE fRenderingMode; DWRITE_TEXTURE_TYPE fTextureType; DWRITE_MEASURING_MODE fMeasuringMode; - SkTScopedComPtr<IDWriteFactory2> fFactory2; + DWRITE_TEXT_ANTIALIAS_MODE fAntiAliasMode; + DWRITE_GRID_FIT_MODE fGridFitMode; bool fIsColorFont; }; diff --git a/src/ports/SkTypeface_win_dw.cpp b/src/ports/SkTypeface_win_dw.cpp index 3acdd213ac..7d18da9d5a 100644 --- a/src/ports/SkTypeface_win_dw.cpp +++ b/src/ports/SkTypeface_win_dw.cpp @@ -253,6 +253,7 @@ SkScalerContext* DWriteFontTypeface::onCreateScalerContext(const SkScalerContext void DWriteFontTypeface::onFilterRec(SkScalerContext::Rec* rec) const { if (rec->fFlags & SkScalerContext::kLCD_Vertical_Flag) { rec->fMaskFormat = SkMask::kA8_Format; + rec->fFlags |= SkScalerContext::kGenA8FromLCD_Flag; } unsigned flagsWeDontSupport = SkScalerContext::kVertical_Flag | @@ -263,8 +264,10 @@ void DWriteFontTypeface::onFilterRec(SkScalerContext::Rec* rec) const { rec->fFlags &= ~flagsWeDontSupport; SkPaint::Hinting h = rec->getHinting(); - // DirectWrite does not provide for hinting hints. - h = SkPaint::kSlight_Hinting; + // DirectWrite2 allows for hinting to be turned off. Force everything else to normal. + if (h != SkPaint::kNo_Hinting || !fFactory2 || !fDWriteFontFace2) { + h = SkPaint::kNormal_Hinting; + } rec->setHinting(h); #if defined(SK_FONT_HOST_USE_SYSTEM_SETTINGS) diff --git a/src/ports/SkTypeface_win_dw.h b/src/ports/SkTypeface_win_dw.h index d4d4facab1..d7c73e2697 100644 --- a/src/ports/SkTypeface_win_dw.h +++ b/src/ports/SkTypeface_win_dw.h @@ -18,6 +18,7 @@ #include <dwrite.h> #include <dwrite_1.h> +#include <dwrite_2.h> class SkFontDescriptor; struct SkScalerContextRec; @@ -57,16 +58,24 @@ private: // http://blogs.msdn.com/b/oldnewthing/archive/2004/03/26/96777.aspx SkASSERT_RELEASE(nullptr == fDWriteFontFace1.get()); } + if (!SUCCEEDED(fDWriteFontFace->QueryInterface(&fDWriteFontFace2))) { + SkASSERT_RELEASE(nullptr == fDWriteFontFace2.get()); + } + if (!SUCCEEDED(fFactory->QueryInterface(&fFactory2))) { + SkASSERT_RELEASE(nullptr == fFactory2.get()); + } } public: SkTScopedComPtr<IDWriteFactory> fFactory; + SkTScopedComPtr<IDWriteFactory2> fFactory2; SkTScopedComPtr<IDWriteFontCollectionLoader> fDWriteFontCollectionLoader; SkTScopedComPtr<IDWriteFontFileLoader> fDWriteFontFileLoader; SkTScopedComPtr<IDWriteFontFamily> fDWriteFontFamily; SkTScopedComPtr<IDWriteFont> fDWriteFont; SkTScopedComPtr<IDWriteFontFace> fDWriteFontFace; SkTScopedComPtr<IDWriteFontFace1> fDWriteFontFace1; + SkTScopedComPtr<IDWriteFontFace2> fDWriteFontFace2; static DWriteFontTypeface* Create(IDWriteFactory* factory, IDWriteFontFace* fontFace, |