diff options
author | Raph Levien <raph@google.com> | 2013-05-22 16:14:27 -0700 |
---|---|---|
committer | Raph Levien <raph@google.com> | 2013-06-14 11:22:35 -0700 |
commit | bcc3dc5a2591a95a57e379e27cbad69c18e91e67 (patch) | |
tree | 6ba40e86f2ef0d52b2e0796f1dda0eb6e5ec4377 /libs | |
parent | 9cc9bbe1461f359f0b27c5e7645c17dda001ab1d (diff) | |
download | android_frameworks_minikin-bcc3dc5a2591a95a57e379e27cbad69c18e91e67.tar.gz android_frameworks_minikin-bcc3dc5a2591a95a57e379e27cbad69c18e91e67.tar.bz2 android_frameworks_minikin-bcc3dc5a2591a95a57e379e27cbad69c18e91e67.zip |
Introduce MinikinFont abstraction
This commit removes the direct dependency on FreeType and replaces it
with a MinikinFont abstraction, which is designed to support both
FreeType and Skia fonts (and possibly others in the future).
Also adds a "total advance" to the Layout, with an API for retrieving
it.
Change-Id: If20f92db9a43fd15b0fe9794b761ba00fb21338c
Diffstat (limited to 'libs')
-rw-r--r-- | libs/minikin/Android.mk | 1 | ||||
-rw-r--r-- | libs/minikin/FontCollection.cpp | 13 | ||||
-rw-r--r-- | libs/minikin/FontFamily.cpp | 24 | ||||
-rw-r--r-- | libs/minikin/Layout.cpp | 100 | ||||
-rw-r--r-- | libs/minikin/MinikinFontFreeType.cpp | 95 |
5 files changed, 172 insertions, 61 deletions
diff --git a/libs/minikin/Android.mk b/libs/minikin/Android.mk index 9795ad0..723ad1f 100644 --- a/libs/minikin/Android.mk +++ b/libs/minikin/Android.mk @@ -24,6 +24,7 @@ LOCAL_SRC_FILES := \ FontCollection.cpp \ FontFamily.cpp \ Layout.cpp \ + MinikinFontFreeType.cpp \ SparseBitSet.cpp LOCAL_MODULE := libminikin diff --git a/libs/minikin/FontCollection.cpp b/libs/minikin/FontCollection.cpp index 7abbd3b..702bd20 100644 --- a/libs/minikin/FontCollection.cpp +++ b/libs/minikin/FontCollection.cpp @@ -45,16 +45,15 @@ FontCollection::FontCollection(const vector<FontFamily*>& typefaces) : FontInstance* instance = &mInstances.back(); instance->mFamily = family; instance->mCoverage = new SparseBitSet; - FT_Face typeface = family->getClosestMatch(defaultStyle); + MinikinFont* typeface = family->getClosestMatch(defaultStyle); #ifdef VERBOSE_DEBUG printf("closest match = %x, family size = %d\n", typeface, family->getNumFonts()); #endif - const uint32_t cmapTag = FT_MAKE_TAG('c', 'm', 'a', 'p'); - FT_ULong cmapSize = 0; - FT_Error error = FT_Load_Sfnt_Table(typeface, cmapTag, 0, NULL, &cmapSize); + const uint32_t cmapTag = MinikinFont::MakeTag('c', 'm', 'a', 'p'); + size_t cmapSize = 0; + bool ok = typeface->GetTable(cmapTag, NULL, &cmapSize); UniquePtr<uint8_t[]> cmapData(new uint8_t[cmapSize]); - error = FT_Load_Sfnt_Table(typeface, cmapTag, 0, - cmapData.get(), &cmapSize); + ok = typeface->GetTable(cmapTag, cmapData.get(), &cmapSize); CmapCoverage::getCoverage(*instance->mCoverage, cmapData.get(), cmapSize); #ifdef VERBOSE_DEBUG printf("font coverage length=%d, first ch=%x\n", instance->mCoverage->length(), @@ -138,7 +137,7 @@ void FontCollection::itemize(const uint16_t *string, size_t string_size, FontSty run->font = NULL; // maybe we should do something different here } else { run->font = family->getClosestMatch(style); - FT_Reference_Face(run->font); + run->font->Ref(); } lastFamily = family; run->start = i; diff --git a/libs/minikin/FontFamily.cpp b/libs/minikin/FontFamily.cpp index dc6e16c..0bc38a7 100644 --- a/libs/minikin/FontFamily.cpp +++ b/libs/minikin/FontFamily.cpp @@ -20,9 +20,7 @@ #include <stdlib.h> #include <stdint.h> #include <utils/UniquePtr.h> -#include <ft2build.h> -#include FT_FREETYPE_H -#include FT_TRUETYPE_TABLES_H +#include <minikin/MinikinFont.h> #include <minikin/AnalyzeStyle.h> #include <minikin/FontFamily.h> @@ -30,14 +28,14 @@ using std::vector; namespace android { -bool FontFamily::addFont(FT_Face typeface) { - const uint32_t os2Tag = FT_MAKE_TAG('O', 'S', '/', '2'); - FT_ULong os2Size = 0; - FT_Error error = FT_Load_Sfnt_Table(typeface, os2Tag, 0, NULL, &os2Size); - if (error != 0) return false; +bool FontFamily::addFont(MinikinFont* typeface) { + const uint32_t os2Tag = MinikinFont::MakeTag('O', 'S', '/', '2'); + size_t os2Size = 0; + bool ok = typeface->GetTable(os2Tag, NULL, &os2Size); + if (!ok) return false; UniquePtr<uint8_t[]> os2Data(new uint8_t[os2Size]); - error = FT_Load_Sfnt_Table(typeface, os2Tag, 0, os2Data.get(), &os2Size); - if (error != 0) return false; + ok = typeface->GetTable(os2Tag, os2Data.get(), &os2Size); + if (!ok) return false; int weight; bool italic; if (analyzeStyle(os2Data.get(), os2Size, &weight, &italic)) { @@ -51,7 +49,7 @@ bool FontFamily::addFont(FT_Face typeface) { return false; } -void FontFamily::addFont(FT_Face typeface, FontStyle style) { +void FontFamily::addFont(MinikinFont* typeface, FontStyle style) { mFonts.push_back(Font(typeface, style)); ALOGD("added font, mFonts.size() = %d", mFonts.size()); } @@ -66,7 +64,7 @@ int computeMatch(FontStyle style1, FontStyle style2) { return score; } -FT_Face FontFamily::getClosestMatch(FontStyle style) const { +MinikinFont* FontFamily::getClosestMatch(FontStyle style) const { const Font* bestFont = NULL; int bestMatch = 0; for (size_t i = 0; i < mFonts.size(); i++) { @@ -84,7 +82,7 @@ size_t FontFamily::getNumFonts() const { return mFonts.size(); } -FT_Face FontFamily::getFont(size_t index) const { +MinikinFont* FontFamily::getFont(size_t index) const { return mFonts[index].typeface; } diff --git a/libs/minikin/Layout.cpp b/libs/minikin/Layout.cpp index a8a596d..d4e09c5 100644 --- a/libs/minikin/Layout.cpp +++ b/libs/minikin/Layout.cpp @@ -18,7 +18,9 @@ #include <vector> #include <fstream> #include <iostream> // for debugging +#include <stdio.h> // ditto +#include <minikin/MinikinFontFreeType.h> #include <minikin/Layout.h> using std::string; @@ -45,9 +47,11 @@ void Bitmap::writePnm(std::ofstream &o) const { o.close(); } -void Bitmap::drawGlyph(const FT_Bitmap& bitmap, int x, int y) { +void Bitmap::drawGlyph(const GlyphBitmap& bitmap, int x, int y) { int bmw = bitmap.width; - int bmh = bitmap.rows; + int bmh = bitmap.height; + x += bitmap.left; + y -= bitmap.top; int x0 = std::max(0, x); int x1 = std::min(width, x + bmw); int y0 = std::max(0, y); @@ -74,19 +78,20 @@ void Layout::setFontCollection(const FontCollection *collection) { } hb_blob_t* referenceTable(hb_face_t* face, hb_tag_t tag, void* userData) { - FT_Face ftFace = reinterpret_cast<FT_Face>(userData); - FT_ULong length = 0; - FT_Error error = FT_Load_Sfnt_Table(ftFace, tag, 0, NULL, &length); - if (error) { + MinikinFont* font = reinterpret_cast<MinikinFont *>(userData); + size_t length = 0; + bool ok = font->GetTable(tag, NULL, &length); + if (!ok) { return 0; } char *buffer = reinterpret_cast<char*>(malloc(length)); if (!buffer) { return 0; } - error = FT_Load_Sfnt_Table(ftFace, tag, 0, - reinterpret_cast<FT_Byte*>(buffer), &length); - if (error) { + ok = font->GetTable(tag, reinterpret_cast<uint8_t*>(buffer), &length); + printf("referenceTable %c%c%c%c length=%d %d\n", + (tag >>24) & 0xff, (tag>>16)&0xff, (tag>>8)&0xff, tag&0xff, length, ok); + if (!ok) { free(buffer); return 0; } @@ -96,23 +101,22 @@ hb_blob_t* referenceTable(hb_face_t* face, hb_tag_t tag, void* userData) { static hb_bool_t harfbuzzGetGlyph(hb_font_t* hbFont, void* fontData, hb_codepoint_t unicode, hb_codepoint_t variationSelector, hb_codepoint_t* glyph, void* userData) { - FT_Face ftFace = reinterpret_cast<FT_Face>(fontData); - FT_UInt glyph_index = FT_Get_Char_Index(ftFace, unicode); - *glyph = glyph_index; - return !!*glyph; -} - -static hb_position_t ft_pos_to_hb(FT_Pos pos) { - return pos << 2; + MinikinPaint* paint = reinterpret_cast<MinikinPaint *>(fontData); + MinikinFont* font = paint->font; + uint32_t glyph_id; + bool ok = font->GetGlyph(unicode, &glyph_id); + if (ok) { + *glyph = glyph_id; + } + return ok; } static hb_position_t harfbuzzGetGlyphHorizontalAdvance(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, void* userData) { - FT_Face ftFace = reinterpret_cast<FT_Face>(fontData); - hb_position_t advance = 0; - - FT_Load_Glyph(ftFace, glyph, FT_LOAD_DEFAULT); - return ft_pos_to_hb(ftFace->glyph->advance.x); + MinikinPaint* paint = reinterpret_cast<MinikinPaint *>(fontData); + MinikinFont* font = paint->font; + float advance = font->GetHorizontalAdvance(glyph, *paint); + return 256 * advance + 0.5; } static hb_bool_t harfbuzzGetGlyphHorizontalOrigin(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, hb_position_t* x, hb_position_t* y, void* userData) @@ -135,10 +139,10 @@ hb_font_funcs_t* getHbFontFuncs() { return hbFontFuncs; } -hb_font_t* create_hb_font(FT_Face ftFace) { - hb_face_t* face = hb_face_create_for_tables(referenceTable, ftFace, NULL); +hb_font_t* create_hb_font(MinikinFont* minikinFont, MinikinPaint* minikinPaint) { + hb_face_t* face = hb_face_create_for_tables(referenceTable, minikinFont, NULL); hb_font_t* font = hb_font_create(face); - hb_font_set_funcs(font, getHbFontFuncs(), ftFace, 0); + hb_font_set_funcs(font, getHbFontFuncs(), minikinPaint, 0); // TODO: manage ownership of face return font; } @@ -164,19 +168,15 @@ void Layout::dump() const { // 1. Deal with multiple sizes in a layout // 2. We'll probably store FT_Face as primary and then use a cache // for the hb fonts -int Layout::findFace(FT_Face face) { +int Layout::findFace(MinikinFont* face, MinikinPaint* paint) { unsigned int ix; for (ix = 0; ix < mFaces.size(); ix++) { if (mFaces[ix] == face) { return ix; } } - double size = mProps.value(fontSize).getFloatValue(); - FT_Error error = FT_Set_Pixel_Sizes(face, 0, size); mFaces.push_back(face); - hb_font_t *font = create_hb_font(face); - hb_font_set_ppem(font, size, size); - hb_font_set_scale(font, HBFloatToFixed(size), HBFloatToFixed(size)); + hb_font_t *font = create_hb_font(face, paint); mHbFonts.push_back(font); return ix; } @@ -190,7 +190,6 @@ static FontStyle styleFromCss(const CssProperties &props) { if (props.hasTag(fontStyle)) { italic = props.value(fontStyle).getIntValue() != 0; } - // TODO: italic property from CSS return FontStyle(weight, italic); } @@ -202,6 +201,10 @@ void Layout::doLayout(const uint16_t* buf, size_t nchars) { FontStyle style = styleFromCss(mProps); mCollection->itemize(buf, nchars, style, &items); + MinikinPaint paint; + double size = mProps.value(fontSize).getFloatValue(); + paint.size = size; + mGlyphs.clear(); mFaces.clear(); mHbFonts.clear(); @@ -209,12 +212,15 @@ void Layout::doLayout(const uint16_t* buf, size_t nchars) { float y = 0; for (size_t run_ix = 0; run_ix < items.size(); run_ix++) { FontCollection::Run &run = items[run_ix]; - int font_ix = findFace(run.font); + int font_ix = findFace(run.font, &paint); + paint.font = mFaces[font_ix]; hb_font_t *hbFont = mHbFonts[font_ix]; #ifdef VERBOSE std::cout << "Run " << run_ix << ", font " << font_ix << " [" << run.start << ":" << run.end << "]" << std::endl; #endif + hb_font_set_ppem(hbFont, size, size); + hb_font_set_scale(hbFont, HBFloatToFixed(size), HBFloatToFixed(size)); hb_buffer_reset(buffer); hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); @@ -236,24 +242,32 @@ void Layout::doLayout(const uint16_t* buf, size_t nchars) { x += HBFixedToFloat(positions[i].x_advance); } } + mAdvance = x; } void Layout::draw(Bitmap* surface, int x0, int y0) const { - FT_Error error; - FT_Int32 load_flags = FT_LOAD_DEFAULT; + /* + TODO: redo as MinikinPaint settings if (mProps.hasTag(minikinHinting)) { int hintflags = mProps.value(minikinHinting).getIntValue(); if (hintflags & 1) load_flags |= FT_LOAD_NO_HINTING; if (hintflags & 2) load_flags |= FT_LOAD_NO_AUTOHINT; } + */ for (size_t i = 0; i < mGlyphs.size(); i++) { const LayoutGlyph& glyph = mGlyphs[i]; - FT_Face face = mFaces[glyph.font_ix]; - error = FT_Load_Glyph(face, glyph.glyph_id, load_flags); - error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); - surface->drawGlyph(face->glyph->bitmap, - x0 + int(floor(glyph.x + 0.5)) + face->glyph->bitmap_left, - y0 + int(floor(glyph.y + 0.5)) - face->glyph->bitmap_top); + MinikinFont *mf = mFaces[glyph.font_ix]; + MinikinFontFreeType *face = static_cast<MinikinFontFreeType *>(mf); + GlyphBitmap glyphBitmap; + MinikinPaint paint; + paint.size = mProps.value(fontSize).getFloatValue(); + bool ok = face->Render(glyph.glyph_id, paint, &glyphBitmap); + printf("glyphBitmap.width=%d, glyphBitmap.height=%d (%d, %d) x=%f, y=%f, ok=%d\n", + glyphBitmap.width, glyphBitmap.height, glyphBitmap.left, glyphBitmap.top, glyph.x, glyph.y, ok); + if (ok) { + surface->drawGlyph(glyphBitmap, + x0 + int(floor(glyph.x + 0.5)), y0 + int(floor(glyph.y + 0.5))); + } } } @@ -261,4 +275,8 @@ void Layout::setProperties(string css) { mProps.parse(css); } +float Layout::getAdvance() const { + return mAdvance; +} + } // namespace android diff --git a/libs/minikin/MinikinFontFreeType.cpp b/libs/minikin/MinikinFontFreeType.cpp new file mode 100644 index 0000000..be61345 --- /dev/null +++ b/libs/minikin/MinikinFontFreeType.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Implementation of MinikinFont abstraction specialized for FreeType + +#include <stdint.h> + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_TRUETYPE_TABLES_H +#include FT_ADVANCES_H + +#include <minikin/MinikinFontFreeType.h> + +namespace android { + +int32_t MinikinFontFreeType::sIdCounter = 0; + +MinikinFontFreeType::MinikinFontFreeType(FT_Face typeface) : + mTypeface(typeface) { + mUniqueId = sIdCounter++; +} + +MinikinFontFreeType::~MinikinFontFreeType() { + FT_Done_Face(mTypeface); +} + +bool MinikinFontFreeType::GetGlyph(uint32_t codepoint, uint32_t *glyph) const { + FT_UInt glyph_index = FT_Get_Char_Index(mTypeface, codepoint); + *glyph = glyph_index; + return !!glyph_index; +} + +float MinikinFontFreeType::GetHorizontalAdvance(uint32_t glyph_id, + const MinikinPaint &paint) const { + FT_Set_Pixel_Sizes(mTypeface, 0, paint.size); + FT_UInt32 flags = FT_LOAD_DEFAULT; // TODO: respect hinting settings + FT_Fixed advance; + FT_Error error = FT_Get_Advance(mTypeface, glyph_id, flags, &advance); + return advance * (1.0 / 65536); +} + +bool MinikinFontFreeType::GetTable(uint32_t tag, uint8_t *buf, size_t *size) { + FT_ULong ftsize = *size; + FT_Error error = FT_Load_Sfnt_Table(mTypeface, tag, 0, buf, &ftsize); + if (error != 0) { + return false; + } + *size = ftsize; + return true; +} + +int32_t MinikinFontFreeType::GetUniqueId() const { + return mUniqueId; +} + +bool MinikinFontFreeType::Render(uint32_t glyph_id, + const MinikinPaint &paint, GlyphBitmap *result) { + FT_Error error; + FT_Int32 load_flags = FT_LOAD_DEFAULT; // TODO: respect hinting settings + error = FT_Load_Glyph(mTypeface, glyph_id, load_flags); + if (error != 0) { + return false; + } + error = FT_Render_Glyph(mTypeface->glyph, FT_RENDER_MODE_NORMAL); + if (error != 0) { + return false; + } + FT_Bitmap &bitmap = mTypeface->glyph->bitmap; + result->buffer = bitmap.buffer; + result->width = bitmap.width; + result->height = bitmap.rows; + result->left = mTypeface->glyph->bitmap_left; + result->top = mTypeface->glyph->bitmap_top; + return true; +} + +MinikinFontFreeType* MinikinFontFreeType::GetFreeType() { + return this; +} + +} // namespace android |