From d0b533daf9033ce7380e1bff88bc4f64de1f8d76 Mon Sep 17 00:00:00 2001 From: bartool Date: Sun, 19 Jun 2022 19:46:17 +0200 Subject: [PATCH] added gfx (empty test) working on gfx module added a few draw function gfx still wip added tamplate for test --- oled/gfx.c | 364 +++++++++++++++++++++++++++++++++++++++ oled/gfx.h | 39 +++++ test/oled/CMakeLists.txt | 11 +- test/oled/oled_test.c | 26 +++ 4 files changed, 435 insertions(+), 5 deletions(-) create mode 100644 oled/gfx.c create mode 100644 oled/gfx.h create mode 100644 test/oled/oled_test.c diff --git a/oled/gfx.c b/oled/gfx.c new file mode 100644 index 0000000..fd2528c --- /dev/null +++ b/oled/gfx.c @@ -0,0 +1,364 @@ +#include "gfx.h" + +/** + * @brief Set the pixel + * + * @param hOled Display object + * @param x X coordinate + * @param y Y coordinate + * @param color Color of pixel WHITE(0), BLACK(1) or INVERSE(2) + */ +void writePixel(OLED_HandleTypeDef *hOled, uint8_t x, uint8_t y, GFX_Color_t color) +{ + if (x > hOled->Width || y > hOled->Height) + return; + + switch (color) + { + case WHITE: + hOled->Buffer[(y / 8) * hOled->Width + x] |= (1 << (y % 8)); + break; + case BLACK: + hOled->Buffer[(y / 8) * hOled->Width + x] &= ~(1 << (y % 8)); + break; + case INVERSE: + hOled->Buffer[(y / 8) * hOled->Width + x] ^= (1 << (y % 8)); + break; + default: + break; + } +} + +typedef struct +{ + uint16_t bitmap_max_idx; + uint8_t buf_row_first; + uint8_t buf_row_last; + uint8_t buf_col_first; + uint8_t buf_col_last; + uint8_t buf_mask_top; + uint8_t buf_mask_bottom; + uint8_t bitmap_col; + uint8_t bitmap_row_first; + uint8_t bitmap_row_last; + uint8_t bitmap_shift; +} buf_bitmap_boundry_t; + +static void _getBoundry(OLED_HandleTypeDef *hOled, buf_bitmap_boundry_t *boundry, uint8_t bitmap_width, uint8_t bitmap_height, int8_t pos_x, int8_t pos_y) +{ + if (pos_x < 0) + { + boundry->bitmap_col = pos_x * -1; + boundry->buf_col_first = 0; + } + else + { + boundry->bitmap_col = 0; + boundry->buf_col_first = pos_x; + } + + if (pos_y < 0) + { + boundry->bitmap_shift = 8 + (pos_y % 8); + boundry->bitmap_row_first = (pos_y / 8) * (-1) + 1; + boundry->buf_row_first = 0; + boundry->buf_mask_top = 0; + } + else + { + boundry->bitmap_shift = pos_y % 8; + boundry->bitmap_row_first = 0; + boundry->buf_row_first = pos_y / 8; + boundry->buf_mask_top = 0xFF >> (8 - boundry->bitmap_shift); + } + boundry->buf_mask_bottom = 0xFF << ((pos_y + bitmap_height) % 8); + if (boundry->buf_mask_bottom == 0xFF) + { + boundry->buf_mask_bottom = 0; + } + + if ((bitmap_width + pos_x) > hOled->Width) + { + boundry->buf_col_last = hOled->Width; + } + else + { + boundry->buf_col_last = bitmap_width + pos_x; + } + + if (bitmap_height + pos_y > hOled->Height) + { + boundry->buf_row_last = hOled->Height / 8; + } + else + { + boundry->buf_row_last = (bitmap_height + pos_y + 7) / 8; + } + + boundry->bitmap_row_last = (pos_y + bitmap_height) / 8; + boundry->bitmap_max_idx = bitmap_width * ((bitmap_height + 7) / 8); +} + +static inline uint8_t _getBitmapByte(const uint8_t *bitmap, uint16_t index, GFX_Color_t color) +{ + switch (color) + { + case INVERSE: + return ~(bitmap[index]); + case WHITE: + return 0xFF; + case BLACK: + return 0x00; + default: + return bitmap[index]; + } +} +/** + * @brief A function that writes a bitmap into the buffer at the given position. + * 0,0 -------->x + * | + * | + * \ / + * y + * @param bitmap A pointer to bitmap array. + * @param bitmap_width Bitmap witdh in pixels. + * @param bitmap_height Bitmap height in pixels. + * @param pos_x Position x in the display + * @param pos_y Position y in the display + * @param color NORMAL (2) normal mode or INVERSE(3) mode for bitmap + * WHITE (0) or BLACK (1) for fill screen + */ +void writeBitmap(OLED_HandleTypeDef *hOled, const uint8_t *bitmap, uint8_t bitmap_width, uint8_t bitmap_height, int8_t pos_x, int8_t pos_y, GFX_Color_t color) +{ + if (bitmap_width + pos_x < 0 || bitmap_height + pos_y < 0) + return; + + uint16_t tmp_buf16, bitmap_idx, buf_idx; + uint8_t tmp_bitmap, bitmap_row; + + buf_bitmap_boundry_t b; + _getBoundry(hOled, &b, bitmap_width, bitmap_height, pos_x, pos_y); + + for (uint8_t col = b.buf_col_first; col < b.buf_col_last; col++, b.bitmap_col++) + { + tmp_buf16 = 0; + bitmap_row = b.bitmap_row_first; + + if (b.bitmap_row_first > 0) + { + tmp_buf16 = _getBitmapByte(bitmap, bitmap_width * (b.bitmap_row_first - 1) + b.bitmap_col, color) >> (8 - b.bitmap_shift); + } + else + { + tmp_buf16 = hOled->Buffer[b.buf_row_first * hOled->Width + col] & b.buf_mask_top; + } + + for (uint8_t buf_row = b.buf_row_first; buf_row < b.buf_row_last; buf_row++, bitmap_row++) + { + bitmap_idx = bitmap_width * bitmap_row + b.bitmap_col; + buf_idx = buf_row * hOled->Width + col; + + if (bitmap_idx < b.bitmap_max_idx) + { + tmp_bitmap = _getBitmapByte(bitmap, bitmap_idx, color); + tmp_buf16 |= tmp_bitmap << b.bitmap_shift; + } + + if (b.bitmap_row_last == buf_row) + { + hOled->Buffer[buf_idx] = (hOled->Buffer[buf_idx] & b.buf_mask_bottom) | (tmp_buf16 & ~(b.buf_mask_bottom)); + } + else + { + hOled->Buffer[buf_idx] = (uint8_t)tmp_buf16; + } + tmp_buf16 = tmp_buf16 >> 8; + } + } +} + +void writeSlashLine(OLED_HandleTypeDef *hOled, int16_t x0, int16_t y0, int16_t x1, int16_t y1, GFX_Color_t color) +{ + uint8_t steep = _diff(y1, y0) > _diff(x1, x0); // bool + if (steep) + { + _swap_int16_t(x0, y0); + _swap_int16_t(x1, y1); + } + + if (x0 > x1) + { + _swap_int16_t(x0, x1); + _swap_int16_t(y0, y1); + } + + int16_t dx = x1 - x0; + int16_t dy = _diff(y1, y0); + int16_t err = dx >> 1; + int16_t step = (y0 < y1) ? 1 : -1; + + for (; x0 <= x1; x0++) + { + if (steep) + { + writePixel(hOled, y0, x0, color); + } + else + { + writePixel(hOled, x0, y0, color); + } + err -= dy; + if (err < 0) + { + err += dx; + y0 += step; + } + } +} + +/**************************************************************************/ +/*! + @brief Write a perfectly vertical line + @param x Top-most x coordinate + @param y Top-most y coordinate + @param height Height in pixels + @param color Color of pixel WHITE(0), BLACK(1) or INVERSE(2) +*/ +/**************************************************************************/ +void writeVerticalLine(OLED_HandleTypeDef *hOled, int16_t x, int16_t y, int16_t height, GFX_Color_t color) +{ + for (int16_t i = y; i < y + height; i++) + { + writePixel(hOled, x, i, color); + } +} + +/**************************************************************************/ +/*! + @brief Write a perfectly horizontal line + @param x Left-most x coordinate + @param y Left-most y coordinate + @param width Width in pixels + @param color Color of pixel WHITE(0), BLACK(1) or INVERSE(2) +*/ +/**************************************************************************/ +void writeHorizontalLine(OLED_HandleTypeDef *hOled, int16_t x, int16_t y, int16_t width, GFX_Color_t color) +{ + for (int16_t i = x; i < x + width; i++) + { + writePixel(hOled, i, y, color); + } +} + +void writeRect(OLED_HandleTypeDef *hOled, int16_t x, int16_t y, int16_t width, int16_t height, GFX_Color_t color) +{ + writeHorizontalLine(hOled, x, y, width, color); + writeHorizontalLine(hOled, x, y + height - 1, width, color); + writeVerticalLine(hOled, x, y, height, color); + writeVerticalLine(hOled, x + width - 1, y, height, color); +} + +void writeFillRect(OLED_HandleTypeDef *hOled, int16_t x, int16_t y, int16_t width, int16_t height, GFX_Color_t color) +{ + writeBitmap(hOled, NULL, width, height, x, y, color); +} + +/**************************************************************************/ +/*! + @brief Draw a circle outline + @param x0 Center-point x coordinate + @param y0 Center-point y coordinate + @param radius Radius of circle + @param color 16-bit 5-6-5 Color to draw with +*/ +/**************************************************************************/ +void writeCircle(OLED_HandleTypeDef *hOled, int16_t x0, int16_t y0, uint8_t radius, GFX_Color_t color) +{ + + int16_t f = 1 - radius; + int16_t ddF_x = 1; + int16_t ddF_y = -2 * radius; + int16_t x = 0; + int16_t y = radius; + + writePixel(hOled, x0, y0 + radius, color); + writePixel(hOled, x0, y0 - radius, color); + writePixel(hOled, x0 + radius, y0, color); + writePixel(hOled, x0 - radius, y0, color); + + while (x < y) + { + if (f >= 0) + { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + + writePixel(hOled, x0 + x, y0 + y, color); + writePixel(hOled, x0 - x, y0 + y, color); + writePixel(hOled, x0 + x, y0 - y, color); + writePixel(hOled, x0 - x, y0 - y, color); + writePixel(hOled, x0 + y, y0 + x, color); + writePixel(hOled, x0 - y, y0 + x, color); + writePixel(hOled, x0 + y, y0 - x, color); + writePixel(hOled, x0 - y, y0 - x, color); + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Quarter-circle drawer, used to do circles and roundrects + @param x0 Center-point x coordinate + @param y0 Center-point y coordinate + @param radius Radius of circle + @param corner Mask bit #1 or bit #2 to indicate which quarters of + the circle we're doing + @param color 16-bit 5-6-5 Color to draw with +*/ +/**************************************************************************/ +void writeQuarterCircle(OLED_HandleTypeDef *hOled, int16_t x0, int16_t y0, uint8_t radius, CIRC_Corners_t corner, GFX_Color_t color) +{ + int16_t f = 1 - radius; + int16_t ddF_x = 1; + int16_t ddF_y = -2 * radius; + int16_t x = 0; + int16_t y = radius; + + while (x < y) + { + if (f >= 0) + { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + if (corner & BOTTOM_LEFT) + { + writePixel(hOled, x0 + x, y0 + y, color); + writePixel(hOled, x0 + y, y0 + x, color); + } + if (corner & BOTTOM_RIGHT) + { + writePixel(hOled, x0 + x, y0 - y, color); + writePixel(hOled, x0 + y, y0 - x, color); + } + if (corner & TOP_LEFT) + { + writePixel(hOled, x0 - y, y0 + x, color); + writePixel(hOled, x0 - x, y0 + y, color); + } + if (corner & TOP_RIGHT) + { + writePixel(hOled, x0 - y, y0 - x, color); + writePixel(hOled, x0 - x, y0 - y, color); + } + } +} \ No newline at end of file diff --git a/oled/gfx.h b/oled/gfx.h new file mode 100644 index 0000000..35dc3c0 --- /dev/null +++ b/oled/gfx.h @@ -0,0 +1,39 @@ +#include "oled.h" + +#ifndef _swap_int16_t +#define _swap_int16_t(a, b) \ + { \ + int16_t t = a; \ + a = b; \ + b = t; \ + } +#endif + +#ifndef _diff +#define _diff(a, b) ((a > b) ? (a - b) : (b - a)) +#endif + +typedef enum +{ + WHITE, + BLACK, + NORMAL, + INVERSE +} GFX_Color_t; + +typedef enum +{ + TOP_RIGHT = 1, + BOTTOM_RIGHT = 2, + BOTTOM_LEFT = 4, + TOP_LEFT = 8 +} CIRC_Corners_t; + +void writePixel(OLED_HandleTypeDef *hOled, uint8_t x, uint8_t y, GFX_Color_t color); +void writeBitmap(OLED_HandleTypeDef *hOled, const uint8_t *bitmap, uint8_t bitmap_width, uint8_t bitmap_height, int8_t pos_x, int8_t pos_y, GFX_Color_t color); +void writeSlashLine(OLED_HandleTypeDef *hOled, int16_t x0, int16_t y0, int16_t x1, int16_t y1, GFX_Color_t color); +void writeVerticalLine(OLED_HandleTypeDef *hOled, int16_t x, int16_t y, int16_t height, GFX_Color_t color); +void writeHorizontalLine(OLED_HandleTypeDef *hOled, int16_t x, int16_t y, int16_t width, GFX_Color_t color); +void writeRect(OLED_HandleTypeDef *hOled, int16_t x, int16_t y, int16_t width, int16_t height, GFX_Color_t color); +void writeCircle(OLED_HandleTypeDef *hOled, int16_t x0, int16_t y0, uint8_t radius, GFX_Color_t color); +void writeQuarterCircle(OLED_HandleTypeDef *hOled, int16_t x0, int16_t y0, uint8_t radius, CIRC_Corners_t corner, GFX_Color_t color); \ No newline at end of file diff --git a/test/oled/CMakeLists.txt b/test/oled/CMakeLists.txt index 368c2d2..3484c33 100644 --- a/test/oled/CMakeLists.txt +++ b/test/oled/CMakeLists.txt @@ -10,12 +10,13 @@ set(INCLUDE_DIRS ) set(SRCS - ../../oled/oled.c - ../../oled/connection.c - ../../oled/ssd1306/ssd1306.c - ../../oled/sh1106/sh1106.c + # ../../oled/oled.c + # ../../oled/connection.c + # ../../oled/ssd1306/ssd1306.c + # ../../oled/sh1106/sh1106.c - helpers/src/mock_i2c.c + # helpers/src/mock_i2c.c + oled_test.c ) add_definitions(-DTEST) diff --git a/test/oled/oled_test.c b/test/oled/oled_test.c new file mode 100644 index 0000000..4ea14ca --- /dev/null +++ b/test/oled/oled_test.c @@ -0,0 +1,26 @@ +#include "unity.h" + + + +void setUp(void) +{ +} + +void tearDown(void) +{ +} + +void test_one(void) +{ + TEST_FAIL(); +} + + + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_one); + + return UNITY_END(); +} \ No newline at end of file