Layout.cpp revision 9cc9bbe1461f359f0b27c5e7645c17dda001ab1d
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <string>
18#include <vector>
19#include <fstream>
20#include <iostream>  // for debugging
21
22#include <minikin/Layout.h>
23
24using std::string;
25using std::vector;
26
27namespace android {
28
29// TODO: globals are not cool, move to a factory-ish object
30hb_buffer_t* buffer = 0;
31
32Bitmap::Bitmap(int width, int height) : width(width), height(height) {
33    buf = new uint8_t[width * height]();
34}
35
36Bitmap::~Bitmap() {
37    delete[] buf;
38}
39
40void Bitmap::writePnm(std::ofstream &o) const {
41    o << "P5" << std::endl;
42    o << width << " " << height << std::endl;
43    o << "255" << std::endl;
44    o.write((const char *)buf, width * height);
45    o.close();
46}
47
48void Bitmap::drawGlyph(const FT_Bitmap& bitmap, int x, int y) {
49    int bmw = bitmap.width;
50    int bmh = bitmap.rows;
51    int x0 = std::max(0, x);
52    int x1 = std::min(width, x + bmw);
53    int y0 = std::max(0, y);
54    int y1 = std::min(height, y + bmh);
55    const unsigned char* src = bitmap.buffer + (y0 - y) * bmw + (x0 - x);
56    uint8_t* dst = buf + y0 * width;
57    for (int yy = y0; yy < y1; yy++) {
58        for (int xx = x0; xx < x1; xx++) {
59            int pixel = (int)dst[xx] + (int)src[xx - x];
60            pixel = pixel > 0xff ? 0xff : pixel;
61            dst[xx] = pixel;
62        }
63        src += bmw;
64        dst += width;
65    }
66}
67
68void Layout::init() {
69    buffer = hb_buffer_create();
70}
71
72void Layout::setFontCollection(const FontCollection *collection) {
73    mCollection = collection;
74}
75
76hb_blob_t* referenceTable(hb_face_t* face, hb_tag_t tag, void* userData)  {
77    FT_Face ftFace = reinterpret_cast<FT_Face>(userData);
78    FT_ULong length = 0;
79    FT_Error error = FT_Load_Sfnt_Table(ftFace, tag, 0, NULL, &length);
80    if (error) {
81        return 0;
82    }
83    char *buffer = reinterpret_cast<char*>(malloc(length));
84    if (!buffer) {
85        return 0;
86    }
87    error = FT_Load_Sfnt_Table(ftFace, tag, 0,
88        reinterpret_cast<FT_Byte*>(buffer), &length);
89    if (error) {
90        free(buffer);
91        return 0;
92    }
93    return hb_blob_create(const_cast<char*>(buffer), length,
94        HB_MEMORY_MODE_WRITABLE, buffer, free);
95}
96
97static hb_bool_t harfbuzzGetGlyph(hb_font_t* hbFont, void* fontData, hb_codepoint_t unicode, hb_codepoint_t variationSelector, hb_codepoint_t* glyph, void* userData)
98{
99    FT_Face ftFace = reinterpret_cast<FT_Face>(fontData);
100    FT_UInt glyph_index = FT_Get_Char_Index(ftFace, unicode);
101    *glyph = glyph_index;
102    return !!*glyph;
103}
104
105static hb_position_t ft_pos_to_hb(FT_Pos pos) {
106    return pos << 2;
107}
108
109static hb_position_t harfbuzzGetGlyphHorizontalAdvance(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, void* userData)
110{
111    FT_Face ftFace = reinterpret_cast<FT_Face>(fontData);
112    hb_position_t advance = 0;
113
114    FT_Load_Glyph(ftFace, glyph, FT_LOAD_DEFAULT);
115    return ft_pos_to_hb(ftFace->glyph->advance.x);
116}
117
118static hb_bool_t harfbuzzGetGlyphHorizontalOrigin(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, hb_position_t* x, hb_position_t* y, void* userData)
119{
120    // Just return true, following the way that Harfbuzz-FreeType
121    // implementation does.
122    return true;
123}
124
125hb_font_funcs_t* getHbFontFuncs() {
126    static hb_font_funcs_t* hbFontFuncs = 0;
127
128    if (hbFontFuncs == 0) {
129        hbFontFuncs = hb_font_funcs_create();
130        hb_font_funcs_set_glyph_func(hbFontFuncs, harfbuzzGetGlyph, 0, 0);
131        hb_font_funcs_set_glyph_h_advance_func(hbFontFuncs, harfbuzzGetGlyphHorizontalAdvance, 0, 0);
132        hb_font_funcs_set_glyph_h_origin_func(hbFontFuncs, harfbuzzGetGlyphHorizontalOrigin, 0, 0);
133        hb_font_funcs_make_immutable(hbFontFuncs);
134    }
135    return hbFontFuncs;
136}
137
138hb_font_t* create_hb_font(FT_Face ftFace) {
139    hb_face_t* face = hb_face_create_for_tables(referenceTable, ftFace, NULL);
140    hb_font_t* font = hb_font_create(face);
141    hb_font_set_funcs(font, getHbFontFuncs(), ftFace, 0);
142    // TODO: manage ownership of face
143    return font;
144}
145
146static float HBFixedToFloat(hb_position_t v)
147{
148    return scalbnf (v, -8);
149}
150
151static hb_position_t HBFloatToFixed(float v)
152{
153    return scalbnf (v, +8);
154}
155
156void Layout::dump() const {
157    for (size_t i = 0; i < mGlyphs.size(); i++) {
158        const LayoutGlyph& glyph = mGlyphs[i];
159        std::cout << glyph.glyph_id << ": " << glyph.x << ", " << glyph.y << std::endl;
160    }
161}
162
163// A couple of things probably need to change:
164// 1. Deal with multiple sizes in a layout
165// 2. We'll probably store FT_Face as primary and then use a cache
166// for the hb fonts
167int Layout::findFace(FT_Face face) {
168    unsigned int ix;
169    for (ix = 0; ix < mFaces.size(); ix++) {
170        if (mFaces[ix] == face) {
171            return ix;
172        }
173    }
174    double size = mProps.value(fontSize).getFloatValue();
175    FT_Error error = FT_Set_Pixel_Sizes(face, 0, size);
176    mFaces.push_back(face);
177    hb_font_t *font = create_hb_font(face);
178    hb_font_set_ppem(font, size, size);
179    hb_font_set_scale(font, HBFloatToFixed(size), HBFloatToFixed(size));
180    mHbFonts.push_back(font);
181    return ix;
182}
183
184static FontStyle styleFromCss(const CssProperties &props) {
185    int weight = 4;
186    if (props.hasTag(fontWeight)) {
187        weight = props.value(fontWeight).getIntValue() / 100;
188    }
189    bool italic = false;
190    if (props.hasTag(fontStyle)) {
191        italic = props.value(fontStyle).getIntValue() != 0;
192    }
193    // TODO: italic property from CSS
194    return FontStyle(weight, italic);
195}
196
197// TODO: API should probably take context
198void Layout::doLayout(const uint16_t* buf, size_t nchars) {
199    FT_Error error;
200
201    vector<FontCollection::Run> items;
202    FontStyle style = styleFromCss(mProps);
203    mCollection->itemize(buf, nchars, style, &items);
204
205    mGlyphs.clear();
206    mFaces.clear();
207    mHbFonts.clear();
208    float x = 0;
209    float y = 0;
210    for (size_t run_ix = 0; run_ix < items.size(); run_ix++) {
211        FontCollection::Run &run = items[run_ix];
212        int font_ix = findFace(run.font);
213        hb_font_t *hbFont = mHbFonts[font_ix];
214#ifdef VERBOSE
215        std::cout << "Run " << run_ix << ", font " << font_ix <<
216            " [" << run.start << ":" << run.end << "]" << std::endl;
217#endif
218
219        hb_buffer_reset(buffer);
220        hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
221        hb_buffer_add_utf16(buffer, buf, nchars, run.start, run.end - run.start);
222        hb_shape(hbFont, buffer, NULL, 0);
223        unsigned int numGlyphs;
224        hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &numGlyphs);
225        hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(buffer, NULL);
226        for (unsigned int i = 0; i < numGlyphs; i++) {
227#ifdef VERBOSE
228            std::cout << positions[i].x_advance << " " << positions[i].y_advance << " " << positions[i].x_offset << " " << positions[i].y_offset << std::endl;            std::cout << "DoLayout " << info[i].codepoint <<
229            ": " << HBFixedToFloat(positions[i].x_advance) << "; " << positions[i].x_offset << ", " << positions[i].y_offset << std::endl;
230#endif
231            hb_codepoint_t glyph_ix = info[i].codepoint;
232            float xoff = HBFixedToFloat(positions[i].x_offset);
233            float yoff = HBFixedToFloat(positions[i].y_offset);
234            LayoutGlyph glyph = {font_ix, glyph_ix, x + xoff, y + yoff};
235            mGlyphs.push_back(glyph);
236            x += HBFixedToFloat(positions[i].x_advance);
237        }
238    }
239}
240
241void Layout::draw(Bitmap* surface, int x0, int y0) const {
242    FT_Error error;
243    FT_Int32 load_flags = FT_LOAD_DEFAULT;
244    if (mProps.hasTag(minikinHinting)) {
245        int hintflags = mProps.value(minikinHinting).getIntValue();
246        if (hintflags & 1) load_flags |= FT_LOAD_NO_HINTING;
247        if (hintflags & 2) load_flags |= FT_LOAD_NO_AUTOHINT;
248    }
249    for (size_t i = 0; i < mGlyphs.size(); i++) {
250        const LayoutGlyph& glyph = mGlyphs[i];
251        FT_Face face = mFaces[glyph.font_ix];
252        error = FT_Load_Glyph(face, glyph.glyph_id, load_flags);
253        error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
254        surface->drawGlyph(face->glyph->bitmap,
255            x0 + int(floor(glyph.x + 0.5)) + face->glyph->bitmap_left,
256            y0 + int(floor(glyph.y + 0.5)) - face->glyph->bitmap_top);
257    }
258}
259
260void Layout::setProperties(string css) {
261    mProps.parse(css);
262}
263
264}  // namespace android
265