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