summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJens Arnold <amiconn@rockbox.org>2004-05-19 17:19:11 +0000
committerJens Arnold <amiconn@rockbox.org>2004-05-19 17:19:11 +0000
commit6920aed85872fa878670a89fe7ace1ef1ecbf49a (patch)
tree5d33d853ee9f5af06832718b3d88bb0a7884918a
parent71c83b9b6b091e2adacbcf651a2c41e4d4c03b59 (diff)
downloadrockbox-6920aed85872fa878670a89fe7ace1ef1ecbf49a.tar.gz
rockbox-6920aed85872fa878670a89fe7ace1ef1ecbf49a.zip
Removed the grayscale framework, gets linked in from the plugin lib instead
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@4652 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/plugins/mandelbrot.c559
1 files changed, 21 insertions, 538 deletions
diff --git a/apps/plugins/mandelbrot.c b/apps/plugins/mandelbrot.c
index 3d6774a882..106d4b69f7 100644
--- a/apps/plugins/mandelbrot.c
+++ b/apps/plugins/mandelbrot.c
@@ -21,6 +21,7 @@
21#include "plugin.h" 21#include "plugin.h"
22 22
23#ifdef HAVE_LCD_BITMAP // this is not fun on the player 23#ifdef HAVE_LCD_BITMAP // this is not fun on the player
24# include "gray.h"
24 25
25static struct plugin_api* rb; 26static struct plugin_api* rb;
26static char buff[32]; 27static char buff[32];
@@ -34,527 +35,6 @@ static int max_iter;
34static unsigned char *gbuf; 35static unsigned char *gbuf;
35static unsigned int gbuf_size = 0; 36static unsigned int gbuf_size = 0;
36 37
37/*********************** Begin grayscale framework *************************/
38
39/* This is a generic framework to use grayscale display within rockbox
40 * plugins. It obviously does not work for the player.
41 *
42 * If you want to use grayscale display within a plugin, copy this section
43 * (up to "End grayscale framework") into your source and you are able to use
44 * it. For detailed documentation look at the head of each public function.
45 *
46 * It requires a global Rockbox api pointer in "rb" and uses the rockbox
47 * timer api so you cannot use that timer for other purposes while
48 * displaying grayscale.
49 *
50 * The framework consists of 3 sections:
51 *
52 * - internal core functions and definitions
53 * - public core functions
54 * - public optional functions
55 *
56 * Usually you will use functions from the latter two sections in your code.
57 * You can cut out functions from the third section that you do not need in
58 * order to not waste space. Don't forget to cut the prototype as well.
59 */
60
61/**** internal core functions and definitions ****/
62
63/* You do not want to touch these if you don't know exactly what you're
64 * doing. */
65
66#define GRAY_RUNNING 0x0001 /* grayscale overlay is running */
67#define GRAY_DEFERRED_UPDATE 0x0002 /* lcd_update() requested */
68
69typedef struct
70{
71 int x;
72 int by; /* 8-pixel units */
73 int width;
74 int height;
75 int bheight; /* 8-pixel units */
76 int plane_size;
77 int depth; /* number_of_bitplanes = (number_of_grayscales - 1) */
78 int cur_plane; /* for the timer isr */
79 unsigned long randmask; /* mask for random value in graypixel() */
80 unsigned long flags; /* various flags, see #defines */
81 unsigned char *data; /* pointer to start of bitplane data */
82 unsigned long *bitpattern; /* pointer to start of pattern table */
83} tGraybuf;
84
85static tGraybuf *graybuf = NULL;
86
87/** prototypes **/
88
89void timer_isr(void);
90void graypixel(int x, int y, unsigned long pattern);
91void grayinvertmasked(int x, int yb, unsigned char mask);
92
93/** implementation **/
94
95/* timer interrupt handler: display next bitplane */
96void timer_isr(void)
97{
98 rb->lcd_blit(graybuf->data + (graybuf->plane_size * graybuf->cur_plane),
99 graybuf->x, graybuf->by, graybuf->width, graybuf->bheight,
100 graybuf->width);
101
102 if (++graybuf->cur_plane >= graybuf->depth)
103 graybuf->cur_plane = 0;
104
105 if (graybuf->flags & GRAY_DEFERRED_UPDATE) /* lcd_update() requested? */
106 {
107 int x1 = MAX(graybuf->x, 0);
108 int x2 = MIN(graybuf->x + graybuf->width, LCD_WIDTH);
109 int y1 = MAX(graybuf->by << 3, 0);
110 int y2 = MIN((graybuf->by + graybuf->bheight) << 3, LCD_HEIGHT);
111
112 if (y1 > 0) /* refresh part above overlay, full width */
113 rb->lcd_update_rect(0, 0, LCD_WIDTH, y1);
114
115 if (y2 < LCD_HEIGHT) /* refresh part below overlay, full width */
116 rb->lcd_update_rect(0, y2, LCD_WIDTH, LCD_HEIGHT - y2);
117
118 if (x1 > 0) /* refresh part to the left of overlay */
119 rb->lcd_update_rect(0, y1, x1, y2 - y1);
120
121 if (x2 < LCD_WIDTH) /* refresh part to the right of overlay */
122 rb->lcd_update_rect(x2, y1, LCD_WIDTH - x2, y2 - y1);
123
124 graybuf->flags &= ~GRAY_DEFERRED_UPDATE; /* clear request */
125 }
126}
127
128/* Set a pixel to a specific bit pattern
129 * This is the fundamental graphics primitive, asm optimized */
130void graypixel(int x, int y, unsigned long pattern)
131{
132 static short random_buffer;
133 register long address, mask, random;
134
135 /* Some (pseudo-)random function must be used here to shift the bit
136 * pattern randomly, otherwise you would get flicker and/or moire.
137 * Since rand() is relatively slow, I've implemented a simple, but very
138 * fast pseudo-random generator based on linear congruency in assembler.
139 * It delivers 16 pseudo-random bits in each iteration. */
140
141 /* simple but fast pseudo-random generator */
142 asm(
143 "mov.w @%1,%0 \n" /* load last value */
144 "mov #75,r1 \n"
145 "mulu %0,r1 \n" /* multiply by 75 */
146 "sts macl,%0 \n" /* get result */
147 "add #74,%0 \n" /* add another 74 */
148 "mov.w %0,@%1 \n" /* store new value */
149 /* Since the lower bits are not very random: */
150 "shlr8 %0 \n" /* get bits 8..15 (need max. 5) */
151 "and %2,%0 \n" /* mask out unneeded bits */
152 : /* outputs */
153 /* %0 */ "=&r"(random)
154 : /* inputs */
155 /* %1 */ "r"(&random_buffer),
156 /* %2 */ "r"(graybuf->randmask)
157 : /* clobbers */
158 "r1","macl"
159 );
160
161 /* precalculate mask and byte address in first bitplane */
162 asm(
163 "mov %3,%0 \n" /* take y as base for address offset */
164 "shlr2 %0 \n" /* shift right by 3 (= divide by 8) */
165 "shlr %0 \n"
166 "mulu %0,%2 \n" /* multiply with width */
167 "and #7,%3 \n" /* get lower 3 bits of y */
168 "sts macl,%0 \n" /* get mulu result */
169 "add %4,%0 \n" /* add base + x to get final address */
170
171 "mov %3,%1 \n" /* move lower 3 bits of y out of r0 */
172 "mova .pp_table,%3 \n" /* get address of mask table in r0 */
173 "bra .pp_end \n" /* skip the table */
174 "mov.b @(%3,%1),%1 \n" /* get entry from mask table */
175
176 ".align 2 \n"
177 ".pp_table: \n" /* mask table */
178 ".byte 0x01 \n"
179 ".byte 0x02 \n"
180 ".byte 0x04 \n"
181 ".byte 0x08 \n"
182 ".byte 0x10 \n"
183 ".byte 0x20 \n"
184 ".byte 0x40 \n"
185 ".byte 0x80 \n"
186
187 ".pp_end: \n"
188 : /* outputs */
189 /* %0 */ "=&r"(address),
190 /* %1 */ "=&r"(mask)
191 : /* inputs */
192 /* %2 */ "r"(graybuf->width),
193 /* %3 = r0 */ "z"(y),
194 /* %4 */ "r"(graybuf->data + x)
195 : /* clobbers */
196 "macl"
197 );
198
199 /* the hard part: set bits in all bitplanes according to pattern */
200 asm(
201 "cmp/hs %1,%5 \n" /* random >= depth ? */
202 "bf .p_ntrim \n"
203 "sub %1,%5 \n" /* yes: random -= depth */
204 /* it's sufficient to do this once, since the mask guarantees
205 * random < 2 * depth */
206 ".p_ntrim: \n"
207
208 /* calculate some addresses */
209 "mulu %4,%1 \n" /* end address offset */
210 "not %3,r1 \n" /* get inverse mask (for "and") */
211 "sts macl,%1 \n" /* result of mulu */
212 "mulu %4,%5 \n" /* address offset of <random>'th plane */
213 "add %2,%1 \n" /* end offset -> end address */
214 "sts macl,%5 \n" /* result of mulu */
215 "add %2,%5 \n" /* address of <random>'th plane */
216 "bra .p_start1 \n"
217 "mov %5,r2 \n" /* copy address */
218
219 /* first loop: set bits from <random>'th bitplane to last */
220 ".p_loop1: \n"
221 "mov.b @r2,r3 \n" /* get data byte */
222 "shlr %0 \n" /* shift bit mask, sets t bit */
223 "and r1,r3 \n" /* reset bit (-> "white") */
224 "bf .p_white1 \n" /* t=0? -> "white" bit */
225 "or %3,r3 \n" /* set bit ("black" bit) */
226 ".p_white1: \n"
227 "mov.b r3,@r2 \n" /* store data byte */
228 "add %4,r2 \n" /* advance address to next bitplane */
229 ".p_start1: \n"
230 "cmp/hi r2,%1 \n" /* address < end address ? */
231 "bt .p_loop1 \n"
232
233 "bra .p_start2 \n"
234 "nop \n"
235
236 /* second loop: set bits from first to <random-1>'th bitplane
237 * Bit setting works the other way round here to equalize average
238 * execution times for bright and dark pixels */
239 ".p_loop2: \n"
240 "mov.b @%2,r3 \n" /* get data byte */
241 "shlr %0 \n" /* shift bit mask, sets t bit */
242 "or %3,r3 \n" /* set bit (-> "black") */
243 "bt .p_black2 \n" /* t=1? -> "black" bit */
244 "and r1,r3 \n" /* reset bit ("white" bit) */
245 ".p_black2: \n"
246 "mov.b r3,@%2 \n" /* store data byte */
247 "add %4,%2 \n" /* advance address to next bitplane */
248 ".p_start2: \n"
249 "cmp/hi %2,%5 \n" /* address < <random>'th address ? */
250 "bt .p_loop2 \n"
251 : /* outputs */
252 : /* inputs */
253 /* %0 */ "r"(pattern),
254 /* %1 */ "r"(graybuf->depth),
255 /* %2 */ "r"(address),
256 /* %3 */ "r"(mask),
257 /* %4 */ "r"(graybuf->plane_size),
258 /* %5 */ "r"(random)
259 : /* clobbers */
260 "r1", "r2", "r3", "macl"
261 );
262}
263
264/* Invert the bits for 1-8 pixels within the buffer */
265void grayinvertmasked(int x, int by, unsigned char mask)
266{
267 asm(
268 "mulu %4,%5 \n" /* width * by (offset of row) */
269 "mov #0,r1 \n" /* current_plane = 0 */
270 "sts macl,r2 \n" /* get mulu result */
271 "add r2,%1 \n" /* -> address in 1st bitplane */
272
273 ".i_loop: \n"
274 "mov.b @%1,r2 \n" /* get data byte */
275 "add #1,r1 \n" /* current_plane++; */
276 "xor %2,r2 \n" /* invert bits */
277 "mov.b r2,@%1 \n" /* store data byte */
278 "add %3,%1 \n" /* advance address to next bitplane */
279 "cmp/hi r1,%0 \n" /* current_plane < depth ? */
280 "bt .i_loop \n"
281 : /* outputs */
282 : /* inputs */
283 /* %0 */ "r"(graybuf->depth),
284 /* %1 */ "r"(graybuf->data + x),
285 /* %2 */ "r"(mask),
286 /* %3 */ "r"(graybuf->plane_size),
287 /* %4 */ "r"(graybuf->width),
288 /* %5 */ "r"(by)
289 : /* clobbers */
290 "r1", "r2", "macl"
291 );
292}
293
294/*** public core functions ***/
295
296/** prototypes **/
297
298int gray_init_buffer(unsigned char *gbuf, int gbuf_size, int width,
299 int bheight, int depth);
300void gray_release_buffer(void);
301void gray_position_display(int x, int by);
302void gray_show_display(bool enable);
303
304/** implementation **/
305
306/* Prepare the grayscale display buffer
307 *
308 * arguments:
309 * gbuf = pointer to the memory area to use (e.g. plugin buffer)
310 * gbuf_size = max usable size of the buffer
311 * width = width in pixels (1..112)
312 * bheight = height in 8-pixel units (1..8)
313 * depth = desired number of shades - 1 (1..32)
314 *
315 * result:
316 * = depth if there was enough memory
317 * < depth if there wasn't enough memory. The number of displayable
318 * shades is smaller than desired, but it still works
319 * = 0 if there wasn't even enough memory for 1 bitplane (black & white)
320 *
321 * You can request any depth from 1 to 32, not just powers of 2. The routine
322 * performs "graceful degradation" if the memory is not sufficient for the
323 * desired depth. As long as there is at least enough memory for 1 bitplane,
324 * it creates as many bitplanes as fit into memory, although 1 bitplane will
325 * only deliver black & white display.
326 *
327 * The total memory needed can be calculated as follows:
328 * total_mem =
329 * sizeof(tGraymap) (= 48 bytes currently)
330 * + sizeof(long) (= 4 bytes)
331 * + (width * bheight + sizeof(long)) * depth
332 * + 0..3 (longword alignment of grayscale display buffer)
333 */
334int gray_init_buffer(unsigned char *gbuf, int gbuf_size, int width,
335 int bheight, int depth)
336{
337 int possible_depth, plane_size;
338 int i, j;
339
340 if (width > LCD_WIDTH || bheight > (LCD_HEIGHT >> 3) || depth < 1)
341 return 0;
342
343 while ((unsigned long)gbuf & 3) /* the buffer has to be long aligned */
344 {
345 gbuf++;
346 gbuf_size--;
347 }
348
349 plane_size = width * bheight;
350 possible_depth = (gbuf_size - sizeof(tGraybuf) - sizeof(unsigned long))
351 / (plane_size + sizeof(unsigned long));
352
353 if (possible_depth < 1)
354 return 0;
355
356 depth = MIN(depth, 32);
357 depth = MIN(depth, possible_depth);
358
359 graybuf = (tGraybuf *) gbuf; /* global pointer to buffer structure */
360
361 graybuf->x = 0;
362 graybuf->by = 0;
363 graybuf->width = width;
364 graybuf->height = bheight << 3;
365 graybuf->bheight = bheight;
366 graybuf->plane_size = plane_size;
367 graybuf->depth = depth;
368 graybuf->cur_plane = 0;
369 graybuf->flags = 0;
370 graybuf->data = gbuf + sizeof(tGraybuf);
371 graybuf->bitpattern = (unsigned long *) (graybuf->data
372 + depth * plane_size);
373
374 i = depth;
375 j = 8;
376 while (i != 0)
377 {
378 i >>= 1;
379 j--;
380 }
381 graybuf->randmask = 0xFF >> j;
382
383 /* initial state is all white */
384 rb->memset(graybuf->data, 0, depth * plane_size);
385
386 /* Precalculate the bit patterns for all possible pixel values */
387 for (i = 0; i <= depth; i++)
388 {
389 unsigned long pattern = 0;
390 int value = 0;
391
392 for (j = 0; j < depth; j++)
393 {
394 pattern <<= 1;
395 value += i;
396
397 if (value >= depth)
398 value -= depth; /* "white" bit */
399 else
400 pattern |= 1; /* "black" bit */
401 }
402 /* now the lower <depth> bits contain the pattern */
403
404 graybuf->bitpattern[i] = pattern;
405 }
406
407 return depth;
408}
409
410/* Release the grayscale display buffer
411 *
412 * Switches the grayscale overlay off at first if it is still running,
413 * then sets the pointer to NULL.
414 * DO CALL either this function or at least gray_show_display(false)
415 * before you exit, otherwise nasty things may happen.
416 */
417void gray_release_buffer(void)
418{
419 gray_show_display(false);
420 graybuf = NULL;
421}
422
423/* Set position of the top left corner of the grayscale overlay
424 *
425 * arguments:
426 * x = left margin in pixels
427 * by = top margin in 8-pixel units
428 *
429 * You may set this in a way that the overlay spills across the right or
430 * bottom display border. In this case it will simply be clipped by the
431 * LCD controller. You can even set negative values, this will clip at the
432 * left or top border. I did not test it, but the limits may be +127 / -128
433 *
434 * If you use this while the grayscale overlay is running, the now-freed area
435 * will be restored.
436 */
437void gray_position_display(int x, int by)
438{
439 if (graybuf == NULL)
440 return;
441
442 graybuf->x = x;
443 graybuf->by = by;
444
445 if (graybuf->flags & GRAY_RUNNING)
446 graybuf->flags |= GRAY_DEFERRED_UPDATE;
447}
448
449/* Switch the grayscale overlay on or off
450 *
451 * arguments:
452 * enable = true: the grayscale overlay is switched on if initialized
453 * = false: the grayscale overlay is switched off and the regular lcd
454 * content is restored
455 *
456 * DO NOT call lcd_update() or any other api function that directly accesses
457 * the lcd while the grayscale overlay is running! If you need to do
458 * lcd_update() to update something outside the grayscale overlay area, use
459 * gray_deferred_update() instead.
460 *
461 * Other functions to avoid are:
462 * lcd_blit() (obviously), lcd_update_rect(), lcd_set_contrast(),
463 * lcd_set_invert_display(), lcd_set_flip(), lcd_roll()
464 *
465 * The grayscale display consumes ~50 % CPU power (for a full screen overlay,
466 * less if the overlay is smaller) when switched on. You can switch the overlay
467 * on and off as many times as you want.
468 */
469void gray_show_display(bool enable)
470{
471 if (graybuf == NULL)
472 return;
473
474 if (enable)
475 {
476 graybuf->flags |= GRAY_RUNNING;
477 rb->plugin_register_timer(FREQ / 67, 1, timer_isr);
478 }
479 else
480 {
481 rb->plugin_unregister_timer();
482 graybuf->flags &= ~GRAY_RUNNING;
483 rb->lcd_update(); /* restore whatever there was before */
484 }
485}
486
487/*** public optional functions ***/
488
489/* Here are the various graphics primitives. Cut out functions you do not
490 * need in order to keep plugin code size down.
491 */
492
493/** prototypes **/
494
495/* functions affecting the whole display */
496void gray_clear_display(void);
497//void gray_black_display(void);
498//void gray_deferred_update(void);
499
500/* scrolling functions */
501//void gray_scroll_left(int count, bool black_border);
502//void gray_scroll_right(int count, bool black_border);
503//void gray_scroll_up8(bool black_border);
504//void gray_scroll_down8(bool black_border);
505//void gray_scroll_up1(bool black_border);
506//void gray_scroll_down1(bool black_border);
507
508/* pixel functions */
509void gray_drawpixel(int x, int y, int brightness);
510//void gray_invertpixel(int x, int y);
511
512/* line functions */
513//void gray_drawline(int x1, int y1, int x2, int y2, int brightness);
514//void gray_invertline(int x1, int y1, int x2, int y2);
515
516/* rectangle functions */
517//void gray_drawrect(int x1, int y1, int x2, int y2, int brightness);
518//void gray_fillrect(int x1, int y1, int x2, int y2, int brightness);
519//void gray_invertrect(int x1, int y1, int x2, int y2);
520
521/* bitmap functions */
522//void gray_drawgraymap(unsigned char *src, int x, int y, int nx, int ny,
523// int stride);
524//void gray_drawbitmap(unsigned char *src, int x, int y, int nx, int ny,
525// int stride, bool draw_bg, int fg_brightness,
526// int bg_brightness);
527
528/** implementation **/
529
530/* Clear the grayscale display (sets all pixels to white)
531 */
532void gray_clear_display(void)
533{
534 if (graybuf == NULL)
535 return;
536
537 rb->memset(graybuf->data, 0, graybuf->depth * graybuf->plane_size);
538}
539
540/* Set a pixel to a specific gray value
541 *
542 * brightness is 0..255 (black to white) regardless of real bit depth
543 */
544void gray_drawpixel(int x, int y, int brightness)
545{
546 if (graybuf == NULL || x >= graybuf->width || y >= graybuf->height
547 || brightness > 255)
548 return;
549
550 graypixel(x, y, graybuf->bitpattern[(brightness
551 * (graybuf->depth + 1)) >> 8]);
552}
553
554
555/*********************** end grayscale framework ***************************/
556
557
558 38
559void init_mandelbrot_set(void){ 39void init_mandelbrot_set(void){
560 x_min = -5<<25; // -2.0<<26 40 x_min = -5<<25; // -2.0<<26
@@ -603,17 +83,17 @@ void calc_mandelbrot_set(void){
603 y = 2 * x * y + b; 83 y = 2 * x * y + b;
604 x = x2 - y2 + a; 84 x = x2 - y2 + a;
605 } 85 }
606 86
607 // "coloring" 87 // "coloring"
608 brightness = 0; 88 brightness = 0;
609 if (n_iter > max_iter){ 89 if (n_iter > max_iter){
610 brightness = 0; // black 90 brightness = 0; // black
611 } else { 91 } else {
612 brightness = 255 - (31 * (n_iter & 7)); 92 brightness = 255 - (31 * (n_iter & 7));
613 } 93 }
614 94
615 gray_drawpixel( x_pixel, y_pixel, brightness); 95 gray_drawpixel( x_pixel, y_pixel, brightness);
616 } 96 }
617 } 97 }
618} 98}
619 99
@@ -627,19 +107,22 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
627 rb = api; 107 rb = api;
628 (void)parameter; 108 (void)parameter;
629 109
110 /* This plugin uses the grayscale framework, so initialize */
111 gray_init(api);
112
630 /* get the remainder of the plugin buffer */ 113 /* get the remainder of the plugin buffer */
631 gbuf = (unsigned char *) rb->plugin_get_buffer(&gbuf_size); 114 gbuf = (unsigned char *) rb->plugin_get_buffer(&gbuf_size);
632 115
633 /* initialize the grayscale buffer: 116 /* initialize the grayscale buffer:
634 * 112 pixels wide, 8 rows (64 pixels) high, (try to) reserve 117 * 112 pixels wide, 8 rows (64 pixels) high, (try to) reserve
635 * 16 bitplanes for 17 shades of gray.*/ 118 * 16 bitplanes for 17 shades of gray.*/
636 grayscales = gray_init_buffer(gbuf, gbuf_size, 112, 8, 16) + 1; 119 grayscales = gray_init_buffer(gbuf, gbuf_size, 112, 8, 16, NULL) + 1;
637 if (grayscales != 17){ 120 if (grayscales != 17){
638 rb->snprintf(buff, sizeof(buff), "%d", grayscales); 121 rb->snprintf(buff, sizeof(buff), "%d", grayscales);
639 rb->lcd_puts(0, 1, buff); 122 rb->lcd_puts(0, 1, buff);
640 rb->lcd_update(); 123 rb->lcd_update();
641 rb->sleep(HZ*2); 124 rb->sleep(HZ*2);
642 return(0); 125 return(0);
643 } 126 }
644 127
645 gray_show_display(true); /* switch on grayscale overlay */ 128 gray_show_display(true); /* switch on grayscale overlay */
@@ -667,7 +150,7 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
667 delta = (x_max - x_min) >> 3; 150 delta = (x_max - x_min) >> 3;
668 redraw = true; 151 redraw = true;
669 break; 152 break;
670 153
671 154
672 case BUTTON_PLAY: 155 case BUTTON_PLAY:
673 x_min += ((delta>>13)*(lcd_aspect_ratio>>13)); 156 x_min += ((delta>>13)*(lcd_aspect_ratio>>13));
@@ -677,7 +160,7 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
677 delta = (x_max - x_min) >> 3; 160 delta = (x_max - x_min) >> 3;
678 redraw = true; 161 redraw = true;
679 break; 162 break;
680 163
681 case BUTTON_UP: 164 case BUTTON_UP:
682 y_min -= delta; 165 y_min -= delta;
683 y_max -= delta; 166 y_max -= delta;