/*************************************************************************** * Copyright (C) 2020 by Federico Izzo IU2NUO, * * Niccolò Izzo IU2KIN, * * Silvano Seva IU2KWO * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see * ***************************************************************************/ /** * This source file provides an RGB implementation for the graphics.h interface * It is suitable for color displays, it will have grayscale and B/W counterparts */ #include #include #include #include "display.h" #include "graphics.h" #include "font_OpenGD77.h" /* This graphics driver is meant for an RGB565 little endian pixel format. * Thus, to accomodate for the endianness, the fields in struct rgb565_t have to * be written in reversed order. * * For more details about endianness and bitfield structs see the following web * page: http://mjfrazer.org/mjfrazer/bitfields/ */ typedef struct { uint16_t b : 5; uint16_t g : 6; uint16_t r : 5; } rgb565_t; bool initialized = 0; rgb565_t *buf; void gfx_init() { display_init(); buf = (rgb565_t *)(display_getFrameBuffer()); initialized = 1; } void gfx_terminate() { display_terminate(); initialized = 0; } void gfx_renderRows(uint8_t startRow, uint8_t endRow) { display_renderRows(startRow, endRow); } void gfx_render() { display_render(); } bool gfx_renderingInProgress() { return display_renderingInProgress(); } rgb565_t _true2highColor(color_t true_color) { rgb565_t high_color; high_color.r = true_color.r >> 3; high_color.g = true_color.g >> 2; high_color.b = true_color.b >> 3; return high_color; } void gfx_clearScreen() { if(!initialized) return; // Set the whole framebuffer to 0x00 = make the screen black memset(buf, 0x00, sizeof(rgb565_t) * SCREEN_WIDTH * SCREEN_HEIGHT); } void gfx_fillScreen(color_t color) { if(!initialized) return; rgb565_t color_565 = _true2highColor(color); for(int y = 0; y < SCREEN_HEIGHT; y++) { for(int x = 0; x < SCREEN_WIDTH; x++) { buf[x + y*SCREEN_WIDTH] = color_565; } } } void gfx_setPixel(point_t pos, color_t color) { if (pos.x >= SCREEN_WIDTH || pos.y >= SCREEN_HEIGHT) return; // off the screen buf[pos.x + pos.y*SCREEN_WIDTH] = _true2highColor(color); } void gfx_drawLine(point_t start, point_t end, color_t color) { if(!initialized) return; rgb565_t color_565 = _true2highColor(color); for(int y = start.y; y < end.y; y++) { for(int x = start.x; x < end.x; x++) { buf[x + y*SCREEN_WIDTH] = color_565; } } } void gfx_drawRect(point_t start, uint16_t width, uint16_t height, color_t color, bool fill) { if(!initialized) return; rgb565_t color_565 = _true2highColor(color); uint16_t x_max = start.x + width; uint16_t y_max = start.y + height; bool perimeter = 0; if(x_max > (SCREEN_WIDTH - 1)) x_max = SCREEN_WIDTH - 1; if(y_max > (SCREEN_HEIGHT - 1)) y_max = SCREEN_HEIGHT - 1; for(int y = start.y; y < y_max; y++) { for(int x = start.x; x < x_max; x++) { if(y == start.y || y == y_max-1 || x == start.x || x == x_max-1) perimeter = 1; else perimeter = 0; // If fill is false, draw only rectangle perimeter if(fill || perimeter) buf[x + y*SCREEN_WIDTH] = color_565; } } } void gfx_print(point_t start, const char *text, fontSize_t size, textAlign_t alignment, color_t color) { uint8_t *currentCharData; uint8_t *currentFont; uint16_t *writePos; uint8_t *readPos; rgb565_t color_565 = _true2highColor(color); switch(size) { case FONT_SIZE_1: currentFont = (uint8_t *) font_6x8; break; case FONT_SIZE_1_BOLD: currentFont = (uint8_t *) font_6x8_bold; break; case FONT_SIZE_2: currentFont = (uint8_t *) font_8x8; break; case FONT_SIZE_3: currentFont = (uint8_t *) font_8x16; break; case FONT_SIZE_4: currentFont = (uint8_t *) font_16x32; break; default: return;// Invalid font selected break; } int16_t startCode = currentFont[2]; // get first defined character int16_t endCode = currentFont[3]; // get last defined character int16_t charWidthPixels = currentFont[4]; // width in pixel of one char int16_t charHeightPixels = currentFont[5]; // page count per char int16_t bytesPerChar = currentFont[7]; // bytes per char int16_t sLen = strlen(text); // Compute amount of letters that fit till the end of the screen if ((charWidthPixels*sLen) + start.x > SCREEN_WIDTH) { sLen = (SCREEN_WIDTH-start.x)/charWidthPixels; } if (sLen < 0) return; switch(alignment) { case TEXT_ALIGN_LEFT: // left aligned, do nothing. break; case TEXT_ALIGN_CENTER: start.x = (SCREEN_WIDTH - (charWidthPixels * sLen))/2; break; case TEXT_ALIGN_RIGHT: start.x = SCREEN_WIDTH - (charWidthPixels * sLen); break; } for (int16_t i=0; i endCode) { charOffset = ('?' - startCode); // Substitute unsupported ASCII code by a question mark } currentCharData = (uint8_t *)¤tFont[8 + (charOffset * bytesPerChar)]; // We print the character from up-left to bottom right for(int16_t vscan=0; vscan < charHeightPixels; vscan++) { for(int16_t hscan=0; hscan < charWidthPixels; hscan++) { int16_t charChunk = vscan / 8; int16_t bitIndex = (hscan + charChunk * charWidthPixels) * 8 + vscan % 8; int16_t byte = bitIndex >> 3; int16_t bitMask = 1 << (bitIndex & 7); if (currentCharData[byte] & bitMask) buf[(start.y + vscan) * SCREEN_WIDTH + start.x + hscan + i * charWidthPixels] = color_565; } } } }