feat: Intel GPU fixes, add tcc compiler, spreadsheet improvements, paint app, file manager improvements
This commit is contained in:
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* drawing.cpp
|
||||
* Canvas drawing operations for Paint app
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include "paint.h"
|
||||
|
||||
// ============================================================================
|
||||
// Basic canvas operations
|
||||
// ============================================================================
|
||||
|
||||
void canvas_put_pixel(int x, int y, Color c) {
|
||||
if (x < 0 || x >= g_canvas_w || y < 0 || y >= g_canvas_h) return;
|
||||
g_canvas[y * g_canvas_w + x] = c.to_pixel();
|
||||
}
|
||||
|
||||
void canvas_clear(Color c) {
|
||||
uint32_t v = c.to_pixel();
|
||||
int total = g_canvas_w * g_canvas_h;
|
||||
for (int i = 0; i < total; i++)
|
||||
g_canvas[i] = v;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Brush (filled circle stamp)
|
||||
// ============================================================================
|
||||
|
||||
void canvas_draw_brush(int cx, int cy, Color c, int size) {
|
||||
if (size <= 1) {
|
||||
canvas_put_pixel(cx, cy, c);
|
||||
return;
|
||||
}
|
||||
int r = size / 2;
|
||||
int r2 = r * r;
|
||||
for (int dy = -r; dy <= r; dy++)
|
||||
for (int dx = -r; dx <= r; dx++)
|
||||
if (dx * dx + dy * dy <= r2)
|
||||
canvas_put_pixel(cx + dx, cy + dy, c);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Bresenham line
|
||||
// ============================================================================
|
||||
|
||||
void canvas_draw_line(int x0, int y0, int x1, int y1, Color c, int thickness) {
|
||||
int dx = x1 - x0;
|
||||
int dy = y1 - y0;
|
||||
if (dx < 0) dx = -dx;
|
||||
if (dy < 0) dy = -dy;
|
||||
|
||||
int sx = x0 < x1 ? 1 : -1;
|
||||
int sy = y0 < y1 ? 1 : -1;
|
||||
int err = dx - dy;
|
||||
|
||||
while (true) {
|
||||
canvas_draw_brush(x0, y0, c, thickness);
|
||||
if (x0 == x1 && y0 == y1) break;
|
||||
int e2 = 2 * err;
|
||||
if (e2 > -dy) { err -= dy; x0 += sx; }
|
||||
if (e2 < dx) { err += dx; y0 += sy; }
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Rectangle
|
||||
// ============================================================================
|
||||
|
||||
void canvas_draw_rect(int x0, int y0, int x1, int y1, Color c, int thickness) {
|
||||
if (x0 > x1) { int t = x0; x0 = x1; x1 = t; }
|
||||
if (y0 > y1) { int t = y0; y0 = y1; y1 = t; }
|
||||
|
||||
for (int t = 0; t < thickness; t++) {
|
||||
for (int x = x0 + t; x <= x1 - t; x++) {
|
||||
canvas_put_pixel(x, y0 + t, c);
|
||||
canvas_put_pixel(x, y1 - t, c);
|
||||
}
|
||||
for (int y = y0 + t; y <= y1 - t; y++) {
|
||||
canvas_put_pixel(x0 + t, y, c);
|
||||
canvas_put_pixel(x1 - t, y, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void canvas_fill_rect(int x0, int y0, int x1, int y1, Color c) {
|
||||
if (x0 > x1) { int t = x0; x0 = x1; x1 = t; }
|
||||
if (y0 > y1) { int t = y0; y0 = y1; y1 = t; }
|
||||
|
||||
for (int y = y0; y <= y1; y++)
|
||||
for (int x = x0; x <= x1; x++)
|
||||
canvas_put_pixel(x, y, c);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Ellipse (midpoint algorithm)
|
||||
// ============================================================================
|
||||
|
||||
static void ellipse_plot4(int cx, int cy, int x, int y, Color c, int thickness) {
|
||||
if (thickness <= 1) {
|
||||
canvas_put_pixel(cx + x, cy + y, c);
|
||||
canvas_put_pixel(cx - x, cy + y, c);
|
||||
canvas_put_pixel(cx + x, cy - y, c);
|
||||
canvas_put_pixel(cx - x, cy - y, c);
|
||||
} else {
|
||||
canvas_draw_brush(cx + x, cy + y, c, thickness);
|
||||
canvas_draw_brush(cx - x, cy + y, c, thickness);
|
||||
canvas_draw_brush(cx + x, cy - y, c, thickness);
|
||||
canvas_draw_brush(cx - x, cy - y, c, thickness);
|
||||
}
|
||||
}
|
||||
|
||||
void canvas_draw_ellipse(int x0, int y0, int x1, int y1, Color c, int thickness) {
|
||||
if (x0 > x1) { int t = x0; x0 = x1; x1 = t; }
|
||||
if (y0 > y1) { int t = y0; y0 = y1; y1 = t; }
|
||||
|
||||
int cx = (x0 + x1) / 2;
|
||||
int cy = (y0 + y1) / 2;
|
||||
int a = (x1 - x0) / 2;
|
||||
int b = (y1 - y0) / 2;
|
||||
if (a == 0 || b == 0) {
|
||||
canvas_draw_line(x0, y0, x1, y1, c, thickness);
|
||||
return;
|
||||
}
|
||||
|
||||
long long a2 = (long long)a * a;
|
||||
long long b2 = (long long)b * b;
|
||||
int x = 0, y = b;
|
||||
long long d1 = b2 - a2 * b + a2 / 4;
|
||||
|
||||
while (a2 * y > b2 * x) {
|
||||
ellipse_plot4(cx, cy, x, y, c, thickness);
|
||||
if (d1 < 0) {
|
||||
d1 += b2 * (2 * x + 3);
|
||||
} else {
|
||||
d1 += b2 * (2 * x + 3) + a2 * (-2 * y + 2);
|
||||
y--;
|
||||
}
|
||||
x++;
|
||||
}
|
||||
|
||||
long long d2 = b2 * ((long long)(x * 2 + 1) * (x * 2 + 1)) / 4
|
||||
+ a2 * ((long long)(y - 1) * (y - 1))
|
||||
- a2 * b2;
|
||||
while (y >= 0) {
|
||||
ellipse_plot4(cx, cy, x, y, c, thickness);
|
||||
if (d2 > 0) {
|
||||
d2 += a2 * (-2 * y + 3);
|
||||
} else {
|
||||
d2 += b2 * (2 * x + 2) + a2 * (-2 * y + 3);
|
||||
x++;
|
||||
}
|
||||
y--;
|
||||
}
|
||||
}
|
||||
|
||||
void canvas_fill_ellipse(int x0, int y0, int x1, int y1, Color c) {
|
||||
if (x0 > x1) { int t = x0; x0 = x1; x1 = t; }
|
||||
if (y0 > y1) { int t = y0; y0 = y1; y1 = t; }
|
||||
|
||||
int cx = (x0 + x1) / 2;
|
||||
int cy = (y0 + y1) / 2;
|
||||
int a = (x1 - x0) / 2;
|
||||
int b = (y1 - y0) / 2;
|
||||
if (a == 0 || b == 0) return;
|
||||
|
||||
for (int dy = -b; dy <= b; dy++) {
|
||||
// x^2/a^2 + y^2/b^2 <= 1 => x <= a * sqrt(1 - y^2/b^2)
|
||||
long long x_extent = (long long)a * a * ((long long)b * b - (long long)dy * dy);
|
||||
if (x_extent < 0) continue;
|
||||
// Integer sqrt approximation
|
||||
long long denom = (long long)b * b;
|
||||
int w = 0;
|
||||
while ((long long)(w + 1) * (w + 1) * denom <= x_extent) w++;
|
||||
for (int dx = -w; dx <= w; dx++)
|
||||
canvas_put_pixel(cx + dx, cy + dy, c);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Flood fill (iterative scanline)
|
||||
// ============================================================================
|
||||
|
||||
void canvas_flood_fill(int sx, int sy, Color fill_color) {
|
||||
if (sx < 0 || sx >= g_canvas_w || sy < 0 || sy >= g_canvas_h) return;
|
||||
|
||||
uint32_t target = g_canvas[sy * g_canvas_w + sx];
|
||||
uint32_t fill_px = fill_color.to_pixel();
|
||||
if (target == fill_px) return;
|
||||
|
||||
// Simple stack-based flood fill with a bounded stack
|
||||
static constexpr int STACK_MAX = 65536;
|
||||
struct Pt { int16_t x, y; };
|
||||
Pt* stack = (Pt*)montauk::malloc(STACK_MAX * sizeof(Pt));
|
||||
if (!stack) return;
|
||||
|
||||
int sp = 0;
|
||||
stack[sp++] = {(int16_t)sx, (int16_t)sy};
|
||||
g_canvas[sy * g_canvas_w + sx] = fill_px;
|
||||
|
||||
while (sp > 0) {
|
||||
Pt p = stack[--sp];
|
||||
int x = p.x, y = p.y;
|
||||
|
||||
// Scan left
|
||||
int left = x;
|
||||
while (left > 0 && g_canvas[y * g_canvas_w + left - 1] == target) {
|
||||
left--;
|
||||
g_canvas[y * g_canvas_w + left] = fill_px;
|
||||
}
|
||||
|
||||
// Scan right
|
||||
int right = x;
|
||||
while (right < g_canvas_w - 1 && g_canvas[y * g_canvas_w + right + 1] == target) {
|
||||
right++;
|
||||
g_canvas[y * g_canvas_w + right] = fill_px;
|
||||
}
|
||||
|
||||
// Check above and below
|
||||
for (int dir = -1; dir <= 1; dir += 2) {
|
||||
int ny = y + dir;
|
||||
if (ny < 0 || ny >= g_canvas_h) continue;
|
||||
bool in_span = false;
|
||||
for (int px = left; px <= right; px++) {
|
||||
if (g_canvas[ny * g_canvas_w + px] == target) {
|
||||
if (!in_span && sp < STACK_MAX) {
|
||||
g_canvas[ny * g_canvas_w + px] = fill_px;
|
||||
stack[sp++] = {(int16_t)px, (int16_t)ny};
|
||||
in_span = true;
|
||||
}
|
||||
} else {
|
||||
in_span = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
montauk::mfree(stack);
|
||||
}
|
||||
Reference in New Issue
Block a user