Browse Source
Everything needed to make a Python variant of the Text example. The Oxygen.ttf font file is the same as in MagnumPlugins/FreeTypeFont/Test in the magnum-plugins repository.pull/16/head
14 changed files with 463 additions and 2 deletions
@ -0,0 +1,69 @@ |
|||||||
|
.. |
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||||
|
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
.. |
||||||
|
|
||||||
|
.. py:class:: magnum.text.FontManager |
||||||
|
:summary: Manager for :ref:`AbstractFont` plugin instances |
||||||
|
|
||||||
|
Each plugin returned by :ref:`instantiate()` or :ref:`load_and_instantiate()` |
||||||
|
references its owning :ref:`FontManager` through |
||||||
|
:ref:`AbstractFont.manager`, ensuring the manager is not deleted before the |
||||||
|
plugin instances are. |
||||||
|
|
||||||
|
.. py:class:: magnum.text.AbstractFont |
||||||
|
|
||||||
|
Similarly to C++, font plugins are loaded through :ref:`FontManager`: |
||||||
|
|
||||||
|
.. |
||||||
|
>>> from magnum import text |
||||||
|
|
||||||
|
.. code:: py |
||||||
|
|
||||||
|
>>> manager = text.FontManager() |
||||||
|
>>> font = manager.load_and_instantiate('StbTrueTypeFont') |
||||||
|
|
||||||
|
Unlike C++, errors in both API usage and file parsing are reported by |
||||||
|
raising an exception. See particular function documentation for detailed |
||||||
|
behavior. |
||||||
|
|
||||||
|
.. py:function:: magnum.text.AbstractFont.open_data |
||||||
|
:raise RuntimeError: If file opening fails |
||||||
|
|
||||||
|
.. py:function:: magnum.text.AbstractFont.open_file |
||||||
|
:raise RuntimeError: If file opening fails |
||||||
|
|
||||||
|
.. py:property:: magnum.text.AbstractFont.size |
||||||
|
:raise AssertionError: If no file is opened |
||||||
|
.. py:property:: magnum.text.AbstractFont.ascent |
||||||
|
:raise AssertionError: If no file is opened |
||||||
|
.. py:property:: magnum.text.AbstractFont.descent |
||||||
|
:raise AssertionError: If no file is opened |
||||||
|
.. py:property:: magnum.text.AbstractFont.line_height |
||||||
|
:raise AssertionError: If no file is opened |
||||||
|
.. py:function:: magnum.text.AbstractFont.glyph_id |
||||||
|
:raise AssertionError: If no file is opened |
||||||
|
.. py:function:: magnum.text.AbstractFont.glyph_advance |
||||||
|
:raise AssertionError: If no file is opened |
||||||
|
.. py:function:: magnum.text.AbstractFont.fill_glyph_cache |
||||||
|
:raise AssertionError: If no file is opened |
||||||
Binary file not shown.
@ -0,0 +1,104 @@ |
|||||||
|
# |
||||||
|
# This file is part of Magnum. |
||||||
|
# |
||||||
|
# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||||
|
# 2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
# |
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
# copy of this software and associated documentation files (the "Software"), |
||||||
|
# to deal in the Software without restriction, including without limitation |
||||||
|
# the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
# and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
# Software is furnished to do so, subject to the following conditions: |
||||||
|
# |
||||||
|
# The above copyright notice and this permission notice shall be included |
||||||
|
# in all copies or substantial portions of the Software. |
||||||
|
# |
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
# DEALINGS IN THE SOFTWARE. |
||||||
|
# |
||||||
|
|
||||||
|
import os |
||||||
|
import sys |
||||||
|
import unittest |
||||||
|
|
||||||
|
from corrade import pluginmanager |
||||||
|
from magnum import * |
||||||
|
from magnum import text |
||||||
|
|
||||||
|
class Font(unittest.TestCase): |
||||||
|
def test(self): |
||||||
|
manager = text.FontManager() |
||||||
|
self.assertIn('StbTrueTypeFont', manager.alias_list) |
||||||
|
self.assertEqual(manager.load_state('StbTrueTypeFont'), pluginmanager.LoadState.NOT_LOADED) |
||||||
|
|
||||||
|
self.assertTrue(manager.load('StbTrueTypeFont') & pluginmanager.LoadState.LOADED) |
||||||
|
self.assertEqual(manager.unload('StbTrueTypeFont'), pluginmanager.LoadState.NOT_LOADED) |
||||||
|
|
||||||
|
with self.assertRaisesRegex(RuntimeError, "can't load plugin"): |
||||||
|
manager.load('NonexistentFont') |
||||||
|
with self.assertRaisesRegex(RuntimeError, "can't unload plugin"): |
||||||
|
manager.unload('NonexistentFont') |
||||||
|
|
||||||
|
def test_no_file_opened(self): |
||||||
|
font = text.FontManager().load_and_instantiate('StbTrueTypeFont') |
||||||
|
self.assertFalse(font.is_opened) |
||||||
|
|
||||||
|
with self.assertRaisesRegex(AssertionError, "no file opened"): |
||||||
|
font.size |
||||||
|
with self.assertRaisesRegex(AssertionError, "no file opened"): |
||||||
|
font.ascent |
||||||
|
with self.assertRaisesRegex(AssertionError, "no file opened"): |
||||||
|
font.descent |
||||||
|
with self.assertRaisesRegex(AssertionError, "no file opened"): |
||||||
|
font.line_height |
||||||
|
with self.assertRaisesRegex(AssertionError, "no file opened"): |
||||||
|
font.glyph_id('A') |
||||||
|
with self.assertRaisesRegex(AssertionError, "no file opened"): |
||||||
|
font.glyph_advance(0) |
||||||
|
# fill_glyph_cache() not tested as it needs a GL context; assuming it's |
||||||
|
# correct |
||||||
|
|
||||||
|
def test_open_failed(self): |
||||||
|
font = text.FontManager().load_and_instantiate('StbTrueTypeFont') |
||||||
|
|
||||||
|
with self.assertRaisesRegex(RuntimeError, "opening nonexistent.ttf failed"): |
||||||
|
font.open_file('nonexistent.ttf', 16.0) |
||||||
|
with self.assertRaisesRegex(RuntimeError, "opening data failed"): |
||||||
|
font.open_data(b'', 16.0) |
||||||
|
|
||||||
|
def test_open(self): |
||||||
|
manager = text.FontManager() |
||||||
|
manager_refcount = sys.getrefcount(manager) |
||||||
|
|
||||||
|
# Font references the manager to ensure it doesn't get GC'd before the |
||||||
|
# plugin instances |
||||||
|
font = manager.load_and_instantiate('StbTrueTypeFont') |
||||||
|
self.assertIs(font.manager, manager) |
||||||
|
self.assertEqual(sys.getrefcount(manager), manager_refcount + 1) |
||||||
|
|
||||||
|
font.open_file(os.path.join(os.path.dirname(__file__), 'Oxygen.ttf'), 16.0) |
||||||
|
self.assertTrue(font.is_opened) |
||||||
|
self.assertEqual(font.size, 16.0) |
||||||
|
self.assertEqual(font.ascent, 17.011186599731445) |
||||||
|
self.assertEqual(font.descent, -4.322147846221924) |
||||||
|
self.assertEqual(font.line_height, 21.33333396911621) |
||||||
|
self.assertEqual(font.glyph_id('A'), 36) |
||||||
|
self.assertEqual(font.glyph_advance(36), (11.7136, 0.0)) |
||||||
|
|
||||||
|
# Deleting the font should decrease manager refcount again |
||||||
|
del font |
||||||
|
self.assertEqual(sys.getrefcount(manager), manager_refcount) |
||||||
|
|
||||||
|
def test_open_data(self): |
||||||
|
font = text.FontManager().load_and_instantiate('StbTrueTypeFont') |
||||||
|
|
||||||
|
with open(os.path.join(os.path.dirname(__file__), 'Oxygen.ttf'), 'rb') as f: |
||||||
|
font.open_data(f.read(), 16.0) |
||||||
|
|
||||||
|
self.assertEqual(font.size, 16.0) |
||||||
@ -0,0 +1,75 @@ |
|||||||
|
# |
||||||
|
# This file is part of Magnum. |
||||||
|
# |
||||||
|
# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||||
|
# 2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
# |
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
# copy of this software and associated documentation files (the "Software"), |
||||||
|
# to deal in the Software without restriction, including without limitation |
||||||
|
# the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
# and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
# Software is furnished to do so, subject to the following conditions: |
||||||
|
# |
||||||
|
# The above copyright notice and this permission notice shall be included |
||||||
|
# in all copies or substantial portions of the Software. |
||||||
|
# |
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
# DEALINGS IN THE SOFTWARE. |
||||||
|
# |
||||||
|
|
||||||
|
import os |
||||||
|
import sys |
||||||
|
import unittest |
||||||
|
|
||||||
|
# setUpModule gets called before everything else, skipping if GL tests can't |
||||||
|
# be run |
||||||
|
from . import GLTestCase, setUpModule |
||||||
|
|
||||||
|
from corrade import pluginmanager |
||||||
|
from magnum import * |
||||||
|
from magnum import gl, text |
||||||
|
|
||||||
|
class GlyphCache(GLTestCase): |
||||||
|
def test(self): |
||||||
|
cache = text.GlyphCache((128, 128), (2, 2)) |
||||||
|
|
||||||
|
self.assertEqual(cache.texture_size, (128, 128)) |
||||||
|
self.assertEqual(cache.padding, (2, 2)) |
||||||
|
|
||||||
|
cache_refcount = sys.getrefcount(cache) |
||||||
|
|
||||||
|
# Returned texture references the cache to ensure it doesn't get GC'd |
||||||
|
# before the texture instances |
||||||
|
texture = cache.texture |
||||||
|
self.assertEqual(sys.getrefcount(cache), cache_refcount + 1) |
||||||
|
|
||||||
|
# Deleting the texture should decrease cache refcount again |
||||||
|
del texture |
||||||
|
self.assertEqual(sys.getrefcount(cache), cache_refcount) |
||||||
|
|
||||||
|
class DistanceFieldGlyphCache(GLTestCase): |
||||||
|
def test(self): |
||||||
|
cache = text.DistanceFieldGlyphCache((1024, 1024), (128, 128), 2) |
||||||
|
|
||||||
|
self.assertEqual(cache.texture_size, (1024, 1024)) |
||||||
|
self.assertEqual(cache.padding, (2, 2)) |
||||||
|
|
||||||
|
class Renderer2D(GLTestCase): |
||||||
|
def test(self): |
||||||
|
font = text.FontManager().load_and_instantiate('StbTrueTypeFont') |
||||||
|
font.open_file(os.path.join(os.path.dirname(__file__), 'Oxygen.ttf'), 16.0) |
||||||
|
|
||||||
|
cache = text.GlyphCache((128, 128)) |
||||||
|
font.fill_glyph_cache(cache, "hello") |
||||||
|
|
||||||
|
renderer = text.Renderer2D(font, cache, 1.0) |
||||||
|
renderer.reserve(16) |
||||||
|
renderer.render("hello") |
||||||
|
|
||||||
|
self.assertEqual(renderer.rectangle, Range2D((0.0625, -0.0625), (2.4807, 0.875))) |
||||||
@ -0,0 +1,181 @@ |
|||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||||
|
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <pybind11/pybind11.h> |
||||||
|
#include <Corrade/Containers/StringStl.h> /** @todo drop once we have our string casters */ |
||||||
|
#include <Magnum/ImageView.h> |
||||||
|
#include <Magnum/Text/AbstractFont.h> |
||||||
|
#include <Magnum/Text/DistanceFieldGlyphCache.h> |
||||||
|
#include <Magnum/Text/Renderer.h> |
||||||
|
|
||||||
|
#include "corrade/pluginmanager.h" |
||||||
|
#include "magnum/bootstrap.h" |
||||||
|
|
||||||
|
namespace magnum { |
||||||
|
|
||||||
|
namespace { |
||||||
|
|
||||||
|
/* For some reason having ...Args as the second (and not last) template
|
||||||
|
argument does not work. So I'm listing all variants used more than once. */ |
||||||
|
template<class R, R(Text::AbstractFont::*f)() const> R checkOpened(Text::AbstractFont& self) { |
||||||
|
if(!self.isOpened()) { |
||||||
|
PyErr_SetString(PyExc_AssertionError, "no file opened"); |
||||||
|
throw py::error_already_set{}; |
||||||
|
} |
||||||
|
return (self.*f)(); |
||||||
|
} |
||||||
|
template<class R, class Arg1, R(Text::AbstractFont::*f)(Arg1)> R checkOpened(Text::AbstractFont& self, Arg1 arg1) { |
||||||
|
if(!self.isOpened()) { |
||||||
|
PyErr_SetString(PyExc_AssertionError, "no file opened"); |
||||||
|
throw py::error_already_set{}; |
||||||
|
} |
||||||
|
return (self.*f)(arg1); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void text(py::module_& m) { |
||||||
|
m.doc() = "Text rendering"; |
||||||
|
|
||||||
|
/* AbstractFont depends on this */ |
||||||
|
py::module_::import("corrade.pluginmanager"); |
||||||
|
|
||||||
|
#ifndef MAGNUM_BUILD_STATIC |
||||||
|
/* These are a part of the same module in the static build, no need to
|
||||||
|
import (also can't import because there it's _magnum.*) */ |
||||||
|
py::module_::import("magnum.gl"); |
||||||
|
#endif |
||||||
|
|
||||||
|
/* Glyph caches */ |
||||||
|
py::class_<Text::AbstractGlyphCache> abstractGlyphCache{m, "AbstractGlyphCache", "Base for glyph caches"}; |
||||||
|
abstractGlyphCache |
||||||
|
/** @todo features */ |
||||||
|
.def_property_readonly("texture_size", &Text::AbstractGlyphCache::textureSize, "Glyph cache texture size") |
||||||
|
.def_property_readonly("padding", &Text::AbstractGlyphCache::padding, "Glyph padding") |
||||||
|
/** @todo glyph iteration and population */ |
||||||
|
; |
||||||
|
py::class_<Text::GlyphCache, Text::AbstractGlyphCache> glyphCache{m, "GlyphCache", "Glyph cache"}; |
||||||
|
glyphCache |
||||||
|
.def(py::init<GL::TextureFormat, const Vector2i&, const Vector2i&, const Vector2i&>(), "Constructor", py::arg("internal_format"), py::arg("original_size"), py::arg("size"), py::arg("padding")) |
||||||
|
.def(py::init<GL::TextureFormat, const Vector2i&, const Vector2i&>(), "Constructor", py::arg("internal_format"), py::arg("size"), py::arg("padding") = Vector2i{}) |
||||||
|
.def(py::init<const Vector2i&, const Vector2i&, const Vector2i&>(), "Constructor", py::arg("original_size"), py::arg("size"), py::arg("padding")) |
||||||
|
.def(py::init<const Vector2i&, const Vector2i&>(), "Constructor", py::arg("size"), py::arg("padding") = Vector2i{}) |
||||||
|
/* The default behavior when returning a reference seems to be that it
|
||||||
|
increfs the originating instance and decrefs it again after the |
||||||
|
variable gets deleted. This is verified in test_text_gl.py to be |
||||||
|
extra sure. */ |
||||||
|
.def_property_readonly("texture", &Text::GlyphCache::texture, "Cache texture"); |
||||||
|
py::class_<Text::DistanceFieldGlyphCache, Text::GlyphCache> distanceFieldGlyphCache{m, "DistanceFieldGlyphCache", "Glyph cache with distance field rendering"}; |
||||||
|
distanceFieldGlyphCache |
||||||
|
.def(py::init<const Vector2i&, const Vector2i&, UnsignedInt>(), "Constructor", py::arg("original_size"), py::arg("size"), py::arg("radius")) |
||||||
|
/** @todo setDistanceFieldImage, once needed for anything */ |
||||||
|
; |
||||||
|
|
||||||
|
/* Font */ |
||||||
|
py::class_<Text::AbstractFont, PluginManager::PyPluginHolder<Text::AbstractFont>> abstractFont{m, "AbstractFont", "Interface for font plugins"}; |
||||||
|
abstractFont |
||||||
|
/** @todo features */ |
||||||
|
.def_property_readonly("is_opened", &Text::AbstractFont::isOpened, "Whether any file is opened") |
||||||
|
.def("open_data", [](Text::AbstractFont& self, Containers::ArrayView<const char> data, Float size) { |
||||||
|
/** @todo log redirection -- but we'd need assertions to not be
|
||||||
|
part of that so when it dies, the user can still see why */ |
||||||
|
if(self.openData(data, size)) return; |
||||||
|
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "opening data failed"); |
||||||
|
throw py::error_already_set{}; |
||||||
|
}, "Open raw data", py::arg("data"), py::arg("size")) |
||||||
|
.def("open_file", [](Text::AbstractFont& self, const std::string& filename, Float size) { |
||||||
|
/** @todo log redirection -- but we'd need assertions to not be
|
||||||
|
part of that so when it dies, the user can still see why */ |
||||||
|
if(self.openFile(filename, size)) return; |
||||||
|
|
||||||
|
PyErr_Format(PyExc_RuntimeError, "opening %s failed", filename.data()); |
||||||
|
throw py::error_already_set{}; |
||||||
|
}, "Open a file", py::arg("filename"), py::arg("size")) |
||||||
|
.def("close", &Text::AbstractFont::close, "Close currently opened file") |
||||||
|
|
||||||
|
.def_property_readonly("size", checkOpened<Float, &Text::AbstractFont::size>, "Font size") |
||||||
|
.def_property_readonly("ascent", checkOpened<Float, &Text::AbstractFont::ascent>, "Font ascent") |
||||||
|
.def_property_readonly("descent", checkOpened<Float, &Text::AbstractFont::descent>, "Font descent") |
||||||
|
.def_property_readonly("line_height", checkOpened<Float, &Text::AbstractFont::lineHeight>, "Line height") |
||||||
|
.def("glyph_id", checkOpened<UnsignedInt, char32_t, &Text::AbstractFont::glyphId>, "Glyph ID for given character", py::arg("character")) |
||||||
|
.def("glyph_advance", checkOpened<Vector2, UnsignedInt, &Text::AbstractFont::glyphAdvance>, "Glyph advance", py::arg("glyph")) |
||||||
|
.def("fill_glyph_cache", [](Text::AbstractFont& self, Text::AbstractGlyphCache& cache, const std::string& characters) { |
||||||
|
if(!self.isOpened()) { |
||||||
|
PyErr_SetString(PyExc_AssertionError, "no file opened"); |
||||||
|
throw py::error_already_set{}; |
||||||
|
} |
||||||
|
return self.fillGlyphCache(cache, characters); |
||||||
|
}, "Fill glyph cache with given character set", py::arg("cache"), py::arg("characters")) |
||||||
|
/** @todo createGlyphCache() */ |
||||||
|
/** @todo layout and AbstractLayouter, once needed for anything */ |
||||||
|
; |
||||||
|
corrade::plugin(abstractFont); |
||||||
|
|
||||||
|
py::class_<PluginManager::Manager<Text::AbstractFont>, PluginManager::AbstractManager> fontManager{m, "FontManager", "Manager for font plugins"}; |
||||||
|
corrade::manager(fontManager); |
||||||
|
|
||||||
|
py::enum_<Text::Alignment>{m, "Alignment", "Text rendering alignment"} |
||||||
|
.value("LINE_LEFT", Text::Alignment::LineLeft) |
||||||
|
.value("LINE_CENTER", Text::Alignment::LineCenter) |
||||||
|
.value("LINE_RIGHT", Text::Alignment::LineRight) |
||||||
|
.value("MIDDLE_LEFT", Text::Alignment::MiddleLeft) |
||||||
|
.value("MIDDLE_CENTER", Text::Alignment::MiddleCenter) |
||||||
|
.value("MIDDLE_RIGHT", Text::Alignment::MiddleRight) |
||||||
|
.value("TOP_LEFT", Text::Alignment::TopLeft) |
||||||
|
.value("TOP_CENTER", Text::Alignment::TopCenter) |
||||||
|
.value("TOP_RIGHT", Text::Alignment::TopRight) |
||||||
|
.value("LINE_CENTER_INTEGRAL", Text::Alignment::LineCenterIntegral) |
||||||
|
.value("MIDDLE_LEFT_INTEGRAL", Text::Alignment::MiddleLeftIntegral) |
||||||
|
.value("MIDDLE_CENTER_INTEGRAL", Text::Alignment::MiddleCenterIntegral) |
||||||
|
.value("MIDDLE_RIGHT_INTEGRAL", Text::Alignment::MiddleRightIntegral); |
||||||
|
|
||||||
|
/** @todo any reason to expose a 3D renderer? it isn't any different
|
||||||
|
currently */ |
||||||
|
py::class_<Text::Renderer2D> renderer2D{m, "Renderer2D", "2D text renderer"}; |
||||||
|
renderer2D |
||||||
|
.def(py::init<Text::AbstractFont&, const Text::GlyphCache&, Float, Text::Alignment>(), "Constructor", py::arg("font"), py::arg("cache"), py::arg("size"), py::arg("alignment") = Text::Alignment::LineLeft) |
||||||
|
.def_property_readonly("capacity", &Text::Renderer2D::capacity, "Capacity for rendered glyphs") |
||||||
|
.def_property_readonly("rectangle", &Text::Renderer2D::rectangle, "Rectangle spanning the rendered text") |
||||||
|
/** @todo are the buffers useful for anything? */ |
||||||
|
/* The default behavior when returning a reference seems to be that it
|
||||||
|
increfs the originating instance and decrefs it again after the |
||||||
|
variable gets deleted. This is verified in test_text.py to be extra |
||||||
|
sure. */ |
||||||
|
.def_property_readonly("mesh", &Text::Renderer2D::mesh, "Mesh") |
||||||
|
.def("reserve", &Text::Renderer2D::reserve, "Reserve capacity for renderered glyphs", py::arg("glyph_count"), py::arg("vertex_buffer_usage") = GL::BufferUsage::StaticDraw, py::arg("index_buffer_usage") = GL::BufferUsage::StaticDraw) |
||||||
|
.def("render", static_cast<void(Text::Renderer2D::*)(const std::string&)>(&Text::Renderer2D::render), "Render text", py::arg("text")); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
#ifndef MAGNUM_BUILD_STATIC |
||||||
|
/* TODO: remove declaration when https://github.com/pybind/pybind11/pull/1863
|
||||||
|
is released */ |
||||||
|
extern "C" PYBIND11_EXPORT PyObject* PyInit_text(); |
||||||
|
PYBIND11_MODULE(text, m) { |
||||||
|
magnum::text(m); |
||||||
|
} |
||||||
|
#endif |
||||||
Loading…
Reference in new issue