Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e7d7a49

Browse files
committed
Merge pull request #6792 from f0k/pgf-transform
ENH: PGF Backend: Support interpolation='none'
1 parent ac01d85 commit e7d7a49

File tree

3 files changed

+82
-39
lines changed

3 files changed

+82
-39
lines changed

examples/api/demo_affine_image.py

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
"""
2-
For the backends that supports draw_image with optional affine
2+
For the backends that support draw_image with optional affine
33
transform (e.g., agg, ps backend), the image of the output should
4-
have its boundary matches the red rectangles.
4+
have its boundary match the dashed yellow rectangle.
55
"""
66

77
import numpy as np
8-
import matplotlib.cm as cm
98
import matplotlib.mlab as mlab
109
import matplotlib.pyplot as plt
1110
import matplotlib.transforms as mtransforms
@@ -21,25 +20,37 @@ def get_image():
2120
return Z
2221

2322

24-
if 1:
23+
def do_plot(ax, Z, transform):
24+
im = ax.imshow(Z, interpolation='none',
25+
origin='lower',
26+
extent=[-2, 4, -3, 2], clip_on=True)
2527

26-
# image rotation
28+
trans_data = transform + ax.transData
29+
im.set_transform(trans_data)
2730

28-
fig, ax1 = plt.subplots(1, 1)
29-
Z = get_image()
30-
im1 = ax1.imshow(Z, interpolation='none',
31-
origin='lower',
32-
extent=[-2, 4, -3, 2], clip_on=True)
31+
# display intended extent of the image
32+
x1, x2, y1, y2 = im.get_extent()
33+
ax.plot([x1, x2, x2, x1, x1], [y1, y1, y2, y2, y1], "y--",
34+
transform=trans_data)
35+
ax.set_xlim(-5, 5)
36+
ax.set_ylim(-4, 4)
3337

34-
trans_data2 = mtransforms.Affine2D().rotate_deg(30) + ax1.transData
35-
im1.set_transform(trans_data2)
3638

37-
# display intended extent of the image
38-
x1, x2, y1, y2 = im1.get_extent()
39-
x3, y3 = x2, y1
39+
# prepare image and figure
40+
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2)
41+
Z = get_image()
42+
43+
# image rotation
44+
do_plot(ax1, Z, mtransforms.Affine2D().rotate_deg(30))
45+
46+
# image skew
47+
do_plot(ax2, Z, mtransforms.Affine2D().skew_deg(30, 15))
48+
49+
# scale and reflection
50+
do_plot(ax3, Z, mtransforms.Affine2D().scale(-1, .5))
4051

41-
ax1.plot([x1, x2, x2, x1, x1], [y1, y1, y2, y2, y1], "--",
42-
transform=trans_data2)
52+
# everything and a translation
53+
do_plot(ax4, Z, mtransforms.Affine2D().
54+
rotate_deg(30).skew_deg(30, 15).scale(-1, .5).translate(.5, -1))
4355

44-
ax1.set_xlim(-3, 5)
45-
ax1.set_ylim(-4, 4)
56+
plt.show()

lib/matplotlib/backend_bases.py

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -515,30 +515,34 @@ def get_image_magnification(self):
515515
"""
516516
return 1.0
517517

518-
def draw_image(self, gc, x, y, im, trans=None):
518+
def draw_image(self, gc, x, y, im, transform=None):
519519
"""
520-
Draw the image instance into the current axes;
520+
Draw an RGBA image.
521521
522522
*gc*
523-
a GraphicsContext containing clipping information
523+
a :class:`GraphicsContextBase` instance with clipping information.
524524
525525
*x*
526-
is the distance in pixels from the left hand side of the canvas.
526+
the distance in physical units (i.e., dots or pixels) from the left
527+
hand side of the canvas.
527528
528529
*y*
529-
the distance from the origin. That is, if origin is
530-
upper, y is the distance from top. If origin is lower, y
531-
is the distance from bottom
530+
the distance in physical units (i.e., dots or pixels) from the
531+
bottom side of the canvas.
532532
533533
*im*
534534
An NxMx4 array of RGBA pixels (of dtype uint8).
535535
536-
*trans*
537-
If the concrete backend is written such that
538-
`option_scale_image` returns `True`, an affine
539-
transformation may also be passed to `draw_image`. The
540-
backend should apply the transformation to the image
541-
before applying the translation of `x` and `y`.
536+
*transform*
537+
If and only if the concrete backend is written such that
538+
:meth:`option_scale_image` returns ``True``, an affine
539+
transformation *may* be passed to :meth:`draw_image`. It takes the
540+
form of a :class:`~matplotlib.transforms.Affine2DBase` instance.
541+
The translation vector of the transformation is given in physical
542+
units (i.e., dots or pixels). Note that the transformation does not
543+
override `x` and `y`, and has to be applied *before* translating
544+
the result by `x` and `y` (this can be accomplished by adding `x`
545+
and `y` to the translation vector defined by `transform`).
542546
"""
543547
raise NotImplementedError
544548

@@ -551,8 +555,8 @@ def option_image_nocomposite(self):
551555

552556
def option_scale_image(self):
553557
"""
554-
override this method for renderers that support arbitrary
555-
scaling of image (most of the vector backend).
558+
override this method for renderers that support arbitrary affine
559+
transformations in :meth:`draw_image` (most vector backends).
556560
"""
557561
return False
558562

lib/matplotlib/backends/backend_pgf.py

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -609,9 +609,23 @@ def _pgf_path_draw(self, stroke=True, fill=False):
609609
actions.append("fill")
610610
writeln(self.fh, r"\pgfusepath{%s}" % ",".join(actions))
611611

612-
def draw_image(self, gc, x, y, im):
613-
# TODO: Almost no documentation for the behavior of this function.
614-
# Something missing?
612+
def option_scale_image(self):
613+
"""
614+
pgf backend supports affine transform of image.
615+
"""
616+
return True
617+
618+
def option_image_nocomposite(self):
619+
"""
620+
return whether to generate a composite image from multiple images on
621+
a set of axes
622+
"""
623+
return not rcParams['image.composite_image']
624+
625+
def draw_image(self, gc, x, y, im, transform=None):
626+
h, w = im.shape[:2]
627+
if w == 0 or h == 0:
628+
return
615629

616630
# save the images to png files
617631
path = os.path.dirname(self.fh.name)
@@ -623,9 +637,23 @@ def draw_image(self, gc, x, y, im):
623637
# reference the image in the pgf picture
624638
writeln(self.fh, r"\begin{pgfscope}")
625639
self._print_pgf_clip(gc)
626-
h, w = im.shape[:2]
627640
f = 1. / self.dpi # from display coords to inch
628-
writeln(self.fh, r"\pgftext[at=\pgfqpoint{%fin}{%fin},left,bottom]{\pgfimage[interpolate=true,width=%fin,height=%fin]{%s}}" % (x * f, y * f, w * f, h * f, fname_img))
641+
if transform is None:
642+
writeln(self.fh,
643+
r"\pgfsys@transformshift{%fin}{%fin}" % (x * f, y * f))
644+
w, h = w * f, h * f
645+
else:
646+
tr1, tr2, tr3, tr4, tr5, tr6 = transform.frozen().to_values()
647+
writeln(self.fh,
648+
r"\pgfsys@transformcm{%f}{%f}{%f}{%f}{%fin}{%fin}" %
649+
(tr1 * f, tr2 * f, tr3 * f, tr4 * f,
650+
(tr5 + x) * f, (tr6 + y) * f))
651+
w = h = 1 # scale is already included in the transform
652+
interp = str(transform is None).lower() # interpolation in PDF reader
653+
writeln(self.fh,
654+
r"\pgftext[left,bottom]"
655+
r"{\pgfimage[interpolate=%s,width=%fin,height=%fin]{%s}}" %
656+
(interp, w, h, fname_img))
629657
writeln(self.fh, r"\end{pgfscope}")
630658

631659
def draw_tex(self, gc, x, y, s, prop, angle, ismath="TeX!", mtext=None):

0 commit comments

Comments
 (0)