]>
Commit | Line | Data |
---|---|---|
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 |