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