8000 Support customizing antialiasing for text and annotation · matplotlib/matplotlib@8890c34 · GitHub
[go: up one dir, main page]

Skip to content

Commit 8890c34

Browse files
stevezhangAlain
authored andcommitted
Support customizing antialiasing for text and annotation
1 parent 63c97a2 commit 8890c34

File tree

7 files changed

+177
-6
lines changed
  • tests
  • 7 files changed

    +177
    -6
    lines changed
    Lines changed: 29 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -0,0 +1,29 @@
    1+
    Default antialiasing behavior changes for ``Text`` and ``Annotation``
    2+
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    3+
    4+
    ``matplotlib.pyplot.annotate()`` and ``matplotlib.pyplot.text()`` now support parameter ``antialiased`` when initializing.
    5+
    Examples:
    6+
    7+
    .. code-block::
    8+
    9+
    mpl.text.Text(.5, .5, "foo\nbar", antialiased=True)
    10+
    plt.text(0.5, 0.5, '6 inches x 2 inches', antialiased=True)
    11+
    ax.annotate('local max', xy=(2, 1), xytext=(3, 1.5), antialiased=False)
    12+
    13+
    See "What's New" for more details on usage.
    14+
    15+
    With this new feature, you may want to make sure that you are creating and saving/showing the figure under the same context::
    16+
    17+
    # previously this was a no-op, now it is what works
    18+
    with rccontext(text.antialiased=False):
    19+
    fig, ax = plt.subplots()
    20+
    ax.annotate('local max', xy=(2, 1), xytext=(3, 1.5))
    21+
    fig.savefig('/tmp/test.png')
    22+
    23+
    # previously this had an effect, now this is a no-op
    24+
    fig, ax = plt.subplots()
    25+
    ax.annotate('local max', xy=(2, 1), xytext=(3, 1.5))
    26+
    with rccontext(text.antialiased=False):
    27+
    fig.savefig('/tmp/test.png')
    28+
    29+
    Also note that antialiasing for tick labeles will be set with ``rcParams['text.antialiased']`` when they are created (usually when a ``Figure`` is created) - This means antialiasing for them can no longer be changed by modifying ``rcParams['text.antialiased']``.
    Lines changed: 39 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -0,0 +1,39 @@
    1+
    Support customizing antialiasing for text and annotation
    2+
    --------------------------------------------------------
    3+
    ``matplotlib.pyplot.annotate()`` and ``matplotlib.pyplot.text()`` now support parameter ``antialiased``.
    4+
    When ``antialiased`` is set to ``True``, antialiasing will be applied to the text.
    5+
    When ``antialiased`` is set to ``False``, antialiasing will not be applied to the text.
    6+
    When ``antialiased`` is not specified, antialiasing will be set by ``rcParams['text.antialiased']`` at the creation time of ``Text`` and ``Annotation`` object.
    7+
    Examples:
    8+
    9+
    .. code-block::
    10+
    11+
    mpl.text.Text(.5, .5, "foo\nbar", antialiased=True)
    12+
    plt.text(0.5, 0.5, '6 inches x 2 inches', antialiased=True)
    13+
    ax.annotate('local max', xy=(2, 1), xytext=(3, 1.5), antialiased=False)
    14+
    15+
    If the text contains math expression, then anaialiasing will be set by ``rcParams['text.antialiased']``, and ``antialiased`` will have no effect
    16+
    This applies to the whole text.
    17+
    Examples:
    18+
    19+
    .. code-block::
    20+
    21+
    # no part will be antialiased for the text below
    22+
    plt.text(0.5, 0.25, r"$I'm \sqrt{x}$", antialiased=False)
    23+
    24+
    Also note that antialiasing for tick labeles will be set with ``rcParams['text.antialiased']`` when they are created (usually when a ``Figure`` is created) and cannot be changed afterwards.
    25+
    26+
    Furthermore, with this new feature, you may want to make sure that you are creating and saving/showing the figure under the same context::
    27+
    28+
    # previously this was a no-op, now it is what works
    29+
    with rccontext(text.antialiased=False):
    30+
    fig, ax = plt.subplots()
    31+
    ax.annotate('local max', xy=(2, 1), xytext=(3, 1.5))
    32+
    fig.savefig('/tmp/test.png')
    33+
    34+
    35+
    # previously this had an effect, now this is a no-op
    36+
    fig, ax = plt.subplots()
    37+
    ax.annotate('local max', xy=(2, 1), xytext=(3, 1.5))
    38+
    with rccontext(text.antialiased=False):
    39+
    fig.savefig('/tmp/test.png')

    lib/matplotlib/backends/backend_agg.py

    Lines changed: 1 addition & 1 deletion
    Original file line numberDiff line numberDiff line change
    @@ -206,7 +206,7 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
    206206
    # space) in the following call to draw_text_image).
    207207
    font.set_text(s, 0, flags=get_hinting_flag())
    208208
    font.draw_glyphs_to_bitmap(
    209-
    antialiased=mpl.rcParams['text.antialiased'])
    209+
    antialiased=gc.get_antialiased())
    210210
    d = font.get_descent() / 64.0
    211211
    # The descent needs to be adjusted for the angle.
    212212
    xo, yo = font.get_bitmap_offset()

    lib/matplotlib/backends/backend_cairo.py

    Lines changed: 4 additions & 4 deletions
    Original file line numberDiff line numberDiff line change
    @@ -25,7 +25,6 @@
    2525
    "cairo backend requires that pycairo>=1.14.0 or cairocffi "
    2626
    "is installed") from err
    2727

    28-
    import matplotlib as mpl
    2928
    from .. import _api, cbook, font_manager
    3029
    from matplotlib.backend_bases import (
    3130
    _Backend, FigureCanvasBase, FigureManagerBase, GraphicsContextBase,
    @@ -204,9 +203,7 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
    204203
    ctx.select_font_face(*_cairo_font_args_from_font_prop(prop))
    205204
    ctx.set_font_size(self.points_to_pixels(prop.get_size_in_points()))
    206205
    opts = cairo.FontOptions()
    207-
    opts.set_antialias(
    208-
    cairo.ANTIALIAS_DEFAULT if mpl.rcParams["text.antialiased"]
    209-
    else cairo.ANTIALIAS_NONE)
    206+
    opts.set_antialias(gc.get_antialiased())
    210207
    ctx.set_font_options(opts)
    211208
    if angle:
    212209
    ctx.rotate(np.deg2rad(-angle))
    @@ -312,6 +309,9 @@ def set_antialiased(self, b):
    312309
    self.ctx.set_antialias(
    313310
    cairo.ANTIALIAS_DEFAULT if b else cairo.ANTIALIAS_NONE)
    314311

    312+
    def get_antialiased(self):
    313+
    return self.ctx.get_antialias()
    314+
    315315
    def set_capstyle(self, cs):
    316316
    self.ctx.set_line_cap(_api.check_getitem(self._capd, capstyle=cs))
    317317
    self._capstyle = cs

    lib/matplotlib/tests/test_text.py

    Lines changed: 72 additions & 1 deletion
    Original file line numberDiff line numberDiff line change
    @@ -14,7 +14,7 @@
    1414
    import matplotlib.transforms as mtransforms
    1515
    from matplotlib.testing.decorators import check_figures_equal, image_comparison
    1616
    from matplotlib.testing._markers import needs_usetex
    17-
    from matplotlib.text import Text
    17+
    from matplotlib.text import Text, Annotation
    1818

    1919

    2020
    @image_comparison(['font_styles'])
    @@ -886,3 +886,74 @@ def call(*args, **kwargs):
    886886
    # Every string gets a miss for the first layouting (extents), then a hit
    887887
    # when drawing, but "foo\nbar" gets two hits as it's drawn twice.
    888888
    assert info.hits > info.misses
    889+
    890+
    891+
    def test_set_antialiased():
    892+
    txt = Text(.5, .5, "foo\nbar")
    893+
    assert txt._antialiased == mpl.rcParams['text.antialiased']
    894+
    895+
    txt.set_antialiased(True)
    896+
    assert txt._antialiased is True
    897+
    898+
    txt.set_antialiased(False)
    899+
    assert txt._antialiased is False
    900+
    901+
    902+
    def test_get_antialiased():
    903+
    904+
    txt2 = Text(.5, .5, "foo\nbar", antialiased=True)
    905+
    assert txt2._antialiased is True
    906+
    assert txt2.get_antialiased() == txt2._antialiased
    907+
    908+
    txt3 = Text(.5, .5, "foo\nbar", antialiased=False)
    909+
    assert txt3._antialiased is False
    910+
    assert txt3.get_antialiased() == txt3._antialiased
    911+
    912+
    txt4 = Text(.5, .5, "foo\nbar")
    913+
    assert txt4.get_antialiased() == mpl.rcParams['text.antialiased']
    914+
    915+
    916+
    def test_annotation_antialiased():
    917+
    annot = Annotation("foo\nbar", (.5, .5), antialiased=True)
    918+
    assert annot._antialiased is True
    919+
    assert annot.get_antialiased() == annot._antialiased
    920+
    921+
    annot2 = Annotation("foo\nbar", (.5, .5), antialiased=False)
    922+
    assert annot2._antialiased is False
    923+
    assert annot2.get_antialiased() == annot2._antialiased
    924+
    925+
    annot3 = Annotation("foo\nbar", (.5, .5), antialiased=False)
    926+
    annot3.set_antialiased(True)
    927+
    assert annot3.get_antialiased() is True
    928+
    assert annot3._antialiased is True
    929+
    930+
    annot4 = Annotation("foo\nbar", (.5, .5))
    931+
    assert annot4._antialiased == mpl.rcParams['text.antialiased']
    932+
    933+
    934+
    @check_figures_equal()
    935+
    def test_text_antialiased_off_default_vs_manual(fig_test, fig_ref):
    936+
    # note that the field antialiased is for Text object
    937+
    # therefore in the test we will make axis antialiasing identical
    938+
    # which is determined by rcParams['text.antialiased'] when axis is created
    939+
    # also note that there is no way to change this for axis after creating it
    940+
    941+
    mpl.rcParams['text.antialiased'] = False
    942+
    # axis: antialiased == rcParams['text.antialiased'] == False
    943+
    # text: antialiased == False
    944+
    fig_test.subplots().text(0.5, 0.5, '6 inches x 2 inches',
    945+
    antialiased=False)
    946+
    947+
    mpl.rcParams['text.antialiased'] = False
    948+
    # axis: antialiased == rcParams['text.antialiased'] == False
    949+
    # text: antialiased == rcParams['text.antialiased'] == False
    950+
    fig_ref.subplots().text(0.5, 0.5, '6 inches x 2 inches')
    951+
    952+
    953+
    @check_figures_equal()
    954+
    def test_text_antialiased_on_default_vs_manual(fig_test, fig_ref):
    955+
    mpl.rcParams['text.antialiased'] = True
    956+
    fig_test.subplots().text(0.5, 0.5, '6 inches x 2 inches', antialiased=True)
    957+
    958+
    mpl.rcParams['text.antialiased'] = True
    959+
    fig_ref.subplots().text(0.5, 0.5, '6 inches x 2 inches')

    lib/matplotlib/text.py

    Lines changed: 29 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -115,6 +115,7 @@ def __init__(self,
    115115
    wrap=False,
    116116
    transform_rotates_text=False,
    117117
    parse_math=None, # defaults to rcParams['text.parse_math']
    118+
    antialiased=None, # defaults to rcParams['text.antialiased']
    118119
    **kwargs
    119120
    ):
    120121
    """
    @@ -135,6 +136,7 @@ def __init__(self,
    135136
    super().__init__()
    136137
    self._x, self._y = x, y
    137138
    self._text = ''
    139+
    self._antialiased = mpl.rcParams['text.antialiased']
    138140
    self._reset_visual_defaults(
    139141
    text=text,
    140142
    color=color,
    @@ -149,6 +151,7 @@ def __init__(self,
    149151
    transform_rotates_text=transform_rotates_text,
    150152
    linespacing=linespacing,
    151153
    rotation_mode=rotation_mode,
    154+
    antialiased=antialiased
    152155
    )
    153156
    self.update(kwargs)
    154157

    @@ -167,6 +170,7 @@ def _reset_visual_defaults(
    167170
    transform_rotates_text=False,
    168171
    linespacing=None,
    169172
    rotation_mode=None,
    173+
    antialiased=None
    170174
    ):
    171175
    self.set_text(text)
    172176
    self.set_color(
    @@ -187,6 +191,8 @@ def _reset_visual_defaults(
    187191
    linespacing = 1.2 # Maybe use rcParam later.
    188192
    self.set_linespacing(linespacing)
    189193
    self.set_rotation_mode(rotation_mode)
    194+
    if antialiased is not None:
    195+
    self.set_antialiased(antialiased)
    190196

    191197
    def update(self, kwargs):
    192198
    # docstring inherited
    @@ -309,6 +315,27 @@ def get_rotation_mode(self):
    309315
    """Return the text rotation mode."""
    310316
    return self._rotation_mode
    311317

    318+
    def set_antialiased(self, b):
    319+
    """
    320+
    Set whether to use antialiased rendering.
    321+
    322+
    Parameters
    323+
    ----------
    324+
    b : bool
    325+
    326+
    Notes
    327+
    -----
    328+
    Antialiasing will be determined by ``rcParams['text.antialiased']``
    329+
    and parameter ``antialiased`` will have no effect if the text contains
    330+
    math expressions.
    331+
    """
    332+
    self._antialiased = b
    333+
    self.stale = True
    334+
    335+
    def get_antialiased(self):
    336+
    """Return whether antialiased rendering is used."""
    337+
    return self._antialiased
    338+
    312339
    def update_from(self, other):
    313340
    # docstring inherited
    314341
    super().update_from(other)
    @@ -322,6 +349,7 @@ def update_from(self, other):
    322349
    self._transform_rotates_text = other._transform_rotates_text
    323350
    self._picker = other._picker
    324351
    self._linespacing = other._linespacing
    352+
    self._antialiased = other._antialiased
    325353
    self.stale = True
    326354

    327355
    def _get_layout(self, renderer):
    @@ -737,6 +765,7 @@ def draw(self, renderer):
    737765
    gc.set_foreground(self.get_color())
    738766
    gc.set_alpha(self.get_alpha())
    739767
    gc.set_url(self._url)
    768+
    gc.set_antialiased(self._antialiased)
    740769
    self._set_gc_clip(gc)
    741770

    742771
    angle = self.get_rotation()

    lib/matplotlib/text.pyi

    Lines changed: 3 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -41,6 +41,7 @@ class Text(Artist):
    4141
    wrap: bool = ...,
    4242
    transform_rotates_text: bool = ...,
    4343
    parse_math: bool | None = ...,
    44+
    antialiased: bool | None = ...,
    4445
    **kwargs
    4546
    ) -> None: ...
    4647
    def update(self, kwargs: dict[str, Any]) -> None: ...
    @@ -104,6 +105,8 @@ class Text(Artist):
    104105
    def set_parse_math(self, parse_math: bool) -> None: ...
    105106
    def get_parse_math(self) -> bool: ...
    106107
    def set_fontname(self, fontname: str | Iterable[str]): ...
    108+
    def get_antialiased(self) -> bool: ...
    109+
    def set_antialiased(self, b: bool): ...
    107110

    108111
    class OffsetFrom:
    109112
    def __init__(

    0 commit comments

    Comments
     (0)
    0