浏览代码

more display update (still need to fix ClearWindow)

philippe44 5 年之前
父节点
当前提交
5109877909

+ 11 - 5
components/display/SH1106.c

@@ -80,19 +80,25 @@ static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
 
 static bool Init( struct GDS_Device* Device ) {
 	Device->FramebufferSize = ( Device->Width * Device->Height ) / 8;	
+	
+// benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy		
+#ifdef SHADOW_BUFFER	
 	Device->Framebuffer = calloc( 1, Device->FramebufferSize );
     NullCheck( Device->Framebuffer, return false );
-		
-#ifdef SHADOW_BUFFER	
 #ifdef USE_IRAM
-	// benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy
 	if (Device->IF == IF_SPI) Device->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
 	else 
-#else
-	Device->Shadowbuffer = malloc( Device->FramebufferSize );	
 #endif
+	Device->Shadowbuffer = malloc( Device->FramebufferSize );	
 	NullCheck( Device->Shadowbuffer, return false );
 	memset(Device->Shadowbuffer, 0xFF, Device->FramebufferSize);
+#else	// not SHADOW_BUFFER
+#ifdef USE_IRAM
+	// benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy
+	if (Device->IF == IF_SPI) Device->Framebuffer = heap_caps_calloc( 1, Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
+	else 
+#endif
+	Device->Framebuffer = calloc( 1, Device->FramebufferSize );
 #endif	
 		
 	// need to be off and disable display RAM

+ 11 - 5
components/display/SSD1306.c

@@ -78,19 +78,25 @@ static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
 
 static bool Init( struct GDS_Device* Device ) {
 	Device->FramebufferSize = ( Device->Width * Device->Height ) / 8;	
+	
+// benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy		
+#ifdef SHADOW_BUFFER	
 	Device->Framebuffer = calloc( 1, Device->FramebufferSize );
     NullCheck( Device->Framebuffer, return false );
-		
-#ifdef SHADOW_BUFFER	
 #ifdef USE_IRAM
-	// benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy
 	if (Device->IF == IF_SPI) Device->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
 	else 
-#else
-	Device->Shadowbuffer = malloc( Device->FramebufferSize );	
 #endif
+	Device->Shadowbuffer = malloc( Device->FramebufferSize );	
 	NullCheck( Device->Shadowbuffer, return false );
 	memset(Device->Shadowbuffer, 0xFF, Device->FramebufferSize);
+#else	// not SHADOW_BUFFER
+#ifdef USE_IRAM
+	// benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy
+	if (Device->IF == IF_SPI) Device->Framebuffer = heap_caps_calloc( 1, Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
+	else 
+#endif
+	Device->Framebuffer = calloc( 1, Device->FramebufferSize );
 #endif	
 		
 	// need to be off and disable display RAM

+ 103 - 18
components/display/SSD132x.c

@@ -32,7 +32,27 @@ struct SSD132x_Private {
 	uint8_t Model;
 };
 
-// Functions are not deckared to minimize # of lines
+static const unsigned char BitReverseTable256[] = 
+{
+  0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 
+  0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 
+  0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 
+  0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 
+  0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 
+  0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
+  0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 
+  0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
+  0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
+  0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, 
+  0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
+  0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
+  0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, 
+  0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
+  0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 
+  0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
+};
+
+// Functions are not declared to minimize # of lines
 
 static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
 	Device->WriteCommand( Device, 0x15 );
@@ -48,17 +68,15 @@ static void SetRowAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End
 
 static void Update4( struct GDS_Device* Device ) {
 	struct SSD132x_Private *Private = (struct SSD132x_Private*) Device->Private;
-	int r;
-	
+		
 	// always update by full lines
 	SetColumnAddress( Device, 0, Device->Width / 2 - 1);
 	
 #ifdef SHADOW_BUFFER
 	uint16_t *optr = (uint16_t*) Device->Shadowbuffer, *iptr = (uint16_t*) Device->Framebuffer;
 	bool dirty = false;
-	int page;
 	
-	for (r = 0, page = 0; r < Device->Height; r++) {
+	for (int r = 0, page = 0; r < Device->Height; r++) {
 		// look for change and update shadow (cheap optimization = width always / by 2)
 		for (int c = Device->Width / 2 / 2; --c >= 0;) {
 			if (*optr != *iptr) {
@@ -74,10 +92,10 @@ static void Update4( struct GDS_Device* Device ) {
 				SetRowAddress( Device, r - page + 1, r );
 				// own use of IRAM has not proven to be much better than letting SPI do its copy
 				if (Private->iRAM) {
-					memcpy(Private->iRAM, Device->Shadowbuffer + (r - page + 1) * Device->Width / 2, Device->Width * page / 2 );
+					memcpy(Private->iRAM, Device->Shadowbuffer + (r - page + 1) * Device->Width / 2, page * Device->Width / 2 );
 					Device->WriteData( Device, Private->iRAM, Device->Width * page / 2 );
 				} else	{
-					Device->WriteData( Device, Device->Shadowbuffer + (r - page + 1) * Device->Width / 2, Device->Width * page / 2 );					
+					Device->WriteData( Device, Device->Shadowbuffer + (r - page + 1) * Device->Width / 2, page * Device->Width / 2 );					
 				}	
 				dirty = false;
 			}	
@@ -85,17 +103,27 @@ static void Update4( struct GDS_Device* Device ) {
 		}	
 	}	
 #else
-	for (r = 0; r < Device->Height; r += Private->PageSize) {
+	for (int r = 0; r < Device->Height; r += Private->PageSize) {
 		SetRowAddress( Device, r, r + Private->PageSize - 1 );
-		Device->WriteData( Device, Device->Framebuffer + r * Device->Width / 2, Device->Width * Private->PageSize / 2 );
+		if (Private->iRAM) {
+			memcpy(Private->iRAM, Device->Framebuffer + r * Device->Width / 2, Private->PageSize * Device->Width / 2 );
+			Device->WriteData( Device, Private->iRAM, Private->PageSize * Device->Width / 2 );
+		} else	{
+			Device->WriteData( Device, Device->Framebuffer + r * Device->Width / 2, Private->PageSize * Device->Width / 2 );
+		}	
 	}	
 #endif	
 }
 
+/* 
+ We have to make a choice here: either we go by row one by one and send lots of 
+ small packets on the serial interface or we do a page of N rows once at least
+ a change has been detected. So far, choice is to go one-by-one
+*/ 
 static void Update1( struct GDS_Device* Device ) {
 #ifdef SHADOW_BUFFER
 	// not sure the compiler does not have to redo all calculation in for loops, so local it is
-	int width = Device->Width, rows = Device->Height / 8;
+	int width = Device->Width / 8, rows = Device->Height;
 	uint8_t *optr = Device->Shadowbuffer, *iptr = Device->Framebuffer;
 	
 	// by row, find first and last columns that have been updated
@@ -117,13 +145,38 @@ static void Update1( struct GDS_Device* Device ) {
 		}
 	}	
 #else	
-	// automatic counter and end Page/Column
-	SetColumnAddress( Device, 0, Device->Width - 1);
-	SetPageAddress( Device, 0, Device->Height / 8 - 1);
+	// automatic counter and end Row/Column
+	SetColumnAddress( Device, 0, Device->Width / 8 - 1);
+	SetRowAddress( Device, 0, Device->Height - 1);
 	Device->WriteData( Device, Device->Framebuffer, Device->FramebufferSize );
 #endif	
 }
 
+// in 1 bit mode, SSD1326 has a different memory map than SSD1306 and SH1106
+static void IRAM_ATTR DrawPixel1Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
+    uint32_t XBit = ( X & 0x07 );
+    uint8_t* FBOffset = Device->Framebuffer + ( ( Y * Device->Width + X ) >> 3 );
+
+    if ( Color == GDS_COLOR_XOR ) {
+        *FBOffset ^= BIT( 7 - XBit );
+    } else {
+		// we might be able to save the 7-Xbit using BitRemap (A0 bit 2)
+        *FBOffset = ( Color == GDS_COLOR_WHITE ) ? *FBOffset | BIT( 7 - XBit ) : *FBOffset & ~BIT( 7 - XBit );
+    }
+}
+
+static void DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int Height, int Color ) {
+	uint8_t *optr = Device->Framebuffer;
+	
+	if (!Height) Height = Device->Height;
+	if (!Width) Width = Device->Width;
+	
+	// just do bitreverse and if BitRemap works, there will be even nothing to do	
+	for (int i = Height * Width >> 3; --i >= 0;) *optr++ = BitReverseTable256[*Data++];
+	
+	// Dirty is set for us
+}
+
 static void SetHFlip( struct GDS_Device* Device, bool On ) { 
 	struct SSD132x_Private *Private = (struct SSD132x_Private*) Device->Private;
 	if (Private->Model == SSD1326) Private->ReMap = On ? (Private->ReMap | ((1 << 0) | (1 << 2))) : (Private->ReMap & ~((1 << 0) | (1 << 2)));
@@ -163,14 +216,38 @@ static bool Init( struct GDS_Device* Device ) {
 	Device->Framebuffer = calloc( 1, Device->FramebufferSize );
     NullCheck( Device->Framebuffer, return false );
 	
+// benchmarks showed little gain to have SPI memory already in IRAM vs letting driver copy		
 #ifdef SHADOW_BUFFER	
-	Device->Shadowbuffer = malloc( Device->FramebufferSize );
-	NullCheck( Device->Shadowbuffer, return false );
+	Device->Framebuffer = calloc( 1, Device->FramebufferSize );
+    NullCheck( Device->Framebuffer, return false );
+#ifdef USE_IRAM
+	if (Device->IF == IF_SPI) {
+		if (Device->Depth == 1) {
+			Device->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
+		} else {
+			Device->Shadowbuffer = malloc( Device->FramebufferSize );	
+			Private->iRAM = heap_caps_malloc( Private->PageSize * Device->Width / 2, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
+		}	
+	} else
+#endif
+	Device->Shadowbuffer = malloc( Device->FramebufferSize );	
 	memset(Device->Shadowbuffer, 0xFF, Device->FramebufferSize);
+#else	// not SHADOW_BUFFER
+#ifdef USE_IRAM
+	if (Device->IF == IF_SPI) {
+		if (Device->Depth == 1) {
+			Device->Framebuffer = heap_caps_calloc( 1, Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
+		} else  {
+			Device->Framebuffer = calloc( 1, Device->FramebufferSize );	
+			Private->iRAM = heap_caps_malloc( Private->PageSize * Device->Width / 2, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
+		}
+	} else 
+#endif	
+	Device->Framebuffer = calloc( 1, Device->FramebufferSize );
 #endif	
 
 	ESP_LOGI(TAG, "SSD1326/7 with bit depth %u, page %u, iRAM %p", Device->Depth, Private->PageSize, Private->iRAM);
-		
+			
 	// need to be off and disable display RAM
 	Device->DisplayOff( Device );
     Device->WriteCommand( Device, 0xA5 );
@@ -225,11 +302,19 @@ struct GDS_Device* SSD132x_Detect(char *Driver, struct GDS_Device* Device) {
 	else return NULL;
 	
 	if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
+	
 	*Device = SSD132x;	
 	((struct SSD132x_Private*) Device->Private)->Model = Model;
+	
 	sscanf(Driver, "%*[^:]:%c", &Device->Depth);
-	if (Model == SSD1326 && Device->Depth == 1) Device->Update = Update1;
-	else Device->Depth = 4;
+	
+	if (Model == SSD1326 && Device->Depth == 1) {
+		Device->Update = Update1;
+		Device->DrawPixelFast = DrawPixel1Fast;
+		Device->DrawBitmapCBR = DrawBitmapCBR;
+	} else {
+		Device->Depth = 4;
+	}	
 		
 	return Device;
 }

+ 36 - 13
components/display/core/gds.c

@@ -21,7 +21,7 @@ static struct GDS_Device Display;
 
 static char TAG[] = "gds";
 
-struct GDS_Device*	GDS_AutoDetect( char *Driver, GDS_DetectFunc* DetectFunc[] ) {
+struct GDS_Device* GDS_AutoDetect( char *Driver, GDS_DetectFunc* DetectFunc[] ) {
 	for (int i = 0; DetectFunc[i]; i++) {
 		if (DetectFunc[i](Driver, &Display)) {
 			ESP_LOGD(TAG, "Detected driver %p", &Display);
@@ -53,38 +53,61 @@ void GDS_ClearExt(struct GDS_Device* Device, bool full, ...) {
 }	
 
 void GDS_Clear( struct GDS_Device* Device, int Color ) {
+	if (Device->Depth == 1) Color = Color == GDS_COLOR_BLACK ? 0 : 0xff;
+	else if (Device->Depth == 4) Color = Color | (Color << 4);
     memset( Device->Framebuffer, Color, Device->FramebufferSize );
 	Device->Dirty = true;
 }
 
 void GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color ) {
-	// driver can provide onw optimized clear window
+	// driver can provide own optimized clear window
 	if (Device->ClearWindow) {
 		Device->ClearWindow( Device, x1, y1, x2, y2, Color );
 	} else if (Device->Depth == 1) {
 		// single shot if we erase all screen
 		if (x2 - x1 == Device->Width - 1 && y2 - y1 == Device->Height - 1) {
-			memset( Device->Framebuffer, Color, Device->FramebufferSize );
+			memset( Device->Framebuffer, Color == GDS_COLOR_BLACK ? 0 : 0xff, Device->FramebufferSize );
 		} else {
 			uint8_t _Color = Color == GDS_COLOR_BLACK ? 0: 0xff;
 			uint8_t Width = Device->Width;
 			// try to do byte processing as much as possible
-			for (int c = x1; c <= x2; c++) {
+			int c;
+			for (c = x1; c <= x2; c++) {
 				int r = y1;
 				while (r & 0x07 && r <= y2) GDS_DrawPixelFast( Device, c, r++, Color);
-				for (; (r >> 3) < (y2 >> 3); r++) Device->Framebuffer[(r >> 3) * Width + c] = _Color;
-				// memset(Device->Framebuffer + (r >> 3) * Width, _Color, (y2 >> 3) - (r >> 3));
+				//for (; (r >> 3) < (y2 >> 3); r++) Device->Framebuffer[(r >> 3) * Width + c] = _Color;
+				memset(Device->Framebuffer + (r >> 3) * Width + c, _Color, (y2 - r) >> 3);
 				while (r <= y2) GDS_DrawPixelFast( Device, c, r++, Color);
 			}
-		}	
+		}
+	} if (Device->Depth == 4) {
+		if (x2 - x1 == Device->Width - 1 && y2 - y1 == Device->Height - 1) {
+			// we assume color is 0..15
+			memset( Device->Framebuffer, Color | (Color << 4), Device->FramebufferSize );
+		} else {
+			uint8_t _Color = Color | (Color << 4);
+			uint8_t Width = Device->Width;
+			// try to do byte processing as much as possible
+			int r;
+			for (r = y1; r <= y2; r++) {
+				int c = x1;
+				if (c & 0x01) GDS_DrawPixelFast( Device, c++, r, Color);
+				//for (; (c >> 1) < (x2 >> 1); c++) Device->Framebuffer[(r * Width + c) >> 1] = _Color;
+				memset(Device->Framebuffer + ((r * Width +c)  >> 1), _Color, (x2 - c) >> 1);
+				if (c < x2) GDS_DrawPixelFast( Device, c, r, Color);
+			}
+		}
 	} else {
-		for (int y = y1; y <= y2; y++) {
-			for (int x = x1; x <= x2; x++) {
+		int y;
+		for (y = y1; y <= y2; y++) {
+			int x;
+			for (x = x1; x <= x2; x++) {
 				GDS_DrawPixelFast( Device, x, y, Color);
-			}		
-		}	
-	}	
-	
+			}
+		}
+	}
+
+	// make sure diplay will do update
 	Device->Dirty = true;
 }
 

+ 31 - 77
components/display/core/gds_draw.c

@@ -197,7 +197,9 @@ void GDS_DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int
 	if (!Width) Width = Device->Width;
 	Height >>= 3;
 		
-	if (Device->Depth == 1) {
+	if (Device->DrawBitmapCBR) {
+		Device->DrawBitmapCBR( Device, Data, Width, Height, Color );
+	} else if (Device->Depth == 1) {
 		// need to do row/col swap and bit-reverse
 		for (int r = 0; r < Height; r++) {
 			uint8_t *optr = Device->Framebuffer + r*Device->Width, *iptr = Data + r;
@@ -276,84 +278,36 @@ void GDS_DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int
 void GDS_DrawRGB16( struct GDS_Device* Device, int x, int y, int Width, int Height, int RGB_Mode, uint16_t **Image ) {
 	if (Device->DrawRGB16) {
 		Device->DrawRGB16( Device, x, y, Width, Height, RGB_Mode, Image );
-	} else if (Device->Depth == 4) {
-		for (int c = 0; c < Width; c++) {
-			for (int r = 0; r < Height; r++) {
-				int pixel = Image[r][c];
-				switch(RGB_Mode) {
-				case GDS_RGB565:
-					pixel = (((pixel & 0x1f) * 11 + (((pixel >> 5) & 0x3f)  * 59) / 2 + (pixel >> 11) * 30) / 100) >> 1;
-					break;
-				case GDS_RGB555:
-					pixel = (((pixel & 0x1f) * 11 + ((pixel >> 5) & 0x1f)  * 59 + (pixel >> 10) * 30) / 100) >> 1;
-					break;
-				case GDS_RGB444:
-					pixel = ((pixel & 0x0f) * 11 + ((pixel >> 4) & 0x0f)  * 59 + (pixel >> 8) * 30) / 100;
-					break;				
-				case GDS_RGB8_GRAY:
-					pixel = Image[r][c] >> 4;
-					break;				
-				}
-				GDS_DrawPixel( Device, c + x, r + y, pixel );
+	} else {
+		int Scale = Device->Depth < 5 ? 5 - Device->Depth : 0;
+		switch(RGB_Mode) {
+		case GDS_RGB565:
+			for (int c = 0; c < Width; c++) {
+				for (int r = 0; r < Height; r++) {
+					int pixel = Image[r][c];
+					pixel = ((pixel & 0x1f) * 11 + ((((pixel >> 5) & 0x3f)  * 59) >> 1) + (pixel >> 11) * 30) / 100;
+					GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale);
+				}	
 			}	
-		}
-	} else if (Device->Depth == 1) {
-		for (int c = 0; c < Width; c++) {
-			for (int r = 0; r < Height; r++) {
-				int pixel = Image[r][c];
-				switch(RGB_Mode) {
-				case GDS_RGB565:
-					pixel = (((pixel & 0x1f) * 21 + (((pixel >> 5) & 0x3f)  * 71) / 2+ (pixel >> 11) * 7) / 100) >> 4;
-					break;
-				case GDS_RGB555:
-					pixel = (((pixel & 0x1f) * 21 + ((pixel >> 5) & 0x1f)  * 71 + (pixel >> 10) * 7) / 100) >> 4;
-					break;
-				case GDS_RGB444:
-					pixel = (((pixel & 0x0f) * 21 + ((pixel >> 4) & 0x0f)  * 71 + (pixel >> 8) * 7) / 100) >> 3;
-					break;		
-				case GDS_RGB8_GRAY:
-					pixel = Image[r][c] >> 7;					
-				}
-				GDS_DrawPixel( Device, c + x, r + y, pixel);
+			break;
+		case GDS_RGB555:
+			for (int c = 0; c < Width; c++) {
+				for (int r = 0; r < Height; r++) {
+					int pixel = Image[r][c];
+					pixel = ((pixel & 0x1f) * 11 + ((pixel >> 5) & 0x1f)  * 59 + (pixel >> 10) * 30) / 100;
+					GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale);
+				}	
 			}	
+			break;
+		case GDS_RGB444:
+			for (int c = 0; c < Width; c++) {
+				for (int r = 0; r < Height; r++) {
+					int pixel = Image[r][c];
+					pixel = (pixel & 0x0f) * 11 + ((pixel >> 4) & 0x0f)  * 59 + (pixel >> 8) * 30;
+					GDS_DrawPixel( Device, c + x, r + y, pixel >> (Scale - 1));
+				}	
+			}	
+			break;				
 		}
 	}	 
 }	
-
-/****************************************************************************************
- * Process graphic display data MSBit first
- * WARNING: this has not been tested yet
- */
- /*
-static void DrawBitmap(int x1, int y1, int x2, int y2, bool by_column, bool MSb, u8_t *data) {
-	// default end point to display size
-	if (x2 == -1) x2 = Display.Width - 1;
-	if (y2 == -1) y2 = Display.Height - 1;
-	
-	display->dirty = true;
-	
-	//	not a boundary draw
-	// same comment about bit depth
-	if (y1 % 8 || y2 % 8 || x1 % 8 | x2 % 8) {
-		ESP_LOGW(TAG, "can't write on non cols/rows boundaries for now");
-	} else {	
-		// set addressing mode to match data
-		if (by_column) {
-			// copy the window and do row/col exchange
-			for (int r = y1/8; r <=  y2/8; r++) {
-				uint8_t *optr = Display.Framebuffer + r*Display.Width + x1, *iptr = data + r;
-				for (int c = x1; c <= x2; c++) {
-					*optr++ = MSb ? BitReverseTable256[*iptr] : *iptr;
-					iptr += (y2-y1)/8 + 1;
-			}	
-			}	
-		} else {
-			// just copy the window inside the frame buffer
-			for (int r = y1/8; r <= y2/8; r++) {
-				uint8_t *optr = Display.Framebuffer + r*Display.Width + x1, *iptr = data + r*(x2-x1+1);
-				for (int c = x1; c <= x2; c++) *optr++ = *iptr++;
-			}	
-		}
-	}	
-}
-*/

+ 1 - 1
components/display/core/gds_draw.h

@@ -11,7 +11,7 @@ extern "C" {
 
 struct GDS_Device;
 
-enum { GDS_RGB565, GDS_RGB555, GDS_RGB444, GDS_RGB8_GRAY };
+enum { GDS_RGB565, GDS_RGB555, GDS_RGB444 };
 
 #ifndef _GDS_PRIVATE_H_
 void IRAM_ATTR GDS_DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color );

+ 2 - 1
components/display/core/gds_private.h

@@ -40,7 +40,7 @@
 #define MAX_LINES	8
 
 #if ! defined BIT
-#define BIT( n ) ( 1 << n )
+#define BIT( n ) ( 1 << ( n ) )
 #endif
 
 struct GDS_Device;
@@ -103,6 +103,7 @@ struct GDS_Device {
 	void (*Update)( struct GDS_Device* Device );
 	// must provide for depth other than 1 (vertical) and 4 (may provide for optimization)
 	void (*DrawPixelFast)( struct GDS_Device* Device, int X, int Y, int Color );
+	void (*DrawBitmapCBR)(struct GDS_Device* Device, uint8_t *Data, int Width, int Height, int Color );
 	// may provide for optimization
 	void (*DrawRGB16)( struct GDS_Device* Device, int x, int y, int Width, int Height, int RGB_Mode, uint16_t **Image );
 	void (*ClearWindow)( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color );

+ 1 - 1
components/display/display.c

@@ -83,7 +83,7 @@ void display_init(char *welcome) {
 		
 	if ((p = strcasestr(config, "width")) != NULL) width = atoi(strchr(p, '=') + 1);
 	if ((p = strcasestr(config, "height")) != NULL) height = atoi(strchr(p, '=') + 1);
-		
+			
 	// so far so good
 	if (display && width > 0 && height > 0) {
 		// Detect driver interface

+ 9 - 4
components/squeezelite/display.c

@@ -637,7 +637,7 @@ static void visu_update(void) {
 	// reset bars for all cases first	
 	for (int i = visu.n; --i >= 0;) visu.bars[i].current = 0;
 	
-	if (visu_export.running && visu_export.running) {
+	if (visu_export.running) {
 					
 		if (visu.mode == VISU_VUMETER) {
 			s16_t *iptr = visu_export.buffer;
@@ -705,15 +705,20 @@ static void visu_update(void) {
 	visu_export.level = 0;
 	pthread_mutex_unlock(&visu_export.mutex);
 
-	GDS_ClearExt(display, false, false, visu.col, visu.row, visu.col + visu.width - 1, visu.row + visu.height - 1);
-	
+	// don't refresh screen if all max are 0 (we were are somewhat idle)
+	int clear = 0;
+	for (int i = visu.n; --i >= 0;) clear = max(clear, visu.bars[i].max);
+	if (clear) GDS_ClearExt(display, false, false, visu.col, visu.row, visu.col + visu.width - 1, visu.row + visu.height - 1);
+
+	// there is much more optimization to be done here, like not redrawing bars unless needed
 	for (int i = visu.n; --i >= 0;) {
 		int x1 = visu.col + visu.border + visu.bar_border + i*(visu.bar_width + visu.bar_gap);
 		int y1 = visu.row + visu.height - 1;
 			
 		if (visu.bars[i].current > visu.bars[i].max) visu.bars[i].max = visu.bars[i].current;
 		else if (visu.bars[i].max) visu.bars[i].max--;
-			
+		else if (!clear) continue;
+		
 		for (int j = 0; j <= visu.bars[i].current; j += 2) 
 			GDS_DrawLine(display, x1, y1 - j, x1 + visu.bar_width - 1, y1 - j, GDS_COLOR_WHITE);