Make automated FSCommand invocation tests show player-side output.
[gnash.git] / libcore / FreetypeGlyphsProvider.cpp
blob63c002fa860ea1b029c5e4e826ecee5deae05d46
1 // FreetypeGlyphsProvider.cpp: Freetype glyphs manager
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 // Free Software Foundation, Inc
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 #ifdef HAVE_CONFIG_H
21 #include "gnashconfig.h"
22 #endif
24 #include "FreetypeGlyphsProvider.h"
26 #include <string>
27 #include <memory> // for unique_ptr
28 #include <cstdint>
29 #include <boost/format.hpp>
31 #include "GnashException.h"
32 #include "ShapeRecord.h"
33 #include "log.h"
34 #include "FillStyle.h"
36 #ifdef USE_FREETYPE
37 # include <ft2build.h>
38 # include FT_OUTLINE_H
39 # include FT_BBOX_H
40 // Methods of FT_Outline_Funcs take a 'const FT_Vector*' in 2.2
41 // and a non-const one in 2.1, so we use an FT_CONST macro to
42 // support both
43 # if FREETYPE_MAJOR == 2 && FREETYPE_MINOR < 2
44 # define FT_CONST
45 # else
46 # define FT_CONST const
47 # endif
48 #endif
50 #ifdef HAVE_FONTCONFIG_FONTCONFIG_H
51 # define HAVE_FONTCONFIG 1
52 #endif
54 #ifdef HAVE_FONTCONFIG
55 # include <fontconfig/fontconfig.h>
56 # include <fontconfig/fcfreetype.h>
57 #endif
59 #ifdef HAIKU_HOST
60 # include <Path.h>
61 # include <FindDirectory.h>
62 #endif
64 // Define the following to make outline decomposition verbose
65 //#define DEBUG_OUTLINE_DECOMPOSITION 1
67 // Define the following to make device font handling verbose
68 //#define GNASH_DEBUG_DEVICEFONTS 1
70 namespace gnash {
72 #ifdef USE_FREETYPE
74 /// Outline glyph walker/decomposer, for drawing an FT_Outline to DynamicShape
76 /// See FT_Outline_Decompose function of freetype2 lib
77 ///
78 class OutlineWalker
81 public:
83 /// Create an outline walker drawing to the given DynamiShape
85 /// @param sh
86 /// The DynamicShape to draw to. Externally owned.
87 ///
88 /// @param scale
89 /// The scale to apply to coordinates.
90 /// This is to match an arbitrary EM
91 ///
92 OutlineWalker(SWF::ShapeRecord& sh, float scale)
94 _shape(sh),
95 _scale(scale),
96 _currPath(nullptr),
97 _x(0),
98 _y(0)
100 /// A default fill style is solid white.
101 FillStyle f = SolidFill(rgba());
102 _subshape.addFillStyle(f);
103 _subshape.addPath(Path(_x, _y, 1, 0, 0));
104 _currPath = &_subshape.currentPath();
107 void finish()
109 _currPath->close();
110 _shape.addSubshape(_subshape);
113 ~OutlineWalker() {}
115 /// Callback function for the move_to member of FT_Outline_Funcs
116 static int
117 walkMoveTo(FT_CONST FT_Vector* to, void* ptr)
119 OutlineWalker* walker = static_cast<OutlineWalker*>(ptr);
120 return walker->moveTo(to);
123 /// Callback function for the line_to member of FT_Outline_Funcs
124 static int
125 walkLineTo(FT_CONST FT_Vector* to, void* ptr)
127 OutlineWalker* walker = static_cast<OutlineWalker*>(ptr);
128 return walker->lineTo(to);
131 /// Callback function for the conic_to member of FT_Outline_Funcs
132 static int
133 walkConicTo(FT_CONST FT_Vector* ctrl, FT_CONST FT_Vector* to, void* ptr)
135 OutlineWalker* walker = static_cast<OutlineWalker*>(ptr);
136 return walker->conicTo(ctrl, to);
139 /// Callback function for the cubic_to member of FT_Outline_Funcs
141 /// Transform the cubic curve into a quadratic one an interpolated point
142 /// falling in the middle of the two control points.
144 static int
145 walkCubicTo(FT_CONST FT_Vector* ctrl1, FT_CONST FT_Vector* ctrl2,
146 FT_CONST FT_Vector* to, void* ptr)
148 OutlineWalker* walker = static_cast<OutlineWalker*>(ptr);
149 return walker->cubicTo(ctrl1, ctrl2, to);
152 private:
154 int moveTo(const FT_Vector* to)
156 #ifdef DEBUG_OUTLINE_DECOMPOSITION
157 log_debug("moveTo: %ld,%ld", to->x, to->y);
158 #endif
159 _x = static_cast<std::int32_t>(to->x * _scale);
160 _y = - static_cast<std::int32_t>(to->y * _scale);
161 _currPath->close();
162 _subshape.addPath(Path(_x, _y, 1, 0, 0));
163 _currPath = &_subshape.currentPath();
164 return 0;
167 int lineTo(const FT_Vector* to)
169 #ifdef DEBUG_OUTLINE_DECOMPOSITION
170 log_debug("lineTo: %ld,%ld", to->x, to->y);
171 #endif
172 _x = static_cast<std::int32_t>(to->x * _scale);
173 _y = - static_cast<std::int32_t>(to->y * _scale);
174 _currPath->drawLineTo(_x, _y);
175 expandBounds(_x, _y);
176 return 0;
179 int conicTo(const FT_Vector* ctrl, const FT_Vector* to)
181 #ifdef DEBUG_OUTLINE_DECOMPOSITION
182 log_debug("conicTo: %ld,%ld %ld,%ld", ctrl->x, ctrl->y, to->x, to->y);
183 #endif
184 std::int32_t x1 = static_cast<std::int32_t>(ctrl->x * _scale);
185 std::int32_t y1 = static_cast<std::int32_t>(ctrl->y * _scale);
186 _x = static_cast<std::int32_t>(to->x * _scale);
187 _y = - static_cast<std::int32_t>(to->y * _scale);
188 _currPath->drawCurveTo(x1, -y1, _x, _y);
189 expandBounds(x1, -y1, _x, _y);
190 return 0;
194 cubicTo(const FT_Vector* ctrl1, const FT_Vector* ctrl2, const FT_Vector* to)
196 #ifdef DEBUG_OUTLINE_DECOMPOSITION
197 log_debug("cubicTo: %ld,%ld %ld,%ld %ld,%ld", ctrl1->x,
198 ctrl1->y, ctrl2->x, ctrl2->y, to->x, to->y);
199 #endif
200 float x = ctrl1->x + ( (ctrl2->x - ctrl1->x) * 0.5 );
201 float y = ctrl1->y + ( (ctrl2->y - ctrl1->y) * 0.5 );
202 std::int32_t x1 = static_cast<std::int32_t>(x * _scale);
203 std::int32_t y1 = static_cast<std::int32_t>(y * _scale);
204 _x = static_cast<std::int32_t>(to->x * _scale);
205 _y = - static_cast<std::int32_t>(to->y * _scale);
207 _currPath->drawCurveTo(x1, -y1, _x, _y);
208 expandBounds(x1, -y1, _x, _y);
209 return 0;
212 void expandBounds(int x, int y) {
213 SWFRect bounds = _shape.getBounds();
214 if (_currPath->size() == 1) _currPath->expandBounds(bounds, 0, 6);
215 else {
216 bounds.expand_to_circle(x, y, 0);
218 _shape.setBounds(bounds);
221 void expandBounds(int ax, int ay, int cx, int cy) {
222 SWFRect bounds = _shape.getBounds();
223 if (_currPath->size() == 1) _currPath->expandBounds(bounds, 0, 6);
224 else {
225 bounds.expand_to_circle(ax, ay, 0);
226 bounds.expand_to_circle(cx, cy, 0);
228 _shape.setBounds(bounds);
231 SWF::Subshape _subshape;
232 SWF::ShapeRecord& _shape;
234 const float _scale;
236 Path* _currPath;
238 std::int32_t _x, _y;
242 // statics
243 FT_Library FreetypeGlyphsProvider::m_lib = nullptr;
244 std::mutex FreetypeGlyphsProvider::m_lib_mutex;
246 // static private
247 void
248 FreetypeGlyphsProvider::init()
250 std::lock_guard<std::mutex> lock(m_lib_mutex);
252 if (m_lib) return;
254 const int error = FT_Init_FreeType(&m_lib);
255 if (error) {
256 boost::format err = boost::format(_("Can't init FreeType! Error "
257 "= %d")) % error;
258 throw GnashException(err.str());
262 // static private
263 void
264 FreetypeGlyphsProvider::close()
266 const int error = FT_Done_FreeType(m_lib);
267 if (error) {
268 log_error(_("Can't close FreeType! Error = %d"), error);
272 // private
273 bool
274 FreetypeGlyphsProvider::getFontFilename(const std::string &name,
275 bool bold, bool italic, std::string& filename)
278 #ifdef HAIKU_HOST
279 // only returns a default
280 BPath bp;
281 if (B_OK != find_directory(B_BEOS_FONTS_DIRECTORY, &bp)) {
282 log_error(_("Failed to find fonts directory, using hard-coded "
283 "font filename \"%s\""), DEFAULT_FONTFILE);
284 filename = DEFAULT_FONTFILE;
285 return true;
288 bp.Append("ttfonts/DejaVuSans.ttf");
289 filename = bp.Path();
290 return true;
291 #endif
293 #ifdef HAVE_FONTCONFIG
294 if (!FcInit ()) {
295 log_error(_("Can't init fontconfig library, using hard-"
296 "coded font filename \"%s\""), DEFAULT_FONTFILE);
297 filename = DEFAULT_FONTFILE;
298 return true;
299 //return false;
302 FcResult result;
304 FcPattern* pat = FcNameParse((const FcChar8*)name.c_str());
306 FcConfigSubstitute (nullptr, pat, FcMatchPattern);
308 if (italic) {
309 FcPatternAddInteger (pat, FC_SLANT, FC_SLANT_ITALIC);
312 if (bold) {
313 FcPatternAddInteger (pat, FC_WEIGHT, FC_WEIGHT_BOLD);
316 FcDefaultSubstitute (pat);
318 FcPattern *match;
319 match = FcFontMatch (nullptr, pat, &result);
320 FcPatternDestroy (pat);
322 FcFontSet* fs = nullptr;
323 if (match) {
324 fs = FcFontSetCreate ();
325 FcFontSetAdd (fs, match);
328 if ( fs ) {
329 #ifdef GNASH_DEBUG_DEVICEFONTS
330 log_debug("Found %d fonts matching the family %s (using "
331 "first)", fs->nfont, name);
332 #endif
334 for (int j = 0; j < fs->nfont; j++) {
335 FcChar8 *file;
336 if (FcPatternGetString (fs->fonts[j], FC_FILE, 0, &file) != FcResultMatch) {
337 #ifdef GNASH_DEBUG_DEVICEFONTS
338 log_debug("Matching font %d has unknown filename, skipping",
340 #endif
341 continue;
344 filename = (char *)file;
345 FcFontSetDestroy(fs);
347 #ifdef GNASH_DEBUG_DEVICEFONTS
348 log_debug("Loading font from file %d", filename);
349 #endif
350 return true;
354 FcFontSetDestroy(fs);
357 log_error(_("No device font matches the name '%s', using hard-coded"
358 " font filename"), name);
359 filename = DEFAULT_FONTFILE;
360 return true;
361 #else
362 log_error(_("Font filename matching not implemented (no fontconfig"
363 " support built-in), using hard-coded font filename"),
364 DEFAULT_FONTFILE);
365 filename = DEFAULT_FONTFILE;
366 return true;
367 #endif
370 #endif // USE_FREETYPE
372 #ifdef USE_FREETYPE
373 // static
374 std::unique_ptr<FreetypeGlyphsProvider>
375 FreetypeGlyphsProvider::createFace(const std::string& name, bool bold, bool italic)
378 std::unique_ptr<FreetypeGlyphsProvider> ret;
380 try {
381 ret.reset(new FreetypeGlyphsProvider(name, bold, italic));
383 catch (const GnashException& ge) {
384 log_error(ge.what());
385 assert(! ret.get());
388 return ret;
391 #else // ndef USE_FREETYPE
392 std::unique_ptr<FreetypeGlyphsProvider>
393 FreetypeGlyphsProvider::createFace(const std::string&, bool, bool)
395 log_error(_("Freetype not supported"));
396 return std::unique_ptr<FreetypeGlyphsProvider>(NULL);
398 #endif
400 unsigned short
401 FreetypeGlyphsProvider::unitsPerEM() const
403 assert(_face);
404 return _face->units_per_EM;
407 float
408 FreetypeGlyphsProvider::descent() const
410 assert(_face);
411 return std::abs(_face->descender);
414 float
415 FreetypeGlyphsProvider::ascent() const
417 assert(_face);
418 return _face->ascender;
421 #ifdef USE_FREETYPE
422 FreetypeGlyphsProvider::FreetypeGlyphsProvider(const std::string& name,
423 bool bold, bool italic)
425 _face(nullptr)
428 if (m_lib == nullptr)
430 init();
433 std::string filename;
434 if (getFontFilename(name, bold, italic, filename) == false)
436 boost::format msg = boost::format(_("Can't find font file "
437 "for font '%s'")) % name;
438 throw GnashException(msg.str());
441 int error = FT_New_Face(m_lib, filename.c_str(), 0, &_face);
442 switch (error)
444 case 0:
445 break;
447 case FT_Err_Unknown_File_Format:
449 boost::format msg = boost::format(_("Font file '%s' "
450 "has bad format")) % filename;
451 throw GnashException(msg.str());
452 break;
455 default:
457 // TODO: return a better error message !
458 boost::format msg = boost::format(_("Some error "
459 "opening font '%s'"))
460 % filename;
461 throw GnashException(msg.str());
462 break;
466 // We want an EM of unitsPerEM, so if units_per_EM is different
467 // we will scale
468 scale = (float)unitsPerEM()/_face->units_per_EM;
470 #ifdef GNASH_DEBUG_DEVICEFONTS
471 log_debug("EM square for font '%s' is %d, scale is this %g",
472 name, _face->units_per_EM, scale);
473 #endif
475 #else // ndef(USE_FREETYPE)
476 FreetypeGlyphsProvider::FreetypeGlyphsProvider(const std::string&, bool, bool)
478 abort(); // should never be called
480 #endif // ndef USE_FREETYPE
482 #ifdef USE_FREETYPE
483 std::unique_ptr<SWF::ShapeRecord>
484 FreetypeGlyphsProvider::getGlyph(std::uint16_t code, float& advance)
486 std::unique_ptr<SWF::ShapeRecord> glyph;
488 FT_Error error = FT_Load_Char(_face, code, FT_LOAD_NO_BITMAP |
489 FT_LOAD_NO_SCALE);
491 if (error) {
492 log_error(_("Error loading freetype outline glyph for char '%c' "
493 "(error: %d)"), code, error);
494 return glyph;
497 // Scale advance by current scale, to match expected output coordinate space
498 advance = _face->glyph->metrics.horiAdvance * scale;
499 #ifdef GNASH_DEBUG_DEVICEFONTS
500 log_debug("Advance value for glyph '%c' is %g (horiAdvance:%ld, "
501 "scale:%g)", code, advance,
502 _face->glyph->metrics.horiAdvance, scale);
503 #endif
505 if ( _face->glyph->format != FT_GLYPH_FORMAT_OUTLINE )
507 unsigned long gf = _face->glyph->format;
508 log_unimpl(_("FT_Load_Char() returned a glyph format != "
509 "FT_GLYPH_FORMAT_OUTLINE (%c%c%c%c)"),
510 static_cast<char>((gf>>24)&0xff),
511 static_cast<char>((gf>>16)&0xff),
512 static_cast<char>((gf>>8)&0xff),
513 static_cast<char>(gf&0xff));
514 return glyph;
517 FT_Outline* outline = &(_face->glyph->outline);
519 FT_Outline_Funcs walk;
520 walk.move_to = OutlineWalker::walkMoveTo;
521 walk.line_to = OutlineWalker::walkLineTo;
522 walk.conic_to = OutlineWalker::walkConicTo;
523 walk.cubic_to = OutlineWalker::walkCubicTo;
524 walk.shift = 0; // ?
525 walk.delta = 0; // ?
527 #ifdef DEBUG_OUTLINE_DECOMPOSITION
528 log_debug("Decomposing glyph outline for DisplayObject %u", code);
529 #endif
531 glyph.reset(new SWF::ShapeRecord);
533 OutlineWalker walker(*glyph, scale);
535 FT_Outline_Decompose(outline, &walk, &walker);
536 #ifdef DEBUG_OUTLINE_DECOMPOSITION
537 #if 0
538 // Don't use VM::get if this is to be re-enabled.
539 SWFRect bound; sh->compute_bound(&bound, VM::get().getSWFVersion());
540 log_debug("Decomposed glyph for DisplayObject '%c' has bounds %s",
541 code, bound.toString());
542 #endif
543 #endif
545 walker.finish();
547 return glyph;
549 #else // ndef(USE_FREETYPE)
551 std::unique_ptr<SWF::ShapeRecord>
552 FreetypeGlyphsProvider::getGlyph(std::uint16_t, float& advance)
554 abort(); // should never be called...
556 #endif
558 FreetypeGlyphsProvider::~FreetypeGlyphsProvider()
560 #ifdef USE_FREETYPE
561 if (_face) {
562 if (FT_Done_Face(_face) != 0) {
563 log_error(_("Could not release FT face resources"));
566 #endif
569 } // namespace gnash