fixed fast line drawing in GFXcanvas* classes

This commit is contained in:
Michael Kamprath 2020-08-18 22:25:19 -07:00
parent 0752d4bd4f
commit 18eec8bb9e
No known key found for this signature in database
GPG Key ID: E5ED361AF3554882
5 changed files with 584 additions and 52 deletions

View File

@ -1882,6 +1882,191 @@ void GFXcanvas1::fillScreen(uint16_t color) {
}
}
/**************************************************************************/
/*!
@brief Speed optimized vertical line drawing
@param x Line horizontal start point
@param y Line vertical start point
@param h length of vertical line to be drawn, including first point
@param color Binary (on or off) color to fill with
*/
/**************************************************************************/
void GFXcanvas1::drawFastVLine(int16_t x, int16_t y, int16_t h,
uint16_t color) {
if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) {
return;
}
if (y + h > height()) {
h = height() - y;
} else if (h < 0) {
// convert negative heights to their postive equivalent
h *= -1;
y -= h - 1;
if (y < 0) {
h += y;
y = 0;
}
}
if (getRotation() == 0) {
drawFastRawVLine(x, y, h, color);
} else if (getRotation() == 1) {
int16_t t = x;
x = WIDTH - 1 - y;
y = t;
x -= h - 1;
drawFastRawHLine(x, y, h, color);
} else if (getRotation() == 2) {
x = WIDTH - 1 - x;
y = HEIGHT - 1 - y;
y -= h - 1;
drawFastRawVLine(x, y, h, color);
} else if (getRotation() == 3) {
int16_t t = x;
x = y;
y = HEIGHT - 1 - t;
drawFastRawHLine(x, y, h, color);
}
}
/**************************************************************************/
/*!
@brief Speed optimized horizontal line drawing
@param x Line horizontal start point
@param y Line vertical start point
@param w length of horizontal line to be drawn, including first point
@param color Binary (on or off) color to fill with
*/
/**************************************************************************/
void GFXcanvas1::drawFastHLine(int16_t x, int16_t y, int16_t w,
uint16_t color) {
if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) {
return;
}
if (x + w > width()) {
w = width() - x;
} else if (w < 0) {
// convert negative widths to their postive equivalent
w *= -1;
x -= w - 1;
if (x < 0) {
w += x;
x = 0;
}
}
if (getRotation() == 0) {
drawFastRawHLine(x, y, w, color);
} else if (getRotation() == 1) {
int16_t t = x;
x = WIDTH - 1 - y;
y = t;
drawFastRawVLine(x, y, w, color);
} else if (getRotation() == 2) {
x = WIDTH - 1 - x;
y = HEIGHT - 1 - y;
x -= w - 1;
drawFastRawHLine(x, y, w, color);
} else if (getRotation() == 3) {
int16_t t = x;
x = y;
y = HEIGHT - 1 - t;
y -= w - 1;
drawFastRawVLine(x, y, w, color);
}
}
void GFXcanvas1::drawFastRawVLine(int16_t x, int16_t y, int16_t h,
uint16_t color) {
// x & y already in raw (rotation 0) coordinates, no need to transform.
int16_t row_bytes = ((WIDTH + 7) / 8);
uint8_t *buffer = this->getBuffer();
uint8_t *ptr = &buffer[(x / 8) + y * row_bytes];
if (color > 0) {
#ifdef __AVR__
uint8_t bit_mask = pgm_read_byte(&GFXsetBit[x & 7]);
#else
uint8_t bit_mask = (0x80 >> (x & 7));
#endif
for (int16_t i = 0; i < h; i++) {
*ptr |= bit_mask;
ptr += row_bytes;
}
} else {
#ifdef __AVR__
uint8_t bit_mask = pgm_read_byte(&GFXclrBit[x & 7]);
#else
uint8_t bit_mask = ~(0x80 >> (x & 7));
#endif
for (int16_t i = 0; i < h; i++) {
*ptr &= bit_mask;
ptr += row_bytes;
}
}
}
void GFXcanvas1::drawFastRawHLine(int16_t x, int16_t y, int16_t w,
uint16_t color) {
// x & y already in raw (rotation 0) coordinates, no need to transform.
int16_t rowBytes = ((WIDTH + 7) / 8);
uint8_t *buffer = this->getBuffer();
uint8_t *ptr = &buffer[(x / 8) + y * rowBytes];
size_t remainingWidthBits = w;
// check to see if first byte needs to be partially filled
if ((x & 7) > 0) {
// create bit mask for first byte
uint8_t startByteBitMask = 0x00;
for (int8_t i = (x & 7); ((i < 8) && (remainingWidthBits > 0)); i++) {
#ifdef __AVR__
startByteBitMask |= pgm_read_byte(&GFXsetBit[i]);
#else
startByteBitMask |= (0x80 >> i);
#endif
remainingWidthBits--;
}
if (color > 0) {
*ptr |= startByteBitMask;
} else {
*ptr &= ~startByteBitMask;
}
ptr++;
}
// do the next remainingWidthBits bits
if (remainingWidthBits > 0) {
size_t remainingWholeBytes = remainingWidthBits / 8;
size_t lastByteBits = remainingWidthBits % 8;
uint8_t wholeByteColor = color > 0 ? 0xFF : 0x00;
memset(ptr, wholeByteColor, remainingWholeBytes);
if (lastByteBits > 0) {
uint8_t lastByteBitMask = 0x00;
for (int8_t i = 0; i < lastByteBits; i++) {
#ifdef __AVR__
lastByteBitMask |= pgm_read_byte(&GFXsetBit[i]);
#else
lastByteBitMask |= (0x80 >> i);
#endif
}
ptr += remainingWholeBytes;
if (color > 0) {
*ptr |= lastByteBitMask;
} else {
*ptr &= ~lastByteBitMask;
}
}
}
}
/**************************************************************************/
/*!
@brief Instatiate a GFX 8-bit canvas context for graphics
@ -2001,41 +2186,119 @@ void GFXcanvas8::fillScreen(uint16_t color) {
}
}
void GFXcanvas8::writeFastHLine(int16_t x, int16_t y, int16_t w,
uint16_t color) {
if ((x >= _width) || (y < 0) || (y >= _height))
/**************************************************************************/
/*!
@brief Speed optimized vertical line drawing
@param x Line horizontal start point
@param y Line vertical start point
@param h length of vertical line to be drawn, including first point
@param color 8-bit Color to fill with. Only lower byte of uint16_t is
used.
*/
/**************************************************************************/
void GFXcanvas8::drawFastVLine(int16_t x, int16_t y, int16_t h,
uint16_t color) {
if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) {
return;
int16_t x2 = x + w - 1;
if (x2 < 0)
return;
// Clip left/right
if (x < 0) {
x = 0;
w = x2 + 1;
}
if (x2 >= _width)
w = _width - x;
int16_t t;
switch (rotation) {
case 1:
t = x;
if (y + h > height()) {
h = height() - y;
} else if (h < 0) {
// convert negative heights to their postive equivalent
h *= -1;
y -= h - 1;
if (y < 0) {
h += y;
y = 0;
}
}
if (getRotation() == 0) {
drawFastRawVLine(x, y, h, color);
} else if (getRotation() == 1) {
int16_t t = x;
x = WIDTH - 1 - y;
y = t;
break;
case 2:
x -= h - 1;
drawFastRawHLine(x, y, h, color);
} else if (getRotation() == 2) {
x = WIDTH - 1 - x;
y = HEIGHT - 1 - y;
break;
case 3:
t = x;
y -= h - 1;
drawFastRawVLine(x, y, h, color);
} else if (getRotation() == 3) {
int16_t t = x;
x = y;
y = HEIGHT - 1 - t;
break;
drawFastRawHLine(x, y, h, color);
}
}
/**************************************************************************/
/*!
@brief Speed optimized horizontal line drawing
@param x Line horizontal start point
@param y Line vertical start point
@param w length of horizontal line to be drawn, including first point
@param color 8-bit Color to fill with. Only lower byte of uint16_t is
used.
*/
/**************************************************************************/
void GFXcanvas8::drawFastHLine(int16_t x, int16_t y, int16_t w,
uint16_t color) {
if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) {
return;
}
if (x + w > width()) {
w = width() - x;
} else if (w < 0) {
// convert negative widths to their postive equivalent
w *= -1;
x -= w - 1;
if (x < 0) {
w += x;
x = 0;
}
}
if (getRotation() == 0) {
drawFastRawHLine(x, y, w, color);
} else if (getRotation() == 1) {
int16_t t = x;
x = WIDTH - 1 - y;
y = t;
drawFastRawVLine(x, y, w, color);
} else if (getRotation() == 2) {
x = WIDTH - 1 - x;
y = HEIGHT - 1 - y;
x -= w - 1;
drawFastRawHLine(x, y, w, color);
} else if (getRotation() == 3) {
int16_t t = x;
x = y;
y = HEIGHT - 1 - t;
y -= w - 1;
drawFastRawVLine(x, y, w, color);
}
}
void GFXcanvas8::drawFastRawVLine(int16_t x, int16_t y, int16_t h,
uint16_t color) {
// x & y already in raw (rotation 0) coordinates, no need to transform.
uint8_t *buffer_ptr = buffer + y * WIDTH + x;
for (int16_t i = 0; i < h; i++) {
(*buffer_ptr) = color;
buffer_ptr += WIDTH;
}
}
void GFXcanvas8::drawFastRawHLine(int16_t x, int16_t y, int16_t w,
uint16_t color) {
// x & y already in raw (rotation 0) coordinates, no need to transform.
memset(buffer + y * WIDTH + x, color, w);
}
@ -2185,3 +2448,120 @@ void GFXcanvas16::byteSwap(void) {
buffer[i] = __builtin_bswap16(buffer[i]);
}
}
/**************************************************************************/
/*!
@brief Speed optimized vertical line drawing
@param x Line horizontal start point
@param y Line vertical start point
@param h length of vertical line to be drawn, including first point
@param color color 16-bit 5-6-5 Color to draw line with
*/
/**************************************************************************/
void GFXcanvas16::drawFastVLine(int16_t x, int16_t y, int16_t h,
uint16_t color) {
if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) {
return;
}
if (y + h > height()) {
h = height() - y;
} else if (h < 0) {
// convert negative heights to their postive equivalent
h *= -1;
y -= h - 1;
if (y < 0) {
h += y;
y = 0;
}
}
if (getRotation() == 0) {
drawFastRawVLine(x, y, h, color);
} else if (getRotation() == 1) {
int16_t t = x;
x = WIDTH - 1 - y;
y = t;
x -= h - 1;
drawFastRawHLine(x, y, h, color);
} else if (getRotation() == 2) {
x = WIDTH - 1 - x;
y = HEIGHT - 1 - y;
y -= h - 1;
drawFastRawVLine(x, y, h, color);
} else if (getRotation() == 3) {
int16_t t = x;
x = y;
y = HEIGHT - 1 - t;
drawFastRawHLine(x, y, h, color);
}
}
/**************************************************************************/
/*!
@brief Speed optimized horizontal line drawing
@param x Line horizontal start point
@param y Line vertical start point
@param w length of horizontal line to be drawn, including first point
@param color color 16-bit 5-6-5 Color to draw line with
*/
/**************************************************************************/
void GFXcanvas16::drawFastHLine(int16_t x, int16_t y, int16_t w,
uint16_t color) {
if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) {
return;
}
if (x + w > width()) {
w = width() - x;
} else if (w < 0) {
// convert negative widths to their postive equivalent
w *= -1;
x -= w - 1;
if (x < 0) {
w += x;
x = 0;
}
}
if (getRotation() == 0) {
drawFastRawHLine(x, y, w, color);
} else if (getRotation() == 1) {
int16_t t = x;
x = WIDTH - 1 - y;
y = t;
drawFastRawVLine(x, y, w, color);
} else if (getRotation() == 2) {
x = WIDTH - 1 - x;
y = HEIGHT - 1 - y;
x -= w - 1;
drawFastRawHLine(x, y, w, color);
} else if (getRotation() == 3) {
int16_t t = x;
x = y;
y = HEIGHT - 1 - t;
y -= w - 1;
drawFastRawVLine(x, y, w, color);
}
}
void GFXcanvas16::drawFastRawVLine(int16_t x, int16_t y, int16_t h,
uint16_t color) {
// x & y already in raw (rotation 0) coordinates, no need to transform.
uint16_t *buffer_ptr = buffer + y * WIDTH + x;
for (int16_t i = 0; i < h; i++) {
(*buffer_ptr) = color;
buffer_ptr += WIDTH;
}
}
void GFXcanvas16::drawFastRawHLine(int16_t x, int16_t y, int16_t w,
uint16_t color) {
// x & y already in raw (rotation 0) coordinates, no need to transform.
size_t buffer_index = y * WIDTH + x;
for (int16_t i = buffer_index; i < buffer_index + w; i++) {
buffer[i] = color;
}
}

View File

@ -310,6 +310,8 @@ public:
~GFXcanvas1(void);
void drawPixel(int16_t x, int16_t y, uint16_t color);
void fillScreen(uint16_t color);
void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
bool getPixel(int16_t x, int16_t y) const;
/**********************************************************************/
/*!
@ -321,6 +323,8 @@ public:
protected:
bool getRawPixel(int16_t x, int16_t y) const;
void drawFastRawVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
void drawFastRawHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
private:
uint8_t *buffer;
@ -338,7 +342,8 @@ public:
~GFXcanvas8(void);
void drawPixel(int16_t x, int16_t y, uint16_t color);
void fillScreen(uint16_t color);
void writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
uint8_t getPixel(int16_t x, int16_t y) const;
/**********************************************************************/
/*!
@ -350,6 +355,8 @@ public:
protected:
uint8_t getRawPixel(int16_t x, int16_t y) const;
void drawFastRawVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
void drawFastRawHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
private:
uint8_t *buffer;
@ -363,6 +370,8 @@ public:
void drawPixel(int16_t x, int16_t y, uint16_t color);
void fillScreen(uint16_t color);
void byteSwap(void);
void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
uint16_t getPixel(int16_t x, int16_t y) const;
/**********************************************************************/
/*!
@ -374,6 +383,8 @@ public:
protected:
uint16_t getRawPixel(int16_t x, int16_t y) const;
void drawFastRawVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
void drawFastRawHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
private:
uint16_t *buffer;

View File

@ -1,6 +1,7 @@
/***
This example is intended to demonstrate the use of getPixel() versus
getRawPixel() in the GFXcanvas family of classes.
getRawPixel() and the fast horizontal and vertical drawing routines
in the GFXcanvas family of classes,
When using the GFXcanvas* classes as the image buffer for a hardware driver,
there is a need to get individual pixel color values at given physical
@ -15,9 +16,9 @@ in the image. This is useful for getting the pixel value to map to a hardware
pixel location. This method was made protected as only the hardware driver
should be accessing it.
The GFXcanvasSerialDemo class in this example will print to Serial the contents
of the underlying GFXcanvas buffer in both the current rotated layout and the
underlying physical layout.
The GFXcanvas*SerialDemo classes in this example will print to Serial the
contents of the underlying GFXcanvas buffer in both the current rotated layout
and the underlying physical layout.
***/
#include "GFXcanvasSerialDemo.h"
@ -26,46 +27,86 @@ underlying physical layout.
void setup() {
Serial.begin(115200);
// first create a rectangular GFXcanvasSerialDemo object and draw to it
GFXcanvasSerialDemo demo(21, 11);
// first create a rectangular GFXcanvas8SerialDemo object and draw to it
GFXcanvas8SerialDemo demo8(21, 11);
demo.fillScreen(0x00);
demo.setRotation(1); // now canvas is 11x21
demo.fillCircle(5, 10, 5, 0xAA);
demo.writeLine(0, 0, 10, 0, 0x11);
demo.writeLine(0, 10, 10, 10, 0x22);
demo.writeLine(0, 20, 10, 20, 0x33);
demo.writeLine(0, 0, 0, 20, 0x44);
demo.writeLine(10, 0, 10, 20, 0x55);
demo8.fillScreen(0x00);
demo8.setRotation(1); // now canvas is 11x21
demo8.fillCircle(5, 10, 5, 0xAA);
demo8.writeFastHLine(0, 0, 11, 0x11);
demo8.writeFastHLine(10, 10, -11, 0x22);
demo8.writeFastHLine(0, 20, 11, 0x33);
demo8.writeFastVLine(0, 0, 21, 0x44);
demo8.writeFastVLine(10, 20, -21, 0x55);
Serial.println("Demonstrating GFXcanvas rotated and raw pixels.\n");
// print it out rotated
Serial.println("The canvas's content in the rotation of '0':\n");
demo.setRotation(0);
demo.print(true);
demo8.setRotation(0);
demo8.print(true);
Serial.println("\n");
Serial.println("The canvas's content in the rotation of '1' (which is what "
"it was drawn in):\n");
demo.setRotation(1);
demo.print(true);
demo8.setRotation(1);
demo8.print(true);
Serial.println("\n");
Serial.println("The canvas's content in the rotation of '2':\n");
demo.setRotation(2);
demo.print(true);
demo8.setRotation(2);
demo8.print(true);
Serial.println("\n");
Serial.println("The canvas's content in the rotation of '3':\n");
demo.setRotation(3);
demo.print(true);
demo8.setRotation(3);
demo8.print(true);
Serial.println("\n");
// print it out unrotated
Serial.println("The canvas's content in it's raw, physical layout:\n");
demo.print(false);
demo8.print(false);
Serial.println("\n");
// Demonstrate GFXcanvas1SerialDemo
GFXcanvas1SerialDemo demo1(21, 11);
demo1.fillScreen(0);
demo1.setRotation(0);
demo1.writeFastHLine(0, 0, 9, 1);
demo1.setRotation(1);
demo1.writeFastHLine(0, 0, 9, 1);
demo1.setRotation(2);
demo1.writeFastHLine(0, 0, 9, 1);
demo1.setRotation(3);
demo1.writeFastHLine(0, 0, 9, 1);
demo1.setRotation(1);
demo1.fillRect(3, 8, 5, 5, 1);
Serial.println("\nThe GFXcanvas1 raw content after drawing a fast horizontal "
"line in each rotation:\n");
demo1.print(false);
Serial.println("\n");
// Demonstrate GFXcanvas16SerialDemo
GFXcanvas16SerialDemo demo16(21, 11);
demo16.fillScreen(0);
demo16.setRotation(0);
demo16.writeFastHLine(0, 0, 9, 0x1111);
demo16.setRotation(1);
demo16.writeFastHLine(0, 0, 9, 0x2222);
demo16.setRotation(2);
demo16.writeFastHLine(0, 0, 9, 0x3333);
demo16.setRotation(3);
demo16.writeFastHLine(0, 0, 9, 0x4444);
demo16.setRotation(1);
demo16.fillRect(3, 8, 5, 5, 0x8888);
Serial.println("\nThe GFXcanvas16 raw content after drawing a fast "
"horizontal line in each rotation:\n");
demo16.print(false);
Serial.println("\n");
}

View File

@ -1,10 +1,40 @@
#include "GFXcanvasSerialDemo.h"
#include <Arduino.h>
GFXcanvasSerialDemo::GFXcanvasSerialDemo(uint16_t w, uint16_t h)
GFXcanvas1SerialDemo::GFXcanvas1SerialDemo(uint16_t w, uint16_t h)
: GFXcanvas1(w, h) {}
void GFXcanvas1SerialDemo::print(bool rotated) {
char pixel_buffer[8];
uint16_t width, height;
if (rotated) {
width = this->width();
height = this->height();
} else {
width = this->WIDTH;
height = this->HEIGHT;
}
for (uint16_t y = 0; y < height; y++) {
for (uint16_t x = 0; x < width; x++) {
bool pixel;
if (rotated) {
pixel = this->getPixel(x, y);
} else {
pixel = this->getRawPixel(x, y);
}
sprintf(pixel_buffer, " %d", pixel);
Serial.print(pixel_buffer);
}
Serial.print("\n");
}
}
GFXcanvas8SerialDemo::GFXcanvas8SerialDemo(uint16_t w, uint16_t h)
: GFXcanvas8(w, h) {}
void GFXcanvasSerialDemo::print(bool rotated) {
void GFXcanvas8SerialDemo::print(bool rotated) {
char pixel_buffer[8];
uint16_t width, height;
@ -30,3 +60,33 @@ void GFXcanvasSerialDemo::print(bool rotated) {
Serial.print("\n");
}
}
GFXcanvas16SerialDemo::GFXcanvas16SerialDemo(uint16_t w, uint16_t h)
: GFXcanvas16(w, h) {}
void GFXcanvas16SerialDemo::print(bool rotated) {
char pixel_buffer[8];
uint16_t width, height;
if (rotated) {
width = this->width();
height = this->height();
} else {
width = this->WIDTH;
height = this->HEIGHT;
}
for (uint16_t y = 0; y < height; y++) {
for (uint16_t x = 0; x < width; x++) {
uint16_t pixel;
if (rotated) {
pixel = this->getPixel(x, y);
} else {
pixel = this->getRawPixel(x, y);
}
sprintf(pixel_buffer, " %04x", pixel);
Serial.print(pixel_buffer);
}
Serial.print("\n");
}
}

View File

@ -8,9 +8,49 @@
for a device driver.
*/
/**********************************************************************/
class GFXcanvasSerialDemo : public GFXcanvas8 {
class GFXcanvas1SerialDemo : public GFXcanvas1 {
public:
GFXcanvasSerialDemo(uint16_t w, uint16_t h);
GFXcanvas1SerialDemo(uint16_t w, uint16_t h);
/**********************************************************************/
/*!
@brief Prints the current contents of the canvas to Serial
@param rotated true to print according to the current GFX rotation,
false to print to the native rotation of the canvas (or unrotated).
*/
/**********************************************************************/
void print(bool rotated);
};
/**********************************************************************/
/*!
@brief Demonstrates using the GFXconvas classes as the backing store
for a device driver.
*/
/**********************************************************************/
class GFXcanvas8SerialDemo : public GFXcanvas8 {
public:
GFXcanvas8SerialDemo(uint16_t w, uint16_t h);
/**********************************************************************/
/*!
@brief Prints the current contents of the canvas to Serial
@param rotated true to print according to the current GFX rotation,
false to print to the native rotation of the canvas (or unrotated).
*/
/**********************************************************************/
void print(bool rotated);
};
/**********************************************************************/
/*!
@brief Demonstrates using the GFXconvas classes as the backing store
for a device driver.
*/
/**********************************************************************/
class GFXcanvas16SerialDemo : public GFXcanvas16 {
public:
GFXcanvas16SerialDemo(uint16_t w, uint16_t h);
/**********************************************************************/
/*!