juegos

Juegos de terminal de Pancho: Catan (TUI, GUI, web, servidor, PicoCalc), ajedrez, calculadora y minijuegos
git clone https://git.lu3dhn.xyz/juegos.git
Log | Files | Refs

gfx.c (6477B)


      1 /* gfx.c - primitivas: rectangulos, poligonos, lineas gruesas, circulos y texto. */
      2 #include "gfx.h"
      3 
      4 void surf_init(Surface *s, int w, int h, void (*span)(Surface *, int, int, int, Color), void *user)
      5 {
      6     s->w = w; s->h = h; s->span = span; s->user = user;
      7     s->hy0 = 0; s->hy1 = h;
      8     gfx_noclip(s);
      9 }
     10 
     11 void gfx_clip(Surface *s, int x, int y, int w, int h)
     12 {
     13     s->cx0 = x < 0 ? 0 : x;
     14     s->cy0 = y < s->hy0 ? s->hy0 : y;
     15     s->cx1 = x + w > s->w ? s->w : x + w;
     16     s->cy1 = y + h > s->hy1 ? s->hy1 : y + h;
     17 }
     18 
     19 void gfx_noclip(Surface *s) { s->cx0 = 0; s->cy0 = s->hy0; s->cx1 = s->w; s->cy1 = s->hy1; }
     20 
     21 void gfx_band(Surface *s, int y0, int y1)
     22 {
     23     s->hy0 = y0 < 0 ? 0 : y0;
     24     s->hy1 = y1 > s->h ? s->h : y1;
     25     gfx_noclip(s);
     26 }
     27 
     28 static void put(Surface *s, int x, int y, int n, Color c)
     29 {
     30     if (y < s->cy0 || y >= s->cy1 || n <= 0) return;
     31     if (x < s->cx0) { n -= s->cx0 - x; x = s->cx0; }
     32     if (x + n > s->cx1) n = s->cx1 - x;
     33     if (n > 0) s->span(s, x, y, n, c);
     34 }
     35 
     36 int isqrt(int v)
     37 {
     38     if (v <= 0) return 0;
     39     int r = 0, bit = 1 << 30;
     40     while (bit > v) bit >>= 2;
     41     while (bit) {
     42         if (v >= r + bit) { v -= r + bit; r = (r >> 1) + bit; }
     43         else r >>= 1;
     44         bit >>= 2;
     45     }
     46     return r;
     47 }
     48 
     49 static int64_t isqrt64(int64_t v)
     50 {
     51     if (v <= 0) return 0;
     52     int64_t r = (int64_t)isqrt(v > 0x7FFFFFFF ? 0x7FFFFFFF : (int)v);
     53     while (r * r > v) r--;
     54     while ((r + 1) * (r + 1) <= v) r++;
     55     return r;
     56 }
     57 
     58 void gfx_rect(Surface *s, int x, int y, int w, int h, Color c)
     59 {
     60     for (int j = 0; j < h; j++) put(s, x, y + j, w, c);
     61 }
     62 
     63 void gfx_frame(Surface *s, int x, int y, int w, int h, Color c)
     64 {
     65     put(s, x, y, w, c);
     66     put(s, x, y + h - 1, w, c);
     67     for (int j = 1; j < h - 1; j++) { put(s, x, y + j, 1, c); put(s, x + w - 1, y + j, 1, c); }
     68 }
     69 
     70 static int floordiv16(int32_t v) { return v >= 0 ? v / 16 : -((-v + 15) / 16); }
     71 static int ceildiv16(int32_t v)  { return -floordiv16(-v); }
     72 
     73 void gfx_poly16(Surface *s, const int32_t *xs, const int32_t *ys, int n, Color c)
     74 {
     75     int32_t ymin = ys[0], ymax = ys[0];
     76     for (int i = 1; i < n; i++) {
     77         if (ys[i] < ymin) ymin = ys[i];
     78         if (ys[i] > ymax) ymax = ys[i];
     79     }
     80     int py0 = floordiv16(ymin), py1 = ceildiv16(ymax);
     81     if (py0 < s->cy0) py0 = s->cy0;
     82     if (py1 > s->cy1) py1 = s->cy1;
     83     for (int py = py0; py < py1; py++) {
     84         int32_t y16 = py * 16 + 8, xi[32];
     85         int k = 0;
     86         for (int i = 0; i < n && k < 32; i++) {
     87             int32_t x0 = xs[i], y0 = ys[i], x1 = xs[(i + 1) % n], y1 = ys[(i + 1) % n];
     88             if ((y0 <= y16 && y16 < y1) || (y1 <= y16 && y16 < y0))
     89                 xi[k++] = x0 + (int32_t)((int64_t)(y16 - y0) * (x1 - x0) / (y1 - y0));
     90         }
     91         for (int i = 1; i < k; i++)
     92             for (int j = i; j > 0 && xi[j] < xi[j - 1]; j--) { int32_t t = xi[j]; xi[j] = xi[j - 1]; xi[j - 1] = t; }
     93         for (int i = 0; i + 1 < k; i += 2) {
     94             int a = ceildiv16(xi[i] - 8), b = ceildiv16(xi[i + 1] - 8);
     95             put(s, a, py, b - a, c);
     96         }
     97     }
     98 }
     99 
    100 void gfx_line(Surface *s, int x0, int y0, int x1, int y1, int thick, Color c)
    101 {
    102     int64_t dx = (int64_t)(x1 - x0) * 16, dy = (int64_t)(y1 - y0) * 16;
    103     int64_t len = isqrt64(dx * dx + dy * dy);
    104     if (len == 0) { gfx_disc(s, x0, y0, thick / 2, c); return; }
    105     int32_t nx = (int32_t)(-dy * thick * 8 / len), ny = (int32_t)(dx * thick * 8 / len);
    106     int32_t X0 = x0 * 16 + 8, Y0 = y0 * 16 + 8, X1 = x1 * 16 + 8, Y1 = y1 * 16 + 8;
    107     int32_t xs[4] = { X0 + nx, X1 + nx, X1 - nx, X0 - nx };
    108     int32_t ys[4] = { Y0 + ny, Y1 + ny, Y1 - ny, Y0 - ny };
    109     gfx_poly16(s, xs, ys, 4, c);
    110     if (thick >= 3) {
    111         gfx_disc(s, x0, y0, thick / 2, c);
    112         gfx_disc(s, x1, y1, thick / 2, c);
    113     }
    114 }
    115 
    116 void gfx_disc(Surface *s, int cx, int cy, int r, Color c)
    117 {
    118     if (r <= 0) { put(s, cx, cy, 1, c); return; }
    119     for (int dy = -r; dy <= r; dy++) {
    120         int dx = isqrt(r * r + r - dy * dy);
    121         put(s, cx - dx, cy + dy, 2 * dx + 1, c);
    122     }
    123 }
    124 
    125 void gfx_ring(Surface *s, int cx, int cy, int r, int thick, Color c)
    126 {
    127     int ri = r - thick;
    128     for (int dy = -r; dy <= r; dy++) {
    129         int dxo = isqrt(r * r + r - dy * dy);
    130         if (ri < 0 || dy < -ri || dy > ri) { put(s, cx - dxo, cy + dy, 2 * dxo + 1, c); continue; }
    131         int dxi = isqrt(ri * ri + ri - dy * dy);
    132         put(s, cx - dxo, cy + dy, dxo - dxi, c);
    133         put(s, cx + dxi + 1, cy + dy, dxo - dxi, c);
    134     }
    135 }
    136 
    137 /* ------------------------------------------------------------------ texto */
    138 
    139 int utf8_next(const char **ps)
    140 {
    141     const unsigned char *s = (const unsigned char *)*ps;
    142     int c = *s;
    143     if (!c) return 0;
    144     if (c < 0x80) { *ps += 1; return c; }
    145     if ((c & 0xE0) == 0xC0 && s[1]) {
    146         *ps += 2;
    147         int cp = ((c & 0x1F) << 6) | (s[1] & 0x3F);
    148         return cp < 256 ? cp : '?';
    149     }
    150     if ((c & 0xF0) == 0xE0 && s[1] && s[2]) {
    151         *ps += 3;
    152         int cp = ((c & 0x0F) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F);
    153         switch (cp) {
    154         case 0x2192: return 0xBB;   /* → como » */
    155         case 0x2190: return 0xAB;
    156         case 0x2014: case 0x2013: return '-';
    157         case 0x2022: return 0xB7;
    158         case 0x2026: return '.';
    159         }
    160         return '?';
    161     }
    162     *ps += 1;
    163     while ((**ps & 0xC0) == 0x80) (*ps)++;
    164     return '?';
    165 }
    166 
    167 static void glyph(Surface *s, const Font *f, int x, int y, int ch, Color c)
    168 {
    169     if (ch < 32 || ch > 255) ch = '?';
    170     const uint8_t *g = f->bits + (ch - 32) * f->h;
    171     for (int row = 0; row < f->h; row++) {
    172         uint8_t b = g[row];
    173         int col = 0;
    174         while (b && col < 8) {
    175             if (b & 0x80) {
    176                 int start = col;
    177                 while (col < 8 && (b & 0x80)) { b <<= 1; col++; }
    178                 put(s, x + start, y + row, col - start, c);
    179             } else { b <<= 1; col++; }
    180         }
    181     }
    182 }
    183 
    184 int gfx_text(Surface *s, const Font *f, int x, int y, const char *t, Color c)
    185 {
    186     int x0 = x, ch;
    187     while ((ch = utf8_next(&t)) != 0) {
    188         glyph(s, f, x, y, ch, c);
    189         x += f->w;
    190     }
    191     return x - x0;
    192 }
    193 
    194 int gfx_text_w(const Font *f, const char *t)
    195 {
    196     int n = 0;
    197     while (utf8_next(&t)) n++;
    198     return n * f->w;
    199 }
    200 
    201 int gfx_text_fit(Surface *s, const Font *f, int x, int y, int maxw, const char *t, Color c)
    202 {
    203     int x0 = x, ch;
    204     while ((ch = utf8_next(&t)) != 0) {
    205         if (x + f->w > x0 + maxw) break;
    206         glyph(s, f, x, y, ch, c);
    207         x += f->w;
    208     }
    209     return x - x0;
    210 }