ยปCore Development>Code coverage>Lib/plat-mac/PixMapWrapper.py

Python code coverage for Lib/plat-mac/PixMapWrapper.py

#countcontent
1n/a"""PixMapWrapper - defines the PixMapWrapper class, which wraps an opaque
2n/aQuickDraw PixMap data structure in a handy Python class. Also provides
3n/amethods to convert to/from pixel data (from, e.g., the img module) or a
4n/aPython Imaging Library Image object.
5n/a
6n/aJ. Strout <joe@strout.net> February 1999"""
7n/a
8n/a
9n/afrom warnings import warnpy3k
10n/awarnpy3k("In 3.x, the PixMapWrapper module is removed.", stacklevel=2)
11n/a
12n/afrom Carbon import Qd
13n/afrom Carbon import QuickDraw
14n/aimport struct
15n/aimport MacOS
16n/aimport img
17n/aimport imgformat
18n/a
19n/a# PixMap data structure element format (as used with struct)
20n/a_pmElemFormat = {
21n/a 'baseAddr':'l', # address of pixel data
22n/a 'rowBytes':'H', # bytes per row, plus 0x8000
23n/a 'bounds':'hhhh', # coordinates imposed over pixel data
24n/a 'top':'h',
25n/a 'left':'h',
26n/a 'bottom':'h',
27n/a 'right':'h',
28n/a 'pmVersion':'h', # flags for Color QuickDraw
29n/a 'packType':'h', # format of compression algorithm
30n/a 'packSize':'l', # size after compression
31n/a 'hRes':'l', # horizontal pixels per inch
32n/a 'vRes':'l', # vertical pixels per inch
33n/a 'pixelType':'h', # pixel format
34n/a 'pixelSize':'h', # bits per pixel
35n/a 'cmpCount':'h', # color components per pixel
36n/a 'cmpSize':'h', # bits per component
37n/a 'planeBytes':'l', # offset in bytes to next plane
38n/a 'pmTable':'l', # handle to color table
39n/a 'pmReserved':'l' # reserved for future use
40n/a}
41n/a
42n/a# PixMap data structure element offset
43n/a_pmElemOffset = {
44n/a 'baseAddr':0,
45n/a 'rowBytes':4,
46n/a 'bounds':6,
47n/a 'top':6,
48n/a 'left':8,
49n/a 'bottom':10,
50n/a 'right':12,
51n/a 'pmVersion':14,
52n/a 'packType':16,
53n/a 'packSize':18,
54n/a 'hRes':22,
55n/a 'vRes':26,
56n/a 'pixelType':30,
57n/a 'pixelSize':32,
58n/a 'cmpCount':34,
59n/a 'cmpSize':36,
60n/a 'planeBytes':38,
61n/a 'pmTable':42,
62n/a 'pmReserved':46
63n/a}
64n/a
65n/aclass PixMapWrapper:
66n/a """PixMapWrapper -- wraps the QD PixMap object in a Python class,
67n/a with methods to easily get/set various pixmap fields. Note: Use the
68n/a PixMap() method when passing to QD calls."""
69n/a
70n/a def __init__(self):
71n/a self.__dict__['data'] = ''
72n/a self._header = struct.pack("lhhhhhhhlllhhhhlll",
73n/a id(self.data)+MacOS.string_id_to_buffer,
74n/a 0, # rowBytes
75n/a 0, 0, 0, 0, # bounds
76n/a 0, # pmVersion
77n/a 0, 0, # packType, packSize
78n/a 72<<16, 72<<16, # hRes, vRes
79n/a QuickDraw.RGBDirect, # pixelType
80n/a 16, # pixelSize
81n/a 2, 5, # cmpCount, cmpSize,
82n/a 0, 0, 0) # planeBytes, pmTable, pmReserved
83n/a self.__dict__['_pm'] = Qd.RawBitMap(self._header)
84n/a
85n/a def _stuff(self, element, bytes):
86n/a offset = _pmElemOffset[element]
87n/a fmt = _pmElemFormat[element]
88n/a self._header = self._header[:offset] \
89n/a + struct.pack(fmt, bytes) \
90n/a + self._header[offset + struct.calcsize(fmt):]
91n/a self.__dict__['_pm'] = None
92n/a
93n/a def _unstuff(self, element):
94n/a offset = _pmElemOffset[element]
95n/a fmt = _pmElemFormat[element]
96n/a return struct.unpack(fmt, self._header[offset:offset+struct.calcsize(fmt)])[0]
97n/a
98n/a def __setattr__(self, attr, val):
99n/a if attr == 'baseAddr':
100n/a raise 'UseErr', "don't assign to .baseAddr -- assign to .data instead"
101n/a elif attr == 'data':
102n/a self.__dict__['data'] = val
103n/a self._stuff('baseAddr', id(self.data) + MacOS.string_id_to_buffer)
104n/a elif attr == 'rowBytes':
105n/a # high bit is always set for some odd reason
106n/a self._stuff('rowBytes', val | 0x8000)
107n/a elif attr == 'bounds':
108n/a # assume val is in official Left, Top, Right, Bottom order!
109n/a self._stuff('left',val[0])
110n/a self._stuff('top',val[1])
111n/a self._stuff('right',val[2])
112n/a self._stuff('bottom',val[3])
113n/a elif attr == 'hRes' or attr == 'vRes':
114n/a # 16.16 fixed format, so just shift 16 bits
115n/a self._stuff(attr, int(val) << 16)
116n/a elif attr in _pmElemFormat.keys():
117n/a # any other pm attribute -- just stuff
118n/a self._stuff(attr, val)
119n/a else:
120n/a self.__dict__[attr] = val
121n/a
122n/a def __getattr__(self, attr):
123n/a if attr == 'rowBytes':
124n/a # high bit is always set for some odd reason
125n/a return self._unstuff('rowBytes') & 0x7FFF
126n/a elif attr == 'bounds':
127n/a # return bounds in official Left, Top, Right, Bottom order!
128n/a return ( \
129n/a self._unstuff('left'),
130n/a self._unstuff('top'),
131n/a self._unstuff('right'),
132n/a self._unstuff('bottom') )
133n/a elif attr == 'hRes' or attr == 'vRes':
134n/a # 16.16 fixed format, so just shift 16 bits
135n/a return self._unstuff(attr) >> 16
136n/a elif attr in _pmElemFormat.keys():
137n/a # any other pm attribute -- just unstuff
138n/a return self._unstuff(attr)
139n/a else:
140n/a return self.__dict__[attr]
141n/a
142n/a
143n/a def PixMap(self):
144n/a "Return a QuickDraw PixMap corresponding to this data."
145n/a if not self.__dict__['_pm']:
146n/a self.__dict__['_pm'] = Qd.RawBitMap(self._header)
147n/a return self.__dict__['_pm']
148n/a
149n/a def blit(self, x1=0,y1=0,x2=None,y2=None, port=None):
150n/a """Draw this pixmap into the given (default current) grafport."""
151n/a src = self.bounds
152n/a dest = [x1,y1,x2,y2]
153n/a if x2 is None:
154n/a dest[2] = x1 + src[2]-src[0]
155n/a if y2 is None:
156n/a dest[3] = y1 + src[3]-src[1]
157n/a if not port: port = Qd.GetPort()
158n/a Qd.CopyBits(self.PixMap(), port.GetPortBitMapForCopyBits(), src, tuple(dest),
159n/a QuickDraw.srcCopy, None)
160n/a
161n/a def fromstring(self,s,width,height,format=imgformat.macrgb):
162n/a """Stuff this pixmap with raw pixel data from a string.
163n/a Supply width, height, and one of the imgformat specifiers."""
164n/a # we only support 16- and 32-bit mac rgb...
165n/a # so convert if necessary
166n/a if format != imgformat.macrgb and format != imgformat.macrgb16:
167n/a # (LATER!)
168n/a raise "NotImplementedError", "conversion to macrgb or macrgb16"
169n/a self.data = s
170n/a self.bounds = (0,0,width,height)
171n/a self.cmpCount = 3
172n/a self.pixelType = QuickDraw.RGBDirect
173n/a if format == imgformat.macrgb:
174n/a self.pixelSize = 32
175n/a self.cmpSize = 8
176n/a else:
177n/a self.pixelSize = 16
178n/a self.cmpSize = 5
179n/a self.rowBytes = width*self.pixelSize/8
180n/a
181n/a def tostring(self, format=imgformat.macrgb):
182n/a """Return raw data as a string in the specified format."""
183n/a # is the native format requested? if so, just return data
184n/a if (format == imgformat.macrgb and self.pixelSize == 32) or \
185n/a (format == imgformat.macrgb16 and self.pixelsize == 16):
186n/a return self.data
187n/a # otherwise, convert to the requested format
188n/a # (LATER!)
189n/a raise "NotImplementedError", "data format conversion"
190n/a
191n/a def fromImage(self,im):
192n/a """Initialize this PixMap from a PIL Image object."""
193n/a # We need data in ARGB format; PIL can't currently do that,
194n/a # but it can do RGBA, which we can use by inserting one null
195n/a # up frontpm =
196n/a if im.mode != 'RGBA': im = im.convert('RGBA')
197n/a data = chr(0) + im.tostring()
198n/a self.fromstring(data, im.size[0], im.size[1])
199n/a
200n/a def toImage(self):
201n/a """Return the contents of this PixMap as a PIL Image object."""
202n/a import Image
203n/a # our tostring() method returns data in ARGB format,
204n/a # whereas Image uses RGBA; a bit of slicing fixes this...
205n/a data = self.tostring()[1:] + chr(0)
206n/a bounds = self.bounds
207n/a return Image.fromstring('RGBA',(bounds[2]-bounds[0],bounds[3]-bounds[1]),data)
208n/a
209n/adef test():
210n/a import MacOS
211n/a import EasyDialogs
212n/a import Image
213n/a path = EasyDialogs.AskFileForOpen("Image File:")
214n/a if not path: return
215n/a pm = PixMapWrapper()
216n/a pm.fromImage( Image.open(path) )
217n/a pm.blit(20,20)
218n/a return pm