diff options
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.cpp | 747 |
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); +} |