Merge branch 'master' into deploy-ng

This commit is contained in:
Mitchell Stokes 2017-04-26 18:22:36 -07:00
commit 0ee6a46a62
20 changed files with 374 additions and 57 deletions

View File

@ -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 Panda3D is licensed under the Modified BSD License. See the LICENSE file for
more details. 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 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 The following command illustrates how to build Panda3D with some common
options: options:
```bash ```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 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 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. `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 macOS
----- -----

View File

@ -35,7 +35,8 @@ class OnscreenText(NodePath):
font = None, font = None,
parent = None, parent = None,
sort = 0, sort = 0,
mayChange = True): mayChange = True,
direction = None):
""" """
Make a text node from string, put it into the 2d sg and set it Make a text node from string, put it into the 2d sg and set it
up with all the indicated parameters. up with all the indicated parameters.
@ -95,6 +96,9 @@ class OnscreenText(NodePath):
mayChange: pass true if the text or its properties may need mayChange: pass true if the text or its properties may need
to be changed at runtime, false if it is static once to be changed at runtime, false if it is static once
created (which leads to better memory optimization). 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: if parent == None:
parent = aspect2d parent = aspect2d
@ -192,6 +196,17 @@ class OnscreenText(NodePath):
textNode.setFrameColor(frame[0], frame[1], frame[2], frame[3]) textNode.setFrameColor(frame[0], frame[1], frame[2], frame[3])
textNode.setFrameAsMargin(0.1, 0.1, 0.1, 0.1) 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. # Create a transform for the text for our scale and position.
# We'd rather do it here, on the text itself, rather than on # We'd rather do it here, on the text itself, rather than on
# our NodePath, so we have one fewer transforms in the scene # our NodePath, so we have one fewer transforms in the scene

View File

@ -531,7 +531,6 @@ class Messenger:
keys.sort() keys.sort()
for event in keys: for event in keys:
if repr(event).find(needle) >= 0: if repr(event).find(needle) >= 0:
print(self.__eventRepr(event))
return {event: self.__callbacks[event]} return {event: self.__callbacks[event]}
def findAll(self, needle, limit=None): def findAll(self, needle, limit=None):
@ -545,7 +544,6 @@ class Messenger:
keys.sort() keys.sort()
for event in keys: for event in keys:
if repr(event).find(needle) >= 0: if repr(event).find(needle) >= 0:
print(self.__eventRepr(event))
matches[event] = self.__callbacks[event] matches[event] = self.__callbacks[event]
# if the limit is not None, decrement and # if the limit is not None, decrement and
# check for break: # check for break:

View File

@ -1658,7 +1658,12 @@ output(ostream &out, int indent_level, CPPScope *scope, bool) const {
break; break;
case T_function: 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; break;
case T_unknown_ident: case T_unknown_ident:

View File

@ -58,18 +58,6 @@
#pragma warning (disable : 4267) #pragma warning (disable : 4267)
/* C4577: 'noexcept' used with no exception handling mode specified */ /* C4577: 'noexcept' used with no exception handling mode specified */
#pragma warning (disable : 4577) #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 */ #endif /* WIN32_VC */
#ifndef __has_builtin #ifndef __has_builtin

View File

@ -29,6 +29,7 @@ class FT_Library;
class FT_Bitmap; class FT_Bitmap;
class FT_Vector; class FT_Vector;
class FT_Span; class FT_Span;
class FT_Outline;
#endif #endif

View File

@ -152,8 +152,6 @@ private:
#define nassert_raise(message) Notify::write_string(message) #define nassert_raise(message) Notify::write_string(message)
#define enter_debugger_if(condition) ((void)0)
#else // NDEBUG #else // NDEBUG
#define nassertr(condition, return_value) \ #define nassertr(condition, return_value) \
@ -183,13 +181,6 @@ private:
#define nassert_raise(message) Notify::ptr()->assert_failure(message, __LINE__, __FILE__) #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 #endif // NDEBUG
#if __cplusplus >= 201103 #if __cplusplus >= 201103

View File

@ -80,8 +80,9 @@ PkgListSet(["PYTHON", "DIRECT", # Python support
"VORBIS", "FFMPEG", "SWSCALE", "SWRESAMPLE", # Audio decoding "VORBIS", "FFMPEG", "SWSCALE", "SWRESAMPLE", # Audio decoding
"ODE", "PHYSX", "BULLET", "PANDAPHYSICS", # Physics "ODE", "PHYSX", "BULLET", "PANDAPHYSICS", # Physics
"SPEEDTREE", # SpeedTree "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 ] + MAYAVERSIONS + MAXVERSIONS + [ "FCOLLADA", "ASSIMP", "EGG", # 3D Formats support
"FREETYPE", "HARFBUZZ", # Text rendering
"VRPN", "OPENSSL", # Transport "VRPN", "OPENSSL", # Transport
"FFTW", # Algorithm helpers "FFTW", # Algorithm helpers
"ARTOOLKIT", "OPENCV", "DIRECTCAM", "VISION", # Augmented Reality "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("CGDX9", GetThirdpartyDir() + "nvidiacg/lib/cgD3D9.lib")
if (PkgSkip("NVIDIACG")==0): LibName("NVIDIACG", GetThirdpartyDir() + "nvidiacg/lib/cg.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("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/rfftw.lib")
if (PkgSkip("FFTW")==0): LibName("FFTW", GetThirdpartyDir() + "fftw/lib/fftw.lib") if (PkgSkip("FFTW")==0): LibName("FFTW", GetThirdpartyDir() + "fftw/lib/fftw.lib")
if (PkgSkip("ARTOOLKIT")==0):LibName("ARTOOLKIT",GetThirdpartyDir() + "artoolkit/lib/libAR.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("FFTW", "", ("rfftw", "fftw"), ("fftw.h", "rfftw.h"))
SmartPkgEnable("FMODEX", "", ("fmodex"), ("fmodex", "fmodex/fmod.h")) SmartPkgEnable("FMODEX", "", ("fmodex"), ("fmodex", "fmodex/fmod.h"))
SmartPkgEnable("FREETYPE", "freetype2", ("freetype"), ("freetype2", "freetype2/freetype/freetype.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("GL", "gl", ("GL"), ("GL/gl.h"), framework = "OpenGL")
SmartPkgEnable("GLES", "glesv1_cm", ("GLESv1_CM"), ("GLES/gl.h"), framework = "OpenGLES") SmartPkgEnable("GLES", "glesv1_cm", ("GLESv1_CM"), ("GLES/gl.h"), framework = "OpenGLES")
SmartPkgEnable("GLES2", "glesv2", ("GLESv2"), ("GLES2/gl2.h")) #framework = "OpenGLES"? SmartPkgEnable("GLES2", "glesv2", ("GLESv2"), ("GLES2/gl2.h")) #framework = "OpenGLES"?
@ -3807,7 +3810,10 @@ if (PkgSkip("FREETYPE")==0 and not RUNTIME):
# #
if (not RUNTIME): if (not RUNTIME):
OPTS=['DIR:panda/src/text', 'BUILDING:PANDA', 'ZLIB', 'FREETYPE'] 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') TargetAdd('p3text_composite1.obj', opts=OPTS, input='p3text_composite1.cxx')
TargetAdd('p3text_composite2.obj', opts=OPTS, input='p3text_composite2.cxx') TargetAdd('p3text_composite2.obj', opts=OPTS, input='p3text_composite2.cxx')
@ -3957,7 +3963,7 @@ if (not RUNTIME):
# #
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', 'TIFF', 'OPENEXR', 'ZLIB', 'OPENSSL', 'FREETYPE', 'FFTW', 'ADVAPI', 'WINSOCK2',
'SQUISH', 'NVIDIACG', 'VORBIS', 'WINUSER', 'WINMM', 'WINGDI', 'IPHLPAPI'] 'SQUISH', 'NVIDIACG', 'VORBIS', 'WINUSER', 'WINMM', 'WINGDI', 'IPHLPAPI']

View File

@ -1501,6 +1501,16 @@ open_buffer() {
return true; 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. * Closes the buffer right now. Called from the window thread.
*/ */

View File

@ -77,6 +77,8 @@ public:
void unregister_shared_depth_buffer(GraphicsOutput *graphics_output); void unregister_shared_depth_buffer(GraphicsOutput *graphics_output);
protected: protected:
virtual GraphicsOutput *get_host();
virtual void close_buffer(); virtual void close_buffer();
virtual bool open_buffer(); virtual bool open_buffer();

View File

@ -1139,7 +1139,7 @@ reset() {
_supports_multisample = false; _supports_multisample = false;
#else #else
_supports_multisample = _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 #endif
#ifdef OPENGLES_1 #ifdef OPENGLES_1
@ -1305,7 +1305,7 @@ reset() {
if (gl_support_shadow_filter && if (gl_support_shadow_filter &&
_supports_depth_texture && _supports_depth_texture &&
(is_at_least_gl_version(1, 4) || has_extension("GL_ARB_shadow")) && (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; _supports_shadow_filter = true;
} }
#endif #endif
@ -2550,8 +2550,8 @@ reset() {
_border_clamp = _edge_clamp; _border_clamp = _edge_clamp;
#ifndef OPENGLES #ifndef OPENGLES
if (gl_support_clamp_to_border && 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; _border_clamp = GL_CLAMP_TO_BORDER;
} }
#endif #endif
@ -2580,6 +2580,11 @@ reset() {
_mirror_clamp = GL_MIRROR_CLAMP_EXT; _mirror_clamp = GL_MIRROR_CLAMP_EXT;
_mirror_edge_clamp = GL_MIRROR_CLAMP_TO_EDGE_EXT; _mirror_edge_clamp = GL_MIRROR_CLAMP_TO_EDGE_EXT;
_mirror_border_clamp = GL_MIRROR_CLAMP_TO_BORDER_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 #endif

View File

@ -52,7 +52,13 @@ ConfigVariableBool text_kerning
("text-kerning", false, ("text-kerning", false,
PRC_DESC("Set this true to enable kerning when the font provides kerning " PRC_DESC("Set this true to enable kerning when the font provides kerning "
"tables. This can result in more aesthetically pleasing spacing " "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 ConfigVariableInt text_anisotropic_degree
("text-anisotropic-degree", 1, ("text-anisotropic-degree", 1,

View File

@ -31,6 +31,7 @@ NotifyCategoryDecl(text, EXPCL_PANDA_TEXT, EXPTP_PANDA_TEXT);
extern ConfigVariableBool text_flatten; extern ConfigVariableBool text_flatten;
extern ConfigVariableBool text_dynamic_merge; extern ConfigVariableBool text_dynamic_merge;
extern ConfigVariableBool text_kerning; extern ConfigVariableBool text_kerning;
extern ConfigVariableBool text_use_harfbuzz;
extern ConfigVariableInt text_anisotropic_degree; extern ConfigVariableInt text_anisotropic_degree;
extern ConfigVariableInt text_texture_margin; extern ConfigVariableInt text_texture_margin;
extern ConfigVariableDouble text_poly_margin; extern ConfigVariableDouble text_poly_margin;

View File

@ -44,6 +44,10 @@
#include "textureAttrib.h" #include "textureAttrib.h"
#include "transparencyAttrib.h" #include "transparencyAttrib.h"
#ifdef HAVE_HARFBUZZ
#include <hb-ft.h>
#endif
TypeHandle DynamicTextFont::_type_handle; TypeHandle DynamicTextFont::_type_handle;
@ -114,7 +118,8 @@ DynamicTextFont(const DynamicTextFont &copy) :
_has_outline(copy._has_outline), _has_outline(copy._has_outline),
_tex_format(copy._tex_format), _tex_format(copy._tex_format),
_needs_image_processing(copy._needs_image_processing), _needs_image_processing(copy._needs_image_processing),
_preferred_page(0) _preferred_page(0),
_hb_font(nullptr)
{ {
} }
@ -123,6 +128,11 @@ DynamicTextFont(const DynamicTextFont &copy) :
*/ */
DynamicTextFont:: DynamicTextFont::
~DynamicTextFont() { ~DynamicTextFont() {
#ifdef HAVE_HARFBUZZ
if (_hb_font != nullptr) {
hb_font_destroy(_hb_font);
}
#endif
} }
/** /**
@ -203,6 +213,13 @@ clear() {
_cache.clear(); _cache.clear();
_pages.clear(); _pages.clear();
_empty_glyphs.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); 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. * Called from both constructors to set up some initial values.
*/ */
@ -328,6 +394,8 @@ initialize() {
_winding_order = WO_default; _winding_order = WO_default;
_preferred_page = 0; _preferred_page = 0;
_hb_font = nullptr;
} }
/** /**

View File

@ -31,6 +31,8 @@
class NurbsCurveResult; class NurbsCurveResult;
typedef struct hb_font_t hb_font_t;
/** /**
* A DynamicTextFont is a special TextFont object that rasterizes its glyphs * 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 * 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 bool get_glyph(int character, CPT(TextGlyph) &glyph);
virtual PN_stdfloat get_kerning(int first, int second) const; 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: private:
void initialize(); void initialize();
void update_filters(); void update_filters();
@ -171,6 +176,8 @@ private:
typedef pvector< PT(TextGlyph) > EmptyGlyphs; typedef pvector< PT(TextGlyph) > EmptyGlyphs;
EmptyGlyphs _empty_glyphs; EmptyGlyphs _empty_glyphs;
mutable hb_font_t *_hb_font;
public: public:
static TypeHandle get_class_type() { static TypeHandle get_class_type() {
return _type_handle; return _type_handle;

View File

@ -31,10 +31,15 @@
#include "geomVertexData.h" #include "geomVertexData.h"
#include "geom.h" #include "geom.h"
#include "modelNode.h" #include "modelNode.h"
#include "dynamicTextFont.h"
#include <ctype.h> #include <ctype.h>
#include <stdio.h> // for sprintf #include <stdio.h> // for sprintf
#ifdef HAVE_HARFBUZZ
#include <hb.h>
#endif
// This is the factor by which CT_small scales the character down. // This is the factor by which CT_small scales the character down.
static const PN_stdfloat small_accent_scale = 0.6f; static const PN_stdfloat small_accent_scale = 0.6f;
@ -1408,6 +1413,12 @@ assemble_row(TextAssembler::TextRow &row,
PN_stdfloat underscore_start = 0.0f; PN_stdfloat underscore_start = 0.0f;
const TextProperties *underscore_properties = NULL; const TextProperties *underscore_properties = NULL;
const ComputedProperties *prev_cprops;
#ifdef HAVE_HARFBUZZ
hb_buffer_t *harfbuff = nullptr;
#endif
TextString::const_iterator si; TextString::const_iterator si;
for (si = row._string.begin(); si != row._string.end(); ++si) { for (si = row._string.begin(); si != row._string.end(); ++si) {
const TextCharacter &tch = (*si); const TextCharacter &tch = (*si);
@ -1448,10 +1459,29 @@ assemble_row(TextAssembler::TextRow &row,
LVecBase4 frame = graphic->get_frame(); LVecBase4 frame = graphic->get_frame();
line_height = max(line_height, frame[3] - frame[2]); line_height = max(line_height, frame[3] - frame[2]);
} else { } else {
// [fabius] this is not the right place to calc line height (see below) line_height = max(line_height, font->get_line_height() * properties->get_glyph_scale() * properties->get_text_scale());
// line_height = max(line_height, font->get_line_height());
} }
#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 == ' ') { if (character == ' ') {
// A space is a special case. // A space is a special case.
xpos += properties->get_glyph_scale() * properties->get_text_scale() * font->get_space_advance(); 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; 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) { if (underscore && underscore_start != xpos) {
draw_underscore(placed_glyphs, underscore_start, xpos, draw_underscore(placed_glyphs, underscore_start, xpos,
underscore_properties); 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 * Creates the geometry to render the underscore line for the indicated range
* of glyphs in this row. * of glyphs in this row.

View File

@ -28,6 +28,7 @@
#include "pmap.h" #include "pmap.h"
typedef struct hb_buffer_t hb_buffer_t;
class TextEncoder; class TextEncoder;
class TextGraphic; class TextGraphic;
@ -247,6 +248,9 @@ private:
PN_stdfloat &row_width, PN_stdfloat &line_height, PN_stdfloat &row_width, PN_stdfloat &line_height,
TextProperties::Alignment &align, PN_stdfloat &wordwrap); 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 // These interfaces are for implementing cheesy accent marks and ligatures
// when the font doesn't support them. // when the font doesn't support them.
enum CheesyPosition { enum CheesyPosition {

View File

@ -797,3 +797,39 @@ INLINE PN_stdfloat TextProperties::
get_text_scale() const { get_text_scale() const {
return _text_scale; 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;
}

View File

@ -31,26 +31,27 @@ TypeHandle TextProperties::_type_handle;
* *
*/ */
TextProperties:: TextProperties::
TextProperties() { TextProperties() :
_specified = 0; _specified(0),
_small_caps = text_small_caps; _small_caps(text_small_caps),
_small_caps_scale = text_small_caps_scale; _small_caps_scale(text_small_caps_scale),
_slant = 0.0f; _slant(0.0f),
_underscore = false; _underscore(false),
_underscore_height = 0.0f; _underscore_height(0.0f),
_align = A_left; _align(A_left),
_indent_width = 0.0f; _indent_width(0.0f),
_wordwrap_width = 0.0f; _wordwrap_width(0.0f),
_preserve_trailing_whitespace = false; _preserve_trailing_whitespace(false),
_text_color.set(1.0f, 1.0f, 1.0f, 1.0f); _text_color(1.0f, 1.0f, 1.0f, 1.0f),
_shadow_color.set(0.0f, 0.0f, 0.0f, 1.0f); _shadow_color(0.0f, 0.0f, 0.0f, 1.0f),
_shadow_offset.set(0.0f, 0.0f); _shadow_offset(0.0f, 0.0f),
_draw_order = 1; _draw_order(1),
_tab_width = text_tab_width; _tab_width(text_tab_width),
_glyph_scale = 1.0f; _glyph_scale(1.0f),
_glyph_shift = 0.0f; _glyph_shift(0.0f),
_text_scale = 1.0f; _text_scale(1.0f),
_direction(D_rtl) {
} }
/** /**
@ -89,6 +90,7 @@ operator = (const TextProperties &copy) {
_glyph_scale = copy._glyph_scale; _glyph_scale = copy._glyph_scale;
_glyph_shift = copy._glyph_shift; _glyph_shift = copy._glyph_shift;
_text_scale = copy._text_scale; _text_scale = copy._text_scale;
_direction = copy._direction;
_text_state.clear(); _text_state.clear();
_shadow_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) { if ((_specified & F_has_text_scale) && _text_scale != other._text_scale) {
return false; return false;
} }
if ((_specified & F_has_direction) && _direction != other._direction) {
return false;
}
return true; return true;
} }
@ -238,6 +243,9 @@ add_properties(const TextProperties &other) {
if (other.has_text_scale()) { if (other.has_text_scale()) {
set_text_scale(other.get_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) indent(out, indent_level)
<< "text scale is " << get_text_scale() << "\n"; << "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;
}
}
} }
/** /**

View File

@ -49,6 +49,11 @@ PUBLISHED:
A_boxed_center A_boxed_center
}; };
enum Direction {
D_ltr,
D_rtl,
};
TextProperties(); TextProperties();
TextProperties(const TextProperties &copy); TextProperties(const TextProperties &copy);
void operator = (const TextProperties &copy); void operator = (const TextProperties &copy);
@ -160,6 +165,11 @@ PUBLISHED:
INLINE bool has_text_scale() const; INLINE bool has_text_scale() const;
INLINE PN_stdfloat get_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 add_properties(const TextProperties &other);
void write(ostream &out, int indent_level = 0) const; void write(ostream &out, int indent_level = 0) const;
@ -197,6 +207,8 @@ PUBLISHED:
set_glyph_shift, clear_glyph_shift); set_glyph_shift, clear_glyph_shift);
MAKE_PROPERTY2(text_scale, has_text_scale, get_text_scale, MAKE_PROPERTY2(text_scale, has_text_scale, get_text_scale,
set_text_scale, clear_text_scale); set_text_scale, clear_text_scale);
MAKE_PROPERTY2(direction, has_direction, get_direction,
set_direction, clear_direction);
public: public:
const RenderState *get_text_state() const; const RenderState *get_text_state() const;
@ -225,6 +237,7 @@ private:
F_has_underscore = 0x00010000, F_has_underscore = 0x00010000,
F_has_underscore_height = 0x00020000, F_has_underscore_height = 0x00020000,
F_has_text_scale = 0x00040000, F_has_text_scale = 0x00040000,
F_has_direction = 0x00080000,
}; };
int _specified; int _specified;
@ -248,6 +261,7 @@ private:
PN_stdfloat _glyph_scale; PN_stdfloat _glyph_scale;
PN_stdfloat _glyph_shift; PN_stdfloat _glyph_shift;
PN_stdfloat _text_scale; PN_stdfloat _text_scale;
Direction _direction;
mutable CPT(RenderState) _text_state; mutable CPT(RenderState) _text_state;
mutable CPT(RenderState) _shadow_state; mutable CPT(RenderState) _shadow_state;