diff --git a/Examples/Thermometr/CMakeLists.txt b/Examples/Thermometr/CMakeLists.txt index 1930d47..948632e 100644 --- a/Examples/Thermometr/CMakeLists.txt +++ b/Examples/Thermometr/CMakeLists.txt @@ -11,6 +11,7 @@ pico_sdk_init() add_executable(${PROJECT_NAME} src/main.c ../../src/core/display.c + ../../src/render/renderer.c ) target_include_directories(${PROJECT_NAME} PRIVATE diff --git a/Examples/Thermometr/src/main.c b/Examples/Thermometr/src/main.c index 27c8dd6..32f387d 100644 --- a/Examples/Thermometr/src/main.c +++ b/Examples/Thermometr/src/main.c @@ -1,34 +1,19 @@ #include "pico/stdlib.h" #include "display/display.h" +#include "display/renderer.h" -#define WIDTH 240 +#define WIDTH 320 #define HEIGHT 240 static void on_frame_done(void) { - // В SAFE режиме swap выполняется внутри submit() + // В режиме SAFE смена буферов выполняется внутри submit() display_submit(); } -static void render_test_pattern(uint16_t* buf) -{ - for (uint16_t y = 0; y < HEIGHT; y++) - { - for (uint16_t x = 0; x < WIDTH; x++) - { - uint16_t r = (x & 0x1F) << 11; - uint16_t g = (y & 0x3F) << 5; - uint16_t b = (x & 0x1F); - - buf[y * WIDTH + x] = r | g | b; - } - } -} - - int main() { stdio_init_all(); @@ -43,19 +28,27 @@ int main() display_init(&cfg); - // Рисуем первый кадр - uint16_t* buf = display_get_draw_buffer(); - render_test_pattern(buf); + display_submit(); /* Запускаем конвейер кадра */ - display_submit(); + float phase = 0.0f; + render_ctx_t rc; while (1) { display_poll(); - // Здесь можно обновлять содержимое буфера - // SAFE + 1 buffer будет ждать окончания DMA - buf = display_get_draw_buffer(); - render_test_pattern(buf); + /* SAFE + 1 буфер: ожидание освобождения внутри display_get_draw_buffer() */ + uint16_t* buf = display_get_draw_buffer(); + render_begin(&rc, buf, WIDTH, HEIGHT); + + render_clear(&rc, 0x10A2); + render_grid(&rc, 20, 20, 40, 0x5ACB); + render_sine_wave(&rc, WIDTH, 50, 2.0f, 0, HEIGHT / 2, phase, 0xF800); + + phase += 0.08f; + if (phase > 6.2831853f) + { + phase -= 6.2831853f; + } } -} \ No newline at end of file +} diff --git a/include/display/renderer.h b/include/display/renderer.h new file mode 100644 index 0000000..f441308 --- /dev/null +++ b/include/display/renderer.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + uint16_t* buf; + uint16_t width; + uint16_t height; + uint16_t clip_x0; + uint16_t clip_y0; + uint16_t clip_x1; + uint16_t clip_y1; +} render_ctx_t; + +void render_begin(render_ctx_t* ctx, uint16_t* buf, uint16_t width, uint16_t height); +void render_set_clip(render_ctx_t* ctx, uint16_t x, uint16_t y, uint16_t width, uint16_t height); +void render_reset_clip(render_ctx_t* ctx); + +void render_clear(render_ctx_t* ctx, uint16_t color); +void render_pixel(render_ctx_t* ctx, int x, int y, uint16_t color); +void render_line(render_ctx_t* ctx, int x0, int y0, int x1, int y1, uint16_t color); +void render_grid(render_ctx_t* ctx, uint16_t x, uint16_t y, uint16_t step, uint16_t color); + +void render_sine_wave( + render_ctx_t* ctx, + uint16_t num_points, + int amplitude, + float frequency, + int offset_x, + int offset_y, + float phase_shift, + uint16_t color +); + +void render_bezier( + render_ctx_t* ctx, + const int* points_x, + const int* points_y, + size_t num_points, + uint16_t color +); + +#ifdef __cplusplus +} +#endif + diff --git a/src/render/renderer.c b/src/render/renderer.c new file mode 100644 index 0000000..28545c6 --- /dev/null +++ b/src/render/renderer.c @@ -0,0 +1,199 @@ +#include "display/renderer.h" +#include +#include + +static inline bool in_clip(const render_ctx_t* ctx, int x, int y) +{ + return x >= (int)ctx->clip_x0 && + x <= (int)ctx->clip_x1 && + y >= (int)ctx->clip_y0 && + y <= (int)ctx->clip_y1; +} + +void render_begin(render_ctx_t* ctx, uint16_t* buf, uint16_t width, uint16_t height) +{ + ctx->buf = buf; + ctx->width = width; + ctx->height = height; + render_reset_clip(ctx); +} + +void render_set_clip(render_ctx_t* ctx, uint16_t x, uint16_t y, uint16_t width, uint16_t height) +{ + if (ctx->width == 0 || ctx->height == 0 || width == 0 || height == 0) + { + ctx->clip_x0 = 0; + ctx->clip_y0 = 0; + ctx->clip_x1 = 0; + ctx->clip_y1 = 0; + return; + } + + uint32_t x1 = (uint32_t)x + (uint32_t)width - 1u; + uint32_t y1 = (uint32_t)y + (uint32_t)height - 1u; + + if (x >= ctx->width || y >= ctx->height) + { + ctx->clip_x0 = 0; + ctx->clip_y0 = 0; + ctx->clip_x1 = 0; + ctx->clip_y1 = 0; + return; + } + + if (x1 >= ctx->width) + x1 = (uint32_t)ctx->width - 1u; + if (y1 >= ctx->height) + y1 = (uint32_t)ctx->height - 1u; + + ctx->clip_x0 = x; + ctx->clip_y0 = y; + ctx->clip_x1 = (uint16_t)x1; + ctx->clip_y1 = (uint16_t)y1; +} + +void render_reset_clip(render_ctx_t* ctx) +{ + if (ctx->width == 0 || ctx->height == 0) + { + ctx->clip_x0 = 0; + ctx->clip_y0 = 0; + ctx->clip_x1 = 0; + ctx->clip_y1 = 0; + return; + } + + ctx->clip_x0 = 0; + ctx->clip_y0 = 0; + ctx->clip_x1 = (uint16_t)(ctx->width - 1u); + ctx->clip_y1 = (uint16_t)(ctx->height - 1u); +} + +void render_clear(render_ctx_t* ctx, uint16_t color) +{ + size_t total = (size_t)ctx->width * (size_t)ctx->height; + for (size_t i = 0; i < total; ++i) + { + ctx->buf[i] = color; + } +} + +void render_pixel(render_ctx_t* ctx, int x, int y, uint16_t color) +{ + if (x < 0 || y < 0 || x >= (int)ctx->width || y >= (int)ctx->height) + return; + + if (!in_clip(ctx, x, y)) + return; + + ctx->buf[(size_t)y * ctx->width + (size_t)x] = color; +} + +void render_line(render_ctx_t* ctx, int x0, int y0, int x1, int y1, uint16_t color) +{ + int dx = (x1 >= x0) ? (x1 - x0) : (x0 - x1); + int sx = (x0 < x1) ? 1 : -1; + int dy = (y1 >= y0) ? (y0 - y1) : (y1 - y0); + int sy = (y0 < y1) ? 1 : -1; + int err = dx + dy; + + while (true) + { + render_pixel(ctx, x0, y0, color); + if (x0 == x1 && y0 == y1) + break; + + int e2 = err * 2; + if (e2 >= dy) + { + err += dy; + x0 += sx; + } + if (e2 <= dx) + { + err += dx; + y0 += sy; + } + } +} + +void render_grid(render_ctx_t* ctx, uint16_t x, uint16_t y, uint16_t step, uint16_t color) +{ + if (step == 0 || ctx->width == 0 || ctx->height == 0) + return; + + for (uint16_t v = x; v < ctx->width; v = (uint16_t)(v + step)) + { + render_line(ctx, v, 0, v, (int)ctx->height - 1, color); + } + + for (uint16_t h = y; h < ctx->height; h = (uint16_t)(h + step)) + { + render_line(ctx, 0, h, (int)ctx->width - 1, h, color); + } +} + +void render_sine_wave( + render_ctx_t* ctx, + uint16_t num_points, + int amplitude, + float frequency, + int offset_x, + int offset_y, + float phase_shift, + uint16_t color +) +{ + if (num_points < 2 || ctx->width == 0 || ctx->height == 0) + return; + + float step = (2.0f * (float)M_PI * frequency) / (float)(num_points - 1u); + float x_step = (float)ctx->width / (float)(num_points - 1u); + + for (uint16_t i = 0; i < num_points; ++i) + { + int x = offset_x + (int)((float)i * x_step); + int y = offset_y + (int)((float)amplitude * sinf((float)i * step + phase_shift)); + render_pixel(ctx, x, y, color); + } +} + +static float bernstein(int i, int n, float t) +{ + float binomial = 1.0f; + for (int j = 0; j < i; ++j) + { + binomial *= (float)(n - j) / (float)(j + 1); + } + return binomial * powf(t, (float)i) * powf(1.0f - t, (float)(n - i)); +} + +void render_bezier( + render_ctx_t* ctx, + const int* points_x, + const int* points_y, + size_t num_points, + uint16_t color +) +{ + if (num_points < 2) + return; + + const int steps = 1000; + for (int s = 0; s <= steps; ++s) + { + float t = (float)s / (float)steps; + float x = 0.0f; + float y = 0.0f; + + for (size_t i = 0; i < num_points; ++i) + { + float b = bernstein((int)i, (int)num_points - 1, t); + x += b * (float)points_x[i]; + y += b * (float)points_y[i]; + } + + render_pixel(ctx, (int)(x + 0.5f), (int)(y + 0.5f), color); + } +} +