symbian-qemu-0.9.1-12/python-2.6.1/Lib/plat-mac/PixMapWrapper.py
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     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.
       
     5 
       
     6 J. Strout <joe@strout.net>  February 1999"""
       
     7 
       
     8 
       
     9 from warnings import warnpy3k
       
    10 warnpy3k("In 3.x, the PixMapWrapper module is removed.", stacklevel=2)
       
    11 
       
    12 from Carbon import Qd
       
    13 from Carbon import QuickDraw
       
    14 import struct
       
    15 import MacOS
       
    16 import img
       
    17 import imgformat
       
    18 
       
    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
       
    40 }
       
    41 
       
    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
       
    63 }
       
    64 
       
    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."""
       
    69 
       
    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)
       
    84 
       
    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
       
    92 
       
    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]
       
    97 
       
    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
       
   121 
       
   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]
       
   141 
       
   142 
       
   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']
       
   148 
       
   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)
       
   160 
       
   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
       
   180 
       
   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"
       
   190 
       
   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])
       
   199 
       
   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)
       
   208 
       
   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