1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
|
#
# The Python Imaging Library.
# $Id: PcxImagePlugin.py 2134 2004-10-06 08:55:20Z fredrik $
#
# PCX file handling
#
# This format was originally used by ZSoft's popular PaintBrush
# program for the IBM PC. It is also supported by many MS-DOS and
# Windows applications, including the Windows PaintBrush program in
# Windows 3.
#
# history:
# 1995-09-01 fl Created
# 1996-05-20 fl Fixed RGB support
# 1997-01-03 fl Fixed 2-bit and 4-bit support
# 1999-02-03 fl Fixed 8-bit support (broken in 1.0b1)
# 1999-02-07 fl Added write support
# 2002-06-09 fl Made 2-bit and 4-bit support a bit more robust
# 2002-07-30 fl Seek from to current position, not beginning of file
# 2003-06-03 fl Extract DPI settings (info["dpi"])
#
# Copyright (c) 1997-2003 by Secret Labs AB.
# Copyright (c) 1995-2003 by Fredrik Lundh.
#
# See the README file for information on usage and redistribution.
#
__version__ = "0.6"
import Image, ImageFile, ImagePalette
def i16(c,o):
return ord(c[o]) + (ord(c[o+1])<<8)
def _accept(prefix):
return ord(prefix[0]) == 10 and ord(prefix[1]) in [0, 2, 3, 5]
##
# Image plugin for Paintbrush images.
class PcxImageFile(ImageFile.ImageFile):
format = "PCX"
format_description = "Paintbrush"
def _open(self):
# header
s = self.fp.read(128)
if not _accept(s):
raise SyntaxError, "not a PCX file"
# image
bbox = i16(s,4), i16(s,6), i16(s,8)+1, i16(s,10)+1
if bbox[2] <= bbox[0] or bbox[3] <= bbox[1]:
raise SyntaxError, "bad PCX image size"
# format
version = ord(s[1])
bits = ord(s[3])
planes = ord(s[65])
stride = i16(s,66)
self.info["dpi"] = i16(s,12), i16(s,14)
if bits == 1 and planes == 1:
mode = rawmode = "1"
elif bits == 1 and planes in (2, 4):
mode = "P"
rawmode = "P;%dL" % planes
self.palette = ImagePalette.raw("RGB", s[16:64])
elif version == 5 and bits == 8 and planes == 1:
mode = rawmode = "L"
# FIXME: hey, this doesn't work with the incremental loader !!!
self.fp.seek(-769, 2)
s = self.fp.read(769)
if len(s) == 769 and ord(s[0]) == 12:
# check if the palette is linear greyscale
for i in range(256):
if s[i*3+1:i*3+4] != chr(i)*3:
mode = rawmode = "P"
break
if mode == "P":
self.palette = ImagePalette.raw("RGB", s[1:])
self.fp.seek(128)
elif version == 5 and bits == 8 and planes == 3:
mode = "RGB"
rawmode = "RGB;L"
else:
raise IOError, "unknown PCX mode"
self.mode = mode
self.size = bbox[2]-bbox[0], bbox[3]-bbox[1]
bbox = (0, 0) + self.size
self.tile = [("pcx", bbox, self.fp.tell(), (rawmode, planes * stride))]
# --------------------------------------------------------------------
# save PCX files
SAVE = {
# mode: (version, bits, planes, raw mode)
"1": (2, 1, 1, "1"),
"L": (5, 8, 1, "L"),
"P": (5, 8, 1, "P"),
"RGB": (5, 8, 3, "RGB;L"),
}
def o16(i):
return chr(i&255) + chr(i>>8&255)
def _save(im, fp, filename, check=0):
try:
version, bits, planes, rawmode = SAVE[im.mode]
except KeyError:
raise ValueError, "Cannot save %s images as PCX" % im.mode
if check:
return check
# bytes per plane
stride = (im.size[0] * bits + 7) / 8
# under windows, we could determine the current screen size with
# "Image.core.display_mode()[1]", but I think that's overkill...
screen = im.size
dpi = 100, 100
# PCX header
fp.write(
chr(10) + chr(version) + chr(1) + chr(bits) + o16(0) +
o16(0) + o16(im.size[0]-1) + o16(im.size[1]-1) + o16(dpi[0]) +
o16(dpi[1]) + chr(0)*24 + chr(255)*24 + chr(0) + chr(planes) +
o16(stride) + o16(1) + o16(screen[0]) + o16(screen[1]) +
chr(0)*54
)
assert fp.tell() == 128
ImageFile._save(im, fp, [("pcx", (0,0)+im.size, 0,
(rawmode, bits*planes))])
if im.mode == "P":
# colour palette
fp.write(chr(12))
fp.write(im.im.getpalette("RGB", "RGB")) # 768 bytes
elif im.mode == "L":
# greyscale palette
fp.write(chr(12))
for i in range(256):
fp.write(chr(i)*3)
# --------------------------------------------------------------------
# registry
Image.register_open("PCX", PcxImageFile, _accept)
Image.register_save("PCX", _save)
Image.register_extension("PCX", ".pcx")
|