1081
Axe / [Tutorial] Optimize pixel-by-pixel drawing
« on: June 21, 2013, 09:22:36 am »
Hallaw,
When you have to draw a great part of the screen pixel by pixel (like for a game of life), the program usually become reaaally slow, since Axe is not good at pixel drawing. So I've thought of a method to greatly speed up this kind of tasks.
So, how does it work ? You know that the TI-z80-s screen (excepted the 84+ C) takes 1 byte to code 8 pixels, so that 1 bit = 1 pixel. The technique consists in calculating each pixel as we would do normally, and then turn on or off the corresponding bit of a variable instead of turning it on on the screen (which is absolutely slower). Then, when the 8 bits of the variable have been modified the way you wanted, you can directly store the variable in the corresponding byte of the buffer, thus turning on/off 8 pixels at a time. Of course, this'll only work if the width of area you want to paint is a multiple of 8.
So let's do it !
Let's say that our function is named BuildByte, since that's what it does.
First, we need a counter. This counter must send a signal every 8 times we call it.
:.Only once
:0→C
:
:Lbl BuildByte
:If C++^8
:.The counter doesn't send the signal
:Else
:.It does
:End
:...
Then, we need the variable which will contain the pixels data :
:.It must be initialized to 0 at the beginning of the program
:0→B
Say that the function BuildByte takes three arguments, the X-coordinate of the byte where the pixel must be drawn (not the pixel, so it must be a number between 0 and 11), the Y-coordinate of the pixel (between 0 and 63), then 0 if the current pixel must be white, or 1 if it must be black. We need to put this 0 or 1 to the corresponding bit of B. Then if C is a multiple of 8, then it means that the 8 bits of B have been set, and we store B in the corresponding byte of L6.
But how do we do set the corresponding bit precisely, and not any other bit ? It's actually pretty simple : we always set the first bit, and then if C modulo 8 is not 0, we shift B left once. Why that ? After 7 shifts, the first pixel/bit you passed to the function will become the last bit → the first pixel of the byte when it's on-screen.
:Lbl BuildByte
:.r1 is the X coordinate
:;r2 is the Y coordinate
:.r3 is the value of the pixel
:
:r3 or B→B
:If C++^8
:B*2→B
:Else
:B→{r2*12+r1+L6}
:0→B
:End
:Return
Remember that this will be efficient only if a relatively high number of calculations are needed for each pixel. For example, this plasma program I wrote a while ago uses this method (attached screenshot runs at 6 MHz).
Hoping that this'll help you
When you have to draw a great part of the screen pixel by pixel (like for a game of life), the program usually become reaaally slow, since Axe is not good at pixel drawing. So I've thought of a method to greatly speed up this kind of tasks.
So, how does it work ? You know that the TI-z80-s screen (excepted the 84+ C) takes 1 byte to code 8 pixels, so that 1 bit = 1 pixel. The technique consists in calculating each pixel as we would do normally, and then turn on or off the corresponding bit of a variable instead of turning it on on the screen (which is absolutely slower). Then, when the 8 bits of the variable have been modified the way you wanted, you can directly store the variable in the corresponding byte of the buffer, thus turning on/off 8 pixels at a time. Of course, this'll only work if the width of area you want to paint is a multiple of 8.
So let's do it !
Let's say that our function is named BuildByte, since that's what it does.
First, we need a counter. This counter must send a signal every 8 times we call it.
:.Only once
:0→C
:
:Lbl BuildByte
:If C++^8
:.The counter doesn't send the signal
:Else
:.It does
:End
:...
Then, we need the variable which will contain the pixels data :
:.It must be initialized to 0 at the beginning of the program
:0→B
Say that the function BuildByte takes three arguments, the X-coordinate of the byte where the pixel must be drawn (not the pixel, so it must be a number between 0 and 11), the Y-coordinate of the pixel (between 0 and 63), then 0 if the current pixel must be white, or 1 if it must be black. We need to put this 0 or 1 to the corresponding bit of B. Then if C is a multiple of 8, then it means that the 8 bits of B have been set, and we store B in the corresponding byte of L6.
But how do we do set the corresponding bit precisely, and not any other bit ? It's actually pretty simple : we always set the first bit, and then if C modulo 8 is not 0, we shift B left once. Why that ? After 7 shifts, the first pixel/bit you passed to the function will become the last bit → the first pixel of the byte when it's on-screen.
:Lbl BuildByte
:.r1 is the X coordinate
:;r2 is the Y coordinate
:.r3 is the value of the pixel
:
:r3 or B→B
:If C++^8
:B*2→B
:Else
:B→{r2*12+r1+L6}
:0→B
:End
:Return
Remember that this will be efficient only if a relatively high number of calculations are needed for each pixel. For example, this plasma program I wrote a while ago uses this method (attached screenshot runs at 6 MHz).
Hoping that this'll help you