487 lines
17 KiB
C++
487 lines
17 KiB
C++
#include "draw.h"
|
|
#include "mini/surf/surf.h"
|
|
#include "mini/view/view.h"
|
|
#include "mini/font/font.h"
|
|
|
|
#include <math.h>
|
|
|
|
namespace mini
|
|
{
|
|
namespace draw
|
|
{
|
|
state_t state;
|
|
|
|
void init() {
|
|
state.mode = DRAWMODE_NORMAL;
|
|
state.fill_pattern = 0b1111111111111111;
|
|
}
|
|
|
|
void quit() {
|
|
|
|
}
|
|
|
|
|
|
namespace pixel {
|
|
static inline void pset_fast(int x, int y, uint8_t color) {
|
|
auto &s = surf::state.surfaces[surf::state.dest_surface];
|
|
uint8_t *p = s.p;
|
|
if (state.trans != color) p[x+y*s.w] = color;
|
|
}
|
|
|
|
static inline void pset_bool(int x, int y, uint8_t color) {
|
|
if (state.trans != color) {
|
|
auto &s = surf::state.surfaces[surf::state.dest_surface];
|
|
uint8_t *p = s.p;
|
|
switch (state.mode) {
|
|
case DRAWMODE_AND:
|
|
p[x+y*s.w] &= color;
|
|
break;
|
|
case DRAWMODE_OR:
|
|
p[x+y*s.w] |= color;
|
|
break;
|
|
case DRAWMODE_XOR:
|
|
p[x+y*s.w] ^= color;
|
|
break;
|
|
case DRAWMODE_NOT:
|
|
p[x+y*s.w] = ~p[x+y*s.w];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void pset_pattern(int x, int y, uint8_t color) {
|
|
int pbx = x % 4, pby = y % 4;
|
|
int pb = pbx+pby*4;
|
|
if (state.fill_pattern & (1 << pb))
|
|
if (state.trans != color) {
|
|
auto &s = surf::state.surfaces[surf::state.dest_surface];
|
|
uint8_t *p = s.p;
|
|
p[x+y*s.w] = color;
|
|
}
|
|
}
|
|
|
|
// Per a les funcions que pinten tot del mateix color
|
|
static inline void direct_pset(int x, int y, uint8_t color) {
|
|
auto &s = surf::state.surfaces[surf::state.dest_surface];
|
|
x += view::state.origin[0]; y += view::state.origin[1];
|
|
if (x < s.clip[0] || x > s.clip[2] || y < s.clip[1] || y > s.clip[3]) return;
|
|
switch (state.mode) {
|
|
case DRAWMODE_NORMAL: pset_fast(x,y,color); break;
|
|
case DRAWMODE_PATTERN: pset_pattern(x,y,color); break;
|
|
default: pset_bool(x,y,color); break;
|
|
}
|
|
}
|
|
|
|
// Per a les funcions que van canviant de color (surf.pixel i draw.surf, bàsicament)
|
|
static inline void subst_pset(int x, int y, uint8_t color) {
|
|
direct_pset(x, y, state.draw_palette[color]);
|
|
}
|
|
|
|
void set(int x, int y, uint8_t color) {
|
|
subst_pset(x,y,color);
|
|
}
|
|
|
|
uint8_t get(int x, int y) {
|
|
if (surf::state.source_surface==-1) return 0;
|
|
auto &s = surf::state.surfaces[surf::state.source_surface];
|
|
if (x < 0 || x > (s.w-1) || y < 0 || y > (s.h-1)) return 0;
|
|
return s.p[x+y*s.w];
|
|
}
|
|
}
|
|
|
|
// Bresenham Line Algorithm
|
|
void line(int x0, int y0, int x1, int y1, uint8_t color) {
|
|
int x, y;
|
|
int dx, dy;
|
|
int incx, incy;
|
|
int balance;
|
|
color = state.draw_palette[color];
|
|
|
|
if (x1 >= x0) { dx = x1 - x0; incx = 1; } else { dx = x0 - x1; incx = -1; }
|
|
if (y1 >= y0) { dy = y1 - y0; incy = 1; } else { dy = y0 - y1; incy = -1; }
|
|
|
|
x = x0; y = y0;
|
|
|
|
if (dx >= dy) {
|
|
dy <<= 1;
|
|
balance = dy - dx;
|
|
dx <<= 1;
|
|
|
|
while (x != x1) {
|
|
pixel::direct_pset(x, y, color);
|
|
if (balance >= 0) { y += incy; balance -= dx; }
|
|
balance += dy;
|
|
x += incx;
|
|
}
|
|
pixel::direct_pset(x, y, color);
|
|
} else {
|
|
dx <<= 1;
|
|
balance = dx - dy;
|
|
dy <<= 1;
|
|
|
|
while (y != y1) {
|
|
pixel::direct_pset(x, y, color);
|
|
if (balance >= 0) { x += incx; balance -= dy; }
|
|
balance += dx;
|
|
y += incy;
|
|
}
|
|
pixel::direct_pset(x, y, color);
|
|
}
|
|
}
|
|
|
|
void hline(int x0, int y, int x1, uint8_t color) {
|
|
color = state.draw_palette[color];
|
|
if (x0>x1) { const int tmp=x0;x0=x1;x1=tmp; }
|
|
for (int x=x0; x<=x1; ++x) pixel::direct_pset(x, y, color);
|
|
}
|
|
|
|
void vline(int x, int y0, int y1, uint8_t color) {
|
|
color = state.draw_palette[color];
|
|
if (y0>y1) { const int tmp=y0;y0=y1;y1=tmp; }
|
|
for (int y=y0; y<=y1; ++y) pixel::direct_pset(x, y, color);
|
|
}
|
|
|
|
void rect(int x, int y, int w, int h, uint8_t color) {
|
|
int x1 = w+x-1;
|
|
int y1 = h+y-1;
|
|
hline(x, y, x1, color);
|
|
hline(x, y1, x1, color);
|
|
vline(x, y, y1, color);
|
|
vline(x1, y, y1, color);
|
|
}
|
|
|
|
void rectf(int x, int y, int w, int h, uint8_t color) {
|
|
int x1 = w+x-1;
|
|
int y1 = h+y-1;
|
|
for (int i=y; i<=y1; ++i) hline(x, i, x1, color);
|
|
}
|
|
|
|
static inline void circ_scanline(int xc, int yc, int x, int y, uint8_t color) {
|
|
pixel::direct_pset(xc+x, yc+y, color);
|
|
pixel::direct_pset(xc-x, yc+y, color);
|
|
pixel::direct_pset(xc+x, yc-y, color);
|
|
pixel::direct_pset(xc-x, yc-y, color);
|
|
pixel::direct_pset(xc+y, yc+x, color);
|
|
pixel::direct_pset(xc-y, yc+x, color);
|
|
pixel::direct_pset(xc+y, yc-x, color);
|
|
pixel::direct_pset(xc-y, yc-x, color);
|
|
}
|
|
|
|
void circ(int x, int y, uint8_t r, uint8_t color) {
|
|
color = state.draw_palette[color];
|
|
int xi=0, yi=r;
|
|
int d=3-2*r;
|
|
circ_scanline(x, y, xi, yi, color);
|
|
while (yi>=xi++) {
|
|
d += d>0 ? 4*(xi-yi--)+10 : 4*xi+6;
|
|
circ_scanline(x, y, xi, yi, color);
|
|
}
|
|
}
|
|
|
|
static inline void circf_scanline(int xc, int yc, int x, int y, uint8_t color) {
|
|
hline(xc-x, yc+y, xc+x, color);
|
|
hline(xc-x, yc-y, xc+x, color);
|
|
hline(xc-y, yc+x, xc+y, color);
|
|
hline(xc-y, yc-x, xc+y, color);
|
|
}
|
|
|
|
void circf(int x, int y, uint8_t r, uint8_t color) {
|
|
int xi=0, yi=r;
|
|
int d=3-2*r;
|
|
circf_scanline(x, y, xi, yi, color);
|
|
while (yi>=xi++) {
|
|
d += d>0 ? 4*(xi-yi--)+10 : 4*xi+6;
|
|
circf_scanline(x, y, xi, yi, color);
|
|
}
|
|
}
|
|
|
|
void roundrect(int x, int y, int w, int h, uint8_t r, uint8_t color) {
|
|
int xi=0, yi=r;
|
|
int d=3-2*r;
|
|
|
|
int xf = w+x-1;
|
|
int yf = h+y-1;
|
|
int x1 = x+r, y1 = y+r;
|
|
int x2 = xf-r, y2 = yf-r;
|
|
hline(x1, y, x2, color);
|
|
hline(x1, yf, x2, color);
|
|
vline(x, y1, y2, color);
|
|
vline(xf, y1, y2, color);
|
|
|
|
color = state.draw_palette[color];
|
|
while (yi>=xi++) {
|
|
d += d>0 ? 4*(xi-yi--)+10 : 4*xi+6;
|
|
pixel::direct_pset(x2+xi, y2+yi, color);
|
|
pixel::direct_pset(x1-xi, y2+yi, color);
|
|
pixel::direct_pset(x2+xi, y1-yi, color);
|
|
pixel::direct_pset(x1-xi, y1-yi, color);
|
|
pixel::direct_pset(x2+yi, y2+xi, color);
|
|
pixel::direct_pset(x1-yi, y2+xi, color);
|
|
pixel::direct_pset(x2+yi, y1-xi, color);
|
|
pixel::direct_pset(x1-yi, y1-xi, color);
|
|
}
|
|
}
|
|
|
|
void roundrectf(int x, int y, int w, int h, uint8_t r, uint8_t color) {
|
|
int xi=0, yi=r;
|
|
int d=3-2*r;
|
|
|
|
int xf = w+x-1;
|
|
int yf = h+y-1;
|
|
int x1 = x+r, y1 = y+r;
|
|
int x2 = xf-r, y2 = yf-r;
|
|
for (int i=y1; i<=y2; ++i) hline(x, i, xf, color);
|
|
|
|
while (yi>=xi++) {
|
|
d += d>0 ? 4*(xi-yi--)+10 : 4*xi+6;
|
|
hline(x1-xi, y2+yi, x2+xi, color);
|
|
hline(x1-xi, y1-yi, x2+xi, color);
|
|
hline(x1-yi, y2+xi, x2+yi, color);
|
|
hline(x1-yi, y1-xi, x2+yi, color);
|
|
}
|
|
}
|
|
|
|
void oval_scanline(int xc, int yc, int x, int y, float xf, float yf, uint8_t color) {
|
|
pixel::direct_pset((xc+x)*xf, (yc+y)*yf, color);
|
|
pixel::direct_pset((xc-x)*xf, (yc+y)*yf, color);
|
|
pixel::direct_pset((xc+x)*xf, (yc-y)*yf, color);
|
|
pixel::direct_pset((xc-x)*xf, (yc-y)*yf, color);
|
|
pixel::direct_pset((xc+y)*xf, (yc+x)*yf, color);
|
|
pixel::direct_pset((xc-y)*xf, (yc+x)*yf, color);
|
|
pixel::direct_pset((xc+y)*xf, (yc-x)*yf, color);
|
|
pixel::direct_pset((xc-y)*xf, (yc-x)*yf, color);
|
|
}
|
|
|
|
void oval(int x0, int y0, int x1, int y1, uint8_t color) {
|
|
color = state.draw_palette[color];
|
|
int rx = (x1-x0)/2;
|
|
int ry = (y1-y0)/2;
|
|
int r = rx;
|
|
int x = x0 + rx;
|
|
int y = y0 + ry;
|
|
float xf = 1.0f, yf = 1.0f;
|
|
if (rx>=ry) {r=rx;yf=float(ry)/float(rx);} else {r=ry;xf=float(rx)/float(ry);}
|
|
int xi=0, yi=r;
|
|
int d=3-2*r;
|
|
oval_scanline(x, y, xi, yi, xf, yf, color);
|
|
while (yi>=xi++) {
|
|
d += d>0 ? 4*(xi-yi--)+10 : 4*xi+6;
|
|
oval_scanline(x, y, xi, yi, xf, yf, color);
|
|
}
|
|
}
|
|
|
|
static inline void ovalf_scanline(int xc, int yc, int x, int y, float xf, float yf, uint8_t color) {
|
|
hline((xc-x)*xf, (yc+y)*yf, (xc+x)*xf, color);
|
|
hline((xc-x)*xf, (yc-y)*yf, (xc+x)*xf, color);
|
|
hline((xc-y)*xf, (yc+x)*yf, (xc+y)*xf, color);
|
|
hline((xc-y)*xf, (yc-x)*yf, (xc+y)*xf, color);
|
|
}
|
|
|
|
void ovalf(int x0, int y0, int x1, int y1, uint8_t color) {
|
|
int rx = (x1-x0)/2;
|
|
int ry = (y1-y0)/2;
|
|
int r = rx;
|
|
int x = x0 + rx;
|
|
int y = y0 + ry;
|
|
float xf = 1.0f, yf = 1.0f;
|
|
if (rx>=ry) {r=rx;yf=float(ry)/float(rx);} else {r=ry;xf=float(rx)/float(ry);}
|
|
int xi=0, yi=r;
|
|
int d=3-2*r;
|
|
ovalf_scanline(x, y, xi, yi, xf, yf, color);
|
|
while (yi>=xi++) {
|
|
d += d>0 ? 4*(xi-yi--)+10 : 4*xi+6;
|
|
ovalf_scanline(x, y, xi, yi, xf, yf, color);
|
|
}
|
|
}
|
|
|
|
namespace pattern {
|
|
void set(uint16_t pat, bool transparent) {
|
|
state.fill_pattern = pat;
|
|
}
|
|
}
|
|
|
|
void surf(int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, bool flip_x, bool flip_y, bool invert) {
|
|
if (dw == 0) dw = sw;
|
|
if (dh == 0) dh = sh;
|
|
|
|
// 16.16 fixed point
|
|
int sdx = (sw << 16) / dw;
|
|
int sdy = (sh << 16) / dh;
|
|
|
|
if (flip_x) sdx = -sdx;
|
|
if (flip_y) sdy = -sdy;
|
|
|
|
int ssx = flip_x ? ((sx + sw - 1) << 16) : (sx << 16);
|
|
int ssy = flip_y ? ((sy + sh - 1) << 16) : (sy << 16);
|
|
|
|
int csy = ssy;
|
|
|
|
if (!invert) {
|
|
for (int y = dy; y < dy + dh; ++y) {
|
|
int csx = ssx;
|
|
for (int x = dx; x < dx + dw; ++x) {
|
|
uint8_t color = pixel::get(csx >> 16, csy >> 16);
|
|
pixel::subst_pset(x, y, color);
|
|
csx += sdx;
|
|
}
|
|
csy += sdy;
|
|
}
|
|
} else {
|
|
for (int y = dy; y < dy + dh; ++y) {
|
|
int csx = ssx;
|
|
for (int x = dx; x < dx + dw; ++x) {
|
|
uint8_t color = pixel::get(csy >> 16, csx >> 16);
|
|
pixel::subst_pset(x, y, color);
|
|
csx += sdx;
|
|
}
|
|
csy += sdy;
|
|
}
|
|
}
|
|
}
|
|
|
|
void surfrot(int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, bool flip_x, bool flip_y, float angle_deg) {
|
|
if (dw == 0) dw = sw;
|
|
if (dh == 0) dh = sh;
|
|
|
|
// Centro del destino (rectángulo sin rotar)
|
|
float dcx = dx + dw * 0.5f;
|
|
float dcy = dy + dh * 0.5f;
|
|
|
|
// Centro del subrectángulo origen
|
|
float scx = sx + sw * 0.5f;
|
|
float scy = sy + sh * 0.5f;
|
|
|
|
// Escalado destino -> origen
|
|
float inv_scale_x = float(sw) / float(dw);
|
|
float inv_scale_y = float(sh) / float(dh);
|
|
|
|
// Flips integrados en la escala
|
|
if (flip_x) inv_scale_x = -inv_scale_x;
|
|
if (flip_y) inv_scale_y = -inv_scale_y;
|
|
|
|
// Ángulo en radianes
|
|
float a = angle_deg * 3.14159265f / 180.0f;
|
|
float ca = cosf(a);
|
|
float sa = sinf(a);
|
|
|
|
// --- 1. Bounding box rotado del rectángulo destino ---
|
|
|
|
float hx = dw * 0.5f;
|
|
float hy = dh * 0.5f;
|
|
|
|
float vx[4] = { -hx, hx, -hx, hx };
|
|
float vy[4] = { -hy, -hy, hy, hy };
|
|
|
|
float min_x = 1e9f, max_x = -1e9f;
|
|
float min_y = 1e9f, max_y = -1e9f;
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
float rr_x = vx[i] * ca - vy[i] * sa;
|
|
float rr_y = vx[i] * sa + vy[i] * ca;
|
|
|
|
float dxp = dcx + rr_x;
|
|
float dyp = dcy + rr_y;
|
|
|
|
if (dxp < min_x) min_x = dxp;
|
|
if (dxp > max_x) max_x = dxp;
|
|
if (dyp < min_y) min_y = dyp;
|
|
if (dyp > max_y) max_y = dyp;
|
|
}
|
|
|
|
int bb_x0 = (int)floorf(min_x);
|
|
int bb_x1 = (int)ceilf (max_x);
|
|
int bb_y0 = (int)floorf(min_y);
|
|
int bb_y1 = (int)ceilf (max_y);
|
|
|
|
// --- 2. Rotación inversa + escalado + clipping estricto ---
|
|
|
|
for (int y = bb_y0; y <= bb_y1; ++y) {
|
|
for (int x = bb_x0; x <= bb_x1; ++x) {
|
|
|
|
// Coordenadas relativas al centro destino
|
|
float rx = x - dcx;
|
|
float ry = y - dcy;
|
|
|
|
// Rotación inversa
|
|
float ux = rx * ca + ry * sa;
|
|
float uy = -rx * sa + ry * ca;
|
|
|
|
// Escalado destino -> origen (con flips)
|
|
float sxp = scx + ux * inv_scale_x;
|
|
float syp = scy + uy * inv_scale_y;
|
|
|
|
// Clipping estricto al subrectángulo origen
|
|
if (sxp < sx || sxp >= sx + sw ||
|
|
syp < sy || syp >= sy + sh)
|
|
continue; // no pintamos nada
|
|
|
|
uint8_t color = pixel::get((int)sxp, (int)syp);
|
|
pixel::subst_pset(x, y, color);
|
|
}
|
|
}
|
|
}
|
|
|
|
void tileblit(uint8_t n, int x, int y, int tw, int th) {
|
|
//const int tw = tile_width;
|
|
//const int th = tile_height;
|
|
|
|
const int tiles_per_row = mini::surf::state.surfaces[mini::surf::state.source_surface].w / tw;
|
|
|
|
// Coordenadas del tile dentro del spritesheet
|
|
int tx = (n % tiles_per_row) * tw;
|
|
int ty = (n / tiles_per_row) * th;
|
|
|
|
int src_y = ty;
|
|
|
|
for (int yi = 0; yi < th; ++yi) {
|
|
int src_x = tx;
|
|
|
|
for (int xi = 0; xi < tw; ++xi) {
|
|
uint8_t c = mini::draw::pixel::get(src_x, src_y);
|
|
mini::draw::pixel::subst_pset(x + xi, y + yi, c);
|
|
src_x++;
|
|
}
|
|
|
|
src_y++;
|
|
}
|
|
}
|
|
|
|
const uint8_t printchar(uint8_t c, int x, int y) {
|
|
font::char_t &chr = font::current_font->chars[c];
|
|
draw::surf(chr.x, chr.y, chr.w, chr.h, x, y-chr.base);
|
|
return chr.w;
|
|
}
|
|
|
|
void text(const char* str, int x, int y, uint8_t color) {
|
|
|
|
const unsigned char* p = (const unsigned char*)str;
|
|
int xpos = x;
|
|
|
|
uint8_t old_source = surf::source::get();
|
|
surf::source::set(font::current_font->surface);
|
|
uint8_t old_color = state.draw_palette[1];
|
|
state.draw_palette[1] = color;
|
|
uint8_t old_trans = state.trans;
|
|
state.trans = 0;
|
|
while (p[0]!=0) {
|
|
uint8_t cp = p[0];
|
|
if (p[0] < 0x80) {
|
|
p+=1;
|
|
} else if ((p[0] & 0xE0) == 0xC0) {
|
|
cp = (p[0] << 6) | (p[1] & 0x3F);
|
|
p+=2;
|
|
}
|
|
xpos += printchar(cp, xpos, y) + font::current_font->spacing;
|
|
}
|
|
state.trans = old_trans;
|
|
state.draw_palette[1] = old_color;
|
|
surf::source::set(old_source);
|
|
}
|
|
|
|
namespace mode {
|
|
void set(uint8_t mode) {
|
|
state.mode = mode;
|
|
}
|
|
}
|
|
}
|
|
} |