//游戏引擎图像绘制库 #include "stdlib.h" #include "string.h" #include "spi.h" #include "lcd.h" #include "GameEngine.h" #include "GE_Draw.h" /**************************************** 显存 ****************************************/ uint8_t GE_Draw_VRam[153600]; /*****************************************************************************************/ _ge_draw_pic_set ge_draw_pic_set; _ge_draw_mono_set ge_draw_mono_set; /** * @brief 初始化 GE_Draw */ void GE_Draw_Init(void) { //绘制图片设置 ge_draw_pic_set.is_reverse = FALSE; ge_draw_pic_set.pos_mode = UP_LEFT; //绘制单色图设置 ge_draw_mono_set.draw_mode = MONO_OR; ge_draw_mono_set.pos_mode = UP_LEFT; ge_draw_mono_set.mono_color = BLACK; ge_draw_mono_set.back_color = WHITE; GE_Draw_ClrAll(WHITE); } /** * @brief 刷新屏幕 */ void GE_Draw_Disp(void) { LCD_SetWin(0, 0, LCD_WIDTH, LCD_HEIGHT); LCD_SendCmdDataBytes(LCD_CMD_RAMWR, GE_Draw_VRam, 153600); } /** * @brief 以指定颜色清屏 * @param color: 颜色 */ void GE_Draw_ClrAll(uint16_t color) { GE_Draw_Fill(0, 0, LCD_WIDTH, LCD_HEIGHT, color); } /** * @brief 以指定颜色填充矩形区域。填充速度较慢 * @param x: 0~319 * @param y: 0~239 * @param width: 区域的宽 * @param height: 区域的高 * @param color: 颜色 */ void GE_Draw_Fill(int16_t x, int16_t y, uint16_t width, uint16_t height, uint16_t color) { uint32_t point_num = width * height; int16_t x0 = x, y0 = y; for (uint32_t i = 0; i < point_num; i++) { GE_Draw_Point(x++, y, color); if (x == x0 + width) { y++; x = x0; } } } /** * @brief 在指定坐标绘指定颜色的点 * @param x: 0~319 * @param y: 0~239 * @param color: 颜色 */ void GE_Draw_Point(int16_t x, int16_t y, uint16_t color) { if (x < 0 || x > LCD_WIDTH - 1 || y < 0 || y > LCD_HEIGHT - 1) return; GE_Draw_VRam[(x + y * 320) * 2] = color >> 8; GE_Draw_VRam[(x + y * 320) * 2 + 1] = color; } /** * @brief 获取指定坐标的点的颜色。坐标不合法返回白色 * @param x: 0~319 * @param y: 0~239 */ uint16_t GE_Draw_GetPoint(int16_t x, int16_t y) { if (x < 0 || x > LCD_WIDTH - 1 || y < 0 || y > LCD_HEIGHT - 1) return WHITE; return ((uint16_t)GE_Draw_VRam[(x + y * 320) * 2] << 8) | GE_Draw_VRam[(x + y * 320) * 2 + 1]; } /** * @brief 用线段连接指定两坐标 * @param x1: 0~319 * @param y1: 0~239 * @param x2: 0~319 * @param y3: 0~239 * @param color: 颜色 */ void GE_Draw_Line(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color) { //使用 Bresenham 算法 int16_t dx = abs(x1 - x0); int8_t sx = x0 < x1 ? 1 : -1; int16_t dy = -abs(y1 - y0); int8_t sy = y0 < y1 ? 1 : -1; int16_t err = dx + dy, e2; while (1) { GE_Draw_Point(x0, y0, color); if (x0 == x1 && y0 == y1) break; e2 = 2 * err; if (e2 >= dy) { err += dy; x0 += sx; } if (e2 <= dx) { err += dx; y0 += sy; } } } /** * @brief 以指定颜色绘制矩形边框 * @param x: 0~319 * @param y: 0~239 * @param width: 矩形的宽 * @param height: 矩形的高 * @param color: 颜色 */ void GE_Draw_Rectangle(int16_t x, int16_t y, uint16_t width, uint16_t height, uint16_t color) { uint16_t i; for (i = 0; i < width; i++) { GE_Draw_Point(x + i, y, color); GE_Draw_Point(x + i, y + height - 1, color); } for (i = 0; i < height; i++) { GE_Draw_Point(x, y + i, color); GE_Draw_Point(x + width - 1, y + i, color); } } /** * @brief 以指定颜色绘制圆 * @param xm: 0~319 * @param ym: 0~239 * @param r: 圆的半径 * @param color: 颜色 */ void GE_Draw_Circle(int16_t xm, int16_t ym, uint16_t r, uint16_t color) { int16_t x = -r, y = 0, err = 2 - 2 * r, rm = r; do { GE_Draw_Point(xm - x, ym + y, color); GE_Draw_Point(xm - y, ym - x, color); GE_Draw_Point(xm + x, ym - y, color); GE_Draw_Point(xm + y, ym + x, color); rm = err; if (rm > x) err += ++x * 2 + 1; if (rm <= y) err += ++y * 2 + 1; } while (x < 0); } /** * @brief 以指定颜色填充圆 * @param xm: 0~319 * @param ym: 0~239 * @param r: 圆的半径 * @param color: 颜色 */ void GE_Draw_FillCircle(int16_t xm, int16_t ym, uint16_t r, uint16_t color) { int16_t x = -r, y = 0, err = 2 - 2 * r, rm = r; do { GE_Draw_Line(xm - x, ym + y, xm + x, ym + y, color); GE_Draw_Line(xm - y, ym - x, xm + y, ym - x, color); GE_Draw_Line(xm + x, ym - y, xm - x, ym - y, color); GE_Draw_Line(xm + y, ym + x, xm - y, ym + x, color); rm = err; if (rm > x) err += ++x * 2 + 1; if (rm <= y) err += ++y * 2 + 1; } while (x < 0); } /** * @brief 绘制矩形图片。图片为 const unsigned char 数组,4096 色,不含图像头。 * 请确保图片完全在屏幕区域内 * @param x: 0~319 * @param y: 0~239 * @param is_reverse: TRUE 为反色。反色模式下刷新较慢 * @param pos_mode: UP_LEFT 为从左上角开始显示,MID 为以坐标为中心显示 * @param pic: 指向图片 * @param width: 图片的宽 * @param height: 图片的高 */ void GE_Draw_Pic( int16_t x, int16_t y, uint8_t is_reverse, uint8_t pos_mode, const unsigned char *pic, uint16_t width, uint16_t height) { if (pos_mode == MID) { x = x - width / 2; y = y - height / 2; } if (x == 0 && y == 0 && width == LCD_WIDTH && height == LCD_HEIGHT) memcpy(GE_Draw_VRam, pic, 153600); uint32_t point_num = width * height; int16_t x0 = x, y0 = y; uint16_t color; if (is_reverse == TRUE) //反色模式 { for (uint32_t i = 0; i < point_num; i++) { color = 0xffff - (((uint16_t)pic[2 * i] << 8) | pic[2 * i + 1]); GE_Draw_Point(x++, y, color); if (x == x0 + width) { y++; x = x0; } } } else //非反色模式 { for (uint32_t i = 0; i < point_num; i++) { color = ((uint16_t)pic[2 * i] << 8) | pic[2 * i + 1]; GE_Draw_Point(x++, y, color); if (x == x0 + width) { y++; x = x0; } } } } /** * @brief 绘制矩形图片,使用设置 * @param x: 0~319 * @param y: 0~239 * @param pic: 指向图片 * @param width: 图片的宽 * @param height: 图片的高 */ void GE_Draw_Pic_WithSet(int16_t x, int16_t y, const unsigned char *pic, uint16_t width, uint16_t height) { GE_Draw_Pic( x, y, ge_draw_pic_set.is_reverse, ge_draw_pic_set.pos_mode, pic, width, height); } /** * @brief 绘制单色位图。图片为 const unsigned char 数组,不含图像头 * 请确保图片完全在屏幕区域内 * @param x: 0~319 * @param y: 0~239 * @param draw_mode: 绘制方式。MONO_OR MONO_AND MONO_XOR MONO_COVER * @param pos_mode: UP_LEFT 为从左上角开始显示,MID 为以坐标为中心显示 * @param mono_color: 颜色 * @param back_color: 背景色。只在 MONO_COVER 时有效 * @param pic: 指向图片 * @param width: 图片的宽 * @param height: 图片的高 */ void GE_Draw_Mono( int16_t x, int16_t y, uint8_t draw_mode, uint8_t pos_mode, uint16_t mono_color, uint16_t back_color, const unsigned char *pic, uint16_t width, uint16_t height) { if (pos_mode == MID) { x = x - width / 2; y = y - height / 2; } uint8_t temp8; int16_t x0 = x, y0 = y; //循环横向输出 for (uint16_t i = 0; y - y0 < height; i++) { temp8 = pic[i]; for (uint16_t j = 0; j < 8; j++) { if (temp8 & 0x80) { if ( (draw_mode == MONO_OR) || (draw_mode == MONO_AND && GE_Draw_GetPoint(x, y) == mono_color) || (draw_mode == MONO_XOR && GE_Draw_GetPoint(x, y) != mono_color) || (draw_mode == MONO_COVER)) { GE_Draw_Point(x, y, mono_color); } } else { if ( (draw_mode == MONO_AND && GE_Draw_GetPoint(x, y) != mono_color) || (draw_mode == MONO_XOR && GE_Draw_GetPoint(x, y) == mono_color) || (draw_mode == MONO_COVER)) { GE_Draw_Point(x, y, back_color); } } temp8 <<= 1; x++; if (x - x0 == width) { x = x0; y++; break; } } } } /** * @brief 绘制单色位图,使用设置。图片为 const unsigned char 数组,不含图像头 * 请确保图片完全在屏幕区域内 * @param x: 0~319 * @param y: 0~239 * @param pic: 指向图片 * @param width: 图片的宽 * @param height: 图片的高 */ void GE_Draw_Mono_WithSet( int16_t x, int16_t y, const unsigned char *pic, uint16_t width, uint16_t height) { GE_Draw_Mono( x, y, ge_draw_mono_set.draw_mode, ge_draw_mono_set.pos_mode, ge_draw_mono_set.mono_color, ge_draw_mono_set.back_color, pic, width, height); }