initial commit
commit
4ac46121da
|
@ -0,0 +1,4 @@
|
||||||
|
CFLAGS:=`sdl2-config --cflags` -g -Og -Wall -Wno-parentheses -Wno-unused-function
|
||||||
|
LDLIBS:=-lSDL2 -lSDL2_image -lm -ldl
|
||||||
|
|
||||||
|
main: main.c draw.c
|
|
@ -0,0 +1,425 @@
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "draw.h"
|
||||||
|
|
||||||
|
#define abs(x) ((x) < 0 ? -(x) : (x))
|
||||||
|
|
||||||
|
Color framebuffer[WIDTH * HEIGHT];
|
||||||
|
|
||||||
|
void to_hsv(u8 r, u8 g, u8 b, u8* h, u8* s, u8* v) {
|
||||||
|
int min = r < g ? (r < b ? r : b) : (g < b ? g : b);
|
||||||
|
int max = r > g ? (r > b ? r : b) : (g > b ? g : b);
|
||||||
|
|
||||||
|
*h = 0;
|
||||||
|
*s = 0;
|
||||||
|
*v = max;
|
||||||
|
|
||||||
|
if (*v != 0) {
|
||||||
|
*s = 255 * ((long)max - min) / *v;
|
||||||
|
if (*s != 0) {
|
||||||
|
if (max == r) *h = 0 + 43 * (g - b) / (max - min);
|
||||||
|
else if (max == g) *h = 85 + 43 * (b - r) / (max - min);
|
||||||
|
else *h = 171 + 43 * (r - g) / (max - min);
|
||||||
|
} else {
|
||||||
|
*h = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void from_hsv(u8 h, u8 s, u8 v, u8* r, u8* g, u8* b) {
|
||||||
|
int region = h / 43;
|
||||||
|
int remainder = (h - (region * 43)) * 6;
|
||||||
|
|
||||||
|
int p = (v * (255 - s)) >> 8;
|
||||||
|
int q = (v * (255 - ((s * remainder) >> 8))) >> 8;
|
||||||
|
int t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8;
|
||||||
|
|
||||||
|
switch (region) {
|
||||||
|
case 0: *r = v; *g = t; *b = p; break;
|
||||||
|
case 1: *r = q; *g = v; *b = p; break;
|
||||||
|
case 2: *r = p; *g = v; *b = t; break;
|
||||||
|
case 3: *r = p; *g = q; *b = v; break;
|
||||||
|
case 4: *r = t; *g = p; *b = v; break;
|
||||||
|
default: *r = v; *g = p; *b = q; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_screen() {
|
||||||
|
memset(framebuffer, 0, WIDTH * HEIGHT * sizeof(Color));
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_point(i32 x, i32 y, Color color) {
|
||||||
|
if (x >= 0 && x < WIDTH && y >= 0 && y < HEIGHT) framebuffer[y * WIDTH + x] = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fill_rect(i32 x, i32 y, i32 width, i32 height, Color color) {
|
||||||
|
if (x < 0) {
|
||||||
|
width += x;
|
||||||
|
x = 0;
|
||||||
|
}
|
||||||
|
if (y < 0) {
|
||||||
|
height += y;
|
||||||
|
y = 0;
|
||||||
|
}
|
||||||
|
if (x + width > WIDTH) width = WIDTH - x;
|
||||||
|
if (y + height > HEIGHT) height = HEIGHT - y;
|
||||||
|
|
||||||
|
for (i32 j = y; j < y + height; ++j) {
|
||||||
|
Color* pixel = &framebuffer[j * WIDTH + x];
|
||||||
|
for (i32 i = 0; i < width; ++i) *(pixel++) = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_rect(i32 x, i32 y, i32 width, i32 height, Color color) {
|
||||||
|
horizontal_line(x, x + width, y, color);
|
||||||
|
for (i32 i = 1; i < height - 1; i++) {
|
||||||
|
draw_point(x, y + i, color);
|
||||||
|
draw_point(x + width - 1, y + i, color);
|
||||||
|
}
|
||||||
|
horizontal_line(x, x + width, y + height - 1, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void blit(i32 x, i32 y, i32 x_source, i32 y_source, i32 width, i32 height, Color* source, i32 source_width, i32 source_height) {
|
||||||
|
if (x_source < 0) {
|
||||||
|
width += x_source;
|
||||||
|
x_source = 0;
|
||||||
|
}
|
||||||
|
if (y_source < 0) {
|
||||||
|
height += y_source;
|
||||||
|
y_source = 0;
|
||||||
|
}
|
||||||
|
if (x_source + width > source_width) width = source_width - x_source;
|
||||||
|
if (y_source + height > source_height) height = source_height - y_source;
|
||||||
|
if (x < 0) {
|
||||||
|
width += x;
|
||||||
|
x_source -= x;
|
||||||
|
x = 0;
|
||||||
|
}
|
||||||
|
if (y < 0) {
|
||||||
|
height += y;
|
||||||
|
y_source -= y;
|
||||||
|
y = 0;
|
||||||
|
}
|
||||||
|
if (x + width >= WIDTH) width = WIDTH - x; // WARNING: x might be larger than WIDTH, resulting in negative width
|
||||||
|
if (y + height >= HEIGHT) height = HEIGHT - y;
|
||||||
|
for (int j = 0; j < height; j++) {
|
||||||
|
int offset = (y + j) * WIDTH + x;
|
||||||
|
int source_offset = (y_source + j) * source_width + x_source;
|
||||||
|
if (width > 0) memcpy(framebuffer + offset, source + source_offset, width * sizeof(Color));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void blit_masked(i32 x, i32 y, i32 x_source, i32 y_source, i32 width, i32 height, Color mask, Color* source, i32 source_width, i32 source_height) {
|
||||||
|
if (x_source < 0) {
|
||||||
|
width += x_source;
|
||||||
|
x_source = 0;
|
||||||
|
}
|
||||||
|
if (y_source < 0) {
|
||||||
|
height += y_source;
|
||||||
|
y_source = 0;
|
||||||
|
}
|
||||||
|
if (x_source + width > source_width) width = source_width - x_source;
|
||||||
|
if (y_source + height > source_height) height = source_height - y_source;
|
||||||
|
if (x < 0) {
|
||||||
|
width += x;
|
||||||
|
x_source -= x;
|
||||||
|
x = 0;
|
||||||
|
}
|
||||||
|
if (y < 0) {
|
||||||
|
height += y;
|
||||||
|
y_source -= y;
|
||||||
|
y = 0;
|
||||||
|
}
|
||||||
|
if (x + width >= WIDTH) width = WIDTH - x; // WARNING: x might be larger than WIDTH, resulting in negative width
|
||||||
|
if (y + height >= HEIGHT) height = HEIGHT - y;
|
||||||
|
for (int j = 0; j < height; j++) {
|
||||||
|
int offset = (y + j) * WIDTH + x;
|
||||||
|
int source_offset = (y_source + j) * source_width + x_source;
|
||||||
|
for (int i = 0; i < width; i++) {
|
||||||
|
if (source[source_offset + i] != mask) framebuffer[offset + i] = source[source_offset + i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_line(i32 x0, i32 y0, i32 x1, i32 y1, Color color) {
|
||||||
|
i32 dx = abs(x1-x0);
|
||||||
|
i32 sx = x0 < x1 ? 1 : -1;
|
||||||
|
i32 dy = -abs(y1-y0);
|
||||||
|
i32 sy = y0<y1 ? 1 : -1;
|
||||||
|
i32 err = dx + dy; /* error value e_xy */
|
||||||
|
while (1) { /* loop */
|
||||||
|
draw_point(x0, y0, color);
|
||||||
|
if (x0 == x1 && y0 == y1) break;
|
||||||
|
i32 e2 = 2*err;
|
||||||
|
if (e2 >= dy) { /* e_xy+e_x > 0 */
|
||||||
|
err += dy;
|
||||||
|
x0 += sx;
|
||||||
|
}
|
||||||
|
if (e2 <= dx) { /* e_xy+e_y < 0 */
|
||||||
|
err += dx;
|
||||||
|
y0 += sy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_circle(i32 xm, i32 ym, i32 r, Color color) {
|
||||||
|
i32 x = -r, y = 0, err = 2 - 2 * r; /* II. Quadrant */
|
||||||
|
do {
|
||||||
|
draw_point(xm - x, ym + y, color); /* I. Quadrant */
|
||||||
|
draw_point(xm - y, ym - x, color); /* II. Quadrant */
|
||||||
|
draw_point(xm + x, ym - y, color); /* III. Quadrant */
|
||||||
|
draw_point(xm + y, ym + x, color); /* IV. Quadrant */
|
||||||
|
r = err;
|
||||||
|
if (r <= y) err += ++y * 2 + 1; /* e_xy+e_y < 0 */
|
||||||
|
if (r > x || err > y) err += ++x * 2 + 1; /* e_xy+e_x > 0 or no 2nd y-step */
|
||||||
|
} while (x < 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void horizontal_line(i32 x1, i32 x2, i32 y, Color color) {
|
||||||
|
if (x1 >= WIDTH || x2 < 0 || y < 0 || y >= HEIGHT) return;
|
||||||
|
if (x1 < 0) x1 = 0;
|
||||||
|
if (x2 >= WIDTH) x2 = WIDTH;
|
||||||
|
int offset = y * WIDTH;
|
||||||
|
while (x1 < x2) {
|
||||||
|
framebuffer[offset + x1] = color;
|
||||||
|
x1 ++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void horizontal_line_wrapped(i32 x1, i32 x2, i32 y, Color color) {
|
||||||
|
if (y < 0 || y >= HEIGHT) return;
|
||||||
|
int offset = y * WIDTH;
|
||||||
|
while (x1 < x2) {
|
||||||
|
framebuffer[offset + x1 % WIDTH] = color;
|
||||||
|
x1 ++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fill_circle(i32 xm, i32 ym, i32 r, Color color) {
|
||||||
|
i32 x = -r, y = 0, err = 2 - 2 * r; /* II. Quadrant */
|
||||||
|
do {
|
||||||
|
horizontal_line(xm + x, xm - x, ym - y, color);
|
||||||
|
horizontal_line(xm + x, xm - x, ym + y, color);
|
||||||
|
r = err;
|
||||||
|
if (r <= y) err += ++y * 2 + 1; /* e_xy+e_y < 0 */
|
||||||
|
if (r > x || err > y) err += ++x * 2 + 1; /* e_xy+e_x > 0 or no 2nd y-step */
|
||||||
|
} while (x < 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void copy(int n, float *points, float* result) {
|
||||||
|
for (int i = 0; i < n; i++) result[i] = points[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void scale(int n, float* points, float factorX, float factorY) {
|
||||||
|
for (int i = 0; i < n; i += 2) {
|
||||||
|
points[i] *= factorX;
|
||||||
|
points[i + 1] *= factorY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void translate(int n, float* points, float x, float y) {
|
||||||
|
for (int i = 0; i < n; i += 2) {
|
||||||
|
points[i] += x;
|
||||||
|
points[i + 1] += y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rotate(int n, float* points, float angle) {
|
||||||
|
float c = cos(angle);
|
||||||
|
float s = sin(angle);
|
||||||
|
for (int i = 0; i < n; i += 2) {
|
||||||
|
float x = c * points[i] - s * points[i + 1];
|
||||||
|
float y = s * points[i] + c * points[i + 1];
|
||||||
|
points[i] = x;
|
||||||
|
points[i + 1] = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(int n, float* points, int close, Color color) {
|
||||||
|
for (int i = 0; i < n - 2; i += 2) {
|
||||||
|
draw_line(points[i], points[i + 1], points[i + 2], points[i + 3], color);
|
||||||
|
}
|
||||||
|
if (close) {
|
||||||
|
draw_line(points[n - 2], points[n - 1], points[0], points[1], color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fill(int n, float* points, int wrap, Color color) {
|
||||||
|
int polyCorners = n >> 1;
|
||||||
|
int nodes, nodeX[n], pixelY, i, j, swap;
|
||||||
|
|
||||||
|
// determine bounding segment on Y axis
|
||||||
|
int minY = HEIGHT, maxY = 0;
|
||||||
|
for (int i = 0; i < n; i += 2) {
|
||||||
|
if (points[i + 1] < minY) minY = points[i + 1];
|
||||||
|
if (points[i + 1] > maxY) maxY = points[i + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minY < 0) minY = 0;
|
||||||
|
if (maxY >= HEIGHT) maxY = HEIGHT - 1;
|
||||||
|
|
||||||
|
for (pixelY = minY; pixelY <= maxY; pixelY++) {
|
||||||
|
|
||||||
|
// Build a list of nodes.
|
||||||
|
nodes = 0; j = polyCorners - 1;
|
||||||
|
for (i = 0; i < polyCorners; i++) {
|
||||||
|
int ii = i << 1;
|
||||||
|
int jj = j << 1;
|
||||||
|
if (points[ii + 1] < pixelY && points[jj + 1] >= pixelY || points[jj + 1] < pixelY && points[ii + 1] >= pixelY) {
|
||||||
|
nodeX[nodes++] = (int) (points[ii] + (pixelY - points[ii + 1]) / (points[jj + 1] - points[ii + 1]) *(points[jj] - points[ii]));
|
||||||
|
}
|
||||||
|
j = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the nodes, via a simple "Bubble" sort.
|
||||||
|
i = 0;
|
||||||
|
while (i < nodes - 1) {
|
||||||
|
if (nodeX[i] > nodeX[i + 1]) {
|
||||||
|
swap = nodeX[i]; nodeX[i] = nodeX[i + 1]; nodeX[i + 1] = swap; if (i) i--;
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill the pixels between node pairs.
|
||||||
|
for (i = 0; i < nodes; i += 2) {
|
||||||
|
if (wrap) {
|
||||||
|
horizontal_line(nodeX[i] - wrap, nodeX[i + 1] - wrap, pixelY, color);
|
||||||
|
horizontal_line(nodeX[i], nodeX[i + 1], pixelY, color);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (nodeX[i] >= WIDTH) break;
|
||||||
|
if (nodeX[i + 1] > 0 ) {
|
||||||
|
if (nodeX[i] < 0 ) nodeX[i] = 0;
|
||||||
|
if (nodeX[i + 1] >= WIDTH ) nodeX[i + 1] = WIDTH - 1;
|
||||||
|
horizontal_line(nodeX[i], nodeX[i + 1], pixelY, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int point_in_polygon(int n, float* points, float x, float y) {
|
||||||
|
int i, j = n - 2 ;
|
||||||
|
int oddNodes = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < n; i += 2) {
|
||||||
|
if ((points[i + 1] < y && points[j + 1] >= y || points[j + 1] < y && points[i + 1] >= y) && (points[i] <= x || points[j] <= x)) {
|
||||||
|
oddNodes ^= (points[i] + (y - points[i + 1]) / (points[j + 1] - points[i + 1]) * (points[j] - points[i]) < x);
|
||||||
|
}
|
||||||
|
j = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return oddNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
int line_intersect(float* l1, float* l2) {
|
||||||
|
float Ax = l1[0], Ay = l1[1], Bx = l1[2], By = l1[3];
|
||||||
|
float Cx = l2[0], Cy = l2[1], Dx = l2[2], Dy = l2[3];
|
||||||
|
|
||||||
|
float distAB, theCos, theSin, newX, ABpos;
|
||||||
|
|
||||||
|
// Fail if either line segment is zero-length.
|
||||||
|
if (Ax == Bx && Ay == By || Cx == Dx && Cy == Dy) return 0;
|
||||||
|
|
||||||
|
// Fail if the segments share an end-point.
|
||||||
|
if (Ax == Cx && Ay == Cy || Bx == Cx && By == Cy || Ax == Dx && Ay == Dy || Bx == Dx && By == Dy) return 0;
|
||||||
|
|
||||||
|
// (1) Translate the system so that point A is on the origin.
|
||||||
|
Bx -= Ax; By -= Ay;
|
||||||
|
Cx -= Ax; Cy -= Ay;
|
||||||
|
Dx -= Ax; Dy -= Ay;
|
||||||
|
|
||||||
|
// Discover the length of segment A-B.
|
||||||
|
distAB = sqrt(Bx * Bx + By * By);
|
||||||
|
|
||||||
|
// (2) Rotate the system so that point B is on the positive X axis.
|
||||||
|
theCos = Bx / distAB;
|
||||||
|
theSin = By / distAB;
|
||||||
|
newX = Cx * theCos + Cy * theSin;
|
||||||
|
Cy = Cy * theCos - Cx * theSin;
|
||||||
|
Cx = newX;
|
||||||
|
newX = Dx * theCos + Dy * theSin;
|
||||||
|
Dy = Dy * theCos - Dx * theSin;
|
||||||
|
Dx = newX;
|
||||||
|
|
||||||
|
// Fail if segment C-D doesn't cross line A-B.
|
||||||
|
if (Cy < 0 && Dy < 0 || Cy >= 0 && Dy >= 0) return 0;
|
||||||
|
|
||||||
|
// (3) Discover the position of the intersection point along line A-B.
|
||||||
|
ABpos = Dx + (Cx - Dx) * Dy / (Dy - Cy);
|
||||||
|
|
||||||
|
// Fail if segment C-D crosses line A-B outside of segment A-B.
|
||||||
|
if (ABpos < 0 || ABpos > distAB) return 0;
|
||||||
|
|
||||||
|
// (4) Apply the discovered position to line A-B in the original coordinate system.
|
||||||
|
//*X = Ax + ABpos * theCos;
|
||||||
|
//*Y = Ay + ABpos * theSin;
|
||||||
|
|
||||||
|
// Success.
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int polygon_intersect(int n, float* a, int m, float* b) {
|
||||||
|
for (int i = 0; i < n; i+=2) {
|
||||||
|
for (int j = 0; j < m; j += 2) {
|
||||||
|
if (line_intersect(a + i, b + j)) return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (point_in_polygon(n, a, b[0], b[1])) return 1;
|
||||||
|
if (point_in_polygon(m, b, a[0], a[1])) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
font_t* load_font(char* glyphs, int font_data_size) {
|
||||||
|
printf("load_font: not implemented\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_text(font_t* font, i32 x, i32 y, Color color, char* text) {
|
||||||
|
while(*text) {
|
||||||
|
int start = 0, end = font->num_glyphs - 1;
|
||||||
|
int current = 0;
|
||||||
|
glyph_t glyph = font->glyphs[0];
|
||||||
|
while (current < 4 && start <= end) {
|
||||||
|
int middle = (start + end) / 2;
|
||||||
|
//printf(" %c <> %c ? %d %d=%d+%d/2\n", text[current], glyph.text[current], current, middle, start, end);
|
||||||
|
glyph = font->glyphs[middle];
|
||||||
|
if (glyph.text[current] > text[current]) end = middle - 1;
|
||||||
|
else if (glyph.text[current] < text[current]) start = middle + 1;
|
||||||
|
else break;
|
||||||
|
//else current++;
|
||||||
|
}
|
||||||
|
//printf("%s %s\n", text, glyph.text);
|
||||||
|
|
||||||
|
int offset = glyph.offset;
|
||||||
|
for (int j = 0; j < glyph.height; j++) {
|
||||||
|
for (int i = 0; i < glyph.width; i++) {
|
||||||
|
int mask = 1 << i;
|
||||||
|
if (font->pixels[offset] & mask) framebuffer[x + i + (j + y) * WIDTH] = color;
|
||||||
|
}
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
x += glyph.width;
|
||||||
|
text += current + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_printf(font_t* font, i32 x, i32 y, Color color, char* format, ...) {
|
||||||
|
char* buffer = NULL;
|
||||||
|
va_list list;
|
||||||
|
va_start(list, format);
|
||||||
|
int result = vasprintf(&buffer, format, list);
|
||||||
|
if (result > -1) {
|
||||||
|
draw_text(font, x, y, color, buffer);
|
||||||
|
}
|
||||||
|
if (buffer) free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: triangle, blit, roto-scale, etc.
|
|
@ -0,0 +1,63 @@
|
||||||
|
#ifndef __DRAW_H__
|
||||||
|
#define __DRAW_H__
|
||||||
|
|
||||||
|
#define WIDTH 480
|
||||||
|
#define HEIGHT 240
|
||||||
|
|
||||||
|
typedef unsigned char u8;
|
||||||
|
typedef signed char i8;
|
||||||
|
typedef unsigned short u16;
|
||||||
|
typedef signed short i16;
|
||||||
|
typedef unsigned int u32;
|
||||||
|
typedef signed int i32;
|
||||||
|
typedef u16 Color;
|
||||||
|
|
||||||
|
extern Color framebuffer[WIDTH * HEIGHT];
|
||||||
|
|
||||||
|
#define rgb565(r, g, b) ((((u16)r & 0b11111000) << 8) | (((u16)g & 0b11111100) << 3) | (((u16)b & 0xff) >> 3))
|
||||||
|
#define rgb(r, g, b) (((u32)(r & 0xff) << 24) | ((u32)(g & 0xff) << 16) | ((u32)(b & 0xff) << 8) | 0xff)
|
||||||
|
|
||||||
|
void from_hsv(u8 h, u8 s, u8 v, u8* r, u8* g, u8* b);
|
||||||
|
void to_hsv(u8 r, u8 g, u8 b, u8* h, u8* s, u8* v);
|
||||||
|
void clear_screen();
|
||||||
|
void draw_point(i32 x, i32 y, Color color);
|
||||||
|
void fill_rect(i32 x, i32 y, i32 width, i32 height, Color color);
|
||||||
|
void draw_rect(i32 x, i32 y, i32 width, i32 height, Color color);
|
||||||
|
void draw_line(i32 x0, i32 y0, i32 x1, i32 y1, Color color);
|
||||||
|
void draw_circle(i32 xm, i32 ym, i32 r, Color color);
|
||||||
|
void horizontal_line(i32 x1, i32 x2, i32 y, Color color);
|
||||||
|
void fill_circle(i32 xm, i32 ym, i32 r, Color color);
|
||||||
|
void copy(int n, float *points, float* result);
|
||||||
|
void scale(int n, float* points, float factorX, float factorY);
|
||||||
|
void translate(int n, float* points, float x, float y);
|
||||||
|
void rotate(int n, float* points, float angle);
|
||||||
|
void draw(int n, float* points, int close, Color color);
|
||||||
|
void fill(int n, float* points, int wrap, Color color);
|
||||||
|
int point_in_polygon(int n, float* points, float x, float y);
|
||||||
|
int line_intersect(float* l1, float* l2);
|
||||||
|
int polygon_intersect(int n, float* a, int m, float* b);
|
||||||
|
|
||||||
|
#define MIRROR_V 1
|
||||||
|
#define MIRROR_H 2
|
||||||
|
#define ROTATE_L 4
|
||||||
|
void blit(i32 x, i32 y, i32 source_x, i32 source_y, i32 width, i32 height, Color* source, i32 source_width, i32 source_height);
|
||||||
|
void blit_masked(i32 x, i32 y, i32 source_x, i32 source_y, i32 width, i32 height, Color mask, Color* source, i32 source_width, i32 source_height);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char text[4];
|
||||||
|
u8 width, height;
|
||||||
|
u16 offset;
|
||||||
|
} glyph_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u8* pixels;
|
||||||
|
glyph_t* glyphs;
|
||||||
|
int num_glyphs;
|
||||||
|
int line_height;
|
||||||
|
} font_t;
|
||||||
|
|
||||||
|
font_t* load_font(char* font_data, int font_data_size);
|
||||||
|
void draw_text(font_t* font, i32 x, i32 y, Color color, char* text);
|
||||||
|
void draw_printf(font_t* font, i32 x, i32 y, Color color, char* format, ...);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,202 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <SDL2/SDL_image.h>
|
||||||
|
|
||||||
|
#include "draw.h"
|
||||||
|
|
||||||
|
#define PRESCALE 4
|
||||||
|
#define FORMAT SDL_PIXELFORMAT_RGB565
|
||||||
|
|
||||||
|
#define WHITE rgb565(255, 255, 255)
|
||||||
|
#define BLACK rgb565(0, 0, 0)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int size;
|
||||||
|
float* points;
|
||||||
|
} Polygon;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int size;
|
||||||
|
Polygon* polygons;
|
||||||
|
} Data;
|
||||||
|
|
||||||
|
Data data;
|
||||||
|
|
||||||
|
void load(char* filename) {
|
||||||
|
FILE* fp = fopen(filename, "r");
|
||||||
|
if (!fp) {
|
||||||
|
perror(filename);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (1 != fscanf(fp, "%d", &data.size)) goto load_error;
|
||||||
|
data.polygons = malloc(sizeof(Polygon) * data.size);
|
||||||
|
for (int i = 0; i < data.size; i++) {
|
||||||
|
float tmp; // skip bbox
|
||||||
|
if (4 != fscanf(fp, "%f %f %f %f", &tmp, &tmp, &tmp, &tmp)) goto load_error;
|
||||||
|
if (1 != fscanf(fp, "%d", &data.polygons[i].size)) goto load_error;
|
||||||
|
data.polygons[i].size *= 2; // number of values is number of points * 2
|
||||||
|
data.polygons[i].points = malloc(sizeof(float) * data.polygons[i].size);
|
||||||
|
for (int j = 0; j < data.polygons[i].size; j++) {
|
||||||
|
if (1 != fscanf(fp, "%f", &data.polygons[i].points[j])) goto load_error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
load_error:
|
||||||
|
perror(filename);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
float center[2] = {WIDTH / 2, 0};
|
||||||
|
float zoom = 1;
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
load("ne_10m_land.txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
void deinit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(float dt) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void redraw() {
|
||||||
|
clear_screen();
|
||||||
|
for (int i = 0; i < data.size; i++) {
|
||||||
|
float transformed[data.polygons[i].size];
|
||||||
|
for (int j = 0; j < data.polygons[i].size; j += 2) {
|
||||||
|
transformed[j] = data.polygons[i].points[j] * zoom * WIDTH / 360 + WIDTH / 2 + center[0] * zoom;
|
||||||
|
transformed[j + 1] = -data.polygons[i].points[j + 1] * zoom * HEIGHT / 180 + HEIGHT / 2 + center[1] * zoom;
|
||||||
|
}
|
||||||
|
//fill(data.polygons[i].size, transformed, WIDTH * zoom, rgb565((i * 128397) % 255, (i * 123551) % 255, (i * 1097123) % 255));
|
||||||
|
fill(data.polygons[i].size, transformed, WIDTH * zoom, rgb565(255, 255, 255));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#define on_key(event)
|
||||||
|
//#define on_mouse(event, kind)
|
||||||
|
#define on_text(event)
|
||||||
|
|
||||||
|
int mouse_pressed = 0;
|
||||||
|
int mouse_start_x, mouse_start_y;
|
||||||
|
int center_start_x, center_start_y;
|
||||||
|
|
||||||
|
void on_key(SDL_Event event) {
|
||||||
|
switch (event.key.keysym.sym) {
|
||||||
|
case SDLK_1: zoom = 1; break;
|
||||||
|
case SDLK_2: zoom = 4; break;
|
||||||
|
case SDLK_3: zoom = 16; break;
|
||||||
|
case SDLK_4: zoom = 64; break;
|
||||||
|
case SDLK_5: zoom = 128; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_mouse(SDL_Event event, int kind) {
|
||||||
|
switch (kind) {
|
||||||
|
case 1:
|
||||||
|
mouse_pressed = 1;
|
||||||
|
mouse_start_x = event.motion.x;
|
||||||
|
mouse_start_y = event.motion.y;
|
||||||
|
center_start_x = center[0];
|
||||||
|
center_start_y = center[1];
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
mouse_pressed = 0;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
if (mouse_pressed) {
|
||||||
|
center[0] = ((event.motion.x - mouse_start_x) / zoom + center_start_x);
|
||||||
|
center[1] = ((event.motion.y - mouse_start_y) / zoom + center_start_y);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
zoom += event.wheel.preciseY / 8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
|
||||||
|
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
||||||
|
fprintf(stderr, "Failed to init SDL\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(IMG_Init(IMG_INIT_PNG) < 0) {
|
||||||
|
fprintf(stderr, "Failed to init SDL_Image\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
|
SDL_Window* window = SDL_CreateWindow("RPG", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 2 * WIDTH, 2 * HEIGHT, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE );
|
||||||
|
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
|
||||||
|
|
||||||
|
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
|
||||||
|
SDL_Texture* scaled = SDL_CreateTexture(renderer, FORMAT, SDL_TEXTUREACCESS_TARGET, PRESCALE * WIDTH, PRESCALE * HEIGHT);
|
||||||
|
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
|
||||||
|
|
||||||
|
SDL_RenderSetLogicalSize(renderer, WIDTH, HEIGHT);
|
||||||
|
//SDL_RenderSetIntegerScale(renderer, 1);
|
||||||
|
//SDL_MaximizeWindow(window);
|
||||||
|
SDL_StartTextInput();
|
||||||
|
|
||||||
|
SDL_SetRenderTarget(renderer, scaled);
|
||||||
|
|
||||||
|
int fullscreen = 0;
|
||||||
|
|
||||||
|
SDL_Texture* screen_texture = SDL_CreateTexture(renderer, FORMAT, SDL_TEXTUREACCESS_STATIC, WIDTH, HEIGHT);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
Uint64 frame_start = SDL_GetPerformanceCounter();
|
||||||
|
|
||||||
|
SDL_Event event;
|
||||||
|
while (SDL_PollEvent(&event)) {
|
||||||
|
if (event.type == SDL_QUIT) { deinit(); exit(0);
|
||||||
|
} else if(event.type == SDL_WINDOWEVENT) {
|
||||||
|
} else if (event.type == SDL_KEYDOWN) {
|
||||||
|
if (event.key.keysym.sym == SDLK_RETURN && event.key.keysym.mod & KMOD_ALT) {
|
||||||
|
fullscreen = SDL_WINDOW_FULLSCREEN_DESKTOP - fullscreen;
|
||||||
|
SDL_SetWindowFullscreen(window, fullscreen);
|
||||||
|
} else if (event.key.keysym.sym == SDLK_q && event.key.keysym.mod & KMOD_CTRL) {
|
||||||
|
deinit(); exit(0);
|
||||||
|
} else on_key(event);
|
||||||
|
} else if (event.type == SDL_KEYUP) { on_key(event);
|
||||||
|
} else if (event.type == SDL_TEXTINPUT) { on_text(event);
|
||||||
|
} else if (event.type == SDL_MOUSEBUTTONDOWN) { on_mouse(event, 1);
|
||||||
|
} else if (event.type == SDL_MOUSEMOTION) { on_mouse(event, 0);
|
||||||
|
} else if (event.type == SDL_MOUSEBUTTONUP) { on_mouse(event, 2);
|
||||||
|
} else if (event.type == SDL_MOUSEWHEEL) { on_mouse(event, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
||||||
|
SDL_RenderClear(renderer);
|
||||||
|
|
||||||
|
SDL_UpdateTexture(screen_texture, NULL, framebuffer, WIDTH * sizeof(Color));
|
||||||
|
SDL_RenderCopy(renderer, screen_texture, NULL, NULL);
|
||||||
|
|
||||||
|
SDL_SetRenderTarget(renderer, NULL);
|
||||||
|
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
||||||
|
SDL_RenderClear(renderer);
|
||||||
|
SDL_RenderCopy(renderer, scaled, NULL, NULL);
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
|
|
||||||
|
SDL_SetRenderTarget(renderer, scaled);
|
||||||
|
|
||||||
|
update(1.0f / 60);
|
||||||
|
redraw();
|
||||||
|
|
||||||
|
Uint64 frame_end = SDL_GetPerformanceCounter();
|
||||||
|
Uint64 duration = (frame_end - frame_start) * 1000 / SDL_GetPerformanceFrequency();
|
||||||
|
if (duration > 0) printf("%ld fps\n", 1000 / duration);
|
||||||
|
if(duration < 1000 / 60) SDL_Delay(1000 / 60 - duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit();
|
||||||
|
|
||||||
|
SDL_DestroyRenderer(renderer);
|
||||||
|
SDL_DestroyWindow(window);
|
||||||
|
SDL_Quit();
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue