Pillow/src/PIL/ImageWin.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

248 lines
7.9 KiB
Python
Raw Normal View History

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.
#
from __future__ import annotations
2010-07-31 06:52:47 +04:00
from . import Image
2010-07-31 06:52:47 +04: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
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
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
``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
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
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.
"""
return self.image.tobytes()
2010-07-31 06:52:47 +04:00
2014-07-06 01:43:22 +04: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
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))