CoolFace
Apppublic

Aluode/PerceptionLabPortable

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
ImageWin.py248 linesDownload Raw Back to PIL
1#
2# The Python Imaging Library.
3# $Id$
4#
5# a Windows DIB display interface
6#
7# History:
8# 1996-05-20 fl   Created
9# 1996-09-20 fl   Fixed subregion exposure
10# 1997-09-21 fl   Added draw primitive (for tzPrint)
11# 2003-05-21 fl   Added experimental Window/ImageWindow classes
12# 2003-09-05 fl   Added fromstring/tostring methods
13#
14# Copyright (c) Secret Labs AB 1997-2003.
15# Copyright (c) Fredrik Lundh 1996-2003.
16#
17# See the README file for information on usage and redistribution.
18#
19from __future__ import annotations
20
21from . import Image
22
23
24class HDC:
25    """
26    Wraps an HDC integer. The resulting object can be passed to the
27    :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose`
28    methods.
29    """
30
31    def __init__(self, dc: int) -> None:
32        self.dc = dc
33
34    def __int__(self) -> int:
35        return self.dc
36
37
38class HWND:
39    """
40    Wraps an HWND integer. The resulting object can be passed to the
41    :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose`
42    methods, instead of a DC.
43    """
44
45    def __init__(self, wnd: int) -> None:
46        self.wnd = wnd
47
48    def __int__(self) -> int:
49        return self.wnd
50
51
52class Dib:
53    """
54    A Windows bitmap with the given mode and size.  The mode can be one of "1",
55    "L", "P", or "RGB".
56
57    If the display requires a palette, this constructor creates a suitable
58    palette and associates it with the image. For an "L" image, 128 graylevels
59    are allocated. For an "RGB" image, a 6x6x6 colour cube is used, together
60    with 20 graylevels.
61
62    To make sure that palettes work properly under Windows, you must call the
63    ``palette`` method upon certain events from Windows.
64
65    :param image: Either a PIL image, or a mode string. If a mode string is
66                  used, a size must also be given.  The mode can be one of "1",
67                  "L", "P", or "RGB".
68    :param size: If the first argument is a mode string, this
69                 defines the size of the image.
70    """
71
72    def __init__(
73        self, image: Image.Image | str, size: tuple[int, int] | None = None
74    ) -> None:
75        if isinstance(image, str):
76            mode = image
77            image = ""
78            if size is None:
79                msg = "If first argument is mode, size is required"
80                raise ValueError(msg)
81        else:
82            mode = image.mode
83            size = image.size
84        if mode not in ["1", "L", "P", "RGB"]:
85            mode = Image.getmodebase(mode)
86        self.image = Image.core.display(mode, size)
87        self.mode = mode
88        self.size = size
89        if image:
90            assert not isinstance(image, str)
91            self.paste(image)
92
93    def expose(self, handle: int | HDC | HWND) -> None:
94        """
95        Copy the bitmap contents to a device context.
96
97        :param handle: Device context (HDC), cast to a Python integer, or an
98                       HDC or HWND instance.  In PythonWin, you can use
99                       ``CDC.GetHandleAttrib()`` to get a suitable handle.
100        """
101        handle_int = int(handle)
102        if isinstance(handle, HWND):
103            dc = self.image.getdc(handle_int)
104            try:
105                self.image.expose(dc)
106            finally:
107                self.image.releasedc(handle_int, dc)
108        else:
109            self.image.expose(handle_int)
110
111    def draw(
112        self,
113        handle: int | HDC | HWND,
114        dst: tuple[int, int, int, int],
115        src: tuple[int, int, int, int] | None = None,
116    ) -> None:
117        """
118        Same as expose, but allows you to specify where to draw the image, and
119        what part of it to draw.
120
121        The destination and source areas are given as 4-tuple rectangles. If
122        the source is omitted, the entire image is copied. If the source and
123        the destination have different sizes, the image is resized as
124        necessary.
125        """
126        if src is None:
127            src = (0, 0) + self.size
128        handle_int = int(handle)
129        if isinstance(handle, HWND):
130            dc = self.image.getdc(handle_int)
131            try:
132                self.image.draw(dc, dst, src)
133            finally:
134                self.image.releasedc(handle_int, dc)
135        else:
136            self.image.draw(handle_int, dst, src)
137
138    def query_palette(self, handle: int | HDC | HWND) -> int:
139        """
140        Installs the palette associated with the image in the given device
141        context.
142
143        This method should be called upon **QUERYNEWPALETTE** and
144        **PALETTECHANGED** events from Windows. If this method returns a
145        non-zero value, one or more display palette entries were changed, and
146        the image should be redrawn.
147
148        :param handle: Device context (HDC), cast to a Python integer, or an
149                       HDC or HWND instance.
150        :return: The number of entries that were changed (if one or more entries,
151                 this indicates that the image should be redrawn).
152        """
153        handle_int = int(handle)
154        if isinstance(handle, HWND):
155            handle = self.image.getdc(handle_int)
156            try:
157                result = self.image.query_palette(handle)
158            finally:
159                self.image.releasedc(handle, handle)
160        else:
161            result = self.image.query_palette(handle_int)
162        return result
163
164    def paste(
165        self, im: Image.Image, box: tuple[int, int, int, int] | None = None
166    ) -> None:
167        """
168        Paste a PIL image into the bitmap image.
169
170        :param im: A PIL image.  The size must match the target region.
171                   If the mode does not match, the image is converted to the
172                   mode of the bitmap image.
173        :param box: A 4-tuple defining the left, upper, right, and
174                    lower pixel coordinate.  See :ref:`coordinate-system`. If
175                    None is given instead of a tuple, all of the image is
176                    assumed.
177        """
178        im.load()
179        if self.mode != im.mode:
180            im = im.convert(self.mode)
181        if box:
182            self.image.paste(im.im, box)
183        else:
184            self.image.paste(im.im)
185
186    def frombytes(self, buffer: bytes) -> None:
187        """
188        Load display memory contents from byte data.
189
190        :param buffer: A buffer containing display data (usually
191                       data returned from :py:func:`~PIL.ImageWin.Dib.tobytes`)
192        """
193        self.image.frombytes(buffer)
194
195    def tobytes(self) -> bytes:
196        """
197        Copy display memory contents to bytes object.
198
199        :return: A bytes object containing display data.
200        """
201        return self.image.tobytes()
202
203
204class Window:
205    """Create a Window with the given title size."""
206
207    def __init__(
208        self, title: str = "PIL", width: int | None = None, height: int | None = None
209    ) -> None:
210        self.hwnd = Image.core.createwindow(
211            title, self.__dispatcher, width or 0, height or 0
212        )
213
214    def __dispatcher(self, action: str, *args: int) -> None:
215        getattr(self, f"ui_handle_{action}")(*args)
216
217    def ui_handle_clear(self, dc: int, x0: int, y0: int, x1: int, y1: int) -> None:
218        pass
219
220    def ui_handle_damage(self, x0: int, y0: int, x1: int, y1: int) -> None:
221        pass
222
223    def ui_handle_destroy(self) -> None:
224        pass
225
226    def ui_handle_repair(self, dc: int, x0: int, y0: int, x1: int, y1: int) -> None:
227        pass
228
229    def ui_handle_resize(self, width: int, height: int) -> None:
230        pass
231
232    def mainloop(self) -> None:
233        Image.core.eventloop()
234
235
236class ImageWindow(Window):
237    """Create an image window which displays the given image."""
238
239    def __init__(self, image: Image.Image | Dib, title: str = "PIL") -> None:
240        if not isinstance(image, Dib):
241            image = Dib(image)
242        self.image = image
243        width, height = image.size
244        super().__init__(title, width=width, height=height)
245
246    def ui_handle_repair(self, dc: int, x0: int, y0: int, x1: int, y1: int) -> None:
247        self.image.draw(dc, (x0, y0, x1, y1))
248 
Aluode/PerceptionLabPortable · CoolFace