A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 401 lines 11 kB view raw
1/* 2 * drawing.c: Intermediary between the drawing interface as 3 * presented to the back end, and that implemented by the front 4 * end. 5 * 6 * Mostly just looks up calls in a vtable and passes them through 7 * unchanged. However, on the printing side it tracks print colours 8 * so the front end API doesn't have to. 9 * 10 * FIXME: 11 * 12 * - I'd _like_ to do automatic draw_updates, but it's a pain for 13 * draw_text in particular. I'd have to invent a front end API 14 * which retrieved the text bounds. 15 * + that might allow me to do the alignment centrally as well? 16 * * perhaps not, because PS can't return this information, 17 * so there would have to be a special case for it. 18 * + however, that at least doesn't stand in the way of using 19 * the text bounds for draw_update, because PS doesn't need 20 * draw_update since it's printing-only. Any _interactive_ 21 * drawing API couldn't get away with refusing to tell you 22 * what parts of the screen a text draw had covered, because 23 * you would inevitably need to erase it later on. 24 */ 25 26#include <stdio.h> 27#include <stdlib.h> 28#include <string.h> 29#include <assert.h> 30#ifdef NO_TGMATH_H 31# include <math.h> 32#else 33# include <tgmath.h> 34#endif 35 36#include "puzzles.h" 37 38struct print_colour { 39 int hatch; 40 int hatch_when; /* 0=never 1=only-in-b&w 2=always */ 41 float r, g, b; 42 float grey; 43}; 44 45typedef struct drawing_internal { 46 /* we implement data hiding by casting `struct drawing*` pointers 47 * to `struct drawing_internal*` */ 48 struct drawing pub; 49 50 /* private data */ 51 struct print_colour *colours; 52 int ncolours, coloursize; 53 float scale; 54 /* `me' is only used in status_bar(), so print-oriented instances of 55 * this may set it to NULL. */ 56 midend *me; 57 char *laststatus; 58} drawing_internal; 59 60#define PRIVATE_CAST(dr) ((drawing_internal*)(dr)) 61#define PUBLIC_CAST(dri) ((drawing*)(dri)) 62 63/* See puzzles.h for a description of the version number. */ 64#define DRAWING_API_VERSION 1 65 66drawing *drawing_new(const drawing_api *api, midend *me, void *handle) 67{ 68 if(api->version != DRAWING_API_VERSION) { 69 fatal("Drawing API version mismatch: expected: %d, actual: %d\n", DRAWING_API_VERSION, api->version); 70 /* shouldn't get here */ 71 return NULL; 72 } 73 74 drawing_internal *dri = snew(drawing_internal); 75 dri->pub.api = api; 76 dri->pub.handle = handle; 77 dri->colours = NULL; 78 dri->ncolours = dri->coloursize = 0; 79 dri->scale = 1.0F; 80 dri->me = me; 81 dri->laststatus = NULL; 82 return PUBLIC_CAST(dri); 83} 84 85void drawing_free(drawing *dr) 86{ 87 drawing_internal *dri = PRIVATE_CAST(dr); 88 sfree(dri->laststatus); 89 sfree(dri->colours); 90 sfree(dri); 91} 92 93void draw_text(drawing *dr, int x, int y, int fonttype, int fontsize, 94 int align, int colour, const char *text) 95{ 96 drawing_internal *dri = PRIVATE_CAST(dr); 97 dri->pub.api->draw_text(dr, x, y, fonttype, fontsize, align, 98 colour, text); 99} 100 101void draw_rect(drawing *dr, int x, int y, int w, int h, int colour) 102{ 103 drawing_internal *dri = PRIVATE_CAST(dr); 104 dri->pub.api->draw_rect(dr, x, y, w, h, colour); 105} 106 107void draw_line(drawing *dr, int x1, int y1, int x2, int y2, int colour) 108{ 109 drawing_internal *dri = PRIVATE_CAST(dr); 110 dri->pub.api->draw_line(dr, x1, y1, x2, y2, colour); 111} 112 113void draw_thick_line(drawing *dr, float thickness, 114 float x1, float y1, float x2, float y2, int colour) 115{ 116 drawing_internal *dri = PRIVATE_CAST(dr); 117 if (thickness < 1.0F) 118 thickness = 1.0F; 119 if (dri->pub.api->draw_thick_line) { 120 dri->pub.api->draw_thick_line(dr, thickness, 121 x1, y1, x2, y2, colour); 122 } else { 123 /* We'll fake it up with a filled polygon. The tweak to the 124 * thickness empirically compensates for rounding errors, because 125 * polygon rendering uses integer coordinates. 126 */ 127 float len = sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1)); 128 float tvhatx = (x2 - x1)/len * (thickness/2 - 0.2F); 129 float tvhaty = (y2 - y1)/len * (thickness/2 - 0.2F); 130 int p[8]; 131 132 p[0] = x1 - tvhaty; 133 p[1] = y1 + tvhatx; 134 p[2] = x2 - tvhaty; 135 p[3] = y2 + tvhatx; 136 p[4] = x2 + tvhaty; 137 p[5] = y2 - tvhatx; 138 p[6] = x1 + tvhaty; 139 p[7] = y1 - tvhatx; 140 dri->pub.api->draw_polygon(dr, p, 4, colour, colour); 141 } 142} 143 144void draw_polygon(drawing *dr, const int *coords, int npoints, 145 int fillcolour, int outlinecolour) 146{ 147 drawing_internal *dri = PRIVATE_CAST(dr); 148 dri->pub.api->draw_polygon(dr, coords, npoints, fillcolour, 149 outlinecolour); 150} 151 152void draw_circle(drawing *dr, int cx, int cy, int radius, 153 int fillcolour, int outlinecolour) 154{ 155 drawing_internal *dri = PRIVATE_CAST(dr); 156 dri->pub.api->draw_circle(dr, cx, cy, radius, fillcolour, 157 outlinecolour); 158} 159 160void draw_update(drawing *dr, int x, int y, int w, int h) 161{ 162 drawing_internal *dri = PRIVATE_CAST(dr); 163 if (dri->pub.api->draw_update) 164 dri->pub.api->draw_update(dr, x, y, w, h); 165} 166 167void clip(drawing *dr, int x, int y, int w, int h) 168{ 169 drawing_internal *dri = PRIVATE_CAST(dr); 170 dri->pub.api->clip(dr, x, y, w, h); 171} 172 173void unclip(drawing *dr) 174{ 175 drawing_internal *dri = PRIVATE_CAST(dr); 176 dri->pub.api->unclip(dr); 177} 178 179void start_draw(drawing *dr) 180{ 181 drawing_internal *dri = PRIVATE_CAST(dr); 182 dri->pub.api->start_draw(dr); 183} 184 185void end_draw(drawing *dr) 186{ 187 drawing_internal *dri = PRIVATE_CAST(dr); 188 dri->pub.api->end_draw(dr); 189} 190 191char *text_fallback(drawing *dr, const char *const *strings, int nstrings) 192{ 193 drawing_internal *dri = PRIVATE_CAST(dr); 194 int i; 195 196 /* 197 * If the drawing implementation provides one of these, use it. 198 */ 199 if (dr && dri->pub.api->text_fallback) 200 return dri->pub.api->text_fallback(dr, strings, nstrings); 201 202 /* 203 * Otherwise, do the simple thing and just pick the first string 204 * that fits in plain ASCII. It will then need no translation 205 * out of UTF-8. 206 */ 207 for (i = 0; i < nstrings; i++) { 208 const char *p; 209 210 for (p = strings[i]; *p; p++) 211 if (*p & 0x80) 212 break; 213 if (!*p) 214 return dupstr(strings[i]); 215 } 216 217 /* 218 * The caller was responsible for making sure _some_ string in 219 * the list was in plain ASCII. 220 */ 221 assert(!"Should never get here"); 222 return NULL; /* placate optimiser */ 223} 224 225void status_bar(drawing *dr, const char *text) 226{ 227 drawing_internal *dri = PRIVATE_CAST(dr); 228 char *rewritten; 229 230 if (!dri->pub.api->status_bar) 231 return; 232 233 assert(dri->me); 234 235 rewritten = midend_rewrite_statusbar(dri->me, text); 236 if (!dri->laststatus || strcmp(rewritten, dri->laststatus)) { 237 dri->pub.api->status_bar(dr, rewritten); 238 sfree(dri->laststatus); 239 dri->laststatus = rewritten; 240 } else { 241 sfree(rewritten); 242 } 243} 244 245blitter *blitter_new(drawing *dr, int w, int h) 246{ 247 drawing_internal *dri = PRIVATE_CAST(dr); 248 return dri->pub.api->blitter_new(dr, w, h); 249} 250 251void blitter_free(drawing *dr, blitter *bl) 252{ 253 drawing_internal *dri = PRIVATE_CAST(dr); 254 dri->pub.api->blitter_free(dr, bl); 255} 256 257void blitter_save(drawing *dr, blitter *bl, int x, int y) 258{ 259 drawing_internal *dri = PRIVATE_CAST(dr); 260 dri->pub.api->blitter_save(dr, bl, x, y); 261} 262 263void blitter_load(drawing *dr, blitter *bl, int x, int y) 264{ 265 drawing_internal *dri = PRIVATE_CAST(dr); 266 dri->pub.api->blitter_load(dr, bl, x, y); 267} 268 269void print_begin_doc(drawing *dr, int pages) 270{ 271 drawing_internal *dri = PRIVATE_CAST(dr); 272 dri->pub.api->begin_doc(dr, pages); 273} 274 275void print_begin_page(drawing *dr, int number) 276{ 277 drawing_internal *dri = PRIVATE_CAST(dr); 278 dri->pub.api->begin_page(dr, number); 279} 280 281void print_begin_puzzle(drawing *dr, float xm, float xc, 282 float ym, float yc, int pw, int ph, float wmm, 283 float scale) 284{ 285 drawing_internal *dri = PRIVATE_CAST(dr); 286 dri->scale = scale; 287 dri->ncolours = 0; 288 dri->pub.api->begin_puzzle(dr, xm, xc, ym, yc, pw, ph, wmm); 289} 290 291void print_end_puzzle(drawing *dr) 292{ 293 drawing_internal *dri = PRIVATE_CAST(dr); 294 dri->pub.api->end_puzzle(dr); 295 dri->scale = 1.0F; 296} 297 298void print_end_page(drawing *dr, int number) 299{ 300 drawing_internal *dri = PRIVATE_CAST(dr); 301 dri->pub.api->end_page(dr, number); 302} 303 304void print_end_doc(drawing *dr) 305{ 306 drawing_internal *dri = PRIVATE_CAST(dr); 307 dri->pub.api->end_doc(dr); 308} 309 310void print_get_colour(drawing *dr, int colour, bool printing_in_colour, 311 int *hatch, float *r, float *g, float *b) 312{ 313 drawing_internal *dri = PRIVATE_CAST(dr); 314 assert(colour >= 0 && colour < dri->ncolours); 315 if (dri->colours[colour].hatch_when == 2 || 316 (dri->colours[colour].hatch_when == 1 && !printing_in_colour)) { 317 *hatch = dri->colours[colour].hatch; 318 } else { 319 *hatch = -1; 320 if (printing_in_colour) { 321 *r = dri->colours[colour].r; 322 *g = dri->colours[colour].g; 323 *b = dri->colours[colour].b; 324 } else { 325 *r = *g = *b = dri->colours[colour].grey; 326 } 327 } 328} 329 330static int print_generic_colour(drawing *dr, float r, float g, float b, 331 float grey, int hatch, int hatch_when) 332{ 333 drawing_internal *dri = PRIVATE_CAST(dr); 334 if (dri->ncolours >= dri->coloursize) { 335 dri->coloursize = dri->ncolours + 16; 336 dri->colours = sresize(dri->colours, dri->coloursize, 337 struct print_colour); 338 } 339 dri->colours[dri->ncolours].hatch = hatch; 340 dri->colours[dri->ncolours].hatch_when = hatch_when; 341 dri->colours[dri->ncolours].r = r; 342 dri->colours[dri->ncolours].g = g; 343 dri->colours[dri->ncolours].b = b; 344 dri->colours[dri->ncolours].grey = grey; 345 return dri->ncolours++; 346} 347 348int print_mono_colour(drawing *dr, int grey) 349{ 350 return print_generic_colour(dr, grey, grey, grey, grey, -1, 0); 351} 352 353int print_grey_colour(drawing *dr, float grey) 354{ 355 return print_generic_colour(dr, grey, grey, grey, grey, -1, 0); 356} 357 358int print_hatched_colour(drawing *dr, int hatch) 359{ 360 return print_generic_colour(dr, 0, 0, 0, 0, hatch, 2); 361} 362 363int print_rgb_mono_colour(drawing *dr, float r, float g, float b, int grey) 364{ 365 return print_generic_colour(dr, r, g, b, grey, -1, 0); 366} 367 368int print_rgb_grey_colour(drawing *dr, float r, float g, float b, float grey) 369{ 370 return print_generic_colour(dr, r, g, b, grey, -1, 0); 371} 372 373int print_rgb_hatched_colour(drawing *dr, float r, float g, float b, int hatch) 374{ 375 return print_generic_colour(dr, r, g, b, 0, hatch, 1); 376} 377 378void print_line_width(drawing *dr, int width) 379{ 380 drawing_internal *dri = PRIVATE_CAST(dr); 381 382 /* 383 * I don't think it's entirely sensible to have line widths be 384 * entirely relative to the puzzle size; there is a point 385 * beyond which lines are just _stupidly_ thick. On the other 386 * hand, absolute line widths aren't particularly nice either 387 * because they start to feel a bit feeble at really large 388 * scales. 389 * 390 * My experimental answer is to scale line widths as the 391 * _square root_ of the main puzzle scale. Double the puzzle 392 * size, and the line width multiplies by 1.4. 393 */ 394 dri->pub.api->line_width(dr, (float)sqrt(dri->scale) * width); 395} 396 397void print_line_dotted(drawing *dr, bool dotted) 398{ 399 drawing_internal *dri = PRIVATE_CAST(dr); 400 dri->pub.api->line_dotted(dr, dotted); 401}