Quellcode durchsuchen

fixed fast line drawing in GFXcanvas* classes

Michael Kamprath vor 5 Jahren
Ursprung
Commit
18eec8bb9e

+ 402 - 22
Adafruit_GFX.cpp

@@ -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)
+  }
+
+  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   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;
+  }
 
-  // Clip left/right
-  if (x < 0) {
-    x = 0;
-    w = x2 + 1;
+  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 (x2 >= _width)
-    w = _width - x;
 
-  int16_t t;
-  switch (rotation) {
-  case 1:
-    t = x;
+  if (getRotation() == 0) {
+    drawFastRawHLine(x, y, w, color);
+  } else if (getRotation() == 1) {
+    int16_t t = x;
     x = WIDTH - 1 - y;
     y = t;
-    break;
-  case 2:
+    drawFastRawVLine(x, y, w, color);
+  } else if (getRotation() == 2) {
     x = WIDTH - 1 - x;
     y = HEIGHT - 1 - y;
-    break;
-  case 3:
-    t = x;
+
+    x -= w - 1;
+    drawFastRawHLine(x, y, w, color);
+  } else if (getRotation() == 3) {
+    int16_t t = x;
     x = y;
     y = HEIGHT - 1 - t;
-    break;
+    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;
+  }
+}

+ 12 - 1
Adafruit_GFX.h

@@ -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;

+ 64 - 23
examples/GFXcanvas/GFXcanvas.ino

@@ -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");
 }
 

+ 62 - 2
examples/GFXcanvas/GFXcanvasSerialDemo.cpp

@@ -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");
+  }
+}

+ 42 - 2
examples/GFXcanvas/GFXcanvasSerialDemo.h

@@ -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);
 
   /**********************************************************************/
   /*!