BurstSPI support for improved performance

Fork of RA8875 by David Smart

Revision:
73:f22a18707b5e
Parent:
72:ecffe56af969
Child:
74:686faa218914
--- a/RA8875.cpp	Sat Oct 11 17:24:29 2014 +0000
+++ b/RA8875.cpp	Sun Nov 09 18:52:46 2014 +0000
@@ -1,7 +1,7 @@
 /// RA8875 Display Controller Library.
-/// 
+///
 /// This is being created for a Raio RA8875-based display from buydisplay.com,
-/// which is 480 x 272 using a 4-wire SPI interface. 
+/// which is 480 x 272 using a 4-wire SPI interface.
 /// This display controller is used in other display resolutions, up to 800x600.
 /// While this driver has not been tested with these variants, nothing was done
 /// to prevent easily supporting them.
@@ -16,10 +16,31 @@
 #define INFO(x, ...) std::printf("[INF %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
 #define WARN(x, ...) std::printf("[WRN %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
 #define ERR(x, ...)  std::printf("[ERR %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
+static void HexDump(char * title, uint8_t * p, int count)
+{
+    int i;
+    char buf[100] = "0000: ";
+
+    if (*title)
+        INFO("%s", title);
+    for (i=0; i<count; ) {
+        sprintf(buf + strlen(buf), "%02X ", *(p+i));
+        if ((++i & 0x0F) == 0x00) {
+            INFO("%s", buf);
+            if (i < count)
+                sprintf(buf, "%04X: ", i);
+            else
+                buf[0] = '\0';
+        }
+    }
+    if (strlen(buf))
+        INFO("%s", buf);
+}
 #else
 #define INFO(x, ...)
 #define WARN(x, ...)
 #define ERR(x, ...)
+#define HexDump(a, b, c)
 #endif
 
 
@@ -31,12 +52,11 @@
 #define PERFORMANCE_RESET performance.reset()
 #define REGISTERPERFORMANCE(a) RegisterPerformance(a)
 #define COUNTIDLETIME(a) CountIdleTime(a)
-static const char *metricsName[] = 
-{
-    "Cls", "Pixel", "Pixel Stream", 
+static const char *metricsName[] = {
+    "Cls", "Pixel", "Pixel Stream",
     "Read Pixel", "Read Pixel Stream",
-    "Line", 
-    "Rectangle", "Rounded Rectangle", 
+    "Line",
+    "Rectangle", "Rounded Rectangle",
     "Triangle", "Circle", "Ellipse"
 };
 #else
@@ -45,7 +65,7 @@
 #define COUNTIDLETIME(a)
 #endif
 
-// When it is going to poll a register for completion, how many 
+// When it is going to poll a register for completion, how many
 // uSec should it wait between each polling activity.
 #define POLLWAITuSec 10
 
@@ -123,14 +143,23 @@
     return noerror;
 }
 
+color_t RA8875::GetBackgroundTransparencyColor(void)
+{
+    RGBQUAD q;
+    q.rgbRed = ReadCommand(0x67);
+    q.rgbGreen = ReadCommand(0x68);
+    q.rgbBlue = ReadCommand(0x69);
+    return RGBQuadToRGB16(&q, 0);
+}
+
 // ### Touch Panel support code additions begin here
 
 RetCode_t RA8875::TouchPanelInit(void)
-{        
+{
     //TPCR0: Set enable bit, default sample time, wakeup, and ADC clock
     WriteCommand(TPCR0, TP_ENABLE | TP_ADC_SAMPLE_DEFAULT_CLKS | TP_ADC_CLKDIV_DEFAULT);
     // TPCR1: Set auto/manual, Ref voltage, debounce, manual mode params
-    WriteCommand(TPCR1, TP_MODE_DEFAULT | TP_DEBOUNCE_DEFAULT);   
+    WriteCommand(TPCR1, TP_MODE_DEFAULT | TP_DEBOUNCE_DEFAULT);
     WriteCommand(INTC1, ReadCommand(INTC1) | RA8875_INT_TP);        // reg INTC1: Enable Touch Panel Interrupts (D2 = 1)
     WriteCommand(INTC2, RA8875_INT_TP);                            // reg INTC2: Clear any TP interrupt flag
     return noerror;
@@ -140,13 +169,13 @@
 {
     // Parameter bounds check
     if( \
-        !(bTpEnable == TP_ENABLE || bTpEnable == TP_ENABLE) || \
-        !(bTpAutoManual == TP_MODE_AUTO || bTpAutoManual == TP_MODE_MANUAL) || \
-        !(bTpDebounce == TP_DEBOUNCE_OFF || bTpDebounce == TP_DEBOUNCE_ON) || \
-        !(bTpManualMode <= TP_MANUAL_LATCH_Y) || \
-        !(bTpAdcClkDiv <= TP_ADC_CLKDIV_128) || \
-        !(bTpAdcSampleTime <= TP_ADC_SAMPLE_65536_CLKS) \
-        ) return bad_parameter;
+            !(bTpEnable == TP_ENABLE || bTpEnable == TP_ENABLE) || \
+            !(bTpAutoManual == TP_MODE_AUTO || bTpAutoManual == TP_MODE_MANUAL) || \
+            !(bTpDebounce == TP_DEBOUNCE_OFF || bTpDebounce == TP_DEBOUNCE_ON) || \
+            !(bTpManualMode <= TP_MANUAL_LATCH_Y) || \
+            !(bTpAdcClkDiv <= TP_ADC_CLKDIV_128) || \
+            !(bTpAdcSampleTime <= TP_ADC_SAMPLE_65536_CLKS) \
+      ) return bad_parameter;
     // Construct the config byte for TPCR0 and write them
     WriteCommand(TPCR0, bTpEnable | bTpAdcClkDiv | bTpAdcSampleTime);    // Note: Wakeup is never enabled
     // Construct the config byte for TPCR1 and write them
@@ -154,7 +183,7 @@
     // Set up the interrupt flag and enable bits
     WriteCommand(INTC1, ReadCommand(INTC1) | RA8875_INT_TP);        // reg INTC1: Enable Touch Panel Interrupts (D2 = 1)
     WriteCommand(INTC2, RA8875_INT_TP);                            // reg INTC2: Clear any TP interrupt flag
-    return noerror;    
+    return noerror;
 }
 
 unsigned char RA8875::TouchPanelRead(loc_t *x, loc_t *y)
@@ -163,26 +192,26 @@
     static int xbuf[TPBUFSIZE], ybuf[TPBUFSIZE], sample = 0;
     int i, j, temp;
 
-    if( (ReadCommand(INTC2) & RA8875_INT_TP) ) {        // Test for TP Interrupt pending in register INTC2    
+    if( (ReadCommand(INTC2) & RA8875_INT_TP) ) {        // Test for TP Interrupt pending in register INTC2
         // Get the next data samples
         ybuf[sample] =  ReadCommand(TPYH) << 2 | ( (ReadCommand(TPXYL) & 0xC) >> 2 );   // D[9:2] from reg TPYH, D[1:0] from reg TPXYL[3:2]
         xbuf[sample] =  ReadCommand(TPXH) << 2 | ( (ReadCommand(TPXYL) & 0x3)      );   // D[9:2] from reg TPXH, D[1:0] from reg TPXYL[1:0]
         // Check for a complete set
         if(++sample == TPBUFSIZE) {
-           // Buffers are full, so process them using Finn's method described in Analog Dialogue No. 44, Feb 2010
-           // This requires sorting the samples in order of size, then discarding the top 25% and
-           //   bottom 25% as noise spikes. Finally, the middle 50% of the values are averaged to
-           //   reduce Gaussian noise.
-           
-           // Sort the Y buffer using an Insertion Sort
+            // Buffers are full, so process them using Finn's method described in Analog Dialogue No. 44, Feb 2010
+            // This requires sorting the samples in order of size, then discarding the top 25% and
+            //   bottom 25% as noise spikes. Finally, the middle 50% of the values are averaged to
+            //   reduce Gaussian noise.
+
+            // Sort the Y buffer using an Insertion Sort
             for(i = 1; i <= TPBUFSIZE; i++) {
                 temp = ybuf[i];
                 j = i;
                 while( j && (ybuf[j-1] > temp) ) {
                     ybuf[j] = ybuf[j-1];
-                    j = j-1;    
+                    j = j-1;
                 }
-                ybuf[j] = temp;             
+                ybuf[j] = temp;
             } // End of Y sort
             // Sort the X buffer the same way
             for(i = 1; i <= TPBUFSIZE; i++) {
@@ -190,31 +219,30 @@
                 j = i;
                 while( j && (xbuf[j-1] > temp) ) {
                     xbuf[j] = xbuf[j-1];
-                    j = j-1;    
+                    j = j-1;
                 }
-                xbuf[j] = temp;             
+                xbuf[j] = temp;
             } // End of X sort
             // Average the middle half of the  Y values and report them
             j = 0;
             for(i = (TPBUFSIZE/4) - 1; i < TPBUFSIZE - TPBUFSIZE/4; i++ ) {
-                j += ybuf[i];    
+                j += ybuf[i];
             }
             *y = j * (float)2/TPBUFSIZE;    // This is the average
             // Average the middle half of the  X values and report them
             j = 0;
             for(i = (TPBUFSIZE/4) - 1; i < TPBUFSIZE - TPBUFSIZE/4; i++ ) {
-                j += xbuf[i];    
+                j += xbuf[i];
             }
-            *x = j * (float)2/TPBUFSIZE;    // This is the average           
-            // Tidy up and return 
+            *x = j * (float)2/TPBUFSIZE;    // This is the average
+            // Tidy up and return
             touchready = 1;
             sample = 0;             // Ready to start on the next set of data samples
-        }
-        else {
+        } else {
             // Buffer not yet full, so do not return any results yet
             touchready = 0;
         }
-        WriteCommand(INTC2, RA8875_INT_TP);            // reg INTC2: Clear that TP interrupt flag                       
+        WriteCommand(INTC2, RA8875_INT_TP);            // reg INTC2: Clear that TP interrupt flag
     } // End of initial if -- data has been read and processed
     else
         touchready = 0;         // Touch Panel "Int" was not set
@@ -225,24 +253,23 @@
 {
     unsigned char touchready;
 
-    if( (ReadCommand(INTC2) & RA8875_INT_TP) ) {        // Test for TP Interrupt pending in register INTC2    
+    if( (ReadCommand(INTC2) & RA8875_INT_TP) ) {        // Test for TP Interrupt pending in register INTC2
         *y =  ReadCommand(TPYH) << 2 | ( (ReadCommand(TPXYL) & 0xC) >> 2 );   // D[9:2] from reg TPYH, D[1:0] from reg TPXYL[3:2]
         *x =  ReadCommand(TPXH) << 2 | ( (ReadCommand(TPXYL) & 0x3)      );   // D[9:2] from reg TPXH, D[1:0] from reg TPXYL[1:0]
-        WriteCommand(INTC2, RA8875_INT_TP);            // reg INTC2: Clear that TP interrupt flag   
+        WriteCommand(INTC2, RA8875_INT_TP);            // reg INTC2: Clear that TP interrupt flag
         touchready = 1;
-    }
-    else
-        touchready = 0;  
+    } else
+        touchready = 0;
     return touchready;
 }
 // #### end of touch panel code additions
 
 
-RetCode_t RA8875::KeypadInit(bool scanEnable, bool longDetect, uint8_t sampleTime, uint8_t scanFrequency, 
-    uint8_t longTimeAdjustment, bool interruptEnable, bool wakeupEnable)
+RetCode_t RA8875::KeypadInit(bool scanEnable, bool longDetect, uint8_t sampleTime, uint8_t scanFrequency,
+                             uint8_t longTimeAdjustment, bool interruptEnable, bool wakeupEnable)
 {
     uint8_t value = 0;
-    
+
     if (sampleTime > 3 || scanFrequency > 7 || longTimeAdjustment  > 3)
         return bad_parameter;
     value |= (scanEnable) ? 0x80 : 0x00;
@@ -250,12 +277,12 @@
     value |= (sampleTime & 0x03) << 4;
     value |= (scanFrequency & 0x07);
     WriteCommand(0xC0, value);   // Enable Key Scan (and ignore possibility of an error)
-    
+
     value = 0;
     value |= (wakeupEnable) ? 0x80 : 0x00;
     value |= (longTimeAdjustment & 0x03) << 2;
     WriteCommand(0xC1, value);  // (and ignore possibility of an error)
-    
+
     value = ReadCommand(0xF0);  // (and ignore possibility of an error)
     value &= ~0x10;
     value |= (interruptEnable) ? 0x10 : 0x00;
@@ -294,7 +321,7 @@
 void RA8875::RegisterPerformance(method_e method)
 {
     unsigned long elapsed = performance.read_us();
-    
+
     if (method < METRICCOUNT && elapsed > metrics[method])
         metrics[method] = elapsed;
 }
@@ -317,10 +344,10 @@
 
 RetCode_t RA8875::WriteCommandW(uint8_t command, uint16_t data)
 {
-    #if 1
+#if 1
     WriteCommand(command, data & 0xFF);
     WriteCommand(command+1, data >> 8);
-    #else
+#else
     // This should be a little faster, but doesn't work...
     INFO("WriteCommandW(%02X, %04X)", command, data);
     select(true);
@@ -330,7 +357,7 @@
     spiwrite(data & 0xFF);
     spiwrite(data >> 8);
     select(false);
-    #endif
+#endif
     return noerror;
 }
 
@@ -380,7 +407,7 @@
 unsigned char RA8875::ReadData(void)
 {
     unsigned char data;
-    
+
     select(true);
     spiwrite(0x40);
     data = spiread();
@@ -392,7 +419,7 @@
 uint16_t RA8875::ReadDataW(void)
 {
     uint16_t data;
-    
+
     select(true);
     spiwrite(0x40);
     data  = spiread();
@@ -405,7 +432,7 @@
 unsigned char RA8875::ReadStatus(void)
 {
     unsigned char data;
-    
+
     select(true);
     spiwrite(0xC0);         // These two bits are for the special "Status Read" [STSR]
     data = spiread();
@@ -506,7 +533,8 @@
 
 RetCode_t RA8875::SetTextCursor(loc_t x, loc_t y)
 {
-    cursor_x = x; cursor_y = y;     // for non-internal fonts
+    cursor_x = x;
+    cursor_y = y;     // for non-internal fonts
     WriteCommandW(0x2A, x);
     WriteCommandW(0x2C, y);
     return noerror;
@@ -537,7 +565,7 @@
     unsigned char mwcr1 = ReadCommand(0x41) & 0x01; // retain currently selected layer
     unsigned char horz = 0;
     unsigned char vert = 0;
-    
+
     mwcr0 |= 0x80;                  // text mode
     if (cursor != NOCURSOR)
         mwcr0 |= 0x40;              // visible
@@ -581,15 +609,15 @@
 
 
 RetCode_t RA8875::SetTextFontControl(fill_t fillit,
-    RA8875::font_angle_t angle, 
-    RA8875::HorizontalScale hScale, 
-    RA8875::VerticalScale vScale, 
-    RA8875::alignment_t alignment)
+                                     RA8875::font_angle_t angle,
+                                     RA8875::HorizontalScale hScale,
+                                     RA8875::VerticalScale vScale,
+                                     RA8875::alignment_t alignment)
 {
-    if (hScale >= 1 && hScale <= 4 && 
-    vScale >= 1 && vScale <= 4) {
+    if (hScale >= 1 && hScale <= 4 &&
+            vScale >= 1 && vScale <= 4) {
         unsigned char x = 0;
-        
+
         if (alignment == align_full)
             x |= 0x80;
         if (fillit == NOFILL)
@@ -609,7 +637,7 @@
 RetCode_t RA8875::SetTextFontSize(RA8875::HorizontalScale hScale, RA8875::VerticalScale vScale)
 {
     unsigned char reg = ReadCommand(0x22);
-    
+
     if (vScale == -1)
         vScale = hScale;
     if (hScale >= 1 && hScale <= 4 && vScale >= 1 && vScale <= 4) {
@@ -664,7 +692,7 @@
 {
     if (c) {
         unsigned char mwcr0;
-        
+
         mwcr0 = ReadCommand(0x40);
         if ((mwcr0 & 0x80) == 0x00) {
             WriteCommand(0x40, 0x80 | mwcr0);    // Put in Text mode if not already
@@ -709,7 +737,7 @@
 RetCode_t RA8875::_putp(color_t pixel)
 {
     WriteDataW((pixel>>8) | (pixel<<8));
-    return noerror;   
+    return noerror;
 }
 
 
@@ -723,7 +751,7 @@
 void RA8875::puts(const char * string)
 {
     unsigned char mwcr0 = ReadCommand(0x40);
-    
+
     if (font == NULL) {
         if ((mwcr0 & 0x80) == 0x00)
             WriteCommand(0x40,0x80);    // Put in Text mode if not already
@@ -731,11 +759,11 @@
         _StartGraphicsStream();
     }
     if (*string != '\0') {
-        #if 1
+#if 1
         while (*string) {           // @TODO calling individual _putc is slower... optimizations?
             _putc(*string++);
         }
-        #else
+#else
         WriteCommand(0x02);
         select(true);
         while (*string != '\0') {
@@ -744,7 +772,7 @@
             _WaitWhileBusy(0x80);
         }
         select(false);
-        #endif
+#endif
     }
     if (font)
         _EndGraphicsStream();
@@ -784,7 +812,7 @@
 RetCode_t RA8875::cls(uint16_t layers)
 {
     RetCode_t ret;
-    
+
     PERFORMANCE_RESET;
     if (layers == 0) {
         ret = clsw(FULLWINDOW);
@@ -821,14 +849,14 @@
 RetCode_t RA8875::pixel(loc_t x, loc_t y, color_t color)
 {
     RetCode_t ret;
-    
+
     PERFORMANCE_RESET;
-    #if 1
+#if 1
     ret = pixelStream(&color, 1, x,y);
-    #else
+#else
     foreground(color);
     ret = pixel(x,y);
-    #endif
+#endif
     REGISTERPERFORMANCE(PRF_DRAWPIXEL);
     return ret;
 }
@@ -837,18 +865,18 @@
 RetCode_t RA8875::pixel(loc_t x, loc_t y)
 {
     RetCode_t ret;
-    
+
     PERFORMANCE_RESET;
     color_t color = GetForeColor();
-    #if 1
+#if 1
     ret = pixelStream(&color, 1, x, y);
-    #else
+#else
     WriteCommand(0x40,0x00);    // Graphics write mode
     SetGraphicsCursor(x, y);
     WriteCommand(0x02);
     WriteDataW(color);
     ret = noerror;
-    #endif
+#endif
     REGISTERPERFORMANCE(PRF_DRAWPIXEL);
     return ret;
 }
@@ -876,7 +904,7 @@
 color_t RA8875::getPixel(loc_t x, loc_t y)
 {
     color_t pixel;
-    
+
     PERFORMANCE_RESET;
     //WriteCommand(0x45,0x00);    // read left->right, top->bottom
     WriteCommand(0x40,0x00);    // Graphics write mode
@@ -896,7 +924,7 @@
 RetCode_t RA8875::getPixelStream(color_t * p, uint32_t count, loc_t x, loc_t y)
 {
     color_t pixel;
-    
+
     PERFORMANCE_RESET;
     //WriteCommand(0x45,0x00);    // read left->right, top->bottom
     WriteCommand(0x40,0x00);    // Graphics write mode
@@ -943,23 +971,23 @@
 }
 
 
-RetCode_t RA8875::fillrect(loc_t x1, loc_t y1, loc_t x2, loc_t y2, 
-    color_t color, fill_t fillit)
+RetCode_t RA8875::fillrect(loc_t x1, loc_t y1, loc_t x2, loc_t y2,
+                           color_t color, fill_t fillit)
 {
     return rect(x1,y1,x2,y2,color,fillit);
 }
 
 
-RetCode_t RA8875::rect(loc_t x1, loc_t y1, loc_t x2, loc_t y2, 
-    color_t color, fill_t fillit)
+RetCode_t RA8875::rect(loc_t x1, loc_t y1, loc_t x2, loc_t y2,
+                       color_t color, fill_t fillit)
 {
     foreground(color);
     return rect(x1,y1,x2,y2,fillit);
 }
 
 
-RetCode_t RA8875::rect(loc_t x1, loc_t y1, loc_t x2, loc_t y2, 
-    fill_t fillit)
+RetCode_t RA8875::rect(loc_t x1, loc_t y1, loc_t x2, loc_t y2,
+                       fill_t fillit)
 {
     PERFORMANCE_RESET;
     if (x1 == x2 && y1 == y2) {
@@ -985,27 +1013,27 @@
 }
 
 
-RetCode_t RA8875::fillroundrect(loc_t x1, loc_t y1, loc_t x2, loc_t y2, 
-    dim_t radius1, dim_t radius2, color_t color, fill_t fillit)
+RetCode_t RA8875::fillroundrect(loc_t x1, loc_t y1, loc_t x2, loc_t y2,
+                                dim_t radius1, dim_t radius2, color_t color, fill_t fillit)
 {
     foreground(color);
     return roundrect(x1,y1,x2,y2,radius1,radius2,fillit);
 }
 
 
-RetCode_t RA8875::roundrect(loc_t x1, loc_t y1, loc_t x2, loc_t y2, 
-    dim_t radius1, dim_t radius2, color_t color, fill_t fillit)
+RetCode_t RA8875::roundrect(loc_t x1, loc_t y1, loc_t x2, loc_t y2,
+                            dim_t radius1, dim_t radius2, color_t color, fill_t fillit)
 {
     foreground(color);
     return roundrect(x1,y1,x2,y2,radius1,radius2,fillit);
 }
 
 
-RetCode_t RA8875::roundrect(loc_t x1, loc_t y1, loc_t x2, loc_t y2, 
-    dim_t radius1, dim_t radius2, fill_t fillit)
+RetCode_t RA8875::roundrect(loc_t x1, loc_t y1, loc_t x2, loc_t y2,
+                            dim_t radius1, dim_t radius2, fill_t fillit)
 {
     RetCode_t ret = noerror;
-    
+
     PERFORMANCE_RESET;
     if (x1 > x2 || y1 > y2 || (radius1 > (x2-x1)/2) || (radius2 > (y2-y1)/2) ) {
         ret = bad_parameter;
@@ -1037,19 +1065,8 @@
 }
 
 
-RetCode_t RA8875::triangle(loc_t x1, loc_t y1, loc_t x2, loc_t y2, 
-    loc_t x3, loc_t y3, color_t color, fill_t fillit)
-{
-    RetCode_t ret;
-    
-    foreground(color);
-    ret = triangle(x1,y1,x2,y2,x3,y3,fillit);
-    return ret;
-}
-
-
-RetCode_t RA8875::filltriangle(loc_t x1, loc_t y1, loc_t x2, loc_t y2, 
-    loc_t x3, loc_t y3, color_t color, fill_t fillit)
+RetCode_t RA8875::triangle(loc_t x1, loc_t y1, loc_t x2, loc_t y2,
+                           loc_t x3, loc_t y3, color_t color, fill_t fillit)
 {
     RetCode_t ret;
 
@@ -1059,11 +1076,22 @@
 }
 
 
-RetCode_t RA8875::triangle(loc_t x1, loc_t y1 ,loc_t x2, loc_t y2, 
-    loc_t x3, loc_t y3, fill_t fillit)
+RetCode_t RA8875::filltriangle(loc_t x1, loc_t y1, loc_t x2, loc_t y2,
+                               loc_t x3, loc_t y3, color_t color, fill_t fillit)
+{
+    RetCode_t ret;
+
+    foreground(color);
+    ret = triangle(x1,y1,x2,y2,x3,y3,fillit);
+    return ret;
+}
+
+
+RetCode_t RA8875::triangle(loc_t x1, loc_t y1 ,loc_t x2, loc_t y2,
+                           loc_t x3, loc_t y3, fill_t fillit)
 {
     RetCode_t ret = noerror;
-    
+
     PERFORMANCE_RESET;
     if (x1 == x2 && y1 == y2 && x1 == x3 && y1 == y3) {
         pixel(x1, y1);
@@ -1085,16 +1113,16 @@
     return ret;
 }
 
-RetCode_t RA8875::circle(loc_t x, loc_t y, dim_t radius, 
-    color_t color, fill_t fillit)
+RetCode_t RA8875::circle(loc_t x, loc_t y, dim_t radius,
+                         color_t color, fill_t fillit)
 {
     foreground(color);
     return circle(x,y,radius,fillit);
 }
 
 
-RetCode_t RA8875::fillcircle(loc_t x, loc_t y, dim_t radius, 
-    color_t color, fill_t fillit)
+RetCode_t RA8875::fillcircle(loc_t x, loc_t y, dim_t radius,
+                             color_t color, fill_t fillit)
 {
     foreground(color);
     return circle(x,y,radius,fillit);
@@ -1104,7 +1132,7 @@
 RetCode_t RA8875::circle(loc_t x, loc_t y, dim_t radius, fill_t fillit)
 {
     RetCode_t ret = noerror;
-    
+
     PERFORMANCE_RESET;
     if (radius <= 0) {
         ret = bad_parameter;
@@ -1139,11 +1167,11 @@
     return ellipse(x,y,radius1,radius2,fillit);
 }
 
-        
+
 RetCode_t RA8875::ellipse(loc_t x, loc_t y, dim_t radius1, dim_t radius2, fill_t fillit)
 {
     RetCode_t ret = noerror;
-    
+
     PERFORMANCE_RESET;
     if (radius1 <= 0 || radius2 <= 0) {
         ;   // do nothing
@@ -1205,7 +1233,7 @@
     WriteCommand(0x01, 0x01);   // Apply Display Off, Reset
     wait_ms(2);                     // no idea if I need to wait, or how long
     WriteCommand(0x01, 0x00);   // Display off, Remove reset
-    wait_ms(2);                     // no idea if I need to wait, or how long    
+    wait_ms(2);                     // no idea if I need to wait, or how long
     init(RA8875_DISPLAY_WIDTH, RA8875_DISPLAY_HEIGHT, RA8875_COLORDEPTH_BPP);
     return noerror;
 }
@@ -1233,7 +1261,7 @@
 RetCode_t RA8875::Backlight(float brightness)
 {
     unsigned char b;
-    
+
     if (brightness >= 1.0)
         b = 255;
     else if (brightness <= 0.0)
@@ -1298,7 +1326,7 @@
 color_t RA8875::GetForeColor(void)
 {
     color_t color;
-    
+
     color  = (ReadCommand(0x63) & 0x1F) << 11;
     color |= (ReadCommand(0x64) & 0x3F) << 5;
     color |= (ReadCommand(0x65) & 0x1F);
@@ -1307,35 +1335,33 @@
 
 
 color_t RA8875::DOSColor(int i)
-    {
-    const color_t colors[16] = 
-        {
+{
+    const color_t colors[16] = {
         Black,    Blue,       Green,       Cyan,
         Red,      Magenta,    Brown,       Gray,
         Charcoal, BrightBlue, BrightGreen, BrightCyan,
         Orange,   Pink,       Yellow,      White
-        };
+    };
     if (i < 16)
         return colors[i];
     else
         return 0;
-    }
+}
 
 
-const char * RA8875::DOSColorNames(int i) 
-    {
-    const char * names[16] = 
-        {
+const char * RA8875::DOSColorNames(int i)
+{
+    const char * names[16] = {
         "Black",    "Blue",       "Green",       "Cyan",
         "Red",      "Magenta",    "Brown",       "Gray",
         "Charcoal", "BrightBlue", "BrightGreen", "BrightCyan",
         "Orange",   "Pink",       "Yellow",      "White"
-        };
+    };
     if (i < 16)
         return names[i];
     else
         return NULL;
-    }
+}
 
 
 ///////////////////////////////////////////////////////////////
@@ -1344,7 +1370,7 @@
 unsigned char RA8875::spiwrite(unsigned char data)
 {
     unsigned char retval;
-    
+
     if (!spiWriteSpeed)
         _setWriteSpeed(true);
     retval = spi.write(data);
@@ -1356,7 +1382,7 @@
 {
     unsigned char retval;
     unsigned char data = 0;
-    
+
     if (spiWriteSpeed)
         _setWriteSpeed(false);
     retval = spi.write(data);
@@ -1378,7 +1404,7 @@
     wait_ms(1);
     WriteCommand(0x89, 0x02);
     wait_ms(1);
-    
+
     // System Config Register (SYSR)
     if (color_bpp == 16) {
         WriteCommand(0x10, 0x0C);               // 16-bpp (65K colors) color depth, 8-bit interface
@@ -1410,7 +1436,7 @@
     } else {
         WriteCommand(0x20, 0x80);               // DPCR - 2-layer mode
     }
-    
+
     // Set display image to Blue on Black as default
     window(0,0, width, height);             // Initialize to full screen
     SetTextCursorControl();
@@ -1438,19 +1464,19 @@
 {
     BITMAPFILEHEADER BMP_Header;
     BITMAPINFOHEADER BMP_Info;
-    
+
     INFO("(%d,%d) - (%d,%d) %s", x,y,w,h,Name_BMP);
     if (x >= 0 && x < width()
-    && y >= 0 && y < height()
-    && w > 0 && x + w <= width()
-    && h > 0 && y + h <= height()) {
+            && y >= 0 && y < height()
+            && w > 0 && x + w <= width()
+            && h > 0 && y + h <= height()) {
 
         BMP_Header.bfType = BF_TYPE;
         BMP_Header.bfSize = (w * h * sizeof(RGBQUAD)) + sizeof(BMP_Header) + sizeof(BMP_Header);
         BMP_Header.bfReserved1 = 0;
         BMP_Header.bfReserved2 = 0;
         BMP_Header.bfOffBits = sizeof(BMP_Header) + sizeof(BMP_Header);
-        
+
         BMP_Info.biSize = sizeof(BMP_Info);
         BMP_Info.biWidth = w;
         BMP_Info.biHeight = h;
@@ -1474,11 +1500,11 @@
         //HexDump("BMP_Header", (uint8_t *)&BMP_Header, sizeof(BMP_Header));
         fwrite(&BMP_Header, sizeof(char), sizeof(BMP_Header), Image);
         //INFO("fwrite returned %d", r);
-        
+
         //HexDump("BMP_Info", (uint8_t *)&BMP_Info, sizeof(BMP_Info));
         fwrite(&BMP_Info, sizeof(char), sizeof(BMP_Info), Image);
         //INFO("fwrite returned %d", r);
-        
+
         int lineBufSize = ((24 * w + 7)/8);
         uint8_t * lineBuffer = (uint8_t *)malloc(lineBufSize);
         if (lineBuffer == NULL) {
@@ -1487,38 +1513,86 @@
             return(not_enough_ram);
         }
         color_t * pixelBuffer = (color_t *)malloc(w * sizeof(color_t));
-        if (pixelBuffer == NULL) {
+        color_t * pixelBuffer2 = (color_t *)malloc(w * sizeof(color_t));
+        color_t transparency = GetBackgroundTransparencyColor();
+        unsigned char ltpr0 = ReadCommand(0x52) & 0x7;
+
+        if (pixelBuffer == NULL || pixelBuffer2 == NULL) {
             fclose(Image);
             free(lineBuffer);
             ERR("Not enough RAM for pixelBuffer");
+            if (pixelBuffer)
+                free(pixelBuffer);
             return(not_enough_ram);
         }
-        
+
+        uint16_t prevLayer = GetDrawingLayer();
+        // If only one of the layers is visible, select that layer
+        switch(ltpr0) {
+            case 0:
+                SelectDrawingLayer(0);
+                break;
+            case 1:
+                SelectDrawingLayer(1);
+                break;
+            default:
+                break;
+        }
+
         // Read the display from the last line toward the top
         // so we can write the file in one pass.
         for (int j = h - 1; j >= 0; j--) {
+            if (ltpr0 >= 2)             // Need to combine the layers...
+                SelectDrawingLayer(0);  // so read layer 0 first
             // Read one line of pixels to a local buffer
             if (getPixelStream(pixelBuffer, w, x,y+j) != noerror) {
                 ERR("getPixelStream error, and no recovery handler...");
             }
+            if (ltpr0 >= 2) {           // Need to combine the layers...
+                SelectDrawingLayer(1);  // so read layer 0 first
+                if (getPixelStream(pixelBuffer2, w, x,y+j) != noerror) {
+                    ERR("getPixelStream error, and no recovery handler...");
+                }
+            }
             // Convert the local buffer to RGBQUAD format
             int lb = 0;
             for (int i=0; i<w; i++) {
-                color_t pixel = pixelBuffer[x+i];
-                // Scale to 24-bits
-                RGBQUAD q = RGB16ToRGBQuad(pixel);
-                lineBuffer[lb++] = q.rgbBlue;
-                lineBuffer[lb++] = q.rgbGreen;
-                lineBuffer[lb++] = q.rgbRed;
+                RGBQUAD q0 = RGB16ToRGBQuad(pixelBuffer[x+i]);      // Scale to 24-bits
+                RGBQUAD q1 = RGB16ToRGBQuad(pixelBuffer2[x+i]);     // Scale to 24-bits
+                switch (ltpr0) {
+                    case 0:
+                    case 1:
+                    case 2: // lighten-overlay  (@TODO Not supported yet)
+                    case 6: // Floating Windows     (@TODO not sure how to support)
+                    default: // Reserved...
+                        lineBuffer[lb++] = q0.rgbBlue;
+                        lineBuffer[lb++] = q0.rgbGreen;
+                        lineBuffer[lb++] = q0.rgbRed;
+                        break;
+                    case 3: // transparent mode (@TODO Read the background color register for transparent)
+                    case 4: // boolean or
+                        lineBuffer[lb++] = q0.rgbBlue | q1.rgbBlue;
+                        lineBuffer[lb++] = q0.rgbGreen | q1.rgbGreen;
+                        lineBuffer[lb++] = q0.rgbRed | q1.rgbRed;
+                        break;
+                    case 5: // boolean AND
+                        lineBuffer[lb++] = q0.rgbBlue & q1.rgbBlue;
+                        lineBuffer[lb++] = q0.rgbGreen & q1.rgbGreen;
+                        lineBuffer[lb++] = q0.rgbRed & q1.rgbRed;
+                        break;
+                }
             }
+            if (j == h - 1)
+                HexDump("Line", lineBuffer, lineBufSize);
             // Write to disk
-            //HexDump("Line", lineBuffer, lineBufSize);
             fwrite(lineBuffer, sizeof(char), lb, Image);
         }
+        SelectDrawingLayer(prevLayer);
         fclose(Image);
-        free(pixelBuffer);  // don't leak memory.
+        free(pixelBuffer2);  // don't leak memory.
+        free(pixelBuffer);
         free(lineBuffer);
-        INFO("Image closed"); 
+        INFO("Image closed");
         return noerror;
     } else {
         return bad_parameter;
@@ -1574,17 +1648,17 @@
     const char * bbCursor = "The Blinking Block cursor should be visible for this text.\r\n";
     const char * p;
     int delay = 100;
-    
+
     if (!SuppressSlowStuff)
         pc.printf("Text Cursor Test\r\n");
-    else 
+    else
         delay = 0;
     display.background(Black);
     display.foreground(Blue);
     display.cls();
     display.Backlight_u8(255);
     display.puts(0,0, "Text Cursor Test.");
-    
+
     // visible, non-blinking
     display.SetTextCursor(0,20);
     display.SetTextCursorControl(RA8875::IBEAM, false);
@@ -1600,7 +1674,7 @@
         display._putc(*p++);
         wait_ms(delay);
     }
-    
+
     display.SetTextCursorControl(RA8875::BLOCK, false);
     p = bCursor;
     while (*p) {
@@ -1678,12 +1752,12 @@
     display.puts(0,0, "External Font Test.");
 
     display.set_font(Small_6);
-    display.puts(0,30, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\r\n");    
+    display.puts(0,30, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\r\n");
 
     display.set_font(Arial12x12);
     display.puts("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\r\n");
     display.set_font();     // restore to internal
-    
+
     display.puts("Normal font again.");
     //display.window(0,0, display.width(), display.height());
 }
@@ -1798,7 +1872,7 @@
     display.line(20,52, 20,52, BrightRed);
     display.line(22,52, 22,52, BrightGreen);
     display.line(24,52, 24,52, BrightBlue);
-    
+
     // point
     display.line(50,50, 50,50, Red);
     display.line(52,52, 52,52, Green);
@@ -1893,7 +1967,7 @@
         if (!SuppressSlowStuff)
             wait_ms(200);
     }
-    
+
     // Restore before we exit
     display.SetLayerTransparency(0, 0);
     display.SetLayerMode(RA8875::ShowLayer0);        // Restore to layer 0
@@ -1910,7 +1984,7 @@
     display.foreground(Blue);
     display.cls();
     display.puts(0,0, "Rounded Rectangle Test");
-    
+
     for (i=0; i<16; i++) {
         x1 = rand() % 240;
         y1 = 50 + rand() % 200;
@@ -2049,7 +2123,7 @@
 {
     LocalFileSystem local("local");
     if (!SuppressSlowStuff)
-        pc.printf("Bitmap File Load\r\n");    
+        pc.printf("Bitmap File Load\r\n");
     display.background(Black);
     display.foreground(Blue);
     display.cls();
@@ -2086,9 +2160,9 @@
     LayerTest(display, pc);
     //TestGraphicsBitmap(display, pc);
     pc.printf("SpeedTest completed in %d msec\r\n", t.read_ms());
-    #ifdef PERF_METRICS
+#ifdef PERF_METRICS
     display.ReportPerformance(pc);
-    #endif
+#endif
     SuppressSlowStuff = false;
 }
 
@@ -2097,7 +2171,7 @@
 {
     LocalFileSystem local("local");
     if (!SuppressSlowStuff)
-        pc.printf("PrintScreen\r\n");    
+        pc.printf("PrintScreen\r\n");
     display.PrintScreen( 0,0, 480,272, "/local/Capture.bmp");
 }
 
@@ -2120,9 +2194,9 @@
                   "A - Auto Test mode    S - Speed Test\r\n"
                   "p - print screen      r - reset  \r\n"
                   "l - layer test        w - wrapping text \r\n"
-                  #ifdef PERF_METRICS
+#ifdef PERF_METRICS
                   "0 - clear performance 1 - report performance\r\n"
-                  #endif
+#endif
                   "> ");
         if (automode == -1 || pc.readable()) {
             automode = -1;
@@ -2133,14 +2207,14 @@
             q = modelist[automode];
         }
         switch(q) {
-            #ifdef PERF_METRICS
+#ifdef PERF_METRICS
             case '0':
                 lcd.ClearPerformance();
                 break;
             case '1':
                 lcd.ReportPerformance(pc);
                 break;
-            #endif
+#endif
             case 'A':
                 automode = 0;
                 break;