Skip to content

Commit 8c852e4

Browse files
authored
Merge pull request #5349 from latosha-maltba/master
2 parents 682e3e2 + 0f8fffb commit 8c852e4

File tree

5 files changed

+56
-20
lines changed

5 files changed

+56
-20
lines changed

Tests/helper.py

+17-10
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,23 @@ def netpbm_available():
257257
return bool(shutil.which("ppmquant") and shutil.which("ppmtogif"))
258258

259259

260-
def imagemagick_available():
261-
return bool(IMCONVERT and shutil.which(IMCONVERT))
260+
def magick_command():
261+
if sys.platform == "win32":
262+
magickhome = os.environ.get("MAGICK_HOME", "")
263+
if magickhome:
264+
imagemagick = [os.path.join(magickhome, "convert.exe")]
265+
graphicsmagick = [os.path.join(magickhome, "gm.exe"), "convert"]
266+
else:
267+
imagemagick = None
268+
graphicsmagick = None
269+
else:
270+
imagemagick = ["convert"]
271+
graphicsmagick = ["gm", "convert"]
272+
273+
if imagemagick and shutil.which(imagemagick[0]):
274+
return imagemagick
275+
elif graphicsmagick and shutil.which(graphicsmagick[0]):
276+
return graphicsmagick
262277

263278

264279
def on_appveyor():
@@ -296,14 +311,6 @@ def is_mingw():
296311
return sysconfig.get_platform() == "mingw"
297312

298313

299-
if sys.platform == "win32":
300-
IMCONVERT = os.environ.get("MAGICK_HOME", "")
301-
if IMCONVERT:
302-
IMCONVERT = os.path.join(IMCONVERT, "convert.exe")
303-
else:
304-
IMCONVERT = "convert"
305-
306-
307314
class cached_property:
308315
def __init__(self, func):
309316
self.func = func

Tests/test_file_palm.py

+6-10
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@
55

66
from PIL import Image
77

8-
from .helper import IMCONVERT, assert_image_equal, hopper, imagemagick_available
9-
10-
_roundtrip = imagemagick_available()
8+
from .helper import assert_image_equal, hopper, magick_command
119

1210

1311
def helper_save_as_palm(tmp_path, mode):
@@ -23,28 +21,26 @@ def helper_save_as_palm(tmp_path, mode):
2321
assert os.path.getsize(outfile) > 0
2422

2523

26-
def open_with_imagemagick(tmp_path, f):
27-
if not imagemagick_available():
28-
raise OSError()
29-
24+
def open_with_magick(magick, tmp_path, f):
3025
outfile = str(tmp_path / "temp.png")
3126
rc = subprocess.call(
32-
[IMCONVERT, f, outfile], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT
27+
magick + [f, outfile], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT
3328
)
3429
if rc:
3530
raise OSError
3631
return Image.open(outfile)
3732

3833

3934
def roundtrip(tmp_path, mode):
40-
if not _roundtrip:
35+
magick = magick_command()
36+
if not magick:
4137
return
4238

4339
im = hopper(mode)
4440
outfile = str(tmp_path / "temp.palm")
4541

4642
im.save(outfile)
47-
converted = open_with_imagemagick(tmp_path, outfile)
43+
converted = open_with_magick(magick, tmp_path, outfile)
4844
assert_image_equal(converted, im)
4945

5046

docs/reference/ImageShow.rst

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ All default viewers convert the image to be shown to PNG format.
1818
The following viewers may be registered on Unix-based systems, if the given command is found:
1919

2020
.. autoclass:: PIL.ImageShow.DisplayViewer
21+
.. autoclass:: PIL.ImageShow.GmDisplayViewer
2122
.. autoclass:: PIL.ImageShow.EogViewer
2223
.. autoclass:: PIL.ImageShow.XVViewer
2324

docs/releasenotes/8.2.0.rst

+21
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,18 @@ instances, so it will only be used by ``im.show()`` or :py:func:`.ImageShow.show
8383
if none of the other viewers are available. This means that the behaviour of
8484
:py:class:`PIL.ImageShow` will stay the same for most Pillow users.
8585

86+
ImageShow.GmDisplayViewer
87+
^^^^^^^^^^^^^^^^^^^^^^^^^
88+
89+
If GraphicsMagick is present, this new :py:class:`PIL.ImageShow.Viewer` subclass will
90+
be registered. It uses GraphicsMagick_, an ImageMagick_ fork, to display images.
91+
92+
The GraphicsMagick based viewer has a lower priority than its ImageMagick
93+
counterpart. Thus, if both ImageMagick and GraphicsMagick are installed,
94+
``im.show()`` and :py:func:`.ImageShow.show()` prefer the viewer based on
95+
ImageMagick, i.e the behaviour stays the same for Pillow users having
96+
ImageMagick installed.
97+
8698
Saving TIFF with ICC profile
8799
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
88100

@@ -143,3 +155,12 @@ PyQt6
143155

144156
Support has been added for PyQt6. If it is installed, it will be used instead of
145157
PySide6, PyQt5 or PySide2.
158+
159+
GraphicsMagick
160+
^^^^^^^^^^^^^^
161+
162+
The test suite can now be run on systems which have GraphicsMagick_ but not
163+
ImageMagick_ installed. If both are installed, the tests prefer ImageMagick.
164+
165+
.. _GraphicsMagick: http://www.graphicsmagick.org/
166+
.. _ImageMagick: https://imagemagick.org/

src/PIL/ImageShow.py

+11
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,15 @@ def get_command_ex(self, file, **options):
194194
return command, executable
195195

196196

197+
class GmDisplayViewer(UnixViewer):
198+
"""The GraphicsMagick ``gm display`` command."""
199+
200+
def get_command_ex(self, file, **options):
201+
executable = "gm"
202+
command = "gm display"
203+
return command, executable
204+
205+
197206
class EogViewer(UnixViewer):
198207
"""The GNOME Image Viewer ``eog`` command."""
199208

@@ -220,6 +229,8 @@ def get_command_ex(self, file, title=None, **options):
220229
if sys.platform not in ("win32", "darwin"): # unixoids
221230
if shutil.which("display"):
222231
register(DisplayViewer)
232+
if shutil.which("gm"):
233+
register(GmDisplayViewer)
223234
if shutil.which("eog"):
224235
register(EogViewer)
225236
if shutil.which("xv"):

0 commit comments

Comments
 (0)