diff --git a/bootloader/bootloader-display.c b/bootloader/bootloader-display.c
index 1fe0cae0ffee19dcf226db64248af754e3c87ac7..abc8dfc209218c80046e46be7a12d1639f4644ec 100644
--- a/bootloader/bootloader-display.c
+++ b/bootloader/bootloader-display.c
@@ -2,7 +2,7 @@
 /* Autogenerated */
 #include "splash-screen.h"
 
-#include "GUI_Paint.h"
+#include "gfx.h"
 #include "display.h"
 
 /*
@@ -16,19 +16,21 @@ static void bootloader_display_splash(void)
 {
 	int idx = 0;
 
+	Color white = gfx_color(&display_screen, WHITE);
+	Color black = gfx_color(&display_screen, BLACK);
 	for (int i = 0; i < sizeof(splash); i++) {
-		uint16_t color = (splash[i] & 0x80) ? 0xffff : 0x0000;
+		Color color    = (splash[i] & 0x80) ? white : black;
 		uint8_t length = splash[i] & 0x7f;
 
 		for (int j = 0; j < length; j++) {
 			uint16_t x = idx % 160;
 			uint16_t y = idx / 160;
-			Paint_SetPixel(x, y, color);
+			gfx_setpixel(&display_screen, x, y, color);
 			idx++;
 		}
 	}
 
-	LCD_Update();
+	gfx_update(&display_screen);
 }
 
 /*
@@ -44,10 +46,8 @@ void bootloader_display_init(void)
  */
 void bootloader_display_header(void)
 {
-	Paint_Clear(0x0000);
-	Paint_DrawString_EN(0, 16 * 0, "Bootloader", &Font16, 0x0000, 0xffff);
-	Paint_DrawString_EN(0, 16 * 1, __DATE__, &Font16, 0x0000, 0xffff);
-	LCD_Update();
+	txt_puts(&display_textb, "Bootloader\n");
+	txt_puts(&display_textb, __DATE__ "\n");
 }
 
 /*
@@ -55,6 +55,7 @@ void bootloader_display_header(void)
  */
 void bootloader_display_line(int line, char *string, uint16_t color)
 {
-	Paint_DrawString_EN(0, 16 * line, string, &Font16, 0x0000, color);
-	LCD_Update();
+	Color black = gfx_color(&display_screen, BLACK);
+	gfx_puts(&Font16, &display_screen, 0, 16 * line, string, color, black);
+	gfx_update(&display_screen);
 }
diff --git a/epicardium/main.c b/epicardium/main.c
index 89d5c07d43bdc8b1e28c15f2366c9bdaeb966e0f..719bb3bba1f6666d8dc8f889b015a9c42e192378 100644
--- a/epicardium/main.c
+++ b/epicardium/main.c
@@ -18,8 +18,9 @@
 #include "modules/filesystem.h"
 #include "api/interrupt-sender.h"
 
-#include <Heart.h>
-#include "GUI_Paint.h"
+#include "Heart.h"
+#include "gfx.h"
+#include "display.h"
 
 #include "FreeRTOS.h"
 #include "task.h"
@@ -50,8 +51,10 @@ int main(void)
 	card10_init();
 	card10_diag();
 
-	Paint_DrawImage(Heart, 0, 0, 160, 80);
-	LCD_Update();
+	gfx_copy_region_raw(
+		&display_screen, 0, 0, 160, 80, 2, (const void *)(Heart)
+	);
+	gfx_update(&display_screen);
 
 	/* TODO: Move this to its own function */
 	SCB->SCR |= SCB_SCR_SEVONPEND_Msk;
diff --git a/epicardium/modules/display.c b/epicardium/modules/display.c
index 1f50acae5cb60a5c199b9864194ffdcd62954e5e..afb0401d2ad0e97666972345d2e120e132f1cc48 100644
--- a/epicardium/modules/display.c
+++ b/epicardium/modules/display.c
@@ -1,11 +1,13 @@
 #include "epicardium.h"
 #include "tmr_utils.h"
 #include "gpio.h"
-#include "GUI_DEV/GUI_Paint.h"
 #include "Fonts/fonts.h"
 #include "tmr.h"
 #include "FreeRTOS.h"
 #include "task.h"
+#include "gfx.h"
+#include "display.h"
+#include "LCD_Driver.h"
 
 static TaskHandle_t lock = NULL;
 
@@ -30,7 +32,7 @@ int epic_disp_print(
 	if (cl < 0) {
 		return cl;
 	} else {
-		Paint_DrawString_EN(posx, posy, pString, &Font20, bg, fg);
+		gfx_puts(&Font20, &display_screen, posx, posy, pString, fg, bg);
 		return 0;
 	}
 }
@@ -41,7 +43,7 @@ int epic_disp_clear(uint16_t color)
 	if (cl < 0) {
 		return cl;
 	} else {
-		LCD_Clear(color);
+		gfx_clear_to_color(&display_screen, color);
 		return 0;
 	}
 }
@@ -52,7 +54,7 @@ int epic_disp_pixel(uint16_t x, uint16_t y, uint16_t color)
 	if (cl < 0) {
 		return cl;
 	} else {
-		Paint_SetPixel(x, y, color);
+		gfx_setpixel(&display_screen, x, y, color);
 		return 0;
 	}
 }
@@ -70,8 +72,15 @@ int epic_disp_line(
 	if (cl < 0) {
 		return cl;
 	} else {
-		Paint_DrawLine(
-			xstart, ystart, xend, yend, color, linestyle, pixelsize
+		/* TODO add linestyle support to gfx code */
+		gfx_thick_line(
+			&display_screen,
+			xstart,
+			ystart,
+			xend,
+			yend,
+			pixelsize,
+			color
 		);
 		return 0;
 	}
@@ -87,14 +96,33 @@ int epic_disp_rect(
 	uint16_t pixelsize
 ) {
 	int cl = check_lock();
-	if (cl < 0) {
+	if (cl < 0)
 		return cl;
-	} else {
-		Paint_DrawRectangle(
-			xstart, ystart, xend, yend, color, fillstyle, pixelsize
+
+	switch (fillstyle) {
+	case FILLSTYLE_EMPTY:
+		gfx_rectangle(
+			&display_screen,
+			xstart,
+			ystart,
+			xend - xstart,
+			yend - ystart,
+			pixelsize,
+			color
 		);
-		return 0;
+		break;
+	case FILLSTYLE_FILLED:
+		gfx_rectangle_fill(
+			&display_screen,
+			xstart,
+			ystart,
+			xend - xstart,
+			yend - ystart,
+			color
+		);
+		break;
 	}
+	return 0;
 }
 
 int epic_disp_circ(
@@ -106,12 +134,19 @@ int epic_disp_circ(
 	uint16_t pixelsize
 ) {
 	int cl = check_lock();
-	if (cl < 0) {
+	if (cl < 0)
 		return cl;
-	} else {
-		Paint_DrawCircle(x, y, rad, color, fillstyle, pixelsize);
-		return 0;
+
+	switch (fillstyle) {
+	case FILLSTYLE_EMPTY:
+		gfx_circle(&display_screen, x, y, rad, pixelsize, color);
+		break;
+	case FILLSTYLE_FILLED:
+		gfx_circle_fill(&display_screen, x, y, rad, color);
+		break;
 	}
+
+	return 0;
 }
 
 int epic_disp_update()
@@ -121,7 +156,7 @@ int epic_disp_update()
 		return cl;
 	}
 
-	LCD_Update();
+	gfx_update(&display_screen);
 	return 0;
 }
 
diff --git a/hw-tests/bmetest/main.c b/hw-tests/bmetest/main.c
index 5cc2c38f0bbb7b733debfec9211e13498b4023fa..16244ec81b1eef71b8c28cc259c86c9ef18ff056 100644
--- a/hw-tests/bmetest/main.c
+++ b/hw-tests/bmetest/main.c
@@ -11,7 +11,9 @@
 #include "gpio.h"
 #include "bme680.h"
 #include "bosch.h"
-#include "GUI_DEV/GUI_Paint.h"
+#include "gfx.h"
+#include "framebuffer.h"
+#include "display.h"
 #include "Fonts/fonts.h"
 
 #include "card10.h"
@@ -73,6 +75,8 @@ int main(void)
 	/* Set the power mode */
 	rslt = bme680_set_sensor_mode(&gas_sensor);
 
+	Color white = gfx_color(&display_screen, WHITE);
+	Color black = gfx_color(&display_screen, BLACK);
 	while (1) {
 		TMR_Delay(MXC_TMR0, MSEC(1000), 0);
 
@@ -86,13 +90,13 @@ int main(void)
 
 		char buf[128];
 		sprintf(buf, "T: %.2Lf degC", data.temperature / 100.0l);
-		Paint_DrawString_EN(0, 0, buf, &Font16, 0x0000, 0xffff);
+		gfx_puts(&Font16, &display_screen, 0, 0, buf, white, black);
 
 		sprintf(buf, "P: %.2Lf hPa", data.pressure / 100.0l);
-		Paint_DrawString_EN(0, 16, buf, &Font16, 0x0000, 0xffff);
+		gfx_puts(&Font16, &display_screen, 0, 16, buf, white, black);
 
 		sprintf(buf, "H: %.2Lf %%rH", data.humidity / 1000.0l);
-		Paint_DrawString_EN(0, 32, buf, &Font16, 0x0000, 0xffff);
+		gfx_puts(&Font16, &display_screen, 0, 32, buf, white, black);
 
 		//printf("%.2f,%.2f,%.2f\n", data.temperature / 100.0f,
 		//    data.pressure / 100.0f, data.humidity / 1000.0f );
@@ -100,12 +104,18 @@ int main(void)
 		if (data.status & BME680_GASM_VALID_MSK) {
 			printf(", G: %ld ohms", data.gas_resistance);
 			sprintf(buf, "G: %ld ohms", data.gas_resistance);
-			Paint_DrawString_EN(
-				0, 48, buf, &Font16, 0x0000, 0xffff
+			gfx_puts(
+				&Font16,
+				&display_screen,
+				0,
+				48,
+				buf,
+				white,
+				black
 			);
 		}
 
-		LCD_Update();
+		gfx_update(&display_screen);
 
 		printf("\n");
 
diff --git a/hw-tests/bootloader-update/main.c b/hw-tests/bootloader-update/main.c
index 9e96aa384c2c4331586ecf1da0dce4ebc6a04139..64b492525405431dbc439dfa8571d99147a95c27 100644
--- a/hw-tests/bootloader-update/main.c
+++ b/hw-tests/bootloader-update/main.c
@@ -6,7 +6,7 @@
 #include "card10.h"
 #include "bootloader-9251ea6.h"
 #include "display.h"
-#include "GUI_Paint.h"
+#include "gfx.h"
 #include "pmic.h"
 
 #include "flc.h"
@@ -42,29 +42,20 @@ int main(void)
 	pmic_set_button_callback(pmic_button);
 
 	printf("Erasing bootloader.\n");
-	Paint_DrawString_EN(
-		0, 16 * 0, "Erasing bootloader", &Font16, 0x0000, 0xffff
-	);
-	LCD_Update();
+	txt_puts(&display_textb, "Erasing bootloader.\n");
 
 	ICC_Disable();
 
 	int ret = FLC_MultiPageErase(0x10000000, 0x10000000 + 1024 * 64 - 1);
 	if (ret != E_NO_ERROR) {
 		printf("FLC_MultiPageErase failed with %d\n", ret);
-		Paint_DrawString_EN(
-			0, 16 * 1, "Fail.", &Font16, 0x0000, 0xffff
-		);
-		LCD_Update();
+		txt_puts(&display_textb, "Fail.\n");
 		while (1)
 			;
 	}
 
 	printf("Writing bootloader.\n");
-	Paint_DrawString_EN(
-		0, 16 * 0, "Writing bootloader ", &Font16, 0x0000, 0xffff
-	);
-	LCD_Update();
+	txt_puts(&display_textb, "Writing bootloader.\n");
 
 	ret = FLC_Write(
 		0x10000000,
@@ -73,21 +64,15 @@ int main(void)
 	);
 	if (ret != E_NO_ERROR) {
 		printf("FLC_Write failed with %d\n", ret);
-		Paint_DrawString_EN(
-			0, 16 * 1, "Fail.", &Font16, 0x0000, 0xffff
-		);
-		LCD_Update();
+		txt_puts(&display_textb, "Fail.\n");
 		while (1)
 			;
 	}
 	ICC_Enable();
 
 	printf("Done.\n");
-	Paint_DrawString_EN(0, 16 * 1, "Done.", &Font16, 0x0000, 0xffff);
-	Paint_DrawString_EN(
-		0, 16 * 2, "Please restart", &Font16, 0x0000, 0xffff
-	);
-	LCD_Update();
+	txt_puts(&display_textb, "Done.\n");
+	txt_puts(&display_textb, "Please restart.\n");
 
 	while (1) {
 		card10_poll();
diff --git a/hw-tests/dual-core/main.c b/hw-tests/dual-core/main.c
index 5c27950215bd835ce488ba17225ab309b0f0468c..60636833753d48b3812833266f01876297037a1d 100644
--- a/hw-tests/dual-core/main.c
+++ b/hw-tests/dual-core/main.c
@@ -7,7 +7,8 @@
 #include "leds.h"
 #include "card10.h"
 
-#include "GUI_Paint.h"
+#include "gfx.h"
+#include "display.h"
 
 #include "tmr_utils.h"
 
@@ -21,8 +22,10 @@ int main(void)
 	card10_init();
 	card10_diag();
 
-	Paint_DrawImage(Heart, 0, 0, 160, 80);
-	LCD_Update();
+	gfx_copy_region_raw(
+		&display_screen, 0, 0, 160, 80, 2, (const void *)(Heart)
+	);
+	gfx_update(&display_screen);
 
 	// Release core1
 	core1_start((void *)0x10080000);
diff --git a/hw-tests/ecgtest/main.c b/hw-tests/ecgtest/main.c
index 965d325c56d3d70b239e0c77bf71ac7812928865..2ca6cbdd84f9f3279af06eebccbc282b37f41fe9 100644
--- a/hw-tests/ecgtest/main.c
+++ b/hw-tests/ecgtest/main.c
@@ -16,7 +16,9 @@
 #include "spi.h"
 #include "pb.h"
 #include "MAX30003.h"
-#include "GUI_DEV/GUI_Paint.h"
+#include "gfx.h"
+#include "LCD_Driver.h"
+#include "display.h"
 #include "pmic.h"
 #include "card10.h"
 #include <stdbool.h>
@@ -150,7 +152,7 @@ static uint8_t prev;
 
 static void clear(void)
 {
-	Paint_Clear(BLACK);
+	gfx_clear(&display_screen);
 	prev = Y_OFFSET;
 }
 
@@ -173,7 +175,7 @@ static void set(uint8_t index, int8_t val)
 	}
 
 	for (int i = min; i < max + 1; i++) {
-		LCD_SetUWORD(SIZE_X - index - 1, i, RED);
+		LCD_SetUWORD(SIZE_X - index - 1, i, 0xf800);
 	}
 
 	prev = pos;
@@ -199,7 +201,15 @@ void update(void)
 		ecg_switch,
 		internal_pull,
 		scale);
-	Paint_DrawString_EN(0, 0, buf, &Font8, 0x0000, 0xffff);
+	gfx_puts(
+		&Font8,
+		&display_screen,
+		0,
+		0,
+		buf,
+		gfx_color(&display_screen, WHITE),
+		gfx_color(&display_screen, BLACK)
+	);
 
 	for (int i = 0; i < SIZE_X; i++) {
 		set(i, (samples[i] / scale));
diff --git a/hw-tests/hello-world/main.c b/hw-tests/hello-world/main.c
index b797233be16784476f33da9201ecd1cab2b70a32..87fdab3ba7d8bf33eea462ce5b71d0bf1eacbd5e 100644
--- a/hw-tests/hello-world/main.c
+++ b/hw-tests/hello-world/main.c
@@ -7,7 +7,8 @@
 #include "leds.h"
 #include "card10.h"
 
-#include "GUI_Paint.h"
+#include "gfx.h"
+#include "display.h"
 
 #include "tmr_utils.h"
 
@@ -29,8 +30,10 @@ int main(void)
 	card10_init();
 	card10_diag();
 
-	Paint_DrawImage(Heart, 0, 0, 160, 80);
-	LCD_Update();
+	gfx_copy_region_raw(
+		&display_screen, 0, 0, 160, 80, 2, (const void *)(Heart)
+	);
+	gfx_update(&display_screen);
 
 	for (int i = 0; i < 11; i++) {
 		//        leds_set_dim(i, 1);
diff --git a/hw-tests/imutest/main.c b/hw-tests/imutest/main.c
index 7e3db4667bc2d226a375cfebedb15bf7cbf91ed0..9472d57f0a8b5f363935df3e05d8b03a8a2df615 100644
--- a/hw-tests/imutest/main.c
+++ b/hw-tests/imutest/main.c
@@ -13,7 +13,8 @@
 #include "gpio.h"
 #include "bhy_uc_driver.h"
 #include "pmic.h"
-#include "GUI_DEV/GUI_Paint.h"
+#include "gfx.h"
+#include "display.h"
 
 #include "card10.h"
 
@@ -48,7 +49,7 @@ void draw_arrow(int angle, int color)
 	int x2 = x1 - sin * 30;
 	int y2 = y1 - cos * 30;
 
-	Paint_DrawLine(x1, y1, x2, y2, color, LINE_STYLE_SOLID, DOT_PIXEL_2X2);
+	gfx_thick_line(&display_screen, x1, y1, x2, y2, 2, color);
 
 	sin = sinf((angle - 140) * 2 * M_PI / 360.);
 	cos = cosf((angle - 140) * 2 * M_PI / 360.);
@@ -56,7 +57,7 @@ void draw_arrow(int angle, int color)
 	int x3 = x2 - sin * 10;
 	int y3 = y2 - cos * 10;
 
-	Paint_DrawLine(x2, y2, x3, y3, color, LINE_STYLE_SOLID, DOT_PIXEL_2X2);
+	gfx_thick_line(&display_screen, x2, y2, x3, y3, 2, color);
 
 	sin = sinf((angle + 140) * 2 * M_PI / 360.);
 	cos = cosf((angle + 140) * 2 * M_PI / 360.);
@@ -64,7 +65,7 @@ void draw_arrow(int angle, int color)
 	int x4 = x2 - sin * 10;
 	int y4 = y2 - cos * 10;
 
-	Paint_DrawLine(x2, y2, x4, y4, color, LINE_STYLE_SOLID, DOT_PIXEL_2X2);
+	gfx_thick_line(&display_screen, x2, y2, x4, y4, 2, color);
 }
 
 /***** Functions *****/
@@ -81,8 +82,12 @@ static void sensors_callback_orientation(
 	int angle = sensor_data->data_vector.x * 360 / 32768;
 
 	if (angle != prev) {
-		Paint_Clear(BLACK);
-		int colors[] = { RED, YELLOW, YELLOW, GREEN };
+		gfx_clear(&display_screen);
+
+		int colors[] = { gfx_color(&display_screen, RED),
+				 gfx_color(&display_screen, YELLOW),
+				 gfx_color(&display_screen, YELLOW),
+				 gfx_color(&display_screen, GREEN) };
 		int color    = colors[sensor_data->data_vector.status];
 		draw_arrow(sensor_data->data_vector.x * 360 / 32768, color);
 
@@ -91,12 +96,18 @@ static void sensors_callback_orientation(
 		//Paint_DrawString_EN(0, 0, buf, &Font12, BLACK, color);
 
 		sprintf(buf, "%3d", angle);
-		Paint_DrawString_EN(0, 30, buf, &Font24, BLACK, color);
-		Paint_DrawCircle(
-			57, 35, 4, color, DRAW_FILL_EMPTY, DOT_PIXEL_1X1
+		gfx_puts(
+			&Font24,
+			&display_screen,
+			0,
+			30,
+			buf,
+			color,
+			gfx_color(&display_screen, BLACK)
 		);
+		gfx_circle(&display_screen, 57, 35, 4, 2, color);
 
-		LCD_Update();
+		gfx_update(&display_screen);
 		prev = angle;
 	}
 }
diff --git a/hw-tests/ips/main.c b/hw-tests/ips/main.c
index a4fd5ad78571b05b5ed5092371ef45e97a81f65b..888185ed07e856e29d47b9559c59efeeee9e91ca 100644
--- a/hw-tests/ips/main.c
+++ b/hw-tests/ips/main.c
@@ -5,10 +5,12 @@
 /***** Includes *****/
 #include "tmr_utils.h"
 #include "gpio.h"
-#include "GUI_DEV/GUI_Paint.h"
+#include "gfx.h"
+#include "display.h"
 #include "Fonts/fonts.h"
 #include "image/image.h"
 #include "tmr.h"
+#include "DEV_Config.h"
 
 #include "card10.h"
 
@@ -24,23 +26,37 @@ int main(void)
 	card10_init();
 	card10_diag();
 
-	Paint_DrawString_EN(0, 0, "123", &Font24, 0x000f, 0xfff0);
-	Paint_DrawString_EN(0, 23, "ABC", &Font24, BLUE, CYAN);
-	Paint_DrawString_CN(20, 42, "΢ѩµç×Ó", &Font24CN, WHITE, RED);
-	Paint_DrawRectangle(
-		70, 10, 100, 40, RED, DRAW_FILL_EMPTY, DOT_PIXEL_2X2
-	);
-	Paint_DrawLine(
-		70, 10, 100, 40, MAGENTA, LINE_STYLE_SOLID, DOT_PIXEL_2X2
+	Color red    = gfx_color(&display_screen, RED);
+	Color green  = gfx_color(&display_screen, GREEN);
+	Color yellow = gfx_color(&display_screen, YELLOW);
+
+	gfx_puts(&Font24, &display_screen, 0, 0, "123", 0x000f, 0xfff8);
+	gfx_puts(&Font24, &display_screen, 23, 0, "ABC", 0x000f, 0xfff8);
+	gfx_rectangle(&display_screen, 70, 10, 100, 40, 2, red);
+	gfx_thick_line(&display_screen, 70, 10, 100, 40, 2, green);
+	gfx_thick_line(&display_screen, 100, 10, 70, 40, 2, yellow);
+	gfx_circle(&display_screen, 85, 25, 22, 2, green);
+
+	gfx_copy_region_raw(
+		&display_screen,
+		120,
+		0,
+		40,
+		40,
+		2,
+		(const void *)(gImage_40X40)
 	);
-	Paint_DrawLine(
-		100, 10, 70, 40, MAGENTA, LINE_STYLE_SOLID, DOT_PIXEL_2X2
+	gfx_copy_region_raw(
+		&display_screen,
+		0,
+		0,
+		160,
+		80,
+		2,
+		(const void *)(gImage_160X80)
 	);
-	Paint_DrawCircle(85, 25, 22, GREEN, DRAW_FILL_EMPTY, DOT_PIXEL_2X2);
+	gfx_update(&display_screen);
 
-	Paint_DrawImage(gImage_40X40, 120, 0, 40, 40);
-	Paint_DrawImage(gImage_160X80, 0, 0, 160, 80);
-	LCD_Update();
 	DEV_Delay_ms(3000);
 
 	while (1) {
diff --git a/lib/card10/display.c b/lib/card10/display.c
index e1d66276646f480cde27c063f162d7bd85dd6138..2756ccb7b1a78a2993ad4b96715ad4d846c77475 100644
--- a/lib/card10/display.c
+++ b/lib/card10/display.c
@@ -1,5 +1,7 @@
 #include "LCD/LCD_Driver.h"
-#include "GUI_DEV/GUI_Paint.h"
+#include "framebuffer.h"
+#include "gfx.h"
+#include "textbuffer.h"
 
 #include "gpio.h"
 #include "tmr.h"
@@ -11,6 +13,9 @@
 /***** Globals *****/
 const gpio_cfg_t DEV_DC_PIN = { PORT_1, PIN_6, GPIO_FUNC_OUT, GPIO_PAD_NONE };
 
+struct gfx_region display_screen;
+struct txt_buffer display_textb;
+
 // Parameters for PWM output
 #define PORT_PWM PORT_0 // port
 #define PIN_PWM PIN_28  // pin
@@ -93,10 +98,8 @@ void display_init(void)
 	PWM_Output();
 	LCD_SetBacklight(500);
 	LCD_Init();
-	LCD_Clear(BLACK);
-
-	Paint_NewImage(LCD_WIDTH, LCD_HEIGHT, 0, WHITE);
 
-	Paint_Clear(BLACK);
-	Paint_SetRotate(180);
+	display_screen = gfx_screen(LCD_framebuffer());
+	txt_init(&display_textb, &display_screen, &Font12);
+	gfx_clear(&display_screen);
 }
diff --git a/lib/card10/display.h b/lib/card10/display.h
index e4244b58a672eed1706ea4c81e4e268fee4147b8..47f82a37f275babff016ab5b32299b0e2e23a475 100644
--- a/lib/card10/display.h
+++ b/lib/card10/display.h
@@ -1,4 +1,9 @@
 #ifndef DISPLAY_H
+#include "framebuffer.h"
+#include "textbuffer.h"
+
+extern struct gfx_region display_screen;
+extern struct txt_buffer display_textb;
 
 void display_init(void);
 
diff --git a/lib/gfx/GUI_DEV/GUI_Paint.c b/lib/gfx/GUI_DEV/GUI_Paint.c
deleted file mode 100644
index 13cbba25f907ac0056d5dc17d0b7830714f6fc6d..0000000000000000000000000000000000000000
--- a/lib/gfx/GUI_DEV/GUI_Paint.c
+++ /dev/null
@@ -1,683 +0,0 @@
-/*****************************************************************************
-* | File      	:   GUI_Paint.c
-* | Author      :   Waveshare team
-* | Function    :	Achieve drawing: draw points, lines, boxes, circles and
-*                   their size, solid dotted line, solid rectangle hollow
-*                   rectangle, solid circle hollow circle.
-* | Info        :
-*   Achieve display characters: Display a single character, string, number
-*   Achieve time display: adaptive size display time minutes and seconds
-*----------------
-* |	This version:   V2.0
-* | Date        :   2018-11-15
-* | Info        :
-* 1.add: Paint_NewImage()
-*    Create an image's properties
-* 2.add: Paint_SelectImage()
-*    Select the picture to be drawn
-* 3.add: Paint_SetRotate()
-*    Set the direction of the cache    
-* 4.add: Paint_RotateImage() 
-*    Can flip the picture, Support 0-360 degrees, 
-*    but only 90.180.270 rotation is better
-* 4.add: Paint_SetMirroring() 
-*    Can Mirroring the picture, horizontal, vertical, origin
-* 5.add: Paint_DrawString_CN() 
-*    Can display Chinese(GB1312)    
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy
-* of this software and associated documnetation files (the "Software"), to deal
-* in the Software without restriction, including without limitation the rights
-* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-* copies of the Software, and to permit persons to  whom the Software is
-* furished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in
-* all copies or substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-* FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-* LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-* THE SOFTWARE.
-*
-******************************************************************************/
-#include "GUI_Paint.h"
-#include "DEV_Config.h"
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h> //memset()
-#include <math.h>
-
-volatile PAINT Paint;
-
-/******************************************************************************
-function:	Create Image
-parameter:
-    image   :   Pointer to the image cache
-    width   :   The width of the picture
-    Height  :   The height of the picture
-    Color   :   Whether the picture is inverted
-******************************************************************************/
-void Paint_NewImage(UWORD Width, UWORD Height, UWORD Rotate, UWORD Color)
-{
-    Paint.WidthMemory = Width;
-    Paint.HeightMemory = Height;
-    Paint.Color = Color;    
-    Paint.WidthByte = Width;
-    Paint.HeightByte = Height;    
-    printf("WidthByte = %d, HeightByte = %d\r\n", Paint.WidthByte, Paint.HeightByte);
-    printf(" EPD_WIDTH / 8 = %d\r\n",  122 / 8);
-   
-    Paint.Rotate = Rotate;
-    Paint.Mirror = MIRROR_NONE;
-    
-    if(Rotate == ROTATE_0 || Rotate == ROTATE_180) {
-        Paint.Width = Width;
-        Paint.Height = Height;
-    } else {
-        Paint.Width = Height;
-        Paint.Height = Width;
-    }
-}
-
-/******************************************************************************
-function:	Select Image Rotate
-parameter:
-    Rotate   :   0,90,180,270
-******************************************************************************/
-void Paint_SetRotate(UWORD Rotate)
-{
-    if(Rotate == ROTATE_0 || Rotate == ROTATE_90 || Rotate == ROTATE_180 || Rotate == ROTATE_270) {
-        Debug("Set image Rotate %d\r\n", Rotate);
-        Paint.Rotate = Rotate;
-    } else {
-        Debug("rotate = 0, 90, 180, 270\r\n");
-      //  exit(0);
-    }
-}
-
-/******************************************************************************
-function:	Select Image mirror
-parameter:
-    mirror   :       Not mirror,Horizontal mirror,Vertical mirror,Origin mirror
-******************************************************************************/
-void Paint_SetMirroring(UBYTE mirror)
-{
-    if(mirror == MIRROR_NONE || mirror == MIRROR_HORIZONTAL || 
-        mirror == MIRROR_VERTICAL || mirror == MIRROR_ORIGIN) {
-        Debug("mirror image x:%s, y:%s\r\n",(mirror & 0x01)? "mirror":"none", ((mirror >> 1) & 0x01)? "mirror":"none");
-        Paint.Mirror = mirror;
-    } else {
-        Debug("mirror should be MIRROR_NONE, MIRROR_HORIZONTAL, \
-        MIRROR_VERTICAL or MIRROR_ORIGIN\r\n");
-//exit(0);
-    }    
-}
-
-/******************************************************************************
-function:	Draw Pixels
-parameter:
-    Xpoint  :   At point X
-    Ypoint  :   At point Y
-    Color   :   Painted colors
-******************************************************************************/
-void Paint_SetPixel(UWORD Xpoint, UWORD Ypoint, UWORD Color)
-{
-    if(Xpoint > Paint.Width || Ypoint > Paint.Height){
-        Debug("Exceeding display boundaries\r\n");
-        return;
-    }      
-    UWORD X, Y;
-
-    switch(Paint.Rotate) {
-    case 0:
-        X = Xpoint;
-        Y = Ypoint;  
-        break;
-    case 90:
-        X = Paint.WidthMemory - Ypoint - 1;
-        Y = Xpoint;
-        break;
-    case 180:
-        X = Paint.WidthMemory - Xpoint - 1;
-        Y = Paint.HeightMemory - Ypoint - 1;
-        break;
-    case 270:
-        X = Ypoint;
-        Y = Paint.HeightMemory - Xpoint - 1;
-        break;
-
-    default:
-        return;
-    }
-    
-    switch(Paint.Mirror) {
-    case MIRROR_NONE:
-        break;
-    case MIRROR_HORIZONTAL:
-        X = Paint.WidthMemory - X - 1;
-        break;
-    case MIRROR_VERTICAL:
-        Y = Paint.HeightMemory - Y - 1;
-        break;
-    case MIRROR_ORIGIN:
-        X = Paint.WidthMemory - X - 1;
-        Y = Paint.HeightMemory - Y - 1;
-        break;
-    default:
-        return;
-    }
-
-    // printf("x = %d, y = %d\r\n", X, Y);
-    if(X > Paint.WidthMemory || Y > Paint.HeightMemory){
-        Debug("Exceeding display boundaries\r\n");
-        return;
-    }
-    
-   // UDOUBLE Addr = X / 8 + Y * Paint.WidthByte;
-		LCD_SetUWORD(X,Y, Color);
-}
-
-/******************************************************************************
-function:	Clear the color of the picture
-parameter:
-    Color   :   Painted colors
-******************************************************************************/
-void Paint_Clear(UWORD Color)
-{	
-	LCD_Clear(Color);
-}
-
-/******************************************************************************
-function:	Clear the color of a window
-parameter:
-    Xstart :   x starting point
-    Ystart :   Y starting point
-    Xend   :   x end point
-    Yend   :   y end point
-******************************************************************************/
-void Paint_ClearWindows(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend, UWORD Color)
-{
-    UWORD X, Y;
-    for (Y = Ystart; Y < Yend; Y++) {
-        for (X = Xstart; X < Xend; X++) {//8 pixel =  1 byte
-            Paint_SetPixel(X, Y, Color);
-        }
-    }
-}
-
-/******************************************************************************
-function:	Draw Point(Xpoint, Ypoint) Fill the color
-parameter:
-    Xpoint		:   The Xpoint coordinate of the point
-    Ypoint		:   The Ypoint coordinate of the point
-    Color		:   Set color
-    Dot_Pixel	:	point size
-******************************************************************************/
-void Paint_DrawPoint(UWORD Xpoint, UWORD Ypoint, UWORD Color,
-                     DOT_PIXEL Dot_Pixel, DOT_STYLE DOT_STYLE)
-{
-    if (Xpoint > Paint.Width || Ypoint > Paint.Height) {
-        // Debug("Paint_DrawPoint Input exceeds the normal display range\r\n");
-        return;
-    }
-
-    int16_t XDir_Num , YDir_Num;
-    if (DOT_STYLE == DOT_FILL_AROUND) {
-        for (XDir_Num = 0; XDir_Num < 2*Dot_Pixel - 1; XDir_Num++) {
-            for (YDir_Num = 0; YDir_Num < 2 * Dot_Pixel - 1; YDir_Num++) {
-                if(Xpoint + XDir_Num - Dot_Pixel < 0 || Ypoint + YDir_Num - Dot_Pixel < 0)
-                    break;
-                // printf("x = %d, y = %d\r\n", Xpoint + XDir_Num - Dot_Pixel, Ypoint + YDir_Num - Dot_Pixel);
-                Paint_SetPixel(Xpoint + XDir_Num - Dot_Pixel, Ypoint + YDir_Num - Dot_Pixel, Color);
-            }
-        }
-    } else {
-        for (XDir_Num = 0; XDir_Num <  Dot_Pixel; XDir_Num++) {
-            for (YDir_Num = 0; YDir_Num <  Dot_Pixel; YDir_Num++) {
-                Paint_SetPixel(Xpoint + XDir_Num - 1, Ypoint + YDir_Num - 1, Color);
-            }
-        }
-    }
-}
-
-/******************************************************************************
-function:	Draw a line of arbitrary slope
-parameter:
-    Xstart :Starting Xpoint point coordinates
-    Ystart :Starting Xpoint point coordinates
-    Xend   :End point Xpoint coordinate
-    Yend   :End point Ypoint coordinate
-    Color  :The color of the line segment
-******************************************************************************/
-void Paint_DrawLine(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend,
-                    UWORD Color, LINE_STYLE Line_Style, DOT_PIXEL Dot_Pixel)
-{
-    if (Xstart > Paint.Width || Ystart > Paint.Height ||
-        Xend > Paint.Width || Yend > Paint.Height) {
-        Debug("Paint_DrawLine Input exceeds the normal display range\r\n");
-        return;
-    }
-
-    UWORD Xpoint = Xstart;
-    UWORD Ypoint = Ystart;
-    int dx = (int)Xend - (int)Xstart >= 0 ? Xend - Xstart : Xstart - Xend;
-    int dy = (int)Yend - (int)Ystart <= 0 ? Yend - Ystart : Ystart - Yend;
-
-    // Increment direction, 1 is positive, -1 is counter;
-    int XAddway = Xstart < Xend ? 1 : -1;
-    int YAddway = Ystart < Yend ? 1 : -1;
-
-    //Cumulative error
-    int Esp = dx + dy;
-    char Dotted_Len = 0;
-
-    for (;;) {
-        Dotted_Len++;
-        //Painted dotted line, 2 point is really virtual
-        if (Line_Style == LINE_STYLE_DOTTED && Dotted_Len % 3 == 0) {
-            //Debug("LINE_DOTTED\r\n");
-            Paint_DrawPoint(Xpoint, Ypoint, IMAGE_BACKGROUND, Dot_Pixel, DOT_STYLE_DFT);
-            Dotted_Len = 0;
-        } else {
-            Paint_DrawPoint(Xpoint, Ypoint, Color, Dot_Pixel, DOT_STYLE_DFT);
-        }
-        if (2 * Esp >= dy) {
-            if (Xpoint == Xend)
-                break;
-            Esp += dy;
-            Xpoint += XAddway;
-        }
-        if (2 * Esp <= dx) {
-            if (Ypoint == Yend)
-                break;
-            Esp += dx;
-            Ypoint += YAddway;
-        }
-    }
-}
-
-/******************************************************************************
-function:	Draw a rectangle
-parameter:
-    Xstart :Rectangular  Starting Xpoint point coordinates
-    Ystart :Rectangular  Starting Xpoint point coordinates
-    Xend   :Rectangular  End point Xpoint coordinate
-    Yend   :Rectangular  End point Ypoint coordinate
-    Color  :The color of the Rectangular segment
-    Filled : Whether it is filled--- 1 solid 0:empty
-******************************************************************************/
-void Paint_DrawRectangle(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend,
-                         UWORD Color, DRAW_FILL Filled, DOT_PIXEL Dot_Pixel)
-{
-    if (Xstart > Paint.Width || Ystart > Paint.Height ||
-        Xend > Paint.Width || Yend > Paint.Height) {
-        Debug("Input exceeds the normal display range\r\n");
-        return;
-    }
-
-    if (Filled ) {
-        UWORD Ypoint;
-        for(Ypoint = Ystart; Ypoint < Yend; Ypoint++) {
-            Paint_DrawLine(Xstart, Ypoint, Xend, Ypoint, Color , LINE_STYLE_SOLID, Dot_Pixel);
-        }
-    } else {
-        Paint_DrawLine(Xstart, Ystart, Xend, Ystart, Color , LINE_STYLE_SOLID, Dot_Pixel);
-        Paint_DrawLine(Xstart, Ystart, Xstart, Yend, Color , LINE_STYLE_SOLID, Dot_Pixel);
-        Paint_DrawLine(Xend, Yend, Xend, Ystart, Color , LINE_STYLE_SOLID, Dot_Pixel);
-        Paint_DrawLine(Xend, Yend, Xstart, Yend, Color , LINE_STYLE_SOLID, Dot_Pixel);
-    }
-}
-
-/******************************************************************************
-function:	Use the 8-point method to draw a circle of the
-            specified size at the specified position->
-parameter:
-    X_Center  :Center X coordinate
-    Y_Center  :Center Y coordinate
-    Radius    :circle Radius
-    Color     :The color of the :circle segment
-    Filled    : Whether it is filled: 1 filling 0:Do not
-******************************************************************************/
-void Paint_DrawCircle(UWORD X_Center, UWORD Y_Center, UWORD Radius,
-                      UWORD Color, DRAW_FILL  Draw_Fill , DOT_PIXEL Dot_Pixel)
-{
-    if (X_Center > Paint.Width || Y_Center >= Paint.Height) {
-        Debug("Paint_DrawCircle Input exceeds the normal display range\r\n");
-        return;
-    }
-
-    //Draw a circle from(0, R) as a starting point
-    int16_t XCurrent, YCurrent;
-    XCurrent = 0;
-    YCurrent = Radius;
-
-    //Cumulative error,judge the next point of the logo
-    int16_t Esp = 3 - (Radius << 1 );
-
-    int16_t sCountY;
-    if (Draw_Fill == DRAW_FILL_FULL) {
-        while (XCurrent <= YCurrent ) { //Realistic circles
-            for (sCountY = XCurrent; sCountY <= YCurrent; sCountY ++ ) {
-                Paint_DrawPoint(X_Center + XCurrent, Y_Center + sCountY, Color, DOT_PIXEL_DFT, DOT_STYLE_DFT);//1
-                Paint_DrawPoint(X_Center - XCurrent, Y_Center + sCountY, Color, DOT_PIXEL_DFT, DOT_STYLE_DFT);//2
-                Paint_DrawPoint(X_Center - sCountY, Y_Center + XCurrent, Color, DOT_PIXEL_DFT, DOT_STYLE_DFT);//3
-                Paint_DrawPoint(X_Center - sCountY, Y_Center - XCurrent, Color, DOT_PIXEL_DFT, DOT_STYLE_DFT);//4
-                Paint_DrawPoint(X_Center - XCurrent, Y_Center - sCountY, Color, DOT_PIXEL_DFT, DOT_STYLE_DFT);//5
-                Paint_DrawPoint(X_Center + XCurrent, Y_Center - sCountY, Color, DOT_PIXEL_DFT, DOT_STYLE_DFT);//6
-                Paint_DrawPoint(X_Center + sCountY, Y_Center - XCurrent, Color, DOT_PIXEL_DFT, DOT_STYLE_DFT);//7
-                Paint_DrawPoint(X_Center + sCountY, Y_Center + XCurrent, Color, DOT_PIXEL_DFT, DOT_STYLE_DFT);
-            }
-            if (Esp < 0 )
-                Esp += 4 * XCurrent + 6;
-            else {
-                Esp += 10 + 4 * (XCurrent - YCurrent );
-                YCurrent --;
-            }
-            XCurrent ++;
-        }
-    } else { //Draw a hollow circle
-        while (XCurrent <= YCurrent ) {
-            Paint_DrawPoint(X_Center + XCurrent, Y_Center + YCurrent, Color, Dot_Pixel, DOT_STYLE_DFT);//1
-            Paint_DrawPoint(X_Center - XCurrent, Y_Center + YCurrent, Color, Dot_Pixel, DOT_STYLE_DFT);//2
-            Paint_DrawPoint(X_Center - YCurrent, Y_Center + XCurrent, Color, Dot_Pixel, DOT_STYLE_DFT);//3
-            Paint_DrawPoint(X_Center - YCurrent, Y_Center - XCurrent, Color, Dot_Pixel, DOT_STYLE_DFT);//4
-            Paint_DrawPoint(X_Center - XCurrent, Y_Center - YCurrent, Color, Dot_Pixel, DOT_STYLE_DFT);//5
-            Paint_DrawPoint(X_Center + XCurrent, Y_Center - YCurrent, Color, Dot_Pixel, DOT_STYLE_DFT);//6
-            Paint_DrawPoint(X_Center + YCurrent, Y_Center - XCurrent, Color, Dot_Pixel, DOT_STYLE_DFT);//7
-            Paint_DrawPoint(X_Center + YCurrent, Y_Center + XCurrent, Color, Dot_Pixel, DOT_STYLE_DFT);//0
-
-            if (Esp < 0 )
-                Esp += 4 * XCurrent + 6;
-            else {
-                Esp += 10 + 4 * (XCurrent - YCurrent );
-                YCurrent --;
-            }
-            XCurrent ++;
-        }
-    }
-}
-
-/******************************************************************************
-function:	Show English characters
-parameter:
-    Xpoint           :X coordinate
-    Ypoint           :Y coordinate
-    Acsii_Char       :To display the English characters
-    Font             :A structure pointer that displays a character size
-    Color_Background : Select the background color of the English character
-    Color_Foreground : Select the foreground color of the English character
-******************************************************************************/
-void Paint_DrawChar(UWORD Xpoint, UWORD Ypoint, const char Acsii_Char,
-                    sFONT* Font, UWORD Color_Background, UWORD Color_Foreground)
-{
-    UWORD Page, Column;
-
-    if (Xpoint > Paint.Width || Ypoint > Paint.Height) {
-        Debug("Paint_DrawChar Input exceeds the normal display range\r\n");
-        return;
-    }
-
-    uint32_t Char_Offset = (Acsii_Char - ' ') * Font->Height * (Font->Width / 8 + (Font->Width % 8 ? 1 : 0));
-    const unsigned char *ptr = &Font->table[Char_Offset];
-
-    for (Page = 0; Page < Font->Height; Page ++ ) {
-        for (Column = 0; Column < Font->Width; Column ++ ) {
-
-            //To determine whether the font background color and screen background color is consistent
-            if (FONT_BACKGROUND == Color_Background) { //this process is to speed up the scan
-                if (*ptr & (0x80 >> (Column % 8)))
-                    Paint_SetPixel(Xpoint + Column, Ypoint + Page, Color_Foreground);
-                    // Paint_DrawPoint(Xpoint + Column, Ypoint + Page, Color_Foreground, DOT_PIXEL_DFT, DOT_STYLE_DFT);
-            } else {
-                if (*ptr & (0x80 >> (Column % 8))) {
-                    Paint_SetPixel(Xpoint + Column, Ypoint + Page, Color_Foreground);
-                    // Paint_DrawPoint(Xpoint + Column, Ypoint + Page, Color_Foreground, DOT_PIXEL_DFT, DOT_STYLE_DFT);
-                } else {
-                    Paint_SetPixel(Xpoint + Column, Ypoint + Page, Color_Background);
-                    // Paint_DrawPoint(Xpoint + Column, Ypoint + Page, Color_Background, DOT_PIXEL_DFT, DOT_STYLE_DFT);
-                }
-            }
-            //One pixel is 8 bits
-            if (Column % 8 == 7)
-                ptr++;
-        }// Write a line
-        if (Font->Width % 8 != 0)
-            ptr++;
-    }// Write all
-}
-
-/******************************************************************************
-function:	Display the string
-parameter:
-    Xstart           :X coordinate
-    Ystart           :Y coordinate
-    pString          :The first address of the English string to be displayed
-    Font             :A structure pointer that displays a character size
-    Color_Background : Select the background color of the English character
-    Color_Foreground : Select the foreground color of the English character
-******************************************************************************/
-void Paint_DrawString_EN(UWORD Xstart, UWORD Ystart, const char * pString,
-                         sFONT* Font, UWORD Color_Background, UWORD Color_Foreground )
-{
-    UWORD Xpoint = Xstart;
-    UWORD Ypoint = Ystart;
-
-    if (Xstart > Paint.Width || Ystart > Paint.Height) {
-        Debug("Paint_DrawString_EN Input exceeds the normal display range\r\n");
-        return;
-    }
-
-    while (* pString != '\0') {
-        //if X direction filled , reposition to(Xstart,Ypoint),Ypoint is Y direction plus the Height of the character
-        if ((Xpoint + Font->Width ) > Paint.Width ) {
-            Xpoint = Xstart;
-            Ypoint += Font->Height;
-        }
-
-        // If the Y direction is full, reposition to(Xstart, Ystart)
-        if ((Ypoint  + Font->Height ) > Paint.Height ) {
-            Xpoint = Xstart;
-            Ypoint = Ystart;
-        }
-        Paint_DrawChar(Xpoint, Ypoint, * pString, Font, Color_Background, Color_Foreground);
-
-        //The next character of the address
-        pString ++;
-
-        //The next word of the abscissa increases the font of the broadband
-        Xpoint += Font->Width;
-    }
-}
-
-
-/******************************************************************************
-function:	Display the string
-parameter:
-    Xstart           :X coordinate
-    Ystart           :Y coordinate
-    pString          :The first address of the Chinese string and English
-                        string to be displayed
-    Font             :A structure pointer that displays a character size
-    Color_Background : Select the background color of the English character
-    Color_Foreground : Select the foreground color of the English character
-******************************************************************************/
-void Paint_DrawString_CN(UWORD Xstart, UWORD Ystart, const char * pString, cFONT* font, UWORD Color_Background, UWORD Color_Foreground)
-{
-    const char* p_text = pString;
-    int x = Xstart, y = Ystart;
-    int i, j,Num;
-
-    /* Send the string character by character on EPD */
-    while (*p_text != 0) {
-        if(*p_text <= 0x7F) {  //ASCII < 126
-            for(Num = 0; Num < font->size; Num++) {
-                if(*p_text== font->table[Num].index[0]) {
-                    const char* ptr = &font->table[Num].matrix[0];
-
-                    for (j = 0; j < font->Height; j++) {
-                        for (i = 0; i < font->Width; i++) {
-                            if (FONT_BACKGROUND == Color_Background) { //this process is to speed up the scan
-                                if (*ptr & (0x80 >> (i % 8))) {
-                                    Paint_SetPixel(x + i, y + j, Color_Foreground);
-                                    // Paint_DrawPoint(x + i, y + j, Color_Foreground, DOT_PIXEL_DFT, DOT_STYLE_DFT);
-                                }
-                            } else {
-                                if (*ptr & (0x80 >> (i % 8))) {
-                                    Paint_SetPixel(x + i, y + j, Color_Foreground);
-                                    // Paint_DrawPoint(x + i, y + j, Color_Foreground, DOT_PIXEL_DFT, DOT_STYLE_DFT);
-                                } else {
-                                    Paint_SetPixel(x + i, y + j, Color_Background);
-                                    // Paint_DrawPoint(x + i, y + j, Color_Background, DOT_PIXEL_DFT, DOT_STYLE_DFT);
-                                }
-                            }
-                            if (i % 8 == 7) {
-                                ptr++;
-                            }
-                        }
-                        if (font->Width % 8 != 0) {
-                            ptr++;
-                        }
-                    }
-                    break;
-                }
-            }
-            /* Point on the next character */
-            p_text += 1;
-            /* Decrement the column position by 16 */
-            x += font->ASCII_Width;
-        } else {        //Chinese
-            for(Num = 0; Num < font->size; Num++) {
-                if((*p_text== font->table[Num].index[0]) && (*(p_text+1) == font->table[Num].index[1])) {
-                    const char* ptr = &font->table[Num].matrix[0];
-
-                    for (j = 0; j < font->Height; j++) {
-                        for (i = 0; i < font->Width; i++) {
-                            if (FONT_BACKGROUND == Color_Background) { //this process is to speed up the scan
-                                if (*ptr & (0x80 >> (i % 8))) {
-                                    Paint_SetPixel(x + i, y + j, Color_Foreground);
-                                    // Paint_DrawPoint(x + i, y + j, Color_Foreground, DOT_PIXEL_DFT, DOT_STYLE_DFT);
-                                }
-                            } else {
-                                if (*ptr & (0x80 >> (i % 8))) {
-                                    Paint_SetPixel(x + i, y + j, Color_Foreground);
-                                    // Paint_DrawPoint(x + i, y + j, Color_Foreground, DOT_PIXEL_DFT, DOT_STYLE_DFT);
-                                } else {
-                                    Paint_SetPixel(x + i, y + j, Color_Background);
-                                    // Paint_DrawPoint(x + i, y + j, Color_Background, DOT_PIXEL_DFT, DOT_STYLE_DFT);
-                                }
-                            }
-                            if (i % 8 == 7) {
-                                ptr++;
-                            }
-                        }
-                        if (font->Width % 8 != 0) {
-                            ptr++;
-                        }
-                    }
-                    break;
-                }
-            }
-            /* Point on the next character */
-            p_text += 2;
-            /* Decrement the column position by 16 */
-            x += font->Width;
-        }
-    }
-}
-
-/******************************************************************************
-function:	Display nummber
-parameter:
-    Xstart           :X coordinate
-    Ystart           : Y coordinate
-    Nummber          : The number displayed
-    Font             :A structure pointer that displays a character size
-    Color_Background : Select the background color of the English character
-    Color_Foreground : Select the foreground color of the English character
-******************************************************************************/
-#define  ARRAY_LEN 255
-void Paint_DrawNum(UWORD Xpoint, UWORD Ypoint, int32_t Nummber,
-                   sFONT* Font, UWORD Color_Background, UWORD Color_Foreground )
-{
-
-    int16_t Num_Bit = 0, Str_Bit = 0;
-    uint8_t Str_Array[ARRAY_LEN] = {0}, Num_Array[ARRAY_LEN] = {0};
-    uint8_t *pStr = Str_Array;
-
-    if (Xpoint > Paint.Width || Ypoint > Paint.Height) {
-        Debug("Paint_DisNum Input exceeds the normal display range\r\n");
-        return;
-    }
-
-    //Converts a number to a string
-    while (Nummber) {
-        Num_Array[Num_Bit] = Nummber % 10 + '0';
-        Num_Bit++;
-        Nummber /= 10;
-    }
-
-    //The string is inverted
-    while (Num_Bit > 0) {
-        Str_Array[Str_Bit] = Num_Array[Num_Bit - 1];
-        Str_Bit ++;
-        Num_Bit --;
-    }
-
-    //show
-    Paint_DrawString_EN(Xpoint, Ypoint, (const char*)pStr, Font, Color_Background, Color_Foreground);
-}
-
-/******************************************************************************
-function:	Display time
-parameter:
-    Xstart           :X coordinate
-    Ystart           : Y coordinate
-    pTime            : Time-related structures
-    Font             :A structure pointer that displays a character size
-    Color            : Select the background color of the English character
-******************************************************************************/
-void Paint_DrawTime(UWORD Xstart, UWORD Ystart, PAINT_TIME *pTime, sFONT* Font,
-                    UWORD Color_Background, UWORD Color_Foreground)
-{
-    uint8_t value[10] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
-
-    UWORD Dx = Font->Width;
-
-    //Write data into the cache
-    Paint_DrawChar(Xstart                           , Ystart, value[pTime->Hour / 10], Font, Color_Background, Color_Foreground);
-    Paint_DrawChar(Xstart + Dx                      , Ystart, value[pTime->Hour % 10], Font, Color_Background, Color_Foreground);
-    Paint_DrawChar(Xstart + Dx  + Dx / 4 + Dx / 2   , Ystart, ':'                    , Font, Color_Background, Color_Foreground);
-    Paint_DrawChar(Xstart + Dx * 2 + Dx / 2         , Ystart, value[pTime->Min / 10] , Font, Color_Background, Color_Foreground);
-    Paint_DrawChar(Xstart + Dx * 3 + Dx / 2         , Ystart, value[pTime->Min % 10] , Font, Color_Background, Color_Foreground);
-    Paint_DrawChar(Xstart + Dx * 4 + Dx / 2 - Dx / 4, Ystart, ':'                    , Font, Color_Background, Color_Foreground);
-    Paint_DrawChar(Xstart + Dx * 5                  , Ystart, value[pTime->Sec / 10] , Font, Color_Background, Color_Foreground);
-    Paint_DrawChar(Xstart + Dx * 6                  , Ystart, value[pTime->Sec % 10] , Font, Color_Background, Color_Foreground);
-}
-
-/******************************************************************************
-function:	Display image
-parameter:
-    image            :Image start address
-    xStart           : X starting coordinates
-    yStart           : Y starting coordinates
-    xEnd             :Image width
-    yEnd             : Image height
-******************************************************************************/
-void Paint_DrawImage(const unsigned char *image, UWORD xStart, UWORD yStart, UWORD W_Image, UWORD H_Image) 
-{
-    int i,j; 
-		for(j = 0; j < H_Image; j++){
-			for(i = 0; i < W_Image; i++){
-				if(xStart+i < LCD_WIDTH  &&  yStart+j < LCD_HEIGHT)//Exceeded part does not display
-					Paint_SetPixel(xStart + i, yStart + j, (*(image + j*W_Image*2 + i*2+1))<<8 | (*(image + j*W_Image*2 + i*2)));
-				//Using arrays is a property of sequential storage, accessing the original array by algorithm
-				//j*W_Image*2 			   Y offset
-				//i*2              	   X offset
-			}
-		}
-      
-}
diff --git a/lib/gfx/GUI_DEV/GUI_Paint.h b/lib/gfx/GUI_DEV/GUI_Paint.h
deleted file mode 100644
index 187c9631470cef6b9e8e5b4da8450602923571dd..0000000000000000000000000000000000000000
--- a/lib/gfx/GUI_DEV/GUI_Paint.h
+++ /dev/null
@@ -1,207 +0,0 @@
-/*****************************************************************************
-* | File      	:   GUI_Paint.h
-* | Author      :   Waveshare team
-* | Function    :	Achieve drawing: draw points, lines, boxes, circles and
-*                   their size, solid dotted line, solid rectangle hollow
-*                   rectangle, solid circle hollow circle.
-* | Info        :
-*   Achieve display characters: Display a single character, string, number
-*   Achieve time display: adaptive size display time minutes and seconds
-*----------------
-* |	This version:   V2.0
-* | Date        :   2018-11-15
-* | Info        :
-* 1.add: Paint_NewImage()
-*    Create an image's properties
-* 2.add: Paint_SelectImage()
-*    Select the picture to be drawn
-* 3.add: Paint_SetRotate()
-*    Set the direction of the cache    
-* 4.add: Paint_RotateImage() 
-*    Can flip the picture, Support 0-360 degrees, 
-*    but only 90.180.270 rotation is better
-* 4.add: Paint_SetMirroring() 
-*    Can Mirroring the picture, horizontal, vertical, origin
-* 5.add: Paint_DrawString_CN() 
-*    Can display Chinese(GB1312)    
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy
-* of this software and associated documnetation files (the "Software"), to deal
-* in the Software without restriction, including without limitation the rights
-* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-* copies of the Software, and to permit persons to  whom the Software is
-* furished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in
-* all copies or substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-* FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-* LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-* THE SOFTWARE.
-*
-******************************************************************************/
-#ifndef __GUI_PAINT_H
-#define __GUI_PAINT_H
-
-#include "DEV_Config.h"
-#include "LCD_Driver.h"
-#include "fonts.h"
-#include "Debug.h"
-
-/**
- * Image attributes
-**/
-typedef struct {
-    UBYTE *Image;
-    UWORD Width;
-    UWORD Height;
-    UWORD WidthMemory;
-    UWORD HeightMemory;
-    UWORD Color;
-    UWORD Rotate;
-    UWORD Mirror;
-    UWORD WidthByte;
-    UWORD HeightByte;
-} PAINT;
-extern volatile PAINT Paint;
-
-/**
- * Display rotate
-**/
-#define ROTATE_0            0
-#define ROTATE_90           90
-#define ROTATE_180          180
-#define ROTATE_270          270
-
-/**
- * Display Flip
-**/
-typedef enum {
-    MIRROR_NONE  = 0x00,
-    MIRROR_HORIZONTAL = 0x01,
-    MIRROR_VERTICAL = 0x02,
-    MIRROR_ORIGIN = 0x03,
-} MIRROR_IMAGE;
-#define MIRROR_IMAGE_DFT MIRROR_NONE
-
-/**
- * image color
-**/
-
-#define WHITE					0xFFFF
-#define BLACK					0x0000	  
-#define BLUE 					0x001F  
-#define BRED 					0XF81F
-#define GRED 					0XFFE0
-#define GBLUE					0X07FF
-#define RED  					0xF800
-#define MAGENTA				0xF81F
-#define GREEN					0x07E0
-#define CYAN 					0x7FFF
-#define YELLOW				0xFFE0
-#define BROWN					0XBC40 
-#define BRRED					0XFC07 
-#define GRAY 					0X8430 
-#define DARKBLUE			0X01CF	
-#define LIGHTBLUE			0X7D7C	 
-#define GRAYBLUE      0X5458 
-#define LIGHTGREEN    0X841F 
-#define LGRAY 			  0XC618 
-#define LGRAYBLUE     0XA651
-#define LBBLUE        0X2B12 
-
-
-#define IMAGE_BACKGROUND    WHITE
-#define FONT_FOREGROUND     BLACK
-#define FONT_BACKGROUND     WHITE
-
-/**
- * The size of the point
-**/
-typedef enum {
-    DOT_PIXEL_1X1  = 1,		// 1 x 1
-    DOT_PIXEL_2X2  , 		// 2 X 2
-    DOT_PIXEL_3X3  ,		// 3 X 3
-    DOT_PIXEL_4X4  ,		// 4 X 4
-    DOT_PIXEL_5X5  , 		// 5 X 5
-    DOT_PIXEL_6X6  , 		// 6 X 6
-    DOT_PIXEL_7X7  , 		// 7 X 7
-    DOT_PIXEL_8X8  , 		// 8 X 8
-} DOT_PIXEL;
-#define DOT_PIXEL_DFT  DOT_PIXEL_1X1  //Default dot pilex
-
-/**
- * Point size fill style
-**/
-typedef enum {
-    DOT_FILL_AROUND  = 1,		// dot pixel 1 x 1
-    DOT_FILL_RIGHTUP  , 		// dot pixel 2 X 2
-} DOT_STYLE;
-#define DOT_STYLE_DFT  DOT_FILL_AROUND  //Default dot pilex
-
-/**
- * Line style, solid or dashed
-**/
-typedef enum {
-    LINE_STYLE_SOLID = 0,
-    LINE_STYLE_DOTTED,
-} LINE_STYLE;
-
-/**
- * Whether the graphic is filled
-**/
-typedef enum {
-    DRAW_FILL_EMPTY = 0,
-    DRAW_FILL_FULL,
-} DRAW_FILL;
-
-/**
- * Custom structure of a time attribute
-**/
-typedef struct {
-    UWORD Year;  //0000
-    UBYTE  Month; //1 - 12
-    UBYTE  Day;   //1 - 30
-    UBYTE  Hour;  //0 - 23
-    UBYTE  Min;   //0 - 59
-    UBYTE  Sec;   //0 - 59
-} PAINT_TIME;
-extern PAINT_TIME sPaint_time;
-
-//init and Clear
-void Paint_NewImage(UWORD Width, UWORD Height, UWORD Rotate, UWORD Color);
-void Paint_SelectImage(UBYTE *image);
-void Paint_SetRotate(UWORD Rotate);
-void Paint_SetMirroring(UBYTE mirror);
-void Paint_SetPixel(UWORD Xpoint, UWORD Ypoint, UWORD Color);
-
-void Paint_Clear(UWORD Color);
-void Paint_ClearWindows(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend, UWORD Color);
-
-//Drawing
-void Paint_DrawPoint(UWORD Xpoint, UWORD Ypoint, UWORD Color, DOT_PIXEL Dot_Pixel, DOT_STYLE Dot_FillWay);
-void Paint_DrawLine(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend, UWORD Color, LINE_STYLE Line_Style, DOT_PIXEL Dot_Pixel);
-void Paint_DrawRectangle(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend, UWORD Color, DRAW_FILL Filled , DOT_PIXEL Dot_Pixel);
-void Paint_DrawCircle(UWORD X_Center, UWORD Y_Center, UWORD Radius, UWORD Color, DRAW_FILL Draw_Fill , DOT_PIXEL Dot_Pixel);
-
-//Display string
-void Paint_DrawChar(UWORD Xstart, UWORD Ystart, const char Acsii_Char, sFONT* Font, UWORD Color_Background, UWORD Color_Foreground);
-void Paint_DrawString_EN(UWORD Xstart, UWORD Ystart, const char * pString, sFONT* Font, UWORD Color_Background, UWORD Color_Foreground);
-void Paint_DrawString_CN(UWORD Xstart, UWORD Ystart, const char * pString, cFONT* font, UWORD Color_Background, UWORD Color_Foreground);
-void Paint_DrawNum(UWORD Xpoint, UWORD Ypoint, int32_t Nummber, sFONT* Font, UWORD Color_Background, UWORD Color_Foreground);
-void Paint_DrawTime(UWORD Xstart, UWORD Ystart, PAINT_TIME *pTime, sFONT* Font, UWORD Color_Background, UWORD Color_Foreground);
-
-//pic
-void Paint_DrawImage(const unsigned char *image,UWORD Startx, UWORD Starty,UWORD Endx, UWORD Endy); 
-
-
-#endif
-
-
-
-
-
diff --git a/lib/gfx/LCD/LCD_Driver.c b/lib/gfx/LCD/LCD_Driver.c
index af5d50e37f886dc6fa212cdca62b60e4594c6f22..481d6929ffdca96dc4711788621200d9faecd9b9 100644
--- a/lib/gfx/LCD/LCD_Driver.c
+++ b/lib/gfx/LCD/LCD_Driver.c
@@ -28,6 +28,8 @@
 #
 ******************************************************************************/
 #include "LCD_Driver.h"
+#include "framebuffer.h"
+
 static uint8_t screen[LCD_HEIGHT][LCD_WIDTH][2];
 
 /*******************************************************************************
@@ -312,10 +314,45 @@ void LCD_Set(uint8_t *data, int len)
 
 uint8_t *LCD_Framebuffer(void)
 {
-    return (uint8_t*)screen;
+	return (uint8_t *)screen;
 }
 
 void LCD_Update(void)
 {
 	LCD_Set((uint8_t *)screen, sizeof(screen));
 }
+
+static Color
+lcd_fb_encode_color_rgb(struct framebuffer *fb, uint8_t r, uint8_t g, uint8_t b)
+{
+	r >>= (8 - 5);
+	g >>= (8 - 6);
+	b >>= (8 - 5);
+
+	// RGB565
+	Color o = 0;
+	o |= (r << 11);
+	o |= (g << 5);
+	o |= b;
+	return o;
+}
+
+static void lcd_fb_update(struct framebuffer *fb)
+{
+	LCD_Update();
+}
+
+static struct framebuffer framebuffer = { .data   = screen,
+					  .width  = LCD_WIDTH,
+					  .height = LCD_HEIGHT,
+					  .stride = LCD_WIDTH * LCD_HEIGHT * 2,
+					  .orientation = FB_O_180,
+
+					  .encode_color_rgb =
+						  lcd_fb_encode_color_rgb,
+					  .update = lcd_fb_update };
+
+struct framebuffer *LCD_framebuffer(void)
+{
+	return &framebuffer;
+}
diff --git a/lib/gfx/LCD/LCD_Driver.h b/lib/gfx/LCD/LCD_Driver.h
index 597956b2f7620dce0ab7f9034cc1ccdea953ab83..49358115beea4ac4a7f8ec30b2b827149c6dfdad 100644
--- a/lib/gfx/LCD/LCD_Driver.h
+++ b/lib/gfx/LCD/LCD_Driver.h
@@ -31,6 +31,7 @@
 #define __LCD_DRIVER_H
 
 #include "DEV_Config.h"
+#include "framebuffer.h"
 
 #define LCD_WIDTH   160 //LCD width
 #define LCD_HEIGHT  80 //LCD height
@@ -52,4 +53,6 @@ uint8_t *LCD_Framebuffer(void);
 void LCD_Set(uint8_t *data, int len);
 void LCD_Update(void);
 
+struct framebuffer *LCD_framebuffer(void);
+
 #endif
diff --git a/lib/gfx/framebuffer.c b/lib/gfx/framebuffer.c
new file mode 100644
index 0000000000000000000000000000000000000000..672e956a3467999a9d8c232acb3510f4711786cb
--- /dev/null
+++ b/lib/gfx/framebuffer.c
@@ -0,0 +1,101 @@
+#include "framebuffer.h"
+
+void fb_clear_to_color(struct framebuffer *fb, Color c)
+{
+	for (int y = 0; y < fb->height; y++) {
+		for (int x = 0; x < fb->width; x++)
+			fb_setpixel(fb, x, y, c);
+	}
+}
+
+void fb_clear(struct framebuffer *fb)
+{
+	Color c = fb->encode_color_rgb(fb, 0, 0, 0);
+	fb_clear_to_color(fb, c);
+}
+
+Color fb_encode_color_rgb(struct framebuffer *fb, int r, int g, int b)
+{
+	return fb->encode_color_rgb(fb, r, g, b);
+}
+
+Color fb_encode_color_rgb_f(struct framebuffer *fb, float r, float g, float b)
+{
+	float r8 = r > 1.0f ? 1.0f : (uint8_t)(r * 255.0f);
+	float g8 = g > 1.0f ? 1.0f : (uint8_t)(g * 255.0f);
+	float b8 = b > 1.0f ? 1.0f : (uint8_t)(b * 255.0f);
+
+	r8 = r8 < .0f ? .0f : r8;
+	g8 = g8 < .0f ? .0f : g8;
+	b8 = b8 < .0f ? .0f : b8;
+
+	return fb->encode_color_rgb(fb, r8, g8, b8);
+}
+
+void fb_copy_raw(struct framebuffer *fb, const void *data, size_t size)
+{
+	size_t to_copy = size < fb->stride ? size : fb->stride;
+	memcpy(fb->data, data, to_copy);
+}
+
+void fb_update(struct framebuffer *fb)
+{
+	fb->update(fb);
+}
+
+size_t fb_bytes_per_pixel(const struct framebuffer *fb)
+{
+	const int pixels = fb->height * fb->width;
+	return fb->stride / pixels;
+}
+
+void *fb_pixel(struct framebuffer *fb, int x, int y)
+{
+	int xo;
+	int yo;
+
+	switch (fb->orientation) {
+	case FB_O_0:
+		xo = x;
+		yo = y;
+		break;
+	case FB_O_90:
+		xo = fb->width - y - 1;
+		yo = x;
+		break;
+	case FB_O_180:
+		xo = fb->width - x - 1;
+		yo = fb->height - y - 1;
+		break;
+	case FB_O_270:
+		xo = y;
+		yo = fb->height - x - 1;
+		break;
+	default:
+		return NULL;
+	}
+
+	if (xo < 0 || yo < 0)
+		return NULL;
+	if (xo >= fb->width || yo >= fb->height)
+		return NULL;
+
+	const size_t bpp = fb_bytes_per_pixel(fb);
+	return (void *)(fb->data) + yo * fb->width * bpp + xo * bpp;
+}
+
+void fb_setpixel(struct framebuffer *fb, int x, int y, Color c)
+{
+	uint8_t *pixel = fb_pixel(fb, x, y);
+	if (pixel == NULL)
+		return;
+
+	const uint8_t *color = (const uint8_t *)(&c);
+	const size_t bpp     = fb_bytes_per_pixel(fb);
+	switch (bpp) {
+	default:
+	case 2:
+		pixel[1] = color[0];
+		pixel[0] = color[1];
+	}
+}
diff --git a/lib/gfx/framebuffer.h b/lib/gfx/framebuffer.h
new file mode 100644
index 0000000000000000000000000000000000000000..e2358c73464e7af26cc287c2348c537e5da15bcd
--- /dev/null
+++ b/lib/gfx/framebuffer.h
@@ -0,0 +1,39 @@
+#ifndef FRAMEBUFFER_H
+#define FRAMEBUFFER_H
+#include <stdint.h>
+#include <string.h>
+
+typedef unsigned int Color;
+
+enum orientation {
+	FB_O_0,
+	FB_O_90,
+	FB_O_180,
+	FB_O_270,
+};
+
+struct framebuffer {
+	void *data;
+	size_t width;
+	size_t height;
+	size_t stride;
+	int orientation;
+
+	Color (*encode_color_rgb)(struct framebuffer *fb, uint8_t r, uint8_t g,
+								    uint8_t b);
+	void (*update)(struct framebuffer *fb);
+};
+
+size_t fb_bytes_per_pixel(const struct framebuffer *fb);
+void *fb_pixel(struct framebuffer *fb, int x, int y);
+void fb_setpixel(struct framebuffer *fb, int x, int y, Color c);
+void fb_clear_to_color(struct framebuffer *fb, Color c);
+void fb_clear(struct framebuffer *fb);
+Color fb_encode_color_rgb(struct framebuffer *fb, int r, int g, int b);
+Color fb_encode_color_rgb_f(struct framebuffer *fb, float r, float g,
+							    float b);
+void fb_copy_raw(struct framebuffer *fb, const void *data,
+					     size_t size);
+void fb_update(struct framebuffer *fb);
+
+#endif
diff --git a/lib/gfx/gfx.c b/lib/gfx/gfx.c
new file mode 100644
index 0000000000000000000000000000000000000000..4bc9f35da40eb1808df664ce7da0d7d41186d106
--- /dev/null
+++ b/lib/gfx/gfx.c
@@ -0,0 +1,254 @@
+#include "gfx.h"
+#include "framebuffer.h"
+#include <stddef.h>
+#include <stdlib.h>
+#include <math.h>
+
+const struct gfx_color_rgb gfx_colors_rgb[COLORS] = {
+	{ 255, 255, 255 }, /* WHITE */
+	{ 0, 0, 0 },       /* BLACK */
+	{ 255, 0, 0 },     /* RED */
+	{ 0, 255, 0 },     /* GREEN */
+	{ 0, 0, 255 },     /* BLUE */
+	{ 255, 255, 0 }    /* YELLOW */
+};
+
+void gfx_setpixel(struct gfx_region *r, int x, int y, Color c)
+{
+	if (x < 0 || y < 0)
+		return;
+	if (x >= r->width || y >= r->height)
+		return;
+
+	fb_setpixel(r->fb, r->x + x, r->y + y, c);
+}
+
+struct gfx_region gfx_screen(struct framebuffer *fb)
+{
+	struct gfx_region r = { .fb     = fb,
+				.x      = 0,
+				.y      = 0,
+				.width  = fb->width,
+				.height = fb->height };
+	return r;
+}
+
+static inline int letter_bit(sFONT *font, char c, int x, int y)
+{
+	if (x < 0 || y < 0)
+		return 0;
+	if (x >= font->Width || y >= font->Height)
+		return 0;
+	if (c < ' ' || c > '~')
+		return 0;
+
+	size_t bytes_per_row      = font->Width / 8 + 1;
+	size_t bytes_per_letter   = bytes_per_row * font->Height;
+	int letter                = c - ' ';
+	const uint8_t *letter_ptr = font->table + bytes_per_letter * letter;
+	int horz_byte             = x / 8;
+	int horz_bit              = 7 - x % 8;
+
+	return (*(letter_ptr + y * bytes_per_row + horz_byte) >> horz_bit) & 1;
+}
+
+void gfx_putchar(
+	sFONT *font,
+	struct gfx_region *r,
+	int x,
+	int y,
+	char ch,
+	Color fg,
+	Color bg
+) {
+	for (int yo = 0; yo < font->Height; yo++) {
+		for (int xo = 0; xo < font->Width; xo++) {
+			int lb = letter_bit(font, ch, xo, yo);
+
+			if (fg != bg) {
+				Color c = lb ? fg : bg;
+				gfx_setpixel(r, x + xo, y + yo, c);
+			} else {
+				if (lb) {
+					gfx_setpixel(r, x + xo, y + yo, fg);
+				}
+			}
+		}
+	}
+}
+
+void gfx_puts(
+	sFONT *font,
+	struct gfx_region *r,
+	int x,
+	int y,
+	const char *str,
+	Color fg,
+	Color bg
+) {
+	while (*str) {
+		x += font->Width;
+		if (x >= r->width) {
+			x = 0;
+			y += font->Height;
+		}
+		if (y >= r->height)
+			return;
+		gfx_putchar(font, r, x, y, *str, fg, bg);
+		str++;
+	}
+}
+
+Color gfx_color_rgb_f(struct gfx_region *reg, float r, float g, float b)
+{
+	return fb_encode_color_rgb_f(reg->fb, r, g, b);
+}
+
+Color gfx_color_rgb(struct gfx_region *reg, uint8_t r, uint8_t g, uint8_t b)
+{
+	return fb_encode_color_rgb(reg->fb, r, g, b);
+}
+
+void gfx_update(struct gfx_region *reg)
+{
+	reg->fb->update(reg->fb);
+}
+
+void gfx_clear_to_color(struct gfx_region *reg, Color c)
+{
+	fb_clear_to_color(reg->fb, c);
+}
+
+void gfx_clear(struct gfx_region *reg)
+{
+	gfx_clear_to_color(reg, gfx_color(reg, BLACK));
+}
+
+void gfx_circle(struct gfx_region *reg, int x, int y, int r, int t, Color c)
+{
+	for (int y_ = y - r; y_ <= y + r; y_++) {
+		for (int x_ = x - r; x_ <= x + r; x_++) {
+			int dx    = (x_ - x) * (x_ - x);
+			int dy    = (y_ - y) * (y_ - y);
+			int outer = (r + t) * (r + t);
+			int inner = r * r;
+			int edge = ((dx + dy) >= inner) && ((dx + dy) <= outer);
+			if (edge)
+				gfx_setpixel(reg, x_, y_, c);
+		}
+	}
+}
+
+void gfx_circle_fill(struct gfx_region *reg, int x, int y, int r, Color c)
+{
+	for (int y_ = y - r; y_ <= y + r; y_++) {
+		for (int x_ = x - r; x_ <= x + r; x_++) {
+			int dx   = (x_ - x) * (x_ - x);
+			int dy   = (y_ - y) * (y_ - y);
+			int edge = r * r;
+			int fill = (dx + dy) <= edge;
+			if (fill)
+				gfx_setpixel(reg, x_, y_, c);
+		}
+	}
+}
+
+void gfx_rectangle(
+	struct gfx_region *reg, int x, int y, int w, int h, int t, Color c
+) {
+	if (t > 1) {
+		gfx_thick_line(reg, x, y, x + w, y, t, c);
+		gfx_thick_line(reg, x, y + h, x + w, y + h, t, c);
+		gfx_thick_line(reg, x, y, x, y + h, t, c);
+		gfx_thick_line(reg, x + w, y, x + w, y + h, t, c);
+	} else {
+		gfx_line(reg, x, y, x + w, y, c);
+		gfx_line(reg, x, y + h, x + w, y + h, c);
+		gfx_line(reg, x, y, x, y + h, c);
+		gfx_line(reg, x + w, y, x + w, y + h, c);
+	}
+}
+
+void gfx_rectangle_fill(
+	struct gfx_region *reg, int x, int y, int w, int h, Color c
+) {
+	for (int y_ = y; y_ < y + h; y_++) {
+		for (int x_ = x; x_ < x + w; x_++)
+			gfx_setpixel(reg, x_, y_, c);
+	}
+}
+
+void gfx_line(struct gfx_region *reg, int x1, int y1, int x2, int y2, Color c)
+{
+	float dx = x2 - x1;
+	float dy = y2 - y1;
+	float de = fabs(dy / dx);
+	float e  = .0f;
+	int y    = y1;
+	for (int x = x1; x < x2; x++) {
+		gfx_setpixel(reg, x, y, c);
+		e += de;
+		if (e >= .5f) {
+			y += dy >= .0f ? 1 : -1;
+			e -= 1.0f;
+		}
+	}
+}
+
+void gfx_thick_line(
+	struct gfx_region *reg, int x1, int y1, int x2, int y2, int t, Color c
+) {
+	float dx = x2 - x1;
+	float dy = y2 - y1;
+	float de = fabs(dy / dx);
+	float e  = .0f;
+	int y    = y1;
+	for (int x = x1; x < x2; x++) {
+		gfx_circle_fill(reg, x, y, t, c);
+		e += de;
+		if (e >= .5f) {
+			y += dy >= .0f ? 1 : -1;
+			e -= 1.0f;
+		}
+	}
+}
+
+Color gfx_color(struct gfx_region *reg, enum gfx_color color)
+{
+	if ((int)(color) >= COLORS)
+		return 0;
+
+	const struct gfx_color_rgb *c = &gfx_colors_rgb[color];
+	return gfx_color_rgb(reg, c->r, c->g, c->b);
+}
+
+void gfx_copy_region_raw(
+	struct gfx_region *reg,
+	int x,
+	int y,
+	int w,
+	int h,
+	size_t bpp,
+	const void *p
+) {
+	for (int y_ = 0; y_ < h; y_++) {
+		for (int x_ = 0; x_ < w; x_++) {
+			Color c;
+
+			switch (bpp) {
+			default:
+			case 2:
+				c = *(const uint16_t *)(p);
+				break;
+			}
+
+			gfx_setpixel(reg, x + x_, y + y_, c);
+			p += bpp;
+		}
+	}
+}
+
+void gfx_copy_raw(struct gfx_region *reg, const void *p, size_t size)
+{
+	fb_copy_raw(reg->fb, p, size);
+}
diff --git a/lib/gfx/gfx.h b/lib/gfx/gfx.h
new file mode 100644
index 0000000000000000000000000000000000000000..fbfe916e53133b7a463e044813edd97793bad814
--- /dev/null
+++ b/lib/gfx/gfx.h
@@ -0,0 +1,59 @@
+#ifndef GFX_H
+#define GFX_H
+#include "fonts.h"
+#include "framebuffer.h"
+#include <stddef.h>
+
+struct gfx_region {
+	struct framebuffer *fb;
+	size_t x;
+	size_t y;
+	size_t width;
+	size_t height;
+};
+
+void gfx_setpixel(struct gfx_region *r, int x, int y, Color c);
+struct gfx_region gfx_screen(struct framebuffer *fb);
+void gfx_putchar(sFONT *font, struct gfx_region *reg, int x, int y, char ch,
+						        Color fg, Color bg);
+void gfx_puts(sFONT *font, struct gfx_region *reg, int x, int y,
+			   const char *str, Color fg, Color bg);
+Color gfx_color_rgb_f(struct gfx_region *reg, float r, float g, float b);
+Color gfx_color_rgb(struct gfx_region *reg, uint8_t r, uint8_t g, uint8_t b);
+void gfx_clear_to_color(struct gfx_region *reg, Color c);
+void gfx_clear(struct gfx_region *reg);
+void gfx_circle(struct gfx_region *reg, int x, int y, int r, int t, Color c);
+void gfx_circle_fill(struct gfx_region *reg, int x, int y, int r, Color c);
+void gfx_rectangle(struct gfx_region *reg, int x, int y, int w, int h,
+						      int t, Color c);
+void gfx_rectangle_fill(struct gfx_region *reg, int x, int y, int w, int h,
+								  Color c);
+void gfx_line(struct gfx_region *reg, int x1, int y1, int x2, int y2, Color c);
+void gfx_thick_line(struct gfx_region *reg, int x1, int y1, int x2, int y2,
+							   int t, Color c);
+void gfx_update(struct gfx_region *reg);
+
+enum gfx_color {
+	WHITE,
+	BLACK,
+	RED,
+	GREEN,
+	BLUE,
+	YELLOW,
+
+	COLORS
+};
+
+struct gfx_color_rgb {
+	uint8_t r;
+	uint8_t g;
+	uint8_t b;
+} extern const gfx_colors_rgb[COLORS];
+
+Color gfx_color(struct gfx_region *reg, enum gfx_color color);
+
+void gfx_copy_region_raw(struct gfx_region *reg, int x, int y, int w, int h,
+					         size_t bpp, const void *p);
+void gfx_copy_raw(struct gfx_region *reg, const void *p, size_t size);
+
+#endif
diff --git a/lib/gfx/meson.build b/lib/gfx/meson.build
index 41b377b1fbe12469e8411b23f597b2af51f40e5c..37b90e436950b8e2b164dcb0bede57373281d4ce 100644
--- a/lib/gfx/meson.build
+++ b/lib/gfx/meson.build
@@ -7,7 +7,6 @@ includes = include_directories(
 
 sources = files(
   './GUI_DEV/DEV_Config.c',
-  './GUI_DEV/GUI_Paint.c',
   './LCD/LCD_Driver.c',
   './Fonts/font8.c',
   './Fonts/font12.c',
@@ -16,6 +15,9 @@ sources = files(
   './Fonts/font20.c',
   './Fonts/font24.c',
   './Fonts/font24CN.c',
+  'framebuffer.c',
+  'gfx.c',
+  'textbuffer.c'
 )
 
 lib = static_library(
diff --git a/lib/gfx/textbuffer.c b/lib/gfx/textbuffer.c
new file mode 100644
index 0000000000000000000000000000000000000000..d2a8a39bcbc5dd1deb6d19ebad5a927e95fa31a3
--- /dev/null
+++ b/lib/gfx/textbuffer.c
@@ -0,0 +1,251 @@
+#include "textbuffer.h"
+#include "gfx.h"
+#include <string.h>
+
+void txt_init(struct txt_buffer *txtb, struct gfx_region *reg, sFONT *f)
+{
+	txtb->reg           = reg;
+	txtb->font          = f;
+	txtb->cursor_column = 0;
+	txtb->cursor_row    = 0;
+	txtb->fg_color      = gfx_color_rgb_f(reg, 1.0f, 1.0f, 1.0f);
+	txtb->bg_color      = gfx_color_rgb_f(reg, .0f, .0f, .0f);
+	txtb->draw_cursor   = 1;
+	txtb->auto_update   = 1;
+
+	txt_clear(txtb);
+}
+
+static inline size_t width_(struct txt_buffer *tm)
+{
+	return tm->reg->width / tm->font->Width;
+}
+
+static inline size_t height_(struct txt_buffer *tm)
+{
+	return tm->reg->height / tm->font->Height;
+}
+
+size_t txt_width(struct txt_buffer *tm)
+{
+	return width_(tm);
+}
+
+size_t txt_height(struct txt_buffer *tm)
+{
+	return height_(tm);
+}
+
+static void scrollup(struct txt_buffer *tm)
+{
+	const int last_row     = height_(tm) - 1;
+	const size_t line_size = width_(tm) * sizeof(struct txt_glyph);
+
+	for (int row = 0; row < last_row; row++)
+		memcpy(&tm->text[row][0], &tm->text[row + 1][0], line_size);
+	for (int col = 0; col < width_(tm); col++) {
+		struct txt_glyph *g = &tm->text[last_row][col];
+		g->ch               = ' ';
+		g->fg_color         = tm->fg_color;
+		g->bg_color         = tm->bg_color;
+	}
+}
+
+static void newline(struct txt_buffer *tm)
+{
+	const int last_row = height_(tm) - 1;
+
+	scrollup(tm);
+	tm->cursor_row    = last_row;
+	tm->cursor_column = 0;
+}
+
+static inline void advance_cursor(struct txt_buffer *tm)
+{
+	const int last_row = height_(tm) - 1;
+
+	tm->cursor_column++;
+	if (tm->cursor_column >= width_(tm)) {
+		tm->cursor_column = 0;
+		tm->cursor_row++;
+		if (tm->cursor_row > last_row)
+			newline(tm);
+	}
+}
+
+static void tab(struct txt_buffer *tm)
+{
+	while (tm->cursor_column % 5)
+		txt_putchar(tm, ' ');
+}
+
+static inline struct txt_glyph *getchar_(struct txt_buffer *tm, int x, int y)
+{
+	if (x < 0 || x >= TEXTBUFFER_MAX_WIDTH)
+		return NULL;
+	if (y < 0 || y >= TEXTBUFFER_MAX_HEIGHT)
+		return NULL;
+	return &tm->text[y][x];
+}
+
+void txt_clear(struct txt_buffer *tm)
+{
+	int w = width_(tm);
+	int h = height_(tm);
+	for (int r = 0; r < h; r++) {
+		for (int c = 0; c < w; c++) {
+			struct txt_glyph *g = getchar_(tm, c, r);
+			if (g == NULL)
+				continue;
+
+			g->ch       = ' ';
+			g->fg_color = tm->fg_color;
+			g->bg_color = tm->bg_color;
+		}
+	}
+
+	tm->cursor_column = 0;
+	tm->cursor_row    = 0;
+
+	if (tm->auto_update)
+		txt_update(tm);
+}
+
+void txt_putchar(struct txt_buffer *tm, char ch)
+{
+	struct txt_glyph *g = getchar_(tm, tm->cursor_column, tm->cursor_row);
+	if (g == NULL)
+		return;
+
+	switch (ch) {
+	case '\n':
+		newline(tm);
+		break;
+	case '\t':
+		tab(tm);
+		break;
+	default:
+		g->ch       = ch;
+		g->fg_color = tm->fg_color;
+		g->bg_color = tm->bg_color;
+		advance_cursor(tm);
+	}
+
+	if (tm->auto_update)
+		txt_update(tm);
+}
+
+void txt_puts(struct txt_buffer *tm, const char *str)
+{
+	while (*str) {
+		txt_putchar(tm, *str);
+		str++;
+	}
+}
+
+static inline int cursor_px_column(struct txt_buffer *tm)
+{
+	return tm->font->Width * tm->cursor_column;
+}
+
+static inline int cursor_px_row(struct txt_buffer *tm)
+{
+	return tm->font->Height * tm->cursor_row;
+}
+
+static void draw_cursor_(struct txt_buffer *tm)
+{
+	const int x = cursor_px_column(tm);
+	const int y = cursor_px_row(tm);
+	const int w = tm->font->Width;
+	const int h = tm->font->Height;
+	gfx_rectangle_fill(tm->reg, x, y, w, h, tm->fg_color);
+}
+
+void txt_draw(struct txt_buffer *tm)
+{
+	const int w   = width_(tm);
+	const int h   = height_(tm);
+	int px_column = 0;
+	int px_row    = 0;
+
+	for (int r = 0; r < h; r++) {
+		px_column = 0;
+		for (int c = 0; c < w; c++) {
+			struct txt_glyph *g = getchar_(tm, c, r);
+			if (g == NULL)
+				continue;
+
+			gfx_putchar(
+				tm->font,
+				tm->reg,
+				px_column,
+				px_row,
+				g->ch,
+				g->fg_color,
+				g->bg_color
+			);
+			px_column += tm->font->Width;
+		}
+
+		px_row += tm->font->Height;
+	}
+
+	if (tm->draw_cursor)
+		draw_cursor_(tm);
+}
+
+void txt_set_color_f(
+	struct txt_buffer *tm, enum txt_color sw, float r, float g, float b
+) {
+	Color c = gfx_color_rgb_f(tm->reg, r, g, b);
+
+	switch (c) {
+	case TEXT_FOREGROUND:
+		tm->fg_color = c;
+		break;
+	case TEXT_BACKGROUND:
+		tm->bg_color = c;
+		break;
+	}
+}
+
+void txt_set_color(struct txt_buffer *tm, enum txt_color sw, int r, int g, int b)
+{
+	Color c = gfx_color_rgb(tm->reg, r, g, b);
+
+	switch (c) {
+	case TEXT_FOREGROUND:
+		tm->fg_color = c;
+		break;
+	case TEXT_BACKGROUND:
+		tm->bg_color = c;
+		break;
+	}
+}
+
+void txt_set_cursor(struct txt_buffer *tm, int x, int y, int draw_cursor)
+{
+	tm->draw_cursor = draw_cursor;
+
+	if (x < 0 || x >= width_(tm))
+		return;
+	if (y < 0 || y >= height_(tm))
+		return;
+
+	tm->cursor_column = x;
+	tm->cursor_row    = y;
+
+	if (tm->auto_update)
+		txt_update(tm);
+}
+
+void txt_set_transparent(struct txt_buffer *tm)
+{
+	tm->bg_color = tm->fg_color;
+}
+
+void txt_update(struct txt_buffer *tm)
+{
+	gfx_update(tm->reg);
+}
diff --git a/lib/gfx/textbuffer.h b/lib/gfx/textbuffer.h
new file mode 100644
index 0000000000000000000000000000000000000000..bec52bbeaad535dcb749de568236f91e200be929
--- /dev/null
+++ b/lib/gfx/textbuffer.h
@@ -0,0 +1,49 @@
+#ifndef TEXTBUFFER_H
+#define TEXTBUFFER_H
+#include "gfx.h"
+#include "fonts.h"
+#include <stdint.h>
+
+#define TEXTBUFFER_MAX_WIDTH 40
+#define TEXTBUFFER_MAX_HEIGHT 20
+
+struct txt_glyph {
+	char ch;
+	Color fg_color;
+	Color bg_color;
+};
+
+struct txt_buffer {
+	struct gfx_region *reg;
+	sFONT *font;
+	int cursor_column;
+	int cursor_row;
+	Color fg_color;
+	Color bg_color;
+	int draw_cursor;
+	int auto_update;
+
+	struct txt_glyph text[TEXTBUFFER_MAX_HEIGHT][TEXTBUFFER_MAX_WIDTH];
+};
+
+enum txt_color {
+	TEXT_FOREGROUND,
+	TEXT_BACKGROUND
+};
+
+void txt_init(struct txt_buffer *txtb, struct gfx_region *reg, sFONT *f);
+size_t txt_width(struct txt_buffer *tm);
+size_t txt_height(struct txt_buffer *tm);
+void txt_clear(struct txt_buffer *tm);
+void txt_putchar(struct txt_buffer *tm, char ch);
+void txt_puts(struct txt_buffer *tm, const char *str);
+void txt_draw(struct txt_buffer *tm);
+void txt_set_color_f(struct txt_buffer *tm, enum txt_color sw, float r, float g,
+								     float b);
+void txt_set_color(struct txt_buffer *tm, enum txt_color sw, int r, int g,
+								 int b);
+void txt_set_transparent(struct txt_buffer *tm);
+void txt_set_cursor(struct txt_buffer *tm, int x, int y, int draw_cursor);
+void txt_update(struct txt_buffer *tm);
+
+#endif