SkScalerContext_DW::SkScalerContext_DW(DWriteFontTypeface* typeface, const SkScalerContextEffects& effects, const SkDescriptor* desc) : SkScalerContext(typeface, effects, desc) , fTypeface(SkRef(typeface)) , fGlyphCount(-1) { #if SK_HAS_DWRITE_2_H fTypeface->fFactory->QueryInterface<IDWriteFactory2>(&fFactory2); SkTScopedComPtr<IDWriteFontFace2> fontFace2; fTypeface->fDWriteFontFace->QueryInterface<IDWriteFontFace2>(&fontFace2); fIsColorFont = fFactory2.get() && fontFace2.get() && fontFace2->IsColorFont(); #endif // 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. SkVector scale; SkMatrix GsA; fRec.computeMatrices(SkScalerContextRec::kVertical_PreMatrixScale, &scale, &fSkXform, &GsA, &fG_inv); 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; 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; // 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. const SkScalar realTextSize = scale.fY; // 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 treatLikeBitmap = false; bool axisAlignedBitmap = false; if (bitmapRequested) { // 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); treatLikeBitmap = has_bitmap_strike(typeface, range); axisAlignedBitmap = is_axis_aligned(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 (treatLikeBitmap && 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 (treatLikeBitmap) { fTextSizeRender = gdiTextSize; fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; fTextSizeMeasure = gdiTextSize; fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; // Fonts that have hints but no gasp table get non-symmetric rendering. // Usually such fonts have low quality hints which were never tested // with anything but GDI ClearType classic. Such fonts often rely on // 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; fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; fTextSizeMeasure = realTextSize; fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL; // 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; } }
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 treatLikeBitmap = false; bool axisAlignedBitmap = false; if (bitmapRequested) { // 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); treatLikeBitmap = has_bitmap_strike(typeface, range); axisAlignedBitmap = is_axis_aligned(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 (treatLikeBitmap && 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 (treatLikeBitmap) { fTextSizeRender = gdiTextSize; fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; fTextSizeMeasure = gdiTextSize; fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; // Fonts that have hints but no gasp table get non-symmetric rendering. // Usually such fonts have low quality hints which were never tested // with anything but GDI ClearType classic. Such fonts often rely on // 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; fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; fTextSizeMeasure = realTextSize; fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL; // 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)); }