Aluode/PerceptionLabPortable
0
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 