From 058670b1e52910e61d9de294a46b5023ed6dd964 Mon Sep 17 00:00:00 2001 From: "bungeman@google.com" Date: Thu, 1 May 2014 20:39:14 +0000 Subject: [PATCH] Add support for kEmbeddedBitmapText_Flag to DirectWrite. R=reed@google.com Review URL: https://codereview.chromium.org/263503004 git-svn-id: http://skia.googlecode.com/svn/trunk@14518 2bbb7eff-a529-9590-31e7-b0007b416f81 --- src/ports/SkFontHost_win_dw.cpp | 309 +++++++++++++++++++++++++------- src/sfnt/SkOTTable_EBDT.h | 109 +++++++++++ src/sfnt/SkOTTable_EBLC.h | 152 ++++++++++++++++ src/sfnt/SkOTTable_EBSC.h | 41 +++++ 4 files changed, 547 insertions(+), 64 deletions(-) create mode 100644 src/sfnt/SkOTTable_EBDT.h create mode 100644 src/sfnt/SkOTTable_EBLC.h create mode 100644 src/sfnt/SkOTTable_EBSC.h diff --git a/src/ports/SkFontHost_win_dw.cpp b/src/ports/SkFontHost_win_dw.cpp index 36ed4d4f0..cd32fdbf4 100644 --- a/src/ports/SkFontHost_win_dw.cpp +++ b/src/ports/SkFontHost_win_dw.cpp @@ -22,7 +22,10 @@ #include "SkGlyph.h" #include "SkHRESULT.h" #include "SkMaskGamma.h" +#include "SkMatrix22.h" #include "SkOnce.h" +#include "SkOTTable_EBLC.h" +#include "SkOTTable_EBSC.h" #include "SkOTTable_head.h" #include "SkOTTable_hhea.h" #include "SkOTTable_OS_2.h" @@ -449,7 +452,22 @@ private: const void* drawDWMask(const SkGlyph& glyph); SkTDArray fBits; + /** The total matrix without the text height scale. */ + SkMatrix fSkXform; + /** The total matrix without the text height scale. */ DWRITE_MATRIX fXform; + /** The non-rotational part of total matrix without the text height scale. + * This is used to find the magnitude of gdi compatible advances. + */ + DWRITE_MATRIX fGsA; + /** The inverse of the rotational part of the total matrix. + * This is used to find the direction of gdi compatible advances. + */ + SkMatrix fG_inv; + /** The text size to render with. */ + SkScalar fTextSizeRender; + /** The text size to measure with. */ + SkScalar fTextSizeMeasure; SkAutoTUnref fTypeface; int fGlyphCount; DWRITE_RENDERING_MODE fRenderingMode; @@ -570,32 +588,224 @@ static bool FindByDWriteFont(SkTypeface* face, SkTypeface::Style requestedStyle, wcscmp(dwFaceFontNameChar.get(), dwFontNameChar.get()) == 0; } +class AutoDWriteTable { +public: + AutoDWriteTable(IDWriteFontFace* fontFace, UINT32 beTag) : fFontFace(fontFace), fExists(FALSE) { + // Any errors are ignored, user must check fExists anyway. + fontFace->TryGetFontTable(beTag, + reinterpret_cast(&fData), &fSize, &fLock, &fExists); + } + ~AutoDWriteTable() { + if (fExists) { + fFontFace->ReleaseFontTable(fLock); + } + } + + const uint8_t* fData; + UINT32 fSize; + BOOL fExists; +private: + // Borrowed reference, the user must ensure the fontFace stays alive. + IDWriteFontFace* fFontFace; + void* fLock; +}; +template class AutoTDWriteTable : public AutoDWriteTable { +public: + static const UINT32 tag = DWRITE_MAKE_OPENTYPE_TAG(T::TAG0, T::TAG1, T::TAG2, T::TAG3); + AutoTDWriteTable(IDWriteFontFace* fontFace) : AutoDWriteTable(fontFace, tag) { } + + const T* get() const { return reinterpret_cast(fData); } + const T* operator->() const { return reinterpret_cast(fData); } +}; + +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) { - fXform.m11 = SkScalarToFloat(fRec.fPost2x2[0][0]); - fXform.m12 = SkScalarToFloat(fRec.fPost2x2[1][0]); - fXform.m21 = SkScalarToFloat(fRec.fPost2x2[0][1]); - fXform.m22 = SkScalarToFloat(fRec.fPost2x2[1][1]); - fXform.dx = 0; - fXform.dy = 0; + // 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. - if (SkMask::kBW_Format == fRec.fMaskFormat) { + // 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 roundedTextSize = SkScalarRoundToScalar(realTextSize * 64.0f) / 64.0f; + SkScalar gdiTextSize = SkScalarFloorToScalar(roundedTextSize); + if (gdiTextSize == 0) { + gdiTextSize = SK_Scalar1; + } + + bool hasBitmap = fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag && + hasBitmapStrike(typeface, SkScalarTruncToInt(gdiTextSize)); + bool axisAligned = isAxisAligned(fRec); + bool isBiLevel = SkMask::kBW_Format == fRec.fMaskFormat || (hasBitmap && axisAligned); + + if (isBiLevel) { + fTextSizeRender = gdiTextSize; fRenderingMode = DWRITE_RENDERING_MODE_ALIASED; fTextureType = DWRITE_TEXTURE_ALIASED_1x1; + fTextSizeMeasure = gdiTextSize; fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; - } else { + } else if (hasBitmap) { + // If rotated but the horizontal text would have used a bitmap, + // render high quality rotated glyphs using the bitmap metrics. + fTextSizeRender = gdiTextSize; fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; + fTextSizeMeasure = gdiTextSize; + fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; + } 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)); + + // 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() { @@ -631,9 +841,9 @@ void SkScalerContext_DW::generateAdvance(SkGlyph* glyph) { DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode) { HRVM(fTypeface->fDWriteFontFace->GetGdiCompatibleGlyphMetrics( - fRec.fTextSize, + fTextSizeMeasure, 1.0f, // pixelsPerDip - &fXform, + &fGsA, DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode, &glyphId, 1, &gm), @@ -645,7 +855,7 @@ void SkScalerContext_DW::generateAdvance(SkGlyph* glyph) { DWRITE_FONT_METRICS dwfm; fTypeface->fDWriteFontFace->GetMetrics(&dwfm); - SkScalar advanceX = SkScalarMulDiv(fRec.fTextSize, + SkScalar advanceX = SkScalarMulDiv(fTextSizeMeasure, SkIntToScalar(gm.advanceWidth), SkIntToScalar(dwfm.designUnitsPerEm)); @@ -654,9 +864,13 @@ void SkScalerContext_DW::generateAdvance(SkGlyph* glyph) { } SkVector vecs[1] = { { advanceX, 0 } }; - SkMatrix mat; - fRec.getMatrixFrom2x2(&mat); - mat.mapVectors(vecs, SK_ARRAY_COUNT(vecs)); + 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); @@ -683,7 +897,7 @@ void SkScalerContext_DW::generateMetrics(SkGlyph* glyph) { run.glyphCount = 1; run.glyphAdvances = &advance; run.fontFace = fTypeface->fDWriteFontFace.get(); - run.fontEmSize = SkScalarToFloat(fRec.fTextSize); + run.fontEmSize = SkScalarToFloat(fTextSizeRender); run.bidiLevel = 0; run.glyphIndices = &glyphId; run.isSideways = FALSE; @@ -728,7 +942,7 @@ void SkScalerContext_DW::generateFontMetrics(SkPaint::FontMetrics* mx, DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode) { fTypeface->fDWriteFontFace->GetGdiCompatibleMetrics( - fRec.fTextSize, + fTextSizeRender, 1.0f, // pixelsPerDip &fXform, &dwfm); @@ -738,28 +952,28 @@ void SkScalerContext_DW::generateFontMetrics(SkPaint::FontMetrics* mx, SkScalar upem = SkIntToScalar(dwfm.designUnitsPerEm); if (mx) { - mx->fTop = -fRec.fTextSize * SkIntToScalar(dwfm.ascent) / upem; + mx->fTop = -fTextSizeRender * SkIntToScalar(dwfm.ascent) / upem; mx->fAscent = mx->fTop; - mx->fDescent = fRec.fTextSize * SkIntToScalar(dwfm.descent) / upem; + mx->fDescent = fTextSizeRender * SkIntToScalar(dwfm.descent) / upem; mx->fBottom = mx->fDescent; - mx->fLeading = fRec.fTextSize * SkIntToScalar(dwfm.lineGap) / upem; - mx->fXHeight = fRec.fTextSize * SkIntToScalar(dwfm.xHeight) / upem; - mx->fUnderlineThickness = fRec.fTextSize * SkIntToScalar(dwfm.underlinePosition) / upem; - mx->fUnderlinePosition = -(fRec.fTextSize * SkIntToScalar(dwfm.underlineThickness) / upem); + mx->fLeading = fTextSizeRender * SkIntToScalar(dwfm.lineGap) / upem; + mx->fXHeight = fTextSizeRender * SkIntToScalar(dwfm.xHeight) / upem; + mx->fUnderlineThickness = fTextSizeRender * SkIntToScalar(dwfm.underlinePosition) / upem; + mx->fUnderlinePosition = -(fTextSizeRender * SkIntToScalar(dwfm.underlineThickness) / upem); mx->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag; mx->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag; } if (my) { - my->fTop = -fRec.fTextSize * SkIntToScalar(dwfm.ascent) / upem; + my->fTop = -fTextSizeRender * SkIntToScalar(dwfm.ascent) / upem; my->fAscent = my->fTop; - my->fDescent = fRec.fTextSize * SkIntToScalar(dwfm.descent) / upem; + my->fDescent = fTextSizeRender * SkIntToScalar(dwfm.descent) / upem; my->fBottom = my->fDescent; - my->fLeading = fRec.fTextSize * SkIntToScalar(dwfm.lineGap) / upem; - my->fXHeight = fRec.fTextSize * SkIntToScalar(dwfm.xHeight) / upem; - my->fUnderlineThickness = fRec.fTextSize * SkIntToScalar(dwfm.underlinePosition) / upem; - my->fUnderlinePosition = -(fRec.fTextSize * SkIntToScalar(dwfm.underlineThickness) / upem); + my->fLeading = fTextSizeRender * SkIntToScalar(dwfm.lineGap) / upem; + my->fXHeight = fTextSizeRender * SkIntToScalar(dwfm.xHeight) / upem; + my->fUnderlineThickness = fTextSizeRender * SkIntToScalar(dwfm.underlinePosition) / upem; + my->fUnderlinePosition = -(fTextSizeRender * SkIntToScalar(dwfm.underlineThickness) / upem); my->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag; my->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag; @@ -888,7 +1102,7 @@ const void* SkScalerContext_DW::drawDWMask(const SkGlyph& glyph) { run.glyphCount = 1; run.glyphAdvances = &advance; run.fontFace = fTypeface->fDWriteFontFace.get(); - run.fontEmSize = SkScalarToFloat(fRec.fTextSize); + run.fontEmSize = SkScalarToFloat(fTextSizeRender); run.bidiLevel = 0; run.glyphIndices = &index; run.isSideways = FALSE; @@ -966,7 +1180,7 @@ void SkScalerContext_DW::generatePath(const SkGlyph& glyph, SkPath* path) { 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(fRec.fTextSize), + HRVM(fTypeface->fDWriteFontFace->GetGlyphRunOutline(SkScalarToFloat(fTextSizeRender), &glyphId, NULL, //advances NULL, //offsets @@ -976,9 +1190,7 @@ void SkScalerContext_DW::generatePath(const SkGlyph& glyph, SkPath* path) { geometryToPath.get()), "Could not create glyph outline."); - SkMatrix mat; - fRec.getMatrixFrom2x2(&mat); - path->transform(mat); + path->transform(fSkXform); } void DWriteFontTypeface::onGetFontDescriptor(SkFontDescriptor* desc, @@ -1146,28 +1358,6 @@ int DWriteFontTypeface::onGetTableTags(SkFontTableTag tags[]) const { return stream.get() ? SkFontStream::GetTableTags(stream, ttcIndex, tags) : 0; } -class AutoDWriteTable { -public: - AutoDWriteTable(IDWriteFontFace* fontFace, UINT32 beTag) : fFontFace(fontFace), fExists(FALSE) { - // Any errors are ignored, user must check fExists anyway. - fontFace->TryGetFontTable(beTag, - reinterpret_cast(&fData), &fSize, &fLock, &fExists); - } - ~AutoDWriteTable() { - if (fExists) { - fFontFace->ReleaseFontTable(fLock); - } - } - - const uint8_t* fData; - UINT32 fSize; - BOOL fExists; -private: - // Borrowed reference, the user must ensure the fontFace stays alive. - IDWriteFontFace* fFontFace; - void* fLock; -}; - size_t DWriteFontTypeface::onGetTableData(SkFontTableTag tag, size_t offset, size_t length, void* data) const { @@ -1260,7 +1450,6 @@ void DWriteFontTypeface::onFilterRec(SkScalerContext::Rec* rec) const { unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag | SkScalerContext::kForceAutohinting_Flag | - SkScalerContext::kEmbeddedBitmapText_Flag | SkScalerContext::kEmbolden_Flag | SkScalerContext::kLCD_BGROrder_Flag | SkScalerContext::kLCD_Vertical_Flag; @@ -1362,14 +1551,6 @@ static bool getWidthAdvance(IDWriteFontFace* fontFace, int gId, int16_t* advance return true; } -template class AutoTDWriteTable : public AutoDWriteTable { -public: - static const UINT32 tag = DWRITE_MAKE_OPENTYPE_TAG(T::TAG0, T::TAG1, T::TAG2, T::TAG3); - AutoTDWriteTable(IDWriteFontFace* fontFace) : AutoDWriteTable(fontFace, tag) { } - - const T* operator->() const { return reinterpret_cast(fData); } -}; - SkAdvancedTypefaceMetrics* DWriteFontTypeface::onGetAdvancedTypefaceMetrics( SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo, const uint32_t* glyphIDs, diff --git a/src/sfnt/SkOTTable_EBDT.h b/src/sfnt/SkOTTable_EBDT.h new file mode 100644 index 000000000..89d7a3abe --- /dev/null +++ b/src/sfnt/SkOTTable_EBDT.h @@ -0,0 +1,109 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkOTTable_EBDT_DEFINED +#define SkOTTable_EBDT_DEFINED + +#include "SkEndian.h" +#include "SkOTTableTypes.h" +#include "SkOTTable_head.h" +#include "SkOTTable_loca.h" +#include "SkTypedEnum.h" + +#pragma pack(push, 1) + +struct SkOTTableEmbeddedBitmapData { + static const SK_OT_CHAR TAG0 = 'E'; + static const SK_OT_CHAR TAG1 = 'B'; + static const SK_OT_CHAR TAG2 = 'D'; + static const SK_OT_CHAR TAG3 = 'T'; + static const SK_OT_ULONG TAG = SkOTTableTAG::value; + + SK_OT_Fixed version; + static const SK_OT_Fixed version_initial = SkTEndian_SwapBE32(0x00020000); + + struct BigGlyphMetrics { + SK_OT_BYTE height; + SK_OT_BYTE width; + SK_OT_CHAR horiBearingX; + SK_OT_CHAR horiBearingY; + SK_OT_BYTE horiAdvance; + SK_OT_CHAR vertBearingX; + SK_OT_CHAR vertBearingY; + SK_OT_BYTE vertAdvance; + }; + + struct SmallGlyphMetrics { + SK_OT_BYTE height; + SK_OT_BYTE width; + SK_OT_CHAR bearingX; + SK_OT_CHAR bearingY; + SK_OT_BYTE advance; + }; + + // Small metrics, byte-aligned data. + struct Format1 { + SmallGlyphMetrics smallGlyphMetrics; + //SK_OT_BYTE[] byteAlignedBitmap; + }; + + // Small metrics, bit-aligned data. + struct Format2 { + SmallGlyphMetrics smallGlyphMetrics; + //SK_OT_BYTE[] bitAlignedBitmap; + }; + + // Format 3 is not used. + + // EBLC metrics (IndexSubTable::header::indexFormat 2 or 5), compressed data. + // Only used on Mac. + struct Format4 { + SK_OT_ULONG whiteTreeOffset; + SK_OT_ULONG blackTreeOffset; + SK_OT_ULONG glyphDataOffset; + }; + + // EBLC metrics (IndexSubTable::header::indexFormat 2 or 5), bit-aligned data. + struct Format5 { + //SK_OT_BYTE[] bitAlignedBitmap; + }; + + // Big metrics, byte-aligned data. + struct Format6 { + BigGlyphMetrics bigGlyphMetrics; + //SK_OT_BYTE[] byteAlignedBitmap; + }; + + // Big metrics, bit-aligned data. + struct Format7 { + BigGlyphMetrics bigGlyphMetrics; + //SK_OT_BYTE[] bitAlignedBitmap; + }; + + struct EBDTComponent { + SK_OT_USHORT glyphCode; // Component glyph code + SK_OT_CHAR xOffset; // Position of component left + SK_OT_CHAR yOffset; // Position of component top + }; + + struct Format8 { + SmallGlyphMetrics smallMetrics; // Metrics information for the glyph + SK_OT_BYTE pad; // Pad to short boundary + SK_OT_USHORT numComponents; // Number of components + //EBDTComponent componentArray[numComponents]; // Glyph code, offset array + }; + + struct Format9 { + BigGlyphMetrics bigMetrics; // Metrics information for the glyph + SK_OT_USHORT numComponents; // Number of components + //EBDTComponent componentArray[numComponents]; // Glyph code, offset array + }; +}; + +#pragma pack(pop) + +#endif diff --git a/src/sfnt/SkOTTable_EBLC.h b/src/sfnt/SkOTTable_EBLC.h new file mode 100644 index 000000000..845418d3e --- /dev/null +++ b/src/sfnt/SkOTTable_EBLC.h @@ -0,0 +1,152 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkOTTable_EBLC_DEFINED +#define SkOTTable_EBLC_DEFINED + +#include "SkEndian.h" +#include "SkOTTable_EBDT.h" +#include "SkOTTableTypes.h" +#include "SkTypedEnum.h" + +#pragma pack(push, 1) + +struct SkOTTableEmbeddedBitmapLocation { + static const SK_OT_CHAR TAG0 = 'E'; + static const SK_OT_CHAR TAG1 = 'B'; + static const SK_OT_CHAR TAG2 = 'L'; + static const SK_OT_CHAR TAG3 = 'C'; + static const SK_OT_ULONG TAG = SkOTTableTAG::value; + + SK_OT_Fixed version; + static const SK_OT_Fixed version_initial = SkTEndian_SwapBE32(0x00020000); + + SK_OT_ULONG numSizes; + + struct SbitLineMetrics { + SK_OT_CHAR ascender; + SK_OT_CHAR descender; + SK_OT_BYTE widthMax; + SK_OT_CHAR caretSlopeNumerator; + SK_OT_CHAR caretSlopeDenominator; + SK_OT_CHAR caretOffset; + SK_OT_CHAR minOriginSB; + SK_OT_CHAR minAdvanceSB; + SK_OT_CHAR maxBeforeBL; + SK_OT_CHAR minAfterBL; + SK_OT_CHAR pad1; + SK_OT_CHAR pad2; + }; + + struct BitmapSizeTable { + SK_OT_ULONG indexSubTableArrayOffset; //offset to indexSubtableArray from beginning of EBLC. + SK_OT_ULONG indexTablesSize; //number of bytes in corresponding index subtables and array + SK_OT_ULONG numberOfIndexSubTables; //an index subtable for each range or format change + SK_OT_ULONG colorRef; //not used; set to 0. + SbitLineMetrics hori; //line metrics for text rendered horizontally + SbitLineMetrics vert; //line metrics for text rendered vertically + SK_OT_USHORT startGlyphIndex; //lowest glyph index for this size + SK_OT_USHORT endGlyphIndex; //highest glyph index for this size + SK_OT_BYTE ppemX; //horizontal pixels per Em + SK_OT_BYTE ppemY; //vertical pixels per Em + struct BitDepth { + SK_TYPED_ENUM(Value, SK_OT_BYTE, + ((BW, 1)) + ((Gray4, 2)) + ((Gray16, 4)) + ((Gray256, 8)) + SK_SEQ_END, + SK_SEQ_END) + SK_OT_BYTE value; + } bitDepth; //the Microsoft rasterizer v.1.7 or greater supports + union Flags { + struct Field { + //0-7 + SK_OT_BYTE_BITFIELD( + Horizontal, // Horizontal small glyph metrics + Vertical, // Vertical small glyph metrics + Reserved02, + Reserved03, + Reserved04, + Reserved05, + Reserved06, + Reserved07) + } field; + struct Raw { + static const SK_OT_CHAR Horizontal = 1u << 0; + static const SK_OT_CHAR Vertical = 1u << 1; + SK_OT_CHAR value; + } raw; + } flags; + }; //bitmapSizeTable[numSizes]; + + struct IndexSubTableArray { + SK_OT_USHORT firstGlyphIndex; //first glyph code of this range + SK_OT_USHORT lastGlyphIndex; //last glyph code of this range (inclusive) + SK_OT_ULONG additionalOffsetToIndexSubtable; //add to BitmapSizeTable::indexSubTableArrayOffset to get offset from beginning of 'EBLC' + }; //indexSubTableArray[BitmapSizeTable::numberOfIndexSubTables]; + + struct IndexSubHeader { + SK_OT_USHORT indexFormat; //format of this indexSubTable + SK_OT_USHORT imageFormat; //format of 'EBDT' image data + SK_OT_ULONG imageDataOffset; //offset to image data in 'EBDT' table + }; + + // Variable metrics glyphs with 4 byte offsets + struct IndexSubTable1 { + IndexSubHeader header; + //SK_OT_ULONG offsetArray[lastGlyphIndex - firstGlyphIndex + 1 + 1]; //last element points to one past end of last glyph + //glyphData = offsetArray[glyphIndex - firstGlyphIndex] + imageDataOffset + }; + + // All Glyphs have identical metrics + struct IndexSubTable2 { + IndexSubHeader header; + SK_OT_ULONG imageSize; // all glyphs are of the same size + SkOTTableEmbeddedBitmapData::BigGlyphMetrics bigMetrics; // all glyphs have the same metrics; glyph data may be compressed, byte-aligned, or bit-aligned + }; + + // Variable metrics glyphs with 2 byte offsets + struct IndexSubTable3 { + IndexSubHeader header; + //SK_OT_USHORT offsetArray[lastGlyphIndex - firstGlyphIndex + 1 + 1]; //last element points to one past end of last glyph, may have extra element to force even number of elements + //glyphData = offsetArray[glyphIndex - firstGlyphIndex] + imageDataOffset + }; + + // Variable metrics glyphs with sparse glyph codes + struct IndexSubTable4 { + IndexSubHeader header; + SK_OT_ULONG numGlyphs; + struct CodeOffsetPair { + SK_OT_USHORT glyphCode; + SK_OT_USHORT offset; //location in EBDT + }; //glyphArray[numGlyphs+1] + }; + + // Constant metrics glyphs with sparse glyph codes + struct IndexSubTable5 { + IndexSubHeader header; + SK_OT_ULONG imageSize; //all glyphs have the same data size + SkOTTableEmbeddedBitmapData::BigGlyphMetrics bigMetrics; //all glyphs have the same metrics + SK_OT_ULONG numGlyphs; + //SK_OT_USHORT glyphCodeArray[numGlyphs] //must have even number of entries (set pad to 0) + }; + + union IndexSubTable { + IndexSubHeader header; + IndexSubTable1 format1; + IndexSubTable2 format2; + IndexSubTable3 format3; + IndexSubTable4 format4; + IndexSubTable5 format5; + }; + +}; + +#pragma pack(pop) + +#endif diff --git a/src/sfnt/SkOTTable_EBSC.h b/src/sfnt/SkOTTable_EBSC.h new file mode 100644 index 000000000..316c45d1c --- /dev/null +++ b/src/sfnt/SkOTTable_EBSC.h @@ -0,0 +1,41 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkOTTable_EBSC_DEFINED +#define SkOTTable_EBSC_DEFINED + +#include "SkEndian.h" +#include "SkOTTable_EBLC.h" +#include "SkOTTableTypes.h" + +#pragma pack(push, 1) + +struct SkOTTableEmbeddedBitmapScaling { + static const SK_OT_CHAR TAG0 = 'E'; + static const SK_OT_CHAR TAG1 = 'S'; + static const SK_OT_CHAR TAG2 = 'B'; + static const SK_OT_CHAR TAG3 = 'C'; + static const SK_OT_ULONG TAG = SkOTTableTAG::value; + + SK_OT_Fixed version; + static const SK_OT_Fixed version_initial = SkTEndian_SwapBE32(0x00020000); + + SK_OT_ULONG numSizes; + + struct BitmapScaleTable { + SkOTTableEmbeddedBitmapLocation::SbitLineMetrics hori; + SkOTTableEmbeddedBitmapLocation::SbitLineMetrics vert; + SK_OT_BYTE ppemX; //target horizontal pixels per EM + SK_OT_BYTE ppemY; //target vertical pixels per EM + SK_OT_BYTE substitutePpemX; //use bitmaps of this size + SK_OT_BYTE substitutePpemY; //use bitmaps of this size + }; //bitmapScaleTable[numSizes]; +}; + +#pragma pack(pop) + +#endif