The getPixels and setPixels functions for reading and writing pixels from / to bitmaps are rather slow.
When changing a large number or even all pixels of a bitmap, it is a good idea to perform the getPixels()
and setPixels()
just once for each horizontal line and do the rest of the work with the resulting array elements.
In the following examples, a 1000x1000 pixels bitmap should be altered by changing every single pixel. This means reading and writing back one million pixels.
In the first example, every single pixel will be read separately, multiplied by a number and written back. We will stop the time in order to compare to the optimized version.
EXAMPLE 1 - UNOPTIMIZED SCRIPT:
b = bitmap 1000 1000 color:red--create new bitmap with red background st = timestamp()--get start time in milliseconds for y = 1 to 1000 do--go through all lines in the bitmap ( for x = 1 to 1000 do--go through all pixels in a line ( pixels = getPixels b [x-1,y-1] 1--read one pixel from X,Y pixels[1] *= (x+y)/2000.0--alter the pixel setPixels b [x-1,y-1] pixels--write modified pixel back )--end x loop )--end y loop et = timestamp()--get end time in milliseconds print (et-st)--print time to finish display b--show a nice black-red gradient
Running this script on a 2.1GHz system resulted in a time stamp of 16094 milliseconds.
In the second example, instead of reading every single pixel, will be read every line and process all its pixels inside the array returned by getPixels()
before writing the whole line back to the bitmap. This means we will make only 1000 read and 1000 write calls instead of one million reads and one million writes.
EXAMPLE 2 - OPTIMIZED SCRIPT:
b = bitmap 1000 1000 color:red--create new bitmap with red background st = timestamp()--get start time in milliseconds for y = 1 to 1000 do--go through all lines in the bitmap ( pixels = getPixels b [0,y-1] 1000--read all 1000 pixels of a single line for x = 1 to 1000 do--go through all pixels in the line ( pixels[x] *= (x+y)/2000.0--alter the pixel ) setPixels b [0,y-1] pixels--write back the complete modified line ) et = timestamp()--get end time in milliseconds print (et-st)--print time to finish display b--show the same nice black-red gradient
Running the optimized script on the same 2.1GHz system resulted in a time stamp of 4937 milliseconds - more than three times faster!
Since 3ds Max 2010, the pasteBitmap() function can be used in a custom function:
mode to perform per-pixel operations using a MAXScript function. This method does not involve the getPixels()
and setPixels()
calls but does the same and is also great for bothcompositing two bitmaps together and for manipulating pixels ina single bitmap as in our test example.We simply pass the same bitmap twice. The result is normally stored in the second bitmap, in this case it is identical to the first bitmap so we effectively read and write using one bitmap value:
EXAMPLE 3 - ALTERNATIVE SCRIPT:
fn compfn c1 p1 c2 p2 = ( c1 * ((p1.x+p1.y)/2000.0) ) b = bitmap 1000 1000 color:red--create new bitmap with red background st = timestamp()--get start time in milliseconds pasteBitmap b b [0,0] [0,0] type:#function function:compfn--call the function per pixel et = timestamp()--get end time in milliseconds print (et-st)--print time to finish display b--show the same nice black-red gradient
Running the alternative script on the same 2.1GHz system resulted in a time stamp of 3828 milliseconds - a second faster than the optimized script above!
The following table shows the times in milliseconds for the three examples at different image resolutions:
2000x2000 | 1000x1000 | 500x500 | 250x250 | 125x125 | |
---|---|---|---|---|---|
EXAMPLE 1 | 64657 | 16094 | 3578 | 906 | 203 |
EXAMPLE 2 | 20000 | 4937 | 1266 | 344 | 62 |
EXAMPLE 3 | 15187 | 3828 | 906 | 172 | 47 |
Previous Tip
Use the 'flagForeground' node viewport state method
Next Tip