|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations |
|
|
|
from . import Image |
|
|
|
|
|
class HDC: |
|
""" |
|
Wraps an HDC integer. The resulting object can be passed to the |
|
:py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` |
|
methods. |
|
""" |
|
|
|
def __init__(self, dc: int) -> None: |
|
self.dc = dc |
|
|
|
def __int__(self) -> int: |
|
return self.dc |
|
|
|
|
|
class HWND: |
|
""" |
|
Wraps an HWND integer. The resulting object can be passed to the |
|
:py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` |
|
methods, instead of a DC. |
|
""" |
|
|
|
def __init__(self, wnd: int) -> None: |
|
self.wnd = wnd |
|
|
|
def __int__(self) -> int: |
|
return self.wnd |
|
|
|
|
|
class Dib: |
|
""" |
|
A Windows bitmap with the given mode and size. The mode can be one of "1", |
|
"L", "P", or "RGB". |
|
|
|
If the display requires a palette, this constructor creates a suitable |
|
palette and associates it with the image. For an "L" image, 128 graylevels |
|
are allocated. For an "RGB" image, a 6x6x6 colour cube is used, together |
|
with 20 graylevels. |
|
|
|
To make sure that palettes work properly under Windows, you must call the |
|
``palette`` method upon certain events from Windows. |
|
|
|
: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. |
|
""" |
|
|
|
def __init__( |
|
self, image: Image.Image | str, size: tuple[int, int] | list[int] | None = None |
|
) -> None: |
|
if isinstance(image, str): |
|
mode = image |
|
image = "" |
|
else: |
|
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: |
|
assert not isinstance(image, str) |
|
self.paste(image) |
|
|
|
def expose(self, handle): |
|
""" |
|
Copy the bitmap contents to a device context. |
|
|
|
: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. |
|
""" |
|
if isinstance(handle, HWND): |
|
dc = self.image.getdc(handle) |
|
try: |
|
result = self.image.expose(dc) |
|
finally: |
|
self.image.releasedc(handle, dc) |
|
else: |
|
result = self.image.expose(handle) |
|
return result |
|
|
|
def draw(self, handle, dst, src=None): |
|
""" |
|
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. |
|
""" |
|
if not src: |
|
src = (0, 0) + self.size |
|
if isinstance(handle, HWND): |
|
dc = self.image.getdc(handle) |
|
try: |
|
result = self.image.draw(dc, dst, src) |
|
finally: |
|
self.image.releasedc(handle, dc) |
|
else: |
|
result = self.image.draw(handle, dst, src) |
|
return result |
|
|
|
def query_palette(self, handle): |
|
""" |
|
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. |
|
:return: A true value if one or more entries were changed (this |
|
indicates that the image should be redrawn). |
|
""" |
|
if isinstance(handle, HWND): |
|
handle = self.image.getdc(handle) |
|
try: |
|
result = self.image.query_palette(handle) |
|
finally: |
|
self.image.releasedc(handle, handle) |
|
else: |
|
result = self.image.query_palette(handle) |
|
return result |
|
|
|
def paste( |
|
self, im: Image.Image, box: tuple[int, int, int, int] | None = None |
|
) -> None: |
|
""" |
|
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. |
|
""" |
|
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) |
|
|
|
def frombytes(self, buffer: bytes) -> None: |
|
""" |
|
Load display memory contents from byte data. |
|
|
|
:param buffer: A buffer containing display data (usually |
|
data returned from :py:func:`~PIL.ImageWin.Dib.tobytes`) |
|
""" |
|
self.image.frombytes(buffer) |
|
|
|
def tobytes(self) -> bytes: |
|
""" |
|
Copy display memory contents to bytes object. |
|
|
|
:return: A bytes object containing display data. |
|
""" |
|
return self.image.tobytes() |
|
|
|
|
|
class Window: |
|
"""Create a Window with the given title size.""" |
|
|
|
def __init__( |
|
self, title: str = "PIL", width: int | None = None, height: int | None = None |
|
) -> None: |
|
self.hwnd = Image.core.createwindow( |
|
title, self.__dispatcher, width or 0, height or 0 |
|
) |
|
|
|
def __dispatcher(self, action, *args): |
|
return getattr(self, f"ui_handle_{action}")(*args) |
|
|
|
def ui_handle_clear(self, dc, x0, y0, x1, y1): |
|
pass |
|
|
|
def ui_handle_damage(self, x0, y0, x1, y1): |
|
pass |
|
|
|
def ui_handle_destroy(self) -> None: |
|
pass |
|
|
|
def ui_handle_repair(self, dc, x0, y0, x1, y1): |
|
pass |
|
|
|
def ui_handle_resize(self, width, height): |
|
pass |
|
|
|
def mainloop(self) -> None: |
|
Image.core.eventloop() |
|
|
|
|
|
class ImageWindow(Window): |
|
"""Create an image window which displays the given image.""" |
|
|
|
def __init__(self, image, title="PIL"): |
|
if not isinstance(image, Dib): |
|
image = Dib(image) |
|
self.image = image |
|
width, height = image.size |
|
super().__init__(title, width=width, height=height) |
|
|
|
def ui_handle_repair(self, dc, x0, y0, x1, y1): |
|
self.image.draw(dc, (x0, y0, x1, y1)) |
|
|