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 }