summaryrefslogtreecommitdiffstats
path: root/chromium/third_party/skia/src/ports/SkScalerContext_win_dw.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/skia/src/ports/SkScalerContext_win_dw.cpp')
-rw-r--r--chromium/third_party/skia/src/ports/SkScalerContext_win_dw.cpp747
1 files changed, 747 insertions, 0 deletions
diff --git a/chromium/third_party/skia/src/ports/SkScalerContext_win_dw.cpp b/chromium/third_party/skia/src/ports/SkScalerContext_win_dw.cpp
new file mode 100644
index 00000000000..28d41c1b9e7
--- /dev/null
+++ b/chromium/third_party/skia/src/ports/SkScalerContext_win_dw.cpp
@@ -0,0 +1,747 @@
+/*
+ * 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 "SkOTTable_gasp.h"
+#include "SkOTTable_maxp.h"
+#include "SkPath.h"
+#include "SkScalerContext.h"
+#include "SkScalerContext_win_dw.h"
+#include "SkTScopedComPtr.h"
+#include "SkTypeface_win_dw.h"
+
+#include <dwrite.h>
+#include <dwrite_1.h>
+
+static bool isLCD(const SkScalerContext::Rec& rec) {
+ return SkMask::kLCD16_Format == rec.fMaskFormat ||
+ SkMask::kLCD32_Format == rec.fMaskFormat;
+}
+
+static bool is_hinted_without_gasp(DWriteFontTypeface* typeface) {
+ AutoTDWriteTable<SkOTTableMaximumProfile> maxp(typeface->fDWriteFontFace.get());
+ if (!maxp.fExists) {
+ return false;
+ }
+ if (maxp.fSize < sizeof(SkOTTableMaximumProfile::Version::TT)) {
+ return false;
+ }
+ if (maxp->version.version != SkOTTableMaximumProfile::Version::TT::VERSION) {
+ return false;
+ }
+
+ if (0 == maxp->version.tt.maxSizeOfInstructions) {
+ // No hints.
+ return false;
+ }
+
+ AutoTDWriteTable<SkOTTableGridAndScanProcedure> gasp(typeface->fDWriteFontFace.get());
+ return !gasp.fExists;
+}
+
+/** A PPEMRange is inclusive, [min, max]. */
+struct PPEMRange {
+ int min;
+ int max;
+};
+
+/** 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) {
+ AutoTDWriteTable<SkOTTableGridAndScanProcedure> gasp(typeface->fDWriteFontFace.get());
+ if (!gasp.fExists) {
+ return;
+ }
+ if (gasp.fSize < sizeof(SkOTTableGridAndScanProcedure)) {
+ return;
+ }
+ if (gasp->version != SkOTTableGridAndScanProcedure::version0 &&
+ gasp->version != SkOTTableGridAndScanProcedure::version1)
+ {
+ return ;
+ }
+
+ uint16_t numRanges = SkEndianSwap16(gasp->numRanges);
+ if (numRanges > 1024 ||
+ gasp.fSize < sizeof(SkOTTableGridAndScanProcedure) +
+ sizeof(SkOTTableGridAndScanProcedure::GaspRange) * numRanges)
+ {
+ return;
+ }
+
+ 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);
+ // 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)
+ {
+ range->min = minPPEM + 1;
+ range->max = maxPPEM;
+ return;
+ }
+ minPPEM = maxPPEM;
+ }
+
+ return;
+}
+
+static bool has_bitmap_strike(DWriteFontTypeface* typeface, PPEMRange range) {
+ {
+ AutoTDWriteTable<SkOTTableEmbeddedBitmapLocation> 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 (numSizes > 1024 ||
+ eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation) +
+ sizeof(SkOTTableEmbeddedBitmapLocation::BitmapSizeTable) * numSizes)
+ {
+ return false;
+ }
+
+ const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable* sizeTable =
+ SkTAfter<const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable>(eblc.get());
+ for (uint32_t i = 0; i < numSizes; ++i, ++sizeTable) {
+ if (sizeTable->ppemX == sizeTable->ppemY &&
+ range.min <= sizeTable->ppemX && sizeTable->ppemX <= range.max)
+ {
+ // 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: Ensure that the bitmaps are bi-level?
+ if (sizeTable->endGlyphIndex >= sizeTable->startGlyphIndex + 3) {
+ return true;
+ }
+ }
+ }
+ }
+
+ {
+ AutoTDWriteTable<SkOTTableEmbeddedBitmapScaling> 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 (numSizes > 1024 ||
+ ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling) +
+ sizeof(SkOTTableEmbeddedBitmapScaling::BitmapScaleTable) * numSizes)
+ {
+ return false;
+ }
+
+ const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable* scaleTable =
+ SkTAfter<const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable>(ebsc.get());
+ for (uint32_t i = 0; i < numSizes; ++i, ++scaleTable) {
+ if (scaleTable->ppemX == scaleTable->ppemY &&
+ range.min <= scaleTable->ppemX && scaleTable->ppemX <= range.max) {
+ // EBSC tables are normally only found in bitmap only fonts.
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+static bool both_zero(SkScalar a, SkScalar b) {
+ return 0 == a && 0 == b;
+}
+
+// returns false if there is any non-90-rotation or skew
+static bool is_axis_aligned(const SkScalerContext::Rec& rec) {
+ return 0 == rec.fPreSkewX &&
+ (both_zero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
+ both_zero(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 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));
+}
+
+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<UINT32*>(&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));
+
+ if (!this->isSubpixel()) {
+ advanceX = SkScalarRoundToScalar(advanceX);
+ }
+
+ SkVector vecs[1] = { { advanceX, 0 } };
+ if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode ||
+ DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
+ {
+ // DirectWrite produced 'compatible' metrics, but while close,
+ // the end result is not always an integer as it would be with GDI.
+ vecs[0].fX = SkScalarRoundToScalar(advanceX);
+ 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<IDWriteGlyphRunAnalysis> 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->fAscent = -fTextSizeRender * SkIntToScalar(dwfm.ascent) / upem;
+ my->fDescent = fTextSizeRender * SkIntToScalar(dwfm.descent) / upem;
+ 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;
+
+ if (NULL != fTypeface->fDWriteFontFace1.get()) {
+ DWRITE_FONT_METRICS1 dwfm1;
+ fTypeface->fDWriteFontFace1->GetMetrics(&dwfm1);
+ my->fTop = -fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxTop) / upem;
+ my->fBottom = -fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxBottom) / upem;
+ my->fXMin = fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxLeft) / upem;
+ my->fXMax = fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxRight) / upem;
+
+ my->fMaxCharWidth = my->fXMax - my->fXMin;
+ } else {
+ AutoTDWriteTable<SkOTTableHead> head(fTypeface->fDWriteFontFace.get());
+ if (head.fExists &&
+ head.fSize >= sizeof(SkOTTableHead) &&
+ head->version == SkOTTableHead::version1)
+ {
+ my->fTop = -fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->yMax) / upem;
+ my->fBottom = -fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->yMin) / upem;
+ my->fXMin = fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->xMin) / upem;
+ my->fXMax = fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->xMax) / upem;
+
+ my->fMaxCharWidth = my->fXMax - my->fXMin;
+ } else {
+ my->fTop = my->fAscent;
+ my->fBottom = my->fDescent;
+ }
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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<uint8_t*>(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<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;
+ 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 r = *(src++);
+ U8CPU g = *(src++);
+ U8CPU b = *(src++);
+ dst[i] = sk_apply_lut_if<APPLY_PREBLEND>((r + g + b) / 3, table8);
+ }
+ dst = (uint8_t*)((char*)dst + dstRB);
+ }
+}
+
+template<bool APPLY_PREBLEND>
+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<uint16_t*>(glyph.fImage);
+
+ for (U16CPU y = 0; y < glyph.fHeight; y++) {
+ for (U16CPU i = 0; i < width; i++) {
+ U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableR);
+ U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableG);
+ U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableB);
+ dst[i] = SkPack888ToRGB16(r, g, b);
+ }
+ dst = (uint16_t*)((char*)dst + dstRB);
+ }
+}
+
+template<bool APPLY_PREBLEND>
+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<SkPMColor*>(glyph.fImage);
+
+ for (U16CPU y = 0; y < glyph.fHeight; y++) {
+ for (U16CPU i = 0; i < width; i++) {
+ U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableR);
+ U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableG);
+ U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(*(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<IDWriteGlyphRunAnalysis> 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<SkGlyph&>(glyph).fMaskFormat = SkMask::kBW_Format;
+ } else if (!isLCD(fRec)) {
+ if (fPreBlend.isApplicable()) {
+ rgb_to_a8<true>(src, glyph, fPreBlend.fG);
+ } else {
+ rgb_to_a8<false>(src, glyph, fPreBlend.fG);
+ }
+ } else if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
+ if (fPreBlend.isApplicable()) {
+ rgb_to_lcd16<true>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+ } else {
+ rgb_to_lcd16<false>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+ }
+ } else {
+ SkASSERT(SkMask::kLCD32_Format == glyph.fMaskFormat);
+ if (fPreBlend.isApplicable()) {
+ rgb_to_lcd32<true>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+ } else {
+ rgb_to_lcd32<false>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+ }
+ }
+}
+
+void SkScalerContext_DW::generatePath(const SkGlyph& glyph, SkPath* path) {
+ SkASSERT(&glyph && path);
+
+ path->reset();
+
+ SkTScopedComPtr<IDWriteGeometrySink> 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);
+}