390 lines
9.9 KiB
C
390 lines
9.9 KiB
C
#include <assert.h>
|
|
#include "sys.h"
|
|
#include "device/device.h"
|
|
|
|
sys_i32 sys_cam_dx, sys_cam_dy;
|
|
sys_i32 sys_clip_x0, sys_clip_y0, sys_clip_x1, sys_clip_y1;
|
|
sys_color sys_dpal[256];
|
|
|
|
// primitives (forward declare)
|
|
void sys_pixel_internal_set(sys_i32 x, sys_i32 y, sys_color c);
|
|
void sys_scanline_internal_set(
|
|
sys_i32 x0, sys_i32 x1, sys_i32 y, sys_color c, bool fill
|
|
);
|
|
void sys_glyph_internal_draw(sys_i32 x, sys_i32 y, sys_glyph g, sys_color c);
|
|
void sys_sprite_internal_draw(sys_i32 x, sys_i32 y, sys_sprite s, bool flip_x, bool flip_y);
|
|
|
|
// == public ==
|
|
void sys_clip_set(sys_i32 x0, sys_i32 y0, sys_i32 x1, sys_i32 y1) {
|
|
assert(sys_get_initialized());
|
|
|
|
sys_clip_set_ext(x0, y0, x1, y1, false);
|
|
}
|
|
|
|
void sys_clip_reset() {
|
|
assert(sys_get_initialized());
|
|
|
|
sys_clip_set(0, 0, DEVICE_W, DEVICE_H);
|
|
}
|
|
|
|
void sys_clip_set_ext(sys_i32 x0, sys_i32 y0, sys_i32 x1, sys_i32 y1, bool clip_previous) {
|
|
assert(sys_get_initialized());
|
|
sys_clip_x0 = sys_max_i32(x0, clip_previous ? sys_clip_x0 : 0);
|
|
sys_clip_y0 = sys_max_i32(y0, clip_previous ? sys_clip_y0 : 0);
|
|
sys_clip_x1 = sys_min_i32(x1, clip_previous ? sys_clip_x1 : DEVICE_W);
|
|
sys_clip_y1 = sys_min_i32(y1, clip_previous ? sys_clip_y1 : DEVICE_H);
|
|
}
|
|
|
|
void sys_pixel_set(sys_i32 x, sys_i32 y, sys_color c) {
|
|
assert(sys_get_initialized());
|
|
|
|
x += sys_cam_dx;
|
|
y += sys_cam_dy;
|
|
|
|
sys_pixel_internal_set(x, y, c);
|
|
}
|
|
|
|
sys_color sys_pixel_get(sys_i32 x, sys_i32 y) {
|
|
assert(sys_get_initialized());
|
|
|
|
x += sys_cam_dx;
|
|
y += sys_cam_dy;
|
|
|
|
if (x < 0 || y < 0 || x >= DEVICE_W || y >= DEVICE_H) {
|
|
return SYS_COLOR_TRANSPARENT;
|
|
}
|
|
|
|
return device_pixels[y][x];
|
|
}
|
|
|
|
void sys_print(char* str, sys_i32 x, sys_i32 y, sys_color col) {
|
|
x += sys_cam_dx;
|
|
y += sys_cam_dy;
|
|
|
|
sys_i32 x_orig = x;
|
|
for (sys_i32 i = 0;; i++) {
|
|
uint8_t c = str[i];
|
|
if (c == 0) { break; }
|
|
if (c == '\n') { x = x_orig; y += 8; continue; }
|
|
if (c == '\r') { x = x_orig; continue; }
|
|
sys_glyph_internal_draw(x, y, font_sys_font_small[c], col);
|
|
x += 8;
|
|
}
|
|
}
|
|
|
|
void sys_cls(sys_color c) {
|
|
assert(sys_get_initialized());
|
|
|
|
for (int x = 0; x < DEVICE_W; x++) {
|
|
for (int y = 0; y < DEVICE_W; y++) {
|
|
sys_pixel_internal_set(x, y, c);
|
|
}
|
|
}
|
|
}
|
|
|
|
void sys_camera_set(sys_i32 x, sys_i32 y) {
|
|
assert(sys_get_initialized());
|
|
|
|
sys_cam_dx = -x;
|
|
sys_cam_dy = -y;
|
|
}
|
|
|
|
void sys_camera_reset() {
|
|
assert(sys_get_initialized());
|
|
|
|
sys_camera_set(0, 0);
|
|
}
|
|
|
|
// Based on bresenham implementations by alois zingl
|
|
// https://zingl.github.io/bresenham.html
|
|
void sys_circ_draw(sys_i32 x, sys_i32 y, sys_i32 r, sys_color c) {
|
|
assert(sys_get_initialized());
|
|
|
|
sys_circ_draw_ext(x, y, r, c, false);
|
|
}
|
|
|
|
void sys_circ_fill(sys_i32 x, sys_i32 y, sys_i32 r, sys_color c) {
|
|
assert(sys_get_initialized());
|
|
|
|
sys_circ_draw_ext(x, y, r, c, true);
|
|
}
|
|
|
|
void sys_circ_draw_ext(
|
|
sys_i32 x, sys_i32 y, sys_i32 r, sys_color c, bool fill
|
|
) {
|
|
assert(sys_get_initialized());
|
|
|
|
if (r < 0) { return; }
|
|
|
|
sys_oval_draw_ext(x - r, y - r, x + r + 1, y + r + 1, c, fill);
|
|
}
|
|
|
|
void sys_oval_draw(
|
|
sys_i32 x0, sys_i32 y0, sys_i32 x1, sys_i32 y1, sys_color c
|
|
) {
|
|
assert(sys_get_initialized());
|
|
|
|
sys_oval_draw_ext(x0, y0, x1, y1, c, false);
|
|
}
|
|
|
|
void sys_oval_fill(
|
|
sys_i32 x0, sys_i32 y0, sys_i32 x1, sys_i32 y1, sys_color c
|
|
) {
|
|
assert(sys_get_initialized());
|
|
|
|
sys_oval_draw_ext(x0, y0, x1, y1, c, true);
|
|
}
|
|
|
|
void sys_oval_draw_ext(
|
|
sys_i32 x0, sys_i32 y0, sys_i32 x1, sys_i32 y1, sys_color c, bool fill
|
|
) {
|
|
x0 += sys_cam_dx;
|
|
y0 += sys_cam_dy;
|
|
x1 += sys_cam_dx;
|
|
y1 += sys_cam_dy;
|
|
|
|
assert(sys_get_initialized());
|
|
|
|
if (x0 == x1 || y0 == y1) { return; }
|
|
if (x0 > x1) { sys_i32 tmp = x0; x0 = x1; x1 = tmp; }
|
|
if (y0 > y1) { sys_i32 tmp = y0; y0 = y1; y1 = tmp; }
|
|
|
|
// alois' algorithm for this implies the bounds are inclusive
|
|
x1 -= 1; y1 -= 1;
|
|
|
|
int64_t a = x1 - x0;
|
|
int64_t b = y1 - y0;
|
|
int64_t b1 = b & 1;
|
|
|
|
int64_t dx = 4 * (1 - a) * b * b;
|
|
int64_t dy = 4 * (b1 + 1) * a * a;
|
|
int64_t err = dx + dy + b1 * a * a;
|
|
|
|
y0 += (b + 1) / 2;
|
|
y1 = y0 - b1;
|
|
int64_t aa8 = 8 * a * a;
|
|
int64_t bb8 = 8 * b * b;
|
|
|
|
do {
|
|
// draw the points at the edge of the line no matter what
|
|
sys_scanline_internal_set(x0, x1, y0, c, false);
|
|
sys_scanline_internal_set(x0, x1, y1, c, false);
|
|
int64_t e2 = 2 * err;
|
|
if (e2 <= dy) {
|
|
if (fill) {
|
|
// draw the whole line
|
|
sys_scanline_internal_set(x0, x1, y0, c, true);
|
|
sys_scanline_internal_set(x0, x1, y1, c, true);
|
|
}
|
|
y0++; y1--; dy += aa8; err += dy;
|
|
}
|
|
if (e2 >= dx || 2 * err > dy) {
|
|
x0++; x1--; dx += bb8; err += dx;
|
|
}
|
|
} while (x0 <= x1);
|
|
|
|
while (y0 - y1 < b) {
|
|
sys_scanline_internal_set(x0 - 1, x1 + 1, y0, c, fill); y0++;
|
|
sys_scanline_internal_set(x0 - 1, x1 + 1, y1, c, fill); y1--;
|
|
}
|
|
}
|
|
|
|
void sys_line_draw(
|
|
sys_i32 x0, sys_i32 y0, sys_i32 x1, sys_i32 y1, sys_color c
|
|
) {
|
|
x0 += sys_cam_dx;
|
|
y0 += sys_cam_dy;
|
|
x1 += sys_cam_dx;
|
|
y1 += sys_cam_dy;
|
|
|
|
assert(sys_get_initialized());
|
|
|
|
if (x0 == x1 || y0 == y1) { return; }
|
|
|
|
sys_i32 dx = sys_abs_i32(x1 - x0);
|
|
sys_i32 sx = sys_sgn_i32(x1 - x0);
|
|
sys_i32 dy = -sys_abs_i32(y1 - y0);
|
|
sys_i32 sy = sys_sgn_i32(y1 - y0);
|
|
sys_i32 err = dx + dy;
|
|
|
|
while (true) {
|
|
if (x0 == x1 || y0 == y1) { return; }
|
|
sys_pixel_internal_set(x0, y0, c);
|
|
sys_i32 e2 = 2 * err;
|
|
if (e2 >= dy) { err += dy; x0 += sx; }
|
|
if (e2 <= dx) { err += dx; y0 += sy; }
|
|
}
|
|
}
|
|
|
|
void sys_spal_set(sys_color c0, sys_screen_color rc1) {
|
|
assert(sys_get_initialized());
|
|
|
|
device_palette[c0] = rc1;
|
|
}
|
|
|
|
void sys_spal_reset() {
|
|
assert(sys_get_initialized());
|
|
|
|
for (int i = 0; i < SYS_COLOR_N; i++) {
|
|
sys_spal_set(i, 0x000000ff);
|
|
}
|
|
}
|
|
|
|
void sys_dpal_set(sys_color c0, sys_color c1) {
|
|
assert(sys_get_initialized());
|
|
|
|
sys_dpal[c0] = c1;
|
|
}
|
|
|
|
void sys_dpal_reset() {
|
|
assert(sys_get_initialized());
|
|
|
|
for (int i = 0; i < SYS_COLOR_N; i++) {
|
|
sys_dpal_set(i, i);
|
|
}
|
|
}
|
|
|
|
void sys_sprite_draw(
|
|
sys_spritesheet spritesheet,
|
|
sys_i32 n,
|
|
sys_i32 x, sys_i32 y
|
|
) {
|
|
sys_sprite_draw_ext(
|
|
spritesheet,
|
|
n,
|
|
x, y,
|
|
1, 1,
|
|
false, false
|
|
);
|
|
}
|
|
|
|
void sys_sprite_draw_ext(
|
|
sys_spritesheet spritesheet,
|
|
sys_i32 n,
|
|
sys_i32 x, sys_i32 y,
|
|
sys_i32 w, sys_i32 h,
|
|
bool flip_x, bool flip_y
|
|
) {
|
|
x += sys_cam_dx;
|
|
y += sys_cam_dy;
|
|
|
|
// map n to a specific entity on the spritesheet
|
|
// (this is necessary for w and h)
|
|
for (int sy = 0; sy < h; sy++) {
|
|
for (int sx = 0; sx < w; sx++) {
|
|
sys_i32 sx_adj = flip_x ? w - 1 - sx : sx;
|
|
sys_i32 sy_adj = flip_y ? h - 1 - sy : sy;
|
|
sys_i32 tile =
|
|
n + sx_adj + sy_adj * spritesheet.width;
|
|
if (tile < 0 || tile >= spritesheet.width * spritesheet.height) {
|
|
continue;
|
|
}
|
|
sys_sprite_internal_draw(
|
|
x + sx * 8,
|
|
y + sy * 8,
|
|
spritesheet.sprites[tile],
|
|
flip_x,
|
|
flip_y
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
void sys_map_draw(
|
|
sys_map map,
|
|
sys_spritesheet spritesheet,
|
|
sys_i32 sx, sys_i32 sy,
|
|
sys_i32 tile_x, sys_i32 tile_y,
|
|
sys_i32 tile_w, sys_i32 tile_h
|
|
) {
|
|
// no need to do this, sys_sprite_draw does it
|
|
// sx += sys_cam_dx;
|
|
// sy += sys_cam_dy;
|
|
|
|
for (sys_i32 ty = 0; ty < tile_h; ty++) {
|
|
for (sys_i32 tx = 0; tx < tile_w; tx++) {
|
|
sys_i32 real_tx = tx + tile_x;
|
|
sys_i32 real_ty = ty + tile_y;
|
|
if (real_tx < 0 || real_tx >= map.width) { continue; }
|
|
if (real_ty < 0 || real_ty >= map.height) { continue; }
|
|
sys_maptile tile = map.tiles[real_tx + map.width * real_ty];
|
|
sys_sprite_draw(spritesheet, tile, sx + tx * 8, sy + ty * 8);
|
|
}
|
|
}
|
|
}
|
|
|
|
sys_maptile sys_map_get(
|
|
sys_map map,
|
|
sys_i32 tile_x, sys_i32 tile_y,
|
|
bool* flag_invalid
|
|
) {
|
|
if (flag_invalid != NULL) { *flag_invalid = false; }
|
|
|
|
if (
|
|
tile_x < 0 || tile_y < 0 ||
|
|
tile_x >= map.width || tile_y >= map.height
|
|
) {
|
|
if (flag_invalid != NULL) { *flag_invalid = true; }
|
|
return 255;
|
|
}
|
|
|
|
return map.tiles[tile_x + map.width * tile_y];
|
|
}
|
|
|
|
// == internal primitives ==
|
|
void sys_pixel_internal_set(sys_i32 x, sys_i32 y, sys_color c) {
|
|
sys_color realc = sys_dpal[c];
|
|
if (realc == SYS_COLOR_TRANSPARENT) { return; }
|
|
|
|
if (
|
|
x < sys_clip_x0 || y < sys_clip_y0 ||
|
|
x >= sys_clip_x1 || y >= sys_clip_y1
|
|
) {
|
|
return;
|
|
}
|
|
|
|
assert(x >= 0);
|
|
assert(y >= 0);
|
|
assert(x < DEVICE_W);
|
|
assert(y < DEVICE_H);
|
|
|
|
device_pixels[y][x] = realc;
|
|
}
|
|
void sys_scanline_internal_set(
|
|
sys_i32 x0, sys_i32 x1, sys_i32 y, sys_color c,
|
|
bool fill
|
|
) {
|
|
if (fill) {
|
|
for (sys_i32 x = x0; x <= x1; x++) {
|
|
sys_pixel_internal_set(x, y, c);
|
|
}
|
|
} else {
|
|
sys_pixel_internal_set(x0, y, c);
|
|
sys_pixel_internal_set(x1, y, c);
|
|
}
|
|
}
|
|
|
|
void sys_glyph_internal_draw(sys_i32 x, sys_i32 y, sys_glyph g, sys_color c) {
|
|
// iterate through the bits of the glyph, and draw the character
|
|
// if that bit is set
|
|
for (int py = 0; py < 8; py++) {
|
|
for (int px = 0; px < 8; px++) {
|
|
uint64_t mask = ((uint64_t) 1) << (uint64_t) (py * 8 + px);
|
|
if ((g & mask) != 0) {
|
|
sys_pixel_internal_set(x + px, y + py, c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void sys_sprite_internal_draw(
|
|
sys_i32 x, sys_i32 y, sys_sprite s, bool flip_x, bool flip_y
|
|
) {
|
|
for (int py = 0; py < 8; py++) {
|
|
for (int px = 0; px < 8; px++) {
|
|
sys_i32 px_adj = flip_x ? 7 - px : px;
|
|
sys_i32 py_adj = flip_y ? 7 - py : py;
|
|
sys_pixel_internal_set(x + px, y + py, s.pixels[py_adj][px_adj]);
|
|
}
|
|
}
|
|
} |