diff options
Diffstat (limited to 'Peak/src/peak.c')
| -rw-r--r-- | Peak/src/peak.c | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/Peak/src/peak.c b/Peak/src/peak.c new file mode 100644 index 0000000..5e09070 --- /dev/null +++ b/Peak/src/peak.c @@ -0,0 +1,254 @@ +static inline void peak__put_pixel(int x, int y, uint32_t color); +static inline uint32_t peak__lerp_color(uint32_t c0, uint32_t c1, float t); +static inline float peak__edge_cross(float ax, float ay, float bx, float by, float cx, float cy); + +static uint32_t *peak_window_buffer; +static size_t peak_window_width; +static size_t peak_window_height; +static int peak_clip_x; +static int peak_clip_y; +static size_t peak_clip_width; +static size_t peak_clip_height; + +void +peak__put_pixel(int x, int y, uint32_t color) +{ + if (x < peak_clip_x || x >= (int)(peak_clip_x + peak_clip_width) || + y < peak_clip_y || y >= (int)(peak_clip_y + peak_clip_height)) { + return; + } + if (x < 0 || x >= (int)peak_window_width || y < 0 || y >= (int)peak_window_height) { + return; + } + peak_window_buffer[y * peak_window_width + x] = color; +} + +uint32_t +peak__lerp_color(uint32_t c0, uint32_t c1, float t) +{ + if (t <= 0.0f) return c0; + if (t >= 1.0f) return c1; + + uint32_t a0 = (c0 >> 24) & 0xFF, a1 = (c1 >> 24) & 0xFF; + uint32_t r0 = (c0 >> 16) & 0xFF, r1 = (c1 >> 16) & 0xFF; + uint32_t g0 = (c0 >> 8) & 0xFF, g1 = (c1 >> 8) & 0xFF; + uint32_t b0 = c0 & 0xFF, b1 = c1 & 0xFF; + + uint32_t a = (uint32_t)(a0 + t * ((float)a1 - (float)a0)); + uint32_t r = (uint32_t)(r0 + t * ((float)r1 - (float)r0)); + uint32_t g = (uint32_t)(g0 + t * ((float)g1 - (float)g0)); + uint32_t b = (uint32_t)(b0 + t * ((float)b1 - (float)b0)); + + return (a << 24) | (r << 16) | (g << 8) | b; +} + +float +peak__edge_cross(float ax, float ay, float bx, float by, float cx, float cy) +{ + return (cx - ax) * (by - ay) - (cy - ay) * (bx - ax); +} + +void +peak_init(void) +{ + peak_platform_window_open(); + peak_window_buffer = peak_platform_window_buffer(&peak_window_width, &peak_window_height); + peak_clip_x = 0; + peak_clip_y = 0; + peak_clip_width = peak_window_width; + peak_clip_height = peak_window_height; +} + +void +peak_quit(void) +{ + peak_platform_window_close(); +} + +void +peak_extensions(void) +{ + // TODO +} + +bool +peak_poll_events(PeakEvent *ev) +{ + return peak_platform_epoll(ev); +} + +void +peak_blit(int offset_x, int offset_y, const uint32_t *rgba, size_t width, size_t height) +{ + for (size_t y = 0; y < height; ++y) { + int target_y = offset_y + (int)y; + if (target_y < peak_clip_y || target_y >= (int)(peak_clip_y + peak_clip_height) || + target_y < 0 || target_y >= (int)peak_window_height) continue; + + int start_x = offset_x < peak_clip_x ? peak_clip_x : offset_x; + int end_x = (offset_x + (int)width) > (int)(peak_clip_x + peak_clip_width) + ? (int)(peak_clip_x + peak_clip_width) + : (offset_x + (int)width); + + if (start_x >= end_x) continue; + + size_t src_offset_x = (size_t)(start_x - offset_x); + size_t copy_pixels = (size_t)(end_x - start_x); + + memcpy(&peak_window_buffer[target_y * peak_window_width + start_x], &rgba[y * width + src_offset_x], copy_pixels * sizeof(uint32_t)); + } +} + +void +peak_clip(int x, int y, size_t w, size_t h) +{ + peak_clip_x = x; + peak_clip_y = y; + peak_clip_width = w; + peak_clip_height = h; +} + +void +peak_clip_reset(void) +{ + peak_clip_x = 0; + peak_clip_y = 0; + peak_clip_width = peak_window_width; + peak_clip_height = peak_window_height; +} + +void +peak_draw_rectangle(int x, int y, size_t w, size_t h, uint32_t color) +{ + int start_x = (x < peak_clip_x) ? peak_clip_x : x; + int start_y = (y < peak_clip_y) ? peak_clip_y : y; + int end_x = (x + (int)w > (int)(peak_clip_x + peak_clip_width)) ? (int)(peak_clip_x + peak_clip_width) : x + (int)w; + int end_y = (y + (int)h > (int)(peak_clip_y + peak_clip_height)) ? (int)(peak_clip_y + peak_clip_height) : y + (int)h; + + for (int py = start_y; py < end_y; ++py) { + if (py < 0 || py >= (int)peak_window_height) continue; + for (int px = start_x; px < end_x; ++px) { + if (px < 0 || px >= (int)peak_window_width) continue; + peak_window_buffer[py * peak_window_width + px] = color; + } + } +} + +void +peak_draw_rectangle_gradient(int x, int y, size_t w, size_t h, uint32_t c0, uint32_t c1, uint32_t c2, uint32_t c3) +{ + for (size_t ry = 0; ry < h; ++ry) { + float ty = (h > 1) ? (float)ry / (float)(h - 1) : 0.0f; + uint32_t left_color = peak__lerp_color(c0, c3, ty); + uint32_t right_color = peak__lerp_color(c1, c2, ty); + + for (size_t rx = 0; rx < w; ++rx) { + float tx = (w > 1) ? (float)rx / (float)(w - 1) : 0.0f; + uint32_t final_color = peak__lerp_color(left_color, right_color, tx); + peak__put_pixel(x + (int)rx, y + (int)ry, final_color); + } + } +} + +void +peak_draw_triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color) +{ + int min_x = (x0 < x1) ? ((x0 < x2) ? x0 : x2) : ((x1 < x2) ? x1 : x2); + int max_x = (x0 > x1) ? ((x0 > x2) ? x0 : x2) : ((x1 > x2) ? x1 : x2); + int min_y = (y0 < y1) ? ((y0 < y2) ? y0 : y2) : ((y1 < y2) ? y1 : y2); + int max_y = (y0 > y1) ? ((y0 > y2) ? y0 : y2) : ((y1 > y2) ? y1 : y2); + + float area = peak__edge_cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x2, (float)y2); + if (area == 0.0f) return; + + for (int y = min_y; y <= max_y; ++y) { + for (int x = min_x; x <= max_x; ++x) { + float w0 = peak__edge_cross((float)x1, (float)y1, (float)x2, (float)y2, (float)x, (float)y); + float w1 = peak__edge_cross((float)x2, (float)y2, (float)x0, (float)y0, (float)x, (float)y); + float w2 = peak__edge_cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x, (float)y); + + if ((w0 >= 0 && w1 >= 0 && w2 >= 0) || (w0 <= 0 && w1 <= 0 && w2 <= 0)) { + peak__put_pixel(x, y, color); + } + } + } +} + +void +peak_draw_triangle_gradient(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t c0, uint32_t c1, uint32_t c2) +{ + int min_x = (x0 < x1) ? ((x0 < x2) ? x0 : x2) : ((x1 < x2) ? x1 : x2); + int max_x = (x0 > x1) ? ((x0 > x2) ? x0 : x2) : ((x1 > x2) ? x1 : x2); + int min_y = (y0 < y1) ? ((y0 < y2) ? y0 : y2) : ((y1 < y2) ? y1 : y2); + int max_y = (y0 > y1) ? ((y0 > y2) ? y0 : y2) : ((y1 > y2) ? y1 : y2); + + float area = peak__edge_cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x2, (float)y2); + if (area == 0.0f) return; + + for (int y = min_y; y <= max_y; ++y) { + for (int x = min_x; x <= max_x; ++x) { + float w0 = peak__edge_cross((float)x1, (float)y1, (float)x2, (float)y2, (float)x, (float)y) / area; + float w1 = peak__edge_cross((float)x2, (float)y2, (float)x0, (float)y0, (float)x, (float)y) / area; + float w2 = peak__edge_cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x, (float)y) / area; + + if (w0 >= 0.0f && w1 >= 0.0f && w2 >= 0.0f) { + uint32_t col_a = peak__lerp_color(c0, c1, w1 / (w0 + w1 + 1e-6f)); + uint32_t final_color = peak__lerp_color(col_a, c2, w2); + peak__put_pixel(x, y, final_color); + } + } + } +} + +void +peak_draw_line(int x0, int y0, int x1, int y1, size_t width, uint32_t color) +{ + int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1; + int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1; + int err = dx + dy, e2; + + int half_w = (int)width / 2; + + while (1) { + /* Draw square footprint for line thickness */ + for (int ox = -half_w; ox <= half_w; ++ox) { + for (int oy = -half_w; oy <= half_w; ++oy) { + peak__put_pixel(x0 + ox, y0 + oy, color); + } + } + if (x0 == x1 && y0 == y1) break; + e2 = 2 * err; + if (e2 >= dy) { err += dy; x0 += sx; } + if (e2 <= dx) { err += dx; y0 += sy; } + } +} + +void +peak_draw_line_gradient(int x0, int y0, int x1, int y1, size_t width, uint32_t c0, uint32_t c1) +{ + float total_dist = sqrtf((float)((x1 - x0)*(x1 - x0) + (y1 - y0)*(y1 - y0))); + int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1; + int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1; + int err = dx + dy, e2; + + int orig_x0 = x0, orig_y0 = y0; + int half_w = (int)width / 2; + + while (1) { + float current_dist = sqrtf((float)((x0 - orig_x0)*(x0 - orig_x0) + (y0 - orig_y0)*(y0 - orig_y0))); + float t = (total_dist > 0.0f) ? (current_dist / total_dist) : 0.0f; + uint32_t color = peak__lerp_color(c0, c1, t); + + for (int ox = -half_w; ox <= half_w; ++ox) { + for (int oy = -half_w; oy <= half_w; ++oy) { + peak__put_pixel(x0 + ox, y0 + oy, color); + } + } + + if (x0 == x1 && y0 == y1) break; + e2 = 2 * err; + if (e2 >= dy) { err += dy; x0 += sx; } + if (e2 <= dx) { err += dx; y0 += sy; } + } +} + |
