From de745ed47350b3b7ed3e5a9d73578d1d7cc1946d Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 7 Apr 2017 13:42:43 +0200 Subject: [PATCH 1/8] readme: update with helpful notes about installing and wheels --- README.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c1cf59a4df..b87fd4ca10 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,23 @@ resources. If you get stuck, ask for help from our active Panda3D is licensed under the Modified BSD License. See the LICENSE file for more details. +Installing Panda3D +================== + +By far, the easiest way to install the latest development build of Panda3D +into an existing Python installation is using the following command: + +```bash +pip install --pre --extra-index-url https://archive.panda3d.org/ panda3d +``` + +If you prefer to install the full SDK with all tools, the latest development +builds can be obtained from this page: + +https://www.panda3d.org/download.php?sdk&version=devel + +These are automatically kept up-to-date with the latest GitHub version of Panda. + Building Panda3D ================ @@ -64,7 +81,7 @@ for you to install, depending on your distribution). The following command illustrates how to build Panda3D with some common options: ```bash -python2.7 makepanda/makepanda.py --everything --installer --no-egl --no-gles --no-gles2 +python makepanda/makepanda.py --everything --installer --no-egl --no-gles --no-gles2 --no-opencv ``` You will probably see some warnings saying that it's unable to find several @@ -93,6 +110,9 @@ may have to use the installpanda.py script instead, which will directly copy the files into the appropriate locations on your computer. You may have to run the `ldconfig` tool in order to update your library cache after installing Panda3D. +Alternatively, you can add the `--wheel` option, which will produce a .whl +file that can be installed into a Python installation using `pip`. + macOS ----- From 2669cc87104f502f02438217bdd5a34bfab8519c Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 7 Apr 2017 13:44:37 +0200 Subject: [PATCH 2/8] Remove unused macros --- dtool/src/dtoolbase/dtoolbase.h | 12 ------------ dtool/src/prc/pnotify.h | 9 --------- 2 files changed, 21 deletions(-) diff --git a/dtool/src/dtoolbase/dtoolbase.h b/dtool/src/dtoolbase/dtoolbase.h index 965069614e..ae74fd644f 100644 --- a/dtool/src/dtoolbase/dtoolbase.h +++ b/dtool/src/dtoolbase/dtoolbase.h @@ -58,18 +58,6 @@ #pragma warning (disable : 4267) /* C4577: 'noexcept' used with no exception handling mode specified */ #pragma warning (disable : 4577) - -#if _MSC_VER >= 1300 - #if _MSC_VER >= 1310 - #define USING_MSVC7_1 -// #pragma message("VC 7.1") - #else -// #pragma message("VC 7.0") - #endif -#define USING_MSVC7 -#else -// #pragma message("VC 6.0") -#endif #endif /* WIN32_VC */ #ifndef __has_builtin diff --git a/dtool/src/prc/pnotify.h b/dtool/src/prc/pnotify.h index 414b97d2a4..e51022c28a 100644 --- a/dtool/src/prc/pnotify.h +++ b/dtool/src/prc/pnotify.h @@ -152,8 +152,6 @@ private: #define nassert_raise(message) Notify::write_string(message) -#define enter_debugger_if(condition) ((void)0) - #else // NDEBUG #define nassertr(condition, return_value) \ @@ -183,13 +181,6 @@ private: #define nassert_raise(message) Notify::ptr()->assert_failure(message, __LINE__, __FILE__) -#define enter_debugger_if(condition) \ - if (_nassert_check(condition)) { \ - Notify::ptr()->assert_failure(#condition, __LINE__, __FILE__); \ - __asm { int 3 } \ - } - - #endif // NDEBUG #if __cplusplus >= 201103 From a175ad6a82b416cca997a0e1ec7a6d5953884bb5 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 7 Apr 2017 13:56:47 +0200 Subject: [PATCH 3/8] interrogate: fix compile error with default values that call scoped functions --- dtool/src/cppparser/cppExpression.cxx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dtool/src/cppparser/cppExpression.cxx b/dtool/src/cppparser/cppExpression.cxx index 0dff4f64b8..adbbaea14e 100644 --- a/dtool/src/cppparser/cppExpression.cxx +++ b/dtool/src/cppparser/cppExpression.cxx @@ -1658,7 +1658,12 @@ output(ostream &out, int indent_level, CPPScope *scope, bool) const { break; case T_function: - out << _u._fgroup->_name; + // Pick any instance; they all have the same name anyway. + if (!_u._fgroup->_instances.empty() && _u._fgroup->_instances[0]->_ident != NULL) { + _u._fgroup->_instances[0]->_ident->output(out, scope); + } else { + out << _u._fgroup->_name; + } break; case T_unknown_ident: From fa1b656961feb0b0323ea46898eed7c450178813 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 9 Apr 2017 22:14:20 +0200 Subject: [PATCH 4/8] glgsg: fix get_host() of GL FBO. Fixes LP 1672560 shadow bug --- panda/src/glstuff/glGraphicsBuffer_src.cxx | 10 ++++++++++ panda/src/glstuff/glGraphicsBuffer_src.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/panda/src/glstuff/glGraphicsBuffer_src.cxx b/panda/src/glstuff/glGraphicsBuffer_src.cxx index a4daaf3931..e76f8fccb9 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.cxx +++ b/panda/src/glstuff/glGraphicsBuffer_src.cxx @@ -1501,6 +1501,16 @@ open_buffer() { return true; } +/** + * This is normally called only from within make_texture_buffer(). When + * called on a ParasiteBuffer, it returns the host of that buffer; but when + * called on some other buffer, it returns the buffer itself. + */ +GraphicsOutput *CLP(GraphicsBuffer):: +get_host() { + return _host; +} + /** * Closes the buffer right now. Called from the window thread. */ diff --git a/panda/src/glstuff/glGraphicsBuffer_src.h b/panda/src/glstuff/glGraphicsBuffer_src.h index 9f99850a7f..d119148247 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.h +++ b/panda/src/glstuff/glGraphicsBuffer_src.h @@ -77,6 +77,8 @@ public: void unregister_shared_depth_buffer(GraphicsOutput *graphics_output); protected: + virtual GraphicsOutput *get_host(); + virtual void close_buffer(); virtual bool open_buffer(); From e67ebd80cd0d29dc18f4829907f7781c195d487f Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 14 Apr 2017 19:37:18 +0200 Subject: [PATCH 5/8] glgsg: deal with false negatives with feature checks on Intel --- panda/src/glstuff/glGraphicsStateGuardian_src.cxx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index b147c9182e..e1a250df9a 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -1139,7 +1139,7 @@ reset() { _supports_multisample = false; #else _supports_multisample = - has_extension("GL_ARB_multisample") || is_at_least_gl_version(1, 3); + is_at_least_gl_version(1, 3) || has_extension("GL_ARB_multisample"); #endif #ifdef OPENGLES_1 @@ -1305,7 +1305,7 @@ reset() { if (gl_support_shadow_filter && _supports_depth_texture && (is_at_least_gl_version(1, 4) || has_extension("GL_ARB_shadow")) && - has_extension("GL_ARB_fragment_program_shadow")) { + (is_at_least_gl_version(2, 0) || has_extension("GL_ARB_fragment_program_shadow"))) { _supports_shadow_filter = true; } #endif @@ -2550,8 +2550,8 @@ reset() { _border_clamp = _edge_clamp; #ifndef OPENGLES if (gl_support_clamp_to_border && - (has_extension("GL_ARB_texture_border_clamp") || - is_at_least_gl_version(1, 3))) { + (is_at_least_gl_version(1, 3) || + has_extension("GL_ARB_texture_border_clamp"))) { _border_clamp = GL_CLAMP_TO_BORDER; } #endif @@ -2580,6 +2580,11 @@ reset() { _mirror_clamp = GL_MIRROR_CLAMP_EXT; _mirror_edge_clamp = GL_MIRROR_CLAMP_TO_EDGE_EXT; _mirror_border_clamp = GL_MIRROR_CLAMP_TO_BORDER_EXT; + + } else if (is_at_least_gl_version(4, 4) || + has_extension("GL_ARB_texture_mirror_clamp_to_edge")) { + _mirror_clamp = GL_MIRROR_CLAMP_TO_EDGE; + _mirror_edge_clamp = GL_MIRROR_CLAMP_TO_EDGE; } #endif From 353ccfebc1ef9fd7b85f7fe4d1183a070731bc35 Mon Sep 17 00:00:00 2001 From: wezu Date: Sat, 22 Apr 2017 13:39:14 +0200 Subject: [PATCH 6/8] Remove print() from Messenger.find() and Messenger.findAll() If one needs to use find() or findAll() not for debugging purposes (like for getting the function or arguments for some binding), then the print functions in these methods are really unwelcome. find() is the only nice way to get this info (other then de-name-mangling __callbacks) --- direct/src/showbase/Messenger.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/direct/src/showbase/Messenger.py b/direct/src/showbase/Messenger.py index 20c02ce17a..92d2014503 100644 --- a/direct/src/showbase/Messenger.py +++ b/direct/src/showbase/Messenger.py @@ -531,7 +531,6 @@ class Messenger: keys.sort() for event in keys: if repr(event).find(needle) >= 0: - print(self.__eventRepr(event)) return {event: self.__callbacks[event]} def findAll(self, needle, limit=None): @@ -545,7 +544,6 @@ class Messenger: keys.sort() for event in keys: if repr(event).find(needle) >= 0: - print(self.__eventRepr(event)) matches[event] = self.__callbacks[event] # if the limit is not None, decrement and # check for break: From 160f652d5856bd854ebfb6e97f05fc44b9846ee4 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 17 Apr 2017 12:39:02 +0200 Subject: [PATCH 7/8] text: use HarfBuzz for text shaping; support right-to-left text --- direct/src/gui/OnscreenText.py | 17 +++- dtool/src/parser-inc/ft2build.h | 1 + makepanda/makepanda.py | 11 ++- panda/src/text/config_text.cxx | 8 +- panda/src/text/config_text.h | 1 + panda/src/text/dynamicTextFont.cxx | 70 +++++++++++++++- panda/src/text/dynamicTextFont.h | 7 ++ panda/src/text/textAssembler.cxx | 124 ++++++++++++++++++++++++++++- panda/src/text/textAssembler.h | 4 + panda/src/text/textProperties.I | 36 +++++++++ panda/src/text/textProperties.cxx | 60 +++++++++----- panda/src/text/textProperties.h | 14 ++++ 12 files changed, 325 insertions(+), 28 deletions(-) diff --git a/direct/src/gui/OnscreenText.py b/direct/src/gui/OnscreenText.py index 1af714c44a..c3accf191d 100644 --- a/direct/src/gui/OnscreenText.py +++ b/direct/src/gui/OnscreenText.py @@ -35,7 +35,8 @@ class OnscreenText(NodePath): font = None, parent = None, sort = 0, - mayChange = True): + mayChange = True, + direction = None): """ Make a text node from string, put it into the 2d sg and set it up with all the indicated parameters. @@ -95,6 +96,9 @@ class OnscreenText(NodePath): mayChange: pass true if the text or its properties may need to be changed at runtime, false if it is static once created (which leads to better memory optimization). + + direction: this can be set to 'ltr' or 'rtl' to override the + direction of the text. """ if parent == None: parent = aspect2d @@ -192,6 +196,17 @@ class OnscreenText(NodePath): textNode.setFrameColor(frame[0], frame[1], frame[2], frame[3]) textNode.setFrameAsMargin(0.1, 0.1, 0.1, 0.1) + if direction is not None: + if isinstance(direction, str): + direction = direction.lower() + if direction == 'rtl': + direction = TextProperties.D_rtl + elif direction == 'ltr': + direction = TextProperties.D_ltr + else: + raise ValueError('invalid direction') + textNode.setDirection(direction) + # Create a transform for the text for our scale and position. # We'd rather do it here, on the text itself, rather than on # our NodePath, so we have one fewer transforms in the scene diff --git a/dtool/src/parser-inc/ft2build.h b/dtool/src/parser-inc/ft2build.h index 6b4184a467..80966f984d 100644 --- a/dtool/src/parser-inc/ft2build.h +++ b/dtool/src/parser-inc/ft2build.h @@ -29,6 +29,7 @@ class FT_Library; class FT_Bitmap; class FT_Vector; class FT_Span; +class FT_Outline; #endif diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index af00b2ea3b..66b3f57545 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -80,8 +80,9 @@ PkgListSet(["PYTHON", "DIRECT", # Python support "VORBIS", "FFMPEG", "SWSCALE", "SWRESAMPLE", # Audio decoding "ODE", "PHYSX", "BULLET", "PANDAPHYSICS", # Physics "SPEEDTREE", # SpeedTree - "ZLIB", "PNG", "JPEG", "TIFF", "OPENEXR", "SQUISH", "FREETYPE", # 2D Formats support + "ZLIB", "PNG", "JPEG", "TIFF", "OPENEXR", "SQUISH", # 2D Formats support ] + MAYAVERSIONS + MAXVERSIONS + [ "FCOLLADA", "ASSIMP", "EGG", # 3D Formats support + "FREETYPE", "HARFBUZZ", # Text rendering "VRPN", "OPENSSL", # Transport "FFTW", # Algorithm helpers "ARTOOLKIT", "OPENCV", "DIRECTCAM", "VISION", # Augmented Reality @@ -638,6 +639,7 @@ if (COMPILER == "MSVC"): if (PkgSkip("NVIDIACG")==0): LibName("CGDX9", GetThirdpartyDir() + "nvidiacg/lib/cgD3D9.lib") if (PkgSkip("NVIDIACG")==0): LibName("NVIDIACG", GetThirdpartyDir() + "nvidiacg/lib/cg.lib") if (PkgSkip("FREETYPE")==0): LibName("FREETYPE", GetThirdpartyDir() + "freetype/lib/freetype.lib") + if (PkgSkip("HARFBUZZ")==0): LibName("HARFBUZZ", GetThirdpartyDir() + "harfbuzz/lib/harfbuzz.lib") if (PkgSkip("FFTW")==0): LibName("FFTW", GetThirdpartyDir() + "fftw/lib/rfftw.lib") if (PkgSkip("FFTW")==0): LibName("FFTW", GetThirdpartyDir() + "fftw/lib/fftw.lib") if (PkgSkip("ARTOOLKIT")==0):LibName("ARTOOLKIT",GetThirdpartyDir() + "artoolkit/lib/libAR.lib") @@ -803,6 +805,7 @@ if (COMPILER=="GCC"): SmartPkgEnable("FFTW", "", ("rfftw", "fftw"), ("fftw.h", "rfftw.h")) SmartPkgEnable("FMODEX", "", ("fmodex"), ("fmodex", "fmodex/fmod.h")) SmartPkgEnable("FREETYPE", "freetype2", ("freetype"), ("freetype2", "freetype2/freetype/freetype.h")) + SmartPkgEnable("HARFBUZZ", "harfbuzz", ("harfbuzz"), ("harfbuzz", "harfbuzz/hb-ft.h")) SmartPkgEnable("GL", "gl", ("GL"), ("GL/gl.h"), framework = "OpenGL") SmartPkgEnable("GLES", "glesv1_cm", ("GLESv1_CM"), ("GLES/gl.h"), framework = "OpenGLES") SmartPkgEnable("GLES2", "glesv2", ("GLESv2"), ("GLES2/gl2.h")) #framework = "OpenGLES"? @@ -3806,7 +3809,9 @@ if (PkgSkip("FREETYPE")==0 and not RUNTIME): # if (not RUNTIME): - OPTS=['DIR:panda/src/text', 'BUILDING:PANDA', 'ZLIB', 'FREETYPE'] + DefSymbol("HARFBUZZ", "HAVE_HARFBUZZ") + + OPTS=['DIR:panda/src/text', 'BUILDING:PANDA', 'ZLIB', 'FREETYPE', 'HARFBUZZ'] TargetAdd('p3text_composite1.obj', opts=OPTS, input='p3text_composite1.cxx') TargetAdd('p3text_composite2.obj', opts=OPTS, input='p3text_composite2.cxx') @@ -3956,7 +3961,7 @@ if (not RUNTIME): # if (not RUNTIME): - OPTS=['DIR:panda/metalibs/panda', 'BUILDING:PANDA', 'JPEG', 'PNG', + OPTS=['DIR:panda/metalibs/panda', 'BUILDING:PANDA', 'JPEG', 'PNG', 'HARFBUZZ', 'TIFF', 'OPENEXR', 'ZLIB', 'OPENSSL', 'FREETYPE', 'FFTW', 'ADVAPI', 'WINSOCK2', 'SQUISH', 'NVIDIACG', 'VORBIS', 'WINUSER', 'WINMM', 'WINGDI', 'IPHLPAPI'] diff --git a/panda/src/text/config_text.cxx b/panda/src/text/config_text.cxx index 36ef375afe..9475eabf55 100644 --- a/panda/src/text/config_text.cxx +++ b/panda/src/text/config_text.cxx @@ -52,7 +52,13 @@ ConfigVariableBool text_kerning ("text-kerning", false, PRC_DESC("Set this true to enable kerning when the font provides kerning " "tables. This can result in more aesthetically pleasing spacing " - "between individual glyphs.")); + "between individual glyphs. Has no effect when text-use-harfbuzz " + "is true, since HarfBuzz offers superior kerning support.")); + +ConfigVariableBool text_use_harfbuzz +("text-use-harfbuzz", false, + PRC_DESC("Set this true to enable HarfBuzz support, which offers superior " + "text shaping and better support for non-Latin text.")); ConfigVariableInt text_anisotropic_degree ("text-anisotropic-degree", 1, diff --git a/panda/src/text/config_text.h b/panda/src/text/config_text.h index 4e5ca1a6ed..895072fdf6 100644 --- a/panda/src/text/config_text.h +++ b/panda/src/text/config_text.h @@ -31,6 +31,7 @@ NotifyCategoryDecl(text, EXPCL_PANDA_TEXT, EXPTP_PANDA_TEXT); extern ConfigVariableBool text_flatten; extern ConfigVariableBool text_dynamic_merge; extern ConfigVariableBool text_kerning; +extern ConfigVariableBool text_use_harfbuzz; extern ConfigVariableInt text_anisotropic_degree; extern ConfigVariableInt text_texture_margin; extern ConfigVariableDouble text_poly_margin; diff --git a/panda/src/text/dynamicTextFont.cxx b/panda/src/text/dynamicTextFont.cxx index f066e07c35..3a349a6d69 100644 --- a/panda/src/text/dynamicTextFont.cxx +++ b/panda/src/text/dynamicTextFont.cxx @@ -44,6 +44,10 @@ #include "textureAttrib.h" #include "transparencyAttrib.h" +#ifdef HAVE_HARFBUZZ +#include +#endif + TypeHandle DynamicTextFont::_type_handle; @@ -114,7 +118,8 @@ DynamicTextFont(const DynamicTextFont ©) : _has_outline(copy._has_outline), _tex_format(copy._tex_format), _needs_image_processing(copy._needs_image_processing), - _preferred_page(0) + _preferred_page(0), + _hb_font(nullptr) { } @@ -123,6 +128,11 @@ DynamicTextFont(const DynamicTextFont ©) : */ DynamicTextFont:: ~DynamicTextFont() { +#ifdef HAVE_HARFBUZZ + if (_hb_font != nullptr) { + hb_font_destroy(_hb_font); + } +#endif } /** @@ -203,6 +213,13 @@ clear() { _cache.clear(); _pages.clear(); _empty_glyphs.clear(); + +#ifdef HAVE_HARFBUZZ + if (_hb_font != nullptr) { + hb_font_destroy(_hb_font); + _hb_font = nullptr; + } +#endif } /** @@ -305,6 +322,55 @@ get_kerning(int first, int second) const { return delta.x / (_font_pixels_per_unit * 64); } +/** + * Like get_glyph, but uses a glyph index. + */ +bool DynamicTextFont:: +get_glyph_by_index(int character, int glyph_index, CPT(TextGlyph) &glyph) { + if (!_is_valid) { + glyph = nullptr; + return false; + } + + Cache::iterator ci = _cache.find(glyph_index); + if (ci != _cache.end()) { + glyph = (*ci).second; + } else { + FT_Face face = acquire_face(); + glyph = make_glyph(character, face, glyph_index); + _cache.insert(Cache::value_type(glyph_index, glyph.p())); + release_face(face); + } + + if (glyph.is_null()) { + glyph = get_invalid_glyph(); + return false; + } + + return true; +} + +/** + * If Panda was compiled with HarfBuzz enabled, returns a HarfBuzz font for + * this font. + */ +hb_font_t *DynamicTextFont:: +get_hb_font() const { +#ifdef HAVE_HARFBUZZ + if (_hb_font != nullptr) { + return _hb_font; + } + + FT_Face face = acquire_face(); + _hb_font = hb_ft_font_create(face, nullptr); + release_face(face); + + return _hb_font; +#else + return nullptr; +#endif +} + /** * Called from both constructors to set up some initial values. */ @@ -328,6 +394,8 @@ initialize() { _winding_order = WO_default; _preferred_page = 0; + + _hb_font = nullptr; } /** diff --git a/panda/src/text/dynamicTextFont.h b/panda/src/text/dynamicTextFont.h index 26433deef6..775fa6a450 100644 --- a/panda/src/text/dynamicTextFont.h +++ b/panda/src/text/dynamicTextFont.h @@ -31,6 +31,8 @@ class NurbsCurveResult; +typedef struct hb_font_t hb_font_t; + /** * A DynamicTextFont is a special TextFont object that rasterizes its glyphs * from a standard font file (e.g. a TTF file) on the fly. It requires the @@ -125,6 +127,9 @@ public: virtual bool get_glyph(int character, CPT(TextGlyph) &glyph); virtual PN_stdfloat get_kerning(int first, int second) const; + bool get_glyph_by_index(int character, int glyph_index, CPT(TextGlyph) &glyph); + hb_font_t *get_hb_font() const; + private: void initialize(); void update_filters(); @@ -171,6 +176,8 @@ private: typedef pvector< PT(TextGlyph) > EmptyGlyphs; EmptyGlyphs _empty_glyphs; + mutable hb_font_t *_hb_font; + public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/text/textAssembler.cxx b/panda/src/text/textAssembler.cxx index 7b8470116b..745488e7c1 100644 --- a/panda/src/text/textAssembler.cxx +++ b/panda/src/text/textAssembler.cxx @@ -31,10 +31,15 @@ #include "geomVertexData.h" #include "geom.h" #include "modelNode.h" +#include "dynamicTextFont.h" #include #include // for sprintf +#ifdef HAVE_HARFBUZZ +#include +#endif + // This is the factor by which CT_small scales the character down. static const PN_stdfloat small_accent_scale = 0.6f; @@ -1408,6 +1413,12 @@ assemble_row(TextAssembler::TextRow &row, PN_stdfloat underscore_start = 0.0f; const TextProperties *underscore_properties = NULL; + const ComputedProperties *prev_cprops; + +#ifdef HAVE_HARFBUZZ + hb_buffer_t *harfbuff = nullptr; +#endif + TextString::const_iterator si; for (si = row._string.begin(); si != row._string.end(); ++si) { const TextCharacter &tch = (*si); @@ -1448,10 +1459,29 @@ assemble_row(TextAssembler::TextRow &row, LVecBase4 frame = graphic->get_frame(); line_height = max(line_height, frame[3] - frame[2]); } else { - // [fabius] this is not the right place to calc line height (see below) - // line_height = max(line_height, font->get_line_height()); + line_height = max(line_height, font->get_line_height() * properties->get_glyph_scale() * properties->get_text_scale()); } +#ifdef HAVE_HARFBUZZ + if (tch._cprops != prev_cprops || graphic != nullptr) { + if (harfbuff != nullptr && hb_buffer_get_length(harfbuff) > 0) { + // Shape the buffer accumulated so far. + shape_buffer(harfbuff, placed_glyphs, xpos, prev_cprops->_properties); + hb_buffer_reset(harfbuff); + + } else if (harfbuff == nullptr && text_use_harfbuzz && + font->is_of_type(DynamicTextFont::get_class_type())) { + harfbuff = hb_buffer_create(); + } + prev_cprops = tch._cprops; + } + + if (graphic == nullptr && harfbuff != nullptr) { + hb_buffer_add(harfbuff, character, character); + continue; + } +#endif + if (character == ' ') { // A space is a special case. xpos += properties->get_glyph_scale() * properties->get_text_scale() * font->get_space_advance(); @@ -1613,10 +1643,16 @@ assemble_row(TextAssembler::TextRow &row, } xpos += advance * glyph_scale; - line_height = max(line_height, font->get_line_height() * glyph_scale); } } +#ifdef HAVE_HARFBUZZ + if (harfbuff != nullptr && hb_buffer_get_length(harfbuff) > 0) { + shape_buffer(harfbuff, placed_glyphs, xpos, prev_cprops->_properties); + } + hb_buffer_destroy(harfbuff); +#endif + if (underscore && underscore_start != xpos) { draw_underscore(placed_glyphs, underscore_start, xpos, underscore_properties); @@ -1640,6 +1676,88 @@ assemble_row(TextAssembler::TextRow &row, } } +/** + * Places the glyphs collected from a HarfBuzz buffer. + */ +void TextAssembler:: +shape_buffer(hb_buffer_t *buf, PlacedGlyphs &placed_glyphs, PN_stdfloat &xpos, + const TextProperties &properties) { + +#ifdef HAVE_HARFBUZZ + // If we did not specify a text direction, harfbuzz will guess it based on + // the script we are using. + hb_direction_t direction = HB_DIRECTION_INVALID; + if (properties.has_direction()) { + switch (properties.get_direction()) { + case TextProperties::D_ltr: + direction = HB_DIRECTION_LTR; + break; + case TextProperties::D_rtl: + direction = HB_DIRECTION_RTL; + break; + } + } + hb_buffer_set_direction(buf, direction); + hb_buffer_guess_segment_properties(buf); + + DynamicTextFont *font = DCAST(DynamicTextFont, properties.get_font()); + hb_font_t *hb_font = font->get_hb_font(); + hb_shape(hb_font, buf, NULL, 0); + + PN_stdfloat glyph_scale = properties.get_glyph_scale() * properties.get_text_scale(); + PN_stdfloat scale = glyph_scale / (font->get_pixels_per_unit() * font->get_scale_factor() * 64.0); + + unsigned int glyph_count; + hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count); + hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count); + + for (unsigned int i = 0; i < glyph_count; ++i) { + int character = glyph_info[i].cluster; + int glyph_index = glyph_info[i].codepoint; + + CPT(TextGlyph) glyph; + if (!font->get_glyph_by_index(character, glyph_index, glyph)) { + char buffer[512]; + sprintf(buffer, "U+%04x", character); + text_cat.warning() + << "No definition in " << font->get_name() + << " for character " << buffer; + if (character < 128 && isprint((unsigned int)character)) { + text_cat.warning(false) + << " ('" << (char)character << "')"; + } + text_cat.warning(false) + << "\n"; + } + + PN_stdfloat advance = glyph_pos[i].x_advance * scale; + if (glyph->is_whitespace()) { + // A space is a special case. + xpos += advance; + continue; + } + + PN_stdfloat x_offset = glyph_pos[i].x_offset * scale; + PN_stdfloat y_offset = glyph_pos[i].y_offset * scale; + + // Build up a GlyphPlacement, indicating all of the Geoms that go into + // this character. Normally, there is only one Geom per character, but + // it may involve multiple Geoms if we need to add cheesy accents or + // ligatures. + GlyphPlacement placement; + placement._glyph = move(glyph); + placement._scale = glyph_scale; + placement._xpos = xpos + x_offset; + placement._ypos = properties.get_glyph_shift() + y_offset; + placement._slant = properties.get_slant(); + placement._properties = &properties; + placed_glyphs.push_back(placement); + + xpos += advance; + } +#endif +} + /** * Creates the geometry to render the underscore line for the indicated range * of glyphs in this row. diff --git a/panda/src/text/textAssembler.h b/panda/src/text/textAssembler.h index 64b4c7ef03..3b45d729b1 100644 --- a/panda/src/text/textAssembler.h +++ b/panda/src/text/textAssembler.h @@ -28,6 +28,7 @@ #include "pmap.h" +typedef struct hb_buffer_t hb_buffer_t; class TextEncoder; class TextGraphic; @@ -247,6 +248,9 @@ private: PN_stdfloat &row_width, PN_stdfloat &line_height, TextProperties::Alignment &align, PN_stdfloat &wordwrap); + void shape_buffer(hb_buffer_t *buf, PlacedGlyphs &glyphs, PN_stdfloat &xpos, + const TextProperties &properties); + // These interfaces are for implementing cheesy accent marks and ligatures // when the font doesn't support them. enum CheesyPosition { diff --git a/panda/src/text/textProperties.I b/panda/src/text/textProperties.I index 4e73399be1..e3e296c95f 100644 --- a/panda/src/text/textProperties.I +++ b/panda/src/text/textProperties.I @@ -797,3 +797,39 @@ INLINE PN_stdfloat TextProperties:: get_text_scale() const { return _text_scale; } + +/** + * Specifies the text direction. If none is specified, it will be guessed + * based on the contents of the string. + */ +INLINE void TextProperties:: +set_direction(Direction direction) { + _direction = direction; + _specified |= F_has_direction; +} + +/** + * Clears the text direction setting. If no text direction is specified, it + * will be guessed based on the contents of the string. + */ +INLINE void TextProperties:: +clear_direction() { + _specified &= ~F_has_direction; + _direction = D_ltr; +} + +/** + * + */ +INLINE bool TextProperties:: +has_direction() const { + return (_specified & F_has_direction) != 0; +} + +/** + * Returns the direction of the text as specified by set_direction(). + */ +INLINE TextProperties::Direction TextProperties:: +get_direction() const { + return _direction; +} diff --git a/panda/src/text/textProperties.cxx b/panda/src/text/textProperties.cxx index 94b02ffc11..341f2385a3 100644 --- a/panda/src/text/textProperties.cxx +++ b/panda/src/text/textProperties.cxx @@ -31,26 +31,27 @@ TypeHandle TextProperties::_type_handle; * */ TextProperties:: -TextProperties() { - _specified = 0; +TextProperties() : + _specified(0), - _small_caps = text_small_caps; - _small_caps_scale = text_small_caps_scale; - _slant = 0.0f; - _underscore = false; - _underscore_height = 0.0f; - _align = A_left; - _indent_width = 0.0f; - _wordwrap_width = 0.0f; - _preserve_trailing_whitespace = false; - _text_color.set(1.0f, 1.0f, 1.0f, 1.0f); - _shadow_color.set(0.0f, 0.0f, 0.0f, 1.0f); - _shadow_offset.set(0.0f, 0.0f); - _draw_order = 1; - _tab_width = text_tab_width; - _glyph_scale = 1.0f; - _glyph_shift = 0.0f; - _text_scale = 1.0f; + _small_caps(text_small_caps), + _small_caps_scale(text_small_caps_scale), + _slant(0.0f), + _underscore(false), + _underscore_height(0.0f), + _align(A_left), + _indent_width(0.0f), + _wordwrap_width(0.0f), + _preserve_trailing_whitespace(false), + _text_color(1.0f, 1.0f, 1.0f, 1.0f), + _shadow_color(0.0f, 0.0f, 0.0f, 1.0f), + _shadow_offset(0.0f, 0.0f), + _draw_order(1), + _tab_width(text_tab_width), + _glyph_scale(1.0f), + _glyph_shift(0.0f), + _text_scale(1.0f), + _direction(D_rtl) { } /** @@ -89,6 +90,7 @@ operator = (const TextProperties ©) { _glyph_scale = copy._glyph_scale; _glyph_shift = copy._glyph_shift; _text_scale = copy._text_scale; + _direction = copy._direction; _text_state.clear(); _shadow_state.clear(); @@ -163,6 +165,9 @@ operator == (const TextProperties &other) const { if ((_specified & F_has_text_scale) && _text_scale != other._text_scale) { return false; } + if ((_specified & F_has_direction) && _direction != other._direction) { + return false; + } return true; } @@ -238,6 +243,9 @@ add_properties(const TextProperties &other) { if (other.has_text_scale()) { set_text_scale(other.get_text_scale()); } + if (other.has_direction()) { + set_direction(other.get_direction()); + } } @@ -361,6 +369,20 @@ write(ostream &out, int indent_level) const { indent(out, indent_level) << "text scale is " << get_text_scale() << "\n"; } + + if (has_direction()) { + indent(out, indent_level) + << "direction is "; + switch (get_direction()) { + case D_ltr: + out << "D_ltr\n"; + break; + + case D_rtl: + out << "D_rtl\n"; + break; + } + } } /** diff --git a/panda/src/text/textProperties.h b/panda/src/text/textProperties.h index c320e9a37b..af899f7b59 100644 --- a/panda/src/text/textProperties.h +++ b/panda/src/text/textProperties.h @@ -49,6 +49,11 @@ PUBLISHED: A_boxed_center }; + enum Direction { + D_ltr, + D_rtl, + }; + TextProperties(); TextProperties(const TextProperties ©); void operator = (const TextProperties ©); @@ -160,6 +165,11 @@ PUBLISHED: INLINE bool has_text_scale() const; INLINE PN_stdfloat get_text_scale() const; + INLINE void set_direction(Direction direction); + INLINE void clear_direction(); + INLINE bool has_direction() const; + INLINE Direction get_direction() const; + void add_properties(const TextProperties &other); void write(ostream &out, int indent_level = 0) const; @@ -197,6 +207,8 @@ PUBLISHED: set_glyph_shift, clear_glyph_shift); MAKE_PROPERTY2(text_scale, has_text_scale, get_text_scale, set_text_scale, clear_text_scale); + MAKE_PROPERTY2(direction, has_direction, get_direction, + set_direction, clear_direction); public: const RenderState *get_text_state() const; @@ -225,6 +237,7 @@ private: F_has_underscore = 0x00010000, F_has_underscore_height = 0x00020000, F_has_text_scale = 0x00040000, + F_has_direction = 0x00080000, }; int _specified; @@ -248,6 +261,7 @@ private: PN_stdfloat _glyph_scale; PN_stdfloat _glyph_shift; PN_stdfloat _text_scale; + Direction _direction; mutable CPT(RenderState) _text_state; mutable CPT(RenderState) _shadow_state; From a3bef3e2854eab2614a48d737328b01a6e257dd7 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 25 Apr 2017 12:37:45 +0200 Subject: [PATCH 8/8] makepanda: allow building without harfbuzz --- makepanda/makepanda.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 66b3f57545..af4cb2dca5 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -3809,7 +3809,8 @@ if (PkgSkip("FREETYPE")==0 and not RUNTIME): # if (not RUNTIME): - DefSymbol("HARFBUZZ", "HAVE_HARFBUZZ") + if not PkgSkip("HARFBUZZ"): + DefSymbol("HARFBUZZ", "HAVE_HARFBUZZ") OPTS=['DIR:panda/src/text', 'BUILDING:PANDA', 'ZLIB', 'FREETYPE', 'HARFBUZZ'] TargetAdd('p3text_composite1.obj', opts=OPTS, input='p3text_composite1.cxx')