2023-12-21 14:13:31 +03:00
|
|
|
from __future__ import annotations
|
2024-01-20 14:23:03 +03:00
|
|
|
|
2015-10-01 16:06:21 +03:00
|
|
|
from io import BytesIO
|
2024-01-31 12:12:58 +03:00
|
|
|
from pathlib import Path
|
2015-10-01 16:06:21 +03:00
|
|
|
|
2019-07-06 23:40:53 +03:00
|
|
|
from PIL import Image, ImageWin
|
|
|
|
|
2020-01-27 14:46:52 +03:00
|
|
|
from .helper import hopper, is_win32
|
2019-07-06 23:40:53 +03:00
|
|
|
|
2015-10-01 16:06:21 +03:00
|
|
|
# see https://github.com/python-pillow/Pillow/pull/1431#issuecomment-144692652
|
|
|
|
|
2019-09-25 12:46:54 +03:00
|
|
|
if is_win32():
|
2022-03-19 06:46:25 +03:00
|
|
|
import ctypes
|
2015-10-01 16:47:16 +03:00
|
|
|
import ctypes.wintypes
|
2015-12-10 01:35:35 +03:00
|
|
|
|
2015-10-01 16:47:16 +03:00
|
|
|
class BITMAPFILEHEADER(ctypes.Structure):
|
|
|
|
_pack_ = 2
|
|
|
|
_fields_ = [
|
2019-06-13 18:54:46 +03:00
|
|
|
("bfType", ctypes.wintypes.WORD),
|
|
|
|
("bfSize", ctypes.wintypes.DWORD),
|
|
|
|
("bfReserved1", ctypes.wintypes.WORD),
|
|
|
|
("bfReserved2", ctypes.wintypes.WORD),
|
|
|
|
("bfOffBits", ctypes.wintypes.DWORD),
|
2015-10-01 16:47:16 +03:00
|
|
|
]
|
|
|
|
|
|
|
|
class BITMAPINFOHEADER(ctypes.Structure):
|
|
|
|
_pack_ = 2
|
|
|
|
_fields_ = [
|
2019-06-13 18:54:46 +03:00
|
|
|
("biSize", ctypes.wintypes.DWORD),
|
|
|
|
("biWidth", ctypes.wintypes.LONG),
|
|
|
|
("biHeight", ctypes.wintypes.LONG),
|
|
|
|
("biPlanes", ctypes.wintypes.WORD),
|
|
|
|
("biBitCount", ctypes.wintypes.WORD),
|
|
|
|
("biCompression", ctypes.wintypes.DWORD),
|
|
|
|
("biSizeImage", ctypes.wintypes.DWORD),
|
|
|
|
("biXPelsPerMeter", ctypes.wintypes.LONG),
|
|
|
|
("biYPelsPerMeter", ctypes.wintypes.LONG),
|
|
|
|
("biClrUsed", ctypes.wintypes.DWORD),
|
|
|
|
("biClrImportant", ctypes.wintypes.DWORD),
|
2015-10-01 16:47:16 +03:00
|
|
|
]
|
|
|
|
|
|
|
|
BI_RGB = 0
|
|
|
|
DIB_RGB_COLORS = 0
|
|
|
|
|
|
|
|
memcpy = ctypes.cdll.msvcrt.memcpy
|
2015-12-10 01:35:35 +03:00
|
|
|
memcpy.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t]
|
2015-10-01 16:47:16 +03:00
|
|
|
|
2024-06-23 23:59:00 +03:00
|
|
|
windll = getattr(ctypes, "windll")
|
|
|
|
CreateCompatibleDC = windll.gdi32.CreateCompatibleDC
|
2015-12-10 01:35:35 +03:00
|
|
|
CreateCompatibleDC.argtypes = [ctypes.wintypes.HDC]
|
2015-10-01 16:47:16 +03:00
|
|
|
CreateCompatibleDC.restype = ctypes.wintypes.HDC
|
|
|
|
|
2024-06-23 23:59:00 +03:00
|
|
|
DeleteDC = windll.gdi32.DeleteDC
|
2015-12-10 01:35:35 +03:00
|
|
|
DeleteDC.argtypes = [ctypes.wintypes.HDC]
|
2015-10-01 16:47:16 +03:00
|
|
|
|
2024-06-23 23:59:00 +03:00
|
|
|
SelectObject = windll.gdi32.SelectObject
|
2015-12-10 01:35:35 +03:00
|
|
|
SelectObject.argtypes = [ctypes.wintypes.HDC, ctypes.wintypes.HGDIOBJ]
|
2015-10-01 16:47:16 +03:00
|
|
|
SelectObject.restype = ctypes.wintypes.HGDIOBJ
|
|
|
|
|
2024-06-23 23:59:00 +03:00
|
|
|
DeleteObject = windll.gdi32.DeleteObject
|
2015-12-10 01:35:35 +03:00
|
|
|
DeleteObject.argtypes = [ctypes.wintypes.HGDIOBJ]
|
2015-10-01 16:47:16 +03:00
|
|
|
|
2024-06-23 23:59:00 +03:00
|
|
|
CreateDIBSection = windll.gdi32.CreateDIBSection
|
2019-06-13 18:54:46 +03:00
|
|
|
CreateDIBSection.argtypes = [
|
|
|
|
ctypes.wintypes.HDC,
|
|
|
|
ctypes.c_void_p,
|
|
|
|
ctypes.c_uint,
|
|
|
|
ctypes.POINTER(ctypes.c_void_p),
|
|
|
|
ctypes.wintypes.HANDLE,
|
|
|
|
ctypes.wintypes.DWORD,
|
|
|
|
]
|
2015-10-01 16:47:16 +03:00
|
|
|
CreateDIBSection.restype = ctypes.wintypes.HBITMAP
|
|
|
|
|
2024-06-09 08:16:17 +03:00
|
|
|
def serialize_dib(bi: BITMAPINFOHEADER, pixels: ctypes.c_void_p) -> bytearray:
|
2015-10-01 16:47:16 +03:00
|
|
|
bf = BITMAPFILEHEADER()
|
2019-06-13 18:54:46 +03:00
|
|
|
bf.bfType = 0x4D42
|
2015-10-01 16:47:16 +03:00
|
|
|
bf.bfOffBits = ctypes.sizeof(bf) + bi.biSize
|
|
|
|
bf.bfSize = bf.bfOffBits + bi.biSizeImage
|
|
|
|
bf.bfReserved1 = bf.bfReserved2 = 0
|
|
|
|
|
|
|
|
buf = (ctypes.c_byte * bf.bfSize)()
|
|
|
|
bp = ctypes.addressof(buf)
|
|
|
|
memcpy(bp, ctypes.byref(bf), ctypes.sizeof(bf))
|
|
|
|
memcpy(bp + ctypes.sizeof(bf), ctypes.byref(bi), bi.biSize)
|
|
|
|
memcpy(bp + bf.bfOffBits, pixels, bi.biSizeImage)
|
2016-11-07 15:33:46 +03:00
|
|
|
return bytearray(buf)
|
2015-10-01 16:47:16 +03:00
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_pointer(tmp_path: Path) -> None:
|
2020-01-27 14:46:52 +03:00
|
|
|
im = hopper()
|
|
|
|
(width, height) = im.size
|
|
|
|
opath = str(tmp_path / "temp.png")
|
|
|
|
imdib = ImageWin.Dib(im)
|
|
|
|
|
|
|
|
hdr = BITMAPINFOHEADER()
|
|
|
|
hdr.biSize = ctypes.sizeof(hdr)
|
|
|
|
hdr.biWidth = width
|
|
|
|
hdr.biHeight = height
|
|
|
|
hdr.biPlanes = 1
|
|
|
|
hdr.biBitCount = 32
|
|
|
|
hdr.biCompression = BI_RGB
|
|
|
|
hdr.biSizeImage = width * height * 4
|
|
|
|
hdr.biClrUsed = 0
|
|
|
|
hdr.biClrImportant = 0
|
|
|
|
|
|
|
|
hdc = CreateCompatibleDC(None)
|
|
|
|
pixels = ctypes.c_void_p()
|
|
|
|
dib = CreateDIBSection(
|
|
|
|
hdc, ctypes.byref(hdr), DIB_RGB_COLORS, ctypes.byref(pixels), None, 0
|
|
|
|
)
|
|
|
|
SelectObject(hdc, dib)
|
|
|
|
|
|
|
|
imdib.expose(hdc)
|
|
|
|
bitmap = serialize_dib(hdr, pixels)
|
|
|
|
DeleteObject(dib)
|
|
|
|
DeleteDC(hdc)
|
|
|
|
|
2021-02-11 13:43:54 +03:00
|
|
|
with Image.open(BytesIO(bitmap)) as im:
|
|
|
|
im.save(opath)
|