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 Carbon
import QuickDraw
15 # PixMap data structure element format (as used with struct)
17 'baseAddr':'l', # address of pixel data
18 'rowBytes':'H', # bytes per row, plus 0x8000
19 'bounds':'hhhh', # coordinates imposed over pixel data
24 'pmVersion':'h', # flags for Color QuickDraw
25 'packType':'h', # format of compression algorithm
26 'packSize':'l', # size after compression
27 'hRes':'l', # horizontal pixels per inch
28 'vRes':'l', # vertical pixels per inch
29 'pixelType':'h', # pixel format
30 'pixelSize':'h', # bits per pixel
31 'cmpCount':'h', # color components per pixel
32 'cmpSize':'h', # bits per component
33 'planeBytes':'l', # offset in bytes to next plane
34 'pmTable':'l', # handle to color table
35 'pmReserved':'l' # reserved for future use
38 # PixMap data structure element offset
62 """PixMapWrapper -- wraps the QD PixMap object in a Python class,
63 with methods to easily get/set various pixmap fields. Note: Use the
64 PixMap() method when passing to QD calls."""
67 self
.__dict
__['data'] = ''
68 self
._header
= struct
.pack("lhhhhhhhlllhhhhlll",
69 id(self
.data
)+MacOS
.string_id_to_buffer
,
73 0, 0, # packType, packSize
74 72<<16, 72<<16, # hRes, vRes
75 QuickDraw
.RGBDirect
, # pixelType
77 2, 5, # cmpCount, cmpSize,
78 0, 0, 0) # planeBytes, pmTable, pmReserved
79 self
.__dict
__['_pm'] = Qd
.RawBitMap(self
._header
)
81 def _stuff(self
, element
, bytes
):
82 offset
= _pmElemOffset
[element
]
83 fmt
= _pmElemFormat
[element
]
84 self
._header
= self
._header
[:offset
] \
85 + struct
.pack(fmt
, bytes
) \
86 + self
._header
[offset
+ struct
.calcsize(fmt
):]
87 self
.__dict
__['_pm'] = None
89 def _unstuff(self
, element
):
90 offset
= _pmElemOffset
[element
]
91 fmt
= _pmElemFormat
[element
]
92 return struct
.unpack(fmt
, self
._header
[offset
:offset
+struct
.calcsize(fmt
)])[0]
94 def __setattr__(self
, attr
, val
):
95 if attr
== 'baseAddr':
96 raise 'UseErr', "don't assign to .baseAddr -- assign to .data instead"
98 self
.__dict
__['data'] = val
99 self
._stuff
('baseAddr', id(self
.data
) + MacOS
.string_id_to_buffer
)
100 elif attr
== 'rowBytes':
101 # high bit is always set for some odd reason
102 self
._stuff
('rowBytes', val |
0x8000)
103 elif attr
== 'bounds':
104 # assume val is in official Left, Top, Right, Bottom order!
105 self
._stuff
('left',val
[0])
106 self
._stuff
('top',val
[1])
107 self
._stuff
('right',val
[2])
108 self
._stuff
('bottom',val
[3])
109 elif attr
== 'hRes' or attr
== 'vRes':
110 # 16.16 fixed format, so just shift 16 bits
111 self
._stuff
(attr
, int(val
) << 16)
112 elif attr
in _pmElemFormat
.keys():
113 # any other pm attribute -- just stuff
114 self
._stuff
(attr
, val
)
116 self
.__dict
__[attr
] = val
118 def __getattr__(self
, attr
):
119 if attr
== 'rowBytes':
120 # high bit is always set for some odd reason
121 return self
._unstuff
('rowBytes') & 0x7FFF
122 elif attr
== 'bounds':
123 # return bounds in official Left, Top, Right, Bottom order!
125 self
._unstuff
('left'),
126 self
._unstuff
('top'),
127 self
._unstuff
('right'),
128 self
._unstuff
('bottom') )
129 elif attr
== 'hRes' or attr
== 'vRes':
130 # 16.16 fixed format, so just shift 16 bits
131 return self
._unstuff
(attr
) >> 16
132 elif attr
in _pmElemFormat
.keys():
133 # any other pm attribute -- just unstuff
134 return self
._unstuff
(attr
)
136 return self
.__dict
__[attr
]
140 "Return a QuickDraw PixMap corresponding to this data."
141 if not self
.__dict
__['_pm']:
142 self
.__dict
__['_pm'] = Qd
.RawBitMap(self
._header
)
143 return self
.__dict
__['_pm']
145 def blit(self
, x1
=0,y1
=0,x2
=None,y2
=None, port
=None):
146 """Draw this pixmap into the given (default current) grafport."""
150 dest
[2] = x1
+ src
[2]-src
[0]
152 dest
[3] = y1
+ src
[3]-src
[1]
153 if not port
: port
= Qd
.GetPort()
154 Qd
.CopyBits(self
.PixMap(), port
.GetPortBitMapForCopyBits(), src
, tuple(dest
),
155 QuickDraw
.srcCopy
, None)
157 def fromstring(self
,s
,width
,height
,format
=imgformat
.macrgb
):
158 """Stuff this pixmap with raw pixel data from a string.
159 Supply width, height, and one of the imgformat specifiers."""
160 # we only support 16- and 32-bit mac rgb...
161 # so convert if necessary
162 if format
!= imgformat
.macrgb
and format
!= imgformat
.macrgb16
:
164 raise "NotImplementedError", "conversion to macrgb or macrgb16"
166 self
.bounds
= (0,0,width
,height
)
168 self
.pixelType
= QuickDraw
.RGBDirect
169 if format
== imgformat
.macrgb
:
175 self
.rowBytes
= width
*self
.pixelSize
/8
177 def tostring(self
, format
=imgformat
.macrgb
):
178 """Return raw data as a string in the specified format."""
179 # is the native format requested? if so, just return data
180 if (format
== imgformat
.macrgb
and self
.pixelSize
== 32) or \
181 (format
== imgformat
.macrgb16
and self
.pixelsize
== 16):
183 # otherwise, convert to the requested format
185 raise "NotImplementedError", "data format conversion"
187 def fromImage(self
,im
):
188 """Initialize this PixMap from a PIL Image object."""
189 # We need data in ARGB format; PIL can't currently do that,
190 # but it can do RGBA, which we can use by inserting one null
192 if im
.mode
!= 'RGBA': im
= im
.convert('RGBA')
193 data
= chr(0) + im
.tostring()
194 self
.fromstring(data
, im
.size
[0], im
.size
[1])
197 """Return the contents of this PixMap as a PIL Image object."""
199 # our tostring() method returns data in ARGB format,
200 # whereas Image uses RGBA; a bit of slicing fixes this...
201 data
= self
.tostring()[1:] + chr(0)
203 return Image
.fromstring('RGBA',(bounds
[2]-bounds
[0],bounds
[3]-bounds
[1]),data
)
209 path
= EasyDialogs
.AskFileForOpen("Image File:")
212 pm
.fromImage( Image
.open(path
) )