Sansa clip zip: update plugins (jewels, pegbox, rockpaint, star)
[maemo-rb.git] / apps / plugins / rockpaint.c
blob9ec289fce436453f4b6c3cc45633f6f3be2f3fb4
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2006 Antoine Cellerier <dionoea -at- videolan -dot- org>
11 * Based on parts of rockpaint 0.45, Copyright (C) 2005 Eli Sherer
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
23 /**
24 * TODO:
25 * - implement 2 layers with alpha colors
26 * - take brush width into account when drawing shapes
27 * - handle bigger than screen bitmaps
30 #include "plugin.h"
31 #include "lib/pluginlib_bmp.h"
32 #include "lib/rgb_hsv.h"
33 #include "lib/playback_control.h"
35 #include "pluginbitmaps/rockpaint.h"
36 #include "pluginbitmaps/rockpaint_hsvrgb.h"
39 /***********************************************************************
40 * Buttons
41 ***********************************************************************/
43 #if CONFIG_KEYPAD == IRIVER_H300_PAD
44 #define ROCKPAINT_QUIT BUTTON_OFF
45 #define ROCKPAINT_DRAW BUTTON_SELECT
46 #define ROCKPAINT_MENU BUTTON_ON
47 #define ROCKPAINT_TOOLBAR BUTTON_REC
48 #define ROCKPAINT_TOOLBAR2 BUTTON_MODE
49 #define ROCKPAINT_UP BUTTON_UP
50 #define ROCKPAINT_DOWN BUTTON_DOWN
51 #define ROCKPAINT_LEFT BUTTON_LEFT
52 #define ROCKPAINT_RIGHT BUTTON_RIGHT
54 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
55 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
56 #define ROCKPAINT_QUIT ( ~BUTTON_MAIN )
57 #define ROCKPAINT_DRAW BUTTON_SELECT
58 #define ROCKPAINT_MENU ( BUTTON_SELECT | BUTTON_MENU )
59 #define ROCKPAINT_TOOLBAR ( BUTTON_MENU | BUTTON_LEFT )
60 #define ROCKPAINT_TOOLBAR2 ( BUTTON_MENU | BUTTON_RIGHT )
61 #define ROCKPAINT_UP BUTTON_MENU
62 #define ROCKPAINT_DOWN BUTTON_PLAY
63 #define ROCKPAINT_LEFT BUTTON_LEFT
64 #define ROCKPAINT_RIGHT BUTTON_RIGHT
66 #elif ( CONFIG_KEYPAD == IAUDIO_X5M5_PAD )
67 #define ROCKPAINT_QUIT BUTTON_POWER
68 #define ROCKPAINT_DRAW BUTTON_SELECT
69 #define ROCKPAINT_MENU BUTTON_PLAY
70 #define ROCKPAINT_TOOLBAR BUTTON_REC
71 #define ROCKPAINT_TOOLBAR2 ( BUTTON_REC | BUTTON_LEFT )
72 #define ROCKPAINT_UP BUTTON_UP
73 #define ROCKPAINT_DOWN BUTTON_DOWN
74 #define ROCKPAINT_LEFT BUTTON_LEFT
75 #define ROCKPAINT_RIGHT BUTTON_RIGHT
77 #elif CONFIG_KEYPAD == GIGABEAT_PAD
78 #define ROCKPAINT_QUIT BUTTON_POWER
79 #define ROCKPAINT_DRAW BUTTON_SELECT
80 #define ROCKPAINT_MENU BUTTON_MENU
81 #define ROCKPAINT_TOOLBAR BUTTON_A
82 #define ROCKPAINT_TOOLBAR2 ( BUTTON_A | BUTTON_LEFT )
83 #define ROCKPAINT_UP BUTTON_UP
84 #define ROCKPAINT_DOWN BUTTON_DOWN
85 #define ROCKPAINT_LEFT BUTTON_LEFT
86 #define ROCKPAINT_RIGHT BUTTON_RIGHT
88 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
89 (CONFIG_KEYPAD == SANSA_C200_PAD)
90 #define ROCKPAINT_QUIT BUTTON_POWER
91 #define ROCKPAINT_DRAW BUTTON_SELECT
92 #define ROCKPAINT_MENU ( BUTTON_SELECT | BUTTON_POWER )
93 #define ROCKPAINT_TOOLBAR BUTTON_REC
94 #define ROCKPAINT_TOOLBAR2 ( BUTTON_REC | BUTTON_LEFT )
95 #define ROCKPAINT_UP BUTTON_UP
96 #define ROCKPAINT_DOWN BUTTON_DOWN
97 #define ROCKPAINT_LEFT BUTTON_LEFT
98 #define ROCKPAINT_RIGHT BUTTON_RIGHT
100 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
101 #define ROCKPAINT_QUIT (BUTTON_HOME|BUTTON_REPEAT)
102 #define ROCKPAINT_DRAW BUTTON_SELECT
103 #define ROCKPAINT_MENU ( BUTTON_SELECT | BUTTON_DOWN )
104 #define ROCKPAINT_TOOLBAR ( BUTTON_SELECT | BUTTON_LEFT )
105 #define ROCKPAINT_TOOLBAR2 ( BUTTON_SELECT | BUTTON_RIGHT )
106 #define ROCKPAINT_UP BUTTON_UP
107 #define ROCKPAINT_DOWN BUTTON_DOWN
108 #define ROCKPAINT_LEFT BUTTON_LEFT
109 #define ROCKPAINT_RIGHT BUTTON_RIGHT
111 #elif ( CONFIG_KEYPAD == IRIVER_H10_PAD )
112 #define ROCKPAINT_QUIT BUTTON_POWER
113 #define ROCKPAINT_DRAW BUTTON_FF
114 #define ROCKPAINT_MENU BUTTON_PLAY
115 #define ROCKPAINT_TOOLBAR BUTTON_REW
116 #define ROCKPAINT_TOOLBAR2 ( BUTTON_REW | BUTTON_LEFT )
117 #define ROCKPAINT_UP BUTTON_SCROLL_UP
118 #define ROCKPAINT_DOWN BUTTON_SCROLL_DOWN
119 #define ROCKPAINT_LEFT BUTTON_LEFT
120 #define ROCKPAINT_RIGHT BUTTON_RIGHT
122 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
123 #define ROCKPAINT_QUIT BUTTON_BACK
124 #define ROCKPAINT_DRAW BUTTON_SELECT
125 #define ROCKPAINT_MENU BUTTON_MENU
126 #define ROCKPAINT_TOOLBAR BUTTON_PLAY
127 #define ROCKPAINT_TOOLBAR2 ( BUTTON_PLAY | BUTTON_LEFT )
128 #define ROCKPAINT_UP BUTTON_UP
129 #define ROCKPAINT_DOWN BUTTON_DOWN
130 #define ROCKPAINT_LEFT BUTTON_LEFT
131 #define ROCKPAINT_RIGHT BUTTON_RIGHT
133 #elif ( CONFIG_KEYPAD == COWON_D2_PAD )
134 #define ROCKPAINT_QUIT BUTTON_POWER
135 #define ROCKPAINT_MENU BUTTON_MENU
137 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
138 #define ROCKPAINT_QUIT BUTTON_BACK
139 #define ROCKPAINT_DRAW BUTTON_SELECT
140 #define ROCKPAINT_MENU BUTTON_MENU
141 #define ROCKPAINT_TOOLBAR BUTTON_PLAY
142 #define ROCKPAINT_TOOLBAR2 ( BUTTON_PLAY | BUTTON_LEFT )
143 #define ROCKPAINT_UP BUTTON_UP
144 #define ROCKPAINT_DOWN BUTTON_DOWN
145 #define ROCKPAINT_LEFT BUTTON_LEFT
146 #define ROCKPAINT_RIGHT BUTTON_RIGHT
148 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
149 #define ROCKPAINT_QUIT BUTTON_POWER
150 #define ROCKPAINT_DRAW BUTTON_SELECT
151 #define ROCKPAINT_MENU BUTTON_MENU
152 #define ROCKPAINT_TOOLBAR BUTTON_VIEW
153 #define ROCKPAINT_TOOLBAR2 BUTTON_PLAYLIST
154 #define ROCKPAINT_UP BUTTON_UP
155 #define ROCKPAINT_DOWN BUTTON_DOWN
156 #define ROCKPAINT_LEFT BUTTON_LEFT
157 #define ROCKPAINT_RIGHT BUTTON_RIGHT
159 #elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
160 #define ROCKPAINT_QUIT BUTTON_POWER
161 #define ROCKPAINT_DRAW BUTTON_PLAY
162 #define ROCKPAINT_MENU BUTTON_MENU
163 #define ROCKPAINT_TOOLBAR BUTTON_PREV
164 #define ROCKPAINT_TOOLBAR2 BUTTON_NEXT
165 #define ROCKPAINT_UP BUTTON_UP
166 #define ROCKPAINT_DOWN BUTTON_DOWN
167 #define ROCKPAINT_LEFT BUTTON_LEFT
168 #define ROCKPAINT_RIGHT BUTTON_RIGHT
170 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
171 #define ROCKPAINT_QUIT BUTTON_POWER
172 #define ROCKPAINT_DRAW BUTTON_PLAY
173 #define ROCKPAINT_MENU BUTTON_MENU
174 #define ROCKPAINT_TOOLBAR BUTTON_RIGHT
175 #define ROCKPAINT_TOOLBAR2 BUTTON_LEFT
176 #define ROCKPAINT_UP BUTTON_UP
177 #define ROCKPAINT_DOWN BUTTON_DOWN
178 #define ROCKPAINT_LEFT BUTTON_PREV
179 #define ROCKPAINT_RIGHT BUTTON_NEXT
181 #elif ( CONFIG_KEYPAD == ONDAVX747_PAD )
182 #define ROCKPAINT_QUIT BUTTON_POWER
183 #define ROCKPAINT_MENU BUTTON_MENU
185 #elif ( CONFIG_KEYPAD == ONDAVX777_PAD )
186 #define ROCKPAINT_QUIT BUTTON_POWER
188 #elif CONFIG_KEYPAD == MROBE500_PAD
189 #define ROCKPAINT_QUIT BUTTON_POWER
191 #elif ( CONFIG_KEYPAD == SAMSUNG_YH_PAD )
192 #define ROCKPAINT_QUIT BUTTON_REC
193 #define ROCKPAINT_DRAW BUTTON_PLAY
194 #define ROCKPAINT_MENU BUTTON_FFWD
195 #define ROCKPAINT_TOOLBAR BUTTON_REW
196 #define ROCKPAINT_TOOLBAR2 ( BUTTON_REW | BUTTON_LEFT )
197 #define ROCKPAINT_UP BUTTON_UP
198 #define ROCKPAINT_DOWN BUTTON_DOWN
199 #define ROCKPAINT_LEFT BUTTON_LEFT
200 #define ROCKPAINT_RIGHT BUTTON_RIGHT
202 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
203 #define ROCKPAINT_QUIT BUTTON_REC
204 #define ROCKPAINT_DRAW BUTTON_PLAY
205 #define ROCKPAINT_MENU BUTTON_MENU
206 #define ROCKPAINT_TOOLBAR BUTTON_OK
207 #define ROCKPAINT_TOOLBAR2 BUTTON_CANCEL
208 #define ROCKPAINT_UP BUTTON_UP
209 #define ROCKPAINT_DOWN BUTTON_DOWN
210 #define ROCKPAINT_LEFT BUTTON_PREV
211 #define ROCKPAINT_RIGHT BUTTON_NEXT
213 #elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
214 #define ROCKPAINT_QUIT BUTTON_POWER
215 #define ROCKPAINT_DRAW BUTTON_SELECT
216 #define ROCKPAINT_MENU BUTTON_PLAYPAUSE
217 #define ROCKPAINT_TOOLBAR BUTTON_BACK
218 #define ROCKPAINT_TOOLBAR2 (BUTTON_BACK|BUTTON_PLAYPAUSE)
219 #define ROCKPAINT_UP BUTTON_UP
220 #define ROCKPAINT_DOWN BUTTON_DOWN
221 #define ROCKPAINT_LEFT BUTTON_LEFT
222 #define ROCKPAINT_RIGHT BUTTON_RIGHT
224 #elif CONFIG_KEYPAD == SANSA_CLIP_PAD
225 #define ROCKPAINT_QUIT BUTTON_POWER
226 #define ROCKPAINT_DRAW BUTTON_SELECT
227 #define ROCKPAINT_MENU BUTTON_HOME
228 #define ROCKPAINT_TOOLBAR BUTTON_VOL_UP
229 #define ROCKPAINT_TOOLBAR2 BUTTON_VOL_DOWN
230 #define ROCKPAINT_UP BUTTON_UP
231 #define ROCKPAINT_DOWN BUTTON_DOWN
232 #define ROCKPAINT_LEFT BUTTON_LEFT
233 #define ROCKPAINT_RIGHT BUTTON_RIGHT
235 #else
236 #error "Please define keys for this keypad"
237 #endif
239 #ifdef HAVE_TOUCHSCREEN
240 #ifndef ROCKPAINT_QUIT
241 #define ROCKPAINT_QUIT BUTTON_TOPLEFT
242 #endif
243 #ifndef ROCKPAINT_DRAW
244 #define ROCKPAINT_DRAW BUTTON_CENTER
245 #endif
246 #ifndef ROCKPAINT_MENU
247 #define ROCKPAINT_MENU BUTTON_TOPRIGHT
248 #endif
249 #ifndef ROCKPAINT_TOOLBAR
250 #define ROCKPAINT_TOOLBAR BUTTON_BOTTOMLEFT
251 #endif
252 #ifndef ROCKPAINT_TOOLBAR2
253 #define ROCKPAINT_TOOLBAR2 BUTTON_BOTTOMRIGHT
254 #endif
255 #ifndef ROCKPAINT_UP
256 #define ROCKPAINT_UP BUTTON_TOPMIDDLE
257 #endif
258 #ifndef ROCKPAINT_DOWN
259 #define ROCKPAINT_DOWN BUTTON_BOTTOMMIDDLE
260 #endif
261 #ifndef ROCKPAINT_LEFT
262 #define ROCKPAINT_LEFT BUTTON_MIDLEFT
263 #endif
264 #ifndef ROCKPAINT_RIGHT
265 #define ROCKPAINT_RIGHT BUTTON_MIDRIGHT
266 #endif
267 #endif
269 /***********************************************************************
270 * Palette Default Colors
271 ***********************************************************************/
272 #define COLOR_BLACK LCD_RGBPACK(0,0,0)
273 #define COLOR_WHITE LCD_RGBPACK(255,255,255)
274 #define COLOR_DARKGRAY LCD_RGBPACK(128,128,128)
275 #define COLOR_LIGHTGRAY LCD_RGBPACK(192,192,192)
276 #define COLOR_RED LCD_RGBPACK(128,0,0)
277 #define COLOR_LIGHTRED LCD_RGBPACK(255,0,0)
278 #define COLOR_DARKYELLOW LCD_RGBPACK(128,128,0)
279 #define COLOR_YELLOW LCD_RGBPACK(255,255,0)
280 #define COLOR_GREEN LCD_RGBPACK(0,128,0)
281 #define COLOR_LIGHTGREN LCD_RGBPACK(0,255,0)
282 #define COLOR_CYAN LCD_RGBPACK(0,128,128)
283 #define COLOR_LIGHTCYAN LCD_RGBPACK(0,255,255)
284 #define COLOR_BLUE LCD_RGBPACK(0,0,128)
285 #define COLOR_LIGHTBLUE LCD_RGBPACK(0,0,255)
286 #define COLOR_PURPLE LCD_RGBPACK(128,0,128)
287 #define COLOR_PINK LCD_RGBPACK(255,0,255)
288 #define COLOR_BROWN LCD_RGBPACK(128,64,0)
289 #define COLOR_LIGHTBROWN LCD_RGBPACK(255,128,64)
291 /***********************************************************************
292 * Program Colors
293 ***********************************************************************/
294 #define ROCKPAINT_PALETTE LCD_RGBPACK(0,64,128)
295 #define ROCKPAINT_SELECTED LCD_RGBPACK(128,192,255)
297 #define ROWS LCD_HEIGHT
298 #define COLS LCD_WIDTH
301 * Toolbar positioning stuff ... don't read this unless you really need to
303 * TB Toolbar
304 * SP Separator
305 * SC Selected Color
306 * PL Palette
307 * TL Tools
310 /* Separator sizes */
311 #define TB_SP_MARGIN 3
312 #define TB_SP_WIDTH (2+2*TB_SP_MARGIN)
314 /* Selected color sizes */
315 #define TB_SC_SIZE 12
317 /* Palette sizes */
318 #define TB_PL_COLOR_SIZE 7
319 #define TB_PL_COLOR_SPACING 2
320 #define TB_PL_WIDTH ( 9 * TB_PL_COLOR_SIZE + 8 * TB_PL_COLOR_SPACING )
321 #define TB_PL_HEIGHT ( TB_PL_COLOR_SIZE * 2 + TB_PL_COLOR_SPACING )
323 /* Tools sizes */
324 #define TB_TL_SIZE 8
325 #define TB_TL_SPACING 2
326 #define TB_TL_WIDTH ( 7 * ( TB_TL_SIZE + TB_TL_SPACING ) - TB_TL_SPACING )
327 #define TB_TL_HEIGHT ( 2 * TB_TL_SIZE + TB_TL_SPACING )
329 /* Menu button size ... gruik */
330 #define TB_MENU_MIN_WIDTH 30
332 /* Selected colors position */
333 #define TB_SC_FG_TOP 2
334 #define TB_SC_FG_LEFT 2
335 #define TB_SC_BG_TOP (TB_SC_FG_TOP+TB_PL_COLOR_SIZE*2+TB_PL_COLOR_SPACING-TB_SC_SIZE)
336 #define TB_SC_BG_LEFT (TB_SC_FG_LEFT+TB_PL_COLOR_SIZE*2+TB_PL_COLOR_SPACING-TB_SC_SIZE)
338 /* Palette position */
339 #define TB_PL_TOP TB_SC_FG_TOP
340 #define TB_PL_LEFT (TB_SC_BG_LEFT + TB_SC_SIZE + TB_PL_COLOR_SPACING)
342 /* Tools position */
343 #define TB_TL_TOP TB_SC_FG_TOP
344 #define TB_TL_LEFT ( TB_PL_LEFT + TB_PL_WIDTH-1 + TB_SP_WIDTH )
346 #if TB_TL_LEFT + TB_TL_WIDTH + TB_MENU_MIN_WIDTH >= LCD_WIDTH
347 #undef TB_TL_TOP
348 #undef TB_TL_LEFT
349 #define TB_TL_TOP ( TB_PL_TOP + TB_PL_HEIGHT + 4 )
350 #define TB_TL_LEFT TB_SC_FG_LEFT
351 #endif
353 /* Menu button position */
354 #define TB_MENU_TOP ( TB_TL_TOP + (TB_TL_HEIGHT-8)/2 )
355 #define TB_MENU_LEFT ( TB_TL_LEFT + TB_TL_WIDTH-1 + TB_SP_WIDTH )
357 #define TB_HEIGHT ( TB_TL_TOP + TB_TL_HEIGHT + 1 )
360 static void draw_pixel(int x,int y);
361 static void draw_line( int x1, int y1, int x2, int y2 );
362 static void draw_rect( int x1, int y1, int x2, int y2 );
363 static void draw_rect_full( int x1, int y1, int x2, int y2 );
364 static void draw_toolbars(bool update);
365 static void inv_cursor(bool update);
366 static void restore_screen(void);
367 static void clear_drawing(void);
368 static void reset_tool(void);
369 static void goto_menu(void);
370 static int load_bitmap( const char *filename );
371 static int save_bitmap( char *filename );
373 /***********************************************************************
374 * Global variables
375 ***********************************************************************/
377 static int drawcolor=0; /* Current color (in palette) */
378 static int bgdrawcolor=9; /* Current background color (in palette) */
379 static int img_height = ROWS;
380 static int img_width = COLS;
381 bool isbg = false; /* gruik ugly hack alert */
383 static int preview=false; /* Is preview mode on ? */
385 /* TODO: clean this up */
386 static int x=0, y=0; /* cursor position */
387 static int prev_x=-1, prev_y=-1; /* previous saved cursor position */
388 static int prev_x2=-1, prev_y2=-1;
389 static int prev_x3=-1, prev_y3=-1;
392 static int bsize=1; /* brush size */
393 static int bspeed=1; /* brush speed */
395 enum Tools { Brush = 0, /* Regular brush */
396 Fill = 1, /* Fill a shape with current color */
397 SelectRectangle = 2,
398 ColorPicker = 3, /* Pick a color */
399 Line = 4, /* Draw a line between two points */
400 Unused = 5, /* THIS IS UNUSED ... */
401 Curve = 6,
402 Text = 7,
403 Rectangle = 8, /* Draw a rectangle */
404 RectangleFull = 9,
405 Oval = 10, /* Draw an oval */
406 OvalFull = 11,
407 LinearGradient = 12,
408 RadialGradient = 13
411 enum States { State0 = 0, /* initial state */
412 State1,
413 State2,
414 State3,
417 enum Tools tool = Brush;
418 enum States state = State0;
420 static bool quit=false;
421 static int gridsize=0;
423 static fb_data rp_colors[18] =
425 COLOR_BLACK, COLOR_DARKGRAY, COLOR_RED, COLOR_DARKYELLOW,
426 COLOR_GREEN, COLOR_CYAN, COLOR_BLUE, COLOR_PURPLE, COLOR_BROWN,
427 COLOR_WHITE, COLOR_LIGHTGRAY, COLOR_LIGHTRED, COLOR_YELLOW,
428 COLOR_LIGHTGREN, COLOR_LIGHTCYAN, COLOR_LIGHTBLUE, COLOR_PINK,
429 COLOR_LIGHTBROWN
432 static fb_data save_buffer[ ROWS*COLS ];
434 struct tool_func {
435 void (*state_func)(void);
436 void (*preview_func)(void);
439 struct incdec_ctx {
440 int max;
441 int step[2];
442 bool wrap;
444 struct incdec_ctx incdec_x = { COLS, { 1, 4}, true };
445 struct incdec_ctx incdec_y = { ROWS, { 1, 4}, true };
447 /* Maximum string size allowed for the text tool */
448 #define MAX_TEXT 256
450 union buf
452 /* Used by fill and gradient algorithms */
453 struct
455 short x;
456 short y;
457 } coord[ ROWS*COLS ];
459 /* Used by bezier curve algorithms */
460 struct
462 short x1, y1;
463 short x2, y2;
464 short x3, y3;
465 short x4, y4;
466 short depth;
467 } bezier[ (ROWS*COLS)/5 ]; /* We have 4.5 times more data per struct
468 * than coord ... so we divide to take
469 * less memory. */
471 /* Used to cut/copy/paste data */
472 fb_data clipboard[ ROWS*COLS ];
474 /* Used for text mode */
475 struct
477 char text[MAX_TEXT];
478 char font[MAX_PATH];
479 bool initialized;
480 size_t cache_used;
481 /* fonts from cache_first to cache_last are stored. */
482 int cache_first;
483 int cache_last;
484 /* save these so that cache can be re-used next time. */
485 int fvi;
486 int si;
487 } text;
490 static union buf *buffer;
491 static bool audio_buf = false;
493 /* Current filename */
494 static char filename[MAX_PATH];
496 static bool incdec_value(int *pval, struct incdec_ctx *ctx, bool inc, bool bigstep)
498 bool of = true;
499 int step = ctx->step[bigstep?1:0];
500 step = inc?step: -step;
501 *pval += step;
502 if (ctx->wrap)
504 if (*pval < 0) *pval += ctx->max;
505 else if (*pval >= ctx->max) *pval -= ctx->max;
506 else of = false;
508 else
510 if (*pval < 0) *pval = 0;
511 else if (*pval > ctx->max) *pval = ctx->max;
512 else of = false;
514 return of;
517 /***********************************************************************
518 * Offscreen buffer/Text/Fonts handling
520 * Parts of code taken from firmware/drivers/lcd-16bit.c
521 ***********************************************************************/
522 static void buffer_mono_bitmap_part(
523 fb_data *buf, int buf_width, int buf_height,
524 const unsigned char *src, int src_x, int src_y,
525 int stride, int x, int y, int width, int height )
526 /* this function only draws the foreground part of the bitmap */
528 const unsigned char *src_end;
529 fb_data *dst, *dst_end;
530 unsigned fgcolor = rb->lcd_get_foreground();
532 /* nothing to draw? */
533 if( ( width <= 0 ) || ( height <= 0 ) || ( x >= buf_width )
534 || ( y >= buf_height ) || ( x + width <= 0 ) || ( y + height <= 0 ) )
535 return;
537 /* clipping */
538 if( x < 0 )
540 width += x;
541 src_x -= x;
542 x = 0;
544 if( y < 0 )
546 height += y;
547 src_y -= y;
548 y = 0;
550 if( x + width > buf_width )
551 width = buf_width - x;
552 if( y + height > buf_height )
553 height = buf_height - y;
555 src += stride * (src_y >> 3) + src_x; /* move starting point */
556 src_y &= 7;
557 src_end = src + width;
559 dst = buf + y*buf_width + x;
563 const unsigned char *src_col = src++;
564 unsigned data = *src_col >> src_y;
565 fb_data *dst_col = dst++;
566 int numbits = 8 - src_y;
568 dst_end = dst_col + height * buf_width;
571 if( data & 0x01 )
572 *dst_col = fgcolor; /* FIXME ? */
574 dst_col += buf_width;
576 data >>= 1;
577 if( --numbits == 0 )
579 src_col += stride;
580 data = *src_col;
581 numbits = 8;
583 } while( dst_col < dst_end );
584 } while( src < src_end );
587 /* draw alpha bitmap for anti-alias font */
588 #define ALPHA_COLOR_FONT_DEPTH 2
589 #define ALPHA_COLOR_LOOKUP_SHIFT (1 << ALPHA_COLOR_FONT_DEPTH)
590 #define ALPHA_COLOR_LOOKUP_SIZE ((1 << ALPHA_COLOR_LOOKUP_SHIFT) - 1)
591 #define ALPHA_COLOR_PIXEL_PER_BYTE (8 >> ALPHA_COLOR_FONT_DEPTH)
592 #define ALPHA_COLOR_PIXEL_PER_WORD (32 >> ALPHA_COLOR_FONT_DEPTH)
593 #ifdef CPU_ARM
594 #define BLEND_INIT do {} while (0)
595 #define BLEND_START(acc, color, alpha) \
596 asm volatile("mul %0, %1, %2" : "=&r" (acc) : "r" (color), "r" (alpha))
597 #define BLEND_CONT(acc, color, alpha) \
598 asm volatile("mla %0, %1, %2, %0" : "+&r" (acc) : "r" (color), "r" (alpha))
599 #define BLEND_OUT(acc) do {} while (0)
600 #elif defined(CPU_COLDFIRE)
601 #define ALPHA_BITMAP_READ_WORDS
602 #define BLEND_INIT coldfire_set_macsr(EMAC_UNSIGNED)
603 #define BLEND_START(acc, color, alpha) \
604 asm volatile("mac.l %0, %1, %%acc0" :: "%d" (color), "d" (alpha))
605 #define BLEND_CONT BLEND_START
606 #define BLEND_OUT(acc) asm volatile("movclr.l %%acc0, %0" : "=d" (acc))
607 #else
608 #define BLEND_INIT do {} while (0)
609 #define BLEND_START(acc, color, alpha) ((acc) = (color) * (alpha))
610 #define BLEND_CONT(acc, color, alpha) ((acc) += (color) * (alpha))
611 #define BLEND_OUT(acc) do {} while (0)
612 #endif
614 /* Blend the given two colors */
615 static inline unsigned blend_two_colors(unsigned c1, unsigned c2, unsigned a)
617 a += a >> (ALPHA_COLOR_LOOKUP_SHIFT - 1);
618 #if (LCD_PIXELFORMAT == RGB565SWAPPED)
619 c1 = swap16(c1);
620 c2 = swap16(c2);
621 #endif
622 unsigned c1l = (c1 | (c1 << 16)) & 0x07e0f81f;
623 unsigned c2l = (c2 | (c2 << 16)) & 0x07e0f81f;
624 unsigned p;
625 BLEND_START(p, c1l, a);
626 BLEND_CONT(p, c2l, ALPHA_COLOR_LOOKUP_SIZE + 1 - a);
627 BLEND_OUT(p);
628 p = (p >> ALPHA_COLOR_LOOKUP_SHIFT) & 0x07e0f81f;
629 p |= (p >> 16);
630 #if (LCD_PIXELFORMAT == RGB565SWAPPED)
631 return swap16(p);
632 #else
633 return p;
634 #endif
637 static void buffer_alpha_bitmap_part(
638 fb_data *buf, int buf_width, int buf_height,
639 const unsigned char *src, int src_x, int src_y,
640 int stride, int x, int y, int width, int height )
642 fb_data *dst;
643 unsigned fg_pattern = rb->lcd_get_foreground();
645 /* nothing to draw? */
646 if ((width <= 0) || (height <= 0) || (x >= buf_width) ||
647 (y >= buf_height) || (x + width <= 0) || (y + height <= 0))
648 return;
650 /* initialize blending */
651 BLEND_INIT;
653 /* clipping */
654 if (x < 0)
656 width += x;
657 src_x -= x;
658 x = 0;
660 if (y < 0)
662 height += y;
663 src_y -= y;
664 y = 0;
666 if (x + width > buf_width)
667 width = buf_width - x;
668 if (y + height > buf_height)
669 height = buf_height - y;
671 dst = buf + y*buf_width + x;
673 int col, row = height;
674 unsigned data, pixels;
675 unsigned skip_end = (stride - width);
676 unsigned skip_start = src_y * stride + src_x;
678 #ifdef ALPHA_BITMAP_READ_WORDS
679 uint32_t *src_w = (uint32_t *)((uintptr_t)src & ~3);
680 skip_start += ALPHA_COLOR_PIXEL_PER_BYTE * ((uintptr_t)src & 3);
681 src_w += skip_start / ALPHA_COLOR_PIXEL_PER_WORD;
682 data = letoh32(*src_w++);
683 #else
684 src += skip_start / ALPHA_COLOR_PIXEL_PER_BYTE;
685 data = *src;
686 #endif
687 pixels = skip_start % ALPHA_COLOR_PIXEL_PER_WORD;
688 data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
689 #ifdef ALPHA_BITMAP_READ_WORDS
690 pixels = 8 - pixels;
691 #endif
695 col = width;
696 #ifdef ALPHA_BITMAP_READ_WORDS
697 #define UPDATE_SRC_ALPHA do { \
698 if (--pixels) \
699 data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
700 else \
702 data = letoh32(*src_w++); \
703 pixels = ALPHA_COLOR_PIXEL_PER_WORD; \
705 } while (0)
706 #elif ALPHA_COLOR_PIXEL_PER_BYTE == 2
707 #define UPDATE_SRC_ALPHA do { \
708 if (pixels ^= 1) \
709 data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
710 else \
711 data = *(++src); \
712 } while (0)
713 #else
714 #define UPDATE_SRC_ALPHA do { \
715 if (pixels = (++pixels % ALPHA_COLOR_PIXEL_PER_BYTE)) \
716 data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
717 else \
718 data = *(++src); \
719 } while (0)
720 #endif
724 *dst=blend_two_colors(*dst, fg_pattern,
725 data & ALPHA_COLOR_LOOKUP_SIZE );
726 dst++;
727 UPDATE_SRC_ALPHA;
729 while (--col);
730 #ifdef ALPHA_BITMAP_READ_WORDS
731 if (skip_end < pixels)
733 pixels -= skip_end;
734 data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT;
735 } else {
736 pixels = skip_end - pixels;
737 src_w += pixels / ALPHA_COLOR_PIXEL_PER_WORD;
738 pixels %= ALPHA_COLOR_PIXEL_PER_WORD;
739 data = letoh32(*src_w++);
740 data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
741 pixels = 8 - pixels;
743 #else
744 if (skip_end)
746 pixels += skip_end;
747 if (pixels >= ALPHA_COLOR_PIXEL_PER_BYTE)
749 src += pixels / ALPHA_COLOR_PIXEL_PER_BYTE;
750 pixels %= ALPHA_COLOR_PIXEL_PER_BYTE;
751 data = *src;
752 data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
753 } else
754 data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT;
756 #endif
757 dst += LCD_WIDTH - width;
758 } while (--row);
761 static void buffer_putsxyofs( fb_data *buf, int buf_width, int buf_height,
762 int x, int y, int ofs, const unsigned char *str )
764 unsigned short ch;
765 unsigned short *ucs;
767 struct font *pf = rb->font_get( FONT_UI );
768 if( !pf ) pf = rb->font_get( FONT_SYSFIXED );
770 ucs = rb->bidi_l2v( str, 1 );
772 while( (ch = *ucs++) != 0 && x < buf_width )
774 int width;
775 const unsigned char *bits;
777 /* get proportional width and glyph bits */
778 width = rb->font_get_width( pf, ch );
780 if( ofs > width )
782 ofs -= width;
783 continue;
786 bits = rb->font_get_bits( pf, ch );
788 if (pf->depth)
789 buffer_alpha_bitmap_part( buf, buf_width, buf_height, bits, ofs, 0,
790 width, x, y, width - ofs, pf->height);
791 else
792 buffer_mono_bitmap_part( buf, buf_width, buf_height, bits, ofs, 0,
793 width, x, y, width - ofs, pf->height);
795 x += width - ofs;
796 ofs = 0;
800 /***********************************************************************
801 * Menu handling
802 ***********************************************************************/
803 enum {
804 /* Main menu */
805 MAIN_MENU_RESUME,
806 MAIN_MENU_NEW, MAIN_MENU_LOAD, MAIN_MENU_SAVE,
807 MAIN_MENU_SET_WIDTH, MAIN_MENU_SET_HEIGHT,
808 MAIN_MENU_BRUSH_SIZE, MAIN_MENU_BRUSH_SPEED, MAIN_MENU_COLOR,
809 MAIN_MENU_GRID_SIZE,
810 MAIN_MENU_PLAYBACK_CONTROL,
811 MAIN_MENU_EXIT,
813 enum {
814 /* Select action menu */
815 SELECT_MENU_CUT, SELECT_MENU_COPY,
816 SELECT_MENU_INVERT, SELECT_MENU_HFLIP, SELECT_MENU_VFLIP,
817 SELECT_MENU_ROTATE90, SELECT_MENU_ROTATE180, SELECT_MENU_ROTATE270,
818 SELECT_MENU_CANCEL,
820 enum {
821 /* Text menu */
822 TEXT_MENU_TEXT, TEXT_MENU_FONT,
823 TEXT_MENU_PREVIEW, TEXT_MENU_APPLY, TEXT_MENU_CANCEL,
826 MENUITEM_STRINGLIST(main_menu, "RockPaint", NULL,
827 "Resume", "New", "Load", "Save",
828 "Set Width", "Set Height",
829 "Brush Size", "Brush Speed",
830 "Choose Color", "Grid Size",
831 "Playback Control", "Exit");
832 MENUITEM_STRINGLIST(select_menu, "Select...", NULL,
833 "Cut", "Copy",
834 "Invert", "Horizontal Flip", "Vertical Flip",
835 "Rotate 90°", "Rotate 180°", "Rotate 270°",
836 "Cancel");
837 MENUITEM_STRINGLIST(text_menu, "Text", NULL,
838 "Set Text", "Change Font",
839 "Preview", "Apply", "Cancel");
840 static const int times_list[] = { 1, 2, 4, 8 };
841 static const int gridsize_list[] = { 0, 5, 10, 20 };
842 static const struct opt_items times_options[] = {
843 { "1x", -1 }, { "2x", -1 }, { "4x", -1 }, { "8x", -1 }
845 static const struct opt_items gridsize_options[] = {
846 { "No grid", -1 }, { "5px", -1 }, { "10px", -1 }, { "20px", -1 }
849 static int draw_window( int height, int width,
850 int *top, int *left,
851 const char *title )
853 int fh;
854 rb->lcd_getstringsize( title, NULL, &fh );
855 fh++;
857 const int _top = ( LCD_HEIGHT - height ) / 2;
858 const int _left = ( LCD_WIDTH - width ) / 2;
859 if( top ) *top = _top;
860 if( left ) *left = _left;
861 rb->lcd_set_background(COLOR_BLUE);
862 rb->lcd_set_foreground(COLOR_LIGHTGRAY);
863 rb->lcd_fillrect( _left, _top, width, height );
864 rb->lcd_set_foreground(COLOR_BLUE);
865 rb->lcd_fillrect( _left, _top, width, fh+4 );
866 rb->lcd_set_foreground(COLOR_WHITE);
867 rb->lcd_putsxy( _left+2, _top+2, title );
868 rb->lcd_set_foreground(COLOR_BLACK);
869 rb->lcd_drawrect( _left, _top, width, height );
870 return _top+fh+4;
873 /***********************************************************************
874 * File browser
875 ***********************************************************************/
877 char bbuf[MAX_PATH]; /* used by file and font browsers */
878 char bbuf_s[MAX_PATH]; /* used by file and font browsers */
879 struct tree_context *tree = NULL;
881 static bool check_extention(const char *filename, const char *ext)
883 const char *p = rb->strrchr( filename, '.' );
884 return ( p != NULL && !rb->strcasecmp( p, ext ) );
887 /* only displayes directories and .bmp files */
888 static bool callback_show_item(char *name, int attr, struct tree_context *tc)
890 (void) tc;
891 if( ( attr & ATTR_DIRECTORY ) ||
892 ( !(attr & ATTR_DIRECTORY) && check_extention( name, ".bmp" ) ) )
894 return true;
896 return false;
899 static bool browse( char *dst, int dst_size, const char *start )
901 struct browse_context browse;
903 rb->browse_context_init(&browse, SHOW_ALL,
904 BROWSE_SELECTONLY|BROWSE_NO_CONTEXT_MENU,
905 NULL, NOICON, start, NULL);
907 browse.callback_show_item = callback_show_item;
908 browse.buf = dst;
909 browse.bufsize = dst_size;
911 rb->rockbox_browse(&browse);
913 return (browse.flags & BROWSE_SELECTED);
916 /***********************************************************************
917 * Font browser
919 * FIXME: This still needs some work ... it currently only works fine
920 * on the simulators, disk spins too much on real targets -> rendered
921 * font buffer needed.
922 ***********************************************************************/
924 * cache font preview handling assumes:
925 * - fvi doesn't decrease by more than 1.
926 * In other words, cache_first-1 must be cached before cache_first-2 is cached.
927 * - there is enough space to store all preview currently displayed.
929 static bool browse_fonts( char *dst, int dst_size )
931 #define LINE_SPACE 2
932 #define PREVIEW_SIZE(x) ((x)->size)
933 #define PREVIEW_NEXT(x) (struct font_preview *)((char*)(x) + PREVIEW_SIZE(x))
935 struct tree_context backup;
936 struct entry *dc, *e;
937 int dirfilter = SHOW_FONT;
939 struct font_preview {
940 unsigned short width;
941 unsigned short height;
942 size_t size; /* to avoid calculating size each time. */
943 fb_data preview[0];
944 } *font_preview = NULL;
946 int top = 0;
948 int fvi = 0; /* first visible item */
949 int lvi = 0; /* last visible item */
950 int si = 0; /* selected item */
951 int li = 0; /* last item */
952 int nvih = 0; /* next visible item height */
953 int i;
954 bool need_redraw = true; /* Do we need to redraw ? */
955 bool reset_font = false;
956 bool ret = false;
958 int cp = 0; /* current position */
959 int sp = 0; /* selected position */
960 int fh, fw; /* font height, width */
962 unsigned char *cache = (unsigned char *) buffer + sizeof(buffer->text);
963 size_t cache_size = sizeof(*buffer) - sizeof(buffer->text);
964 size_t cache_used = 0;
965 int cache_first = 0, cache_last = -1;
966 char *a;
968 rb->snprintf( bbuf_s, MAX_PATH, FONT_DIR "/%s.fnt",
969 rb->global_settings->font_file );
971 tree = rb->tree_get_context();
972 backup = *tree;
973 dc = rb->tree_get_entries(tree);
974 a = backup.currdir+rb->strlen(backup.currdir)-1;
975 if( *a != '/' )
977 *++a = '/';
979 rb->strcpy( a+1, dc[tree->selected_item].name );
980 tree->dirfilter = &dirfilter;
981 tree->browse = NULL;
982 rb->strcpy( bbuf, FONT_DIR "/" );
983 rb->set_current_file( bbuf );
985 if( buffer->text.initialized )
987 cache_used = buffer->text.cache_used;
988 cache_first = buffer->text.cache_first;
989 cache_last = buffer->text.cache_last;
990 fvi = buffer->text.fvi;
991 si = buffer->text.si;
993 buffer->text.initialized = true;
995 while( 1 )
997 if( !need_redraw )
999 /* we don't need to redraw ... but we need to unselect
1000 * the previously selected item */
1001 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1002 rb->lcd_fillrect( 10, sp, LCD_WIDTH-10, font_preview->height );
1003 rb->lcd_set_drawmode(DRMODE_SOLID);
1006 if( need_redraw )
1008 need_redraw = false;
1010 rb->lcd_set_foreground(COLOR_BLACK);
1011 rb->lcd_set_background(COLOR_LIGHTGRAY);
1012 rb->lcd_clear_display();
1014 rb->font_getstringsize( "Fonts", NULL, &fh, FONT_UI );
1015 rb->lcd_putsxy( 2, 2, "Fonts" );
1016 top = fh + 4 + LINE_SPACE;
1018 font_preview = (struct font_preview *) cache;
1019 /* get first font preview to be displayed. */
1020 for( i = cache_first; i < cache_last && i < fvi; i++ )
1022 font_preview = PREVIEW_NEXT(font_preview);
1024 for( ; fvi < lvi && nvih > 0; fvi++ )
1026 nvih -= font_preview->height + LINE_SPACE;
1027 font_preview = PREVIEW_NEXT(font_preview);
1029 nvih = 0;
1030 i = fvi;
1032 cp = top;
1033 while( cp <= LCD_HEIGHT+LINE_SPACE && i < tree->filesindir )
1035 e = &dc[i];
1036 if( i < cache_first || i > cache_last )
1038 size_t siz;
1039 reset_font = true;
1040 rb->snprintf( bbuf, MAX_PATH, FONT_DIR "/%s", e->name );
1041 rb->font_getstringsize( e->name, &fw, &fh, FONT_UI );
1042 if( fw > LCD_WIDTH ) fw = LCD_WIDTH;
1043 siz = (sizeof(struct font_preview) + fw*fh*FB_DATA_SZ+3) & ~3;
1044 if( i < cache_first )
1046 /* insert font preview to the top. */
1047 cache_used = 0;
1048 for( ; cache_first <= cache_last; cache_first++ )
1050 font_preview = (struct font_preview *) (cache + cache_used);
1051 size_t size = PREVIEW_SIZE(font_preview);
1052 if( cache_used + size >= cache_size - siz )
1053 break;
1054 cache_used += size;
1056 cache_last = cache_first-1;
1057 cache_first = i;
1058 rb->memmove( cache+siz, cache, cache_used );
1059 font_preview = (struct font_preview *) cache;
1061 else /* i > cache_last */
1063 /* add font preview to the bottom. */
1064 font_preview = (struct font_preview *) cache;
1065 while( cache_used >= cache_size - siz )
1067 cache_used -= PREVIEW_SIZE(font_preview);
1068 font_preview = PREVIEW_NEXT(font_preview);
1069 cache_first++;
1071 cache_last = i;
1072 rb->memmove( cache, font_preview, cache_used );
1073 font_preview = (struct font_preview *) (cache + cache_used);
1075 cache_used += siz;
1076 /* create preview cache. */
1077 font_preview->width = fw;
1078 font_preview->height = fh;
1079 font_preview->size = siz;
1080 /* clear with background. */
1081 for( siz = fw*fh; siz > 0; )
1083 font_preview->preview[--siz] = COLOR_LIGHTGRAY;
1085 buffer_putsxyofs( font_preview->preview,
1086 fw, fh, 0, 0, 0, e->name );
1088 else
1090 fw = font_preview->width;
1091 fh = font_preview->height;
1093 if( cp + fh >= LCD_HEIGHT )
1095 nvih = fh;
1096 break;
1098 rb->lcd_bitmap( font_preview->preview, 10, cp, fw, fh );
1099 cp += fh + LINE_SPACE;
1100 i++;
1101 font_preview = PREVIEW_NEXT(font_preview);
1103 lvi = i-1;
1104 li = tree->filesindir-1;
1105 if( reset_font )
1107 // fixme rb->font_load(NULL, bbuf_s );
1108 reset_font = false;
1110 if( lvi-fvi+1 < tree->filesindir )
1112 rb->gui_scrollbar_draw( rb->screens[SCREEN_MAIN], 0, top,
1113 9, LCD_HEIGHT-top,
1114 tree->filesindir, fvi, lvi+1, VERTICAL );
1118 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1119 sp = top;
1120 font_preview = (struct font_preview *) cache;
1121 for( i = cache_first; i < si; i++ )
1123 if( i >= fvi )
1124 sp += font_preview->height + LINE_SPACE;
1125 font_preview = PREVIEW_NEXT(font_preview);
1127 rb->lcd_fillrect( 10, sp, LCD_WIDTH-10, font_preview->height );
1128 rb->lcd_set_drawmode(DRMODE_SOLID);
1130 rb->lcd_update();
1132 switch( rb->button_get(true) )
1134 case ROCKPAINT_UP:
1135 case ROCKPAINT_UP|BUTTON_REPEAT:
1136 if( si > 0 )
1138 si--;
1139 if( si < fvi )
1141 fvi = si;
1142 nvih = 0;
1143 need_redraw = true;
1146 break;
1148 case ROCKPAINT_DOWN:
1149 case ROCKPAINT_DOWN|BUTTON_REPEAT:
1150 if( si < li )
1152 si++;
1153 if( si > lvi )
1155 need_redraw = true;
1158 break;
1160 case ROCKPAINT_RIGHT:
1161 case ROCKPAINT_DRAW:
1162 ret = true;
1163 rb->snprintf( dst, dst_size, FONT_DIR "/%s", dc[si].name );
1164 /* fall through */
1165 case ROCKPAINT_LEFT:
1166 case ROCKPAINT_QUIT:
1167 buffer->text.cache_used = cache_used;
1168 buffer->text.cache_first = cache_first;
1169 buffer->text.cache_last = cache_last;
1170 buffer->text.fvi = fvi;
1171 buffer->text.si = si;
1172 *tree = backup;
1173 rb->set_current_file( backup.currdir );
1174 return ret;
1177 #undef LINE_SPACE
1178 #undef PREVIEW_SIZE
1179 #undef PREVIEW_NEXT
1182 /***********************************************************************
1183 * HSVRGB Color chooser
1184 ***********************************************************************/
1185 static unsigned int color_chooser( unsigned int color )
1187 int red = RGB_UNPACK_RED( color );
1188 int green = RGB_UNPACK_GREEN( color );
1189 int blue = RGB_UNPACK_BLUE( color );
1190 int hue, saturation, value;
1191 int r, g, b; /* temp variables */
1192 int i, top, left;
1193 int button;
1194 int *pval;
1195 static struct incdec_ctx ctxs[] = {
1196 { 3600, { 10, 100}, true }, /* hue */
1197 { 0xff, { 1, 8}, false }, /* the others */
1200 enum BaseColor { Hue = 0, Saturation = 1, Value = 2,
1201 Red = 3, Green = 4, Blue = 5 };
1202 enum BaseColor current = Red;
1203 bool has_changed;
1205 restore_screen();
1207 rgb2hsv( red, green, blue, &hue, &saturation, &value );
1209 while( 1 )
1211 has_changed = false;
1212 color = LCD_RGBPACK( red, green, blue );
1214 #define HEIGHT ( 100 )
1215 #define WIDTH ( 150 )
1217 top = draw_window( HEIGHT, WIDTH, NULL, &left, "Color chooser" );
1218 top -= 15;
1220 for( i=0; i<100; i++ )
1222 hsv2rgb( i*36, saturation, value, &r, &g, &b );
1223 rb->lcd_set_foreground( LCD_RGBPACK( r, g, b ) );
1224 rb->lcd_vline( left+15+i, top+20, top+27 );
1225 hsv2rgb( hue, i*255/100, value, &r, &g, &b );
1226 rb->lcd_set_foreground( LCD_RGBPACK( r, g, b ) );
1227 rb->lcd_vline( left+15+i, top+30, top+37 );
1228 hsv2rgb( hue, saturation, i*255/100, &r, &g, &b );
1229 rb->lcd_set_foreground( LCD_RGBPACK( r, g, b ) );
1230 rb->lcd_vline( left+15+i, top+40, top+47 );
1231 rb->lcd_set_foreground( LCD_RGBPACK( i*255/100, green, blue ) );
1232 rb->lcd_vline( left+15+i, top+50, top+57 );
1233 rb->lcd_set_foreground( LCD_RGBPACK( red, i*255/100, blue ) );
1234 rb->lcd_vline( left+15+i, top+60, top+67 );
1235 rb->lcd_set_foreground( LCD_RGBPACK( red, green, i*255/100 ) );
1236 rb->lcd_vline( left+15+i, top+70, top+77 );
1239 rb->lcd_set_foreground(COLOR_BLACK);
1240 #define POSITION( a, i ) \
1241 rb->lcd_drawpixel( left+14+i, top + 19 + a ); \
1242 rb->lcd_drawpixel( left+16+i, top + 19 + a ); \
1243 rb->lcd_drawpixel( left+14+i, top + 28 + a ); \
1244 rb->lcd_drawpixel( left+16+i, top + 28 + a );
1245 POSITION( 0, hue/36 );
1246 POSITION( 10, saturation*99/255 );
1247 POSITION( 20, value*99/255 );
1248 POSITION( 30, red*99/255 );
1249 POSITION( 40, green*99/255 );
1250 POSITION( 50, blue*99/255 );
1251 #undef POSITION
1252 rb->lcd_set_background(COLOR_LIGHTGRAY);
1253 rb->lcd_setfont( FONT_SYSFIXED );
1254 rb->lcd_putsxyf( left + 117, top + 20, "%d", hue/10 );
1255 rb->lcd_putsxyf( left + 117, top + 30, "%d.%d",
1256 saturation/255, ((saturation*100)/255)%100 );
1257 rb->lcd_putsxyf( left + 117, top + 40, "%d.%d",
1258 value/255, ((value*100)/255)%100 );
1259 rb->lcd_putsxyf( left + 117, top + 50, "%d", red );
1260 rb->lcd_putsxyf( left + 117, top + 60, "%d", green );
1261 rb->lcd_putsxyf( left + 117, top + 70, "%d", blue );
1262 rb->lcd_setfont( FONT_UI );
1264 #define CURSOR( l ) \
1265 rb->lcd_bitmap_transparent_part( rockpaint_hsvrgb, 1, 1, 16, left+l+1, top+20, 6, 58 ); \
1266 rb->lcd_bitmap_transparent_part( rockpaint_hsvrgb, 8, 10*current, 16, left+l, top+19+10*current, 8, 10 );
1267 CURSOR( 5 );
1268 #undef CURSOR
1270 rb->lcd_set_foreground( color );
1271 rb->lcd_fillrect( left+15, top+85, 100, 8 );
1273 rb->lcd_update();
1275 switch( button = rb->button_get(true) )
1277 case ROCKPAINT_UP:
1278 current = ( current + 5 )%6;
1279 break;
1281 case ROCKPAINT_DOWN:
1282 current = ( current + 1 )%6;
1283 break;
1285 case ROCKPAINT_LEFT:
1286 case ROCKPAINT_LEFT|BUTTON_REPEAT:
1287 case ROCKPAINT_RIGHT:
1288 case ROCKPAINT_RIGHT|BUTTON_REPEAT:
1289 has_changed = true;
1290 switch( current )
1292 case Hue:
1293 pval = &hue;
1294 break;
1295 case Saturation:
1296 pval = &saturation;
1297 break;
1298 case Value:
1299 pval = &value;
1300 break;
1301 case Red:
1302 pval = &red;
1303 break;
1304 case Green:
1305 pval = &green;
1306 break;
1307 case Blue:
1308 pval = &blue;
1309 break;
1310 default:
1311 pval = NULL;
1312 break;
1314 if (pval)
1316 incdec_value(pval, &ctxs[(current != Hue? 1: 0)],
1317 (button&ROCKPAINT_RIGHT), (button&BUTTON_REPEAT));
1319 break;
1321 case ROCKPAINT_DRAW:
1322 return color;
1324 if( has_changed )
1326 switch( current )
1328 case Hue:
1329 case Saturation:
1330 case Value:
1331 hsv2rgb( hue, saturation, value, &red, &green, &blue );
1332 break;
1334 case Red:
1335 case Green:
1336 case Blue:
1337 rgb2hsv( red, green, blue, &hue, &saturation, &value );
1338 break;
1341 #undef HEIGHT
1342 #undef WIDTH
1346 /***********************************************************************
1347 * Misc routines
1348 ***********************************************************************/
1349 static void init_buffer(void)
1351 int i;
1352 fb_data color = rp_colors[ bgdrawcolor ];
1353 for( i = 0; i < ROWS*COLS; i++ )
1355 save_buffer[i] = color;
1359 static void draw_pixel(int x,int y)
1361 if( !preview )
1363 if( x < 0 || x >= COLS || y < 0 || y >= ROWS ) return;
1364 if( isbg )
1366 save_buffer[ x+y*COLS ] = rp_colors[bgdrawcolor];
1368 else
1370 save_buffer[ x+y*COLS ] = rp_colors[drawcolor];
1373 rb->lcd_drawpixel(x,y);
1376 static void color_picker( int x, int y )
1378 if( preview )
1380 rb->lcd_set_foreground( save_buffer[ x+y*COLS ] );
1381 #define PSIZE 12
1382 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1383 if( x >= COLS - PSIZE ) x -= PSIZE + 2;
1384 if( y >= ROWS - PSIZE ) y -= PSIZE + 2;
1385 rb->lcd_drawrect( x + 2, y + 2, PSIZE - 2, PSIZE - 2 );
1386 rb->lcd_set_drawmode(DRMODE_SOLID);
1387 rb->lcd_fillrect( x + 3, y + 3, PSIZE - 4, PSIZE - 4 );
1388 #undef PSIZE
1389 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1391 else
1393 rp_colors[ drawcolor ] = save_buffer[ x+y*COLS ];
1397 static void draw_select_rectangle( int x1, int y1, int x2, int y2 )
1398 /* This is a preview mode only function */
1400 int i,a;
1401 if( x1 > x2 )
1403 i = x1;
1404 x1 = x2;
1405 x2 = i;
1407 if( y1 > y2 )
1409 i = y1;
1410 y1 = y2;
1411 y2 = i;
1413 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1414 i = 0;
1415 for( a = x1; a < x2; a++, i++ )
1416 if( i%2 )
1417 rb->lcd_drawpixel( a, y1 );
1418 for( a = y1; a < y2; a++, i++ )
1419 if( i%2 )
1420 rb->lcd_drawpixel( x2, a );
1421 if( y2 != y1 )
1422 for( a = x2; a > x1; a--, i++ )
1423 if( i%2 )
1424 rb->lcd_drawpixel( a, y2 );
1425 if( x2 != x1 )
1426 for( a = y2; a > y1; a--, i++ )
1427 if( i%2 )
1428 rb->lcd_drawpixel( x1, a );
1429 rb->lcd_set_drawmode(DRMODE_SOLID);
1432 static void copy_to_clipboard( void )
1434 /* This needs to be optimised ... but i'm lazy ATM */
1435 rb->memcpy( buffer->clipboard, save_buffer, COLS*ROWS*sizeof( fb_data ) );
1438 /* no preview mode handling atm ... do we need it ? (one if) */
1439 static void draw_invert( int x1, int y1, int x2, int y2 )
1441 int i;
1442 if( x1 > x2 )
1444 i = x1;
1445 x1 = x2;
1446 x2 = i;
1448 if( y1 > y2 )
1450 i = y1;
1451 y1 = y2;
1452 y2 = i;
1455 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1456 rb->lcd_fillrect( x1, y1, x2-x1+1, y2-y1+1 );
1457 rb->lcd_set_drawmode(DRMODE_SOLID);
1459 for( ; y1<=y2; y1++ )
1461 for( i = x1; i<=x2; i++ )
1463 save_buffer[ y1*COLS + i ] = ~save_buffer[ y1*COLS + i ];
1466 /*if( update )*/ rb->lcd_update();
1469 static void draw_hflip( int x1, int y1, int x2, int y2 )
1471 int i;
1472 if( x1 > x2 )
1474 i = x1;
1475 x1 = x2;
1476 x2 = i;
1478 if( y1 > y2 )
1480 i = y1;
1481 y1 = y2;
1482 y2 = i;
1485 copy_to_clipboard();
1487 for( i = 0; i <= y2 - y1; i++ )
1489 rb->memcpy( save_buffer+(y1+i)*COLS+x1,
1490 buffer->clipboard+(y2-i)*COLS+x1,
1491 (x2-x1+1)*sizeof( fb_data ) );
1493 restore_screen();
1494 rb->lcd_update();
1497 static void draw_vflip( int x1, int y1, int x2, int y2 )
1499 int i;
1500 if( x1 > x2 )
1502 i = x1;
1503 x1 = x2;
1504 x2 = i;
1506 if( y1 > y2 )
1508 i = y1;
1509 y1 = y2;
1510 y2 = i;
1513 copy_to_clipboard();
1515 for( ; y1 <= y2; y1++ )
1517 for( i = 0; i <= x2 - x1; i++ )
1519 save_buffer[y1*COLS+x1+i] = buffer->clipboard[y1*COLS+x2-i];
1522 restore_screen();
1523 rb->lcd_update();
1526 /* direction: -1 = left, 1 = right */
1527 static void draw_rot_90_deg( int x1, int y1, int x2, int y2, int direction )
1529 int i, j;
1530 if( x1 > x2 )
1532 i = x1;
1533 x1 = x2;
1534 x2 = i;
1536 if( y1 > y2 )
1538 i = y1;
1539 y1 = y2;
1540 y2 = i;
1543 copy_to_clipboard();
1545 fb_data color = rp_colors[ bgdrawcolor ];
1546 const int width = x2 - x1, height = y2 - y1;
1547 const int sub_half = width/2-height/2, add_half = (width+height)/2;
1548 if( width > height )
1550 for( i = 0; i <= height; i++ )
1552 for( j = 0; j < sub_half; j++ )
1553 save_buffer[(y1+i)*COLS+x1+j] = color;
1554 for( j = add_half+1; j <= width; j++ )
1555 save_buffer[(y1+i)*COLS+x1+j] = color;
1558 else if( width < height )
1560 for( j = 0; j <= width; j++ )
1562 for( i = 0; i < -sub_half; i++ )
1563 save_buffer[(y1+i)*COLS+x1+j] = color;
1564 for( i = add_half+1; i <= height; i++ )
1565 save_buffer[(y1+i)*COLS+x1+j] = color;
1568 int x3 = x1 + sub_half, y3 = y1 - sub_half;
1569 int is = x3<0?-x3:0, ie = COLS-x3-1, js = y3<0?-y3:0, je = ROWS-y3-1;
1570 if( ie > height ) ie = height;
1571 if( je > width ) je = width;
1572 for( i = is; i <= ie; i++ )
1574 for( j = js; j <= je; j++ )
1576 int x, y;
1577 if(direction > 0)
1579 x = x1+j;
1580 y = y1+height-i;
1582 else
1584 x = x1+width-j;
1585 y = y1+i;
1587 save_buffer[(y3+j)*COLS+x3+i] = buffer->clipboard[y*COLS+x];
1590 restore_screen();
1591 rb->lcd_update();
1594 static void draw_paste_rectangle( int src_x1, int src_y1, int src_x2,
1595 int src_y2, int x1, int y1, int cut )
1597 int i, width, height;
1598 if( cut )
1600 i = drawcolor;
1601 drawcolor = bgdrawcolor;
1602 draw_rect_full( src_x1, src_y1, src_x2, src_y2 );
1603 drawcolor = i;
1605 if( src_x1 > src_x2 )
1607 i = src_x1;
1608 src_x1 = src_x2;
1609 src_x2 = i;
1611 if( src_y1 > src_y2 )
1613 i = src_y1;
1614 src_y1 = src_y2;
1615 src_y2 = i;
1617 width = src_x2 - src_x1 + 1;
1618 height = src_y2 - src_y1 + 1;
1619 /* clipping */
1620 if( x1 + width > COLS )
1621 width = COLS - x1;
1622 if( y1 + height > ROWS )
1623 height = ROWS - y1;
1625 rb->lcd_bitmap_part( buffer->clipboard, src_x1, src_y1, COLS,
1626 x1, y1, width, height );
1627 if( !preview )
1629 for( i = 0; i < height; i++ )
1631 rb->memcpy( save_buffer+(y1+i)*COLS+x1,
1632 buffer->clipboard+(src_y1+i)*COLS+src_x1,
1633 width*sizeof( fb_data ) );
1638 static void show_grid( bool update )
1640 int i;
1641 if( gridsize > 0 )
1643 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1644 for( i = gridsize; i < img_width; i+= gridsize )
1646 rb->lcd_vline( i, 0, img_height-1 );
1648 for( i = gridsize; i < img_height; i+= gridsize )
1650 rb->lcd_hline( 0, img_width-1, i );
1652 rb->lcd_set_drawmode(DRMODE_SOLID);
1653 if( update ) rb->lcd_update();
1657 static void draw_text( int x, int y )
1659 int selected = 0;
1660 int current_font_id = rb->global_status->font_id[SCREEN_MAIN];
1661 buffer->text.text[0] = '\0';
1662 buffer->text.font[0] = '\0';
1663 while( 1 )
1665 switch( rb->do_menu( &text_menu, &selected, NULL, NULL ) )
1667 case TEXT_MENU_TEXT:
1668 rb->lcd_set_foreground(COLOR_BLACK);
1669 rb->kbd_input( buffer->text.text, MAX_TEXT );
1670 break;
1672 case TEXT_MENU_FONT:
1673 if (current_font_id != rb->global_status->font_id[SCREEN_MAIN])
1674 rb->font_unload(current_font_id);
1675 if(browse_fonts( buffer->text.font, MAX_PATH ) )
1677 current_font_id = rb->font_load(buffer->text.font );
1678 rb->lcd_setfont(current_font_id);
1680 break;
1682 case TEXT_MENU_PREVIEW:
1683 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1684 while( 1 )
1686 int button;
1687 restore_screen();
1688 rb->lcd_putsxy( x, y, buffer->text.text );
1689 rb->lcd_update();
1690 switch( button = rb->button_get( true ) )
1692 case ROCKPAINT_LEFT:
1693 case ROCKPAINT_LEFT | BUTTON_REPEAT:
1694 case ROCKPAINT_RIGHT:
1695 case ROCKPAINT_RIGHT | BUTTON_REPEAT:
1696 incdec_value(&x, &incdec_x,
1697 (button&ROCKPAINT_RIGHT), (button&BUTTON_REPEAT));
1698 break;
1700 case ROCKPAINT_UP:
1701 case ROCKPAINT_UP | BUTTON_REPEAT:
1702 case ROCKPAINT_DOWN:
1703 case ROCKPAINT_DOWN | BUTTON_REPEAT:
1704 incdec_value(&y, &incdec_y,
1705 (button&ROCKPAINT_DOWN), (button&BUTTON_REPEAT));
1706 break;
1708 case ROCKPAINT_DRAW:
1709 break;
1710 default:
1711 if(rb->default_event_handler(button)
1712 == SYS_USB_CONNECTED)
1713 button = ROCKPAINT_DRAW;
1714 break;
1716 if( button == ROCKPAINT_DRAW ) break;
1718 break;
1720 case TEXT_MENU_APPLY:
1721 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1722 buffer_putsxyofs( save_buffer, COLS, ROWS, x, y, 0,
1723 buffer->text.text );
1724 case TEXT_MENU_CANCEL:
1725 default:
1726 restore_screen();
1727 if( buffer->text.font[0] )
1729 rb->snprintf( buffer->text.font, MAX_PATH,
1730 FONT_DIR "/%s.fnt",
1731 rb->global_settings->font_file );
1732 if (current_font_id != rb->global_status->font_id[SCREEN_MAIN])
1733 rb->font_unload(current_font_id);
1734 rb->lcd_setfont(FONT_UI);
1736 return;
1741 static void draw_brush( int x, int y )
1743 int i,j;
1744 for( i=-bsize/2+(bsize+1)%2; i<=bsize/2; i++ )
1746 for( j=-bsize/2+(bsize+1)%2; j<=bsize/2; j++ )
1748 draw_pixel( x+i, y+j );
1753 /* This is an implementation of Bresenham's line algorithm.
1754 * See http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm.
1756 static void draw_line( int x1, int y1, int x2, int y2 )
1758 int x = x1;
1759 int y = y1;
1760 int deltax = x2 - x1;
1761 int deltay = y2 - y1;
1762 int i;
1764 int xerr = abs(deltax);
1765 int yerr = abs(deltay);
1766 int xstep = deltax > 0 ? 1 : -1;
1767 int ystep = deltay > 0 ? 1 : -1;
1768 int err;
1770 if (yerr > xerr)
1772 /* more vertical */
1773 err = yerr;
1774 xerr <<= 1;
1775 yerr <<= 1;
1777 /* to leave off the last pixel of the line, leave off the "+ 1" */
1778 for (i = err + 1; i; --i)
1780 draw_pixel(x, y);
1781 y += ystep;
1782 err -= xerr;
1783 if (err < 0) {
1784 x += xstep;
1785 err += yerr;
1789 else
1791 /* more horizontal */
1792 err = xerr;
1793 xerr <<= 1;
1794 yerr <<= 1;
1796 for (i = err + 1; i; --i)
1798 draw_pixel(x, y);
1799 x += xstep;
1800 err -= yerr;
1801 if (err < 0) {
1802 y += ystep;
1803 err += xerr;
1809 static void draw_curve( int x1, int y1, int x2, int y2,
1810 int xa, int ya, int xb, int yb )
1812 int i = 0;
1813 short xl1, yl1;
1814 short xl2, yl2;
1815 short xl3, yl3;
1816 short xl4, yl4;
1817 short xr1, yr1;
1818 short xr2, yr2;
1819 short xr3, yr3;
1820 short xr4, yr4;
1821 short depth;
1822 short xh, yh;
1824 if( x1 == x2 && y1 == y2 )
1826 draw_pixel( x1, y1 );
1827 return;
1830 // if( preview )
1832 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1833 if( xa == -1 || ya == -1 )
1835 rb->lcd_drawline( x1, y1, xb, yb );
1836 rb->lcd_drawline( x2, y2, xb, yb );
1838 else
1840 rb->lcd_drawline( x1, y1, xa, ya );
1841 rb->lcd_drawline( x2, y2, xb, yb );
1843 rb->lcd_set_drawmode(DRMODE_SOLID);
1846 if( xa == -1 || ya == -1 )
1847 /* We only have 3 of the points
1848 * This will currently only be used in preview mode */
1850 #define PUSH( a1, b1, a2, b2, a3, b3, d ) \
1851 buffer->bezier[i].x1 = a1; \
1852 buffer->bezier[i].y1 = b1; \
1853 buffer->bezier[i].x2 = a2; \
1854 buffer->bezier[i].y2 = b2; \
1855 buffer->bezier[i].x3 = a3; \
1856 buffer->bezier[i].y3 = b3; \
1857 buffer->bezier[i].depth = d; \
1858 i++;
1859 #define POP( a1, b1, a2, b2, a3, b3, d ) \
1860 i--; \
1861 a1 = buffer->bezier[i].x1; \
1862 b1 = buffer->bezier[i].y1; \
1863 a2 = buffer->bezier[i].x2; \
1864 b2 = buffer->bezier[i].y2; \
1865 a3 = buffer->bezier[i].x3; \
1866 b3 = buffer->bezier[i].y3; \
1867 d = buffer->bezier[i].depth;
1869 PUSH( x1<<4, y1<<4, xb<<4, yb<<4, x2<<4, y2<<4, 0 );
1870 while( i )
1872 /* de Casteljau's algorithm (see wikipedia) */
1873 POP( xl1, yl1, xb, yb, xr3, yr3, depth );
1874 if( depth < 10 ) /* check that the stack's 'i' doesn't overflow */
1876 xl2 = ( xl1 + xb )>>1;
1877 yl2 = ( yl1 + yb )>>1;
1878 xr2 = ( xb + xr3 )>>1;
1879 yr2 = ( yb + yr3 )>>1;
1880 xr1 = ( xl2 + xr2 )>>1;
1881 yr1 = ( yl2 + yr2 )>>1;
1882 xl3 = xr1;
1883 yl3 = yr1;
1884 PUSH( xl1, yl1, xl2, yl2, xl3, yl3, depth+1 );
1885 PUSH( xr1, yr1, xr2, yr2, xr3, yr3, depth+1 );
1887 else
1889 draw_line( ((xl1>>3)+1)>>1, ((yl1>>3)+1)>>1,
1890 ((xr3>>3)+1)>>1, ((yr3>>3)+1)>>1 );
1893 #undef PUSH
1894 #undef POP
1896 else /* We have the 4 points */
1898 #define PUSH( a1, b1, a2, b2, a3, b3, a4, b4, d ) \
1899 buffer->bezier[i].x1 = a1; \
1900 buffer->bezier[i].y1 = b1; \
1901 buffer->bezier[i].x2 = a2; \
1902 buffer->bezier[i].y2 = b2; \
1903 buffer->bezier[i].x3 = a3; \
1904 buffer->bezier[i].y3 = b3; \
1905 buffer->bezier[i].x4 = a4; \
1906 buffer->bezier[i].y4 = b4; \
1907 buffer->bezier[i].depth = d; \
1908 i++;
1909 #define POP( a1, b1, a2, b2, a3, b3, a4, b4, d ) \
1910 i--; \
1911 a1 = buffer->bezier[i].x1; \
1912 b1 = buffer->bezier[i].y1; \
1913 a2 = buffer->bezier[i].x2; \
1914 b2 = buffer->bezier[i].y2; \
1915 a3 = buffer->bezier[i].x3; \
1916 b3 = buffer->bezier[i].y3; \
1917 a4 = buffer->bezier[i].x4; \
1918 b4 = buffer->bezier[i].y4; \
1919 d = buffer->bezier[i].depth;
1921 PUSH( x1<<4, y1<<4, xa<<4, ya<<4, xb<<4, yb<<4, x2<<4, y2<<4, 0 );
1922 while( i )
1924 /* de Casteljau's algorithm (see wikipedia) */
1925 POP( xl1, yl1, xa, ya, xb, yb, xr4, yr4, depth );
1926 if( depth < 10 ) /* check that the stack's 'i' doesn't overflow */
1928 xl2 = ( xl1 + xa )>>1;
1929 yl2 = ( yl1 + ya )>>1;
1930 xh = ( xa + xb )>>1;
1931 yh = ( ya + yb )>>1;
1932 xr3 = ( xb + xr4 )>>1;
1933 yr3 = ( yb + yr4 )>>1;
1934 xl3 = ( xl2 + xh )>>1;
1935 yl3 = ( yl2 + yh )>>1;
1936 xr2 = ( xr3 + xh )>>1;
1937 yr2 = ( yr3 + yh )>>1;
1938 xl4 = ( xl3 + xr2 )>>1;
1939 yl4 = ( yl3 + yr2 )>>1;
1940 xr1 = xl4;
1941 yr1 = yl4;
1942 PUSH( xl1, yl1, xl2, yl2, xl3, yl3, xl4, yl4, depth+1 );
1943 PUSH( xr1, yr1, xr2, yr2, xr3, yr3, xr4, yr4, depth+1 );
1945 else
1947 draw_line( ((xl1>>3)+1)>>1, ((yl1>>3)+1)>>1,
1948 ((xr4>>3)+1)>>1, ((yr4>>3)+1)>>1 );
1951 #undef PUSH
1952 #undef POP
1956 static void draw_rect( int x1, int y1, int x2, int y2 )
1958 draw_line( x1, y1, x1, y2 );
1959 draw_line( x1, y1, x2, y1 );
1960 draw_line( x1, y2, x2, y2 );
1961 draw_line( x2, y1, x2, y2 );
1964 static void togglebg( void )
1966 if( isbg )
1968 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1970 else
1972 rb->lcd_set_foreground( rp_colors[ bgdrawcolor ] );
1974 isbg = !isbg;
1977 static void draw_rect_full( int x1, int y1, int x2, int y2 )
1979 /* GRUIK */
1980 int x;
1981 togglebg();
1982 if( x1 > x2 )
1984 x = x1;
1985 x1 = x2;
1986 x2 = x;
1988 x = x1;
1989 do {
1990 draw_line( x, y1, x, y2 );
1991 } while( ++x <= x2 );
1992 togglebg();
1993 draw_rect( x1, y1, x2, y2 );
1996 static void draw_oval( int x1, int y1, int x2, int y2, bool full )
1998 /* TODO: simplify :) */
1999 int cx = (x1+x2)>>1;
2000 int cy = (y1+y2)>>1;
2002 int rx = (x1-x2)>>1;
2003 int ry = (y1-y2)>>1;
2004 if( rx < 0 ) rx *= -1;
2005 if( ry < 0 ) ry *= -1;
2007 if( rx == 0 || ry == 0 )
2009 draw_line( x1, y1, x2, y2 );
2010 return;
2013 int x,y;
2014 int dst, old_dst;
2016 for( x = 0; x < rx; x++ )
2018 y = 0;
2019 dst = -0xfff;
2020 do {
2021 old_dst = dst;
2022 dst = ry * ry * x * x + rx * rx * y * y - rx * rx * ry * ry;
2023 y++;
2024 } while( dst < 0 );
2025 if( -old_dst < dst ) y--;
2026 if( full )
2028 draw_line( cx+x, cy, cx+x, cy+y );
2029 draw_line( cx+x, cy, cx+x, cy-y );
2030 draw_line( cx-x, cy, cx-x, cy+y );
2031 draw_line( cx-x, cy, cx-x, cy-y );
2033 else
2035 draw_pixel( cx+x, cy+y );
2036 draw_pixel( cx+x, cy-y );
2037 draw_pixel( cx-x, cy+y );
2038 draw_pixel( cx-x, cy-y );
2041 for( y = 0; y < ry; y++ )
2043 x = 0;
2044 dst = -0xfff;
2045 do {
2046 old_dst = dst;
2047 dst = ry * ry * x * x + rx * rx * y * y - rx * rx * ry * ry;
2048 x++;
2049 } while( dst < 0 );
2050 if( -old_dst < dst ) x--;
2051 if( full )
2053 draw_line( cx+x, cy, cx+x, cy+y );
2054 draw_line( cx+x, cy, cx+x, cy-y );
2055 draw_line( cx-x, cy, cx-x, cy+y );
2056 draw_line( cx-x, cy, cx-x, cy-y );
2058 else
2060 draw_pixel( cx+x, cy+y );
2061 draw_pixel( cx+x, cy-y );
2062 draw_pixel( cx-x, cy+y );
2063 draw_pixel( cx-x, cy-y );
2068 static void draw_oval_empty( int x1, int y1, int x2, int y2 )
2070 draw_oval( x1, y1, x2, y2, false );
2073 static void draw_oval_full( int x1, int y1, int x2, int y2 )
2075 togglebg();
2076 draw_oval( x1, y1, x2, y2, true );
2077 togglebg();
2078 draw_oval( x1, y1, x2, y2, false );
2081 static void draw_fill( int x0, int y0 )
2083 #define PUSH( a, b ) \
2084 draw_pixel( (int)a, (int)b ); \
2085 buffer->coord[i].x = a; \
2086 buffer->coord[i].y = b; \
2087 i++;
2088 #define POP( a, b ) \
2089 i--; \
2090 a = buffer->coord[i].x; \
2091 b = buffer->coord[i].y;
2093 unsigned int i=0;
2094 short x = x0;
2095 short y = y0;
2096 unsigned int prev_color = save_buffer[ x0+y0*COLS ];
2098 if( preview )
2099 return;
2100 if( prev_color == rp_colors[ drawcolor ] ) return;
2102 PUSH( x, y );
2104 while( i != 0 )
2106 POP( x, y );
2107 if( x > 0 && save_buffer[x-1+y*COLS] == prev_color )
2109 PUSH( x-1, y );
2111 if( x < COLS-1 && save_buffer[x+1+y*COLS] == prev_color )
2113 PUSH( x+1, y );
2115 if( y > 0 && save_buffer[x+(y-1)*COLS] == prev_color )
2117 PUSH( x, y-1 );
2119 if( y < ROWS - 1 && save_buffer[x+(y+1)*COLS] == prev_color )
2121 PUSH( x, y+1 );
2124 #undef PUSH
2125 #undef POP
2129 /* For preview purposes only */
2130 /* use same algorithm as draw_line() to draw line. */
2131 static void line_gradient( int x1, int y1, int x2, int y2 )
2133 int h1, s1, v1, h2, s2, v2, r, g, b;
2134 int xerr = x2 - x1, yerr = y2 - y1, xstep, ystep;
2135 int i, delta, err;
2136 fb_data color1, color2;
2138 if( xerr == 0 && yerr == 0 )
2140 draw_pixel( x1, y1 );
2141 return;
2144 xstep = xerr > 0 ? 1 : -1;
2145 ystep = yerr > 0 ? 1 : -1;
2146 xerr = abs(xerr) << 1;
2147 yerr = abs(yerr) << 1;
2149 color1 = rp_colors[ bgdrawcolor ];
2150 color2 = rp_colors[ drawcolor ];
2152 r = RGB_UNPACK_RED( color1 );
2153 g = RGB_UNPACK_GREEN( color1 );
2154 b = RGB_UNPACK_BLUE( color1 );
2155 rgb2hsv( r, g, b, &h1, &s1, &v1 );
2157 r = RGB_UNPACK_RED( color2 );
2158 g = RGB_UNPACK_GREEN( color2 );
2159 b = RGB_UNPACK_BLUE( color2 );
2160 rgb2hsv( r, g, b, &h2, &s2, &v2 );
2162 if( xerr > yerr )
2164 err = xerr>>1;
2165 delta = err+1;
2166 /* to leave off the last pixel of the line, leave off the "+ 1" */
2167 for (i = delta; i; --i)
2169 hsv2rgb( h2+((h1-h2)*i)/delta,
2170 s2+((s1-s2)*i)/delta,
2171 v2+((v1-v2)*i)/delta,
2172 &r, &g, &b );
2173 rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
2174 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2175 draw_pixel(x1, y1);
2176 x1 += xstep;
2177 err -= yerr;
2178 if (err < 0) {
2179 y1 += ystep;
2180 err += xerr;
2184 else /* yerr >= xerr */
2186 err = yerr>>1;
2187 delta = err+1;
2188 for (i = delta; i; --i)
2190 hsv2rgb( h2+((h1-h2)*i)/delta,
2191 s2+((s1-s2)*i)/delta,
2192 v2+((v1-v2)*i)/delta,
2193 &r, &g, &b );
2194 rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
2195 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2196 draw_pixel(x1, y1);
2197 y1 += ystep;
2198 err -= xerr;
2199 if (err < 0) {
2200 x1 += xstep;
2201 err += yerr;
2205 rp_colors[ drawcolor ] = color2;
2208 /* macros used by linear_gradient() and radial_gradient(). */
2209 #define PUSH( _x, _y ) \
2210 save_buffer[(_x)+(_y)*COLS] = mark_color; \
2211 buffer->coord[i].x = (short)(_x); \
2212 buffer->coord[i].y = (short)(_y); \
2213 i++;
2214 #define POP( _x, _y ) \
2215 i--; \
2216 _x = (int)buffer->coord[i].x; \
2217 _y = (int)buffer->coord[i].y;
2218 #define PUSH2( _x, _y ) \
2219 j--; \
2220 buffer->coord[j].x = (short)(_x); \
2221 buffer->coord[j].y = (short)(_y);
2222 #define POP2( _x, _y ) \
2223 _x = (int)buffer->coord[j].x; \
2224 _y = (int)buffer->coord[j].y; \
2225 j++;
2227 static void linear_gradient( int x1, int y1, int x2, int y2 )
2229 int r1 = RGB_UNPACK_RED( rp_colors[ bgdrawcolor ] );
2230 int g1 = RGB_UNPACK_GREEN( rp_colors[ bgdrawcolor ] );
2231 int b1 = RGB_UNPACK_BLUE( rp_colors[ bgdrawcolor ] );
2232 int r2 = RGB_UNPACK_RED( rp_colors[ drawcolor ] );
2233 int g2 = RGB_UNPACK_GREEN( rp_colors[ drawcolor ] );
2234 int b2 = RGB_UNPACK_BLUE( rp_colors[ drawcolor ] );
2235 fb_data color = rp_colors[ drawcolor ];
2237 int h1, s1, v1, h2, s2, v2, r, g, b;
2239 /* radius^2 */
2240 int radius2 = ( x1 - x2 ) * ( x1 - x2 ) + ( y1 - y2 ) * ( y1 - y2 );
2241 int dist2, i=0, j=COLS*ROWS;
2243 /* We only propagate the gradient to neighboring pixels with the same
2244 * color as ( x1, y1 ) */
2245 fb_data prev_color = save_buffer[ x1+y1*COLS ];
2246 /* to mark pixel that the pixel is already in LIFO. */
2247 fb_data mark_color = ~prev_color;
2249 int x = x1;
2250 int y = y1;
2252 if( radius2 == 0 ) return;
2253 if( preview )
2255 line_gradient( x1, y1, x2, y2 );
2256 return;
2258 if( rp_colors[ drawcolor ] == rp_colors[ bgdrawcolor ] )
2260 draw_fill( x1, y1 );
2261 return;
2264 rgb2hsv( r1, g1, b1, &h1, &s1, &v1 );
2265 rgb2hsv( r2, g2, b2, &h2, &s2, &v2 );
2267 PUSH( x, y );
2269 while( i > 0 )
2271 POP( x, y );
2273 dist2 = ( x2 - x1 ) * ( x - x1 ) + ( y2 - y1 ) * ( y - y1 );
2274 if( dist2 <= 0 )
2276 rp_colors[ drawcolor ] = rp_colors[ bgdrawcolor ];
2278 else if( dist2 < radius2 )
2280 hsv2rgb( h1+((h2-h1)*dist2)/radius2,
2281 s1+((s2-s1)*dist2)/radius2,
2282 v1+((v2-v1)*dist2)/radius2,
2283 &r, &g, &b );
2284 rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
2286 else
2288 rp_colors[ drawcolor ] = color;
2290 if( rp_colors[ drawcolor ] == prev_color )
2292 /* "mark" that pixel was checked. correct color later. */
2293 PUSH2( x, y );
2294 rp_colors[ drawcolor ] = mark_color;
2296 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2297 draw_pixel( x, y );
2299 if( x > 0 && save_buffer[x-1+y*COLS] == prev_color )
2301 PUSH( x-1, y );
2303 if( x < COLS-1 && save_buffer[x+1+y*COLS] == prev_color )
2305 PUSH( x+1, y );
2307 if( y > 0 && save_buffer[x+(y-1)*COLS] == prev_color )
2309 PUSH( x, y-1 );
2311 if( y < ROWS - 1 && save_buffer[x+(y+1)*COLS] == prev_color )
2313 PUSH( x, y+1 );
2316 while (j < COLS*ROWS)
2318 /* correct color. */
2319 POP2( x, y );
2320 rp_colors[ drawcolor ] = prev_color;
2321 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2322 draw_pixel( x, y );
2324 rp_colors[ drawcolor ] = color;
2327 static void radial_gradient( int x1, int y1, int x2, int y2 )
2329 int r1 = RGB_UNPACK_RED( rp_colors[ bgdrawcolor ] );
2330 int g1 = RGB_UNPACK_GREEN( rp_colors[ bgdrawcolor ] );
2331 int b1 = RGB_UNPACK_BLUE( rp_colors[ bgdrawcolor ] );
2332 int r2 = RGB_UNPACK_RED( rp_colors[ drawcolor ] );
2333 int g2 = RGB_UNPACK_GREEN( rp_colors[ drawcolor ] );
2334 int b2 = RGB_UNPACK_BLUE( rp_colors[ drawcolor ] );
2335 fb_data color = rp_colors[ drawcolor ];
2337 int h1, s1, v1, h2, s2, v2, r, g, b;
2339 /* radius^2 */
2340 int radius2 = ( x1 - x2 ) * ( x1 - x2 ) + ( y1 - y2 ) * ( y1 - y2 );
2341 int dist2, i=0, j=COLS*ROWS;
2343 /* We only propagate the gradient to neighboring pixels with the same
2344 * color as ( x1, y1 ) */
2345 fb_data prev_color = save_buffer[ x1+y1*COLS ];
2346 /* to mark pixel that the pixel is already in LIFO. */
2347 fb_data mark_color = ~prev_color;
2349 int x = x1;
2350 int y = y1;
2352 if( radius2 == 0 ) return;
2353 if( preview )
2355 line_gradient( x1, y1, x2, y2 );
2356 return;
2358 if( rp_colors[ drawcolor ] == rp_colors[ bgdrawcolor ] )
2360 draw_fill( x1, y1 );
2361 return;
2364 rgb2hsv( r1, g1, b1, &h1, &s1, &v1 );
2365 rgb2hsv( r2, g2, b2, &h2, &s2, &v2 );
2367 PUSH( x, y );
2369 while( i > 0 )
2371 POP( x, y );
2373 dist2 = ( x - x1 ) * ( x - x1 ) + ( y - y1 ) * ( y - y1 );
2374 if( dist2 < radius2 )
2376 hsv2rgb( h1+((h2-h1)*dist2)/radius2,
2377 s1+((s2-s1)*dist2)/radius2,
2378 v1+((v2-v1)*dist2)/radius2,
2379 &r, &g, &b );
2380 rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
2382 else
2384 rp_colors[ drawcolor ] = color;
2386 if( rp_colors[ drawcolor ] == prev_color )
2388 /* "mark" that pixel was checked. correct color later. */
2389 PUSH2( x, y );
2390 rp_colors[ drawcolor ] = mark_color;
2392 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2393 draw_pixel( x, y );
2395 if( x > 0 && save_buffer[x-1+y*COLS] == prev_color )
2397 PUSH( x-1, y );
2399 if( x < COLS-1 && save_buffer[x+1+y*COLS] == prev_color )
2401 PUSH( x+1, y );
2403 if( y > 0 && save_buffer[x+(y-1)*COLS] == prev_color )
2405 PUSH( x, y-1 );
2407 if( y < ROWS - 1 && save_buffer[x+(y+1)*COLS] == prev_color )
2409 PUSH( x, y+1 );
2412 while (j < COLS*ROWS)
2414 /* correct color. */
2415 POP2( x, y );
2416 rp_colors[ drawcolor ] = prev_color;
2417 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2418 draw_pixel( x, y );
2420 rp_colors[ drawcolor ] = color;
2423 #undef PUSH
2424 #undef POP
2425 #undef PUSH2
2426 #undef POP2
2428 static void draw_toolbars(bool update)
2430 int i;
2431 #define TOP (LCD_HEIGHT-TB_HEIGHT)
2432 rb->lcd_set_background( COLOR_LIGHTGRAY );
2433 rb->lcd_set_foreground( COLOR_LIGHTGRAY );
2434 rb->lcd_fillrect( 0, TOP, LCD_WIDTH, TB_HEIGHT );
2435 rb->lcd_set_foreground( COLOR_BLACK );
2436 rb->lcd_drawrect( 0, TOP, LCD_WIDTH, TB_HEIGHT );
2438 rb->lcd_set_foreground( rp_colors[ bgdrawcolor ] );
2439 rb->lcd_fillrect( TB_SC_BG_LEFT, TOP+TB_SC_BG_TOP,
2440 TB_SC_SIZE, TB_SC_SIZE );
2441 rb->lcd_set_foreground(ROCKPAINT_PALETTE);
2442 rb->lcd_drawrect( TB_SC_BG_LEFT, TOP+TB_SC_BG_TOP,
2443 TB_SC_SIZE, TB_SC_SIZE );
2444 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2445 rb->lcd_fillrect( TB_SC_FG_LEFT, TOP+TB_SC_FG_TOP,
2446 TB_SC_SIZE, TB_SC_SIZE );
2447 rb->lcd_set_foreground(ROCKPAINT_PALETTE);
2448 rb->lcd_drawrect( TB_SC_FG_LEFT, TOP+TB_SC_FG_TOP,
2449 TB_SC_SIZE, TB_SC_SIZE );
2451 for( i=0; i<18; i++ )
2453 rb->lcd_set_foreground( rp_colors[i] );
2454 rb->lcd_fillrect(
2455 TB_PL_LEFT+(i%9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING ),
2456 TOP+TB_PL_TOP+(i/9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING),
2457 TB_PL_COLOR_SIZE, TB_PL_COLOR_SIZE );
2458 rb->lcd_set_foreground( ROCKPAINT_PALETTE );
2459 rb->lcd_drawrect(
2460 TB_PL_LEFT+(i%9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING ),
2461 TOP+TB_PL_TOP+(i/9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING),
2462 TB_PL_COLOR_SIZE, TB_PL_COLOR_SIZE );
2465 #define SEPARATOR( x, y ) \
2466 rb->lcd_set_foreground( COLOR_WHITE ); \
2467 rb->lcd_vline( x, TOP+y, TOP+y+TB_PL_HEIGHT-1 ); \
2468 rb->lcd_set_foreground( COLOR_DARKGRAY ); \
2469 rb->lcd_vline( x+1, TOP+y, TOP+y+TB_PL_HEIGHT-1 );
2470 SEPARATOR( TB_PL_LEFT + TB_PL_WIDTH - 1 + TB_SP_MARGIN, TB_PL_TOP );
2472 rb->lcd_bitmap_transparent( rockpaint, TB_TL_LEFT, TOP+TB_TL_TOP,
2473 TB_TL_WIDTH, TB_TL_HEIGHT );
2474 rb->lcd_set_foreground(ROCKPAINT_PALETTE);
2475 rb->lcd_drawrect( TB_TL_LEFT+(TB_TL_SIZE+TB_TL_SPACING)*(tool/2),
2476 TOP+TB_TL_TOP+(TB_TL_SIZE+TB_TL_SPACING)*(tool%2),
2477 TB_TL_SIZE, TB_TL_SIZE );
2479 SEPARATOR( TB_TL_LEFT + TB_TL_WIDTH - 1 + TB_SP_MARGIN, TB_TL_TOP );
2481 rb->lcd_setfont( FONT_SYSFIXED );
2482 rb->lcd_putsxy( TB_MENU_LEFT, TOP+TB_MENU_TOP, "Menu" );
2483 rb->lcd_setfont( FONT_UI );
2484 #undef TOP
2486 if( update ) rb->lcd_update();
2489 static void toolbar( void )
2491 int button, i, j;
2492 restore_screen();
2493 draw_toolbars( false );
2494 y = LCD_HEIGHT-TB_HEIGHT/2;
2495 inv_cursor( true );
2496 while( 1 )
2498 switch( button = rb->button_get( true ) )
2500 case ROCKPAINT_DRAW:
2501 #define TOP ( LCD_HEIGHT - TB_HEIGHT )
2502 if( y >= TOP + TB_SC_FG_TOP
2503 && y < TOP + TB_SC_FG_TOP + TB_SC_SIZE
2504 && x >= TB_SC_FG_LEFT
2505 && x < TB_SC_FG_LEFT + TB_SC_SIZE )
2507 /* click on the foreground color */
2508 rp_colors[drawcolor] = color_chooser( rp_colors[drawcolor] );
2510 else if( y >= TOP + TB_SC_BG_TOP
2511 && y < TOP + TB_SC_BG_TOP + TB_SC_SIZE
2512 && x >= TB_SC_BG_LEFT
2513 && x < TB_SC_BG_LEFT + TB_SC_SIZE )
2515 /* click on the background color */
2516 i = drawcolor;
2517 drawcolor = bgdrawcolor;
2518 bgdrawcolor = i;
2520 else if( y >= TOP + TB_PL_TOP
2521 && y < TOP + TB_PL_TOP + TB_PL_HEIGHT
2522 && x >= TB_PL_LEFT
2523 && x < TB_PL_LEFT + TB_PL_WIDTH )
2525 /* click on the palette */
2526 i = (x - TB_PL_LEFT)%(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
2527 j = (y - (TOP+TB_PL_TOP) )%(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
2528 if( i >= TB_PL_COLOR_SIZE || j >= TB_PL_COLOR_SIZE )
2529 break;
2530 i = ( x - TB_PL_LEFT )/(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
2531 j = ( y - (TOP+TB_PL_TOP) )/(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
2532 drawcolor = j*(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING)+i;
2534 else if( y >= TOP+TB_TL_TOP
2535 && y < TOP + TB_TL_TOP + TB_TL_HEIGHT
2536 && x >= TB_TL_LEFT
2537 && x <= TB_TL_LEFT + TB_TL_WIDTH )
2539 /* click on the tools */
2540 i = (x - TB_TL_LEFT ) % (TB_TL_SIZE+TB_TL_SPACING);
2541 j = (y - (TOP+TB_TL_TOP) ) %(TB_TL_SIZE+TB_TL_SPACING);
2542 if( i >= TB_TL_SIZE || j >= TB_TL_SIZE ) break;
2543 i = ( x - TB_TL_LEFT )/(TB_TL_SIZE+TB_TL_SPACING);
2544 j = ( y - (TOP+TB_TL_TOP) )/(TB_TL_SIZE+TB_TL_SPACING);
2545 tool = i*2+j;
2546 reset_tool();
2547 if( tool == Text )
2549 buffer->text.initialized = false;
2552 else if( x >= TB_MENU_LEFT && y >= TOP+TB_MENU_TOP-2)
2554 /* menu button */
2555 goto_menu();
2557 #undef TOP
2558 restore_screen();
2559 draw_toolbars( false );
2560 inv_cursor( true );
2561 break;
2563 case ROCKPAINT_LEFT:
2564 case ROCKPAINT_LEFT | BUTTON_REPEAT:
2565 case ROCKPAINT_RIGHT:
2566 case ROCKPAINT_RIGHT | BUTTON_REPEAT:
2567 inv_cursor(false);
2568 incdec_value(&x, &incdec_x,
2569 (button&ROCKPAINT_RIGHT), (button&BUTTON_REPEAT));
2570 inv_cursor(true);
2571 break;
2573 case ROCKPAINT_UP:
2574 case ROCKPAINT_UP | BUTTON_REPEAT:
2575 case ROCKPAINT_DOWN:
2576 case ROCKPAINT_DOWN | BUTTON_REPEAT:
2577 inv_cursor(false);
2578 if (incdec_value(&y, &incdec_y,
2579 (button&ROCKPAINT_DOWN), (button&BUTTON_REPEAT))
2580 || y < LCD_HEIGHT-TB_HEIGHT)
2582 /* went out of region. exit toolbar. */
2583 return;
2585 inv_cursor(true);
2586 break;
2588 case ROCKPAINT_TOOLBAR:
2589 case ROCKPAINT_TOOLBAR2:
2590 return;
2592 if( quit ) return;
2596 static void inv_cursor(bool update)
2598 rb->lcd_set_foreground(COLOR_BLACK);
2599 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
2600 /* cross painting */
2601 rb->lcd_hline(x-4,x+4,y);
2602 rb->lcd_vline(x,y-4,y+4);
2603 rb->lcd_set_foreground(rp_colors[drawcolor]);
2604 rb->lcd_set_drawmode(DRMODE_SOLID);
2606 if( update ) rb->lcd_update();
2609 static void restore_screen(void)
2611 rb->lcd_bitmap( save_buffer, 0, 0, COLS, ROWS );
2612 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
2613 rb->lcd_vline( img_width, 0, ROWS );
2614 rb->lcd_hline( 0, COLS, img_height );
2615 rb->lcd_drawpixel( img_width, img_height );
2616 rb->lcd_set_drawmode(DRMODE_SOLID);
2619 static void clear_drawing(void)
2621 init_buffer();
2622 img_height = ROWS;
2623 img_width = COLS;
2624 rb->lcd_set_foreground( rp_colors[ bgdrawcolor ] );
2625 rb->lcd_fillrect( 0, 0, COLS, ROWS );
2626 rb->lcd_update();
2629 static void goto_menu(void)
2631 int multi;
2632 int selected = 0;
2634 while( 1 )
2636 switch( rb->do_menu( &main_menu, &selected, NULL, false ) )
2638 case MAIN_MENU_NEW:
2639 clear_drawing();
2640 return;
2642 case MAIN_MENU_LOAD:
2643 if( browse( filename, MAX_PATH, "/" ) )
2645 if( load_bitmap( filename ) <= 0 )
2647 rb->splashf( 1*HZ, "Error while loading %s",
2648 filename );
2649 clear_drawing();
2651 else
2653 rb->splashf( 1*HZ, "Image loaded (%s)", filename );
2654 restore_screen();
2655 inv_cursor(true);
2656 return;
2659 break;
2661 case MAIN_MENU_SAVE:
2662 rb->lcd_set_foreground(COLOR_BLACK);
2663 if (!filename[0])
2664 rb->strcpy(filename,"/");
2665 if( !rb->kbd_input( filename, MAX_PATH ) )
2667 if( !check_extention( filename, ".bmp" ) )
2668 rb->strcat(filename, ".bmp");
2669 save_bitmap( filename );
2670 rb->splashf( 1*HZ, "File saved (%s)", filename );
2672 break;
2674 case MAIN_MENU_SET_WIDTH:
2675 rb->set_int( "Set Width", "px", UNIT_INT, &img_width,
2676 NULL, 1, 1, COLS, NULL );
2677 break;
2678 case MAIN_MENU_SET_HEIGHT:
2679 rb->set_int( "Set Height", "px", UNIT_INT, &img_height,
2680 NULL, 1, 1, ROWS, NULL );
2681 break;
2682 case MAIN_MENU_BRUSH_SIZE:
2683 for(multi = 0; multi<4; multi++)
2684 if(bsize == times_list[multi]) break;
2685 rb->set_option( "Brush Size", &multi, INT, times_options, 4, NULL );
2686 if( multi >= 0 )
2687 bsize = times_list[multi];
2688 break;
2690 case MAIN_MENU_BRUSH_SPEED:
2691 for(multi = 0; multi<3; multi++)
2692 if(bspeed == times_list[multi]) break;
2693 rb->set_option( "Brush Speed", &multi, INT, times_options, 3, NULL );
2694 if( multi >= 0 ) {
2695 bspeed = times_list[multi];
2696 incdec_x.step[0] = bspeed;
2697 incdec_x.step[1] = bspeed * 4;
2698 incdec_y.step[0] = bspeed;
2699 incdec_y.step[1] = bspeed * 4;
2701 break;
2703 case MAIN_MENU_COLOR:
2704 rp_colors[drawcolor] = color_chooser( rp_colors[drawcolor] );
2705 break;
2707 case MAIN_MENU_GRID_SIZE:
2708 for(multi = 0; multi<4; multi++)
2709 if(gridsize == gridsize_list[multi]) break;
2710 rb->set_option( "Grid Size", &multi, INT, gridsize_options, 4, NULL );
2711 if( multi >= 0 )
2712 gridsize = gridsize_list[multi];
2713 break;
2715 case MAIN_MENU_PLAYBACK_CONTROL:
2716 if (!audio_buf)
2717 playback_control( NULL );
2718 else
2719 rb->splash(HZ, "Cannot restart playback");
2720 break;
2722 case MAIN_MENU_EXIT:
2723 restore_screen();
2724 quit=true;
2725 return;
2727 case MAIN_MENU_RESUME:
2728 default:
2729 restore_screen();
2730 return;
2731 }/* end switch */
2732 }/* end while */
2735 static void reset_tool( void )
2737 prev_x = -1;
2738 prev_y = -1;
2739 prev_x2 = -1;
2740 prev_y2 = -1;
2741 prev_x3 = -1;
2742 prev_y3 = -1;
2743 /* reset state */
2744 state = State0;
2745 /* always preview color picker */
2746 preview = (tool == ColorPicker);
2749 /* brush tool */
2750 static void state_func_brush(void)
2752 if( state == State0 )
2754 state = State1;
2756 else
2758 state = State0;
2762 /* fill tool */
2763 static void state_func_fill(void)
2765 draw_fill( x, y );
2766 restore_screen();
2769 /* select rectangle tool */
2770 static void state_func_select(void)
2772 int mode;
2773 if( state == State0 )
2775 prev_x = x;
2776 prev_y = y;
2777 preview = true;
2778 state = State1;
2780 else if( state == State1 )
2782 mode = rb->do_menu( &select_menu, NULL, NULL, false );
2783 switch( mode )
2785 case SELECT_MENU_CUT:
2786 case SELECT_MENU_COPY:
2787 prev_x2 = x;
2788 prev_y2 = y;
2789 if( prev_x < x ) x = prev_x;
2790 if( prev_y < y ) y = prev_y;
2791 prev_x3 = abs(prev_x2 - prev_x);
2792 prev_y3 = abs(prev_y2 - prev_y);
2793 copy_to_clipboard();
2794 state = (mode == SELECT_MENU_CUT? State2: State3);
2795 break;
2797 case SELECT_MENU_INVERT:
2798 draw_invert( prev_x, prev_y, x, y );
2799 reset_tool();
2800 break;
2802 case SELECT_MENU_HFLIP:
2803 draw_hflip( prev_x, prev_y, x, y );
2804 reset_tool();
2805 break;
2807 case SELECT_MENU_VFLIP:
2808 draw_vflip( prev_x, prev_y, x, y );
2809 reset_tool();
2810 break;
2812 case SELECT_MENU_ROTATE90:
2813 draw_rot_90_deg( prev_x, prev_y, x, y, 1 );
2814 reset_tool();
2815 break;
2817 case SELECT_MENU_ROTATE180:
2818 draw_hflip( prev_x, prev_y, x, y );
2819 draw_vflip( prev_x, prev_y, x, y );
2820 reset_tool();
2821 break;
2823 case SELECT_MENU_ROTATE270:
2824 draw_rot_90_deg( prev_x, prev_y, x, y, -1 );
2825 reset_tool();
2826 break;
2828 case SELECT_MENU_CANCEL:
2829 reset_tool();
2830 break;
2832 default:
2833 break;
2835 restore_screen();
2837 else
2839 preview = false;
2840 draw_paste_rectangle( prev_x, prev_y, prev_x2, prev_y2,
2841 x, y, state == State2 );
2842 reset_tool();
2843 restore_screen();
2847 static void preview_select(void)
2849 if( state == State1 )
2851 /* we are defining the selection */
2852 draw_select_rectangle( prev_x, prev_y, x, y );
2854 else
2856 /* we are pasting the selected data */
2857 draw_paste_rectangle( prev_x, prev_y, prev_x2, prev_y2,
2858 x, y, state == State2 );
2859 draw_select_rectangle( x, y, x+prev_x3, y+prev_y3 );
2863 /* color picker tool */
2864 static void state_func_picker(void)
2866 preview = false;
2867 color_picker( x, y );
2868 reset_tool();
2871 static void preview_picker(void)
2873 color_picker( x, y );
2876 /* curve tool */
2877 static void state_func_curve(void)
2879 if( state == State0 )
2881 prev_x = x;
2882 prev_y = y;
2883 preview = true;
2884 state = State1;
2886 else if( state == State1 )
2888 prev_x2 = x;
2889 prev_y2 = y;
2890 state = State2;
2892 else if( state == State2 )
2894 prev_x3 = x;
2895 prev_y3 = y;
2896 state = State3;
2898 else
2900 preview = false;
2901 draw_curve( prev_x, prev_y, prev_x2, prev_y2,
2902 prev_x3, prev_y3, x, y );
2903 reset_tool();
2904 restore_screen();
2908 static void preview_curve(void)
2910 if( state == State1 )
2912 draw_line( prev_x, prev_y, x, y );
2914 else
2916 draw_curve( prev_x, prev_y, prev_x2, prev_y2,
2917 prev_x3, prev_y3, x, y );
2921 /* text tool */
2922 static void state_func_text(void)
2924 draw_text( x, y );
2927 /* tools which take 2 point */
2928 static void preview_2point(void);
2929 static void state_func_2point(void)
2931 if( state == State0 )
2933 prev_x = x;
2934 prev_y = y;
2935 state = State1;
2936 preview = true;
2938 else
2940 preview = false;
2941 preview_2point();
2942 reset_tool();
2943 restore_screen();
2947 static void preview_2point(void)
2949 if( state == State1 )
2951 switch( tool )
2953 case Line:
2954 draw_line( prev_x, prev_y, x, y );
2955 break;
2956 case Rectangle:
2957 draw_rect( prev_x, prev_y, x, y );
2958 break;
2959 case RectangleFull:
2960 draw_rect_full( prev_x, prev_y, x, y );
2961 break;
2962 case Oval:
2963 draw_oval_empty( prev_x, prev_y, x, y );
2964 break;
2965 case OvalFull:
2966 draw_oval_full( prev_x, prev_y, x, y );
2967 break;
2968 case LinearGradient:
2969 linear_gradient( prev_x, prev_y, x, y );
2970 break;
2971 case RadialGradient:
2972 radial_gradient( prev_x, prev_y, x, y );
2973 break;
2974 default:
2975 break;
2977 if( !preview )
2979 reset_tool();
2980 restore_screen();
2985 static const struct tool_func tools[14] = {
2986 [Brush] = { state_func_brush, NULL },
2987 [Fill] = { state_func_fill, NULL },
2988 [SelectRectangle] = { state_func_select, preview_select },
2989 [ColorPicker] = { state_func_picker, preview_picker },
2990 [Line] = { state_func_2point, preview_2point },
2991 [Unused] = { NULL, NULL },
2992 [Curve] = { state_func_curve, preview_curve },
2993 [Text] = { state_func_text, NULL },
2994 [Rectangle] = { state_func_2point, preview_2point },
2995 [RectangleFull] = { state_func_2point, preview_2point },
2996 [Oval] = { state_func_2point, preview_2point },
2997 [OvalFull] = { state_func_2point, preview_2point },
2998 [LinearGradient] = { state_func_2point, preview_2point },
2999 [RadialGradient] = { state_func_2point, preview_2point },
3002 static bool rockpaint_loop( void )
3004 int button = 0, i, j;
3005 bool bigstep;
3007 x = 10;
3008 toolbar();
3009 x = 0; y = 0;
3010 restore_screen();
3011 inv_cursor(true);
3013 while (!quit) {
3014 button = rb->button_get(true);
3015 bigstep = (button & BUTTON_REPEAT) && !(tool == Brush && state == State1);
3017 switch(button)
3019 case ROCKPAINT_QUIT:
3020 if (state != State0)
3022 reset_tool();
3023 restore_screen();
3024 inv_cursor(true);
3026 else
3028 rb->lcd_set_drawmode(DRMODE_SOLID);
3029 return PLUGIN_OK;
3031 break;
3033 case ROCKPAINT_MENU:
3034 goto_menu();
3035 restore_screen();
3036 inv_cursor(true);
3037 break;
3039 case ROCKPAINT_DRAW:
3040 if( tools[tool].state_func )
3042 inv_cursor(false);
3043 tools[tool].state_func();
3044 inv_cursor(true);
3046 break;
3048 case ROCKPAINT_DRAW|BUTTON_REPEAT:
3049 if( tool == Curve && state != State0 )
3051 /* 3 point bezier curve */
3052 preview = false;
3053 draw_curve( prev_x, prev_y, prev_x2, prev_y2,
3054 -1, -1, x, y );
3055 reset_tool();
3056 restore_screen();
3057 inv_cursor( true );
3059 break;
3061 case ROCKPAINT_TOOLBAR:
3062 case ROCKPAINT_TOOLBAR2:
3063 i = x; j = y;
3064 x = (button == ROCKPAINT_TOOLBAR2) ? 110: 10;
3065 toolbar();
3066 x = i; y = j;
3067 restore_screen();
3068 inv_cursor(true);
3069 break;
3071 case ROCKPAINT_LEFT:
3072 case ROCKPAINT_LEFT | BUTTON_REPEAT:
3073 case ROCKPAINT_RIGHT:
3074 case ROCKPAINT_RIGHT | BUTTON_REPEAT:
3075 inv_cursor(false);
3076 incdec_value(&x, &incdec_x,
3077 (button&ROCKPAINT_RIGHT), bigstep);
3078 inv_cursor(true);
3079 break;
3081 case ROCKPAINT_UP:
3082 case ROCKPAINT_UP | BUTTON_REPEAT:
3083 case ROCKPAINT_DOWN:
3084 case ROCKPAINT_DOWN | BUTTON_REPEAT:
3085 inv_cursor(false);
3086 if (incdec_value(&y, &incdec_y,
3087 (button&ROCKPAINT_DOWN), bigstep)
3088 && (button&ROCKPAINT_DOWN))
3090 toolbar();
3091 restore_screen();
3093 inv_cursor(true);
3094 break;
3096 default:
3097 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
3098 return PLUGIN_USB_CONNECTED;
3099 break;
3101 if( tool == Brush && state == State1 )
3103 inv_cursor(false);
3104 draw_brush( x, y );
3105 inv_cursor(true);
3107 if( preview && tools[tool].preview_func )
3109 restore_screen();
3110 tools[tool].preview_func();
3111 inv_cursor( true );
3113 if( gridsize > 0 )
3115 show_grid( true );
3116 show_grid( false );
3120 return PLUGIN_OK;
3123 static int load_bitmap( const char *file )
3125 struct bitmap bm;
3126 bool ret;
3127 int i, j;
3128 fb_data color = rp_colors[ bgdrawcolor ];
3130 bm.data = (char*)save_buffer;
3131 ret = rb->read_bmp_file( file, &bm, ROWS*COLS*sizeof( fb_data ),
3132 FORMAT_NATIVE, NULL );
3134 if((bm.width > COLS ) || ( bm.height > ROWS ))
3135 return -1;
3137 img_width = bm.width;
3138 img_height = bm.height;
3139 for( i = bm.height-1; i >= 0; i-- )
3141 rb->memmove( save_buffer+i*COLS, save_buffer+i*bm.width,
3142 sizeof( fb_data )*bm.width );
3143 for( j = bm.width; j < COLS; j++ )
3144 save_buffer[j+i*COLS] = color;
3146 for( i = bm.height*COLS; i < ROWS*COLS; i++ )
3147 save_buffer[i] = color;
3149 return ret;
3152 static int save_bitmap( char *file )
3154 struct bitmap bm;
3155 int i;
3156 for(i = 0; i < img_height; i++)
3158 rb->memcpy( buffer->clipboard+i*img_width, save_buffer+i*COLS,
3159 sizeof( fb_data )*img_width );
3161 bm.data = (char*)buffer->clipboard;
3162 bm.height = img_height;
3163 bm.width = img_width;
3164 bm.format = FORMAT_NATIVE;
3165 return save_bmp_file( file, &bm );
3168 enum plugin_status plugin_start(const void* parameter)
3170 size_t buffer_size;
3171 unsigned char *temp;
3172 temp = rb->plugin_get_buffer(&buffer_size);
3173 if (buffer_size < sizeof(*buffer) + 3)
3175 /* steal from audiobuffer if plugin buffer is too small */
3176 temp = rb->plugin_get_audio_buffer(&buffer_size);
3177 if (buffer_size < sizeof(*buffer) + 3)
3179 rb->splash(HZ, "Not enough memory");
3180 return PLUGIN_ERROR;
3182 audio_buf = true;
3184 buffer = (union buf*) (((uintptr_t)temp + 3) & ~3);
3186 rb->lcd_set_foreground(COLOR_WHITE);
3187 rb->lcd_set_backdrop(NULL);
3188 rb->lcd_fillrect(0,0,LCD_WIDTH,LCD_HEIGHT);
3189 rb->splash( HZ/2, "Rock Paint");
3191 rb->lcd_clear_display();
3193 filename[0] = '\0';
3195 if( parameter )
3197 if( load_bitmap( parameter ) <= 0 )
3199 rb->splash( 1*HZ, "File Open Error");
3200 clear_drawing();
3202 else
3204 rb->splashf( 1*HZ, "Image loaded (%s)", (char *)parameter );
3205 restore_screen();
3206 rb->strcpy( filename, parameter );
3209 else
3211 clear_drawing();
3213 inv_cursor(true);
3215 return rockpaint_loop();