2010-07-31 06:52:47 +04:00
|
|
|
#
|
|
|
|
# The Python Imaging Library.
|
|
|
|
# $Id$
|
|
|
|
#
|
|
|
|
# a Windows DIB display interface
|
|
|
|
#
|
|
|
|
# History:
|
|
|
|
# 1996-05-20 fl Created
|
|
|
|
# 1996-09-20 fl Fixed subregion exposure
|
|
|
|
# 1997-09-21 fl Added draw primitive (for tzPrint)
|
|
|
|
# 2003-05-21 fl Added experimental Window/ImageWindow classes
|
|
|
|
# 2003-09-05 fl Added fromstring/tostring methods
|
|
|
|
#
|
|
|
|
# Copyright (c) Secret Labs AB 1997-2003.
|
|
|
|
# Copyright (c) Fredrik Lundh 1996-2003.
|
|
|
|
#
|
|
|
|
# See the README file for information on usage and redistribution.
|
|
|
|
#
|
2023-12-21 14:13:31 +03:00
|
|
|
from __future__ import annotations
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2017-01-17 16:22:18 +03:00
|
|
|
from . import Image
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
|
2019-09-30 17:56:31 +03:00
|
|
|
class HDC:
|
2013-10-14 09:19:12 +04:00
|
|
|
"""
|
2014-07-06 01:43:22 +04:00
|
|
|
Wraps an HDC integer. The resulting object can be passed to the
|
2013-10-14 09:19:12 +04:00
|
|
|
:py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose`
|
|
|
|
methods.
|
|
|
|
"""
|
2019-03-21 16:28:20 +03:00
|
|
|
|
2024-05-18 09:06:50 +03:00
|
|
|
def __init__(self, dc: int) -> None:
|
2010-07-31 06:52:47 +04:00
|
|
|
self.dc = dc
|
2014-07-06 01:43:22 +04:00
|
|
|
|
2024-05-18 09:06:50 +03:00
|
|
|
def __int__(self) -> int:
|
2010-07-31 06:52:47 +04:00
|
|
|
return self.dc
|
|
|
|
|
2014-07-06 01:43:22 +04:00
|
|
|
|
2019-09-30 17:56:31 +03:00
|
|
|
class HWND:
|
2013-10-14 09:19:12 +04:00
|
|
|
"""
|
2014-07-06 01:43:22 +04:00
|
|
|
Wraps an HWND integer. The resulting object can be passed to the
|
2013-10-14 09:19:12 +04:00
|
|
|
:py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose`
|
|
|
|
methods, instead of a DC.
|
|
|
|
"""
|
2019-03-21 16:28:20 +03:00
|
|
|
|
2024-05-18 09:06:50 +03:00
|
|
|
def __init__(self, wnd: int) -> None:
|
2010-07-31 06:52:47 +04:00
|
|
|
self.wnd = wnd
|
2014-07-06 01:43:22 +04:00
|
|
|
|
2024-05-18 09:06:50 +03:00
|
|
|
def __int__(self) -> int:
|
2010-07-31 06:52:47 +04:00
|
|
|
return self.wnd
|
|
|
|
|
|
|
|
|
2019-09-30 17:56:31 +03:00
|
|
|
class Dib:
|
2013-10-14 09:19:12 +04:00
|
|
|
"""
|
|
|
|
A Windows bitmap with the given mode and size. The mode can be one of "1",
|
|
|
|
"L", "P", or "RGB".
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2013-10-14 09:19:12 +04:00
|
|
|
If the display requires a palette, this constructor creates a suitable
|
2023-10-19 11:12:01 +03:00
|
|
|
palette and associates it with the image. For an "L" image, 128 graylevels
|
2013-10-14 09:19:12 +04:00
|
|
|
are allocated. For an "RGB" image, a 6x6x6 colour cube is used, together
|
2023-10-19 11:12:01 +03:00
|
|
|
with 20 graylevels.
|
2013-10-14 09:19:12 +04:00
|
|
|
|
|
|
|
To make sure that palettes work properly under Windows, you must call the
|
2020-07-10 23:12:20 +03:00
|
|
|
``palette`` method upon certain events from Windows.
|
2013-10-14 09:19:12 +04:00
|
|
|
|
|
|
|
:param image: Either a PIL image, or a mode string. If a mode string is
|
|
|
|
used, a size must also be given. The mode can be one of "1",
|
|
|
|
"L", "P", or "RGB".
|
|
|
|
:param size: If the first argument is a mode string, this
|
|
|
|
defines the size of the image.
|
|
|
|
"""
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2024-06-24 14:04:33 +03:00
|
|
|
def __init__(
|
2024-07-15 12:23:36 +03:00
|
|
|
self, image: Image.Image | str, size: tuple[int, int] | None = None
|
2024-06-24 14:04:33 +03:00
|
|
|
) -> None:
|
|
|
|
if isinstance(image, str):
|
|
|
|
mode = image
|
|
|
|
image = ""
|
2024-07-15 12:23:36 +03:00
|
|
|
if size is None:
|
|
|
|
msg = "If first argument is mode, size is required"
|
|
|
|
raise ValueError(msg)
|
2024-06-24 14:04:33 +03:00
|
|
|
else:
|
2010-07-31 06:52:47 +04:00
|
|
|
mode = image.mode
|
|
|
|
size = image.size
|
|
|
|
if mode not in ["1", "L", "P", "RGB"]:
|
|
|
|
mode = Image.getmodebase(mode)
|
|
|
|
self.image = Image.core.display(mode, size)
|
|
|
|
self.mode = mode
|
|
|
|
self.size = size
|
|
|
|
if image:
|
2024-06-24 14:04:33 +03:00
|
|
|
assert not isinstance(image, str)
|
2010-07-31 06:52:47 +04:00
|
|
|
self.paste(image)
|
|
|
|
|
2024-07-20 06:14:18 +03:00
|
|
|
def expose(self, handle: int | HDC | HWND) -> None:
|
2013-10-14 09:19:12 +04:00
|
|
|
"""
|
|
|
|
Copy the bitmap contents to a device context.
|
|
|
|
|
2014-07-06 01:43:22 +04:00
|
|
|
:param handle: Device context (HDC), cast to a Python integer, or an
|
2020-07-14 15:37:03 +03:00
|
|
|
HDC or HWND instance. In PythonWin, you can use
|
|
|
|
``CDC.GetHandleAttrib()`` to get a suitable handle.
|
2013-10-14 09:19:12 +04:00
|
|
|
"""
|
2024-09-16 01:22:58 +03:00
|
|
|
handle_int = int(handle)
|
2010-07-31 06:52:47 +04:00
|
|
|
if isinstance(handle, HWND):
|
2024-09-16 01:22:58 +03:00
|
|
|
dc = self.image.getdc(handle_int)
|
2010-07-31 06:52:47 +04:00
|
|
|
try:
|
2024-07-20 06:14:18 +03:00
|
|
|
self.image.expose(dc)
|
2010-07-31 06:52:47 +04:00
|
|
|
finally:
|
2024-09-16 01:22:58 +03:00
|
|
|
self.image.releasedc(handle_int, dc)
|
2010-07-31 06:52:47 +04:00
|
|
|
else:
|
2024-09-16 01:22:58 +03:00
|
|
|
self.image.expose(handle_int)
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2024-07-15 12:23:36 +03:00
|
|
|
def draw(
|
|
|
|
self,
|
2024-07-20 06:14:18 +03:00
|
|
|
handle: int | HDC | HWND,
|
2024-07-15 12:23:36 +03:00
|
|
|
dst: tuple[int, int, int, int],
|
|
|
|
src: tuple[int, int, int, int] | None = None,
|
2024-07-20 06:14:18 +03:00
|
|
|
) -> None:
|
2013-10-14 09:19:12 +04:00
|
|
|
"""
|
|
|
|
Same as expose, but allows you to specify where to draw the image, and
|
|
|
|
what part of it to draw.
|
|
|
|
|
|
|
|
The destination and source areas are given as 4-tuple rectangles. If
|
|
|
|
the source is omitted, the entire image is copied. If the source and
|
|
|
|
the destination have different sizes, the image is resized as
|
|
|
|
necessary.
|
|
|
|
"""
|
2024-07-15 12:23:36 +03:00
|
|
|
if src is None:
|
2014-07-06 01:43:22 +04:00
|
|
|
src = (0, 0) + self.size
|
2024-09-16 01:22:58 +03:00
|
|
|
handle_int = int(handle)
|
2010-07-31 06:52:47 +04:00
|
|
|
if isinstance(handle, HWND):
|
2024-09-16 01:22:58 +03:00
|
|
|
dc = self.image.getdc(handle_int)
|
2010-07-31 06:52:47 +04:00
|
|
|
try:
|
2024-07-20 06:14:18 +03:00
|
|
|
self.image.draw(dc, dst, src)
|
2010-07-31 06:52:47 +04:00
|
|
|
finally:
|
2024-09-16 01:22:58 +03:00
|
|
|
self.image.releasedc(handle_int, dc)
|
2010-07-31 06:52:47 +04:00
|
|
|
else:
|
2024-09-16 01:22:58 +03:00
|
|
|
self.image.draw(handle_int, dst, src)
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2024-07-20 06:14:18 +03:00
|
|
|
def query_palette(self, handle: int | HDC | HWND) -> int:
|
2013-10-14 09:19:12 +04:00
|
|
|
"""
|
|
|
|
Installs the palette associated with the image in the given device
|
|
|
|
context.
|
|
|
|
|
|
|
|
This method should be called upon **QUERYNEWPALETTE** and
|
|
|
|
**PALETTECHANGED** events from Windows. If this method returns a
|
|
|
|
non-zero value, one or more display palette entries were changed, and
|
|
|
|
the image should be redrawn.
|
|
|
|
|
|
|
|
:param handle: Device context (HDC), cast to a Python integer, or an
|
|
|
|
HDC or HWND instance.
|
2024-07-20 06:14:18 +03:00
|
|
|
:return: The number of entries that were changed (if one or more entries,
|
|
|
|
this indicates that the image should be redrawn).
|
2013-10-14 09:19:12 +04:00
|
|
|
"""
|
2024-09-16 01:22:58 +03:00
|
|
|
handle_int = int(handle)
|
2010-07-31 06:52:47 +04:00
|
|
|
if isinstance(handle, HWND):
|
2024-09-16 01:22:58 +03:00
|
|
|
handle = self.image.getdc(handle_int)
|
2010-07-31 06:52:47 +04:00
|
|
|
try:
|
|
|
|
result = self.image.query_palette(handle)
|
|
|
|
finally:
|
|
|
|
self.image.releasedc(handle, handle)
|
|
|
|
else:
|
2024-09-16 01:22:58 +03:00
|
|
|
result = self.image.query_palette(handle_int)
|
2010-07-31 06:52:47 +04:00
|
|
|
return result
|
|
|
|
|
2024-05-18 09:06:50 +03:00
|
|
|
def paste(
|
|
|
|
self, im: Image.Image, box: tuple[int, int, int, int] | None = None
|
|
|
|
) -> None:
|
2013-10-14 09:19:12 +04:00
|
|
|
"""
|
|
|
|
Paste a PIL image into the bitmap image.
|
|
|
|
|
|
|
|
:param im: A PIL image. The size must match the target region.
|
|
|
|
If the mode does not match, the image is converted to the
|
|
|
|
mode of the bitmap image.
|
|
|
|
:param box: A 4-tuple defining the left, upper, right, and
|
2018-06-24 07:34:01 +03:00
|
|
|
lower pixel coordinate. See :ref:`coordinate-system`. If
|
|
|
|
None is given instead of a tuple, all of the image is
|
|
|
|
assumed.
|
2013-10-14 09:19:12 +04:00
|
|
|
"""
|
2010-07-31 06:52:47 +04:00
|
|
|
im.load()
|
|
|
|
if self.mode != im.mode:
|
|
|
|
im = im.convert(self.mode)
|
|
|
|
if box:
|
|
|
|
self.image.paste(im.im, box)
|
|
|
|
else:
|
|
|
|
self.image.paste(im.im)
|
|
|
|
|
2024-05-18 09:06:50 +03:00
|
|
|
def frombytes(self, buffer: bytes) -> None:
|
2013-10-14 09:19:12 +04:00
|
|
|
"""
|
|
|
|
Load display memory contents from byte data.
|
|
|
|
|
|
|
|
:param buffer: A buffer containing display data (usually
|
2020-06-11 15:42:13 +03:00
|
|
|
data returned from :py:func:`~PIL.ImageWin.Dib.tobytes`)
|
2013-10-14 09:19:12 +04:00
|
|
|
"""
|
2024-05-18 09:06:50 +03:00
|
|
|
self.image.frombytes(buffer)
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2024-05-18 09:06:50 +03:00
|
|
|
def tobytes(self) -> bytes:
|
2013-10-14 09:19:12 +04:00
|
|
|
"""
|
|
|
|
Copy display memory contents to bytes object.
|
|
|
|
|
|
|
|
:return: A bytes object containing display data.
|
|
|
|
"""
|
py3k: The big push
There are two main issues fixed with this commit:
* bytes vs. str: All file, image, and palette data are now handled as
bytes. A new _binary module consolidates the hacks needed to do this
across Python versions. tostring/fromstring methods have been renamed to
tobytes/frombytes, but the Python 2.6/2.7 versions alias them to the old
names for compatibility. Users should move to tobytes/frombytes.
One other potentially-breaking change is that text data in image files
(such as tags, comments) are now explicitly handled with a specific
character encoding in mind. This works well with the Unicode str in
Python 3, but may trip up old code expecting a straight byte-for-byte
translation to a Python string. This also required a change to Gohlke's
tags tests (in Tests/test_file_png.py) to expect Unicode strings from
the code.
* True div vs. floor div: Many division operations used the "/" operator
to do floor division, which is now the "//" operator in Python 3. These
were fixed.
As of this commit, on the first pass, I have one failing test (improper
handling of a slice object in a C module, test_imagepath.py) in Python 3,
and three that that I haven't tried running yet (test_imagegl,
test_imagegrab, and test_imageqt). I also haven't tested anything on
Windows. All but the three skipped tests run flawlessly against Pythons
2.6 and 2.7.
2012-10-21 01:01:53 +04:00
|
|
|
return self.image.tobytes()
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2014-07-06 01:43:22 +04:00
|
|
|
|
2019-09-30 17:56:31 +03:00
|
|
|
class Window:
|
2016-05-24 10:36:14 +03:00
|
|
|
"""Create a Window with the given title size."""
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2024-05-18 09:06:50 +03:00
|
|
|
def __init__(
|
|
|
|
self, title: str = "PIL", width: int | None = None, height: int | None = None
|
|
|
|
) -> None:
|
2010-07-31 06:52:47 +04:00
|
|
|
self.hwnd = Image.core.createwindow(
|
|
|
|
title, self.__dispatcher, width or 0, height or 0
|
|
|
|
)
|
|
|
|
|
2024-07-20 06:14:18 +03:00
|
|
|
def __dispatcher(self, action: str, *args: int) -> None:
|
|
|
|
getattr(self, f"ui_handle_{action}")(*args)
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2024-07-20 06:14:18 +03:00
|
|
|
def ui_handle_clear(self, dc: int, x0: int, y0: int, x1: int, y1: int) -> None:
|
2010-07-31 06:52:47 +04:00
|
|
|
pass
|
|
|
|
|
2024-07-20 06:14:18 +03:00
|
|
|
def ui_handle_damage(self, x0: int, y0: int, x1: int, y1: int) -> None:
|
2010-07-31 06:52:47 +04:00
|
|
|
pass
|
|
|
|
|
2024-05-04 13:51:54 +03:00
|
|
|
def ui_handle_destroy(self) -> None:
|
2010-07-31 06:52:47 +04:00
|
|
|
pass
|
|
|
|
|
2024-07-20 06:14:18 +03:00
|
|
|
def ui_handle_repair(self, dc: int, x0: int, y0: int, x1: int, y1: int) -> None:
|
2010-07-31 06:52:47 +04:00
|
|
|
pass
|
|
|
|
|
2024-07-20 06:14:18 +03:00
|
|
|
def ui_handle_resize(self, width: int, height: int) -> None:
|
2010-07-31 06:52:47 +04:00
|
|
|
pass
|
|
|
|
|
2024-05-04 13:51:54 +03:00
|
|
|
def mainloop(self) -> None:
|
2010-07-31 06:52:47 +04:00
|
|
|
Image.core.eventloop()
|
|
|
|
|
2014-07-06 01:43:22 +04:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
class ImageWindow(Window):
|
2016-05-24 10:36:14 +03:00
|
|
|
"""Create an image window which displays the given image."""
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2024-07-20 06:14:18 +03:00
|
|
|
def __init__(self, image: Image.Image | Dib, title: str = "PIL") -> None:
|
2010-07-31 06:52:47 +04:00
|
|
|
if not isinstance(image, Dib):
|
|
|
|
image = Dib(image)
|
|
|
|
self.image = image
|
|
|
|
width, height = image.size
|
2016-11-05 20:31:11 +03:00
|
|
|
super().__init__(title, width=width, height=height)
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2024-07-20 06:14:18 +03:00
|
|
|
def ui_handle_repair(self, dc: int, x0: int, y0: int, x1: int, y1: int) -> None:
|
2010-07-31 06:52:47 +04:00
|
|
|
self.image.draw(dc, (x0, y0, x1, y1))
|