Frequently Asked Questions > Writing Better and Faster Scripts > How To Make It Faster? >

# Never get a single pixel when you can get a whole line

MAXScript FAQ > How To Make It Faster > Never get a single pixel when you can get a whole line

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

Only calculate once if possible