Make drawLine smarter on PIXEL_SAFE_MODE

The current drawLine leaves all the pixel-safety checks to drawPixel, which is not quite optimal for off-screen lines and partially off-screen lines. This commit changes its behavior by having it skip the draw when both ends are off-screen, and also skip when the "current pixel" goes from on-screen to off-screen.

To achieve this end some API changes are done to drawPixel, since it after all does not tell whether a draw has been attempted. A (very internal; not in header) drawPixelMaybe function is now composed of drawPixel's checking part, and a drawPixelRaw for a complete absense of checks in other "safe" functions. The drawPixel signature is unchanged; it is hoped that drawPixelMaybe would be inlined, its returns stripped, exposing the drawPixelRaw call as a tail call.
This commit is contained in:
Mingye Wang 2018-05-04 20:45:42 -04:00
parent 0eae58c760
commit ecf87428fd
2 changed files with 54 additions and 5 deletions

View File

@ -313,14 +313,26 @@ const uint8_t bitshift_left[] PROGMEM = {
}; };
void Arduboy2Base::drawPixel(int16_t x, int16_t y, uint8_t color) void Arduboy2Base::drawPixel(int16_t x, int16_t y, uint8_t color)
{
drawPixelMaybe(int16_t x, int16_t y, uint8_t color);
}
// Draw a pixel, tell me whether it has been done.
// The intent for inlining is to let drawPixel strip away the return part
// and make drawPixelRaw a tail call.
inline bool drawPixelMaybe(int16_t x, int16_t y, uint8_t color)
{ {
#ifdef PIXEL_SAFE_MODE #ifdef PIXEL_SAFE_MODE
if (x < 0 || x > (WIDTH-1) || y < 0 || y > (HEIGHT-1)) if (!validPixel(x, y))
{ {
return; return false;
} }
#endif #endif
drawPixelRaw(x, y, color);
return true;
}
void Arduboy2Base::drawPixelRaw(int16_t x, int16_t y, uint8_t color)
uint16_t row_offset; uint16_t row_offset;
uint8_t bit; uint8_t bit;
@ -501,6 +513,13 @@ void Arduboy2Base::fillCircleHelper
void Arduboy2Base::drawLine void Arduboy2Base::drawLine
(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint8_t color) (int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint8_t color)
{ {
#ifdef PIXEL_SAFE_MODE
// No need to draw
if (!(validPixel(x0, y0) || validPixel(x1, y1))) {
return;
}
#endif
// bresenham's algorithm - thx wikpedia // bresenham's algorithm - thx wikpedia
bool steep = abs(y1 - y0) > abs(x1 - x0); bool steep = abs(y1 - y0) > abs(x1 - x0);
if (steep) { if (steep) {
@ -520,6 +539,9 @@ void Arduboy2Base::drawLine
int16_t err = dx / 2; int16_t err = dx / 2;
int8_t ystep; int8_t ystep;
bool initial_validity = steep ? validPixel(y0, x0) : validPixel(x0, y0);
bool valid;
if (y0 < y1) if (y0 < y1)
{ {
ystep = 1; ystep = 1;
@ -533,11 +555,16 @@ void Arduboy2Base::drawLine
{ {
if (steep) if (steep)
{ {
drawPixel(y0, x0, color); valid = drawPixel(y0, x0, color);
} }
else else
{ {
drawPixel(x0, y0, color); valid = drawPixel(x0, y0, color);
}
// We have reached the end of valid pixels.
if (initial_validity && !valid) {
break;
} }
err -= dy; err -= dy;
@ -561,10 +588,14 @@ void Arduboy2Base::drawRect
void Arduboy2Base::drawFastVLine void Arduboy2Base::drawFastVLine
(int16_t x, int16_t y, uint8_t h, uint8_t color) (int16_t x, int16_t y, uint8_t h, uint8_t color)
{ {
// Do x bounds checks
if (x < 0 || x >= WIDTH)
return;
int end = y+h; int end = y+h;
for (int a = max(0,y); a < min(end,HEIGHT); a++) for (int a = max(0,y); a < min(end,HEIGHT); a++)
{ {
drawPixel(x,a,color); drawPixelRaw(x,a,color);
} }
} }

View File

@ -430,6 +430,20 @@ class Arduboy2Base : public Arduboy2Core
*/ */
void display(bool clear); void display(bool clear);
/** \brief
* Tells whether a given coordinate points to a valid pixel.
*
* \param x The X coordinate of the pixel.
* \param y The Y coordinate of the pixel.
*
* \details
* This function returns true if x is in the interval [0, WIDTH) and y is in
* the interval [0, HEIGHT).
*/
bool validPixel(int16_t x, int16_t y) {
return !(x < 0 || x > (WIDTH-1) || y < 0 || y > (HEIGHT-1));
}
/** \brief /** \brief
* Set a single pixel in the display buffer to the specified color. * Set a single pixel in the display buffer to the specified color.
* *
@ -444,6 +458,10 @@ class Arduboy2Base : public Arduboy2Core
*/ */
void drawPixel(int16_t x, int16_t y, uint8_t color = WHITE); void drawPixel(int16_t x, int16_t y, uint8_t color = WHITE);
// Draw a pixel without checking.
// (Not officially part of the API)
void drawPixelRaw(int16_t x, int16_t y, uint8_t color);
/** \brief /** \brief
* Returns the state of the given pixel in the screen buffer. * Returns the state of the given pixel in the screen buffer.
* *