CoolFace
Apppublic

Aluode/PerceptionLabPortable

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
PalmImagePlugin.py218 linesDownload Raw Back to PIL
1#
2# The Python Imaging Library.
3# $Id$
4#
5
6##
7# Image plugin for Palm pixmap images (output only).
8##
9from __future__ import annotations
10
11from typing import IO
12
13from . import Image, ImageFile
14from ._binary import o8
15from ._binary import o16be as o16b
16
17# fmt: off
18_Palm8BitColormapValues = (
19    (255, 255, 255), (255, 204, 255), (255, 153, 255), (255, 102, 255),
20    (255,  51, 255), (255,   0, 255), (255, 255, 204), (255, 204, 204),
21    (255, 153, 204), (255, 102, 204), (255,  51, 204), (255,   0, 204),
22    (255, 255, 153), (255, 204, 153), (255, 153, 153), (255, 102, 153),
23    (255,  51, 153), (255,   0, 153), (204, 255, 255), (204, 204, 255),
24    (204, 153, 255), (204, 102, 255), (204,  51, 255), (204,   0, 255),
25    (204, 255, 204), (204, 204, 204), (204, 153, 204), (204, 102, 204),
26    (204,  51, 204), (204,   0, 204), (204, 255, 153), (204, 204, 153),
27    (204, 153, 153), (204, 102, 153), (204,  51, 153), (204,   0, 153),
28    (153, 255, 255), (153, 204, 255), (153, 153, 255), (153, 102, 255),
29    (153,  51, 255), (153,   0, 255), (153, 255, 204), (153, 204, 204),
30    (153, 153, 204), (153, 102, 204), (153,  51, 204), (153,   0, 204),
31    (153, 255, 153), (153, 204, 153), (153, 153, 153), (153, 102, 153),
32    (153,  51, 153), (153,   0, 153), (102, 255, 255), (102, 204, 255),
33    (102, 153, 255), (102, 102, 255), (102,  51, 255), (102,   0, 255),
34    (102, 255, 204), (102, 204, 204), (102, 153, 204), (102, 102, 204),
35    (102,  51, 204), (102,   0, 204), (102, 255, 153), (102, 204, 153),
36    (102, 153, 153), (102, 102, 153), (102,  51, 153), (102,   0, 153),
37    (51,  255, 255), (51,  204, 255), (51,  153, 255), (51,  102, 255),
38    (51,   51, 255), (51,    0, 255), (51,  255, 204), (51,  204, 204),
39    (51,  153, 204), (51,  102, 204), (51,   51, 204), (51,    0, 204),
40    (51,  255, 153), (51,  204, 153), (51,  153, 153), (51,  102, 153),
41    (51,   51, 153), (51,    0, 153), (0,   255, 255), (0,   204, 255),
42    (0,   153, 255), (0,   102, 255), (0,    51, 255), (0,     0, 255),
43    (0,   255, 204), (0,   204, 204), (0,   153, 204), (0,   102, 204),
44    (0,    51, 204), (0,     0, 204), (0,   255, 153), (0,   204, 153),
45    (0,   153, 153), (0,   102, 153), (0,    51, 153), (0,     0, 153),
46    (255, 255, 102), (255, 204, 102), (255, 153, 102), (255, 102, 102),
47    (255,  51, 102), (255,   0, 102), (255, 255,  51), (255, 204,  51),
48    (255, 153,  51), (255, 102,  51), (255,  51,  51), (255,   0,  51),
49    (255, 255,   0), (255, 204,   0), (255, 153,   0), (255, 102,   0),
50    (255,  51,   0), (255,   0,   0), (204, 255, 102), (204, 204, 102),
51    (204, 153, 102), (204, 102, 102), (204,  51, 102), (204,   0, 102),
52    (204, 255,  51), (204, 204,  51), (204, 153,  51), (204, 102,  51),
53    (204,  51,  51), (204,   0,  51), (204, 255,   0), (204, 204,   0),
54    (204, 153,   0), (204, 102,   0), (204,  51,   0), (204,   0,   0),
55    (153, 255, 102), (153, 204, 102), (153, 153, 102), (153, 102, 102),
56    (153,  51, 102), (153,   0, 102), (153, 255,  51), (153, 204,  51),
57    (153, 153,  51), (153, 102,  51), (153,  51,  51), (153,   0,  51),
58    (153, 255,   0), (153, 204,   0), (153, 153,   0), (153, 102,   0),
59    (153,  51,   0), (153,   0,   0), (102, 255, 102), (102, 204, 102),
60    (102, 153, 102), (102, 102, 102), (102,  51, 102), (102,   0, 102),
61    (102, 255,  51), (102, 204,  51), (102, 153,  51), (102, 102,  51),
62    (102,  51,  51), (102,   0,  51), (102, 255,   0), (102, 204,   0),
63    (102, 153,   0), (102, 102,   0), (102,  51,   0), (102,   0,   0),
64    (51,  255, 102), (51,  204, 102), (51,  153, 102), (51,  102, 102),
65    (51,   51, 102), (51,    0, 102), (51,  255,  51), (51,  204,  51),
66    (51,  153,  51), (51,  102,  51), (51,   51,  51), (51,    0,  51),
67    (51,  255,   0), (51,  204,   0), (51,  153,   0), (51,  102,   0),
68    (51,   51,   0), (51,    0,   0), (0,   255, 102), (0,   204, 102),
69    (0,   153, 102), (0,   102, 102), (0,    51, 102), (0,     0, 102),
70    (0,   255,  51), (0,   204,  51), (0,   153,  51), (0,   102,  51),
71    (0,    51,  51), (0,     0,  51), (0,   255,   0), (0,   204,   0),
72    (0,   153,   0), (0,   102,   0), (0,    51,   0), (17,   17,  17),
73    (34,   34,  34), (68,   68,  68), (85,   85,  85), (119, 119, 119),
74    (136, 136, 136), (170, 170, 170), (187, 187, 187), (221, 221, 221),
75    (238, 238, 238), (192, 192, 192), (128,   0,   0), (128,   0, 128),
76    (0,   128,   0), (0,   128, 128), (0,     0,   0), (0,     0,   0),
77    (0,     0,   0), (0,     0,   0), (0,     0,   0), (0,     0,   0),
78    (0,     0,   0), (0,     0,   0), (0,     0,   0), (0,     0,   0),
79    (0,     0,   0), (0,     0,   0), (0,     0,   0), (0,     0,   0),
80    (0,     0,   0), (0,     0,   0), (0,     0,   0), (0,     0,   0),
81    (0,     0,   0), (0,     0,   0), (0,     0,   0), (0,     0,   0),
82    (0,     0,   0), (0,     0,   0), (0,     0,   0), (0,     0,   0))
83# fmt: on
84
85
86# so build a prototype image to be used for palette resampling
87def build_prototype_image() -> Image.Image:
88    image = Image.new("L", (1, len(_Palm8BitColormapValues)))
89    image.putdata(list(range(len(_Palm8BitColormapValues))))
90    palettedata: tuple[int, ...] = ()
91    for colormapValue in _Palm8BitColormapValues:
92        palettedata += colormapValue
93    palettedata += (0, 0, 0) * (256 - len(_Palm8BitColormapValues))
94    image.putpalette(palettedata)
95    return image
96
97
98Palm8BitColormapImage = build_prototype_image()
99
100# OK, we now have in Palm8BitColormapImage,
101# a "P"-mode image with the right palette
102#
103# --------------------------------------------------------------------
104
105_FLAGS = {"custom-colormap": 0x4000, "is-compressed": 0x8000, "has-transparent": 0x2000}
106
107_COMPRESSION_TYPES = {"none": 0xFF, "rle": 0x01, "scanline": 0x00}
108
109
110#
111# --------------------------------------------------------------------
112
113##
114# (Internal) Image save plugin for the Palm format.
115
116
117def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
118    if im.mode == "P":
119        rawmode = "P"
120        bpp = 8
121        version = 1
122
123    elif im.mode == "L":
124        if im.encoderinfo.get("bpp") in (1, 2, 4):
125            # this is 8-bit grayscale, so we shift it to get the high-order bits,
126            # and invert it because
127            # Palm does grayscale from white (0) to black (1)
128            bpp = im.encoderinfo["bpp"]
129            maxval = (1 << bpp) - 1
130            shift = 8 - bpp
131            im = im.point(lambda x: maxval - (x >> shift))
132        elif im.info.get("bpp") in (1, 2, 4):
133            # here we assume that even though the inherent mode is 8-bit grayscale,
134            # only the lower bpp bits are significant.
135            # We invert them to match the Palm.
136            bpp = im.info["bpp"]
137            maxval = (1 << bpp) - 1
138            im = im.point(lambda x: maxval - (x & maxval))
139        else:
140            msg = f"cannot write mode {im.mode} as Palm"
141            raise OSError(msg)
142
143        # we ignore the palette here
144        im._mode = "P"
145        rawmode = f"P;{bpp}"
146        version = 1
147
148    elif im.mode == "1":
149        # monochrome -- write it inverted, as is the Palm standard
150        rawmode = "1;I"
151        bpp = 1
152        version = 0
153
154    else:
155        msg = f"cannot write mode {im.mode} as Palm"
156        raise OSError(msg)
157
158    #
159    # make sure image data is available
160    im.load()
161
162    # write header
163
164    cols = im.size[0]
165    rows = im.size[1]
166
167    rowbytes = int((cols + (16 // bpp - 1)) / (16 // bpp)) * 2
168    transparent_index = 0
169    compression_type = _COMPRESSION_TYPES["none"]
170
171    flags = 0
172    if im.mode == "P":
173        flags |= _FLAGS["custom-colormap"]
174        colormap = im.im.getpalette()
175        colors = len(colormap) // 3
176        colormapsize = 4 * colors + 2
177    else:
178        colormapsize = 0
179
180    if "offset" in im.info:
181        offset = (rowbytes * rows + 16 + 3 + colormapsize) // 4
182    else:
183        offset = 0
184
185    fp.write(o16b(cols) + o16b(rows) + o16b(rowbytes) + o16b(flags))
186    fp.write(o8(bpp))
187    fp.write(o8(version))
188    fp.write(o16b(offset))
189    fp.write(o8(transparent_index))
190    fp.write(o8(compression_type))
191    fp.write(o16b(0))  # reserved by Palm
192
193    # now write colormap if necessary
194
195    if colormapsize:
196        fp.write(o16b(colors))
197        for i in range(colors):
198            fp.write(o8(i))
199            fp.write(colormap[3 * i : 3 * i + 3])
200
201    # now convert data to raw form
202    ImageFile._save(
203        im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, (rawmode, rowbytes, 1))]
204    )
205
206    if hasattr(fp, "flush"):
207        fp.flush()
208
209
210#
211# --------------------------------------------------------------------
212
213Image.register_save("Palm", _save)
214
215Image.register_extension("Palm", ".palm")
216
217Image.register_mime("Palm", "image/palm")
218