1#!/usr/bin/env python 2 3import wx 4 5# use the numpy code instead of the raw access code for comparison 6USE_NUMPY = False 7 8# time the execution of making a bitmap? 9TIMEIT = False 10 11# how big to make the bitmaps 12DIM = 100 13 14# should we use a wx.GraphicsContext for painting? 15TEST_GC = False 16 17#---------------------------------------------------------------------- 18# attempt to import a numeric module if requested to 19 20if USE_NUMPY: 21 try: 22 import numpy 23 def makeByteArray(shape): 24 return numpy.empty(shape, numpy.uint8) 25 numtype = 'numpy' 26 except ImportError: 27 try: 28 import numarray 29 def makeByteArray(shape): 30 arr = numarray.array(shape=shape, typecode='u1') 31 arr[:] = 0 32 return arr 33 numtype = 'numarray' 34 except ImportError: 35 USE_NUMPY = False 36 37 38#---------------------------------------------------------------------- 39 40class TestPanel(wx.Panel): 41 def __init__(self, parent, log): 42 self.log = log 43 wx.Panel.__init__(self, parent, -1) 44 self.Bind(wx.EVT_PAINT, self.OnPaint) 45 46 if TIMEIT: 47 import timeit 48 timeit.s = self # Put self in timeit's global namespace as 49 # 's' so it can be found in the code 50 # snippets being tested. 51 if not USE_NUMPY: 52 t = timeit.Timer("bmp = s.MakeBitmap(10, 20, 30)") 53 else: 54 t = timeit.Timer("bmp = s.MakeBitmap2(10, 20, 30)") 55 log.write("Timing...\n") 56 num = 100 57 tm = t.timeit(num) 58 log.write("%d passes in %f seconds == %f seconds per pass " % 59 (num, tm, tm/num)) 60 61 if not USE_NUMPY: 62 log.write("using raw access\n") 63 self.redBmp = self.MakeBitmap(178, 34, 34) 64 self.greenBmp = self.MakeBitmap( 35, 142, 35) 65 self.blueBmp = self.MakeBitmap( 0, 0, 139) 66 else: 67 log.write("using %s\n" % numtype) 68 self.redBmp = self.MakeBitmap2(178, 34, 34) 69 self.greenBmp = self.MakeBitmap2( 35, 142, 35) 70 self.blueBmp = self.MakeBitmap2( 0, 0, 139) 71 72 73 def OnPaint(self, evt): 74 dc = wx.PaintDC(self) 75 if not TEST_GC: 76 dc.DrawBitmap(self.redBmp, 50, 50, True) 77 dc.DrawBitmap(self.greenBmp, 110, 110, True) 78 dc.DrawBitmap(self.blueBmp, 170, 50, True) 79 self.log.write("using wx.DC\n") 80 else: 81 gc = wx.GraphicsContext.Create(dc) 82 gc.DrawBitmap(self.redBmp, 50, 50, DIM,DIM) 83 gc.DrawBitmap(self.greenBmp, 110, 110, DIM,DIM) 84 gc.DrawBitmap(self.blueBmp, 170, 50, DIM,DIM) 85 self.log.write("using wx.GraphicsContext\n") 86 87 def MakeBitmap(self, red, green, blue, alpha=128): 88 # Create the bitmap that we will stuff pixel values into using 89 # the raw bitmap access classes. 90 bmp = wx.Bitmap(DIM, DIM, 32) 91 92 # Create an object that facilitates access to the bitmap's 93 # pixel buffer 94 pixelData = wx.AlphaPixelData(bmp) 95 if not pixelData: 96 raise RuntimeError("Failed to gain raw access to bitmap data.") 97 98 # We have two ways to access each pixel, first we'll use an 99 # iterator to set every pixel to the colour and alpha values 100 # passed in. 101 for pixel in pixelData: 102 pixel.Set(red, green, blue, alpha) 103 104 # This block of code is another way to do the same as above, 105 # but with the accessor interface instead of the Python 106 # iterator. It is a bit faster than the above because it 107 # avoids the iterator/generator magic, but it is not nearly as 108 # 'clean' looking ;-) 109 #pixels = pixelData.GetPixels() 110 #for y in range(DIM): 111 # pixels.MoveTo(pixelData, 0, y) 112 # for x in range(DIM): 113 # pixels.Set(red, green, blue, alpha) 114 # pixels.nextPixel() 115 116 117 118 # Next we'll use the pixel accessor to set the border pixels 119 # to be fully opaque 120 pixels = pixelData.GetPixels() 121 for x in range(DIM): 122 pixels.MoveTo(pixelData, x, 0) 123 pixels.Set(red, green, blue, wx.ALPHA_OPAQUE) 124 pixels.MoveTo(pixelData, x, DIM-1) 125 pixels.Set(red, green, blue, wx.ALPHA_OPAQUE) 126 for y in range(DIM): 127 pixels.MoveTo(pixelData, 0, y) 128 pixels.Set(red, green, blue, wx.ALPHA_OPAQUE) 129 pixels.MoveTo(pixelData, DIM-1, y) 130 pixels.Set(red, green, blue, wx.ALPHA_OPAQUE) 131 132 return bmp 133 134 135 def MakeBitmap2(self, red, green, blue, alpha=128): 136 # Make an array of bytes that is DIM*DIM in size, with enough 137 # slots for each pixel to have a RGB and A value 138 arr = makeByteArray( (DIM,DIM, 4) ) 139 140 # just some indexes to keep track of which byte is which 141 R, G, B, A = range(4) 142 143 # initialize all pixel values to the values passed in 144 arr[:,:,R] = red 145 arr[:,:,G] = green 146 arr[:,:,B] = blue 147 arr[:,:,A] = alpha 148 149 # Set the alpha for the border pixels to be fully opaque 150 arr[0, 0:DIM, A] = wx.ALPHA_OPAQUE # first row 151 arr[DIM-1, 0:DIM, A] = wx.ALPHA_OPAQUE # last row 152 arr[0:DIM, 0, A] = wx.ALPHA_OPAQUE # first col 153 arr[0:DIM, DIM-1, A] = wx.ALPHA_OPAQUE # last col 154 155 # finally, use the array to create a bitmap 156 bmp = wx.BitmapFromBufferRGBA(DIM, DIM, arr) 157 return bmp 158 159 160 161#---------------------------------------------------------------------- 162 163def runTest(frame, nb, log): 164 win = TestPanel(nb, log) 165 return win 166 167#---------------------------------------------------------------------- 168 169 170 171overview = """<html><body> 172<h2><center>Raw Bitmap Access</center></h2> 173 174wx.NativePixelData and wx.AlphaPixelData provide a cross-platform way 175to access the platform-specific pixel buffer within a wx.Bitmap. They 176provide both a random access method, and an iterator interface. 177 178<p>Unfortunately, although these classes are convienient ways to access 179and update the contents of a wx.Bitmap, we lose most of the efficiency 180of the C++ classes by requiring one or more Python-to-C++ transitions 181for each pixel. In fact it can be <b>much</b> slower than the other 182ways of creating a bitmap from scratch, especially now that 183wx.BitmapFromBuffer exists and can save the time needed to copy from a 184wx.Image. 185 186<p>To see this difference for yourself this module has been 187instrumented to allow you to experiment with using either the raw 188access or numpy/numarray, and also to time how long it takes to create 189100 bitmaps like you see on the screen. Simply edit this module in 190the \"Demo Code\" tab and set TIMEIT to True and then watch 191the log window when the sample is reloaded. To try numpy or numarray 192(if you have them installed) then set USE_NUMPY to True as well, and 193watch the log window again. On my machines there is about <b>an 194order of magnitude</b> difference between the raw access functions 195and using a numarray.array with wx.BitmapFromBufferRGBA! Almost 196another order of magnitude improvement can be gained with using the 197new numpy module! 198 199</body></html> 200""" 201 202 203 204if __name__ == '__main__': 205 import sys,os 206 import run 207 run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:]) 208 209