diff options
author | Franklin Wei <git@fwei.tk> | 2017-10-27 18:55:19 -0400 |
---|---|---|
committer | Franklin Wei <git@fwei.tk> | 2017-10-27 19:10:53 -0400 |
commit | 8b8e25f1954ab3313e3940cdd1348785d78b682e (patch) | |
tree | fc0ad5d276bfca941dbecb452debe43d6ec2fd2d /apps/plugins/puzzles/rockbox.c | |
parent | e4a46c8d8805e7a0e7a49de00bd52d9e5681f429 (diff) | |
download | rockbox-8b8e25f1954ab3313e3940cdd1348785d78b682e.tar.gz rockbox-8b8e25f1954ab3313e3940cdd1348785d78b682e.zip |
puzzles: add "Zoom In" option to improve usability
This adds a "Zoom In" option to the pause menu of each puzzle, which
displays the puzzle at triple size (subject to change). This should
help with tiny screens, modulo memory concerns associated with
allocating the temporary framebuffer to which drawing operations are
redirected. Coincidentally, there's an upstream bug with Map that
causes the cursor's positioning to be incorrectly displayed when
zoomed.
Change-Id: Ic8b7c2942acf558e295f4271dd7dc458cd336895
Diffstat (limited to 'apps/plugins/puzzles/rockbox.c')
-rw-r--r-- | apps/plugins/puzzles/rockbox.c | 854 |
1 files changed, 678 insertions, 176 deletions
diff --git a/apps/plugins/puzzles/rockbox.c b/apps/plugins/puzzles/rockbox.c index 37d15b75b5..7b838ddb79 100644 --- a/apps/plugins/puzzles/rockbox.c +++ b/apps/plugins/puzzles/rockbox.c | |||
@@ -64,6 +64,11 @@ | |||
64 | #define midend_colors midend_colours | 64 | #define midend_colors midend_colours |
65 | #endif | 65 | #endif |
66 | 66 | ||
67 | #define PAN_X LCD_WIDTH / 4 | ||
68 | #define PAN_Y LCD_WIDTH / 4 /* not a typo */ | ||
69 | |||
70 | #define ZOOM_FACTOR 3 | ||
71 | |||
67 | static midend *me = NULL; | 72 | static midend *me = NULL; |
68 | static unsigned *colors = NULL; | 73 | static unsigned *colors = NULL; |
69 | static int ncolors = 0; | 74 | static int ncolors = 0; |
@@ -79,41 +84,206 @@ static int help_times = 0; | |||
79 | static void fix_size(void); | 84 | static void fix_size(void); |
80 | 85 | ||
81 | static struct viewport clip_rect; | 86 | static struct viewport clip_rect; |
82 | static bool clipped = false; | 87 | static bool clipped = false, audiobuf_available, zoom_enabled = false; |
83 | extern bool audiobuf_available; | 88 | |
89 | static fb_data *zoom_fb; | ||
90 | static int zoom_w, zoom_h, zoom_clipu, zoom_clipd, zoom_clipl, zoom_clipr; | ||
91 | static int cur_font = FONT_UI; | ||
84 | 92 | ||
85 | static struct settings_t { | 93 | static struct settings_t { |
86 | int slowmo_factor; | 94 | int slowmo_factor; |
87 | bool timerflash, clipoff, shortcuts, no_aa, polyanim; | 95 | bool timerflash, clipoff, shortcuts, no_aa, polyanim; |
88 | } settings; | 96 | } settings; |
89 | 97 | ||
98 | static inline void plot(fb_data *fb, int w, int h, | ||
99 | unsigned x, unsigned y, unsigned long a, | ||
100 | unsigned long r1, unsigned long g1, unsigned long b1, | ||
101 | unsigned cl, unsigned cr, unsigned cu, unsigned cd); | ||
102 | |||
103 | |||
104 | /* re-implementations of many rockbox primitives, adapted to draw into | ||
105 | * a custom framebuffer. */ | ||
106 | static void zoom_drawpixel(int x, int y) | ||
107 | { | ||
108 | if(y < zoom_clipu || y >= zoom_clipd) | ||
109 | return; | ||
110 | |||
111 | if(x < zoom_clipl || x >= zoom_clipr) | ||
112 | return; | ||
113 | |||
114 | zoom_fb[y * zoom_w + x] = rb->lcd_get_foreground(); | ||
115 | } | ||
116 | |||
117 | static void zoom_hline(int l, int r, int y) | ||
118 | { | ||
119 | if(y < zoom_clipu || y >= zoom_clipd) | ||
120 | return; | ||
121 | if(l > r) | ||
122 | { | ||
123 | int t = l; | ||
124 | l = r; | ||
125 | r = t; | ||
126 | } | ||
127 | |||
128 | if(l < zoom_clipl) | ||
129 | l = zoom_clipl; | ||
130 | if(r >= zoom_clipr) | ||
131 | r = zoom_clipr; | ||
132 | |||
133 | fb_data pixel = rb->lcd_get_foreground(); | ||
134 | fb_data *ptr = zoom_fb + y * zoom_w + l; | ||
135 | for(int i = 0; i < r - l; ++i) | ||
136 | *ptr++ = pixel; | ||
137 | } | ||
138 | |||
139 | static void zoom_fillcircle(int cx, int cy, int radius) | ||
140 | { | ||
141 | /* copied straight from xlcd_draw.c */ | ||
142 | int d = 3 - (radius * 2); | ||
143 | int x = 0; | ||
144 | int y = radius; | ||
145 | while(x <= y) | ||
146 | { | ||
147 | zoom_hline(cx - x, cx + x, cy + y); | ||
148 | zoom_hline(cx - x, cx + x, cy - y); | ||
149 | zoom_hline(cx - y, cx + y, cy + x); | ||
150 | zoom_hline(cx - y, cx + y, cy - x); | ||
151 | if(d < 0) | ||
152 | { | ||
153 | d += (x * 4) + 6; | ||
154 | } | ||
155 | else | ||
156 | { | ||
157 | d += ((x - y) * 4) + 10; | ||
158 | --y; | ||
159 | } | ||
160 | ++x; | ||
161 | } | ||
162 | } | ||
163 | |||
164 | static void zoom_drawcircle(int cx, int cy, int radius) | ||
165 | { | ||
166 | int d = 3 - (radius * 2); | ||
167 | int x = 0; | ||
168 | int y = radius; | ||
169 | while(x <= y) | ||
170 | { | ||
171 | zoom_drawpixel(cx + x, cy + y); | ||
172 | zoom_drawpixel(cx - x, cy + y); | ||
173 | zoom_drawpixel(cx + x, cy - y); | ||
174 | zoom_drawpixel(cx - x, cy - y); | ||
175 | zoom_drawpixel(cx + y, cy + x); | ||
176 | zoom_drawpixel(cx - y, cy + x); | ||
177 | zoom_drawpixel(cx + y, cy - x); | ||
178 | zoom_drawpixel(cx - y, cy - x); | ||
179 | if(d < 0) | ||
180 | { | ||
181 | d += (x * 4) + 6; | ||
182 | } | ||
183 | else | ||
184 | { | ||
185 | d += ((x - y) * 4) + 10; | ||
186 | --y; | ||
187 | } | ||
188 | ++x; | ||
189 | } | ||
190 | } | ||
191 | |||
192 | /* This format is pretty crazy: each byte holds the states of 8 pixels | ||
193 | * in a column, with bit 0 being the topmost pixel. Who needs cache | ||
194 | * efficiency? */ | ||
195 | static void zoom_mono_bitmap(const unsigned char *bits, int x, int y, int w, int h) | ||
196 | { | ||
197 | for(int i = 0; i < h / 8 + 1; ++i) | ||
198 | { | ||
199 | for(int j = 0; j < w; ++j) | ||
200 | { | ||
201 | unsigned char column = bits[i * w + j]; | ||
202 | for(int dy = 0; dy < 8; ++dy) | ||
203 | { | ||
204 | if(column & 1) | ||
205 | zoom_fb[(y + i * 8 + dy) * zoom_w + x + j] = LCD_BLACK; | ||
206 | column >>= 1; | ||
207 | } | ||
208 | } | ||
209 | } | ||
210 | } | ||
211 | |||
212 | /* Rockbox's alpha format is actually pretty sane: each byte holds | ||
213 | * alpha values for two horizontally adjacent pixels. Low half is | ||
214 | * leftmost pixel. See lcd-16bit-common.c for more info. */ | ||
215 | static void zoom_alpha_bitmap(const unsigned char *bits, int x, int y, int w, int h) | ||
216 | { | ||
217 | const unsigned char *ptr = bits; | ||
218 | unsigned char buf; | ||
219 | int n_read = 0; /* how many 4-bit nibbles we've read (read new when even) */ | ||
220 | for(int i = 0; i < h; ++i) | ||
221 | { | ||
222 | for(int j = 0; j < w; ++j) | ||
223 | { | ||
224 | if(n_read % 2 == 0) | ||
225 | { | ||
226 | buf = *ptr++; | ||
227 | } | ||
228 | int pix_alpha = (n_read++ % 2) ? buf >> 4 : buf & 0xF; /* in reverse order */ | ||
229 | |||
230 | pix_alpha = 15 - pix_alpha; /* correct order now, still 0-F */ | ||
231 | |||
232 | int plot_alpha = (pix_alpha << 4) | pix_alpha; /* so 0xF -> 0xFF, 0x1 -> 0x11, etc. */ | ||
233 | |||
234 | plot(zoom_fb, zoom_w, zoom_h, x + j, y + i, plot_alpha, 0, 0, 0, | ||
235 | 0, zoom_w, 0, zoom_h); | ||
236 | } | ||
237 | } | ||
238 | } | ||
239 | |||
90 | /* clipping is implemented through viewports and offsetting | 240 | /* clipping is implemented through viewports and offsetting |
91 | * coordinates */ | 241 | * coordinates */ |
92 | static void rb_clip(void *handle, int x, int y, int w, int h) | 242 | static void rb_clip(void *handle, int x, int y, int w, int h) |
93 | { | 243 | { |
94 | if(!settings.clipoff) | 244 | if(!zoom_enabled) |
95 | { | 245 | { |
96 | LOGF("rb_clip(%d %d %d %d)", x, y, w, h); | 246 | if(!settings.clipoff) |
97 | clip_rect.x = MAX(0, x); | 247 | { |
98 | clip_rect.y = MAX(0, y); | 248 | LOGF("rb_clip(%d %d %d %d)", x, y, w, h); |
99 | clip_rect.width = MIN(LCD_WIDTH, w); | 249 | clip_rect.x = MAX(0, x); |
100 | clip_rect.height = MIN(LCD_HEIGHT, h); | 250 | clip_rect.y = MAX(0, y); |
101 | clip_rect.font = FONT_UI; | 251 | clip_rect.width = MIN(LCD_WIDTH, w); |
102 | clip_rect.drawmode = DRMODE_SOLID; | 252 | clip_rect.height = MIN(LCD_HEIGHT, h); |
253 | clip_rect.font = FONT_UI; | ||
254 | clip_rect.drawmode = DRMODE_SOLID; | ||
103 | #if LCD_DEPTH > 1 | 255 | #if LCD_DEPTH > 1 |
104 | clip_rect.fg_pattern = LCD_DEFAULT_FG; | 256 | clip_rect.fg_pattern = LCD_DEFAULT_FG; |
105 | clip_rect.bg_pattern = LCD_DEFAULT_BG; | 257 | clip_rect.bg_pattern = LCD_DEFAULT_BG; |
106 | #endif | 258 | #endif |
107 | rb->lcd_set_viewport(&clip_rect); | 259 | rb->lcd_set_viewport(&clip_rect); |
108 | clipped = true; | 260 | clipped = true; |
261 | } | ||
262 | } | ||
263 | else | ||
264 | { | ||
265 | zoom_clipu = y; | ||
266 | zoom_clipd = y + h; | ||
267 | zoom_clipl = x; | ||
268 | zoom_clipr = x + w; | ||
109 | } | 269 | } |
110 | } | 270 | } |
111 | 271 | ||
112 | static void rb_unclip(void *handle) | 272 | static void rb_unclip(void *handle) |
113 | { | 273 | { |
114 | LOGF("rb_unclip"); | 274 | if(!zoom_enabled) |
115 | rb->lcd_set_viewport(NULL); | 275 | { |
116 | clipped = false; | 276 | LOGF("rb_unclip"); |
277 | rb->lcd_set_viewport(NULL); | ||
278 | clipped = false; | ||
279 | } | ||
280 | else | ||
281 | { | ||
282 | zoom_clipu = 0; | ||
283 | zoom_clipd = LCD_HEIGHT; | ||
284 | zoom_clipl = 0; | ||
285 | zoom_clipr = LCD_WIDTH; | ||
286 | } | ||
117 | } | 287 | } |
118 | 288 | ||
119 | static void offset_coords(int *x, int *y) | 289 | static void offset_coords(int *x, int *y) |
@@ -158,7 +328,8 @@ static void unload_fonts(void) | |||
158 | loaded_fonts[i].status = -3; | 328 | loaded_fonts[i].status = -3; |
159 | } | 329 | } |
160 | access_counter = -1; | 330 | access_counter = -1; |
161 | rb->lcd_setfont(FONT_UI); | 331 | cur_font = FONT_UI; |
332 | rb->lcd_setfont(cur_font); | ||
162 | } | 333 | } |
163 | 334 | ||
164 | static void init_fonttab(void) | 335 | static void init_fonttab(void) |
@@ -241,7 +412,8 @@ static void rb_setfont(int type, int size) | |||
241 | goto fallback; | 412 | goto fallback; |
242 | loaded_fonts[font_idx].last_use = access_counter++; | 413 | loaded_fonts[font_idx].last_use = access_counter++; |
243 | n_fonts++; | 414 | n_fonts++; |
244 | rb->lcd_setfont(loaded_fonts[font_idx].status); | 415 | cur_font = loaded_fonts[font_idx].status; |
416 | rb->lcd_setfont(cur_font); | ||
245 | break; | 417 | break; |
246 | } | 418 | } |
247 | case -2: | 419 | case -2: |
@@ -249,15 +421,16 @@ static void rb_setfont(int type, int size) | |||
249 | goto fallback; | 421 | goto fallback; |
250 | default: | 422 | default: |
251 | loaded_fonts[font_idx].last_use = access_counter++; | 423 | loaded_fonts[font_idx].last_use = access_counter++; |
252 | rb->lcd_setfont(loaded_fonts[font_idx].status); | 424 | cur_font = loaded_fonts[font_idx].status; |
425 | rb->lcd_setfont(cur_font); | ||
253 | break; | 426 | break; |
254 | } | 427 | } |
255 | 428 | ||
256 | return; | 429 | return; |
257 | 430 | ||
258 | fallback: | 431 | fallback: |
259 | 432 | cur_font = type == FONT_FIXED ? FONT_SYSFIXED : FONT_UI; | |
260 | rb->lcd_setfont(type == FONT_FIXED ? FONT_SYSFIXED : FONT_UI); | 433 | rb->lcd_setfont(cur_font); |
261 | 434 | ||
262 | return; | 435 | return; |
263 | } | 436 | } |
@@ -266,37 +439,95 @@ static void rb_draw_text(void *handle, int x, int y, int fonttype, | |||
266 | int fontsize, int align, int color, char *text) | 439 | int fontsize, int align, int color, char *text) |
267 | { | 440 | { |
268 | (void) fontsize; | 441 | (void) fontsize; |
269 | LOGF("rb_draw_text(%d %d %s)", x, y, text); | 442 | if(!zoom_enabled) |
443 | { | ||
444 | LOGF("rb_draw_text(%d %d %s)", x, y, text); | ||
445 | |||
446 | offset_coords(&x, &y); | ||
447 | |||
448 | rb_setfont(fonttype, fontsize); | ||
449 | |||
450 | int w, h; | ||
451 | rb->lcd_getstringsize(text, &w, &h); | ||
452 | |||
453 | if(align & ALIGN_VNORMAL) | ||
454 | y -= h; | ||
455 | else if(align & ALIGN_VCENTRE) | ||
456 | y -= h / 2; | ||
457 | |||
458 | if(align & ALIGN_HCENTRE) | ||
459 | x -= w / 2; | ||
460 | else if(align & ALIGN_HRIGHT) | ||
461 | x -= w; | ||
270 | 462 | ||
271 | offset_coords(&x, &y); | 463 | rb_color(color); |
464 | rb->lcd_set_drawmode(DRMODE_FG); | ||
465 | rb->lcd_putsxy(x, y, text); | ||
466 | rb->lcd_set_drawmode(DRMODE_SOLID); | ||
467 | } | ||
468 | else | ||
469 | { | ||
470 | rb_setfont(fonttype, fontsize); /* size will be clamped if too large */ | ||
272 | 471 | ||
273 | rb_setfont(fonttype, fontsize); | 472 | int w, h; |
473 | rb->lcd_getstringsize(text, &w, &h); | ||
274 | 474 | ||
275 | int w, h; | 475 | if(align & ALIGN_VNORMAL) |
276 | rb->lcd_getstringsize(text, &w, &h); | 476 | y -= h; |
477 | else if(align & ALIGN_VCENTRE) | ||
478 | y -= h / 2; | ||
277 | 479 | ||
278 | if(align & ALIGN_VNORMAL) | 480 | if(align & ALIGN_HCENTRE) |
279 | y -= h; | 481 | x -= w / 2; |
280 | else if(align & ALIGN_VCENTRE) | 482 | else if(align & ALIGN_HRIGHT) |
281 | y -= h / 2; | 483 | x -= w; |
282 | 484 | ||
283 | if(align & ALIGN_HCENTRE) | 485 | /* we need to access the font bitmap directly */ |
284 | x -= w / 2; | 486 | struct font *pf = rb->font_get(cur_font); |
285 | else if(align & ALIGN_HRIGHT) | ||
286 | x -= w; | ||
287 | 487 | ||
288 | rb_color(color); | 488 | while(*text) |
289 | rb->lcd_set_drawmode(DRMODE_FG); | 489 | { |
290 | rb->lcd_putsxy(x, y, text); | 490 | /* still only reads 1 byte */ |
291 | rb->lcd_set_drawmode(DRMODE_SOLID); | 491 | unsigned short c = *text++; |
492 | const unsigned char *bits = rb->font_get_bits(pf, c); | ||
493 | int width = rb->font_get_width(pf, c); | ||
494 | |||
495 | /* straight from lcd-bitmap-common.c */ | ||
496 | #if defined(HAVE_LCD_COLOR) | ||
497 | if (pf->depth) | ||
498 | { | ||
499 | /* lcd_alpha_bitmap_part isn't exported directly. However, | ||
500 | * we can create a null bitmap struct with only an alpha | ||
501 | * channel to make lcd_bmp_part call it for us. */ | ||
502 | |||
503 | zoom_alpha_bitmap(bits, x, y, width, pf->height); | ||
504 | } | ||
505 | else | ||
506 | #endif | ||
507 | zoom_mono_bitmap(bits, x, y, width, pf->height); | ||
508 | x += width; | ||
509 | } | ||
510 | } | ||
292 | } | 511 | } |
293 | 512 | ||
294 | static void rb_draw_rect(void *handle, int x, int y, int w, int h, int color) | 513 | static void rb_draw_rect(void *handle, int x, int y, int w, int h, int color) |
295 | { | 514 | { |
296 | LOGF("rb_draw_rect(%d, %d, %d, %d, %d)", x, y, w, h, color); | 515 | if(!zoom_enabled) |
297 | rb_color(color); | 516 | { |
298 | offset_coords(&x, &y); | 517 | LOGF("rb_draw_rect(%d, %d, %d, %d, %d)", x, y, w, h, color); |
299 | rb->lcd_fillrect(x, y, w, h); | 518 | rb_color(color); |
519 | offset_coords(&x, &y); | ||
520 | rb->lcd_fillrect(x, y, w, h); | ||
521 | } | ||
522 | else | ||
523 | { | ||
524 | /* TODO: clipping */ | ||
525 | for(int i = y; i < y + h; ++i) | ||
526 | for(int j = x; j < x + w; ++j) | ||
527 | { | ||
528 | zoom_fb[i * zoom_w + j] = colors[color]; | ||
529 | } | ||
530 | } | ||
300 | } | 531 | } |
301 | 532 | ||
302 | #define SWAP(a, b, t) do { t = a; a = b; b = t; } while(0); | 533 | #define SWAP(a, b, t) do { t = a; a = b; b = t; } while(0); |
@@ -306,8 +537,9 @@ static void rb_draw_rect(void *handle, int x, int y, int w, int h, int color) | |||
306 | 537 | ||
307 | #define FRACBITS 16 | 538 | #define FRACBITS 16 |
308 | 539 | ||
309 | /* most of our time drawing lines is spent in this function! */ | 540 | /* a goes from 0-255, with a = 255 being fully opaque and a = 0 transparent */ |
310 | static inline void plot(unsigned x, unsigned y, unsigned long a, | 541 | static inline void plot(fb_data *fb, int w, int h, |
542 | unsigned x, unsigned y, unsigned long a, | ||
311 | unsigned long r1, unsigned long g1, unsigned long b1, | 543 | unsigned long r1, unsigned long g1, unsigned long b1, |
312 | unsigned cl, unsigned cr, unsigned cu, unsigned cd) | 544 | unsigned cl, unsigned cr, unsigned cu, unsigned cd) |
313 | { | 545 | { |
@@ -319,7 +551,7 @@ static inline void plot(unsigned x, unsigned y, unsigned long a, | |||
319 | if(!(cl <= x && x < cr && cu <= y && y < cd)) | 551 | if(!(cl <= x && x < cr && cu <= y && y < cd)) |
320 | return; | 552 | return; |
321 | 553 | ||
322 | fb_data *ptr = rb->lcd_framebuffer + y * LCD_WIDTH + x; | 554 | fb_data *ptr = fb + y * w + x; |
323 | fb_data orig = *ptr; | 555 | fb_data orig = *ptr; |
324 | unsigned long r2, g2, b2; | 556 | unsigned long r2, g2, b2; |
325 | #if LCD_DEPTH != 24 | 557 | #if LCD_DEPTH != 24 |
@@ -351,18 +583,28 @@ static inline void plot(unsigned x, unsigned y, unsigned long a, | |||
351 | * lines/sec at full optimization on ipod6g */ | 583 | * lines/sec at full optimization on ipod6g */ |
352 | 584 | ||
353 | /* expects UN-OFFSET coordinates, directly access framebuffer */ | 585 | /* expects UN-OFFSET coordinates, directly access framebuffer */ |
354 | static void draw_antialiased_line(int x0, int y0, int x1, int y1) | 586 | static void draw_antialiased_line(fb_data *fb, int w, int h, int x0, int y0, int x1, int y1) |
355 | { | 587 | { |
356 | /* fixed-point Wu's algorithm, modified for integer-only endpoints */ | 588 | /* fixed-point Wu's algorithm, modified for integer-only endpoints */ |
357 | 589 | ||
358 | /* passed to plot() to avoid re-calculation */ | 590 | /* passed to plot() to avoid re-calculation */ |
359 | unsigned short l = 0, r = LCD_WIDTH, u = 0, d = LCD_HEIGHT; | 591 | unsigned short l = 0, r = w, u = 0, d = h; |
360 | if(clipped) | 592 | if(!zoom_enabled) |
361 | { | 593 | { |
362 | l = clip_rect.x; | 594 | if(clipped) |
363 | r = clip_rect.x + clip_rect.width; | 595 | { |
364 | u = clip_rect.y; | 596 | l = clip_rect.x; |
365 | d = clip_rect.y + clip_rect.height; | 597 | r = clip_rect.x + clip_rect.width; |
598 | u = clip_rect.y; | ||
599 | d = clip_rect.y + clip_rect.height; | ||
600 | } | ||
601 | } | ||
602 | else | ||
603 | { | ||
604 | l = zoom_clipl; | ||
605 | r = zoom_clipr; | ||
606 | u = zoom_clipu; | ||
607 | d = zoom_clipd; | ||
366 | } | 608 | } |
367 | 609 | ||
368 | bool steep = ABS(y1 - y0) > ABS(x1 - x0); | 610 | bool steep = ABS(y1 - y0) > ABS(x1 - x0); |
@@ -402,8 +644,8 @@ static void draw_antialiased_line(int x0, int y0, int x1, int y1) | |||
402 | unsigned y = intery >> FRACBITS; | 644 | unsigned y = intery >> FRACBITS; |
403 | unsigned alpha = fp_fpart(intery, FRACBITS) >> (FRACBITS - 8); | 645 | unsigned alpha = fp_fpart(intery, FRACBITS) >> (FRACBITS - 8); |
404 | 646 | ||
405 | plot(y, x, (1 << 8) - alpha, r1, g1, b1, l, r, u, d); | 647 | plot(fb, w, h, y, x, (1 << 8) - alpha, r1, g1, b1, l, r, u, d); |
406 | plot(y + 1, x, alpha, r1, g1, b1, l, r, u, d); | 648 | plot(fb, w, h, y + 1, x, alpha, r1, g1, b1, l, r, u, d); |
407 | } | 649 | } |
408 | } | 650 | } |
409 | else | 651 | else |
@@ -413,8 +655,8 @@ static void draw_antialiased_line(int x0, int y0, int x1, int y1) | |||
413 | unsigned y = intery >> FRACBITS; | 655 | unsigned y = intery >> FRACBITS; |
414 | unsigned alpha = fp_fpart(intery, FRACBITS) >> (FRACBITS - 8); | 656 | unsigned alpha = fp_fpart(intery, FRACBITS) >> (FRACBITS - 8); |
415 | 657 | ||
416 | plot(x, y, (1 << 8) - alpha, r1, g1, b1, l, r, u, d); | 658 | plot(fb, w, h, x, y, (1 << 8) - alpha, r1, g1, b1, l, r, u, d); |
417 | plot(x, y + 1, alpha, r1, g1, b1, l, r, u, d); | 659 | plot(fb, w, h, x, y + 1, alpha, r1, g1, b1, l, r, u, d); |
418 | } | 660 | } |
419 | } | 661 | } |
420 | } | 662 | } |
@@ -422,16 +664,27 @@ static void draw_antialiased_line(int x0, int y0, int x1, int y1) | |||
422 | static void rb_draw_line(void *handle, int x1, int y1, int x2, int y2, | 664 | static void rb_draw_line(void *handle, int x1, int y1, int x2, int y2, |
423 | int color) | 665 | int color) |
424 | { | 666 | { |
425 | LOGF("rb_draw_line(%d, %d, %d, %d, %d)", x1, y1, x2, y2, color); | 667 | if(!zoom_enabled) |
426 | rb_color(color); | ||
427 | if(settings.no_aa) | ||
428 | { | 668 | { |
429 | offset_coords(&x1, &y1); | 669 | LOGF("rb_draw_line(%d, %d, %d, %d, %d)", x1, y1, x2, y2, color); |
430 | offset_coords(&x2, &y2); | 670 | rb_color(color); |
431 | rb->lcd_drawline(x1, y1, x2, y2); | 671 | if(settings.no_aa) |
672 | { | ||
673 | offset_coords(&x1, &y1); | ||
674 | offset_coords(&x2, &y2); | ||
675 | rb->lcd_drawline(x1, y1, x2, y2); | ||
676 | } | ||
677 | else | ||
678 | draw_antialiased_line(rb->lcd_framebuffer, LCD_WIDTH, LCD_HEIGHT, x1, y1, x2, y2); | ||
432 | } | 679 | } |
433 | else | 680 | else |
434 | draw_antialiased_line(x1, y1, x2, y2); | 681 | { |
682 | /* draw_antialiased_line uses rb->lcd_get_foreground() to get | ||
683 | * the color */ | ||
684 | rb_color(color); | ||
685 | |||
686 | draw_antialiased_line(zoom_fb, zoom_w, zoom_h, x1, y1, x2, y2); | ||
687 | } | ||
435 | } | 688 | } |
436 | 689 | ||
437 | #if 0 | 690 | #if 0 |
@@ -489,7 +742,7 @@ static void fill_poly_line(int scanline, int count, int *pxy) | |||
489 | intersection[num_of_intersects] = | 742 | intersection[num_of_intersects] = |
490 | x1+((scanline-y1)*(x2-x1))/(y2-y1); | 743 | x1+((scanline-y1)*(x2-x1))/(y2-y1); |
491 | if ( (direct!=old_direct) | 744 | if ( (direct!=old_direct) |
492 | || (intersection[num_of_intersects] != intersection[num_of_intersects-1]) | 745 | || (intersection[num_of_intersects] != intersection[num_of_intersects-1]) |
493 | ) | 746 | ) |
494 | ++num_of_intersects; | 747 | ++num_of_intersects; |
495 | } | 748 | } |
@@ -531,32 +784,176 @@ static void v_fillarea(int count, int *pxy) | |||
531 | } | 784 | } |
532 | #endif | 785 | #endif |
533 | 786 | ||
787 | /* I'm a horrible person: this was copy-pasta'd straight from | ||
788 | * xlcd_draw.c */ | ||
789 | |||
790 | /* sort the given coordinates by increasing x value */ | ||
791 | static void sort_points_by_increasing_x(int* x1, int* y1, | ||
792 | int* x2, int* y2, | ||
793 | int* x3, int* y3) | ||
794 | { | ||
795 | int x, y; | ||
796 | if (*x1 > *x3) | ||
797 | { | ||
798 | if (*x2 < *x3) /* x2 < x3 < x1 */ | ||
799 | { | ||
800 | x = *x1; *x1 = *x2; *x2 = *x3; *x3 = x; | ||
801 | y = *y1; *y1 = *y2; *y2 = *y3; *y3 = y; | ||
802 | } | ||
803 | else if (*x2 > *x1) /* x3 < x1 < x2 */ | ||
804 | { | ||
805 | x = *x1; *x1 = *x3; *x3 = *x2; *x2 = x; | ||
806 | y = *y1; *y1 = *y3; *y3 = *y2; *y2 = y; | ||
807 | } | ||
808 | else /* x3 <= x2 <= x1 */ | ||
809 | { | ||
810 | x = *x1; *x1 = *x3; *x3 = x; | ||
811 | y = *y1; *y1 = *y3; *y3 = y; | ||
812 | } | ||
813 | } | ||
814 | else | ||
815 | { | ||
816 | if (*x2 < *x1) /* x2 < x1 <= x3 */ | ||
817 | { | ||
818 | x = *x1; *x1 = *x2; *x2 = x; | ||
819 | y = *y1; *y1 = *y2; *y2 = y; | ||
820 | } | ||
821 | else if (*x2 > *x3) /* x1 <= x3 < x2 */ | ||
822 | { | ||
823 | x = *x2; *x2 = *x3; *x3 = x; | ||
824 | y = *y2; *y2 = *y3; *y3 = y; | ||
825 | } | ||
826 | /* else already sorted */ | ||
827 | } | ||
828 | } | ||
829 | |||
830 | #define sort_points_by_increasing_y(x1, y1, x2, y2, x3, y3) \ | ||
831 | sort_points_by_increasing_x(y1, x1, y2, x2, y3, x3) | ||
832 | |||
833 | /* draw a filled triangle, using horizontal lines for speed */ | ||
834 | static void zoom_filltriangle(int x1, int y1, | ||
835 | int x2, int y2, | ||
836 | int x3, int y3) | ||
837 | { | ||
838 | long fp_x1, fp_x2, fp_dx1, fp_dx2; | ||
839 | int y; | ||
840 | sort_points_by_increasing_y(&x1, &y1, &x2, &y2, &x3, &y3); | ||
841 | |||
842 | if (y1 < y3) /* draw */ | ||
843 | { | ||
844 | fp_dx1 = ((x3 - x1) << 16) / (y3 - y1); | ||
845 | fp_x1 = (x1 << 16) + (1<<15) + (fp_dx1 >> 1); | ||
846 | |||
847 | if (y1 < y2) /* first part */ | ||
848 | { | ||
849 | fp_dx2 = ((x2 - x1) << 16) / (y2 - y1); | ||
850 | fp_x2 = (x1 << 16) + (1<<15) + (fp_dx2 >> 1); | ||
851 | for (y = y1; y < y2; y++) | ||
852 | { | ||
853 | zoom_hline(fp_x1 >> 16, fp_x2 >> 16, y); | ||
854 | fp_x1 += fp_dx1; | ||
855 | fp_x2 += fp_dx2; | ||
856 | } | ||
857 | } | ||
858 | if (y2 < y3) /* second part */ | ||
859 | { | ||
860 | fp_dx2 = ((x3 - x2) << 16) / (y3 - y2); | ||
861 | fp_x2 = (x2 << 16) + (1<<15) + (fp_dx2 >> 1); | ||
862 | for (y = y2; y < y3; y++) | ||
863 | { | ||
864 | zoom_hline(fp_x1 >> 16, fp_x2 >> 16, y); | ||
865 | fp_x1 += fp_dx1; | ||
866 | fp_x2 += fp_dx2; | ||
867 | } | ||
868 | } | ||
869 | } | ||
870 | } | ||
871 | |||
534 | static void rb_draw_poly(void *handle, int *coords, int npoints, | 872 | static void rb_draw_poly(void *handle, int *coords, int npoints, |
535 | int fillcolor, int outlinecolor) | 873 | int fillcolor, int outlinecolor) |
536 | { | 874 | { |
537 | LOGF("rb_draw_poly"); | 875 | if(!zoom_enabled) |
538 | |||
539 | if(fillcolor >= 0) | ||
540 | { | 876 | { |
541 | rb_color(fillcolor); | 877 | LOGF("rb_draw_poly"); |
878 | |||
879 | if(fillcolor >= 0) | ||
880 | { | ||
881 | rb_color(fillcolor); | ||
542 | #if 1 | 882 | #if 1 |
543 | /* serious hack: draw a bunch of triangles between adjacent points */ | 883 | /* serious hack: draw a bunch of triangles between adjacent points */ |
544 | /* this generally works, even with some concave polygons */ | 884 | /* this generally works, even with some concave polygons */ |
545 | for(int i = 2; i < npoints; ++i) | 885 | for(int i = 2; i < npoints; ++i) |
886 | { | ||
887 | int x1, y1, x2, y2, x3, y3; | ||
888 | x1 = coords[0]; | ||
889 | y1 = coords[1]; | ||
890 | x2 = coords[(i - 1) * 2]; | ||
891 | y2 = coords[(i - 1) * 2 + 1]; | ||
892 | x3 = coords[i * 2]; | ||
893 | y3 = coords[i * 2 + 1]; | ||
894 | offset_coords(&x1, &y1); | ||
895 | offset_coords(&x2, &y2); | ||
896 | offset_coords(&x3, &y3); | ||
897 | xlcd_filltriangle(x1, y1, | ||
898 | x2, y2, | ||
899 | x3, y3); | ||
900 | |||
901 | #ifdef DEBUG_MENU | ||
902 | if(settings.polyanim) | ||
903 | { | ||
904 | rb->lcd_update(); | ||
905 | rb->sleep(HZ/4); | ||
906 | } | ||
907 | #endif | ||
908 | #if 0 | ||
909 | /* debug code */ | ||
910 | rb->lcd_set_foreground(LCD_RGBPACK(255,0,0)); | ||
911 | rb->lcd_drawpixel(x1, y1); | ||
912 | rb->lcd_drawpixel(x2, y2); | ||
913 | rb->lcd_drawpixel(x3, y3); | ||
914 | rb->lcd_update(); | ||
915 | rb->sleep(HZ); | ||
916 | rb_color(fillcolor); | ||
917 | rb->lcd_drawpixel(x1, y1); | ||
918 | rb->lcd_drawpixel(x2, y2); | ||
919 | rb->lcd_drawpixel(x3, y3); | ||
920 | rb->lcd_update(); | ||
921 | #endif | ||
922 | } | ||
923 | #else | ||
924 | int *pxy = smalloc(sizeof(int) * 2 * npoints + 2); | ||
925 | /* copy points, offsetted */ | ||
926 | for(int i = 0; i < npoints; ++i) | ||
927 | { | ||
928 | pxy[2 * i + 0] = coords[2 * i + 0]; | ||
929 | pxy[2 * i + 1] = coords[2 * i + 1]; | ||
930 | offset_coords(&pxy[2*i+0], &pxy[2*i+1]); | ||
931 | } | ||
932 | v_fillarea(npoints, pxy); | ||
933 | sfree(pxy); | ||
934 | #endif | ||
935 | } | ||
936 | |||
937 | /* draw outlines last so they're not covered by the fill */ | ||
938 | assert(outlinecolor >= 0); | ||
939 | rb_color(outlinecolor); | ||
940 | |||
941 | for(int i = 1; i < npoints; ++i) | ||
546 | { | 942 | { |
547 | int x1, y1, x2, y2, x3, y3; | 943 | int x1, y1, x2, y2; |
548 | x1 = coords[0]; | 944 | x1 = coords[2 * (i - 1)]; |
549 | y1 = coords[1]; | 945 | y1 = coords[2 * (i - 1) + 1]; |
550 | x2 = coords[(i - 1) * 2]; | 946 | x2 = coords[2 * i]; |
551 | y2 = coords[(i - 1) * 2 + 1]; | 947 | y2 = coords[2 * i + 1]; |
552 | x3 = coords[i * 2]; | 948 | if(settings.no_aa) |
553 | y3 = coords[i * 2 + 1]; | 949 | { |
554 | offset_coords(&x1, &y1); | 950 | offset_coords(&x1, &y1); |
555 | offset_coords(&x2, &y2); | 951 | offset_coords(&x2, &y2); |
556 | offset_coords(&x3, &y3); | 952 | rb->lcd_drawline(x1, y1, |
557 | xlcd_filltriangle(x1, y1, | 953 | x2, y2); |
558 | x2, y2, | 954 | } |
559 | x3, y3); | 955 | else |
956 | draw_antialiased_line(rb->lcd_framebuffer, LCD_WIDTH, LCD_HEIGHT, x1, y1, x2, y2); | ||
560 | 957 | ||
561 | #ifdef DEBUG_MENU | 958 | #ifdef DEBUG_MENU |
562 | if(settings.polyanim) | 959 | if(settings.polyanim) |
@@ -565,97 +962,102 @@ static void rb_draw_poly(void *handle, int *coords, int npoints, | |||
565 | rb->sleep(HZ/4); | 962 | rb->sleep(HZ/4); |
566 | } | 963 | } |
567 | #endif | 964 | #endif |
568 | #if 0 | ||
569 | /* debug code */ | ||
570 | rb->lcd_set_foreground(LCD_RGBPACK(255,0,0)); | ||
571 | rb->lcd_drawpixel(x1, y1); | ||
572 | rb->lcd_drawpixel(x2, y2); | ||
573 | rb->lcd_drawpixel(x3, y3); | ||
574 | rb->lcd_update(); | ||
575 | rb->sleep(HZ); | ||
576 | rb_color(fillcolor); | ||
577 | rb->lcd_drawpixel(x1, y1); | ||
578 | rb->lcd_drawpixel(x2, y2); | ||
579 | rb->lcd_drawpixel(x3, y3); | ||
580 | rb->lcd_update(); | ||
581 | #endif | ||
582 | } | 965 | } |
583 | #else | ||
584 | int *pxy = smalloc(sizeof(int) * 2 * npoints + 2); | ||
585 | /* copy points, offsetted */ | ||
586 | for(int i = 0; i < npoints; ++i) | ||
587 | { | ||
588 | pxy[2 * i + 0] = coords[2 * i + 0]; | ||
589 | pxy[2 * i + 1] = coords[2 * i + 1]; | ||
590 | offset_coords(&pxy[2*i+0], &pxy[2*i+1]); | ||
591 | } | ||
592 | v_fillarea(npoints, pxy); | ||
593 | sfree(pxy); | ||
594 | #endif | ||
595 | } | ||
596 | |||
597 | /* draw outlines last so they're not covered by the fill */ | ||
598 | assert(outlinecolor >= 0); | ||
599 | rb_color(outlinecolor); | ||
600 | 966 | ||
601 | for(int i = 1; i < npoints; ++i) | ||
602 | { | ||
603 | int x1, y1, x2, y2; | 967 | int x1, y1, x2, y2; |
604 | x1 = coords[2 * (i - 1)]; | 968 | x1 = coords[0]; |
605 | y1 = coords[2 * (i - 1) + 1]; | 969 | y1 = coords[1]; |
606 | x2 = coords[2 * i]; | 970 | x2 = coords[2 * (npoints - 1)]; |
607 | y2 = coords[2 * i + 1]; | 971 | y2 = coords[2 * (npoints - 1) + 1]; |
608 | if(settings.no_aa) | 972 | if(settings.no_aa) |
609 | { | 973 | { |
610 | offset_coords(&x1, &y1); | 974 | offset_coords(&x1, &y1); |
611 | offset_coords(&x2, &y2); | 975 | offset_coords(&x2, &y2); |
976 | |||
612 | rb->lcd_drawline(x1, y1, | 977 | rb->lcd_drawline(x1, y1, |
613 | x2, y2); | 978 | x2, y2); |
614 | } | 979 | } |
615 | else | 980 | else |
616 | draw_antialiased_line(x1, y1, x2, y2); | 981 | draw_antialiased_line(rb->lcd_framebuffer, LCD_WIDTH, LCD_HEIGHT, x1, y1, x2, y2); |
982 | } | ||
983 | else | ||
984 | { | ||
985 | LOGF("rb_draw_poly"); | ||
617 | 986 | ||
618 | #ifdef DEBUG_MENU | 987 | if(fillcolor >= 0) |
619 | if(settings.polyanim) | ||
620 | { | 988 | { |
621 | rb->lcd_update(); | 989 | rb_color(fillcolor); |
622 | rb->sleep(HZ/4); | 990 | |
991 | /* serious hack: draw a bunch of triangles between adjacent points */ | ||
992 | /* this generally works, even with some concave polygons */ | ||
993 | for(int i = 2; i < npoints; ++i) | ||
994 | { | ||
995 | int x1, y1, x2, y2, x3, y3; | ||
996 | x1 = coords[0]; | ||
997 | y1 = coords[1]; | ||
998 | x2 = coords[(i - 1) * 2]; | ||
999 | y2 = coords[(i - 1) * 2 + 1]; | ||
1000 | x3 = coords[i * 2]; | ||
1001 | y3 = coords[i * 2 + 1]; | ||
1002 | zoom_filltriangle(x1, y1, | ||
1003 | x2, y2, | ||
1004 | x3, y3); | ||
1005 | } | ||
623 | } | 1006 | } |
624 | #endif | ||
625 | } | ||
626 | 1007 | ||
627 | int x1, y1, x2, y2; | 1008 | /* draw outlines last so they're not covered by the fill */ |
628 | x1 = coords[0]; | 1009 | assert(outlinecolor >= 0); |
629 | y1 = coords[1]; | 1010 | rb_color(outlinecolor); |
630 | x2 = coords[2 * (npoints - 1)]; | 1011 | |
631 | y2 = coords[2 * (npoints - 1) + 1]; | 1012 | for(int i = 1; i < npoints; ++i) |
632 | if(settings.no_aa) | 1013 | { |
633 | { | 1014 | int x1, y1, x2, y2; |
634 | offset_coords(&x1, &y1); | 1015 | x1 = coords[2 * (i - 1)]; |
635 | offset_coords(&x2, &y2); | 1016 | y1 = coords[2 * (i - 1) + 1]; |
1017 | x2 = coords[2 * i]; | ||
1018 | y2 = coords[2 * i + 1]; | ||
1019 | draw_antialiased_line(zoom_fb, zoom_w, zoom_h, x1, y1, x2, y2); | ||
1020 | } | ||
636 | 1021 | ||
637 | rb->lcd_drawline(x1, y1, | 1022 | int x1, y1, x2, y2; |
638 | x2, y2); | 1023 | x1 = coords[0]; |
1024 | y1 = coords[1]; | ||
1025 | x2 = coords[2 * (npoints - 1)]; | ||
1026 | y2 = coords[2 * (npoints - 1) + 1]; | ||
1027 | draw_antialiased_line(zoom_fb, zoom_w, zoom_h, x1, y1, x2, y2); | ||
639 | } | 1028 | } |
640 | else | ||
641 | draw_antialiased_line(x1, y1, x2, y2); | ||
642 | } | 1029 | } |
643 | 1030 | ||
644 | static void rb_draw_circle(void *handle, int cx, int cy, int radius, | 1031 | static void rb_draw_circle(void *handle, int cx, int cy, int radius, |
645 | int fillcolor, int outlinecolor) | 1032 | int fillcolor, int outlinecolor) |
646 | { | 1033 | { |
647 | LOGF("rb_draw_circle(%d, %d, %d)", cx, cy, radius); | 1034 | if(!zoom_enabled) |
648 | offset_coords(&cx, &cy); | ||
649 | |||
650 | if(fillcolor >= 0) | ||
651 | { | 1035 | { |
652 | rb_color(fillcolor); | 1036 | LOGF("rb_draw_circle(%d, %d, %d)", cx, cy, radius); |
653 | xlcd_fillcircle(cx, cy, radius - 1); | 1037 | offset_coords(&cx, &cy); |
1038 | |||
1039 | if(fillcolor >= 0) | ||
1040 | { | ||
1041 | rb_color(fillcolor); | ||
1042 | xlcd_fillcircle(cx, cy, radius - 1); | ||
1043 | } | ||
1044 | |||
1045 | assert(outlinecolor >= 0); | ||
1046 | rb_color(outlinecolor); | ||
1047 | xlcd_drawcircle(cx, cy, radius - 1); | ||
654 | } | 1048 | } |
1049 | else | ||
1050 | { | ||
1051 | if(fillcolor >= 0) | ||
1052 | { | ||
1053 | rb_color(fillcolor); | ||
1054 | zoom_fillcircle(cx, cy, radius - 1); | ||
1055 | } | ||
655 | 1056 | ||
656 | assert(outlinecolor >= 0); | 1057 | assert(outlinecolor >= 0); |
657 | rb_color(outlinecolor); | 1058 | rb_color(outlinecolor); |
658 | xlcd_drawcircle(cx, cy, radius - 1); | 1059 | zoom_drawcircle(cx, cy, radius - 1); |
1060 | } | ||
659 | } | 1061 | } |
660 | 1062 | ||
661 | struct blitter { | 1063 | struct blitter { |
@@ -680,11 +1082,14 @@ static void trim_rect(int *x, int *y, int *w, int *h) | |||
680 | x1 = *x + *w; | 1082 | x1 = *x + *w; |
681 | y1 = *y + *h; | 1083 | y1 = *y + *h; |
682 | 1084 | ||
1085 | int screenw = zoom_enabled ? zoom_w : LCD_WIDTH; | ||
1086 | int screenh = zoom_enabled ? zoom_h : LCD_HEIGHT; | ||
1087 | |||
683 | /* Clip each coordinate at both extremes of the canvas */ | 1088 | /* Clip each coordinate at both extremes of the canvas */ |
684 | x0 = (x0 < 0 ? 0 : x0 > LCD_WIDTH - 1 ? LCD_WIDTH - 1: x0); | 1089 | x0 = (x0 < 0 ? 0 : x0 > screenw - 1 ? screenw - 1: x0); |
685 | x1 = (x1 < 0 ? 0 : x1 > LCD_WIDTH - 1 ? LCD_WIDTH - 1: x1); | 1090 | x1 = (x1 < 0 ? 0 : x1 > screenw - 1 ? screenw - 1: x1); |
686 | y0 = (y0 < 0 ? 0 : y0 > LCD_HEIGHT - 1 ? LCD_HEIGHT - 1: y0); | 1091 | y0 = (y0 < 0 ? 0 : y0 > screenh - 1 ? screenh - 1: y0); |
687 | y1 = (y1 < 0 ? 0 : y1 > LCD_HEIGHT - 1 ? LCD_HEIGHT - 1: y1); | 1092 | y1 = (y1 < 0 ? 0 : y1 > screenh - 1 ? screenh - 1: y1); |
688 | 1093 | ||
689 | /* Transform back into x,y,w,h to return */ | 1094 | /* Transform back into x,y,w,h to return */ |
690 | *x = x0; | 1095 | *x = x0; |
@@ -723,12 +1128,13 @@ static void rb_blitter_save(void *handle, blitter *bl, int x, int y) | |||
723 | { | 1128 | { |
724 | int w = bl->bmp.width, h = bl->bmp.height; | 1129 | int w = bl->bmp.width, h = bl->bmp.height; |
725 | trim_rect(&x, &y, &w, &h); | 1130 | trim_rect(&x, &y, &w, &h); |
1131 | fb_data *fb = zoom_enabled ? zoom_fb : rb->lcd_framebuffer; | ||
726 | LOGF("rb_blitter_save(%d, %d, %d, %d)", x, y, w, h); | 1132 | LOGF("rb_blitter_save(%d, %d, %d, %d)", x, y, w, h); |
727 | for(int i = 0; i < h; ++i) | 1133 | for(int i = 0; i < h; ++i) |
728 | { | 1134 | { |
729 | /* copy line-by-line */ | 1135 | /* copy line-by-line */ |
730 | rb->memcpy(bl->bmp.data + sizeof(fb_data) * i * w, | 1136 | rb->memcpy(bl->bmp.data + sizeof(fb_data) * i * w, |
731 | rb->lcd_framebuffer + (y + i) * LCD_WIDTH + x, | 1137 | fb + (y + i) * LCD_WIDTH + x, |
732 | w * sizeof(fb_data)); | 1138 | w * sizeof(fb_data)); |
733 | } | 1139 | } |
734 | bl->x = x; | 1140 | bl->x = x; |
@@ -748,9 +1154,24 @@ static void rb_blitter_load(void *handle, blitter *bl, int x, int y) | |||
748 | if(x == BLITTER_FROMSAVED) x = bl->x; | 1154 | if(x == BLITTER_FROMSAVED) x = bl->x; |
749 | if(y == BLITTER_FROMSAVED) y = bl->y; | 1155 | if(y == BLITTER_FROMSAVED) y = bl->y; |
750 | 1156 | ||
751 | offset_coords(&x, &y); | 1157 | if(!zoom_enabled) |
1158 | offset_coords(&x, &y); | ||
1159 | |||
752 | trim_rect(&x, &y, &w, &h); | 1160 | trim_rect(&x, &y, &w, &h); |
753 | rb->lcd_bitmap((fb_data*)bl->bmp.data, x, y, w, h); | 1161 | |
1162 | if(!zoom_enabled) | ||
1163 | { | ||
1164 | rb->lcd_bitmap((fb_data*)bl->bmp.data, x, y, w, h); | ||
1165 | } | ||
1166 | else | ||
1167 | { | ||
1168 | for(int i = 0; i < h; ++i) | ||
1169 | { | ||
1170 | rb->memcpy(zoom_fb + i * zoom_w + x, | ||
1171 | bl->bmp.data + sizeof(fb_data) * i * w, | ||
1172 | w * sizeof(fb_data)); | ||
1173 | } | ||
1174 | } | ||
754 | } | 1175 | } |
755 | 1176 | ||
756 | static bool need_draw_update = false; | 1177 | static bool need_draw_update = false; |
@@ -793,11 +1214,17 @@ static void rb_start_draw(void *handle) | |||
793 | static void rb_end_draw(void *handle) | 1214 | static void rb_end_draw(void *handle) |
794 | { | 1215 | { |
795 | (void) handle; | 1216 | (void) handle; |
1217 | if(!zoom_enabled) | ||
1218 | { | ||
1219 | LOGF("rb_end_draw"); | ||
796 | 1220 | ||
797 | LOGF("rb_end_draw"); | 1221 | if(need_draw_update) |
798 | 1222 | rb->lcd_update_rect(MAX(0, ud_l), MAX(0, ud_u), MIN(LCD_WIDTH, ud_r - ud_l), MIN(LCD_HEIGHT, ud_d - ud_u)); | |
799 | if(need_draw_update) | 1223 | } |
800 | rb->lcd_update_rect(MAX(0, ud_l), MAX(0, ud_u), MIN(LCD_WIDTH, ud_r - ud_l), MIN(LCD_HEIGHT, ud_d - ud_u)); | 1224 | else |
1225 | { | ||
1226 | /* stubbed */ | ||
1227 | } | ||
801 | } | 1228 | } |
802 | 1229 | ||
803 | static char *titlebar = NULL; | 1230 | static char *titlebar = NULL; |
@@ -824,7 +1251,8 @@ static void draw_title(void) | |||
824 | rb_unclip(NULL); | 1251 | rb_unclip(NULL); |
825 | 1252 | ||
826 | int h; | 1253 | int h; |
827 | rb->lcd_setfont(FONT_UI); | 1254 | cur_font = FONT_UI; |
1255 | rb->lcd_setfont(cur_font); | ||
828 | rb->lcd_getstringsize(str, NULL, &h); | 1256 | rb->lcd_getstringsize(str, NULL, &h); |
829 | 1257 | ||
830 | rb->lcd_set_foreground(BG_COLOR); | 1258 | rb->lcd_set_foreground(BG_COLOR); |
@@ -866,6 +1294,73 @@ const drawing_api rb_drawing = { | |||
866 | NULL, | 1294 | NULL, |
867 | }; | 1295 | }; |
868 | 1296 | ||
1297 | /* render to a virtual framebuffer and let the user pan (but not make any moves) */ | ||
1298 | static void zoom(void) | ||
1299 | { | ||
1300 | zoom_w = LCD_WIDTH * ZOOM_FACTOR, zoom_h = LCD_HEIGHT * ZOOM_FACTOR; | ||
1301 | |||
1302 | zoom_clipu = 0; | ||
1303 | zoom_clipd = zoom_h; | ||
1304 | zoom_clipl = 0; | ||
1305 | zoom_clipr = zoom_w; | ||
1306 | |||
1307 | midend_size(me, &zoom_w, &zoom_h, TRUE); | ||
1308 | |||
1309 | /* allocate a framebuffer */ | ||
1310 | zoom_fb = smalloc(zoom_w * zoom_h * sizeof(fb_data)); | ||
1311 | zoom_enabled = true; | ||
1312 | |||
1313 | /* draws go to the enlarged framebuffer */ | ||
1314 | midend_force_redraw(me); | ||
1315 | |||
1316 | int x = 0, y = 0; | ||
1317 | |||
1318 | rb->lcd_bitmap_part(zoom_fb, x, y, STRIDE(SCREEN_MAIN, zoom_w, zoom_h), | ||
1319 | 0, 0, LCD_WIDTH, LCD_HEIGHT); | ||
1320 | rb->lcd_update(); | ||
1321 | |||
1322 | /* pan around the image */ | ||
1323 | while(1) | ||
1324 | { | ||
1325 | int button = rb->button_get(true); | ||
1326 | switch(button) | ||
1327 | { | ||
1328 | case BTN_UP: | ||
1329 | y -= PAN_Y; /* clamped later */ | ||
1330 | break; | ||
1331 | case BTN_DOWN: | ||
1332 | y += PAN_Y; /* clamped later */ | ||
1333 | break; | ||
1334 | case BTN_LEFT: | ||
1335 | x -= PAN_X; /* clamped later */ | ||
1336 | break; | ||
1337 | case BTN_RIGHT: | ||
1338 | x += PAN_X; /* clamped later */ | ||
1339 | break; | ||
1340 | case BTN_PAUSE: | ||
1341 | zoom_enabled = false; | ||
1342 | sfree(zoom_fb); | ||
1343 | fix_size(); | ||
1344 | return; | ||
1345 | default: | ||
1346 | break; | ||
1347 | } | ||
1348 | |||
1349 | if(y < 0) | ||
1350 | y = 0; | ||
1351 | if(x < 0) | ||
1352 | x = 0; | ||
1353 | if(y + LCD_HEIGHT >= zoom_h) | ||
1354 | y = zoom_h - LCD_HEIGHT; | ||
1355 | if(x + LCD_WIDTH >= zoom_w) | ||
1356 | x = zoom_w - LCD_WIDTH; | ||
1357 | |||
1358 | rb->lcd_bitmap_part(zoom_fb, x, y, STRIDE(SCREEN_MAIN, zoom_w, zoom_h), | ||
1359 | 0, 0, LCD_WIDTH, LCD_HEIGHT); | ||
1360 | rb->lcd_update(); | ||
1361 | } | ||
1362 | } | ||
1363 | |||
869 | void frontend_default_color(frontend *fe, float *out) | 1364 | void frontend_default_color(frontend *fe, float *out) |
870 | { | 1365 | { |
871 | *out++ = BG_R; | 1366 | *out++ = BG_R; |
@@ -1123,7 +1618,8 @@ static bool config_menu(void) | |||
1123 | char *title; | 1618 | char *title; |
1124 | config_item *config = midend_get_config(me, CFG_SETTINGS, &title); | 1619 | config_item *config = midend_get_config(me, CFG_SETTINGS, &title); |
1125 | 1620 | ||
1126 | rb->lcd_setfont(FONT_UI); | 1621 | cur_font = FONT_UI; |
1622 | rb->lcd_setfont(cur_font); | ||
1127 | 1623 | ||
1128 | bool success = false; | 1624 | bool success = false; |
1129 | 1625 | ||
@@ -1318,7 +1814,8 @@ static void full_help(const char *name) | |||
1318 | rb->lcd_set_foreground(LCD_WHITE); | 1814 | rb->lcd_set_foreground(LCD_WHITE); |
1319 | rb->lcd_set_background(LCD_BLACK); | 1815 | rb->lcd_set_background(LCD_BLACK); |
1320 | unload_fonts(); | 1816 | unload_fonts(); |
1321 | rb->lcd_setfont(FONT_UI); | 1817 | cur_font = FONT_UI; |
1818 | rb->lcd_setfont(cur_font); | ||
1322 | 1819 | ||
1323 | char *buf = smalloc(help_text_len); | 1820 | char *buf = smalloc(help_text_len); |
1324 | LZ4_decompress_tiny(help_text, buf, help_text_len); | 1821 | LZ4_decompress_tiny(help_text, buf, help_text_len); |
@@ -1354,7 +1851,7 @@ static void bench_aa(void) | |||
1354 | int i = 0; | 1851 | int i = 0; |
1355 | while(*rb->current_tick < next) | 1852 | while(*rb->current_tick < next) |
1356 | { | 1853 | { |
1357 | draw_antialiased_line(0, 0, 20, 31); | 1854 | draw_antialiased_line(rb->lcd_framebuffer, LCD_WIDTH, LCD_HEIGHT, 0, 0, 20, 31); |
1358 | ++i; | 1855 | ++i; |
1359 | } | 1856 | } |
1360 | rb->splashf(HZ, "%d AA lines/sec", i); | 1857 | rb->splashf(HZ, "%d AA lines/sec", i); |
@@ -1460,7 +1957,7 @@ static int pausemenu_cb(int action, const struct menu_item_ex *this_item) | |||
1460 | if(!midend_get_presets(me, NULL)->n_entries) | 1957 | if(!midend_get_presets(me, NULL)->n_entries) |
1461 | return ACTION_EXIT_MENUITEM; | 1958 | return ACTION_EXIT_MENUITEM; |
1462 | break; | 1959 | break; |
1463 | case 10: | 1960 | case 11: |
1464 | #if defined(FOR_REAL) && defined(DEBUG_MENU) | 1961 | #if defined(FOR_REAL) && defined(DEBUG_MENU) |
1465 | if(debug_mode) | 1962 | if(debug_mode) |
1466 | break; | 1963 | break; |
@@ -1468,7 +1965,7 @@ static int pausemenu_cb(int action, const struct menu_item_ex *this_item) | |||
1468 | #else | 1965 | #else |
1469 | break; | 1966 | break; |
1470 | #endif | 1967 | #endif |
1471 | case 11: | 1968 | case 12: |
1472 | if(!midend_which_game(me)->can_configure) | 1969 | if(!midend_which_game(me)->can_configure) |
1473 | return ACTION_EXIT_MENUITEM; | 1970 | return ACTION_EXIT_MENUITEM; |
1474 | break; | 1971 | break; |
@@ -1507,6 +2004,7 @@ static int pause_menu(void) | |||
1507 | "Undo", | 2004 | "Undo", |
1508 | "Redo", | 2005 | "Redo", |
1509 | "Solve", | 2006 | "Solve", |
2007 | "Zoom In", | ||
1510 | "Quick Help", | 2008 | "Quick Help", |
1511 | "Extensive Help", | 2009 | "Extensive Help", |
1512 | "Playback Control", | 2010 | "Playback Control", |
@@ -1568,15 +2066,18 @@ static int pause_menu(void) | |||
1568 | break; | 2066 | break; |
1569 | } | 2067 | } |
1570 | case 6: | 2068 | case 6: |
1571 | quick_help(); | 2069 | zoom(); |
1572 | break; | 2070 | break; |
1573 | case 7: | 2071 | case 7: |
1574 | full_help(midend_which_game(me)->name); | 2072 | quick_help(); |
1575 | break; | 2073 | break; |
1576 | case 8: | 2074 | case 8: |
1577 | playback_control(NULL); | 2075 | full_help(midend_which_game(me)->name); |
1578 | break; | 2076 | break; |
1579 | case 9: | 2077 | case 9: |
2078 | playback_control(NULL); | ||
2079 | break; | ||
2080 | case 10: | ||
1580 | if(presets_menu()) | 2081 | if(presets_menu()) |
1581 | { | 2082 | { |
1582 | midend_new_game(me); | 2083 | midend_new_game(me); |
@@ -1586,12 +2087,12 @@ static int pause_menu(void) | |||
1586 | quit = true; | 2087 | quit = true; |
1587 | } | 2088 | } |
1588 | break; | 2089 | break; |
1589 | case 10: | 2090 | case 11: |
1590 | #ifdef DEBUG_MENU | 2091 | #ifdef DEBUG_MENU |
1591 | debug_menu(); | 2092 | debug_menu(); |
1592 | #endif | 2093 | #endif |
1593 | break; | 2094 | break; |
1594 | case 11: | 2095 | case 12: |
1595 | if(config_menu()) | 2096 | if(config_menu()) |
1596 | { | 2097 | { |
1597 | midend_new_game(me); | 2098 | midend_new_game(me); |
@@ -1601,9 +2102,9 @@ static int pause_menu(void) | |||
1601 | quit = true; | 2102 | quit = true; |
1602 | } | 2103 | } |
1603 | break; | 2104 | break; |
1604 | case 12: | ||
1605 | return -2; | ||
1606 | case 13: | 2105 | case 13: |
2106 | return -2; | ||
2107 | case 14: | ||
1607 | return -3; | 2108 | return -3; |
1608 | default: | 2109 | default: |
1609 | break; | 2110 | break; |
@@ -1846,7 +2347,8 @@ static size_t giant_buffer_len = 0; /* set on start */ | |||
1846 | static void fix_size(void) | 2347 | static void fix_size(void) |
1847 | { | 2348 | { |
1848 | int w = LCD_WIDTH, h = LCD_HEIGHT, h_x; | 2349 | int w = LCD_WIDTH, h = LCD_HEIGHT, h_x; |
1849 | rb->lcd_setfont(FONT_UI); | 2350 | cur_font = FONT_UI; |
2351 | rb->lcd_setfont(cur_font); | ||
1850 | rb->lcd_getstringsize("X", NULL, &h_x); | 2352 | rb->lcd_getstringsize("X", NULL, &h_x); |
1851 | h -= h_x; | 2353 | h -= h_x; |
1852 | midend_size(me, &w, &h, TRUE); | 2354 | midend_size(me, &w, &h, TRUE); |