Skip to content

Commit

Permalink
Make the font cache a separate object (celiagg#74)
Browse files Browse the repository at this point in the history
This is a rather sizable refactor which aims to fix the [Windows] text rendering bugs introduced in PR celiagg#65.

Instead of creating `thread_local` globals in `Font` for management of glyph caching, I've separated out the font cache into a new `FontCache` object which exists independently of the `Canvas*` and `Font` objects. A font cache can be shared between any number of canvas instances, as long as one takes care not to use the cache simultaneously from multiple threads.

One big downside of this change is that it **breaks** the existing `Font` API by moving the `Font.string_width` method to `FontCache.width`. Therefore I intend to bump the package's major version number to 2, as an extra warning to downstream users. `Canvas*` classes also need to be passed a `FontCache` instance at construction time, but existing code can continue to work _without modification_ due to some Python wrappers in the package's `__init__` module.
  • Loading branch information
jwiggins authored Feb 24, 2021
1 parent 85a2962 commit fda66e2
Show file tree
Hide file tree
Showing 29 changed files with 795 additions and 343 deletions.
5 changes: 5 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
include celiagg/*.hxx
include celiagg/*.h
include celiagg/*.pxd
include celiagg/*.pxi
include celiagg/*.pyx
include celiagg/tests/data/*
include LICENSE
include README.rst
graft agg-svn/agg-2.4/include
Expand Down
118 changes: 108 additions & 10 deletions celiagg/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# The MIT License (MIT)
#
# Copyright (c) 2014-2016 WUSTL ZPLAB
# Copyright (c) 2016-2021 Celiagg Contributors
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
Expand All @@ -23,16 +24,113 @@
# Authors: Erik Hvatum <[email protected]>
# John Wiggins <[email protected]>

# flake8: noqa
from __future__ import absolute_import

from ._celiagg import HAS_TEXT
from . import _celiagg
from ._celiagg import (
AggError, BlendMode, BSpline, DrawingMode, Font, FontCacheType,
AggError, BSpline, BlendMode, DrawingMode, Font, FontCache, FontCacheType,
GradientSpread, GradientUnits, GraphicsState, Image, InnerJoin,
LinearGradientPaint, LineCap, LineJoin, RadialGradientPaint, Path,
PatternPaint, PatternStyle, PixelFormat, Rect, ShapeAtPoints, SolidPaint,
TextDrawingMode, Transform
LineCap, LineJoin, LinearGradientPaint, Path, PatternPaint, PatternStyle,
PixelFormat, RadialGradientPaint, Rect, ShapeAtPoints, SolidPaint,
TextDrawingMode, Transform,
)

# Query the library
HAS_TEXT = _celiagg.has_text_rendering()

# Be explicit
__all__ = [
'HAS_TEXT',

'AggError', 'BlendMode', 'BSpline', 'DrawingMode', 'Font', 'FontCache',
'FontCacheType', 'GradientSpread', 'GradientUnits', 'GraphicsState',
'Image', 'InnerJoin', 'LinearGradientPaint', 'LineCap', 'LineJoin',
'RadialGradientPaint', 'Path', 'PatternPaint', 'PatternStyle',
'PixelFormat', 'Rect', 'ShapeAtPoints', 'SolidPaint', 'TextDrawingMode',
'Transform',

'CanvasG8', 'CanvasGA16', 'CanvasRGB24', 'CanvasRGBA32', 'CanvasBGRA32',
'CanvasRGBA128',
]

# Keep a font cache for callers that don't want to mess with it
__global_font_cache = None

_canvas_doc_string = """{klass_name}(array, bottom_up=False, font_cache=None)
Provides AGG (Anti-Grain Geometry) drawing routines that render to the
numpy array passed as the constructor argument. Because this array is
modified in place, it must be of type ``{array_type}``, must be
C-contiguous, and must be {channel_desc}.
:param array: A ``{array_type}`` array with shape {array_shape}.
:param bottom_up: If True, the origin is the bottom left, instead of top-left
:param font_cache: A ``FontCache`` instance. Defaults to a global instance.
"""


def _use_global_cache():
""" Return the global ``FontCache`` instance.
"""
global __global_font_cache
if __global_font_cache is None:
__global_font_cache = FontCache()
return __global_font_cache


def _build_canvas_factory(klass_name, array_type, array_shape, channel_desc):
""" Generate a Canvas factory.
This is done to preserve the v1.0.0 interface of canvas class constructors.
Using these factory functions keeps the ``font_cache`` parameter optional.
"""
klass = getattr(_celiagg, klass_name)

def factory(array, bottom_up=False, font_cache=None):
cache = _use_global_cache() if font_cache is None else font_cache
return klass(array, cache, bottom_up=bottom_up)

factory.__doc__ = _canvas_doc_string.format(
klass_name=klass_name,
array_type=array_type,
array_shape=array_shape,
channel_desc=channel_desc,
)
factory.__name__ = klass_name
return factory


# Generate the canvas classes
CanvasG8 = _build_canvas_factory(
'CanvasG8',
'numpy.uint8',
'(H, W)',
'MxN (1 channel: intensity)',
)
CanvasGA16 = _build_canvas_factory(
'CanvasGA16',
'numpy.uint8',
'(H, W, 2)',
'MxNx2 (2 channels: intensity and alpha)',
)
CanvasRGB24 = _build_canvas_factory(
'CanvasRGB24',
'numpy.uint8',
'(H, W, 3',
'MxNx3 (3 channels: red, green, and blue)',
)
CanvasRGBA32 = _build_canvas_factory(
'CanvasRGBA32',
'numpy.uint8',
'(H, W, 4)',
'MxNx4 (4 channels: red, green, blue, and alpha)',
)
CanvasBGRA32 = _build_canvas_factory(
'CanvasBGRA32',
'numpy.uint8',
'(H, W, 4)',
'MxNx4 (4 channels: blue, green, red, and alpha)',
)
CanvasRGBA128 = _build_canvas_factory(
'CanvasRGBA128',
'numpy.float32',
'(H, W, 4)',
'MxNx4 (2 channels: red, green, blue, and alpha)',
)
from ._celiagg import (CanvasG8, CanvasGA16, CanvasRGB24, CanvasRGBA32,
CanvasBGRA32, CanvasRGBA128)
16 changes: 10 additions & 6 deletions celiagg/_celiagg.pyx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# The MIT License (MIT)
#
# Copyright (c) 2014 WUSTL ZPLAB
# Copyright (c) 2016-2021 Celiagg Contributors
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
Expand Down Expand Up @@ -30,25 +31,27 @@ cimport numpy
import numpy

cimport _enums
cimport _font_cache
cimport _font
cimport _graphics_state
cimport _image
cimport _ndarray_canvas
cimport _paint
cimport _vertex_source
cimport _text_support
cimport _transform

# This is a global flag which tells you if text rendering support was compiled
IF _ENABLE_TEXT_RENDERING:
HAS_TEXT = True
ELSE:
HAS_TEXT = False


class AggError(Exception):
pass


def has_text_rendering():
""" Returns True if text rendering is available
"""
return _text_support._has_text_rendering()


cdef _get_utf8_text(text, exp_msg):
# Ensure UTF-8 encoded text is passed to C++ code.
if isinstance(text, unicode):
Expand All @@ -60,6 +63,7 @@ cdef _get_utf8_text(text, exp_msg):

include "enums.pxi"
include "font.pxi"
include "font_cache.pxi"
include "graphics_state.pxi"
include "image.pxi"
include "ndarray_canvas.pxi"
Expand Down
10 changes: 6 additions & 4 deletions celiagg/_font.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ cdef extern from "font.h":
unsigned face_index)

_enums.FontCacheType cache_type() const
unsigned face_index() const
const char* filepath() const
bool flip() const
void flip(bool flip)
double height() const
void height(double height)
bool hinting() const
void hinting(bool hint)
double height() const
const char* filepath() const
double string_width(char* str)
unsigned face_index() const
34 changes: 34 additions & 0 deletions celiagg/_font_cache.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# The MIT License (MIT)
#
# Copyright (c) 2016-2021 Celiagg Contributors
#
# 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.
#
# Authors: John Wiggins

cimport _font
cimport _transform


cdef extern from "font_cache.h":
cdef cppclass FontCache:
FontCache()

void activate(_font.Font& font, _transform.trans_affine& transform);
double measure_width(char* str)
2 changes: 2 additions & 0 deletions celiagg/_ndarray_canvas.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ cimport numpy
import numpy
from libcpp cimport bool

cimport _font_cache
cimport _font
cimport _graphics_state
cimport _image
Expand Down Expand Up @@ -77,4 +78,5 @@ cdef extern from "ndarray_canvas.h":
const unsigned height,
const int stride,
const size_t channel_count,
_font_cache.FontCache& cache,
const bool bottom_up)
28 changes: 28 additions & 0 deletions celiagg/_text_support.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# The MIT License (MIT)
#
# Copyright (c) 2016-2021 Celiagg Contributors
#
# 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.
#
# Authors: John Wiggins

from libcpp cimport bool

cdef extern from "text_support.h":
cdef bool _has_text_rendering()
Loading

0 comments on commit fda66e2

Please sign in to comment.