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)
{
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
if (x < 0 || x > (WIDTH-1) || y < 0 || y > (HEIGHT-1))
if (!validPixel(x, y))
{
return;
return false;
}
#endif
drawPixelRaw(x, y, color);
return true;
}
void Arduboy2Base::drawPixelRaw(int16_t x, int16_t y, uint8_t color)
uint16_t row_offset;
uint8_t bit;
@ -501,6 +513,13 @@ void Arduboy2Base::fillCircleHelper
void Arduboy2Base::drawLine
(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
bool steep = abs(y1 - y0) > abs(x1 - x0);
if (steep) {
@ -519,6 +538,9 @@ void Arduboy2Base::drawLine
int16_t err = dx / 2;
int8_t ystep;
bool initial_validity = steep ? validPixel(y0, x0) : validPixel(x0, y0);
bool valid;
if (y0 < y1)
{
@ -533,11 +555,16 @@ void Arduboy2Base::drawLine
{
if (steep)
{
drawPixel(y0, x0, color);
valid = drawPixel(y0, x0, color);
}
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;
@ -561,10 +588,14 @@ void Arduboy2Base::drawRect
void Arduboy2Base::drawFastVLine
(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;
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);
/** \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
* 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);
// Draw a pixel without checking.
// (Not officially part of the API)
void drawPixelRaw(int16_t x, int16_t y, uint8_t color);
/** \brief
* Returns the state of the given pixel in the screen buffer.
*