/* * 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 "SkTypes.h" #undef GetGlyphIndices #include "SkDWrite.h" #include "SkDWriteGeometrySink.h" #include "SkEndian.h" #include "SkGlyph.h" #include "SkHRESULT.h" #include "SkMaskGamma.h" #include "SkMatrix22.h" #include "SkOTTable_EBLC.h" #include "SkOTTable_EBSC.h" #include "SkPath.h" #include "SkScalerContext.h" #include "SkScalerContext_win_dw.h" #include "SkTScopedComPtr.h" #include "SkTypeface_win_dw.h" #include static bool isLCD(const SkScalerContext::Rec& rec) { return SkMask::kLCD16_Format == rec.fMaskFormat || SkMask::kLCD32_Format == rec.fMaskFormat; } static bool hasBitmapStrike(DWriteFontTypeface* typeface, int size) { { AutoTDWriteTable eblc(typeface->fDWriteFontFace.get()); if (!eblc.fExists) { return false; } if (eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation)) { return false; } if (eblc->version != SkOTTableEmbeddedBitmapLocation::version_initial) { return false; } uint32_t numSizes = SkEndianSwap32(eblc->numSizes); if (eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation) + sizeof(SkOTTableEmbeddedBitmapLocation::BitmapSizeTable) * numSizes) { return false; } const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable* sizeTable = SkTAfter(eblc.get()); for (uint32_t i = 0; i < numSizes; ++i, ++sizeTable) { if (sizeTable->ppemX == size && sizeTable->ppemY == size) { // TODO: determine if we should dig through IndexSubTableArray/IndexSubTable // to determine the actual number of glyphs with bitmaps. // TODO: Ensure that the bitmaps actually cover a significant portion of the strike. //TODO: Endure that the bitmaps are bi-level. if (sizeTable->endGlyphIndex >= sizeTable->startGlyphIndex + 3) { return true; } } } } { AutoTDWriteTable ebsc(typeface->fDWriteFontFace.get()); if (!ebsc.fExists) { return false; } if (ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling)) { return false; } if (ebsc->version != SkOTTableEmbeddedBitmapScaling::version_initial) { return false; } uint32_t numSizes = SkEndianSwap32(ebsc->numSizes); if (ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling) + sizeof(SkOTTableEmbeddedBitmapScaling::BitmapScaleTable) * numSizes) { return false; } const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable* scaleTable = SkTAfter(ebsc.get()); for (uint32_t i = 0; i < numSizes; ++i, ++scaleTable) { if (scaleTable->ppemX == size && scaleTable->ppemY == size) { // EBSC tables are normally only found in bitmap only fonts. return true; } } } return false; } static bool bothZero(SkScalar a, SkScalar b) { return 0 == a && 0 == b; } // returns false if there is any non-90-rotation or skew static bool isAxisAligned(const SkScalerContext::Rec& rec) { return 0 == rec.fPreSkewX && (bothZero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) || bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1])); } SkScalerContext_DW::SkScalerContext_DW(DWriteFontTypeface* typeface, const SkDescriptor* desc) : SkScalerContext(typeface, desc) , fTypeface(SkRef(typeface)) , fGlyphCount(-1) { // In general, all glyphs should use CLEARTYPE_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). // // DirectWrite's IDWriteFontFace::GetRecommendedRenderingMode does not do // this. As a result, determine the actual size of the text and then see if // there are any embedded bi-level bitmaps of that size. If there are, then // force bitmaps by requesting bi-level rendering. // // FreeType allows for separate ppemX and ppemY, but DirectWrite assumes // square pixels and only uses ppemY. Therefore the transform must track any // non-uniform x-scale. // // Also, rotated glyphs should have the same absolute advance widths as // horizontal glyphs and the subpixel flag should not affect glyph shapes. // A is the total matrix. SkMatrix A; fRec.getSingleMatrix(&A); // h is where A maps the horizontal baseline. SkPoint h = SkPoint::Make(SK_Scalar1, 0); A.mapPoints(&h, 1); // G is the Givens Matrix for A (rotational matrix where GA[0][1] == 0). SkMatrix G; SkComputeGivensRotation(h, &G); // GA is the matrix A with rotation removed. SkMatrix GA(G); GA.preConcat(A); // realTextSize is the actual device size we want (as opposed to the size the user requested). // gdiTextSize is the size we request when GDI compatible. // If the scale is negative, this means the matrix will do the flip anyway. SkScalar realTextSize = SkScalarAbs(GA.get(SkMatrix::kMScaleY)); // Due to floating point math, the lower bits are suspect. Round carefully. SkScalar gdiTextSize = SkScalarRoundToScalar(realTextSize * 64.0f) / 64.0f; if (gdiTextSize == 0) { gdiTextSize = SK_Scalar1; } bool bitmapRequested = SkToBool(fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag); bool hasBitmap = false; bool axisAlignedBitmap = false; if (bitmapRequested) { hasBitmap = hasBitmapStrike(typeface, SkScalarTruncToInt(gdiTextSize)); axisAlignedBitmap = isAxisAligned(fRec); } // If the user requested aliased, do so with aliased compatible metrics. if (SkMask::kBW_Format == fRec.fMaskFormat) { fTextSizeRender = gdiTextSize; fRenderingMode = DWRITE_RENDERING_MODE_ALIASED; fTextureType = DWRITE_TEXTURE_ALIASED_1x1; fTextSizeMeasure = gdiTextSize; fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; // If we can use a bitmap, use gdi classic rendering and measurement. // This will not always provide a bitmap, but matches expected behavior. } else if (hasBitmap && axisAlignedBitmap) { fTextSizeRender = gdiTextSize; fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC; fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; fTextSizeMeasure = gdiTextSize; fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; // If rotated but the horizontal text could have used a bitmap, // render high quality rotated glyphs but measure using bitmap metrics. } else if (hasBitmap) { fTextSizeRender = gdiTextSize; fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; fTextSizeMeasure = gdiTextSize; fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; // The normal case is to use natural symmetric rendering and linear metrics. } else { fTextSizeRender = realTextSize; fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; fTextSizeMeasure = realTextSize; fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL; } if (this->isSubpixel()) { fTextSizeMeasure = realTextSize; fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL; } // Remove the realTextSize, as that is the text height scale currently in A. SkScalar scale = SkScalarInvert(realTextSize); // fSkXform is the total matrix A without the text height scale. fSkXform = A; fSkXform.preScale(scale, scale); //remove the text height scale. fXform.m11 = SkScalarToFloat(fSkXform.getScaleX()); fXform.m12 = SkScalarToFloat(fSkXform.getSkewY()); fXform.m21 = SkScalarToFloat(fSkXform.getSkewX()); fXform.m22 = SkScalarToFloat(fSkXform.getScaleY()); fXform.dx = 0; fXform.dy = 0; // GsA is the non-rotational part of A without the text height scale. SkMatrix GsA(GA); GsA.preScale(scale, scale); //remove text height scale, G is rotational so reorders with scale. fGsA.m11 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleX)); fGsA.m12 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewY)); // This should be ~0. fGsA.m21 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewX)); fGsA.m22 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleY)); fGsA.dx = 0; fGsA.dy = 0; // fG_inv is G inverse, which is fairly simple since G is 2x2 rotational. fG_inv.setAll(G.get(SkMatrix::kMScaleX), -G.get(SkMatrix::kMSkewX), G.get(SkMatrix::kMTransX), -G.get(SkMatrix::kMSkewY), G.get(SkMatrix::kMScaleY), G.get(SkMatrix::kMTransY), G.get(SkMatrix::kMPersp0), G.get(SkMatrix::kMPersp1), G.get(SkMatrix::kMPersp2)); } SkScalerContext_DW::~SkScalerContext_DW() { } unsigned SkScalerContext_DW::generateGlyphCount() { if (fGlyphCount < 0) { fGlyphCount = fTypeface->fDWriteFontFace->GetGlyphCount(); } return fGlyphCount; } uint16_t SkScalerContext_DW::generateCharToGlyph(SkUnichar uni) { uint16_t index = 0; fTypeface->fDWriteFontFace->GetGlyphIndices(reinterpret_cast(&uni), 1, &index); return index; } void SkScalerContext_DW::generateAdvance(SkGlyph* glyph) { //Delta is the difference between the right/left side bearing metric //and where the right/left side bearing ends up after hinting. //DirectWrite does not provide this information. glyph->fRsbDelta = 0; glyph->fLsbDelta = 0; glyph->fAdvanceX = 0; glyph->fAdvanceY = 0; uint16_t glyphId = glyph->getGlyphID(); DWRITE_GLYPH_METRICS gm; if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode || DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode) { HRVM(fTypeface->fDWriteFontFace->GetGdiCompatibleGlyphMetrics( fTextSizeMeasure, 1.0f, // pixelsPerDip &fGsA, DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode, &glyphId, 1, &gm), "Could not get gdi compatible glyph metrics."); } else { HRVM(fTypeface->fDWriteFontFace->GetDesignGlyphMetrics(&glyphId, 1, &gm), "Could not get design metrics."); } DWRITE_FONT_METRICS dwfm; fTypeface->fDWriteFontFace->GetMetrics(&dwfm); SkScalar advanceX = SkScalarMulDiv(fTextSizeMeasure, SkIntToScalar(gm.advanceWidth), SkIntToScalar(dwfm.designUnitsPerEm)); SkVector vecs[1] = { { advanceX, 0 } }; if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode || DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode) { fG_inv.mapVectors(vecs, SK_ARRAY_COUNT(vecs)); } else { fSkXform.mapVectors(vecs, SK_ARRAY_COUNT(vecs)); } glyph->fAdvanceX = SkScalarToFixed(vecs[0].fX); glyph->fAdvanceY = SkScalarToFixed(vecs[0].fY); } void SkScalerContext_DW::generateMetrics(SkGlyph* glyph) { glyph->fWidth = 0; this->generateAdvance(glyph); //Measure raster size. fXform.dx = SkFixedToFloat(glyph->getSubXFixed()); fXform.dy = SkFixedToFloat(glyph->getSubYFixed()); FLOAT advance = 0; UINT16 glyphId = glyph->getGlyphID(); DWRITE_GLYPH_OFFSET offset; offset.advanceOffset = 0.0f; offset.ascenderOffset = 0.0f; DWRITE_GLYPH_RUN run; run.glyphCount = 1; run.glyphAdvances = &advance; run.fontFace = fTypeface->fDWriteFontFace.get(); run.fontEmSize = SkScalarToFloat(fTextSizeRender); run.bidiLevel = 0; run.glyphIndices = &glyphId; run.isSideways = FALSE; run.glyphOffsets = &offset; SkTScopedComPtr glyphRunAnalysis; HRVM(fTypeface->fFactory->CreateGlyphRunAnalysis( &run, 1.0f, // pixelsPerDip, &fXform, fRenderingMode, fMeasuringMode, 0.0f, // baselineOriginX, 0.0f, // baselineOriginY, &glyphRunAnalysis), "Could not create glyph run analysis."); RECT bbox; HRVM(glyphRunAnalysis->GetAlphaTextureBounds(fTextureType, &bbox), "Could not get texture bounds."); glyph->fWidth = SkToU16(bbox.right - bbox.left); glyph->fHeight = SkToU16(bbox.bottom - bbox.top); glyph->fLeft = SkToS16(bbox.left); glyph->fTop = SkToS16(bbox.top); } void SkScalerContext_DW::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) { if (!(mx || my)) return; if (mx) { sk_bzero(mx, sizeof(*mx)); } if (my) { sk_bzero(my, sizeof(*my)); } DWRITE_FONT_METRICS dwfm; if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode || DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode) { fTypeface->fDWriteFontFace->GetGdiCompatibleMetrics( fTextSizeRender, 1.0f, // pixelsPerDip &fXform, &dwfm); } else { fTypeface->fDWriteFontFace->GetMetrics(&dwfm); } SkScalar upem = SkIntToScalar(dwfm.designUnitsPerEm); if (mx) { mx->fTop = -fTextSizeRender * SkIntToScalar(dwfm.ascent) / upem; mx->fAscent = mx->fTop; mx->fDescent = fTextSizeRender * SkIntToScalar(dwfm.descent) / upem; mx->fBottom = mx->fDescent; mx->fLeading = fTextSizeRender * SkIntToScalar(dwfm.lineGap) / upem; mx->fXHeight = fTextSizeRender * SkIntToScalar(dwfm.xHeight) / upem; mx->fUnderlineThickness = fTextSizeRender * SkIntToScalar(dwfm.underlineThickness) / upem; mx->fUnderlinePosition = -(fTextSizeRender * SkIntToScalar(dwfm.underlinePosition) / upem); mx->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag; mx->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag; } if (my) { my->fTop = -fTextSizeRender * SkIntToScalar(dwfm.ascent) / upem; my->fAscent = my->fTop; my->fDescent = fTextSizeRender * SkIntToScalar(dwfm.descent) / upem; my->fBottom = my->fDescent; my->fLeading = fTextSizeRender * SkIntToScalar(dwfm.lineGap) / upem; my->fXHeight = fTextSizeRender * SkIntToScalar(dwfm.xHeight) / upem; my->fUnderlineThickness = fTextSizeRender * SkIntToScalar(dwfm.underlineThickness) / upem; my->fUnderlinePosition = -(fTextSizeRender * SkIntToScalar(dwfm.underlinePosition) / upem); my->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag; my->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag; } } /////////////////////////////////////////////////////////////////////////////// #include "SkColorPriv.h" static void bilevel_to_bw(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph) { const int width = glyph.fWidth; const size_t dstRB = (width + 7) >> 3; uint8_t* SK_RESTRICT dst = static_cast(glyph.fImage); int byteCount = width >> 3; int bitCount = width & 7; for (int y = 0; y < glyph.fHeight; ++y) { if (byteCount > 0) { for (int i = 0; i < byteCount; ++i) { unsigned byte = 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; } } if (bitCount > 0) { unsigned byte = 0; unsigned mask = 0x80; for (int i = 0; i < bitCount; i++) { byte |= (src[i]) & mask; mask >>= 1; } dst[byteCount] = byte; } src += bitCount; dst += dstRB; } } template 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; uint8_t* SK_RESTRICT dst = static_cast(glyph.fImage); for (U16CPU y = 0; y < glyph.fHeight; y++) { for (U16CPU i = 0; i < width; i++) { U8CPU r = *(src++); U8CPU g = *(src++); U8CPU b = *(src++); dst[i] = sk_apply_lut_if((r + g + b) / 3, table8); } dst = (uint8_t*)((char*)dst + dstRB); } } template static void rgb_to_lcd16(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph, const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) { const size_t dstRB = glyph.rowBytes(); const U16CPU width = glyph.fWidth; uint16_t* SK_RESTRICT dst = static_cast(glyph.fImage); for (U16CPU y = 0; y < glyph.fHeight; y++) { for (U16CPU i = 0; i < width; i++) { U8CPU r = sk_apply_lut_if(*(src++), tableR); U8CPU g = sk_apply_lut_if(*(src++), tableG); U8CPU b = sk_apply_lut_if(*(src++), tableB); dst[i] = SkPack888ToRGB16(r, g, b); } dst = (uint16_t*)((char*)dst + dstRB); } } template static void rgb_to_lcd32(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph, const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) { const size_t dstRB = glyph.rowBytes(); const U16CPU width = glyph.fWidth; SkPMColor* SK_RESTRICT dst = static_cast(glyph.fImage); for (U16CPU y = 0; y < glyph.fHeight; y++) { for (U16CPU i = 0; i < width; i++) { U8CPU r = sk_apply_lut_if(*(src++), tableR); U8CPU g = sk_apply_lut_if(*(src++), tableG); U8CPU b = sk_apply_lut_if(*(src++), tableB); dst[i] = SkPackARGB32(0xFF, r, g, b); } dst = (SkPMColor*)((char*)dst + dstRB); } } const void* SkScalerContext_DW::drawDWMask(const SkGlyph& glyph) { int sizeNeeded = glyph.fWidth * glyph.fHeight; if (DWRITE_RENDERING_MODE_ALIASED != fRenderingMode) { sizeNeeded *= 3; } if (sizeNeeded > fBits.count()) { fBits.setCount(sizeNeeded); } // erase memset(fBits.begin(), 0, sizeNeeded); fXform.dx = SkFixedToFloat(glyph.getSubXFixed()); fXform.dy = SkFixedToFloat(glyph.getSubYFixed()); FLOAT advance = 0.0f; UINT16 index = glyph.getGlyphID(); DWRITE_GLYPH_OFFSET offset; offset.advanceOffset = 0.0f; offset.ascenderOffset = 0.0f; DWRITE_GLYPH_RUN run; run.glyphCount = 1; run.glyphAdvances = &advance; run.fontFace = fTypeface->fDWriteFontFace.get(); run.fontEmSize = SkScalarToFloat(fTextSizeRender); run.bidiLevel = 0; run.glyphIndices = &index; run.isSideways = FALSE; run.glyphOffsets = &offset; SkTScopedComPtr glyphRunAnalysis; HRNM(fTypeface->fFactory->CreateGlyphRunAnalysis(&run, 1.0f, // pixelsPerDip, &fXform, fRenderingMode, 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. RECT bbox; bbox.left = glyph.fLeft; bbox.top = glyph.fTop; bbox.right = glyph.fLeft + glyph.fWidth; bbox.bottom = glyph.fTop + glyph.fHeight; HRNM(glyphRunAnalysis->CreateAlphaTexture(fTextureType, &bbox, fBits.begin(), sizeNeeded), "Could not draw mask."); return fBits.begin(); } void SkScalerContext_DW::generateImage(const SkGlyph& glyph) { //Create the mask. const void* bits = this->drawDWMask(glyph); if (!bits) { sk_bzero(glyph.fImage, glyph.computeImageSize()); return; } //Copy the mask into the glyph. const uint8_t* src = (const uint8_t*)bits; if (DWRITE_RENDERING_MODE_ALIASED == fRenderingMode) { bilevel_to_bw(src, glyph); const_cast(glyph).fMaskFormat = SkMask::kBW_Format; } else if (!isLCD(fRec)) { if (fPreBlend.isApplicable()) { rgb_to_a8(src, glyph, fPreBlend.fG); } else { rgb_to_a8(src, glyph, fPreBlend.fG); } } else if (SkMask::kLCD16_Format == glyph.fMaskFormat) { if (fPreBlend.isApplicable()) { rgb_to_lcd16(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } else { rgb_to_lcd16(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } } else { SkASSERT(SkMask::kLCD32_Format == glyph.fMaskFormat); if (fPreBlend.isApplicable()) { rgb_to_lcd32(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } else { rgb_to_lcd32(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } } } void SkScalerContext_DW::generatePath(const SkGlyph& glyph, SkPath* path) { SkASSERT(&glyph && path); path->reset(); SkTScopedComPtr geometryToPath; HRVM(SkDWriteGeometrySink::Create(path, &geometryToPath), "Could not create geometry to path converter."); uint16_t glyphId = glyph.getGlyphID(); //TODO: convert to<->from DIUs? This would make a difference if hinting. //It may not be needed, it appears that DirectWrite only hints at em size. HRVM(fTypeface->fDWriteFontFace->GetGlyphRunOutline(SkScalarToFloat(fTextSizeRender), &glyphId, NULL, //advances NULL, //offsets 1, //num glyphs FALSE, //sideways FALSE, //rtl geometryToPath.get()), "Could not create glyph outline."); path->transform(fSkXform); }